@@ -1366,58 +1366,57 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, regNumber reg, ssize_t imm,
13661366 }
13671367 else
13681368 {
1369+ // Arm64 allows any arbitrary 16-bit constant to be loaded into a register halfword
1370+ // There are three forms
1371+ // movk which loads into any halfword preserving the remaining halfwords
1372+ // movz which loads into any halfword zeroing the remaining halfwords
1373+ // movn which loads into any halfword zeroing the remaining halfwords then bitwise inverting the register
1374+ // In some cases it is preferable to use movn, because it has the side effect of filling the other halfwords
1375+ // with ones
1376+
13691377 // Determine whether movn or movz will require the fewest instructions to populate the immediate
13701378 int preferMovn = 0 ;
13711379
13721380 for (int i = (size == EA_8BYTE) ? 48 : 16 ; i >= 0 ; i -= 16 )
13731381 {
1374- if ((( imm >> i) & 0xffffLL ) == 0xffffLL )
1375- ++preferMovn;
1376- else if ((( imm >> i) & 0xffffLL ) == 0x0000 )
1377- --preferMovn;
1382+ if (uint16_t ( imm >> i) == 0xffff )
1383+ ++preferMovn; // a single movk 0xffff could be skipped if movn was used
1384+ else if (uint16_t ( imm >> i) == 0x0000 )
1385+ --preferMovn; // a single movk 0 could be skipped if movz was used
13781386 }
13791387
1380- // Initial movz or movn will fill the remaining bytes with the skipVal
1381- // This can allow skipping mov* which will be effectively nop.
1382- ssize_t skipVal = (preferMovn > 0 ) ? 0xffffLL : 0 ;
1383-
1384- instruction ins = (skipVal) ? INS_movn : INS_movz;
1385-
1386- ssize_t imm16 = (((ins != INS_movn) ? imm : ~imm) >> (0 )) & 0xffffLL ;
1388+ // Select the first instruction. Any additional instruction will use movk
1389+ instruction ins = (preferMovn > 0 ) ? INS_movn : INS_movz;
13871390
1388- if (imm16 != skipVal)
1389- {
1390- getEmitter ()->emitIns_R_I_I (ins, size, reg, imm16, 0 , INS_OPTS_LSL);
1391- ins = INS_movk;
1392- }
1393-
1394- imm16 = (((ins != INS_movn) ? imm : ~imm) >> (16 )) & 0xffffLL ;
1391+ // Initial movz or movn will fill the remaining bytes with the skipVal
1392+ // This can allow skipping filling a halfword
1393+ uint16_t skipVal = (preferMovn > 0 ) ? 0xffff : 0 ;
13951394
1396- if (imm16 != skipVal)
1397- {
1398- getEmitter ()->emitIns_R_I_I (ins, size, reg, imm16, 16 , INS_OPTS_LSL);
1399- ins = INS_movk;
1400- }
1395+ unsigned bits = (size == EA_8BYTE) ? 64 : 32 ;
14011396
1402- if (size == EA_8BYTE )
1397+ for ( unsigned i = 0 ; i < bits; i += 16 )
14031398 {
1404- imm16 = (((ins != INS_movn) ? imm : ~imm) >> ( 32 )) & 0xffffLL ;
1399+ uint16_t imm16 = uint16_t ( imm >> i) ;
14051400
14061401 if (imm16 != skipVal)
14071402 {
1408- getEmitter ()->emitIns_R_I_I (ins, size, reg, imm16, 32 , INS_OPTS_LSL);
1409- ins = INS_movk;
1410- }
1403+ if (ins == INS_movn)
1404+ {
1405+ // For the movn case, we need to bitwise invert the immediate. This is because
1406+ // (movn x0, ~imm16) === (movz x0, imm16; or x0, x0, #0xffff`ffff`ffff`0000)
1407+ imm16 = ~imm16;
1408+ }
14111409
1412- imm16 = ((( ins != INS_movn) ? imm : ~imm) >> ( 48 )) & 0xffffLL ;
1410+ getEmitter ()-> emitIns_R_I_I ( ins, size, reg, imm16, i, INS_OPTS_LSL) ;
14131411
1414- if (imm16 != skipVal)
1415- {
1416- getEmitter ()->emitIns_R_I_I (ins, size, reg, imm16, 48 , INS_OPTS_LSL);
1412+ // Once the initial movz/movn is emitted the remaining instructions will all use movk
14171413 ins = INS_movk;
14181414 }
14191415 }
14201416
1417+ // We must emit a movn or movz or we have not done anything
1418+ // The cases which hit this assert should be (emitIns_valid_imm_for_mov() == true) and
1419+ // should not be in this else condition
14211420 assert (ins == INS_movk);
14221421 }
14231422 // The caller may have requested that the flags be set on this mov (rarely/never)
0 commit comments