diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 489aa6a744942..9a83060ce1870 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -2241,7 +2241,8 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, { if (emitter::emitIns_valid_imm_for_mov(imm, size)) { - GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm, INS_OPTS_NONE DEBUGARG(targetHandle) DEBUGARG(gtFlags)); + GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm, INS_OPTS_NONE, + INS_SCALABLE_OPTS_NONE DEBUGARG(targetHandle) DEBUGARG(gtFlags)); } else { diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 330cfc9f435d0..18c4782ebf13b 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -5613,6 +5613,68 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_F(INS_sve_fmov, EA_SCALABLE, REG_V4, -0.125, INS_OPTS_SCALABLE_S); // FMOV ., # theEmitter->emitIns_R_F(INS_sve_fmov, EA_SCALABLE, REG_V5, 31.0, INS_OPTS_SCALABLE_D); // FMOV ., # + // IF_SVE_EB_1A + theEmitter->emitIns_R_I(INS_sve_dup, EA_SCALABLE, REG_V0, -128, + INS_OPTS_SCALABLE_B); // DUP ., #{, } + theEmitter->emitIns_R_I(INS_sve_dup, EA_SCALABLE, REG_V1, 0, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_SHIFT); // DUP ., #{, } + theEmitter->emitIns_R_I(INS_sve_dup, EA_SCALABLE, REG_V2, 5, + INS_OPTS_SCALABLE_S); // DUP ., #{, } + theEmitter->emitIns_R_I(INS_sve_dup, EA_SCALABLE, REG_V3, 127, + INS_OPTS_SCALABLE_D); // DUP ., #{, } + theEmitter->emitIns_R_I(INS_sve_mov, EA_SCALABLE, REG_V4, 0, + INS_OPTS_SCALABLE_B); // MOV ., #{, } + theEmitter->emitIns_R_I(INS_sve_mov, EA_SCALABLE, REG_V5, -128, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_SHIFT); // MOV ., #{, } + theEmitter->emitIns_R_I(INS_sve_mov, EA_SCALABLE, REG_V6, 5, INS_OPTS_SCALABLE_S, + INS_SCALABLE_OPTS_SHIFT); // MOV ., #{, } + theEmitter->emitIns_R_I(INS_sve_mov, EA_SCALABLE, REG_V7, 127, INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_SHIFT); // MOV ., #{, } + + // IF_SVE_EC_1A + theEmitter->emitIns_R_I(INS_sve_add, EA_SCALABLE, REG_V0, 0, + INS_OPTS_SCALABLE_B); // ADD ., ., #{, } + theEmitter->emitIns_R_I(INS_sve_sqadd, EA_SCALABLE, REG_V1, 0, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_SHIFT); // SQADD ., ., #{, } + theEmitter->emitIns_R_I(INS_sve_sqsub, EA_SCALABLE, REG_V2, 1, + INS_OPTS_SCALABLE_S); // SQSUB ., ., #{, } + theEmitter->emitIns_R_I(INS_sve_sub, EA_SCALABLE, REG_V3, 128, + INS_OPTS_SCALABLE_D); // SUB ., ., #{, } + theEmitter->emitIns_R_I(INS_sve_subr, EA_SCALABLE, REG_V4, 255, + INS_OPTS_SCALABLE_B); // SUBR ., ., #{, } + theEmitter->emitIns_R_I(INS_sve_uqadd, EA_SCALABLE, REG_V5, 5, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_SHIFT); // UQADD ., ., #{, } + theEmitter->emitIns_R_I(INS_sve_uqsub, EA_SCALABLE, REG_V6, 255, INS_OPTS_SCALABLE_S, + INS_SCALABLE_OPTS_SHIFT); // UQSUB ., ., #{, } + + // IF_SVE_ED_1A + theEmitter->emitIns_R_I(INS_sve_smax, EA_SCALABLE, REG_V0, -128, + INS_OPTS_SCALABLE_B); // SMAX ., ., # + theEmitter->emitIns_R_I(INS_sve_smax, EA_SCALABLE, REG_V1, 127, + INS_OPTS_SCALABLE_H); // SMAX ., ., # + theEmitter->emitIns_R_I(INS_sve_smin, EA_SCALABLE, REG_V2, -128, + INS_OPTS_SCALABLE_S); // SMIN ., ., # + theEmitter->emitIns_R_I(INS_sve_smin, EA_SCALABLE, REG_V3, 127, + INS_OPTS_SCALABLE_D); // SMIN ., ., # + theEmitter->emitIns_R_I(INS_sve_umax, EA_SCALABLE, REG_V4, 0, + INS_OPTS_SCALABLE_B); // UMAX ., ., # + theEmitter->emitIns_R_I(INS_sve_umax, EA_SCALABLE, REG_V5, 255, + INS_OPTS_SCALABLE_H); // UMAX ., ., # + theEmitter->emitIns_R_I(INS_sve_umin, EA_SCALABLE, REG_V6, 0, + INS_OPTS_SCALABLE_S); // UMIN ., ., # + theEmitter->emitIns_R_I(INS_sve_umin, EA_SCALABLE, REG_V7, 255, + INS_OPTS_SCALABLE_D); // UMIN ., ., # + + // IF_SVE_EE_1A + theEmitter->emitIns_R_I(INS_sve_mul, EA_SCALABLE, REG_V0, -128, + INS_OPTS_SCALABLE_B); // MUL ., ., # + theEmitter->emitIns_R_I(INS_sve_mul, EA_SCALABLE, REG_V1, 0, + INS_OPTS_SCALABLE_H); // MUL ., ., # + theEmitter->emitIns_R_I(INS_sve_mul, EA_SCALABLE, REG_V2, 5, + INS_OPTS_SCALABLE_S); // MUL ., ., # + theEmitter->emitIns_R_I(INS_sve_mul, EA_SCALABLE, REG_V3, 127, + INS_OPTS_SCALABLE_D); // MUL ., ., # + // IF_SVE_IH_3A theEmitter->emitIns_R_R_R_I(INS_sve_ld1d, EA_SCALABLE, REG_V5, REG_P3, REG_R4, 0, INS_OPTS_SCALABLE_D); // LD1D {.D }, /Z, [{, #, MUL VL}] diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 07394731019f3..4be970a9e9fd8 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1443,6 +1443,16 @@ class emitter assert(!idIsSmallDsc()); idAddr()->_idRegBit = val ? 1 : 0; } + bool idOptionalShift() const + { + assert(!idIsSmallDsc()); + return (idAddr()->_idRegBit == 1); + } + void idOptionalShift(bool val) + { + assert(!idIsSmallDsc()); + idAddr()->_idRegBit = val ? 1 : 0; + } insSvePattern idSvePattern() const { assert(!idIsSmallDsc()); diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 7338328b788ad..b706548c7ab0e 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1404,8 +1404,40 @@ void emitter::emitInsSanityCheck(instrDesc* id) case IF_SVE_EA_1A: // ........xx...... ...iiiiiiiiddddd -- SVE broadcast floating-point immediate (unpredicated) assert(insOptsScalableAtLeastHalf(id->idInsOpt())); assert(isVectorRegister(id->idReg1())); // ddddd + assert(isValidUimm8(emitGetInsSC(id))); // iiiiiiii assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + break; + + case IF_SVE_EB_1A: // ........xx...... ..hiiiiiiiiddddd -- SVE broadcast integer immediate (unpredicated) + assert(insOptsScalableStandard(id->idInsOpt())); + // Size specifier must be able to fit left-shifted immediate + assert(insOptsScalableAtLeastHalf(id->idInsOpt()) || !id->idOptionalShift()); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isValidSimm8(emitGetInsSC(id))); // iiiiiiii + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + break; + + case IF_SVE_EC_1A: // ........xx...... ..hiiiiiiiiddddd -- SVE integer add/subtract immediate (unpredicated) + assert(insOptsScalableStandard(id->idInsOpt())); + // Size specifier must be able to fit left-shifted immediate + assert(insOptsScalableAtLeastHalf(id->idInsOpt()) || !id->idOptionalShift()); + assert(isVectorRegister(id->idReg1())); // ddddd assert(isValidUimm8(emitGetInsSC(id))); // iiiiiiii + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + break; + + case IF_SVE_ED_1A: // ........xx...... ...iiiiiiiiddddd -- SVE integer min/max immediate (unpredicated) + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isValidSimm8(emitGetInsSC(id)) || isValidUimm8(emitGetInsSC(id))); // iiiiiiii + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + break; + + case IF_SVE_EE_1A: // ........xx...... ...iiiiiiiiddddd -- SVE integer multiply immediate (unpredicated) + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isValidSimm8(emitGetInsSC(id))); // iiiiiiii + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx break; case IF_SVE_IH_3A: // ............iiii ...gggnnnnnttttt -- SVE contiguous load (quadwords, scalar plus @@ -6085,17 +6117,21 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg, insOpts o * Add an instruction referencing a register and a constant. */ -void emitter::emitIns_R_I(instruction ins, - emitAttr attr, - regNumber reg, - ssize_t imm, - insOpts opt /* = INS_OPTS_NONE */ +void emitter::emitIns_R_I(instruction ins, + emitAttr attr, + regNumber reg, + ssize_t imm, + insOpts opt /* = INS_OPTS_NONE */, + insScalableOpts sopt /* = INS_SCALABLE_OPTS_NONE */ DEBUGARG(size_t targetHandle /* = 0 */) DEBUGARG(GenTreeFlags gtFlags /* = GTF_EMPTY */)) { - emitAttr size = EA_SIZE(attr); - emitAttr elemsize = EA_UNKNOWN; - insFormat fmt = IF_NONE; - bool canEncode = false; + emitAttr size = EA_SIZE(attr); + emitAttr elemsize = EA_UNKNOWN; + insFormat fmt = IF_NONE; + bool canEncode = false; + bool signedImm = false; + bool optionalShift = false; + bool hasShift = true; /* Figure out the encoding format of the instruction */ switch (ins) @@ -6324,6 +6360,79 @@ void emitter::emitIns_R_I(instruction ins, } break; + case INS_sve_smax: + case INS_sve_smin: + signedImm = true; + + FALLTHROUGH; + case INS_sve_umax: + case INS_sve_umin: + assert(insOptsScalableStandard(opt)); + assert(isVectorRegister(reg)); // ddddd + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + + if (signedImm) + { + assert(isValidSimm8(imm)); // iiiiiiii + } + else + { + assert(isValidUimm8(imm)); // iiiiiiii + } + + fmt = IF_SVE_ED_1A; + canEncode = true; + break; + + case INS_sve_mul: + assert(insOptsScalableStandard(opt)); + assert(isVectorRegister(reg)); // ddddd + assert(isValidSimm8(imm)); // iiiiiiii + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + fmt = IF_SVE_EE_1A; + canEncode = true; + break; + + case INS_sve_mov: + case INS_sve_dup: + optionalShift = true; + hasShift = (sopt == INS_SCALABLE_OPTS_SHIFT); + + assert(insOptsScalableStandard(opt)); + // Size specifier must be able to fit left-shifted immediate + assert(!hasShift || insOptsScalableAtLeastHalf(opt)); + assert(insScalableOptsNone(sopt) || hasShift); // h + assert(isVectorRegister(reg)); // ddddd + assert(isValidSimm8(imm)); // iiiiiiii + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + fmt = IF_SVE_EB_1A; + canEncode = true; + + // MOV is an alias for DUP, and is always the preferred disassembly. + ins = INS_sve_mov; + break; + + case INS_sve_add: + case INS_sve_sub: + case INS_sve_sqadd: + case INS_sve_sqsub: + case INS_sve_uqadd: + case INS_sve_uqsub: + case INS_sve_subr: + optionalShift = true; + hasShift = (sopt == INS_SCALABLE_OPTS_SHIFT); + + assert(insOptsScalableStandard(opt)); + // Size specifier must be able to fit left-shifted immediate + assert(!hasShift || insOptsScalableAtLeastHalf(opt)); + assert(insScalableOptsNone(sopt) || hasShift); // h + assert(isVectorRegister(reg)); // ddddd + assert(isValidUimm8(imm)); // iiiiiiii + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + fmt = IF_SVE_EC_1A; + canEncode = true; + break; + default: unreached(); break; @@ -6333,7 +6442,18 @@ void emitter::emitIns_R_I(instruction ins, assert(canEncode); assert(fmt != IF_NONE); - instrDesc* id = emitNewInstrSC(attr, imm); + instrDesc* id; + + if (!optionalShift) + { + id = emitNewInstrSC(attr, imm); + } + else + { + // Instructions with optional shifts (MOV, DUP, etc.) need larger instrDesc to store state + id = emitNewInstrCns(attr, imm); + id->idOptionalShift(hasShift); + } id->idIns(ins); id->idInsFmt(fmt); @@ -14901,7 +15021,7 @@ void emitter::emitIns_Call(EmitCallType callType, /***************************************************************************** * - * Returns the encoding for the immediate value as 4-bits starting from 1, at bit locations '19-16'. + * Returns the encoding for the immediate value as 4-bits starting from 1, at bit locations '19-16'. */ /*static*/ emitter::code_t emitter::insEncodeUimm4From1_19_to_16(ssize_t imm) @@ -14910,6 +15030,17 @@ void emitter::emitIns_Call(EmitCallType callType, return (code_t)(imm - 1) << 16; } +/***************************************************************************** + * + * Returns the encoding for the immediate value as 8-bits at bit locations '12-5'. + */ + +/*static*/ emitter::code_t emitter::insEncodeImm8_12_to_5(ssize_t imm) +{ + assert(isValidSimm8(imm) || isValidUimm8(imm)); + return (code_t)((imm & 0xFF) << 5); +} + /***************************************************************************** * * Returns the encoding to select the 4/8-byte width specifier @@ -17195,12 +17326,30 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) break; case IF_SVE_EA_1A: // ........xx...... ...iiiiiiiiddddd -- SVE broadcast floating-point immediate (unpredicated) + case IF_SVE_ED_1A: // ........xx...... ...iiiiiiiiddddd -- SVE integer min/max immediate (unpredicated) + case IF_SVE_EE_1A: // ........xx...... ...iiiiiiiiddddd -- SVE integer multiply immediate (unpredicated) + { + imm = emitGetInsSC(id); code = emitInsCodeSve(ins, fmt); code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd - code |= ((code_t)emitGetInsSC(id) << 5); // iiiiiiii + code |= insEncodeImm8_12_to_5(imm); // iiiiiiii code |= insEncodeElemsize(optGetSveElemsize(id->idInsOpt())); // xx dst += emitOutput_Instr(dst, code); break; + } + + case IF_SVE_EB_1A: // ........xx...... ..hiiiiiiiiddddd -- SVE broadcast integer immediate (unpredicated) + case IF_SVE_EC_1A: // ........xx...... ..hiiiiiiiiddddd -- SVE integer add/subtract immediate (unpredicated) + { + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= insEncodeImm8_12_to_5(imm); // iiiiiiii + code |= (id->idOptionalShift() ? 0x2000 : 0); // h + code |= insEncodeElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + } case IF_SVE_DU_3A: // ........xx.mmmmm ......nnnnn.DDDD -- SVE pointer conflict compare code = emitInsCodeSve(ins, fmt); @@ -17712,18 +17861,18 @@ void emitter::emitDispFloatImm(ssize_t imm8) /***************************************************************************** * - * Display an immediate that is optionally LSL12. + * Display an immediate with an optional left-shift. */ -void emitter::emitDispImmOptsLSL12(ssize_t imm, insOpts opt) +void emitter::emitDispImmOptsLSL(ssize_t imm, bool hasShift, unsigned shiftAmount) { - if (!strictArmAsm && insOptsLSL12(opt)) + if (!strictArmAsm && hasShift) { - imm <<= 12; + imm <<= shiftAmount; } emitDispImm(imm, false); - if (strictArmAsm && insOptsLSL12(opt)) + if (strictArmAsm && hasShift) { - printf(", LSL #12"); + printf(", LSL #%u", shiftAmount); } } @@ -18892,7 +19041,7 @@ void emitter::emitDispInsHelp( case IF_DI_1A: // DI_1A X.......shiiiiii iiiiiinnnnn..... Rn imm(i12,sh) emitDispReg(id->idReg1(), size, true); - emitDispImmOptsLSL12(emitGetInsSC(id), id->idInsOpt()); + emitDispImmOptsLSL(emitGetInsSC(id), insOptsLSL12(id->idInsOpt()), 12); emitDispCommentForHandle(0, id->idDebugOnlyInfo()->idMemCookie, id->idDebugOnlyInfo()->idFlags); break; @@ -18949,7 +19098,7 @@ void emitter::emitDispInsHelp( } else { - emitDispImmOptsLSL12(emitGetInsSC(id), id->idInsOpt()); + emitDispImmOptsLSL(emitGetInsSC(id), insOptsLSL12(id->idInsOpt()), 12); } break; @@ -20036,6 +20185,36 @@ void emitter::emitDispInsHelp( emitDispFloatImm(emitGetInsSC(id)); // iiiiiiii break; + // ADD ., ., #{, } + // SQADD ., ., #{, } + // UQADD ., ., #{, } + // SUB ., ., #{, } + // SUBR ., ., #{, } + // SQSUB ., ., #{, } + // UQSUB ., ., #{, } + case IF_SVE_EC_1A: // ........xx...... ..hiiiiiiiiddddd -- SVE integer add/subtract immediate (unpredicated) + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + + FALLTHROUGH; + // DUP ., #{, } + // MOV ., #{, } + case IF_SVE_EB_1A: // ........xx...... ..hiiiiiiiiddddd -- SVE broadcast integer immediate (unpredicated) + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispImmOptsLSL(emitGetInsSC(id), id->idOptionalShift(), 8); // iiiiiiii, h + break; + + // SMAX ., ., # + // SMIN ., ., # + // UMAX ., ., # + // UMIN ., ., # + case IF_SVE_ED_1A: // ........xx...... ...iiiiiiiiddddd -- SVE integer min/max immediate (unpredicated) + // MUL ., ., # + case IF_SVE_EE_1A: // ........xx...... ...iiiiiiiiddddd -- SVE integer multiply immediate (unpredicated) + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispImm(emitGetInsSC(id), false); // iiiiiiii + break; + // { .D }, /Z, [{, #, MUL VL}] // Some of these formats may allow changing the element size instead of using 'D' for all instructions. case IF_SVE_IH_3A: // ............iiii ...gggnnnnnttttt -- SVE contiguous load (quadwords, scalar plus @@ -22883,13 +23062,30 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins break; case IF_SVE_DZ_1A: // ........xx...... .............DDD -- sve_int_pn_ptrue + case IF_SVE_EA_1A: // ........xx...... ...iiiiiiiiddddd -- SVE broadcast floating-point immediate (unpredicated) + case IF_SVE_EB_1A: // ........xx...... ..hiiiiiiiiddddd -- SVE broadcast integer immediate (unpredicated) + case IF_SVE_EC_1A: // ........xx...... ..hiiiiiiiiddddd -- SVE integer add/subtract immediate (unpredicated) result.insThroughput = PERFSCORE_THROUGHPUT_2C; result.insLatency = PERFSCORE_LATENCY_2C; break; - case IF_SVE_EA_1A: // ........xx...... ...iiiiiiiiddddd -- SVE broadcast floating-point immediate (unpredicated) - result.insThroughput = PERFSCORE_THROUGHPUT_2C; - result.insLatency = PERFSCORE_LATENCY_2C; + case IF_SVE_ED_1A: // ........xx...... ...iiiiiiiiddddd -- SVE integer min/max immediate (unpredicated) + switch (ins) + { + case INS_sve_umin: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + default: + result.insThroughput = PERFSCORE_THROUGHPUT_2C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + } + break; + + case IF_SVE_EE_1A: // ........xx...... ...iiiiiiiiddddd -- SVE integer multiply immediate (unpredicated) + result.insThroughput = PERFSCORE_THROUGHPUT_2X; + result.insLatency = PERFSCORE_LATENCY_5C; break; case IF_SVE_IH_3A: // ............iiii ...gggnnnnnttttt -- SVE contiguous load (quadwords, scalar plus diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index d3ea66913664a..74a9c7fcb58db 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -41,7 +41,7 @@ void emitDispInst(instruction ins); void emitDispImm(ssize_t imm, bool addComma, bool alwaysHex = false, bool isAddrOffset = false); void emitDispFloatZero(); void emitDispFloatImm(ssize_t imm8); -void emitDispImmOptsLSL12(ssize_t imm, insOpts opt); +void emitDispImmOptsLSL(ssize_t imm, bool hasShift, unsigned shiftAmount); void emitDispCond(insCond cond); void emitDispFlags(insCflags flags); void emitDispBarrier(insBarrier barrier); @@ -539,6 +539,9 @@ static code_t insEncodeUimm7_20_to_14(ssize_t imm); // Returns the encoding for the immediate value as 4-bits starting from 1, at bit locations '19-16'. static code_t insEncodeUimm4From1_19_to_16(ssize_t imm); +// Returns the encoding for the immediate value as 8-bits at bit locations '12-5'. +static code_t insEncodeImm8_12_to_5(ssize_t imm); + // Returns the encoding to select the elemsize for an Arm64 SVE vector instruction plus an immediate. // This specifically encodes the field 'tszh:tszl' at bit locations '23-22:9-8'. static code_t insEncodeSveShift_23_to_22_9_to_0(emitAttr size, bool isRightShift, size_t imm); @@ -622,6 +625,12 @@ static bool isValidUimm8(ssize_t value) return (0 <= value) && (value <= 0xFFLL); }; +// Returns true if 'value' is a legal signed immediate 8 bit encoding (such as for SMAX, SMIN). +static bool isValidSimm8(ssize_t value) +{ + return (-128 <= value) && (value <= 127); +}; + // Returns true if 'value' is a legal unsigned immediate 12 bit encoding (such as for CMP, CMN). static bool isValidUimm12(ssize_t value) { @@ -1116,11 +1125,12 @@ void emitIns_I(instruction ins, emitAttr attr, ssize_t imm); void emitIns_R(instruction ins, emitAttr attr, regNumber reg, insOpts opt = INS_OPTS_NONE); -void emitIns_R_I(instruction ins, - emitAttr attr, - regNumber reg, - ssize_t imm, - insOpts opt = INS_OPTS_NONE DEBUGARG(size_t targetHandle = 0) +void emitIns_R_I(instruction ins, + emitAttr attr, + regNumber reg, + ssize_t imm, + insOpts opt = INS_OPTS_NONE, + insScalableOpts sopt = INS_SCALABLE_OPTS_NONE DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); void emitIns_R_F(instruction ins, emitAttr attr, regNumber reg, double immDbl, insOpts opt = INS_OPTS_NONE); diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index ea5795e96e13b..0717b24da4430 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -327,6 +327,7 @@ enum insScalableOpts : unsigned INS_SCALABLE_OPTS_WITH_PREDICATE_PAIR, // Variants with {., .} predicate pair (eg whilege) INS_SCALABLE_OPTS_VL_2X, // Variants with a vector length specifier of 2x (eg whilege) INS_SCALABLE_OPTS_VL_4X, // Variants with a vector length specifier of 4x (eg whilege) + INS_SCALABLE_OPTS_SHIFT, // Variants with an optional shift operation (eg dup) INS_SCALABLE_OPTS_LSL_N, // Variants with a LSL #N (eg {.}, , [, , LSL #2]) INS_SCALABLE_OPTS_MOD_N, // Variants with a #N (eg {.S }, , [, .S, #2])