@@ -222,6 +222,14 @@ static uint32_t enc_cmp_reg(int is64, uint8_t rn, uint8_t rm) {
222222 ((uint32_t )rm << 16 ) | ((uint32_t )rn << 5 );
223223}
224224
225+ static uint32_t enc_clz (int is64 , uint8_t rd , uint8_t rn ) {
226+ return (is64 ? 0xDAC01000u : 0x5AC01000u ) | ((uint32_t )rn << 5 ) | rd ;
227+ }
228+
229+ static uint32_t enc_rbit (int is64 , uint8_t rd , uint8_t rn ) {
230+ return (is64 ? 0xDAC00000u : 0x5AC00000u ) | ((uint32_t )rn << 5 ) | rd ;
231+ }
232+
225233static uint32_t enc_cset (int is64 , uint8_t rd , uint8_t cond ) {
226234 uint32_t base = is64 ? 0x9A9F07E0u : 0x1A9F07E0u ;
227235 uint8_t inv = (uint8_t )(cond ^ 1u );
@@ -507,6 +515,31 @@ static int zxc_arm64_size_at(const uint8_t* in, size_t in_len, size_t off,
507515 size_t n_off = off + 4 ;
508516
509517 switch (op ) {
518+ case 0x35 : /* CLZ */
519+ case 0x45 : /* CLZ64 */
520+ sz = 4 ;
521+ break ;
522+ case 0x36 : /* CTZ */
523+ case 0x46 : /* CTZ64 */
524+ sz = 8 ;
525+ break ;
526+ case 0x37 : /* POPC */
527+ case 0x47 : /* POPC64 */
528+ {
529+ int is64p = (op == 0x47 );
530+ uint64_t mask1 = is64p ? 0x5555555555555555ull : 0x55555555ull ;
531+ uint64_t mask2 = is64p ? 0x3333333333333333ull : 0x33333333ull ;
532+ uint64_t mask3 = is64p ? 0x0F0F0F0F0F0F0F0Full : 0x0F0F0F0Full ;
533+ uint64_t maskf = is64p ? 0x7Full : 0x3Full ;
534+
535+ /* POPC emission uses 4 constant materializations (mask1, mask2, mask3, final mask)
536+ plus a fixed number of ALU/shift instructions. Keep in sync with translate(). */
537+ size_t mov_sz = mov_imm64_size (mask1 ) + mov_imm64_size (mask2 ) +
538+ mov_imm64_size (mask3 ) + mov_imm64_size (maskf );
539+ size_t fixed_insn = is64p ? 23u : 20u ;
540+ sz = mov_sz + fixed_insn * 4u ;
541+ }
542+ break ;
510543 case ZOP_DIVS :
511544 case ZOP_DIVU :
512545 case ZOP_DIVS64 :
@@ -751,6 +784,225 @@ zxc_result_t zxc_arm64_translate(const uint8_t* in, size_t in_len,
751784 /* Read the dropped register but discard the value (matches WASM local.get + drop). */
752785 enc = enc_orr_reg (1 , 31 , rd_m , 31 );
753786 break ;
787+ case 0x35 : /* CLZ */
788+ enc = enc_clz (0 , rd_m , rd_m );
789+ break ;
790+ case 0x45 : /* CLZ64 */
791+ enc = enc_clz (1 , rd_m , rd_m );
792+ break ;
793+ case 0x36 : /* CTZ */
794+ if (!emit_u32 (out , out_cap , & out_len , enc_rbit (0 , ZXC_SCRATCH , rd_m ))) {
795+ res .err = ZXC_ERR_OUTBUF ;
796+ res .in_off = insn_off ;
797+ res .out_len = out_len ;
798+ return res ;
799+ }
800+ enc = enc_clz (0 , rd_m , ZXC_SCRATCH );
801+ break ;
802+ case 0x46 : /* CTZ64 */
803+ if (!emit_u32 (out , out_cap , & out_len , enc_rbit (1 , ZXC_SCRATCH , rd_m ))) {
804+ res .err = ZXC_ERR_OUTBUF ;
805+ res .in_off = insn_off ;
806+ res .out_len = out_len ;
807+ return res ;
808+ }
809+ enc = enc_clz (1 , rd_m , ZXC_SCRATCH );
810+ break ;
811+ case 0x37 : /* POPC */
812+ case 0x47 : /* POPC64 */
813+ {
814+ int is64p = (op == 0x47 );
815+ uint8_t x = rd_m ;
816+ uint8_t t = ZXC_SCRATCH ;
817+ uint8_t sh = ZXC_SCRATCH2 ;
818+ uint8_t m1 = ZXC_SCRATCH3 ;
819+ uint8_t m2 = ZXC_SCRATCH4 ;
820+
821+ uint64_t mask1 = is64p ? 0x5555555555555555ull : 0x55555555ull ;
822+ uint64_t mask2 = is64p ? 0x3333333333333333ull : 0x33333333ull ;
823+ uint64_t mask3 = is64p ? 0x0F0F0F0F0F0F0F0Full : 0x0F0F0F0Full ;
824+ uint64_t maskf = is64p ? 0x7Full : 0x3Full ;
825+
826+ if (!emit_mov_imm64 (out , out_cap , & out_len , m1 , mask1 )) {
827+ res .err = ZXC_ERR_OUTBUF ;
828+ res .in_off = insn_off ;
829+ res .out_len = out_len ;
830+ return res ;
831+ }
832+ if (!emit_mov_imm64 (out , out_cap , & out_len , m2 , mask2 )) {
833+ res .err = ZXC_ERR_OUTBUF ;
834+ res .in_off = insn_off ;
835+ res .out_len = out_len ;
836+ return res ;
837+ }
838+
839+ /* x = x - ((x >> 1) & mask1) */
840+ if (!emit_u32 (out , out_cap , & out_len , enc_add_imm (is64p , sh , 31 , 1 ))) {
841+ res .err = ZXC_ERR_OUTBUF ;
842+ res .in_off = insn_off ;
843+ res .out_len = out_len ;
844+ return res ;
845+ }
846+ if (!emit_u32 (out , out_cap , & out_len , enc_lsrv (is64p , t , x , sh ))) {
847+ res .err = ZXC_ERR_OUTBUF ;
848+ res .in_off = insn_off ;
849+ res .out_len = out_len ;
850+ return res ;
851+ }
852+ if (!emit_u32 (out , out_cap , & out_len , enc_and_reg (is64p , t , t , m1 ))) {
853+ res .err = ZXC_ERR_OUTBUF ;
854+ res .in_off = insn_off ;
855+ res .out_len = out_len ;
856+ return res ;
857+ }
858+ if (!emit_u32 (out , out_cap , & out_len , enc_sub_reg (is64p , x , x , t ))) {
859+ res .err = ZXC_ERR_OUTBUF ;
860+ res .in_off = insn_off ;
861+ res .out_len = out_len ;
862+ return res ;
863+ }
864+
865+ /* x = (x & mask2) + ((x >> 2) & mask2) */
866+ if (!emit_u32 (out , out_cap , & out_len , enc_add_imm (is64p , sh , 31 , 2 ))) {
867+ res .err = ZXC_ERR_OUTBUF ;
868+ res .in_off = insn_off ;
869+ res .out_len = out_len ;
870+ return res ;
871+ }
872+ if (!emit_u32 (out , out_cap , & out_len , enc_lsrv (is64p , t , x , sh ))) {
873+ res .err = ZXC_ERR_OUTBUF ;
874+ res .in_off = insn_off ;
875+ res .out_len = out_len ;
876+ return res ;
877+ }
878+ if (!emit_u32 (out , out_cap , & out_len , enc_and_reg (is64p , t , t , m2 ))) {
879+ res .err = ZXC_ERR_OUTBUF ;
880+ res .in_off = insn_off ;
881+ res .out_len = out_len ;
882+ return res ;
883+ }
884+ if (!emit_u32 (out , out_cap , & out_len , enc_and_reg (is64p , sh , x , m2 ))) {
885+ res .err = ZXC_ERR_OUTBUF ;
886+ res .in_off = insn_off ;
887+ res .out_len = out_len ;
888+ return res ;
889+ }
890+ if (!emit_u32 (out , out_cap , & out_len , enc_add_reg (is64p , x , sh , t ))) {
891+ res .err = ZXC_ERR_OUTBUF ;
892+ res .in_off = insn_off ;
893+ res .out_len = out_len ;
894+ return res ;
895+ }
896+
897+ /* x = (x + (x >> 4)) & mask3 */
898+ if (!emit_u32 (out , out_cap , & out_len , enc_add_imm (is64p , sh , 31 , 4 ))) {
899+ res .err = ZXC_ERR_OUTBUF ;
900+ res .in_off = insn_off ;
901+ res .out_len = out_len ;
902+ return res ;
903+ }
904+ if (!emit_u32 (out , out_cap , & out_len , enc_lsrv (is64p , t , x , sh ))) {
905+ res .err = ZXC_ERR_OUTBUF ;
906+ res .in_off = insn_off ;
907+ res .out_len = out_len ;
908+ return res ;
909+ }
910+ if (!emit_u32 (out , out_cap , & out_len , enc_add_reg (is64p , x , x , t ))) {
911+ res .err = ZXC_ERR_OUTBUF ;
912+ res .in_off = insn_off ;
913+ res .out_len = out_len ;
914+ return res ;
915+ }
916+ if (!emit_mov_imm64 (out , out_cap , & out_len , m1 , mask3 )) {
917+ res .err = ZXC_ERR_OUTBUF ;
918+ res .in_off = insn_off ;
919+ res .out_len = out_len ;
920+ return res ;
921+ }
922+ if (!emit_u32 (out , out_cap , & out_len , enc_and_reg (is64p , x , x , m1 ))) {
923+ res .err = ZXC_ERR_OUTBUF ;
924+ res .in_off = insn_off ;
925+ res .out_len = out_len ;
926+ return res ;
927+ }
928+
929+ /* x += x>>8; x += x>>16; (x += x>>32 for 64-bit) */
930+ if (!emit_u32 (out , out_cap , & out_len , enc_add_imm (is64p , sh , 31 , 8 ))) {
931+ res .err = ZXC_ERR_OUTBUF ;
932+ res .in_off = insn_off ;
933+ res .out_len = out_len ;
934+ return res ;
935+ }
936+ if (!emit_u32 (out , out_cap , & out_len , enc_lsrv (is64p , t , x , sh ))) {
937+ res .err = ZXC_ERR_OUTBUF ;
938+ res .in_off = insn_off ;
939+ res .out_len = out_len ;
940+ return res ;
941+ }
942+ if (!emit_u32 (out , out_cap , & out_len , enc_add_reg (is64p , x , x , t ))) {
943+ res .err = ZXC_ERR_OUTBUF ;
944+ res .in_off = insn_off ;
945+ res .out_len = out_len ;
946+ return res ;
947+ }
948+
949+ if (!emit_u32 (out , out_cap , & out_len , enc_add_imm (is64p , sh , 31 , 16 ))) {
950+ res .err = ZXC_ERR_OUTBUF ;
951+ res .in_off = insn_off ;
952+ res .out_len = out_len ;
953+ return res ;
954+ }
955+ if (!emit_u32 (out , out_cap , & out_len , enc_lsrv (is64p , t , x , sh ))) {
956+ res .err = ZXC_ERR_OUTBUF ;
957+ res .in_off = insn_off ;
958+ res .out_len = out_len ;
959+ return res ;
960+ }
961+ if (!emit_u32 (out , out_cap , & out_len , enc_add_reg (is64p , x , x , t ))) {
962+ res .err = ZXC_ERR_OUTBUF ;
963+ res .in_off = insn_off ;
964+ res .out_len = out_len ;
965+ return res ;
966+ }
967+
968+ if (is64p ) {
969+ if (!emit_u32 (out , out_cap , & out_len , enc_add_imm (is64p , sh , 31 , 32 ))) {
970+ res .err = ZXC_ERR_OUTBUF ;
971+ res .in_off = insn_off ;
972+ res .out_len = out_len ;
973+ return res ;
974+ }
975+ if (!emit_u32 (out , out_cap , & out_len , enc_lsrv (is64p , t , x , sh ))) {
976+ res .err = ZXC_ERR_OUTBUF ;
977+ res .in_off = insn_off ;
978+ res .out_len = out_len ;
979+ return res ;
980+ }
981+ if (!emit_u32 (out , out_cap , & out_len , enc_add_reg (is64p , x , x , t ))) {
982+ res .err = ZXC_ERR_OUTBUF ;
983+ res .in_off = insn_off ;
984+ res .out_len = out_len ;
985+ return res ;
986+ }
987+ }
988+
989+ if (!emit_mov_imm64 (out , out_cap , & out_len , m2 , maskf )) {
990+ res .err = ZXC_ERR_OUTBUF ;
991+ res .in_off = insn_off ;
992+ res .out_len = out_len ;
993+ return res ;
994+ }
995+ if (!emit_u32 (out , out_cap , & out_len , enc_and_reg (is64p , x , x , m2 ))) {
996+ res .err = ZXC_ERR_OUTBUF ;
997+ res .in_off = insn_off ;
998+ res .out_len = out_len ;
999+ return res ;
1000+ }
1001+
1002+ enc = 0 ;
1003+ off += 4 ;
1004+ continue ;
1005+ }
7541006 case ZOP_ADD : is64 = 0 ; enc = enc_add_reg (is64 , rd_m , rs1_m , rs2_m ); break ;
7551007 case ZOP_SUB : is64 = 0 ; enc = enc_sub_reg (is64 , rd_m , rs1_m , rs2_m ); break ;
7561008 case ZOP_MUL : is64 = 0 ; enc = enc_madd (is64 , rd_m , rs1_m , rs2_m , 31 ); break ;
0 commit comments