From 5efaf50e2baa57d7098a4799440aa02fd4c6445d Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Tue, 13 Feb 2024 10:43:09 -0500 Subject: [PATCH 001/158] JIT ARM64-SVE: Add FR_2A, GB_2A, FV_2A, FY_3A (#98248) * wip * Add IF_SVE_FR_2A, IF_SVE_GB_2A * Add IF_SVE_FV_2A * Add IF_SVE_FY_3A * Fix comment * Add isValidRot90_or_270 * Remove isValidRot90_to_270; consistent rot checks * Fix hex values * Use rotation decode helper --- src/coreclr/jit/codegenarm64test.cpp | 96 +++++++ src/coreclr/jit/emitarm64.cpp | 399 ++++++++++++++++++++++++++- src/coreclr/jit/emitarm64.h | 29 +- 3 files changed, 507 insertions(+), 17 deletions(-) diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 6688ad38141718..dd29bb88b9521e 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -5430,6 +5430,40 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_I(INS_sve_uqrshrn, EA_SCALABLE, REG_V15, REG_V12, 1, INS_OPTS_SCALABLE_H); // UQRSHRN .H, {.S-.S }, # + // IF_SVE_GB_2A + theEmitter->emitIns_R_R_I(INS_sve_rshrnb, EA_SCALABLE, REG_V0, REG_V1, 1, + INS_OPTS_SCALABLE_B); // RSHRNB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_rshrnt, EA_SCALABLE, REG_V2, REG_V3, 1, + INS_OPTS_SCALABLE_H); // RSHRNT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_shrnb, EA_SCALABLE, REG_V4, REG_V5, 1, + INS_OPTS_SCALABLE_S); // SHRNB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_shrnt, EA_SCALABLE, REG_V6, REG_V7, 2, + INS_OPTS_SCALABLE_B); // SHRNT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sqrshrnb, EA_SCALABLE, REG_V8, REG_V9, 3, + INS_OPTS_SCALABLE_H); // SQRSHRNB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sqrshrnt, EA_SCALABLE, REG_V10, REG_V11, 4, + INS_OPTS_SCALABLE_S); // SQRSHRNT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sqrshrunb, EA_SCALABLE, REG_V12, REG_V13, 5, + INS_OPTS_SCALABLE_B); // SQRSHRUNB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sqrshrunt, EA_SCALABLE, REG_V14, REG_V15, 8, + INS_OPTS_SCALABLE_H); // SQRSHRUNT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sqshrnb, EA_SCALABLE, REG_V16, REG_V17, 8, + INS_OPTS_SCALABLE_S); // SQSHRNB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sqshrnt, EA_SCALABLE, REG_V18, REG_V19, 6, + INS_OPTS_SCALABLE_B); // SQSHRNT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sqshrunb, EA_SCALABLE, REG_V20, REG_V21, 13, + INS_OPTS_SCALABLE_H); // SQSHRUNB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sqshrunt, EA_SCALABLE, REG_V22, REG_V23, 16, + INS_OPTS_SCALABLE_S); // SQSHRUNT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_uqrshrnb, EA_SCALABLE, REG_V24, REG_V25, 7, + INS_OPTS_SCALABLE_B); // UQRSHRNB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_uqrshrnt, EA_SCALABLE, REG_V26, REG_V27, 16, + INS_OPTS_SCALABLE_H); // UQRSHRNT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_uqshrnb, EA_SCALABLE, REG_V28, REG_V29, 32, + INS_OPTS_SCALABLE_S); // UQSHRNB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_uqshrnt, EA_SCALABLE, REG_V30, REG_V31, 8, + INS_OPTS_SCALABLE_B); // UQSHRNT ., ., # + // IF_SVE_DL_2A theEmitter->emitIns_R_R(INS_sve_cntp, EA_8BYTE, REG_R0, REG_P0, INS_OPTS_SCALABLE_B, INS_SCALABLE_OPTS_VL_2X); // CNTP , ., @@ -6214,6 +6248,68 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R_I(INS_sve_sqrdmlsh, EA_SCALABLE, REG_V22, REG_V23, REG_V15, 1, INS_OPTS_SCALABLE_D); // SQRDMLSH .D, .D, .D[] + // IF_SVE_FR_2A + theEmitter->emitIns_R_R_I(INS_sve_sshllb, EA_SCALABLE, REG_V0, REG_V1, 1, + INS_OPTS_SCALABLE_B); // SSHLLB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sshllt, EA_SCALABLE, REG_V2, REG_V3, 3, + INS_OPTS_SCALABLE_B); // SSHLLT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_ushllb, EA_SCALABLE, REG_V4, REG_V5, 5, + INS_OPTS_SCALABLE_B); // USHLLB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_ushllt, EA_SCALABLE, REG_V6, REG_V7, 7, + INS_OPTS_SCALABLE_B); // USHLLT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sshllb, EA_SCALABLE, REG_V8, REG_V9, 0, + INS_OPTS_SCALABLE_H); // SSHLLB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sshllt, EA_SCALABLE, REG_V10, REG_V11, 5, + INS_OPTS_SCALABLE_H); // SSHLLT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_ushllb, EA_SCALABLE, REG_V12, REG_V13, 10, + INS_OPTS_SCALABLE_H); // USHLLB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_ushllt, EA_SCALABLE, REG_V14, REG_V15, 15, + INS_OPTS_SCALABLE_H); // USHLLT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sshllb, EA_SCALABLE, REG_V16, REG_V17, 8, + INS_OPTS_SCALABLE_S); // SSHLLB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_sshllt, EA_SCALABLE, REG_V18, REG_V19, 16, + INS_OPTS_SCALABLE_S); // SSHLLT ., ., # + theEmitter->emitIns_R_R_I(INS_sve_ushllb, EA_SCALABLE, REG_V20, REG_V21, 24, + INS_OPTS_SCALABLE_S); // USHLLB ., ., # + theEmitter->emitIns_R_R_I(INS_sve_ushllt, EA_SCALABLE, REG_V22, REG_V23, 31, + INS_OPTS_SCALABLE_S); // USHLLT ., ., # + + // IF_SVE_FV_2A + theEmitter->emitIns_R_R_I(INS_sve_cadd, EA_SCALABLE, REG_V0, REG_V1, 90, + INS_OPTS_SCALABLE_B); // CADD ., ., ., + theEmitter->emitIns_R_R_I(INS_sve_cadd, EA_SCALABLE, REG_V2, REG_V3, 90, + INS_OPTS_SCALABLE_H); // CADD ., ., ., + theEmitter->emitIns_R_R_I(INS_sve_cadd, EA_SCALABLE, REG_V4, REG_V5, 270, + INS_OPTS_SCALABLE_S); // CADD ., ., ., + theEmitter->emitIns_R_R_I(INS_sve_cadd, EA_SCALABLE, REG_V6, REG_V7, 270, + INS_OPTS_SCALABLE_D); // CADD ., ., ., + theEmitter->emitIns_R_R_I(INS_sve_sqcadd, EA_SCALABLE, REG_V8, REG_V9, 270, + INS_OPTS_SCALABLE_B); // SQCADD ., ., ., + theEmitter->emitIns_R_R_I(INS_sve_sqcadd, EA_SCALABLE, REG_V10, REG_V11, 270, + INS_OPTS_SCALABLE_H); // SQCADD ., ., ., + theEmitter->emitIns_R_R_I(INS_sve_sqcadd, EA_SCALABLE, REG_V12, REG_V13, 90, + INS_OPTS_SCALABLE_S); // SQCADD ., ., ., + theEmitter->emitIns_R_R_I(INS_sve_sqcadd, EA_SCALABLE, REG_V14, REG_V15, 90, + INS_OPTS_SCALABLE_D); // SQCADD ., ., ., + + // IF_SVE_FY_3A + theEmitter->emitIns_R_R_R(INS_sve_adclb, EA_SCALABLE, REG_V0, REG_V1, REG_V2, + INS_OPTS_SCALABLE_S); // ADCLB ., ., . + theEmitter->emitIns_R_R_R(INS_sve_adclb, EA_SCALABLE, REG_V3, REG_V4, REG_V5, + INS_OPTS_SCALABLE_D); // ADCLB ., ., . + theEmitter->emitIns_R_R_R(INS_sve_adclt, EA_SCALABLE, REG_V6, REG_V7, REG_V8, + INS_OPTS_SCALABLE_S); // ADCLT ., ., . + theEmitter->emitIns_R_R_R(INS_sve_adclt, EA_SCALABLE, REG_V9, REG_V10, REG_V11, + INS_OPTS_SCALABLE_D); // ADCLT ., ., . + theEmitter->emitIns_R_R_R(INS_sve_sbclb, EA_SCALABLE, REG_V12, REG_V13, REG_V14, + INS_OPTS_SCALABLE_S); // SBCLB ., ., . + theEmitter->emitIns_R_R_R(INS_sve_sbclb, EA_SCALABLE, REG_V15, REG_V16, REG_V17, + INS_OPTS_SCALABLE_D); // SBCLB ., ., . + theEmitter->emitIns_R_R_R(INS_sve_sbclt, EA_SCALABLE, REG_V18, REG_V19, REG_V20, + INS_OPTS_SCALABLE_S); // SBCLT ., ., . + theEmitter->emitIns_R_R_R(INS_sve_sbclt, EA_SCALABLE, REG_V21, REG_V22, REG_V23, + INS_OPTS_SCALABLE_D); // SBCLT ., ., . + // IF_SVE_ED_1A theEmitter->emitIns_R_I(INS_sve_smax, EA_SCALABLE, REG_V0, -128, INS_OPTS_SCALABLE_B); // SMAX ., ., # diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index f460833a28e31c..de6af7d816caa7 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1554,6 +1554,80 @@ void emitter::emitInsSanityCheck(instrDesc* id) // x break; + case IF_SVE_FR_2A: // .........x.xxiii ......nnnnnddddd -- SVE2 bitwise shift left long + { + assert(insOptsScalableWide(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isVectorRegister(id->idReg2())); // nnnnn + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // x xx + const ssize_t imm = emitGetInsSC(id); + + switch (id->idInsOpt()) + { + case INS_OPTS_SCALABLE_B: + assert(isValidUimm3(imm)); // iii + break; + + case INS_OPTS_SCALABLE_H: + assert(isValidUimm4(imm)); // x iii + break; + + case INS_OPTS_SCALABLE_S: + assert(isValidUimm5(imm)); // xx iii + break; + + default: + unreached(); + break; + } + break; + } + + case IF_SVE_GB_2A: // .........x.xxiii ......nnnnnddddd -- SVE2 bitwise shift right narrow + { + assert(insOptsScalableWide(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isVectorRegister(id->idReg2())); // nnnnn + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // x xx + const ssize_t imm = emitGetInsSC(id); + + switch (id->idInsOpt()) + { + case INS_OPTS_SCALABLE_B: + assert(isValidUimm3From1(imm)); // iii + break; + + case INS_OPTS_SCALABLE_H: + assert(isValidUimm4From1(imm)); // x iii + break; + + case INS_OPTS_SCALABLE_S: + assert(isValidUimm5From1(imm)); // xx iii + break; + + default: + unreached(); + break; + } + break; + } + + case IF_SVE_FV_2A: // ........xx...... .....rmmmmmddddd -- SVE2 complex integer add + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isVectorRegister(id->idReg2())); // nnnnn + assert(emitIsValidEncodedRotationImm90_or_270(emitGetInsSC(id))); // r + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + break; + + case IF_SVE_FY_3A: // .........x.mmmmm ......nnnnnddddd -- SVE2 integer add/subtract long with carry + assert(insOptsScalableWords(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isVectorRegister(id->idReg2())); // nnnnn + assert(isVectorRegister(id->idReg3())); // mmmmm + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // x + break; + case IF_SVE_GK_2A: // ................ ......mmmmmddddd -- SVE2 crypto destructive binary operations elemsize = id->idOpSize(); assert(insOptsScalableStandard(id->idInsOpt())); @@ -1679,7 +1753,7 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(insOptsScalableStandard(id->idInsOpt())); assert(isVectorRegister(id->idReg1())); // ddddd assert(isVectorRegister(id->idReg2())); // nnnnn - assert(isValidUimm2(emitGetInsSC(id))); // rr + assert(emitIsValidEncodedRotationImm0_to_270(emitGetInsSC(id))); // rr assert(isVectorRegister(id->idReg3())); // mmmmm assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx break; @@ -9160,6 +9234,92 @@ void emitter::emitIns_R_R_I(instruction ins, } break; + case INS_sve_sshllb: + case INS_sve_sshllt: + case INS_sve_ushllb: + case INS_sve_ushllt: + assert(insOptsScalableWide(opt)); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // x xx + + switch (opt) + { + case INS_OPTS_SCALABLE_B: + assert(isValidUimm3(imm)); // iii + break; + + case INS_OPTS_SCALABLE_H: + assert(isValidUimm4(imm)); // x iii + break; + + case INS_OPTS_SCALABLE_S: + assert(isValidUimm5(imm)); // xx iii + break; + + default: + unreached(); + break; + } + + fmt = IF_SVE_FR_2A; + break; + + case INS_sve_sqshrunb: + case INS_sve_sqshrunt: + case INS_sve_sqrshrunb: + case INS_sve_sqrshrunt: + case INS_sve_shrnb: + case INS_sve_shrnt: + case INS_sve_rshrnb: + case INS_sve_rshrnt: + case INS_sve_sqshrnb: + case INS_sve_sqshrnt: + case INS_sve_sqrshrnb: + case INS_sve_sqrshrnt: + case INS_sve_uqshrnb: + case INS_sve_uqshrnt: + case INS_sve_uqrshrnb: + case INS_sve_uqrshrnt: + assert(insOptsScalableWide(opt)); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // x xx + + switch (opt) + { + case INS_OPTS_SCALABLE_B: + assert(isValidUimm3From1(imm)); // iii + break; + + case INS_OPTS_SCALABLE_H: + assert(isValidUimm4From1(imm)); // x iii + break; + + case INS_OPTS_SCALABLE_S: + assert(isValidUimm5From1(imm)); // xx iii + break; + + default: + unreached(); + break; + } + + fmt = IF_SVE_GB_2A; + break; + + case INS_sve_cadd: + case INS_sve_sqcadd: + assert(insOptsScalableStandard(opt)); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + + // Convert rot to bitwise representation: 0 if 90, 1 if 270 + imm = emitEncodeRotationImm90_or_270(imm); // r + fmt = IF_SVE_FV_2A; + break; + case INS_sve_ftmad: assert(insOptsScalableAtLeastHalf(opt)); assert(insScalableOptsNone(sopt)); @@ -10893,6 +11053,18 @@ void emitter::emitIns_R_R_R(instruction ins, fmt = IF_SVE_CZ_4A; break; + case INS_sve_adclb: + case INS_sve_adclt: + case INS_sve_sbclb: + case INS_sve_sbclt: + assert(insOptsScalableWords(opt)); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isVectorRegister(reg3)); // mmmmm + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // x + fmt = IF_SVE_FY_3A; + break; + case INS_sve_mlapt: unreached(); // TODO-SVE: Not yet supported. assert(insOptsNone(opt)); @@ -11507,7 +11679,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx // Convert rot to bitwise representation - imm /= 90; + imm = emitEncodeRotationImm0_to_270(imm); fmt = IF_SVE_EJ_3A; break; @@ -11521,7 +11693,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx // Convert rot to bitwise representation - imm /= 90; + imm = emitEncodeRotationImm0_to_270(imm); fmt = IF_SVE_EK_3A; break; @@ -18633,6 +18805,17 @@ void emitter::emitIns_Call(EmitCallType callType, return (code_t)imm << 16; } +/***************************************************************************** + * + * Returns the encoding for the unsigned immediate value as 5-bits at bit locations '20-16'. + */ + +/*static*/ emitter::code_t emitter::insEncodeUimm5_20_to_16(ssize_t imm) +{ + assert(isValidUimm5(imm)); + return (code_t)imm << 16; +} + /***************************************************************************** * * Returns the encoding for the immediate value as 2-bits at bit locations '9-8'. @@ -18666,6 +18849,17 @@ void emitter::emitIns_Call(EmitCallType callType, return (code_t)imm << 19; } +/***************************************************************************** + * + * Returns the encoding for the immediate value as 1 bit at bit location '10'. + */ + +/*static*/ emitter::code_t emitter::insEncodeImm1_10(ssize_t imm) +{ + assert(isValidImm1(imm)); + return (code_t)imm << 10; +} + /***************************************************************************** * * Returns the encoding for the immediate value as 1 bit at bit location '11'. @@ -18748,6 +18942,35 @@ void emitter::emitIns_Call(EmitCallType callType, return 0; } +/***************************************************************************** + * + * Returns the immediate value for instructions that encode it as a difference + * from tszh:tszl:imm3. + */ +/*static*/ ssize_t emitter::insGetImmDiff(const ssize_t imm, const insOpts opt) +{ + switch (opt) + { + case INS_OPTS_SCALABLE_B: + assert(isValidUimm3From1(imm)); + return (8 - imm); + + case INS_OPTS_SCALABLE_H: + assert(isValidUimm4From1(imm)); + return (16 - imm); + + case INS_OPTS_SCALABLE_S: + assert(isValidUimm5From1(imm)); + return (32 - imm); + + default: + unreached(); + break; + } + + return 0; +} + /***************************************************************************** * * Returns the encoding to select an insSvePattern @@ -21420,6 +21643,48 @@ BYTE* emitter::emitOutput_InstrSve(BYTE* dst, instrDesc* id) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_FR_2A: // .........x.xxiii ......nnnnnddddd -- SVE2 bitwise shift left long + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= insEncodeReg_V_9_to_5(id->idReg2()); // nnnnn + code |= insEncodeUimm5_20_to_16(emitGetInsSC(id)); // iii + code |= insEncodeSveElemsize_tszh_22_tszl_20_to_19(optGetSveElemsize(id->idInsOpt())); // xx + // x + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_GB_2A: // .........x.xxiii ......nnnnnddddd -- SVE2 bitwise shift right narrow + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= insEncodeReg_V_9_to_5(id->idReg2()); // nnnnn + code |= insEncodeUimm5_20_to_16(insGetImmDiff(emitGetInsSC(id), id->idInsOpt())); // iii + code |= insEncodeSveElemsize_tszh_22_tszl_20_to_19(optGetSveElemsize(id->idInsOpt())); // xx + // x + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_FV_2A: // ........xx...... .....rmmmmmddddd -- SVE2 complex integer add + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= insEncodeReg_V_9_to_5(id->idReg2()); // mmmmm + code |= insEncodeImm1_10(emitGetInsSC(id)); // r + code |= insEncodeElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_FY_3A: // .........x.mmmmm ......nnnnnddddd -- SVE2 integer add/subtract long with carry + { + // Size encoding: 1 if INS_OPTS_SCALABLE_D, 0 if INS_OPTS_SCALABLE_S + const ssize_t sizeEncoding = (id->idInsOpt() == INS_OPTS_SCALABLE_D) ? 1 : 0; + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= insEncodeReg_V_9_to_5(id->idReg2()); // nnnnn + code |= insEncodeReg_V_20_to_16(id->idReg3()); // mmmmm + code |= insEncodeImm1_22(sizeEncoding); // x + dst += emitOutput_Instr(dst, code); + break; + } + case IF_SVE_GK_2A: // ................ ......mmmmmddddd -- SVE2 crypto destructive binary operations code = emitInsCodeSve(ins, fmt); code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd @@ -24802,6 +25067,45 @@ void emitter::emitDispInsHelp( emitDispSveReg(id->idReg2(), optWidenSveElemsizeArrangement(id->idInsOpt()), false); // nnnnn break; + // ., ., # + case IF_SVE_FR_2A: // .........x.xxiii ......nnnnnddddd -- SVE2 bitwise shift left long + { + const insOpts largeSizeSpecifier = (insOpts)(id->idInsOpt() + 1); + emitDispSveReg(id->idReg1(), largeSizeSpecifier, true); // ddddd + emitDispSveReg(id->idReg2(), id->idInsOpt(), true); // nnnnn + emitDispImm(emitGetInsSC(id), false); // iii + break; + } + + // ., ., # + case IF_SVE_GB_2A: // .........x.xxiii ......nnnnnddddd -- SVE2 bitwise shift right narrow + { + const insOpts largeSizeSpecifier = (insOpts)(id->idInsOpt() + 1); + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispSveReg(id->idReg2(), largeSizeSpecifier, true); // nnnnn + emitDispImm(emitGetInsSC(id), false); // iii + break; + } + + // ., ., ., + case IF_SVE_FV_2A: // ........xx...... .....rmmmmmddddd -- SVE2 complex integer add + { + // Rotation bit implies rotation is 270 if set, else rotation is 90 + const ssize_t rot = emitDecodeRotationImm90_or_270(emitGetInsSC(id)); + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispSveReg(id->idReg2(), id->idInsOpt(), true); // mmmmm + emitDispImm(rot, false); // r + break; + } + + // ., ., . + case IF_SVE_FY_3A: // .........x.mmmmm ......nnnnnddddd -- SVE2 integer add/subtract long with carry + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispSveReg(id->idReg2(), id->idInsOpt(), true); // nnnnn + emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm + break; + // .B, .B, .B // .S, .S, .S case IF_SVE_GK_2A: // ................ ......mmmmmddddd -- SVE2 crypto destructive binary operations @@ -28255,6 +28559,8 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins 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) case IF_SVE_EB_1B: // ........xx...... ...........ddddd -- SVE broadcast integer immediate (unpredicated) + case IF_SVE_FV_2A: // ........xx...... .....rmmmmmddddd -- SVE2 complex integer add + case IF_SVE_FY_3A: // .........x.mmmmm ......nnnnnddddd -- SVE2 integer add/subtract long with carry result.insThroughput = PERFSCORE_THROUGHPUT_2C; result.insLatency = PERFSCORE_LATENCY_2C; break; @@ -28462,20 +28768,87 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins } break; - case IF_SVE_JM_3A: // ............iiii ...gggnnnnnttttt -- SVE contiguous non-temporal store (scalar plus - // immediate) - result.insThroughput = PERFSCORE_THROUGHPUT_1C; - result.insLatency = PERFSCORE_LATENCY_2C; - break; - - case IF_SVE_JN_3C: // ............iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) + case IF_SVE_FR_2A: // .........x.xxiii ......nnnnnddddd -- SVE2 bitwise shift left long + case IF_SVE_JM_3A: // ............iiii ...gggnnnnnttttt -- SVE contiguous non-temporal store (scalar plus + // immediate) + case IF_SVE_JN_3C: // ............iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) + case IF_SVE_JN_3C_D: // ............iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) result.insThroughput = PERFSCORE_THROUGHPUT_1C; result.insLatency = PERFSCORE_LATENCY_2C; break; - case IF_SVE_JN_3C_D: // ............iiii ...gggnnnnnttttt -- SVE contiguous store (scalar plus immediate) - result.insThroughput = PERFSCORE_THROUGHPUT_1C; - result.insLatency = PERFSCORE_LATENCY_2C; + case IF_SVE_GB_2A: // .........x.xxiii ......nnnnnddddd -- SVE2 bitwise shift right narrow + switch (ins) + { + case INS_sve_sqshrunb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_sqshrunt: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_sqrshrunb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_sqrshrunt: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_shrnb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + case INS_sve_shrnt: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + case INS_sve_rshrnb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_rshrnt: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_sqshrnb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_sqshrnt: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_sqrshrnb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_sqrshrnt: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_uqshrnb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_uqshrnt: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_uqrshrnb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_uqrshrnt: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + default: + // all other instructions + perfScoreUnhandledInstruction(id, &result); + break; + } break; case IF_SVE_JO_3A: // ............iiii ...gggnnnnnttttt -- SVE store multiple structures (scalar plus immediate) diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index abc745e9421c53..a15e2c453efbd5 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -600,6 +600,9 @@ static code_t insEncodeSimm4_MultipleOf32_19_to_16(ssize_t imm); // Returns the encoding for the immediate value as 5-bits at bit locations '20-16'. static code_t insEncodeSimm5_20_to_16(ssize_t imm); +// Returns the encoding for the unsigned immediate value as 5-bits at bit locations '20-16'. +static code_t insEncodeUimm5_20_to_16(ssize_t imm); + // Returns the encoding for the immediate value as 2-bits at bit locations '9-8'. static code_t insEncodeUimm2_9_to_8(ssize_t imm); @@ -609,6 +612,9 @@ static code_t insEncodeUimm2_11_to_10(ssize_t imm); // Returns the encoding for the immediate value as 2-bits at bit locations '20-19'. static code_t insEncodeUimm2_20_to_19(ssize_t imm); +// Returns the encoding for the immediate value as 1 bit at bit location '10'. +static code_t insEncodeImm1_10(ssize_t imm); + // Returns the encoding for the immediate value as 1 bit at bit location '11'. static code_t insEncodeImm1_11(ssize_t imm); @@ -635,6 +641,9 @@ static code_t insEncodeSveShift_23_to_22_9_to_0(emitAttr size, bool isRightShift // for an Arm64 Sve instruction. static code_t insEncodeSveElemsize_R_22(emitAttr size); +// Returns the immediate value for instructions that encode it as a difference from tszh:tszl:imm3. +static ssize_t insGetImmDiff(const ssize_t imm, const insOpts opt); + // Returns the encoding to select an insSvePattern static code_t insEncodeSvePattern(insSvePattern pattern); @@ -659,7 +668,7 @@ static bool isValidSimm4(ssize_t value) // Returns true if 'value' is a legal signed immediate 9 bit encoding (such as for LDR). static bool isValidSimm9(ssize_t value) { - return (-256 <= value) && (value <= 255); + return (-0x100 <= value) && (value <= 0xFF); }; // Returns true if 'value' is a legal signed multiple of 2 immediate 4 bit encoding (such as for LD2Q). @@ -710,16 +719,22 @@ static bool isValidUimm3(ssize_t value) return (0 <= value) && (value <= 7); }; +// Returns true if 'value' is a legal unsigned immediate 3 bit encoding, starting from 1 (such as for SHRNB). +static bool isValidUimm3From1(ssize_t value) +{ + return (1 <= value) && (value <= 8); +}; + // Returns true if 'value' is a legal unsigned immediate 4 bit encoding. static bool isValidUimm4(ssize_t value) { - return (0 <= value) && (value <= 15); + return (0 <= value) && (value <= 0xF); }; // Returns true if 'value' is a legal unsigned immediate 4 bit encoding, starting from 1 (such as for CNTB). static bool isValidUimm4From1(ssize_t value) { - return (1 <= value) && (value <= 16); + return (1 <= value) && (value <= 0x10); }; // Returns true if 'value' is a legal unsigned immediate 5 bit encoding (such as for CCMP). @@ -728,6 +743,12 @@ static bool isValidUimm5(ssize_t value) return (0 <= value) && (value <= 0x1FLL); }; +// Returns true if 'value' is a legal unsigned immediate 5 bit encoding, starting from 1 (such as for SHRNB). +static bool isValidUimm5From1(ssize_t value) +{ + return (1 <= value) && (value <= 0x20); +}; + // Returns true if 'value' is a legal unsigned immediate 7 bit encoding (such as for CMPLT, CMPNE). static bool isValidUimm7(ssize_t value) { @@ -743,7 +764,7 @@ static bool isValidUimm8(ssize_t value) // 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); + return (-0x80 <= value) && (value <= 0x7F); }; // Returns true if 'value' is a legal unsigned immediate 12 bit encoding (such as for CMP, CMN). From b9bd1de448a1539c5e98b05bc624ad4bacb2de28 Mon Sep 17 00:00:00 2001 From: Jo Shields Date: Tue, 13 Feb 2024 11:00:58 -0500 Subject: [PATCH 002/158] Add metabuild properties to enable property-controlled VMR build modes. (#98321) --- eng/DotNetBuild.props | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eng/DotNetBuild.props b/eng/DotNetBuild.props index 7d502a881bd8ba..82ca8e4eccf834 100644 --- a/eng/DotNetBuild.props +++ b/eng/DotNetBuild.props @@ -58,6 +58,9 @@ $(InnerBuildArgs) /p:AdditionalRuntimeIdentifierParent=$(BaseOS) + + $(InnerBuildArgs) /p:WasmEnableThreads=true + $(InnerBuildArgs) $(FlagParameterPrefix)s clr.nativeaotlibs+clr.nativeaotruntime+libs+packs /p:BuildNativeAOTRuntimePack=true /p:SkipLibrariesNativeRuntimePackages=true $(InnerBuildArgs) /p:ArcadeBuildFromSource=true From 6d877c5e510ab95be7c7506debc922873dec092c Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 13 Feb 2024 08:09:41 -0800 Subject: [PATCH 003/158] Update where and when vzeroupper is emitted (#98261) * Update where and when vzeroupper is emitted * Ensure we emit vzeroupper for JIT helpers that need it * Make sure vzeroupper is in genRestoreCalleeSavedFltRegs * Scope when vzeroupper is emitted to fewer places * Revert the simplification done to SetContainsAVXFlags * Try to minify the TP impact of the improved vzeroupper handling --- src/coreclr/jit/codegen.h | 2 - src/coreclr/jit/codegenxarch.cpp | 82 +++++++++++++++----------------- src/coreclr/jit/compiler.cpp | 1 + src/coreclr/jit/compiler.h | 2 + src/coreclr/jit/emitxarch.h | 10 ++++ src/coreclr/jit/gentree.cpp | 69 +++++++++++++++++++++++++++ src/coreclr/jit/gentree.h | 4 ++ src/coreclr/jit/lsraxarch.cpp | 10 ++++ 8 files changed, 135 insertions(+), 45 deletions(-) diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 0f91f798bb9468..aee8ecc0c4e590 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -476,8 +476,6 @@ class CodeGen final : public CodeGenInterface // Save/Restore callee saved float regs to stack void genPreserveCalleeSavedFltRegs(unsigned lclFrameSize); void genRestoreCalleeSavedFltRegs(unsigned lclFrameSize); - // Generate VZeroupper instruction to avoid AVX/SSE transition penalty - void genVzeroupperIfNeeded(bool check256bitOnly = true); #endif // TARGET_XARCH diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 03a255d98078a9..b877262b1583d8 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -6074,16 +6074,18 @@ void CodeGen::genCall(GenTreeCall* call) } #endif // defined(DEBUG) && defined(TARGET_X86) - // When it's a PInvoke call and the call type is USER function, we issue VZEROUPPER here - // if the function contains 256bit AVX instructions, this is to avoid AVX-256 to Legacy SSE - // transition penalty, assuming the user function contains legacy SSE instruction. - // To limit code size increase impact: we only issue VZEROUPPER before PInvoke call, not issue - // VZEROUPPER after PInvoke call because transition penalty from legacy SSE to AVX only happens - // when there's preceding 256-bit AVX to legacy SSE transition penalty. - // This applies to 512bit AVX512 instructions as well. - if (call->IsPInvoke() && (call->gtCallType == CT_USER_FUNC) && (GetEmitter()->Contains256bitOrMoreAVX())) - { - assert(compiler->canUseVexEncoding()); + if (GetEmitter()->Contains256bitOrMoreAVX() && call->NeedsVzeroupper(compiler)) + { + // The Intel optimization manual guidance in `3.11.5.3 Fixing Instruction Slowdowns` states: + // Insert a VZEROUPPER to tell the hardware that the state of the higher registers is clean + // between the VEX and the legacy SSE instructions. Often the best way to do this is to insert a + // VZEROUPPER before returning from any function that uses VEX (that does not produce a VEX + // register) and before any call to an unknown function. + + // This method contains a call that needs vzeroupper but also uses 256-bit or higher + // AVX itself. This means we couldn't optimize to only emitting a single vzeroupper in + // the method prologue and instead need to insert one before each call that needs it. + instGen(INS_vzeroupper); } @@ -11188,12 +11190,27 @@ void CodeGen::genZeroInitFrameUsingBlockInit(int untrLclHi, int untrLclLo, regNu // funclet frames: this will be FuncletInfo.fiSpDelta. void CodeGen::genPreserveCalleeSavedFltRegs(unsigned lclFrameSize) { - genVzeroupperIfNeeded(false); regMaskTP regMask = compiler->compCalleeFPRegsSavedMask; // Only callee saved floating point registers should be in regMask assert((regMask & RBM_FLT_CALLEE_SAVED) == regMask); + if (GetEmitter()->ContainsCallNeedingVzeroupper() && !GetEmitter()->Contains256bitOrMoreAVX()) + { + // The Intel optimization manual guidance in `3.11.5.3 Fixing Instruction Slowdowns` states: + // Insert a VZEROUPPER to tell the hardware that the state of the higher registers is clean + // between the VEX and the legacy SSE instructions. Often the best way to do this is to insert a + // VZEROUPPER before returning from any function that uses VEX (that does not produce a VEX + // register) and before any call to an unknown function. + + // This method contains a call that needs vzeroupper but also doesn't use 256-bit or higher + // AVX itself. Thus we can optimize to only emitting a single vzeroupper in the function prologue + // This reduces the overall amount of codegen, particularly for more common paths not using any + // SIMD or floating-point. + + instGen(INS_vzeroupper); + } + // fast path return if (regMask == RBM_NONE) { @@ -11241,10 +11258,20 @@ void CodeGen::genRestoreCalleeSavedFltRegs(unsigned lclFrameSize) // Only callee saved floating point registers should be in regMask assert((regMask & RBM_FLT_CALLEE_SAVED) == regMask); + if (GetEmitter()->Contains256bitOrMoreAVX()) + { + // The Intel optimization manual guidance in `3.11.5.3 Fixing Instruction Slowdowns` states: + // Insert a VZEROUPPER to tell the hardware that the state of the higher registers is clean + // between the VEX and the legacy SSE instructions. Often the best way to do this is to insert a + // VZEROUPPER before returning from any function that uses VEX (that does not produce a VEX + // register) and before any call to an unknown function. + + instGen(INS_vzeroupper); + } + // fast path return if (regMask == RBM_NONE) { - genVzeroupperIfNeeded(); return; } @@ -11287,37 +11314,6 @@ void CodeGen::genRestoreCalleeSavedFltRegs(unsigned lclFrameSize) offset -= XMM_REGSIZE_BYTES; } } - genVzeroupperIfNeeded(); -} - -// Generate Vzeroupper instruction as needed to zero out upper 128b-bit of all YMM registers so that the -// AVX/Legacy SSE transition penalties can be avoided. This function is been used in genPreserveCalleeSavedFltRegs -// (prolog) and genRestoreCalleeSavedFltRegs (epilog). Issue VZEROUPPER in Prolog if the method contains -// 128-bit or 256-bit AVX code, to avoid legacy SSE to AVX transition penalty, which could happen when native -// code contains legacy SSE code calling into JIT AVX code (e.g. reverse pinvoke). Issue VZEROUPPER in Epilog -// if the method contains 256-bit AVX code, to avoid AVX to legacy SSE transition penalty. -// -// Params -// check256bitOnly - true to check if the function contains 256-bit AVX instruction and generate Vzeroupper -// instruction, false to check if the function contains AVX instruction (either 128-bit or 256-bit). -// -void CodeGen::genVzeroupperIfNeeded(bool check256bitOnly /* = true*/) -{ - bool emitVzeroUpper = false; - if (check256bitOnly) - { - emitVzeroUpper = GetEmitter()->Contains256bitOrMoreAVX(); - } - else - { - emitVzeroUpper = GetEmitter()->ContainsAVX(); - } - - if (emitVzeroUpper) - { - assert(compiler->canUseVexEncoding()); - instGen(INS_vzeroupper); - } } //----------------------------------------------------------------------------------- diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index a4d15fe5706b3e..77e2e1d184c27e 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2312,6 +2312,7 @@ void Compiler::compSetProcessor() // Assume each JITted method does not contain AVX instruction at first codeGen->GetEmitter()->SetContainsAVX(false); codeGen->GetEmitter()->SetContains256bitOrMoreAVX(false); + codeGen->GetEmitter()->SetContainsCallNeedingVzeroupper(false); } if (canUseEvexEncoding()) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 1d4dbcdb82aa8a..07d1ff60aee960 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9383,6 +9383,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX } #ifdef TARGET_XARCH +public: bool canUseVexEncoding() const { return compOpportunisticallyDependsOn(InstructionSet_AVX); @@ -9399,6 +9400,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX return compOpportunisticallyDependsOn(InstructionSet_AVX512F); } +private: //------------------------------------------------------------------------ // DoJitStressEvexEncoding- Answer the question: Do we force EVEX encoding. // diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index d842f91f06a5da..2877346ab4fd36 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -468,6 +468,16 @@ void SetContains256bitOrMoreAVX(bool value) contains256bitOrMoreAVXInstruction = value; } +bool containsCallNeedingVzeroupper = false; +bool ContainsCallNeedingVzeroupper() const +{ + return containsCallNeedingVzeroupper; +} +void SetContainsCallNeedingVzeroupper(bool value) +{ + containsCallNeedingVzeroupper = value; +} + bool IsDstDstSrcAVXInstruction(instruction ins) const; bool IsDstSrcSrcAVXInstruction(instruction ins) const; bool IsThreeOperandAVXInstruction(instruction ins) const; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 58c28f322f3759..6d41f802ef3d45 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2076,6 +2076,75 @@ void CallArgs::Remove(CallArg* arg) assert(!"Did not find arg to remove in CallArgs::Remove"); } +#ifdef TARGET_XARCH +//--------------------------------------------------------------- +// NeedsVzeroupper: Determines if the call needs a vzeroupper emitted before it is invoked +// +// Parameters: +// comp - the compiler +// +// Returns: +// true if a vzeroupper needs to be emitted; otherwise, false +// +bool GenTreeCall::NeedsVzeroupper(Compiler* comp) +{ + bool needsVzeroupper = false; + + if (IsPInvoke() && comp->canUseVexEncoding()) + { + // The Intel optimization manual guidance in `3.11.5.3 Fixing Instruction Slowdowns` states: + // Insert a VZEROUPPER to tell the hardware that the state of the higher registers is clean + // between the VEX and the legacy SSE instructions. Often the best way to do this is to insert a + // VZEROUPPER before returning from any function that uses VEX (that does not produce a VEX + // register) and before any call to an unknown function. + + switch (gtCallType) + { + case CT_USER_FUNC: + case CT_INDIRECT: + { + // Since P/Invokes are not compiled by the runtime, they are typically "unknown" since they + // may use the legacy encoding. This includes both CT_USER_FUNC and CT_INDIRECT + + needsVzeroupper = true; + break; + } + + case CT_HELPER: + { + // Most helpers are well known to not use any floating-point or SIMD logic internally, but + // a few do exist so we need to ensure they are handled. They are identified by taking or + // returning a floating-point or SIMD type, regardless of how it is actually passed/returned. + + if (varTypeUsesFloatReg(this)) + { + needsVzeroupper = true; + } + else + { + for (CallArg& arg : gtArgs.Args()) + { + if (varTypeUsesFloatReg(arg.GetSignatureType())) + { + needsVzeroupper = true; + break; + } + } + } + break; + } + + default: + { + unreached(); + } + } + } + + return needsVzeroupper; +} +#endif // TARGET_XARCH + //--------------------------------------------------------------- // GetOtherRegMask: Get the reg mask of gtOtherRegs of call node // diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index a1dd574b5a64d4..49486d48750f48 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -5124,6 +5124,10 @@ struct GenTreeCall final : public GenTree #endif } +#ifdef TARGET_XARCH + bool NeedsVzeroupper(Compiler* comp); +#endif // TARGET_XARCH + // Get reg mask of all the valid registers of gtOtherRegs array regMaskTP GetOtherRegMask() const; diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 9e7bda99880067..f43febae6c3a42 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -1341,6 +1341,16 @@ int LinearScan::BuildCall(GenTreeCall* call) srcCount += BuildOperandUses(ctrlExpr, ctrlExprCandidates); } + if (call->NeedsVzeroupper(compiler)) + { + // Much like for Contains256bitOrMoreAVX, we want to track if any + // call needs a vzeroupper inserted. This allows us to reduce + // the total number of vzeroupper being inserted for cases where + // no 256+ AVX is used directly by the method. + + compiler->GetEmitter()->SetContainsCallNeedingVzeroupper(true); + } + buildInternalRegisterUses(); // Now generate defs and kills. From b0bcc86b96e80bb13948b7d2ec325f91e8843632 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:16:22 -0600 Subject: [PATCH 004/158] Update dependencies from https://github.com/dotnet/source-build-externals build 20240212.1 (#98353) Microsoft.SourceBuild.Intermediate.source-build-externals From Version 9.0.0-alpha.1.24106.1 -> To Version 9.0.0-alpha.1.24112.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 15c7921e29ab2a..5fa7355abbd30c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -117,9 +117,9 @@ - + https://github.com/dotnet/source-build-externals - f1ef074dfcf79d2f2da6e6ff9df8696a32aa063c + ddfb60463c966af55fd0e222c2266170e83d1324 From 53453a5999cbaab95e08526dc9fe3dd842d4d71d Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Tue, 13 Feb 2024 18:18:51 +0200 Subject: [PATCH 005/158] [mono][interp] Prevent SSA IRops from producing jiterp aborts (#98345) --- src/mono/mono/mini/interp/jiterpreter-opcode-values.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mono/mono/mini/interp/jiterpreter-opcode-values.h b/src/mono/mono/mini/interp/jiterpreter-opcode-values.h index db88ec1dd03ca9..bf9965dc148a5f 100644 --- a/src/mono/mono/mini/interp/jiterpreter-opcode-values.h +++ b/src/mono/mono/mini/interp/jiterpreter-opcode-values.h @@ -69,6 +69,8 @@ OP(MINT_TIER_MONITOR_JITERPRETER, NONE) OP(MINT_TIER_ENTER_JITERPRETER, NONE) OP(MINT_NOP, NONE) OP(MINT_DEF, NONE) +OP(MINT_DEF_ARG, NONE) +OP(MINT_DEF_TIER_VAR, NONE) OP(MINT_DUMMY_USE, NONE) OP(MINT_IL_SEQ_POINT, NONE) OP(MINT_TIER_PATCHPOINT_DATA, NONE) From f04473a9b4192a2a8818aec845d6143ecc0909b4 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:19:17 -0600 Subject: [PATCH 006/158] [main] Update dependencies from dotnet/roslyn (#97388) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://github.com/dotnet/roslyn build 20240122.4 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-1.24072.4 * Update dependencies from https://github.com/dotnet/roslyn build 20240123.8 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-1.24073.8 * Update dependencies from https://github.com/dotnet/roslyn build 20240124.3 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-1.24074.3 * Update dependencies from https://github.com/dotnet/roslyn build 20240125.16 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-1.24075.16 * Update dependencies from https://github.com/dotnet/roslyn build 20240126.10 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-1.24076.10 * Update dependencies from https://github.com/dotnet/roslyn build 20240126.10 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-1.24076.10 * Update dependencies from https://github.com/dotnet/roslyn build 20240126.10 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-1.24076.10 * Update dependencies from https://github.com/dotnet/roslyn build 20240129.14 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-2.24079.14 * Update dependencies from https://github.com/dotnet/roslyn build 20240130.8 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-2.24080.8 * Update dependencies from https://github.com/dotnet/roslyn build 20240131.14 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-2.24081.14 * Update dependencies from https://github.com/dotnet/roslyn build 20240201.7 Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-2.24101.7 * Update dependencies from https://github.com/dotnet/roslyn build 20240209.1 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-2.24109.1 * Update dependencies from https://github.com/dotnet/roslyn build 20240209.17 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-2.24109.17 * Update dependencies from https://github.com/dotnet/roslyn build 20240209.17 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-2.24109.17 * Update dependencies from https://github.com/dotnet/roslyn build 20240209.17 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-2.24109.17 * Comment out tests which fail to compile due to C# breaking change * Update dependencies from https://github.com/dotnet/roslyn build 20240212.13 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-1.24069.13 -> To Version 4.10.0-2.24112.13 --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Larry Ewing Co-authored-by: Alexander Köplinger --- eng/Version.Details.xml | 17 +- eng/Versions.props | 6 +- ...ce.dynamic.namedandoptional.usage.other.cs | 204 +++++++++--------- 3 files changed, 116 insertions(+), 111 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 5fa7355abbd30c..de7d8e409cd530 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -392,17 +392,18 @@ https://github.com/dotnet/runtime-assets b5ac2d9031d4b2dc40683b31de86b05a20b670af - + https://github.com/dotnet/roslyn - 2fe96bca1092f880e91eea6eb17ea3487d89309a + 892d5f01f7f41dfe7fcd82522276c5628255ef74 - + https://github.com/dotnet/roslyn - 2fe96bca1092f880e91eea6eb17ea3487d89309a + 892d5f01f7f41dfe7fcd82522276c5628255ef74 + - + https://github.com/dotnet/roslyn - 2fe96bca1092f880e91eea6eb17ea3487d89309a + 892d5f01f7f41dfe7fcd82522276c5628255ef74 https://github.com/dotnet/roslyn-analyzers @@ -413,9 +414,9 @@ e39798fc8357615ab319c81b20acfb036ef7b513 - + https://github.com/dotnet/roslyn - 2fe96bca1092f880e91eea6eb17ea3487d89309a + 892d5f01f7f41dfe7fcd82522276c5628255ef74 diff --git a/eng/Versions.props b/eng/Versions.props index d6aa3dd91ebdb4..dedf56f14b51ea 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -42,9 +42,9 @@ Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure they do not break the local dev experience. --> - 4.10.0-1.24069.13 - 4.10.0-1.24069.13 - 4.10.0-1.24069.13 + 4.10.0-2.24112.13 + 4.10.0-2.24112.13 + 4.10.0-2.24112.13 diff --git a/eng/Versions.props b/eng/Versions.props index dedf56f14b51ea..6aab65a01319ff 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -34,8 +34,8 @@ - 3.11.0-beta1.24072.1 - 9.0.0-preview.24072.1 + 3.11.0-beta1.24109.1 + 9.0.0-preview.24109.1 $(InnerBuildArgs) $(FlagParameterPrefix)arch $(TargetArch) - $(InnerBuildArgs) $(FlagParameterPrefix)os $(TargetOS) + $(InnerBuildArgs) $(FlagParameterPrefix)os $(TargetOS) $(InnerBuildArgs) $(FlagParameterPrefix)cross $(InnerBuildArgs) $(FlagParameterPrefix)configuration $(Configuration) $(InnerBuildArgs) $(FlagParameterPrefix)allconfigurations From be5694d8e8494f533bfe5d48d987f5e050df80d2 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 13 Feb 2024 15:50:26 -0800 Subject: [PATCH 016/158] Gracefully error-out on StringBuilder BSTR marshalling on Mono (#98379) --- src/mono/mono/component/marshal-ilgen.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mono/mono/component/marshal-ilgen.c b/src/mono/mono/component/marshal-ilgen.c index ee5c60bd419535..c554dab5600dfe 100644 --- a/src/mono/mono/component/marshal-ilgen.c +++ b/src/mono/mono/component/marshal-ilgen.c @@ -2247,6 +2247,12 @@ emit_marshal_object_ilgen (EmitMarshalContext *m, int argnum, MonoType *t, encoding = cb_to_mono->get_string_encoding (m->piinfo, spec); conv = cb_to_mono->get_ptr_to_stringbuilder_conv (m->piinfo, spec, &need_free); + if (conv == MONO_MARSHAL_CONV_INVALID) { + char *msg = g_strdup_printf ("stringbuilder marshalling conversion %d not implemented", encoding); + cb_to_mono->methodBuilder.emit_exception_marshal_directive (mb, msg); + break; + } + g_assert (encoding != -1); if (m_type_is_byref (t)) { From 49fe3b06cdfce737ea1c8964e01f2dd2a4e77d44 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Tue, 13 Feb 2024 17:46:08 -0800 Subject: [PATCH 017/158] Add some more null guards in the OSSL crypto PAL --- .../tests/X509Certificates/CertTests.cs | 6 ++++++ .../tests/X509Certificates/TestData.cs | 2 ++ .../apibridge.c | 17 ++++++++++++++++- .../openssl.c | 5 +++++ .../pal_pkcs7.c | 10 ++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs index 74e10d31338ccb..c97b8040d34a16 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs @@ -591,6 +591,12 @@ public static void UseAfterDispose() } } + [Fact] + public static void EmptyPkcs7ThrowsException() + { + Assert.ThrowsAny(() => new X509Certificate2(TestData.EmptyPkcs7)); + } + [Fact] public static void ExportPublicKeyAsPkcs12() { diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs index e3deb00b354d06..6f62e7a067163e 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs @@ -4224,5 +4224,7 @@ internal static DSAParameters GetDSA1024Params() "09463C6E50BCA36EB3F8BCB00D8A415D2D0DB5AE08303B301F300706052B0E03" + "021A0414A57105D833610A6D07EBFBE51E5486CD3F8BCE0D0414DB32290CC077" + "37E9D9446E37F104FA876C861C0102022710").HexToByteArray(); + + internal static readonly byte[] EmptyPkcs7 = "300B06092A864886F70D010702".HexToByteArray(); } } diff --git a/src/native/libs/System.Security.Cryptography.Native/apibridge.c b/src/native/libs/System.Security.Cryptography.Native/apibridge.c index 5c8d05d17c74d4..bf1eb9d9ecb96b 100644 --- a/src/native/libs/System.Security.Cryptography.Native/apibridge.c +++ b/src/native/libs/System.Security.Cryptography.Native/apibridge.c @@ -112,7 +112,7 @@ int32_t local_X509_get_version(const X509* x509) X509_PUBKEY* local_X509_get_X509_PUBKEY(const X509* x509) { - if (x509) + if (x509 && x509->cert_info) { return x509->cert_info->key; } @@ -123,13 +123,28 @@ X509_PUBKEY* local_X509_get_X509_PUBKEY(const X509* x509) int32_t local_X509_PUBKEY_get0_param( ASN1_OBJECT** palgOid, const uint8_t** pkeyBytes, int* pkeyBytesLen, X509_ALGOR** palg, X509_PUBKEY* pubkey) { + if (!pubkey) + { + return 0; + } + if (palgOid) { + if (!pubkey->algor) + { + return 0; + } + *palgOid = pubkey->algor->algorithm; } if (pkeyBytes) { + if (!pubkey->public_key) + { + return 0; + } + *pkeyBytes = pubkey->public_key->data; *pkeyBytesLen = pubkey->public_key->length; } diff --git a/src/native/libs/System.Security.Cryptography.Native/openssl.c b/src/native/libs/System.Security.Cryptography.Native/openssl.c index ba713b6fdcc212..73822045895425 100644 --- a/src/native/libs/System.Security.Cryptography.Native/openssl.c +++ b/src/native/libs/System.Security.Cryptography.Native/openssl.c @@ -669,6 +669,11 @@ BIO* CryptoNative_GetX509NameInfo(X509* x509, int32_t nameType, int32_t forIssue 0 == strncmp(localOid, szOidUpn, sizeof(szOidUpn))) { // OTHERNAME->ASN1_TYPE->union.field + if (!value->value) + { + return NULL; + } + str = value->value->value.asn1_string; } } diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_pkcs7.c b/src/native/libs/System.Security.Cryptography.Native/pal_pkcs7.c index efb0a738966f55..bc6c1215d632d2 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_pkcs7.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_pkcs7.c @@ -53,9 +53,19 @@ int32_t CryptoNative_GetPkcs7Certificates(PKCS7* p7, X509Stack** certs) switch (OBJ_obj2nid(p7->type)) { case NID_pkcs7_signed: + if (!p7->d.sign) + { + return 0; + } + *certs = p7->d.sign->cert; return 1; case NID_pkcs7_signedAndEnveloped: + if (!p7->d.signed_and_enveloped) + { + return 0; + } + *certs = p7->d.signed_and_enveloped->cert; return 1; } From ad25468103550b322351efb21a45fb54566a8127 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Wed, 14 Feb 2024 09:12:02 +0100 Subject: [PATCH 018/158] [debugger][mt][browser] Disable `TestDebugUsingMultiThreadedRuntime` that times out (#98111) * Disabling * Known issue improvement. --- src/mono/browser/debugger/DebuggerTestSuite/MiscTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mono/browser/debugger/DebuggerTestSuite/MiscTests.cs b/src/mono/browser/debugger/DebuggerTestSuite/MiscTests.cs index 80345ecb16add5..d82c2ecb22006f 100644 --- a/src/mono/browser/debugger/DebuggerTestSuite/MiscTests.cs +++ b/src/mono/browser/debugger/DebuggerTestSuite/MiscTests.cs @@ -1173,6 +1173,7 @@ public static TheoryData CountToTen() return data; } + [ActiveIssue("https://github.com/dotnet/runtime/issues/98110")] [ConditionalTheory(nameof(WasmMultiThreaded))] [MemberData(nameof(CountToTen))] public async Task TestDebugUsingMultiThreadedRuntime(int _attempt) From 28b6eef286b0d9b34c1202ab8236a9ffcb9e9e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 14 Feb 2024 17:20:40 +0900 Subject: [PATCH 019/158] Shrink eh_frame section in naot executables (#98355) `-fno-exceptions` is not enough to stop generating `.eh_frames`. We need to also pass `-fno-asynchronous-unwind-tables`. Saves 50 kB on an app using WKS gc. Possibly more on SRV GC since that has two copies of the GC. Note that we still get unwinding information for the debugger because `-g` is going to force generation of `.debug_frame` instead. But `.debug_frame` goes to the symbols file. --- src/coreclr/nativeaot/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/nativeaot/CMakeLists.txt b/src/coreclr/nativeaot/CMakeLists.txt index 65c69a5db901ba..4b10cefe57387a 100644 --- a/src/coreclr/nativeaot/CMakeLists.txt +++ b/src/coreclr/nativeaot/CMakeLists.txt @@ -15,6 +15,7 @@ endif (MSVC) if(CLR_CMAKE_HOST_UNIX) add_compile_options(-fno-exceptions) # Native AOT runtime doesn't use C++ exception handling + add_compile_options(-fno-asynchronous-unwind-tables) add_compile_options(-nostdlib) if(CLR_CMAKE_TARGET_APPLE) From 0ce39a165a08bf96bfc9da0781a5e4572c818c59 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 14 Feb 2024 09:50:01 +0100 Subject: [PATCH 020/158] JIT: Add a simple `Counter` dumpable (#98350) This allows easily measuring and outputting a single value at the end of all JIT compilations. --- src/coreclr/jit/compiler.hpp | 13 +++++++++++++ src/coreclr/jit/utils.cpp | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 2af3444ee0a816..62fef7e441ce73 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -294,6 +294,19 @@ class Dumpable virtual void dump(FILE* output) = 0; }; +// Helper class record and display a simple single value. +class Counter : public Dumpable +{ +public: + int64_t Value; + + Counter(int64_t initialValue = 0) : Value(initialValue) + { + } + + void dump(FILE* output); +}; + // Helper class to record and display a histogram of different values. // Usage like: // static unsigned s_buckets[] = { 1, 2, 5, 10, 0 }; // Must have terminating 0 diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 619482dd6bc987..aed8cda7c24df9 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1098,6 +1098,11 @@ void ConfigDoubleArray::Dump() #if CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE || MEASURE_MEM_ALLOC +void Counter::dump(FILE* output) +{ + fprintf(output, "%lld\n", (long long)Value); +} + /***************************************************************************** * Histogram class. */ From d4643138e8b9f23482618a32535cc40552da0bb7 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 14 Feb 2024 09:50:48 +0100 Subject: [PATCH 021/158] JIT: Canonicalize loop exits (#98096) This adds another canonicalization requirement for loops during the optimization phases, namely that all regular loop exit blocks have only loop predecessors. This gives a natural place to insert IR that we want to run only when we know the loop was entered. Exceptional loop exit blocks can still have non-loop predecessors, so these must still be accounted for by optimizations. --- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/compiler.h | 17 +- src/coreclr/jit/compiler.hpp | 54 +++++ src/coreclr/jit/fgdiagnostic.cpp | 10 +- src/coreclr/jit/loopcloning.cpp | 11 +- src/coreclr/jit/optimizer.cpp | 345 ++++++++++++++++++++++++++++--- 6 files changed, 403 insertions(+), 36 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 77e2e1d184c27e..f48c3a76943daf 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -5036,7 +5036,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl } } - optLoopsRequirePreHeaders = false; + optLoopsCanonical = false; #ifdef DEBUG DoPhase(this, PHASE_STRESS_SPLIT_TREE, &Compiler::StressSplitTree); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 07d1ff60aee960..4d7002295ee16c 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2184,6 +2184,9 @@ class FlowGraphNaturalLoop template BasicBlockVisit VisitLoopBlocksLexical(TFunc func); + template + BasicBlockVisit VisitRegularExitBlocks(TFunc func); + BasicBlock* GetLexicallyTopMostBlock(); BasicBlock* GetLexicallyBottomMostBlock(); @@ -4976,7 +4979,11 @@ class Compiler FlowGraphDominatorTree* m_domTree; BlockReachabilitySets* m_reachabilitySets; - bool optLoopsRequirePreHeaders; // Do we require that all loops (in m_loops) have pre-headers? + // Do we require loops to be in canonical form? The canonical form ensures that: + // 1. All loops have preheaders (single entry blocks that always enter the loop) + // 2. All loop exits where bbIsHandlerBeg(exit) is false have only loop predecessors. + // + bool optLoopsCanonical; unsigned optNumNaturalLoopsFound; // Number of natural loops found in the loop finding phase bool fgBBVarSetsInited; @@ -5914,7 +5921,7 @@ class Compiler PhaseStatus fgCanonicalizeFirstBB(); - void fgSetEHRegionForNewPreheader(BasicBlock* preheader); + void fgSetEHRegionForNewPreheaderOrExit(BasicBlock* preheader); void fgUnreachableBlock(BasicBlock* block); @@ -6793,6 +6800,7 @@ class Compiler void optFindLoops(); bool optCanonicalizeLoops(); + void optCompactLoops(); void optCompactLoop(FlowGraphNaturalLoop* loop); BasicBlock* optFindLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, BasicBlock* top); @@ -6800,6 +6808,11 @@ class Compiler bool optCreatePreheader(FlowGraphNaturalLoop* loop); void optSetPreheaderWeight(FlowGraphNaturalLoop* loop, BasicBlock* preheader); + bool optCanonicalizeExits(FlowGraphNaturalLoop* loop); + bool optCanonicalizeExit(FlowGraphNaturalLoop* loop, BasicBlock* exit); + weight_t optEstimateEdgeLikelihood(BasicBlock* from, BasicBlock* to, bool* fromProfile); + void optSetExitWeight(FlowGraphNaturalLoop* loop, BasicBlock* exit); + PhaseStatus optCloneLoops(); void optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* context); PhaseStatus optUnrollLoops(); // Unrolls loops (needs to have cost info) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 62fef7e441ce73..1fc695d8a3f67b 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4983,6 +4983,60 @@ BasicBlockVisit FlowGraphNaturalLoop::VisitLoopBlocksLexical(TFunc func) return BasicBlockVisit::Continue; } +//------------------------------------------------------------------------------ +// FlowGraphNaturalLoop::VisitRegularExitBlocks: Visit non-handler blocks that +// are outside the loop but that may have regular predecessors inside the loop. +// +// Type parameters: +// TFunc - Callback functor type +// +// Arguments: +// func - Callback functor that takes a BasicBlock* and returns a +// BasicBlockVisit. +// +// Returns: +// BasicBlockVisit that indicated whether the visit was aborted by the +// callback or whether all blocks were visited. +// +// Remarks: +// Note that no handler begins are visited by this function, even if they +// have regular predecessors inside the loop (for example, finally handlers +// can have regular BBJ_CALLFINALLY predecessors inside the loop). This +// choice is motivated by the fact that such handlers will also show up as +// exceptional exit blocks that must always be handled specially by client +// code regardless. +// +template +BasicBlockVisit FlowGraphNaturalLoop::VisitRegularExitBlocks(TFunc func) +{ + Compiler* comp = m_dfsTree->GetCompiler(); + + BitVecTraits traits = m_dfsTree->PostOrderTraits(); + BitVec visited(BitVecOps::MakeEmpty(&traits)); + + for (FlowEdge* edge : ExitEdges()) + { + BasicBlockVisit result = edge->getSourceBlock()->VisitRegularSuccs(comp, [&](BasicBlock* succ) { + assert(m_dfsTree->Contains(succ)); + if (!comp->bbIsHandlerBeg(succ) && !ContainsBlock(succ) && + BitVecOps::TryAddElemD(&traits, visited, succ->bbPostorderNum) && + (func(succ) == BasicBlockVisit::Abort)) + { + return BasicBlockVisit::Abort; + } + + return BasicBlockVisit::Continue; + }); + + if (result == BasicBlockVisit::Abort) + { + return BasicBlockVisit::Abort; + } + } + + return BasicBlockVisit::Continue; +} + /*****************************************************************************/ #endif //_COMPILER_HPP_ /*****************************************************************************/ diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 3cdb70930e63c6..65248487fb118e 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -4723,12 +4723,20 @@ void Compiler::fgDebugCheckLoops() { return; } - if (optLoopsRequirePreHeaders) + if (optLoopsCanonical) { for (FlowGraphNaturalLoop* loop : m_loops->InReversePostOrder()) { assert(loop->EntryEdges().size() == 1); assert(loop->EntryEdge(0)->getSourceBlock()->KindIs(BBJ_ALWAYS)); + + loop->VisitRegularExitBlocks([=](BasicBlock* exit) { + for (BasicBlock* pred : exit->PredBlocks()) + { + assert(loop->ContainsBlock(pred)); + } + return BasicBlockVisit::Continue; + }); } } } diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 23848154bcff15..ca4c2572fa41d9 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -2969,11 +2969,18 @@ PhaseStatus Compiler::optCloneLoops() if (optLoopsCloned > 0) { - fgRenumberBlocks(); - fgInvalidateDfsTree(); m_dfsTree = fgComputeDfs(); m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); + + if (optCanonicalizeLoops()) + { + fgInvalidateDfsTree(); + m_dfsTree = fgComputeDfs(); + m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); + } + + fgRenumberBlocks(); } #ifdef DEBUG diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index bcb0bc90d071cf..37d66eef6c660b 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -21,8 +21,8 @@ void Compiler::optInit() { fgHasLoops = false; - optLoopsRequirePreHeaders = false; - optNumNaturalLoopsFound = 0; + optLoopsCanonical = false; + optNumNaturalLoopsFound = 0; #ifdef DEBUG loopAlignCandidates = 0; @@ -1319,6 +1319,13 @@ PhaseStatus Compiler::optUnrollLoops() fgDfsBlocksAndRemove(); m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); + if (optCanonicalizeLoops()) + { + fgInvalidateDfsTree(); + m_dfsTree = fgComputeDfs(); + m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); + } + fgRenumberBlocks(); DBEXEC(verbose, fgDispBasicBlocks()); @@ -2680,8 +2687,8 @@ void Compiler::optFindLoops() fgRenumberBlocks(); - // Starting now, we require all loops to have pre-headers. - optLoopsRequirePreHeaders = true; + // Starting now we require all loops to be in canonical form. + optLoopsCanonical = true; // Leave a bread crumb for future phases like loop alignment about whether // looking for loops makes sense. We generally do not expect phases to @@ -2709,11 +2716,28 @@ void Compiler::optFindLoops() bool Compiler::optCanonicalizeLoops() { bool changed = false; + for (FlowGraphNaturalLoop* loop : m_loops->InReversePostOrder()) { changed |= optCreatePreheader(loop); } + // At this point we've created preheaders. That means we are working with + // stale loop and DFS data. However, we can do exit canonicalization even + // on the stale data; this relies on the fact that exiting blocks do not + // change as a result of creating preheaders. On the other hand the exit + // blocks themselves may have changed (previously it may have been another + // loop's header, now it might be its preheader instead). Exit + // canonicalization stil works even with this. + // + // The exit canonicalization needs to be done in post order (inner -> outer + // loops) so that inner exits that also exit outer loops have proper exit + // blocks created for each loop. + for (FlowGraphNaturalLoop* loop : m_loops->InPostOrder()) + { + changed |= optCanonicalizeExits(loop); + } + return changed; } @@ -2948,7 +2972,7 @@ bool Compiler::optCreatePreheader(FlowGraphNaturalLoop* loop) BasicBlock* preheader = fgNewBBbefore(BBJ_ALWAYS, insertBefore, false, header); preheader->SetFlags(BBF_INTERNAL); - fgSetEHRegionForNewPreheader(preheader); + fgSetEHRegionForNewPreheaderOrExit(preheader); if (preheader->NextIs(header)) { @@ -3103,6 +3127,271 @@ void Compiler::optSetPreheaderWeight(FlowGraphNaturalLoop* loop, BasicBlock* pre edgeFromPreheader->setEdgeWeights(preheader->bbWeight, preheader->bbWeight, loop->GetHeader()); } +//----------------------------------------------------------------------------- +// optCanonicalizeExits: Canonicalize all regular exits of the loop so that +// they have only loop predecessors. +// +// Parameters: +// loop - The loop +// +// Returns: +// True if any flow graph modifications were made. +// +bool Compiler::optCanonicalizeExits(FlowGraphNaturalLoop* loop) +{ + bool changed = false; + + for (FlowEdge* edge : loop->ExitEdges()) + { + // Find all blocks outside the loop from this exiting block. Those + // blocks are exits. Note that we may see preheaders created by + // previous canonicalization here, which are not part of the DFS tree + // or properly maintained in a parent loop. The canonicalization here + // works despite this. + edge->getSourceBlock()->VisitRegularSuccs(this, [=, &changed](BasicBlock* succ) { + if (!loop->ContainsBlock(succ)) + { + changed |= optCanonicalizeExit(loop, succ); + } + + return BasicBlockVisit::Continue; + }); + } + + return changed; +} + +//----------------------------------------------------------------------------- +// optCanonicalizeExit: Canonicalize a single exit block to have only loop +// predecessors. +// +// Parameters: +// loop - The loop +// +// Returns: +// True if any flow graph modifications were made. +// +bool Compiler::optCanonicalizeExit(FlowGraphNaturalLoop* loop, BasicBlock* exit) +{ + assert(!loop->ContainsBlock(exit)); + + if (bbIsHandlerBeg(exit)) + { + return false; + } + + bool allLoopPreds = true; + for (BasicBlock* pred : exit->PredBlocks()) + { + if (!loop->ContainsBlock(pred)) + { + allLoopPreds = false; + break; + } + } + + if (allLoopPreds) + { + // Already canonical + JITDUMP("All preds of exit " FMT_BB " of " FMT_LP " are already in the loop, no exit canonicalization needed\n", + exit->bbNum, loop->GetIndex()); + return false; + } + + BasicBlock* newExit; + +#if FEATURE_EH_CALLFINALLY_THUNKS + if (exit->KindIs(BBJ_CALLFINALLY)) + { + // Branches to a BBJ_CALLFINALLY _must_ come from inside its associated + // try region, and when we have callfinally thunks the BBJ_CALLFINALLY + // is outside it. First try to see if the lexically bottom most block + // is part of the try; if so, inserting after that is a good choice. + BasicBlock* finallyBlock = exit->GetTarget(); + assert(finallyBlock->hasHndIndex()); + BasicBlock* bottom = loop->GetLexicallyBottomMostBlock(); + if (bottom->hasTryIndex() && (bottom->getTryIndex() == finallyBlock->getHndIndex()) && !bottom->hasHndIndex()) + { + newExit = fgNewBBafter(BBJ_ALWAYS, bottom, true, exit); + } + else + { + // Otherwise just do the heavy-handed thing and insert it anywhere in the right region. + newExit = fgNewBBinRegion(BBJ_ALWAYS, finallyBlock->bbHndIndex, 0, nullptr, exit, /* putInFilter */ false, + /* runRarely */ false, /* insertAtEnd */ true); + } + } + else +#endif + { + newExit = fgNewBBbefore(BBJ_ALWAYS, exit, false, exit); + newExit->SetFlags(BBF_NONE_QUIRK); + fgSetEHRegionForNewPreheaderOrExit(newExit); + } + + newExit->SetFlags(BBF_INTERNAL); + + fgAddRefPred(exit, newExit); + + newExit->bbCodeOffs = exit->bbCodeOffs; + + JITDUMP("Created new exit " FMT_BB " to replace " FMT_BB " for " FMT_LP "\n", newExit->bbNum, exit->bbNum, + loop->GetIndex()); + + for (BasicBlock* pred : exit->PredBlocks()) + { + if (loop->ContainsBlock(pred)) + { + fgReplaceJumpTarget(pred, exit, newExit); + } + } + + optSetExitWeight(loop, newExit); + return true; +} + +//----------------------------------------------------------------------------- +// optEstimateEdgeLikelihood: Given a block "from" that may transfer control to +// "to", estimate the likelihood that this will happen taking profile into +// account if available. +// +// Parameters: +// from - From block +// to - To block +// fromProfile - [out] Whether or not the estimate is based on profile data +// +// Returns: +// Estimated likelihood of the edge being taken. +// +weight_t Compiler::optEstimateEdgeLikelihood(BasicBlock* from, BasicBlock* to, bool* fromProfile) +{ + *fromProfile = (from->HasFlag(BBF_PROF_WEIGHT) != BBF_EMPTY) && (to->HasFlag(BBF_PROF_WEIGHT) != BBF_EMPTY); + if (!fgIsUsingProfileWeights() || !from->HasFlag(BBF_PROF_WEIGHT) || !to->HasFlag(BBF_PROF_WEIGHT) || + from->KindIs(BBJ_ALWAYS)) + { + return 1.0 / from->NumSucc(this); + } + + bool useEdgeWeights = fgHaveValidEdgeWeights; + + weight_t takenCount = 0; + weight_t notTakenCount = 0; + + if (useEdgeWeights) + { + from->VisitRegularSuccs(this, [&, to](BasicBlock* succ) { + *fromProfile &= succ->hasProfileWeight(); + FlowEdge* edge = fgGetPredForBlock(succ, from); + weight_t edgeWeight = (edge->edgeWeightMin() + edge->edgeWeightMax()) / 2.0; + + if (succ == to) + { + takenCount += edgeWeight; + } + else + { + notTakenCount += edgeWeight; + } + return BasicBlockVisit::Continue; + }); + + // Watch out for cases where edge weights were not properly maintained + // so that it appears no profile flow goes to 'to'. + // + useEdgeWeights = !fgProfileWeightsConsistent(takenCount, BB_ZERO_WEIGHT); + } + + if (!useEdgeWeights) + { + takenCount = 0; + notTakenCount = 0; + + from->VisitRegularSuccs(this, [&, to](BasicBlock* succ) { + *fromProfile &= succ->hasProfileWeight(); + if (succ == to) + { + takenCount += succ->bbWeight; + } + else + { + notTakenCount += succ->bbWeight; + } + + return BasicBlockVisit::Continue; + }); + } + + if (!*fromProfile) + { + return 1.0 / from->NumSucc(this); + } + + if (fgProfileWeightsConsistent(takenCount, BB_ZERO_WEIGHT)) + { + return 0; + } + + weight_t likelihood = takenCount / (takenCount + notTakenCount); + return likelihood; +} + +//----------------------------------------------------------------------------- +// optSetExitWeight: Set the weight of a newly created exit, after it +// has been added to the flowgraph. +// +// Parameters: +// loop - The loop +// preheader - The new exit block +// +void Compiler::optSetExitWeight(FlowGraphNaturalLoop* loop, BasicBlock* exit) +{ + bool hasProfWeight = true; + + // Inherit first estimate from the exit target; optEstimateEdgeLikelihood + // may use it in its estimate if we do not have edge weights to estimate + // from (we also assume the exiting -> exit edges already inherited their + // edge weights from the previous edge). + exit->inheritWeight(exit->GetTarget()); + + weight_t exitWeight = BB_ZERO_WEIGHT; + for (FlowEdge* exitEdge : exit->PredEdges()) + { + BasicBlock* exiting = exitEdge->getSourceBlock(); + + bool fromProfile = false; + weight_t likelihood = optEstimateEdgeLikelihood(exiting, exit, &fromProfile); + hasProfWeight &= fromProfile; + + weight_t contribution = exiting->bbWeight * likelihood; + JITDUMP(" Estimated likelihood " FMT_BB " -> " FMT_BB " to be " FMT_WT " (contribution: " FMT_WT ")\n", + exiting->bbNum, exit->bbNum, likelihood, contribution); + + exitWeight += contribution; + + // Normalize exiting -> new exit weight + exitEdge->setEdgeWeights(contribution, contribution, exit); + } + + exit->RemoveFlags(BBF_PROF_WEIGHT | BBF_RUN_RARELY); + + exit->bbWeight = exitWeight; + if (hasProfWeight) + { + exit->SetFlags(BBF_PROF_WEIGHT); + } + + if (exitWeight == BB_ZERO_WEIGHT) + { + exit->SetFlags(BBF_RUN_RARELY); + return; + } + + // Normalize new exit -> old exit weight + FlowEdge* const edgeFromNewExit = fgGetPredForBlock(exit->GetTarget(), exit); + assert(edgeFromNewExit != nullptr); + edgeFromNewExit->setEdgeWeights(exit->bbWeight, exit->bbWeight, exit->GetTarget()); +} + /***************************************************************************** * * See if the given tree can be computed in the given precision (which must @@ -5083,43 +5372,39 @@ bool Compiler::optVNIsLoopInvariant(ValueNum vn, FlowGraphNaturalLoop* loop, VNS } //------------------------------------------------------------------------------ -// fgSetEHRegionForNewPreheader: Set the EH region for a newly inserted -// preheader. -// -// In which EH region should the header live? +// fgSetEHRegionForNewPreheaderOrExit: Set the EH region for a newly inserted +// preheader or exit block. // -// The preheader block is expected to have been added immediately before a -// block `next` in the loop that is also in the same EH region as the header. -// This is usually the lexically first block of the loop, but may also be the -// header itself. +// In which EH region should the block live? // -// If the `next` block is NOT the first block of a `try` region, the preheader -// can simply extend the header block's EH region. +// If the `next` block is NOT the first block of a `try` region, the new block +// can simply extend the next block's EH region. // // If the `next` block IS the first block of a `try`, we find its parent region // and use that. For mutual-protect regions, we need to find the actual parent, // as the block stores the most "nested" mutual region. For non-mutual-protect // regions, due to EH canonicalization, we are guaranteed that no other EH // regions begin on the same block, so looking to just the parent is -// sufficient. Note that we can't just extend the EH region of the header to -// the preheader, because the header will still be the target of backward -// branches from within the loop. If those backward branches come from outside -// the `try` (say, only the top half of the loop is a `try` region), then we -// can't branch to a non-first `try` region block (you always must enter the -// `try` in the first block). +// sufficient. +// Note that we can't just extend the EH region of the next block to the new +// block, because it may still be the target of other branches. If those +// branches come from outside the `try` then we can't branch to a non-first +// `try` region block (you always must enter the `try` in the first block). For +// example, for the preheader we can have backedges that come from outside the +// `try` (if, say, only the top half of the loop is a `try` region). For exits, +// we could similarly have branches to the old exit block from outside the `try`. // // Note that hoisting any code out of a try region, for example, to a preheader // block in a different EH region, needs to ensure that no exceptions will be -// thrown. +// thrown. Similar considerations are required for exits. // // Arguments: -// preheader - the new preheader block, which has already been added to the -// block list before a block inside the loop that shares EH -// region with the header. +// block - the new block, which has already been added to the +// block list. // -void Compiler::fgSetEHRegionForNewPreheader(BasicBlock* preheader) +void Compiler::fgSetEHRegionForNewPreheaderOrExit(BasicBlock* block) { - BasicBlock* next = preheader->Next(); + BasicBlock* next = block->Next(); if (bbIsTryBeg(next)) { @@ -5129,15 +5414,15 @@ void Compiler::fgSetEHRegionForNewPreheader(BasicBlock* preheader) if (newTryIndex == EHblkDsc::NO_ENCLOSING_INDEX) { // No EH try index. - preheader->clearTryIndex(); + block->clearTryIndex(); } else { - preheader->setTryIndex(newTryIndex); + block->setTryIndex(newTryIndex); } // What handler region to use? Use the same handler region as `next`. - preheader->copyHndIndex(next); + block->copyHndIndex(next); } else { From 99a16f64e22b73136ef06f65e709b88a8773fd49 Mon Sep 17 00:00:00 2001 From: Radek Zikmund <32671551+rzikm@users.noreply.github.com> Date: Wed, 14 Feb 2024 13:10:54 +0100 Subject: [PATCH 022/158] Disable failing OCSP tests tests (#98362) --- .../UnitTests/SslStreamCertificateContextOcspLinuxTests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Net.Security/tests/UnitTests/SslStreamCertificateContextOcspLinuxTests.cs b/src/libraries/System.Net.Security/tests/UnitTests/SslStreamCertificateContextOcspLinuxTests.cs index b712f814a02bbe..5e31aafc5cce4c 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/SslStreamCertificateContextOcspLinuxTests.cs +++ b/src/libraries/System.Net.Security/tests/UnitTests/SslStreamCertificateContextOcspLinuxTests.cs @@ -77,6 +77,7 @@ await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97836")] public async Task FetchOcspResponse_FirstInvalidThenValid() { await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity, ctxFactory, responder) => @@ -94,6 +95,7 @@ await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97779")] public async Task RefreshOcspResponse_BeforeExpiration() { await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity, ctxFactory, responder) => @@ -121,6 +123,7 @@ await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97779")] public async Task RefreshOcspResponse_AfterExpiration() { await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity, ctxFactory, responder) => From 13e63af5df865ec7cffb7a97519f9e4f1db783d2 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 14 Feb 2024 13:58:58 +0100 Subject: [PATCH 023/158] Remove synchronization from Http2Connection.ExtendWindow (#97878) _pendingWindowUpdate is the only state the ExtendWindow method touches under the lock. There is no other code that reads or writes _pendingWindowUpdate after the creation of the connection object. Originating from ProcessIncomingFramesAsync, calls to ExtendWindow are sequential, and never expected to happen concurrently. --- .../SocketsHttpHandler/Http2Connection.cs | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index 46a6aedb6530db..4d84e54748e3ab 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -1771,23 +1771,18 @@ private bool ExtendWindow(int amount) { if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(amount)}={amount}"); Debug.Assert(amount > 0); + Debug.Assert(_pendingWindowUpdate < ConnectionWindowThreshold); - int windowUpdateSize; - lock (SyncObject) + _pendingWindowUpdate += amount; + if (_pendingWindowUpdate < ConnectionWindowThreshold) { - Debug.Assert(_pendingWindowUpdate < ConnectionWindowThreshold); - - _pendingWindowUpdate += amount; - if (_pendingWindowUpdate < ConnectionWindowThreshold) - { - if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(_pendingWindowUpdate)} {_pendingWindowUpdate} < {ConnectionWindowThreshold}."); - return false; - } - - windowUpdateSize = _pendingWindowUpdate; - _pendingWindowUpdate = 0; + if (NetEventSource.Log.IsEnabled()) Trace($"{nameof(_pendingWindowUpdate)} {_pendingWindowUpdate} < {ConnectionWindowThreshold}."); + return false; } + int windowUpdateSize = _pendingWindowUpdate; + _pendingWindowUpdate = 0; + LogExceptions(SendWindowUpdateAsync(0, windowUpdateSize)); return true; } From 9561bedea638f59bd42b9075f04c17a91eb4f72f Mon Sep 17 00:00:00 2001 From: snickolls-arm <151848422+snickolls-arm@users.noreply.github.com> Date: Wed, 14 Feb 2024 13:45:43 +0000 Subject: [PATCH 024/158] Add ARM64 encodings for group IF_SVE_GQ_3A (#98352) * Add ARM64 encodings for group IF_SVE_GQ_2A * Address review comments --- src/coreclr/jit/codegenarm64test.cpp | 12 +++ src/coreclr/jit/emitarm64.cpp | 132 +++++++++++++++++++++++++++ src/coreclr/jit/emitarm64.h | 9 ++ src/coreclr/jit/instrsarm64sve.h | 8 +- 4 files changed, 157 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index dd29bb88b9521e..9e537de5877b2f 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -5035,6 +5035,18 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R_R(INS_sve_nmatch, EA_SCALABLE, REG_P0, REG_P7, REG_V11, REG_V31, INS_OPTS_SCALABLE_H); // NMATCH ., /Z, ., . + // IF_SVE_GQ_3A + theEmitter->emitIns_R_R_R(INS_sve_bfcvtnt, EA_SCALABLE, REG_V3, REG_P0, REG_V4); // BFCVTNT .H, /M, .S + theEmitter->emitIns_R_R_R(INS_sve_fcvtlt, EA_SCALABLE, REG_V0, REG_P7, REG_V1, + INS_OPTS_S_TO_D); // FCVTLT .D, /M, .S + theEmitter->emitIns_R_R_R(INS_sve_fcvtlt, EA_SCALABLE, REG_V14, REG_P7, REG_V20, + INS_OPTS_H_TO_S); // FCVTLT .S, /M, .H + theEmitter->emitIns_R_R_R(INS_sve_fcvtnt, EA_SCALABLE, REG_V18, REG_P3, REG_V9, + INS_OPTS_S_TO_H); // FCVTNT .H, /M, .S + theEmitter->emitIns_R_R_R(INS_sve_fcvtnt, EA_SCALABLE, REG_V12, REG_P3, REG_V5, + INS_OPTS_D_TO_S); // FCVTNT .S, /M, .D + theEmitter->emitIns_R_R_R(INS_sve_fcvtxnt, EA_SCALABLE, REG_V1, REG_P2, REG_V3); // FCVTXNT .S, /M, .D + // IF_SVE_GR_3A theEmitter->emitIns_R_R_R(INS_sve_faddp, EA_SCALABLE, REG_V16, REG_P3, REG_V19, INS_OPTS_SCALABLE_H); // FADDP ., /M, ., . diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index de6af7d816caa7..53923701301757 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1343,6 +1343,25 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isVectorRegister(id->idReg4())); // mmmmm break; + case IF_SVE_GQ_3A: // ................ ...gggnnnnnddddd -- SVE floating-point convert precision odd elements + switch (id->idIns()) + { + case INS_sve_fcvtnt: + case INS_sve_fcvtlt: + assert(insOptsConvertFloatStepwise(id->idInsOpt())); + FALLTHROUGH; + case INS_sve_fcvtxnt: + case INS_sve_bfcvtnt: + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isLowPredicateRegister(id->idReg2())); // ggg + assert(isVectorRegister(id->idReg3())); // nnnnn + break; + default: + assert(!"unreachable"); + break; + } + break; + case IF_SVE_HT_4A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE floating-point compare vectors elemsize = id->idOpSize(); assert(isScalableVectorSize(elemsize)); @@ -6551,6 +6570,53 @@ emitter::code_t emitter::emitInsCodeSve(instruction ins, insFormat fmt) return registerListSize; } +/***************************************************************************** + * + * Expands an option that has different size operands (INS_OPTS_*_TO_*) into + * a pair of scalable options where the first describes the size of the + * destination operand and the second describes the size of the source operand. + */ + +/*static*/ void emitter::optExpandConversionPair(insOpts opt, insOpts& dst, insOpts& src) +{ + dst = INS_OPTS_NONE; + src = INS_OPTS_NONE; + + switch (opt) + { + case INS_OPTS_H_TO_S: + dst = INS_OPTS_SCALABLE_S; + src = INS_OPTS_SCALABLE_H; + break; + case INS_OPTS_S_TO_H: + dst = INS_OPTS_SCALABLE_H; + src = INS_OPTS_SCALABLE_S; + break; + case INS_OPTS_S_TO_D: + dst = INS_OPTS_SCALABLE_D; + src = INS_OPTS_SCALABLE_S; + break; + case INS_OPTS_D_TO_S: + dst = INS_OPTS_SCALABLE_S; + src = INS_OPTS_SCALABLE_D; + break; + case INS_OPTS_H_TO_D: + dst = INS_OPTS_SCALABLE_D; + src = INS_OPTS_SCALABLE_H; + break; + case INS_OPTS_D_TO_H: + dst = INS_OPTS_SCALABLE_H; + src = INS_OPTS_SCALABLE_D; + break; + default: + noway_assert(!"unreachable"); + break; + } + + assert(dst != INS_OPTS_NONE && src != INS_OPTS_NONE); + return; +} + // For the given 'arrangement' returns the 'datasize' specified by the vector register arrangement // asserts and returns EA_UNKNOWN if an invalid 'arrangement' value is passed // @@ -10890,6 +10956,18 @@ void emitter::emitIns_R_R_R(instruction ins, fmt = IF_SVE_EU_3A; break; + case INS_sve_fcvtnt: + case INS_sve_fcvtlt: + assert(insOptsConvertFloatStepwise(opt)); + FALLTHROUGH; + case INS_sve_fcvtxnt: + case INS_sve_bfcvtnt: + assert(isVectorRegister(reg1)); // ddddd + assert(isLowPredicateRegister(reg2)); // ggg + assert(isVectorRegister(reg3)); // nnnnn + fmt = IF_SVE_GQ_3A; + break; + case INS_sve_faddp: case INS_sve_fmaxnmp: case INS_sve_fmaxp: @@ -21284,6 +21362,24 @@ BYTE* emitter::emitOutput_InstrSve(BYTE* dst, instrDesc* id) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_GQ_3A: // ................ ...gggnnnnnddddd -- SVE floating-point convert precision odd elements + code = emitInsCodeSve(ins, fmt); + + if (ins == INS_sve_fcvtnt && id->idInsOpt() == INS_OPTS_D_TO_S) + { + code |= (1 << 22 | 1 << 17); + } + else if (ins == INS_sve_fcvtlt && id->idInsOpt() == INS_OPTS_S_TO_D) + { + code |= (1 << 22 | 1 << 17); + } + + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_V_9_to_5(id->idReg3()); // nnnnn + dst += emitOutput_Instr(dst, code); + break; + // Scalable to general register. case IF_SVE_CO_3A: // ........xx...... ...gggmmmmmddddd -- SVE conditionally extract element to general register case IF_SVE_CS_3A: // ........xx...... ...gggnnnnnddddd -- SVE extract element to general register @@ -25287,6 +25383,37 @@ void emitter::emitDispInsHelp( break; } + // .H, /M, .S + // .S, /M, .D + // .D, /M, .S + // .S, /M, .H + case IF_SVE_GQ_3A: // ................ ...gggnnnnnddddd -- SVE floating-point convert precision odd elements + { + insOpts opt = id->idInsOpt(); + + switch (ins) + { + // These cases have only one combination of operands so the option may be omitted. + case INS_sve_fcvtxnt: + opt = INS_OPTS_D_TO_S; + break; + case INS_sve_bfcvtnt: + opt = INS_OPTS_S_TO_H; + break; + default: + break; + } + + insOpts dst = INS_OPTS_NONE; + insOpts src = INS_OPTS_NONE; + optExpandConversionPair(opt, dst, src); + + emitDispSveReg(id->idReg1(), dst, true); // ddddd + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); // ggg + emitDispSveReg(id->idReg3(), src, false); // nnnnn + 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 @@ -28396,6 +28523,11 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insThroughput = PERFSCORE_THROUGHPUT_1C; break; + case IF_SVE_GQ_3A: // ................ ...gggnnnnnddddd -- SVE floating-point convert precision odd elements + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_3C; + break; + // Floating point arithmetic // Floating point min/max pairwise case IF_SVE_GR_3A: // ........xx...... ...gggmmmmmddddd -- SVE2 floating-point pairwise operations diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index a15e2c453efbd5..e12e405b8e21bc 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -848,6 +848,10 @@ static insOpts optMakeArrangement(emitAttr datasize, emitAttr elemsize); // For the given 'datasize' and 'opt' returns true if it specifies a valid vector register arrangement static bool isValidArrangement(emitAttr datasize, insOpts opt); +// Expands an option that has different size operands (INS_OPTS_*_TO_*) into a pair of scalable options where +// the first describes the size of the destination operand and the second describes the size of the source operand. +static void optExpandConversionPair(insOpts opt, insOpts& dst, insOpts& src); + // For the given 'arrangement' returns the 'datasize' specified by the vector register arrangement static emitAttr optGetDatasize(insOpts arrangement); @@ -1156,6 +1160,11 @@ inline static bool insOptsAnyArrangement(insOpts opt) return ((opt >= INS_OPTS_8B) && (opt <= INS_OPTS_2D)); } +inline static bool insOptsConvertFloatStepwise(insOpts opt) +{ + return (opt == INS_OPTS_H_TO_S || opt == INS_OPTS_S_TO_H || opt == INS_OPTS_D_TO_S || opt == INS_OPTS_S_TO_D); +} + inline static bool insOptsConvertFloatToFloat(insOpts opt) { return ((opt >= INS_OPTS_S_TO_D) && (opt <= INS_OPTS_D_TO_H)); diff --git a/src/coreclr/jit/instrsarm64sve.h b/src/coreclr/jit/instrsarm64sve.h index 710928a35eaffe..7350a288c92f5f 100644 --- a/src/coreclr/jit/instrsarm64sve.h +++ b/src/coreclr/jit/instrsarm64sve.h @@ -1312,8 +1312,8 @@ INST2(pmullt, "pmullt", 0, IF_SV // enum name info SVE_GQ_3A SVE_HG_2A -INST2(fcvtnt, "fcvtnt", 0, IF_SVE_2BJ, 0x64CAA000, 0x650A3C00 ) - // FCVTNT .S, /M, .D SVE_GQ_3A 0110010011001010 101gggnnnnnddddd 64CA A000 +INST2(fcvtnt, "fcvtnt", 0, IF_SVE_2BJ, 0x6488A000, 0x650A3C00 ) + // FCVTNT .H, /M, .S SVE_GQ_3A 0110010010001000 101gggnnnnnddddd 6488 A000 // FCVTNT .B, {.S-.S } SVE_HG_2A 0110010100001010 001111nnnn0ddddd 650A 3C00 @@ -2669,8 +2669,8 @@ INST1(histcnt, "histcnt", 0, IF_SV INST1(bfcvtnt, "bfcvtnt", 0, IF_SVE_GQ_3A, 0x648AA000 ) // BFCVTNT .H, /M, .S SVE_GQ_3A 0110010010001010 101gggnnnnnddddd 648A A000 -INST1(fcvtlt, "fcvtlt", 0, IF_SVE_GQ_3A, 0x64CBA000 ) - // FCVTLT .D, /M, .S SVE_GQ_3A 0110010011001011 101gggnnnnnddddd 64CB A000 +INST1(fcvtlt, "fcvtlt", 0, IF_SVE_GQ_3A, 0x6489A000 ) + // FCVTLT .S, /M, .H SVE_GQ_3A 0110010010001001 101gggnnnnnddddd 6489 A000 INST1(fcvtxnt, "fcvtxnt", 0, IF_SVE_GQ_3A, 0x640AA000 ) // FCVTXNT .S, /M, .D SVE_GQ_3A 0110010000001010 101gggnnnnnddddd 640A A000 From 35030ef4f71bbb48217a977bc238a39bbd64a4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 14 Feb 2024 22:59:31 +0900 Subject: [PATCH 025/158] Fix stepping into assembly thunks (#98401) Stepping into assembly thunks didn't work with the new object writer. This fixes it. For following program, trying to step into `ToString` (in an unoptimized version of this code) was resulting into a step over. The issue is that the `ToString` call in unoptimized code looks like this: ``` 00007FF71B8A27EA lea rax,[__VirtualCall_Object__ToString (07FF71B591007h)] 00007FF71B8A27F1 mov qword ptr [rbp-18h],rax 00007FF71B8A27F5 mov rcx,qword ptr [p] 00007FF71B8A27F9 mov rax,qword ptr [rbp-18h] 00007FF71B8A27FD cmp dword ptr [rcx],ecx 00007FF71B8A27FF call rax ``` I.e. we call a `__VirtualCall_Object__ToString` assembly helper that does the actual call. The helper looks like this: ``` 00007FF71B591007 mov rax,qword ptr [rcx] 00007FF71B59100A jmp qword ptr [rax+18h] ``` Without debug information, the debugger was simply throwing up hands and stepping over this. We do however generate debug information using the magic F00F00 and FEEFEE line numbers. These line numbers instruct the debugger to ignore the helper and step into the thing the helper calls. The new object writer was dropping this debug info on the floor. --- .../Compiler/ObjectWriter/CoffObjectWriter.cs | 29 +++++++++++++++++++ .../Compiler/ObjectWriter/ObjectWriter.cs | 23 +++++++++++---- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs index 304c9f6b5cca9f..3198fafaf5d22d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs @@ -628,6 +628,35 @@ private protected override void EmitDebugFunctionInfo( } } + private protected override void EmitDebugThunkInfo( + string methodName, + SymbolDefinition methodSymbol, + INodeWithDebugInfo debugNode) + { + if (!debugNode.GetNativeSequencePoints().Any()) + return; + + CodeViewSymbolsBuilder debugSymbolsBuilder; + + if (ShouldShareSymbol((ObjectNode)debugNode)) + { + // If the method is emitted in COMDAT section then we need to create an + // associated COMDAT section for the debugging symbols. + var sectionWriter = GetOrCreateSection(DebugSymbolSection, methodName, null); + debugSymbolsBuilder = new CodeViewSymbolsBuilder(_nodeFactory.Target.Architecture, sectionWriter); + } + else + { + debugSymbolsBuilder = _debugSymbolsBuilder; + } + + debugSymbolsBuilder.EmitLineInfo( + _debugFileTableBuilder, + methodName, + methodSymbol.Size, + debugNode.GetNativeSequencePoints()); + } + private protected override void EmitDebugSections(IDictionary definedSymbols) { _debugSymbolsBuilder.WriteUserDefinedTypes(_debugTypesBuilder.UserDefinedTypes); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs index 0de7f8214e5095..eef554bdf15e3e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs @@ -338,6 +338,13 @@ private protected abstract void EmitDebugFunctionInfo( INodeWithDebugInfo debugNode, bool hasSequencePoints); + private protected virtual void EmitDebugThunkInfo( + string methodName, + SymbolDefinition methodSymbol, + INodeWithDebugInfo debugNode) + { + } + private protected abstract void EmitDebugSections(IDictionary definedSymbols); private void EmitObject(string objectFilePath, IReadOnlyCollection nodes, IObjectDumper dumper, Logger logger) @@ -490,15 +497,21 @@ private void EmitObject(string objectFilePath, IReadOnlyCollection Date: Wed, 14 Feb 2024 23:22:47 +0900 Subject: [PATCH 026/158] Add linux-musl-arm to ILCompilerRIDs (#98412) linux-arm was already present. --- .../projects/Microsoft.DotNet.ILCompiler/ILCompilerRIDs.props | 1 + 1 file changed, 1 insertion(+) diff --git a/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/ILCompilerRIDs.props b/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/ILCompilerRIDs.props index 1d51a78880ae2d..ffec11de57b317 100644 --- a/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/ILCompilerRIDs.props +++ b/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/ILCompilerRIDs.props @@ -2,6 +2,7 @@ + From b51e35261f0ea6f86b475df7f81976cb14f61f5a Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 14 Feb 2024 15:39:38 +0100 Subject: [PATCH 027/158] Throw correct overflow exception in IDiv helper (#98416) --- .../Internal/Runtime/CompilerHelpers/MathHelpers.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs index a471c3410abfc2..d7195891320f00 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs @@ -295,7 +295,7 @@ public static int IDiv(int i, int j) if (j == 0) return ThrowIntDivByZero(); else if (j == -1 && i == int.MinValue) - return ThrowIntArithExc(); + return ThrowIntOvf(); else return RhpIDiv(i, j); } @@ -320,6 +320,8 @@ public static int IMod(int i, int j) { if (j == 0) return ThrowIntDivByZero(); + else if (j == -1 && i == int.MinValue) + return ThrowIntOvf(); else return RhpIMod(i, j); } @@ -378,12 +380,6 @@ private static uint ThrowUIntDivByZero() { throw new DivideByZeroException(); } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static int ThrowIntArithExc() - { - throw new ArithmeticException(); - } #endif // TARGET_ARM } } From c4a81c49fe60430317afb309a15cf003411fc5dc Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 14 Feb 2024 09:42:04 -0500 Subject: [PATCH 028/158] Restore RSA 16384 tests to innerloop --- .../RSA/ImportExport.cs | 3 +- .../RSA/RSAKeyFileTests.cs | 42 ++----------------- .../AlgorithmImplementations/RSA/RSAXml.cs | 30 ++----------- 3 files changed, 8 insertions(+), 67 deletions(-) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs index 473e54797f16ce..72ffbef169263a 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs @@ -10,8 +10,7 @@ namespace System.Security.Cryptography.Rsa.Tests [SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")] public partial class ImportExport { - private static readonly Lazy s_supports16384 = new Lazy(TestRsa16384); - public static bool Supports16384 => s_supports16384.Value; + public static bool Supports16384 { get; } = TestRsa16384(); [Fact] public static void ExportAutoKey() diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs index 65f73cdef43519..daa175dda47a93 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs @@ -3,7 +3,6 @@ using System.Security.Cryptography.Encryption.RC2.Tests; using System.Text; -using Microsoft.DotNet.XUnitExtensions; using Test.Cryptography; using Xunit; @@ -123,17 +122,9 @@ public static void ReadWriteDiminishedDPPrivatePkcs1() TestData.DiminishedDPParameters); } - [ConditionalFact] - [OuterLoop("RSA 16384 takes considerable time.")] + [ConditionalFact(typeof(ImportExport), nameof(ImportExport.Supports16384))] public static void ReadWritePublicPkcs1() { - // Do not move this to the [ConditionalFact], otherwise the platform will check if RSA 16384 is supported - // during test discovery for innerloop, and the check itself is expensive. - if (!ImportExport.Supports16384) - { - throw new SkipTestException("Platform does not support RSA 16384."); - } - ReadWriteBase64PublicPkcs1( @" MIIICgKCCAEAmyxwX6kQNx+LSMao1StC1p5rKCEwcBjzI136An3B/BjthgezAOuu @@ -207,18 +198,9 @@ public static void ReadWriteSubjectPublicKeyInfo_DiminishedDPKey() TestData.DiminishedDPParameters); } - - [ConditionalFact] - [OuterLoop("RSA 16384 takes considerable time.")] + [ConditionalFact(typeof(ImportExport), nameof(ImportExport.Supports16384))] public static void ReadWriteRsa16384SubjectPublicKeyInfo() { - // Do not move this to the [ConditionalFact], otherwise the platform will check if RSA 16384 is supported - // during test discovery for innerloop, and the check itself is expensive. - if (!ImportExport.Supports16384) - { - throw new SkipTestException("Platform does not support RSA 16384."); - } - ReadWriteBase64SubjectPublicKeyInfo( @" MIIIIjANBgkqhkiG9w0BAQEFAAOCCA8AMIIICgKCCAEAmyxwX6kQNx+LSMao1StC @@ -268,17 +250,9 @@ public static void ReadWriteRsa16384SubjectPublicKeyInfo() TestData.RSA16384Params); } - [ConditionalFact] - [OuterLoop("RSA 16384 takes considerable time.")] + [ConditionalFact(typeof(ImportExport), nameof(ImportExport.Supports16384))] public static void ReadWrite16384Pkcs8() { - // Do not move this to the [ConditionalFact], otherwise the platform will check if RSA 16384 is supported - // during test discovery for innerloop, and the check itself is expensive. - if (!ImportExport.Supports16384) - { - throw new SkipTestException("Platform does not support RSA 16384"); - } - ReadWriteBase64Pkcs8( @" MIIkQgIBADANBgkqhkiG9w0BAQEFAASCJCwwgiQoAgEAAoIIAQCbLHBfqRA3H4tI @@ -551,17 +525,9 @@ public static void ReadEncryptedRsa1032() TestData.RSA1032Parameters); } - [ConditionalFact] - [OuterLoop("RSA 16384 takes considerable time.")] + [ConditionalFact(typeof(ImportExport), nameof(ImportExport.Supports16384))] public static void ReadEncryptedRsa16384() { - // Do not move this to the [ConditionalFact], otherwise the platform will check if RSA 16384 is supported - // during test discovery for innerloop, and the check itself is expensive. - if (!ImportExport.Supports16384) - { - throw new SkipTestException("Platform does not support RSA 16384"); - } - // PBES2: PBKDF2 + des (single DES, not 3DES). const string base64 = @" MIIkizA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQI63upT8JPNNcCAggA diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs index eb354216e77fca..666f9bea3c4095 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Xml.Linq; -using Microsoft.DotNet.XUnitExtensions; using Xunit; namespace System.Security.Cryptography.Rsa.Tests @@ -77,17 +76,9 @@ public static void TestRead1032Parameters_Private() TestData.RSA1032Parameters); } - [ConditionalFact] - [OuterLoop("RSA 16384 takes considerable time.")] + [ConditionalFact(typeof(ImportExport), nameof(ImportExport.Supports16384))] public static void TestRead16384Parameters_Public() { - // Do not move this to the [ConditionalFact], otherwise the platform will check if RSA 16384 is supported - // during test discovery for innerloop, and the check itself is expensive. - if (!ImportExport.Supports16384) - { - throw new SkipTestException("Platform does not support RSA 16384"); - } - RSAParameters expectedParameters = ImportExport.MakePublic(TestData.RSA16384Params); // Bonus trait of this XML: the Modulus and Exponent parameters @@ -166,16 +157,9 @@ iC2wXFMDafnWp1lxXiGcVVu9dE2LeglCgnMUps9QlJD0aXaJHYi2VDQ3zFdMvn8A imlqKtZGdGf9 expectedParameters); } - [ConditionalFact] + [ConditionalFact(typeof(ImportExport), nameof(ImportExport.Supports16384))] public static void TestRead16384Parameters_Private() { - // Do not move this to the [ConditionalFact], otherwise the platform will check if RSA 16384 is supported - // during test discovery for innerloop, and the check itself is expensive. - if (!ImportExport.Supports16384) - { - throw new SkipTestException("Platform does not support RSA 16384"); - } - // Bonus trait of this XML: the D parameter is not in // canonical order. TestReadXml( @@ -650,19 +634,11 @@ public static void TestWrite2048Parameters(bool includePrivateParameters) )); } - [ConditionalTheory] + [ConditionalTheory(typeof(ImportExport), nameof(ImportExport.Supports16384))] [InlineData(true)] [InlineData(false)] - [OuterLoop("RSA 16384 takes considerable time for primality tests.")] public static void TestWrite16384Parameters(bool includePrivateParameters) { - // Do not move this to the [ConditionalFact], otherwise the platform will check if RSA 16384 is supported - // during test discovery for innerloop, and the check itself is expensive. - if (!ImportExport.Supports16384) - { - throw new SkipTestException("Platform does not support RSA 16384"); - } - TestWriteXml( TestData.RSA16384Params, includePrivateParameters, From 9aaac42bcdfb36225d9d6eb8c45e15bbbf816d11 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 14 Feb 2024 15:54:38 +0100 Subject: [PATCH 029/158] JIT: Use FlowEdge destination block in a few loop related cases (#98403) --- src/coreclr/jit/compiler.hpp | 17 ++++------------- src/coreclr/jit/flowgraph.cpp | 12 +++--------- src/coreclr/jit/optimizer.cpp | 6 ++++-- 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 1fc695d8a3f67b..59e53baeb19f8a 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -5016,19 +5016,10 @@ BasicBlockVisit FlowGraphNaturalLoop::VisitRegularExitBlocks(TFunc func) for (FlowEdge* edge : ExitEdges()) { - BasicBlockVisit result = edge->getSourceBlock()->VisitRegularSuccs(comp, [&](BasicBlock* succ) { - assert(m_dfsTree->Contains(succ)); - if (!comp->bbIsHandlerBeg(succ) && !ContainsBlock(succ) && - BitVecOps::TryAddElemD(&traits, visited, succ->bbPostorderNum) && - (func(succ) == BasicBlockVisit::Abort)) - { - return BasicBlockVisit::Abort; - } - - return BasicBlockVisit::Continue; - }); - - if (result == BasicBlockVisit::Abort) + BasicBlock* exit = edge->getDestinationBlock(); + assert(m_dfsTree->Contains(exit) && !ContainsBlock(exit)); + if (!comp->bbIsHandlerBeg(exit) && BitVecOps::TryAddElemD(&traits, visited, exit->bbPostorderNum) && + (func(exit) == BasicBlockVisit::Abort)) { return BasicBlockVisit::Abort; } diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 2291139538e128..85860f494f8139 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -4710,14 +4710,8 @@ void FlowGraphNaturalLoop::Dump(FlowGraphNaturalLoop* loop) for (FlowEdge* const edge : loop->ExitEdges()) { BasicBlock* const exitingBlock = edge->getSourceBlock(); - printf("%s" FMT_BB " ->", first ? "" : "; ", exitingBlock->bbNum); - exitingBlock->VisitRegularSuccs(comp, [=](BasicBlock* succ) { - if (comp->fgGetPredForBlock(succ, exitingBlock) == edge) - { - printf(" " FMT_BB, succ->bbNum); - } - return BasicBlockVisit::Continue; - }); + BasicBlock* const exitBlock = edge->getDestinationBlock(); + printf("%s" FMT_BB " -> " FMT_BB, first ? "" : "; ", exitingBlock->bbNum, exitBlock->bbNum); first = false; } } @@ -4993,7 +4987,7 @@ bool FlowGraphNaturalLoop::AnalyzeIteration(NaturalLoopIterInfo* info) info->TestBlock = cond; info->IterVar = iterVar; info->IterTree = iterTree; - info->ExitedOnTrue = !ContainsBlock(cond->GetTrueTarget()); + info->ExitedOnTrue = exitEdge->getDestinationBlock() == cond->GetTrueTarget(); break; } diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 37d66eef6c660b..b54d224f227da2 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -3146,8 +3146,10 @@ bool Compiler::optCanonicalizeExits(FlowGraphNaturalLoop* loop) // Find all blocks outside the loop from this exiting block. Those // blocks are exits. Note that we may see preheaders created by // previous canonicalization here, which are not part of the DFS tree - // or properly maintained in a parent loop. The canonicalization here - // works despite this. + // or properly maintained in a parent loop. This also means the + // destination block of the exit edge may no longer be right, so we + // cannot use VisitRegularExitBlocks. The canonicalization here works + // despite this. edge->getSourceBlock()->VisitRegularSuccs(this, [=, &changed](BasicBlock* succ) { if (!loop->ContainsBlock(succ)) { From 7e981ea46acd5fa9c9148ad4c601793c51ba0e98 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 14 Feb 2024 17:08:38 +0100 Subject: [PATCH 030/158] Fix remaining issues before enabling new EH by default (#98322) * Fix remaining issues before enabling new EH by default * Nonvolatile floating point registers unwinding during EH unwind * Long standing bug in arm context capturing - wrong bit for floating point context flag * Bug in thumb address adjustment in stack trace generation (it was adjusted by -1, which effectively just removed the thumb bit) * Bug in detecting of hasFaulted flag that was left set even if we've adjusted the context from a native helper to the managed caller (e.g. for GC write barrier, etc) * Bug in RuntimeWrappedException handling where the wrapped exception was also considered when only the unwrapped one should be looked for. * Bug in the values of the InlinedCallFrameMarker bits for arm32. There are only two available bits in the MethodDesc pointer and it was using the same bits as 64 bitplatforms that use bits 1 and 2 since bit 0 is used for other purpose on those. * Change in how unhandled exception is processed on Windows. We were previously raising the failfast exception in the second pass when we have detected that there are no more managed frames, but that prevented 3rd party hosts to get a chance to handle the exception. The new way is to raise SEH exception instead and change the personality routine for managed frames to do nothing for this case. The exception then flows either to a handler in the host or is detected as unhandled exception by the OS and triggers unhandled exception callback. * Reflect PR feedback Make the UpdateFloatingPointRegisters static and remove the sp argument --- src/coreclr/debug/ee/debugger.inl | 2 +- .../src/System/Runtime/ExceptionHandling.cs | 26 ++-- src/coreclr/pal/src/arch/arm/context2.S | 5 +- src/coreclr/vm/amd64/cgenamd64.cpp | 35 ++++- src/coreclr/vm/arm/asmhelpers.S | 3 +- src/coreclr/vm/arm/stubs.cpp | 35 ++++- src/coreclr/vm/arm64/stubs.cpp | 35 ++++- src/coreclr/vm/debugdebugger.cpp | 2 +- src/coreclr/vm/excep.cpp | 9 +- src/coreclr/vm/exceptionhandling.cpp | 126 +++++++++++------- src/coreclr/vm/exceptionhandling.h | 6 + src/coreclr/vm/exinfo.cpp | 16 ++- src/coreclr/vm/frames.cpp | 16 +++ src/coreclr/vm/frames.h | 41 +++--- src/coreclr/vm/i386/cgenx86.cpp | 22 +-- src/coreclr/vm/jithelpers.cpp | 5 +- src/coreclr/vm/loongarch64/stubs.cpp | 35 ++++- src/coreclr/vm/riscv64/stubs.cpp | 35 ++++- src/coreclr/vm/stackwalk.cpp | 13 +- src/coreclr/vm/threads.h | 2 + src/coreclr/vm/threadsuspend.cpp | 3 +- 21 files changed, 336 insertions(+), 136 deletions(-) diff --git a/src/coreclr/debug/ee/debugger.inl b/src/coreclr/debug/ee/debugger.inl index 61b44c9466e584..8b7a973f48efff 100644 --- a/src/coreclr/debug/ee/debugger.inl +++ b/src/coreclr/debug/ee/debugger.inl @@ -213,7 +213,7 @@ inline TADDR FuncEvalFrame::GetReturnAddressPtr() // // This updates the register display for a FuncEvalFrame. // -inline void FuncEvalFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +inline void FuncEvalFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { SUPPORTS_DAC; DebuggerEval * pDE = GetDebuggerEval(); diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs index c29e68d1c050a8..c8d9a0a74c2645 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -1084,27 +1084,21 @@ private static bool ShouldTypedClauseCatchThisException(object exception, Method return TypeCast.IsInstanceOfException(pClauseType, exception); #else - bool retry = false; - do + if (tryUnwrapException && exception is RuntimeWrappedException ex) { - MethodTable* mt = RuntimeHelpers.GetMethodTable(exception); - while (mt != null) - { - if (pClauseType == mt) - { - return true; - } - - mt = mt->ParentMethodTable; - } + exception = ex.WrappedException; + } - if (tryUnwrapException && exception is RuntimeWrappedException ex) + MethodTable* mt = RuntimeHelpers.GetMethodTable(exception); + while (mt != null) + { + if (pClauseType == mt) { - exception = ex.WrappedException; - retry = true; + return true; } + + mt = mt->ParentMethodTable; } - while (retry); return false; #endif diff --git a/src/coreclr/pal/src/arch/arm/context2.S b/src/coreclr/pal/src/arch/arm/context2.S index 32983c196969fb..e292ca26fe2adc 100644 --- a/src/coreclr/pal/src/arch/arm/context2.S +++ b/src/coreclr/pal/src/arch/arm/context2.S @@ -18,9 +18,8 @@ #define CONTEXT_CONTROL 1 // Sp, Lr, Pc, Cpsr #define CONTEXT_INTEGER 2 // R0-R12 -#define CONTEXT_SEGMENTS 4 // -#define CONTEXT_FLOATING_POINT 8 -#define CONTEXT_DEBUG_REGISTERS 16 // +#define CONTEXT_FLOATING_POINT 4 +#define CONTEXT_DEBUG_REGISTERS 8 // #define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) diff --git a/src/coreclr/vm/amd64/cgenamd64.cpp b/src/coreclr/vm/amd64/cgenamd64.cpp index 26c22810260668..261ecec5c46d8a 100644 --- a/src/coreclr/vm/amd64/cgenamd64.cpp +++ b/src/coreclr/vm/amd64/cgenamd64.cpp @@ -58,10 +58,18 @@ void ClearRegDisplayArgumentAndScratchRegisters(REGDISPLAY * pRD) pContextPointers->R11 = NULL; } -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_CONTRACT; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + _ASSERTE(pRD->pCurrentContext->Rip == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -76,7 +84,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(rip:%p, rsp:%p)\n", pRD->ControlPC, pRD->SP)); } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -97,6 +105,13 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) return; } +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -117,7 +132,7 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK InlinedCallFrame::UpdateRegDisplay(rip:%p, rsp:%p)\n", pRD->ControlPC, pRD->SP)); } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -129,6 +144,14 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + _ASSERTE(pRD->pCurrentContext->Rip == m_MachState.m_Rip); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -196,7 +219,7 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) ClearRegDisplayArgumentAndScratchRegisters(pRD); } -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_DAC_CONTRACT; @@ -233,7 +256,7 @@ TADDR ResumableFrame::GetReturnAddressPtr() return dac_cast(m_Regs) + offsetof(CONTEXT, Rip); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -273,7 +296,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } // The HijackFrame has to know the registers that are pushed by OnHijackTripThread -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { NOTHROW; diff --git a/src/coreclr/vm/arm/asmhelpers.S b/src/coreclr/vm/arm/asmhelpers.S index 91e18dc81faf21..27a44b62c119b3 100644 --- a/src/coreclr/vm/arm/asmhelpers.S +++ b/src/coreclr/vm/arm/asmhelpers.S @@ -127,7 +127,8 @@ LOCAL_LABEL(LReturnDone): EPILOG_STACK_RESTORE_OFFSET r7, #8 EPILOG_POP "{r4,r5,r7,pc}" -PATCH_LABEL CallDescrWorkerInternalReturnAddressOffset +CallDescrWorkerInternalReturnAddressOffset: + .global CallDescrWorkerInternalReturnAddressOffset .word LOCAL_LABEL(CallDescrWorkerInternalReturnAddress) - C_FUNC(CallDescrWorkerInternal) NESTED_END CallDescrWorkerInternal,_TEXT diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index 771d2440967022..d72c32201700e2 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -629,7 +629,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, unwoundstate->_isValid = true; } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -640,6 +640,14 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -1500,8 +1508,16 @@ void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegis pRD->pCurrentContextPointers->Lr = NULL; } -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -1529,7 +1545,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(rip:%p, rsp:%p)\n", pRD->ControlPC, pRD->SP)); } -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_DAC_CONTRACT; @@ -1555,7 +1571,7 @@ void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -1581,6 +1597,13 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) return; } +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + } +#endif // DACCESS_COMPILE + // reset pContext; it's only valid for active (top-most) frame pRD->pContext = NULL; @@ -1613,7 +1636,7 @@ TADDR ResumableFrame::GetReturnAddressPtr(void) return dac_cast(m_Regs) + offsetof(T_CONTEXT, Pc); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -1649,7 +1672,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. } -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { NOTHROW; diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index cc5dbf7d66b8d5..871a115097b602 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -426,7 +426,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, unwoundstate->_isValid = TRUE; } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -437,6 +437,14 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -601,8 +609,16 @@ void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegis } -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -626,7 +642,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_DAC_CONTRACT; @@ -659,7 +675,7 @@ void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK FaultingExceptionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -680,6 +696,13 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) return; } +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; @@ -722,7 +745,7 @@ TADDR ResumableFrame::GetReturnAddressPtr(void) return dac_cast(m_Regs) + offsetof(T_CONTEXT, Pc); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -762,7 +785,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) RETURN; } -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/debugdebugger.cpp b/src/coreclr/vm/debugdebugger.cpp index fc6e7a5019e725..bb5bed368de9fe 100644 --- a/src/coreclr/vm/debugdebugger.cpp +++ b/src/coreclr/vm/debugdebugger.cpp @@ -1245,7 +1245,7 @@ void DebugStackTrace::DebugStackTraceElement::InitPass2() bRes = g_pDebugInterface->GetILOffsetFromNative( pFunc, (LPCBYTE)this->ip, - fAdjustOffset ? this->dwOffset - 1 : this->dwOffset, + fAdjustOffset ? this->dwOffset - STACKWALK_CONTROLPC_ADJUST_OFFSET : this->dwOffset, &this->dwILOffset); } diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 245909f7a72f70..e723e087d57f4c 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -3383,7 +3383,7 @@ BOOL StackTraceInfo::AppendElement(BOOL bAllowAllocMem, UINT_PTR currentIP, UINT } else if (!pCf->HasFaulted() && pStackTraceElem->ip != 0) { - pStackTraceElem->ip -= 1; + pStackTraceElem->ip -= STACKWALK_CONTROLPC_ADJUST_OFFSET; pStackTraceElem->flags |= STEF_IP_ADJUSTED; } @@ -6563,7 +6563,6 @@ void HandleManagedFaultNew(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext #if defined(FEATURE_EH_FUNCLETS) *frame->GetGSCookiePtr() = GetProcessGSCookie(); #endif // FEATURE_EH_FUNCLETS - pContext->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; frame->InitAndLink(pContext); Thread *pThread = GetThread(); @@ -7103,12 +7102,14 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandlerPhase3(PEXCEPTION_POINTERS pExcepti // // On 64-bit, some additional work is required.. #ifdef FEATURE_EH_FUNCLETS + pContext->ContextFlags &= ~CONTEXT_EXCEPTION_ACTIVE; return VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION; #endif // defined(FEATURE_EH_FUNCLETS) } else if (AdjustContextForVirtualStub(pExceptionRecord, pContext)) { #ifdef FEATURE_EH_FUNCLETS + pContext->ContextFlags &= ~CONTEXT_EXCEPTION_ACTIVE; return VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION; #endif } @@ -7445,6 +7446,10 @@ LONG WINAPI CLRVectoredExceptionHandlerShim(PEXCEPTION_POINTERS pExceptionInfo) return EXCEPTION_CONTINUE_SEARCH; } +#ifdef FEATURE_EH_FUNCLETS + pExceptionInfo->ContextRecord->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; +#endif // FEATURE_EH_FUNCLETS + // WARNING // // We must preserve this so that GCStress=4 eh processing doesnt kill last error. diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 4d28ed16a5e71c..8c10895a4f12ea 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -866,6 +866,24 @@ UINT_PTR ExceptionTracker::FinishSecondPass( void CleanUpForSecondPass(Thread* pThread, bool fIsSO, LPVOID MemoryStackFpForFrameChain, LPVOID MemoryStackFp); +static void PopExplicitFrames(Thread *pThread, void *targetSp) +{ + Frame* pFrame = pThread->GetFrame(); + while (pFrame < targetSp) + { + pFrame->ExceptionUnwind(); + pFrame->Pop(pThread); + pFrame = pThread->GetFrame(); + } + + GCFrame* pGCFrame = pThread->GetGCFrame(); + while (pGCFrame && pGCFrame < targetSp) + { + pGCFrame->Pop(); + pGCFrame = pThread->GetGCFrame(); + } +} + EXTERN_C EXCEPTION_DISPOSITION ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord, IN PVOID pEstablisherFrame, @@ -881,6 +899,19 @@ ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord, STATIC_CONTRACT_GC_TRIGGERS; STATIC_CONTRACT_THROWS; + Thread* pThread = GetThread(); + + if (pThread->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException)) + { + if ((pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)) + { + GCX_COOP(); + PopExplicitFrames(pThread, (void*)GetSP(pContextRecord)); + ExInfo::PopExInfos(pThread, (void*)GetSP(pContextRecord)); + } + return ExceptionContinueSearch; + } + #ifndef HOST_UNIX if (!(pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)) { @@ -890,7 +921,6 @@ ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord, EEPOLICY_HANDLE_FATAL_ERROR(pExceptionRecord->ExceptionCode); } - Thread* pThread = GetThread(); ClrUnwindEx(pExceptionRecord, (UINT_PTR)pThread, INVALID_RESUME_ADDRESS, @@ -899,15 +929,8 @@ ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord, else { GCX_COOP(); - FrameWithCookie frameWithCookie; - FaultingExceptionFrame *frame = &frameWithCookie; - #if defined(FEATURE_EH_FUNCLETS) - *frame->GetGSCookiePtr() = GetProcessGSCookie(); - #endif // FEATURE_EH_FUNCLETS - frame->InitAndLink(pContextRecord); - OBJECTREF oref = ExceptionTracker::CreateThrowable(pExceptionRecord, FALSE); - DispatchManagedException(oref); + DispatchManagedException(oref, pContextRecord); } #endif // !HOST_UNIX EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, _T("SEH exception leaked into managed code")); @@ -931,8 +954,7 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, if (g_isNewExceptionHandlingEnabled) { - ProcessCLRExceptionNew(pExceptionRecord, pEstablisherFrame, pContextRecord, pDispatcherContext); - UNREACHABLE(); + return ProcessCLRExceptionNew(pExceptionRecord, pEstablisherFrame, pContextRecord, pDispatcherContext); } // We must preserve this so that GCStress=4 eh processing doesnt kill last error. @@ -3567,6 +3589,7 @@ void ExceptionTracker::PopTrackerIfEscaping( } CONTRACTL_END; + _ASSERTE(!g_isNewExceptionHandlingEnabled); Thread* pThread = GetThread(); ThreadExceptionState* pExState = pThread->GetExceptionState(); ExceptionTracker* pTracker = (ExceptionTracker*)pExState->m_pCurrentTracker; @@ -3615,6 +3638,11 @@ void ExceptionTracker::PopTrackers( } CONTRACTL_END; + if (g_isNewExceptionHandlingEnabled) + { + return; + } + Thread* pThread = GetThreadNULLOk(); ExceptionTracker* pTracker = (pThread ? (ExceptionTracker*)pThread->GetExceptionState()->m_pCurrentTracker : NULL); @@ -4930,9 +4958,13 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar { if (g_isNewExceptionHandlingEnabled) { + if (!isHardwareException) + { + RtlCaptureContext(ex.GetContextRecord()); + } GCX_COOP(); OBJECTREF throwable = ExceptionTracker::CreateThrowable(ex.GetExceptionRecord(), FALSE); - DispatchManagedException(throwable); + DispatchManagedException(throwable, ex.GetContextRecord()); } do @@ -5544,7 +5576,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) #endif // TARGET_UNIX -VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preserveStackTrace) +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pExceptionContext, bool preserveStackTrace) { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_TRIGGERS; @@ -5554,13 +5586,15 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preser _ASSERTE(IsException(throwable->GetMethodTable())); + Thread *pThread = GetThread(); + if (preserveStackTrace) { + pThread->IncPreventAbort(); ExceptionPreserveStackTrace(throwable); + pThread->DecPreventAbort(); } - Thread *pThread = GetThread(); - ULONG_PTR hr = GetHRFromThrowable(throwable); EXCEPTION_RECORD exceptionRecord; @@ -5570,10 +5604,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preser exceptionRecord.NumberParameters = MarkAsThrownByUs(exceptionRecord.ExceptionInformation, hr); exceptionRecord.ExceptionRecord = NULL; - CONTEXT exceptionContext; - RtlCaptureContext(&exceptionContext); - - ExInfo exInfo(pThread, &exceptionRecord, &exceptionContext, ExKind::Throw); + ExInfo exInfo(pThread, &exceptionRecord, pExceptionContext, ExKind::Throw); if (pThread->IsAbortInitiated () && IsExceptionOfType(kThreadAbortException,&throwable)) { @@ -5605,6 +5636,19 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preser UNREACHABLE(); } +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preserveStackTrace) +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_COOPERATIVE; + + CONTEXT exceptionContext; + RtlCaptureContext(&exceptionContext); + + DispatchManagedException(throwable, &exceptionContext, preserveStackTrace); + UNREACHABLE(); +} + VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind) { STATIC_CONTRACT_THROWS; @@ -6076,7 +6120,7 @@ void CleanUpForSecondPass(Thread* pThread, bool fIsSO, LPVOID MemoryStackFpForFr // Instead, we rely on the END_SO_TOLERANT_CODE macro to call ClearExceptionStateAfterSO(). Of course, // we may leak in the UMThunkStubCommon() case where we don't have this macro lower on the stack // (stack grows up). - if (!fIsSO) + if (!fIsSO && !g_isNewExceptionHandlingEnabled) { ExceptionTracker::PopTrackerIfEscaping(MemoryStackFp); } @@ -7519,24 +7563,6 @@ extern "C" void QCALLTYPE AppendExceptionStackFrame(QCall::ObjectHandleOnStack e END_QCALL; } -static void PopExplicitFrames(Thread *pThread, void *targetSp) -{ - Frame* pFrame = pThread->GetFrame(); - while (pFrame < targetSp) - { - pFrame->ExceptionUnwind(); - pFrame->Pop(pThread); - pFrame = pThread->GetFrame(); - } - - GCFrame* pGCFrame = pThread->GetGCFrame(); - while (pGCFrame && pGCFrame < targetSp) - { - pGCFrame->Pop(); - pGCFrame = pThread->GetGCFrame(); - } -} - UINT_PTR GetEstablisherFrame(REGDISPLAY* pvRegDisplay, ExInfo* exInfo) { #ifdef HOST_AMD64 @@ -7777,8 +7803,6 @@ extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) BOOL fIntercepted = pThread->GetExceptionState()->GetFlags()->DebuggerInterceptInfo(); _ASSERTE(fIntercepted); - ExInfo::PopExInfos(pThread, (void*)targetSp); - // retrieve the interception information MethodDesc *pInterceptMD = NULL; StackFrame sfInterceptStackFrame; @@ -7787,6 +7811,8 @@ extern "C" void QCALLTYPE ResumeAtInterceptionLocation(REGDISPLAY* pvRegDisplay) pThread->GetExceptionState()->GetDebuggerState()->GetDebuggerInterceptInfo(&pInterceptMD, NULL, (PBYTE*)&(sfInterceptStackFrame.SP), &ulRelOffset, NULL); + ExInfo::PopExInfos(pThread, (void*)targetSp); + PCODE pStartAddress = pInterceptMD->GetNativeCode(); EECodeInfo codeInfo(pStartAddress); @@ -8041,7 +8067,6 @@ static void NotifyExceptionPassStarted(StackFrameIterator *pThis, Thread *pThrea GCX_COOP(); pThread->SafeSetThrowables(pExInfo->m_exception); EEToProfilerExceptionInterfaceWrapper::ExceptionThrown(pThread); - UpdatePerformanceMetrics(&pThis->m_crawl, false, ((uint8_t)pExInfo->m_kind & (uint8_t)ExKind::RethrowFlag) == 0); } else // pExInfo->m_passNumber == 2 { @@ -8155,7 +8180,7 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk LONG disposition = InternalUnhandledExceptionFilter_Worker((EXCEPTION_POINTERS *)&pExInfo->m_ptrs); #ifdef HOST_WINDOWS CreateCrashDumpIfEnabled(/* fSOException */ FALSE); - RaiseFailFastException(pExInfo->m_ptrs.ExceptionRecord, pExInfo->m_ptrs.ContextRecord, 0); + RaiseFailFastException(pExInfo->m_ptrs.ExceptionRecord, NULL, 0); #else CrashDumpAndTerminateProcess(pExInfo->m_ExceptionCode); #endif @@ -8165,7 +8190,13 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk pThread->FillRegDisplay(pRD, pStackwalkCtx); new (pThis) StackFrameIterator(); - result = pThis->Init(pThread, pFrame, pRD, THREAD_EXECUTING_MANAGED_CODE) != FALSE; + result = pThis->Init(pThread, pFrame, pRD, THREAD_EXECUTING_MANAGED_CODE | UNWIND_FLOATS) != FALSE; + + if (result && (pExInfo->m_passNumber == 1)) + { + GCX_COOP(); + UpdatePerformanceMetrics(&pThis->m_crawl, false, ((uint8_t)pExInfo->m_kind & (uint8_t)ExKind::RethrowFlag) == 0); + } // Walk the stack until it finds the first managed method while (result && pThis->GetFrameState() != StackFrameIterator::SFITER_FRAMELESS_METHOD) @@ -8214,7 +8245,7 @@ extern "C" bool QCALLTYPE SfiInit(StackFrameIterator* pThis, CONTEXT* pStackwalk if (result) { TADDR controlPC = pThis->m_crawl.GetRegisterSet()->ControlPC; - if (!pThis->m_crawl.HasFaulted()) + if (!pThis->m_crawl.HasFaulted() && !pThis->m_crawl.IsIPadjusted()) { controlPC -= STACKWALK_CONTROLPC_ADJUST_OFFSET; } @@ -8337,7 +8368,8 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla else { #ifdef HOST_WINDOWS - RaiseFailFastException(pTopExInfo->m_ptrs.ExceptionRecord, pTopExInfo->m_ptrs.ContextRecord, 0); + GetThread()->SetThreadStateNC(Thread::TSNC_ProcessedUnhandledException); + RaiseException(pTopExInfo->m_ExceptionCode, EXCEPTION_NONCONTINUABLE_EXCEPTION, pTopExInfo->m_ptrs.ExceptionRecord->NumberParameters, pTopExInfo->m_ptrs.ExceptionRecord->ExceptionInformation); #else CrashDumpAndTerminateProcess(pTopExInfo->m_ExceptionCode); #endif @@ -8411,7 +8443,7 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla { retVal = MoveToNextNonSkippedFrame(pThis); } - while ((retVal == SWA_CONTINUE) && pThis->m_crawl.GetRegisterSet()->SP != pPrevExInfo->m_regDisplay.SP); + while ((retVal == SWA_CONTINUE) && !(pThis->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD && pThis->m_crawl.GetRegisterSet()->SP == pPrevExInfo->m_regDisplay.SP)); _ASSERTE(retVal != SWA_FAILED); pThis->ResetNextExInfoForSP(pThis->m_crawl.GetRegisterSet()->SP); @@ -8457,7 +8489,7 @@ Exit:; if (retVal != SWA_FAILED) { TADDR controlPC = pThis->m_crawl.GetRegisterSet()->ControlPC; - if (!pThis->m_crawl.HasFaulted()) + if (!pThis->m_crawl.HasFaulted() && !pThis->m_crawl.IsIPadjusted()) { controlPC -= STACKWALK_CONTROLPC_ADJUST_OFFSET; } diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index 74818b9485b6ca..7be99adfd2021f 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -22,6 +22,7 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, IN OUT PT_CONTEXT pContextRecord, IN OUT PT_DISPATCHER_CONTEXT pDispatcherContext); +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT *pExceptionContext, bool preserveStackTrace = true); VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, bool preserveStackTrace = true); VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind); @@ -50,8 +51,13 @@ typedef DPTR(ExInfo) PTR_ExInfo; // InlinedCallFrame::m_Datum field for details). enum class InlinedCallFrameMarker { +#ifdef HOST_64BIT ExceptionHandlingHelper = 2, SecondPassFuncletCaller = 4, +#else // HOST_64BIT + ExceptionHandlingHelper = 1, + SecondPassFuncletCaller = 2, +#endif // HOST_64BIT Mask = ExceptionHandlingHelper | SecondPassFuncletCaller }; diff --git a/src/coreclr/vm/exinfo.cpp b/src/coreclr/vm/exinfo.cpp index 741bae687d81da..6bade35ec8963a 100644 --- a/src/coreclr/vm/exinfo.cpp +++ b/src/coreclr/vm/exinfo.cpp @@ -326,12 +326,22 @@ ExInfo::ExInfo(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pEx m_propagateExceptionContext(NULL), #endif // HOST_UNIX m_CurrentClause({}), - m_pMDToReportFunctionLeave(NULL), - m_exContext({}) + m_pMDToReportFunctionLeave(NULL) { m_StackTraceInfo.AllocateStackTrace(); pThread->GetExceptionState()->m_pCurrentTracker = this; - m_exContext.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + if (exceptionKind == ExKind::HardwareFault) + { + // Hardware exception handling needs to start on the FaultingExceptionFrame, so we are + // passing in a context with zeroed out IP. + SetIP(&m_exContext, 0); + m_exContext.ContextFlags = CONTEXT_FULL; + } + else + { + memcpy(&m_exContext, pExceptionContext, sizeof(CONTEXT)); + m_exContext.ContextFlags = m_exContext.ContextFlags & (CONTEXT_FULL | CONTEXT_EXCEPTION_ACTIVE); + } } #if defined(TARGET_UNIX) diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index 90c21e54aa813b..a429ee4ad422b5 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -464,6 +464,22 @@ void Frame::PopIfChained() } #endif // TARGET_UNIX && !DACCESS_COMPILE +#if !defined(TARGET_X86) || defined(TARGET_UNIX) +/* static */ +void Frame::UpdateFloatingPointRegisters(const PREGDISPLAY pRD) +{ + _ASSERTE(!ExecutionManager::IsManagedCode(::GetIP(pRD->pCurrentContext))); + while (!ExecutionManager::IsManagedCode(::GetIP(pRD->pCurrentContext))) + { +#ifdef TARGET_UNIX + PAL_VirtualUnwind(pRD->pCurrentContext, NULL); +#else + Thread::VirtualUnwindCallFrame(pRD); +#endif + } +} +#endif // !TARGET_X86 || TARGET_UNIX + //----------------------------------------------------------------------- #endif // #ifndef DACCESS_COMPILE //--------------------------------------------------------------- diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 907cc2e0e3eb74..780e0e87242315 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -512,7 +512,7 @@ class Frame : public FrameBase // UpdateRegDisplay is generally used to fill out the REGDISPLAY parameter, some // overrides (e.g., code:ResumableFrame::UpdateRegDisplay) will actually READ what // you pass in. So be sure to pass in a valid or zeroed out REGDISPLAY. - virtual void UpdateRegDisplay(const PREGDISPLAY) + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false) { LIMITED_METHOD_DAC_CONTRACT; return; @@ -754,6 +754,12 @@ class Frame : public FrameBase LIMITED_METHOD_CONTRACT; } +#ifndef DACCESS_COMPILE +#if !defined(TARGET_X86) || defined(TARGET_UNIX) + static void UpdateFloatingPointRegisters(const PREGDISPLAY pRD); +#endif // !TARGET_X86 || TARGET_UNIX +#endif // DACCESS_COMPILE + #if defined(TARGET_UNIX) && !defined(DACCESS_COMPILE) virtual ~Frame() { LIMITED_METHOD_CONTRACT; } @@ -795,7 +801,7 @@ class ResumableFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY pRD); + virtual void UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats = false); virtual unsigned GetFrameAttribs() { LIMITED_METHOD_DAC_CONTRACT; @@ -1000,7 +1006,7 @@ class TransitionFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); #ifdef TARGET_X86 void UpdateRegDisplayHelper(const PREGDISPLAY, UINT cbStackPop); #endif @@ -1080,7 +1086,11 @@ class FaultingExceptionFrame : public Frame unsigned GetFrameAttribs() { LIMITED_METHOD_DAC_CONTRACT; +#ifdef FEATURE_EH_FUNCLETS + return FRAME_ATTR_EXCEPTION | (!!(m_ctx.ContextFlags & CONTEXT_EXCEPTION_ACTIVE) ? FRAME_ATTR_FAULTED : 0); +#else return FRAME_ATTR_EXCEPTION | FRAME_ATTR_FAULTED; +#endif } #ifndef FEATURE_EH_FUNCLETS @@ -1114,7 +1124,7 @@ class FaultingExceptionFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); // Keep as last entry in class DEFINE_VTABLE_GETTER_AND_DTOR(FaultingExceptionFrame) @@ -1176,7 +1186,7 @@ class FuncEvalFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); virtual DebuggerEval * GetDebuggerEval(); @@ -1263,7 +1273,7 @@ class HelperMethodFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); virtual Interception GetInterception() { @@ -2037,7 +2047,7 @@ class PInvokeCalliFrame : public FramedMethodFrame } #ifdef TARGET_X86 - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); #endif // TARGET_X86 BOOL TraceFrame(Thread *thread, BOOL fromPatch, @@ -2081,7 +2091,7 @@ class HijackFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); virtual void GcScanRoots(promote_func *fn, ScanContext* sc); // HijackFrames are created by trip functions. See OnHijackTripThread() @@ -2176,7 +2186,7 @@ class StubDispatchFrame : public FramedMethodFrame PTR_BYTE GetGCRefMap(); #ifdef TARGET_X86 - virtual void UpdateRegDisplay(const PREGDISPLAY pRD); + virtual void UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats = false); virtual PCODE GetReturnAddress(); #endif // TARGET_X86 @@ -2319,7 +2329,7 @@ class ExternalMethodFrame : public FramedMethodFrame Interception GetInterception(); #ifdef TARGET_X86 - virtual void UpdateRegDisplay(const PREGDISPLAY pRD); + virtual void UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats = false); #endif // Keep as last entry in class @@ -2341,7 +2351,7 @@ class DynamicHelperFrame : public FramedMethodFrame virtual void GcScanRoots(promote_func *fn, ScanContext* sc); #ifdef TARGET_X86 - virtual void UpdateRegDisplay(const PREGDISPLAY pRD); + virtual void UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats = false); #endif virtual ETransitionType GetTransitionType() @@ -2800,7 +2810,8 @@ class InlinedCallFrame : public Frame { WRAPPER_NO_CONTRACT; if (FrameHasActiveCall(this) && HasFunction()) - return PTR_MethodDesc(m_Datum); + // Mask off marker bits + return PTR_MethodDesc((dac_cast(m_Datum) & ~(sizeof(TADDR) - 1))); else return NULL; } @@ -2811,7 +2822,7 @@ class InlinedCallFrame : public Frame #ifdef HOST_64BIT // See code:GenericPInvokeCalliHelper - return ((m_Datum != NULL) && !(dac_cast(m_Datum) & 0x3)); + return ((m_Datum != NULL) && !(dac_cast(m_Datum) & 0x1)); #else // HOST_64BIT return ((dac_cast(m_Datum) & ~0xffff) != 0); #endif // HOST_64BIT @@ -2868,7 +2879,7 @@ class InlinedCallFrame : public Frame #endif // defined(TARGET_X86) || defined(TARGET_ARM) } - virtual void UpdateRegDisplay(const PREGDISPLAY); + virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); // m_Datum contains MethodDesc ptr or // - on 64 bit host: CALLI target address (if lowest bit is set) @@ -3034,7 +3045,7 @@ class TailCallFrame : public Frame return TRUE; } - virtual void UpdateRegDisplay(const PREGDISPLAY pRD); + virtual void UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats = false); private: // Keep as last entry in class diff --git a/src/coreclr/vm/i386/cgenx86.cpp b/src/coreclr/vm/i386/cgenx86.cpp index 496c7c3f34366b..9bbc1046e806a2 100644 --- a/src/coreclr/vm/i386/cgenx86.cpp +++ b/src/coreclr/vm/i386/cgenx86.cpp @@ -139,7 +139,7 @@ void EHContext::UpdateFrame(PREGDISPLAY regs) } #endif // FEATURE_EH_FUNCLETS -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -211,7 +211,7 @@ void TransitionFrame::UpdateRegDisplayHelper(const PREGDISPLAY pRD, UINT cbStack RETURN; } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -391,7 +391,7 @@ EXTERN_C MachState* STDCALL HelperMethodFrameConfirmState(HelperMethodFrame* fra } #endif -void ExternalMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ExternalMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -411,7 +411,7 @@ void ExternalMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } -void StubDispatchFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void StubDispatchFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -468,7 +468,7 @@ PCODE StubDispatchFrame::GetReturnAddress() return retAddress; } -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -521,7 +521,7 @@ void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) RETURN; } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -615,7 +615,7 @@ TADDR ResumableFrame::GetReturnAddressPtr() return dac_cast(m_Regs) + offsetof(CONTEXT, Eip); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -696,7 +696,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) // The HijackFrame has to know the registers that are pushed by OnHijackTripThread // -> HijackFrame::UpdateRegDisplay should restore all the registers pushed by OnHijackTripThread -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { NOTHROW; @@ -753,7 +753,7 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) #endif // FEATURE_HIJACK -void PInvokeCalliFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void PInvokeCalliFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -774,7 +774,7 @@ void PInvokeCalliFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } #ifndef UNIX_X86_ABI -void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -822,7 +822,7 @@ void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) #endif // !UNIX_X86_ABI #ifdef FEATURE_READYTORUN -void DynamicHelperFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void DynamicHelperFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { WRAPPER_NO_CONTRACT; UpdateRegDisplayHelper(pRD, 0); diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 61c85e5ae35423..450752ae367789 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -4289,7 +4289,10 @@ void RethrowNew() ExInfo *pActiveExInfo = (ExInfo*)pThread->GetExceptionState()->GetCurrentExceptionTracker(); - ExInfo exInfo(pThread, pActiveExInfo->m_ptrs.ExceptionRecord, pActiveExInfo->m_ptrs.ContextRecord, ExKind::None); + CONTEXT exceptionContext; + RtlCaptureContext(&exceptionContext); + + ExInfo exInfo(pThread, pActiveExInfo->m_ptrs.ExceptionRecord, &exceptionContext, ExKind::None); GCPROTECT_BEGIN(exInfo.m_exception); PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_RETHROW); diff --git a/src/coreclr/vm/loongarch64/stubs.cpp b/src/coreclr/vm/loongarch64/stubs.cpp index 5fe3599d0dc507..da93bd587ed344 100644 --- a/src/coreclr/vm/loongarch64/stubs.cpp +++ b/src/coreclr/vm/loongarch64/stubs.cpp @@ -450,7 +450,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, unwoundstate->_isValid = TRUE; } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -461,6 +461,14 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -620,8 +628,16 @@ void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegis pContextPointers->Ra = (PDWORD64)&pCalleeSaved->ra; } -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -643,7 +659,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_DAC_CONTRACT; @@ -676,7 +692,7 @@ void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK FaultingExceptionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -697,6 +713,13 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) return; } +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; @@ -739,7 +762,7 @@ TADDR ResumableFrame::GetReturnAddressPtr(void) return dac_cast(m_Regs) + offsetof(T_CONTEXT, Pc); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -796,7 +819,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) RETURN; } -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 0f0273da0a47bd..40c945a810b674 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -344,7 +344,7 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, unwoundstate->_isValid = TRUE; } -void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACTL { @@ -355,6 +355,14 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD) } CONTRACTL_END; +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -534,8 +542,16 @@ void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegis pContextPointers->Ra = (PDWORD64)&pCalleeSaved->ra; } -void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + _ASSERTE(pRD->pCurrentContext->Pc == GetReturnAddress()); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. @@ -557,7 +573,7 @@ void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK TransitionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } -void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_DAC_CONTRACT; @@ -593,7 +609,7 @@ void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD) LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK FaultingExceptionFrame::UpdateRegDisplay(pc:%p, sp:%p)\n", pRD->ControlPC, pRD->SP)); } -void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -614,6 +630,13 @@ void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD) return; } +#ifndef DACCESS_COMPILE + if (updateFloats) + { + UpdateFloatingPointRegisters(pRD); + } +#endif // DACCESS_COMPILE + pRD->IsCallerContextValid = FALSE; pRD->IsCallerSPValid = FALSE; @@ -659,7 +682,7 @@ TADDR ResumableFrame::GetReturnAddressPtr(void) return dac_cast(m_Regs) + offsetof(T_CONTEXT, Pc); } -void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { CONTRACT_VOID { @@ -716,7 +739,7 @@ void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD) RETURN; } -void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) +void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index 92a0cd9dc76b47..cd971db4236f28 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -1234,6 +1234,12 @@ BOOL StackFrameIterator::Init(Thread * pThread, // process the REGDISPLAY and stop at the first frame ProcessIp(GetControlPC(m_crawl.pRD)); +#ifdef FEATURE_EH_FUNCLETS + if (m_crawl.isFrameless && !!(m_crawl.pRD->pCurrentContext->ContextFlags & CONTEXT_EXCEPTION_ACTIVE)) + { + m_crawl.hasFaulted = true; + } +#endif // FEATURE_EH_FUNCLETS ProcessCurrentFrame(); // advance to the next frame which matches the stackwalk flags @@ -1373,7 +1379,7 @@ BOOL StackFrameIterator::ResetRegDisp(PREGDISPLAY pRegDisp, else { // unwind the REGDISPLAY using the transition frame and check the EBP - m_crawl.pFrame->UpdateRegDisplay(&tmpRD); + m_crawl.pFrame->UpdateRegDisplay(&tmpRD, m_flags & UNWIND_FLOATS); if (GetRegdisplayFP(&tmpRD) != curEBP) { break; @@ -1400,8 +1406,7 @@ BOOL StackFrameIterator::ResetRegDisp(PREGDISPLAY pRegDisp, m_crawl.isIPadjusted = false; } - m_crawl.pFrame->UpdateRegDisplay(m_crawl.pRD); - + m_crawl.pFrame->UpdateRegDisplay(m_crawl.pRD, m_flags & UNWIND_FLOATS); _ASSERTE(curPc == GetControlPC(m_crawl.pRD)); } @@ -2721,7 +2726,7 @@ StackWalkAction StackFrameIterator::NextRaw(void) if (m_crawl.isFrameless) { - m_crawl.pFrame->UpdateRegDisplay(m_crawl.pRD); + m_crawl.pFrame->UpdateRegDisplay(m_crawl.pRD, m_flags & UNWIND_FLOATS); #if defined(RECORD_RESUMABLE_FRAME_SP) CONSISTENCY_CHECK(NULL == m_pvResumableFrameTargetSP); diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index bfb154b0e539de..ef5ba294a1582f 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -2768,6 +2768,8 @@ class Thread // may still execute GS cookie tracking/checking code paths. #define SKIP_GSCOOKIE_CHECK 0x10000 + #define UNWIND_FLOATS 0x20000 + StackWalkAction StackWalkFramesEx( PREGDISPLAY pRD, // virtual register set at crawl start PSTACKWALKFRAMESCALLBACK pCallback, diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 84d1ade6037be2..ed6d740f1f9022 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -3973,7 +3973,8 @@ ThrowControlForThread( exceptionRecord.ExceptionFlags = 0; OBJECTREF throwable = ExceptionTracker::CreateThrowable(&exceptionRecord, TRUE); - DispatchManagedException(throwable); + pfef->GetExceptionContext()->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE; + DispatchManagedException(throwable, pfef->GetExceptionContext()); } else #endif // FEATURE_EH_FUNCLETS From d4ffdfaf922a0c2a46e62dbf7202c97d4eca7a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 15 Feb 2024 01:58:42 +0900 Subject: [PATCH 031/158] Don't place MSBuild files to target-specific ILCompiler packages (#98411) Fixes #96556. --- .../Microsoft.DotNet.ILCompiler.pkgproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj b/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj index 3995419fc90984..33d4b7ed137f24 100644 --- a/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj +++ b/src/installer/pkg/projects/Microsoft.DotNet.ILCompiler/Microsoft.DotNet.ILCompiler.pkgproj @@ -24,7 +24,7 @@ - + From eebaf2e990244f2fca6d06f6fa58adda5f8d149b Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Wed, 14 Feb 2024 18:01:20 +0100 Subject: [PATCH 032/158] JIT: Unify preheader/exit weight calculation logic (#98407) The preheader/exit weight calculation is essentially the same logic, so unify to use the logic from the exit weight calculation. This also fixes a bug in the preheader weight calculation introduced by my recent change where we sometimes mistakenly used the weight of the newly inserted preheader instead of the header block to compute a likelihood. --- src/coreclr/jit/compiler.h | 5 +- src/coreclr/jit/optimizer.cpp | 187 ++++++---------------------------- 2 files changed, 32 insertions(+), 160 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 4d7002295ee16c..26ade9a4683f0d 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6806,12 +6806,11 @@ class Compiler BasicBlock* optFindLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, BasicBlock* top); BasicBlock* optTryAdvanceLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, BasicBlock* insertionPoint, BasicBlock* top, BasicBlock* bottom); bool optCreatePreheader(FlowGraphNaturalLoop* loop); - void optSetPreheaderWeight(FlowGraphNaturalLoop* loop, BasicBlock* preheader); + void optSetWeightForPreheaderOrExit(FlowGraphNaturalLoop* loop, BasicBlock* block); + weight_t optEstimateEdgeLikelihood(BasicBlock* from, BasicBlock* to, bool* fromProfile); bool optCanonicalizeExits(FlowGraphNaturalLoop* loop); bool optCanonicalizeExit(FlowGraphNaturalLoop* loop, BasicBlock* exit); - weight_t optEstimateEdgeLikelihood(BasicBlock* from, BasicBlock* to, bool* fromProfile); - void optSetExitWeight(FlowGraphNaturalLoop* loop, BasicBlock* exit); PhaseStatus optCloneLoops(); void optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* context); diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index b54d224f227da2..9d5c50f25d429f 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -2994,139 +2994,11 @@ bool Compiler::optCreatePreheader(FlowGraphNaturalLoop* loop) fgReplaceJumpTarget(enterBlock, header, preheader); } - optSetPreheaderWeight(loop, preheader); + optSetWeightForPreheaderOrExit(loop, preheader); return true; } -//----------------------------------------------------------------------------- -// optSetPreheaderWeight: Set the weight of a newly created preheader, after it -// has been added to the flowgraph. -// -// Parameters: -// loop - The loop -// preheader - The new preheader block -// -void Compiler::optSetPreheaderWeight(FlowGraphNaturalLoop* loop, BasicBlock* preheader) -{ - if (loop->EntryEdges().size() == 0) - { - return; - } - - // The preheader is only considered to have profile weights when all the - // following conditions are true: - // - The loop header has a profile weight - // - All entering blocks have a profile weight - // - The successors of all entering blocks have a profile weight (so that - // we can trust the computed taken/not taken ratio) - // - bool hasProfWeight = fgIsUsingProfileWeights(); - weight_t preheaderWeight = BB_ZERO_WEIGHT; - - for (FlowEdge* entryEdge : loop->EntryEdges()) - { - BasicBlock* prevEntering = entryEdge->getSourceBlock(); - - hasProfWeight &= prevEntering->HasFlag(BBF_PROF_WEIGHT) != BBF_EMPTY; - - if (!fgIsUsingProfileWeights() || !prevEntering->HasFlag(BBF_PROF_WEIGHT) || - !loop->GetHeader()->HasFlag(BBF_PROF_WEIGHT) || prevEntering->KindIs(BBJ_ALWAYS)) - { - preheaderWeight += prevEntering->bbWeight / prevEntering->NumSucc(this); - continue; - } - - bool succsHaveProfileWeights = true; - bool useEdgeWeights = fgHaveValidEdgeWeights; - - weight_t loopEnterCount = 0; - weight_t loopSkipCount = 0; - - if (useEdgeWeights) - { - prevEntering->VisitRegularSuccs(this, [&, preheader](BasicBlock* succ) { - FlowEdge* edge = fgGetPredForBlock(succ, prevEntering); - weight_t edgeWeight = (edge->edgeWeightMin() + edge->edgeWeightMax()) / 2.0; - - if (succ == preheader) - { - loopEnterCount += edgeWeight; - } - else - { - succsHaveProfileWeights &= succ->hasProfileWeight(); - loopSkipCount += edgeWeight; - } - return BasicBlockVisit::Continue; - }); - - // Watch out for cases where edge weights were not properly maintained - // so that it appears no profile flow enters the loop. - // - useEdgeWeights = !fgProfileWeightsConsistent(loopEnterCount, BB_ZERO_WEIGHT); - } - - if (!useEdgeWeights) - { - loopEnterCount = 0; - loopSkipCount = 0; - - prevEntering->VisitRegularSuccs(this, [&, preheader](BasicBlock* succ) { - if (succ == preheader) - { - loopEnterCount += succ->bbWeight; - } - else - { - succsHaveProfileWeights &= succ->hasProfileWeight(); - loopSkipCount += succ->bbWeight; - } - - return BasicBlockVisit::Continue; - }); - } - - if (!succsHaveProfileWeights) - { - preheaderWeight += prevEntering->bbWeight / prevEntering->NumSucc(this); - hasProfWeight = false; - continue; - } - - weight_t loopTakenRatio = loopEnterCount / (loopEnterCount + loopSkipCount); - - JITDUMP("%s edge weights; loopEnterCount " FMT_WT " loopSkipCount " FMT_WT " taken ratio " FMT_WT "\n", - fgHaveValidEdgeWeights ? (useEdgeWeights ? "valid" : "ignored") : "invalid", loopEnterCount, - loopSkipCount, loopTakenRatio); - - weight_t enterContribution = prevEntering->bbWeight * loopTakenRatio; - preheaderWeight += enterContribution; - - // Normalize prevEntering -> preheader edge - FlowEdge* const edgeToPreheader = fgGetPredForBlock(preheader, prevEntering); - assert(edgeToPreheader != nullptr); - edgeToPreheader->setEdgeWeights(enterContribution, enterContribution, preheader); - } - - preheader->bbWeight = preheaderWeight; - if (hasProfWeight) - { - preheader->SetFlags(BBF_PROF_WEIGHT); - } - - if (preheaderWeight == BB_ZERO_WEIGHT) - { - preheader->SetFlags(BBF_RUN_RARELY); - return; - } - - // Normalize preheader -> header weight - FlowEdge* const edgeFromPreheader = fgGetPredForBlock(loop->GetHeader(), preheader); - assert(edgeFromPreheader != nullptr); - edgeFromPreheader->setEdgeWeights(preheader->bbWeight, preheader->bbWeight, loop->GetHeader()); -} - //----------------------------------------------------------------------------- // optCanonicalizeExits: Canonicalize all regular exits of the loop so that // they have only loop predecessors. @@ -3248,7 +3120,7 @@ bool Compiler::optCanonicalizeExit(FlowGraphNaturalLoop* loop, BasicBlock* exit) } } - optSetExitWeight(loop, newExit); + optSetWeightForPreheaderOrExit(loop, newExit); return true; } @@ -3338,60 +3210,61 @@ weight_t Compiler::optEstimateEdgeLikelihood(BasicBlock* from, BasicBlock* to, b } //----------------------------------------------------------------------------- -// optSetExitWeight: Set the weight of a newly created exit, after it -// has been added to the flowgraph. +// optSetWeightForPreheaderOrExit: Set the weight of a newly created preheader +// or exit, after it has been added to the flowgraph. // // Parameters: -// loop - The loop -// preheader - The new exit block +// loop - The loop +// block - The new preheader or exit block // -void Compiler::optSetExitWeight(FlowGraphNaturalLoop* loop, BasicBlock* exit) +void Compiler::optSetWeightForPreheaderOrExit(FlowGraphNaturalLoop* loop, BasicBlock* block) { bool hasProfWeight = true; - // Inherit first estimate from the exit target; optEstimateEdgeLikelihood + assert(block->GetUniqueSucc() != nullptr); + // Inherit first estimate from the target target; optEstimateEdgeLikelihood // may use it in its estimate if we do not have edge weights to estimate - // from (we also assume the exiting -> exit edges already inherited their - // edge weights from the previous edge). - exit->inheritWeight(exit->GetTarget()); + // from (we also assume the edges into 'block' already inherited their edge + // weights from the previous edge). + block->inheritWeight(block->GetTarget()); - weight_t exitWeight = BB_ZERO_WEIGHT; - for (FlowEdge* exitEdge : exit->PredEdges()) + weight_t newWeight = BB_ZERO_WEIGHT; + for (FlowEdge* edge : block->PredEdges()) { - BasicBlock* exiting = exitEdge->getSourceBlock(); + BasicBlock* predBlock = edge->getSourceBlock(); bool fromProfile = false; - weight_t likelihood = optEstimateEdgeLikelihood(exiting, exit, &fromProfile); + weight_t likelihood = optEstimateEdgeLikelihood(predBlock, block, &fromProfile); hasProfWeight &= fromProfile; - weight_t contribution = exiting->bbWeight * likelihood; + weight_t contribution = predBlock->bbWeight * likelihood; JITDUMP(" Estimated likelihood " FMT_BB " -> " FMT_BB " to be " FMT_WT " (contribution: " FMT_WT ")\n", - exiting->bbNum, exit->bbNum, likelihood, contribution); + predBlock->bbNum, block->bbNum, likelihood, contribution); - exitWeight += contribution; + newWeight += contribution; - // Normalize exiting -> new exit weight - exitEdge->setEdgeWeights(contribution, contribution, exit); + // Normalize pred -> new block weight + edge->setEdgeWeights(contribution, contribution, block); } - exit->RemoveFlags(BBF_PROF_WEIGHT | BBF_RUN_RARELY); + block->RemoveFlags(BBF_PROF_WEIGHT | BBF_RUN_RARELY); - exit->bbWeight = exitWeight; + block->bbWeight = newWeight; if (hasProfWeight) { - exit->SetFlags(BBF_PROF_WEIGHT); + block->SetFlags(BBF_PROF_WEIGHT); } - if (exitWeight == BB_ZERO_WEIGHT) + if (newWeight == BB_ZERO_WEIGHT) { - exit->SetFlags(BBF_RUN_RARELY); + block->SetFlags(BBF_RUN_RARELY); return; } - // Normalize new exit -> old exit weight - FlowEdge* const edgeFromNewExit = fgGetPredForBlock(exit->GetTarget(), exit); - assert(edgeFromNewExit != nullptr); - edgeFromNewExit->setEdgeWeights(exit->bbWeight, exit->bbWeight, exit->GetTarget()); + // Normalize block -> target weight + FlowEdge* const edgeFromBlock = fgGetPredForBlock(block->GetTarget(), block); + assert(edgeFromBlock != nullptr); + edgeFromBlock->setEdgeWeights(block->bbWeight, block->bbWeight, block->GetTarget()); } /***************************************************************************** From c7c15f7c2c55578b4e64450e4c236f5f59360827 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 11:56:01 -0600 Subject: [PATCH 033/158] Update dependencies from https://github.com/dotnet/arcade build 20240212.1 (#98418) Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.24106.2 -> To Version 9.0.0-beta.24112.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 84 +++++++++---------- eng/Versions.props | 32 +++---- eng/common/post-build/publish-using-darc.ps1 | 4 +- .../templates/job/publish-build-assets.yml | 16 ++-- .../templates/post-build/post-build.yml | 16 ++-- eng/common/templates/steps/send-to-helix.yml | 6 +- .../templates/variables/pool-providers.yml | 12 +-- global.json | 6 +- 8 files changed, 89 insertions(+), 87 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 43369f9344e494..23e32a1a368bef 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -124,87 +124,87 @@ - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb https://github.com/dotnet/runtime-assets @@ -364,9 +364,9 @@ https://github.com/dotnet/xharness 2f3f51a1adec18475563c8a49fd9b668ae9f2f31 - + https://github.com/dotnet/arcade - f7eb7794c703dc29a83b414b786e9a154f0ca042 + c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb https://dev.azure.com/dnceng/internal/_git/dotnet-optimization diff --git a/eng/Versions.props b/eng/Versions.props index 6aab65a01319ff..522ee1892a22da 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -83,22 +83,22 @@ 9.0.100-preview.2.24112.1 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 - 2.6.7-beta.24106.2 - 9.0.0-beta.24106.2 - 2.6.7-beta.24106.2 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 - 9.0.0-beta.24106.2 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 + 2.6.7-beta.24112.1 + 9.0.0-beta.24112.1 + 2.6.7-beta.24112.1 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 + 9.0.0-beta.24112.1 1.4.0 diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 index 1e779fec4dd1ea..5a3a32ea8d75b4 100644 --- a/eng/common/post-build/publish-using-darc.ps1 +++ b/eng/common/post-build/publish-using-darc.ps1 @@ -12,7 +12,7 @@ param( try { . $PSScriptRoot\post-build-utils.ps1 - $darc = Get-Darc + $darc = Get-Darc $optionalParams = [System.Collections.ArrayList]::new() @@ -46,7 +46,7 @@ try { } Write-Host 'done.' -} +} catch { Write-Host $_ Write-PipelineTelemetryError -Category 'PromoteBuild' -Message "There was an error while trying to publish build '$BuildId' to default channels." diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml index 3115990d511474..bb42240f865b56 100644 --- a/eng/common/templates/job/publish-build-assets.yml +++ b/eng/common/templates/job/publish-build-assets.yml @@ -58,7 +58,7 @@ jobs: demands: Cmd # If it's not devdiv, it's dnceng ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: $(DncEngInternalBuildPool) + name: NetCore1ESPool-Publishing-Internal demands: ImageOverride -equals windows.vs2019.amd64 steps: @@ -66,7 +66,7 @@ jobs: - checkout: self fetchDepth: 3 clean: true - + - task: DownloadBuildArtifacts@0 displayName: Download artifact inputs: @@ -75,7 +75,7 @@ jobs: checkDownloadedFiles: true condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - + - task: NuGetAuthenticate@1 - task: PowerShell@2 @@ -90,7 +90,7 @@ jobs: /p:OfficialBuildId=$(Build.BuildNumber) condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - + - task: powershell@2 displayName: Create ReleaseConfigs Artifact inputs: @@ -99,7 +99,7 @@ jobs: Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(BARBuildId) Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value "$(DefaultChannels)" Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(IsStableBuild) - + - task: PublishBuildArtifacts@1 displayName: Publish ReleaseConfigs Artifact inputs: @@ -125,7 +125,7 @@ jobs: - task: PublishBuildArtifacts@1 displayName: Publish SymbolPublishingExclusionsFile Artifact - condition: eq(variables['SymbolExclusionFile'], 'true') + condition: eq(variables['SymbolExclusionFile'], 'true') inputs: PathtoPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt' PublishLocation: Container @@ -141,7 +141,7 @@ jobs: displayName: Publish Using Darc inputs: filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) + arguments: -BuildId $(BARBuildId) -PublishingInfraVersion 3 -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' -MaestroToken '$(MaestroApiAccessToken)' @@ -152,4 +152,4 @@ jobs: - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: - template: /eng/common/templates/steps/publish-logs.yml parameters: - JobLabel: 'Publish_Artifacts_Logs' + JobLabel: 'Publish_Artifacts_Logs' diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index bbc010fe73261e..ee70e2b399c5a9 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -39,7 +39,7 @@ parameters: displayName: Enable NuGet validation type: boolean default: true - + - name: publishInstallersAndChecksums displayName: Publish installers and checksums type: boolean @@ -131,8 +131,8 @@ stages: displayName: Validate inputs: filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 - arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ - -ToolDestinationPath $(Agent.BuildDirectory)/Extract/ + arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ + -ToolDestinationPath $(Agent.BuildDirectory)/Extract/ - job: displayName: Signing Validation @@ -222,9 +222,9 @@ stages: displayName: Validate inputs: filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1 - arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ - -ExtractPath $(Agent.BuildDirectory)/Extract/ - -GHRepoName $(Build.Repository.Name) + arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ + -ExtractPath $(Agent.BuildDirectory)/Extract/ + -GHRepoName $(Build.Repository.Name) -GHCommit $(Build.SourceVersion) -SourcelinkCliVersion $(SourceLinkCLIVersion) continueOnError: true @@ -259,7 +259,7 @@ stages: demands: Cmd # If it's not devdiv, it's dnceng ${{ else }}: - name: $(DncEngInternalBuildPool) + name: NetCore1ESPool-Publishing-Internal demands: ImageOverride -equals windows.vs2019.amd64 steps: - template: setup-maestro-vars.yml @@ -273,7 +273,7 @@ stages: displayName: Publish Using Darc inputs: filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) + arguments: -BuildId $(BARBuildId) -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' -MaestroToken '$(MaestroApiAccessToken)' diff --git a/eng/common/templates/steps/send-to-helix.yml b/eng/common/templates/steps/send-to-helix.yml index 3eb7e2d5f840c7..68fa739c4ab215 100644 --- a/eng/common/templates/steps/send-to-helix.yml +++ b/eng/common/templates/steps/send-to-helix.yml @@ -5,6 +5,8 @@ parameters: HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number HelixTargetQueues: '' # required -- semicolon-delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group + HelixProjectPath: 'eng/common/helixpublish.proj' # optional -- path to the project file to build relative to BUILD_SOURCESDIRECTORY + HelixProjectArguments: '' # optional -- arguments passed to the build command HelixConfiguration: '' # optional -- additional property attached to a job HelixPreCommands: '' # optional -- commands to run before Helix work item execution HelixPostCommands: '' # optional -- commands to run after Helix work item execution @@ -29,7 +31,7 @@ parameters: continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false steps: - - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY\eng\common\helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"' + - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY/${{ parameters.HelixProjectPath }} /restore /p:TreatWarningsAsErrors=false ${{ parameters.HelixProjectArguments }} /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"' displayName: ${{ parameters.DisplayNamePrefix }} (Windows) env: BuildConfig: $(_BuildConfig) @@ -59,7 +61,7 @@ steps: SYSTEM_ACCESSTOKEN: $(System.AccessToken) condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT')) continueOnError: ${{ parameters.continueOnError }} - - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/eng/common/helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog + - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/${{ parameters.HelixProjectPath }} /restore /p:TreatWarningsAsErrors=false ${{ parameters.HelixProjectArguments }} /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog displayName: ${{ parameters.DisplayNamePrefix }} (Unix) env: BuildConfig: $(_BuildConfig) diff --git a/eng/common/templates/variables/pool-providers.yml b/eng/common/templates/variables/pool-providers.yml index 9cc5c550d3b36f..d236f9fdbb153b 100644 --- a/eng/common/templates/variables/pool-providers.yml +++ b/eng/common/templates/variables/pool-providers.yml @@ -1,15 +1,15 @@ -# Select a pool provider based off branch name. Anything with branch name containing 'release' must go into an -Svc pool, +# Select a pool provider based off branch name. Anything with branch name containing 'release' must go into an -Svc pool, # otherwise it should go into the "normal" pools. This separates out the queueing and billing of released branches. -# Motivation: +# Motivation: # Once a given branch of a repository's output has been officially "shipped" once, it is then considered to be COGS # (Cost of goods sold) and should be moved to a servicing pool provider. This allows both separation of queueing # (allowing release builds and main PR builds to not intefere with each other) and billing (required for COGS. -# Additionally, the pool provider name itself may be subject to change when the .NET Core Engineering Services -# team needs to move resources around and create new and potentially differently-named pools. Using this template +# Additionally, the pool provider name itself may be subject to change when the .NET Core Engineering Services +# team needs to move resources around and create new and potentially differently-named pools. Using this template # file from an Arcade-ified repo helps guard against both having to update one's release/* branches and renaming. -# How to use: +# How to use: # This yaml assumes your shipped product branches use the naming convention "release/..." (which many do). # If we find alternate naming conventions in broad usage it can be added to the condition below. # @@ -54,4 +54,4 @@ variables: False, 'NetCore1ESPool-Internal' ) - ] \ No newline at end of file + ] diff --git a/global.json b/global.json index a6fc52f27fa093..75b4add3bebfc9 100644 --- a/global.json +++ b/global.json @@ -8,9 +8,9 @@ "dotnet": "9.0.100-alpha.1.23615.4" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24106.2", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24106.2", - "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.24106.2", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24112.1", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24112.1", + "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.24112.1", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", "Microsoft.NET.Sdk.IL": "9.0.0-preview.2.24111.9" From a45fe68236fa874aa53e5fb5430ba1952ad52bdd Mon Sep 17 00:00:00 2001 From: Andy Gocke Date: Wed, 14 Feb 2024 11:33:03 -0800 Subject: [PATCH 034/158] Add build analysis config file to runtime (#97465) * Add build analysis config file to runtime * Update build-analysis-configuration.json --- eng/build-analysis-configuration.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 eng/build-analysis-configuration.json diff --git a/eng/build-analysis-configuration.json b/eng/build-analysis-configuration.json new file mode 100644 index 00000000000000..7f532220c1001f --- /dev/null +++ b/eng/build-analysis-configuration.json @@ -0,0 +1,8 @@ +{ + "PipelinesToAnalyze":[ + { + "PipelineId": 129, + "PipelineName": "runtime" + } + ] +} From e41af7b62ed95997363fb6920059a43321103f87 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Wed, 14 Feb 2024 14:39:35 -0600 Subject: [PATCH 035/158] Pull the Wix version from the arcade.sdk (#98437) --- src/workloads/workloads.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workloads/workloads.csproj b/src/workloads/workloads.csproj index 2c0bb6784c7dde..e8f9b0726f4433 100644 --- a/src/workloads/workloads.csproj +++ b/src/workloads/workloads.csproj @@ -31,7 +31,7 @@ - + From c858f508661cdd46c45450c23fa732bf163cabaf Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:46:59 -0600 Subject: [PATCH 036/158] Update dependencies from https://github.com/dotnet/emsdk build 20240213.2 (#98419) Microsoft.SourceBuild.Intermediate.emsdk , Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-preview.2.24111.1 -> To Version 9.0.0-preview.2.24113.2 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 12 ++++++------ eng/Versions.props | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 23e32a1a368bef..375dfe3baa91a2 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -12,9 +12,9 @@ https://github.com/dotnet/wcf 7f504aabb1988e9a093c1e74d8040bd52feb2f01 - + https://github.com/dotnet/emsdk - 4106737f31432e0408d3afd95bf242233daf48a6 + 4bbafbd3474cb3c927f792a9b6749fe89277ef7d https://github.com/dotnet/llvm-project @@ -100,14 +100,14 @@ 93dcb576e191a965008eae9b622527436653873f - + https://github.com/dotnet/emsdk - 4106737f31432e0408d3afd95bf242233daf48a6 + 4bbafbd3474cb3c927f792a9b6749fe89277ef7d - + https://github.com/dotnet/emsdk - 4106737f31432e0408d3afd95bf242233daf48a6 + 4bbafbd3474cb3c927f792a9b6749fe89277ef7d diff --git a/eng/Versions.props b/eng/Versions.props index 522ee1892a22da..f7f28f795968eb 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -243,9 +243,9 @@ Note: when the name is updated, make sure to update dependency name in eng/pipelines/common/xplat-setup.yml like - DarcDependenciesChanged.Microsoft_NET_Workload_Emscripten_Current_Manifest-9_0_100_Transport --> - 9.0.0-preview.2.24111.1 + 9.0.0-preview.2.24113.2 $(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportVersion) - 9.0.0-preview.2.24111.1 + 9.0.0-preview.2.24113.2 1.1.87-gba258badda 1.0.0-v3.14.0.5722 From d3ebc972c6c878a22201629833683de544e8f532 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Wed, 14 Feb 2024 14:58:49 -0600 Subject: [PATCH 037/158] Fast FieldInfo GetValue and SetValue (#98199) --- .../src/System/Reflection/RtFieldInfo.cs | 135 +---- .../src/System/Reflection/RuntimeFieldInfo.cs | 2 +- .../src/System/RuntimeHandles.cs | 14 +- .../src/System/RuntimeType.CoreCLR.cs | 13 - src/coreclr/vm/corelib.h | 8 +- src/coreclr/vm/ecalllist.h | 3 + src/coreclr/vm/field.cpp | 12 +- src/coreclr/vm/field.h | 10 +- src/coreclr/vm/invokeutil.cpp | 27 +- src/coreclr/vm/invokeutil.h | 4 +- src/coreclr/vm/object.h | 1 + src/coreclr/vm/reflectioninvocation.cpp | 179 ++++-- src/coreclr/vm/runtimehandles.h | 7 +- .../src/Resources/Strings.resx | 3 + .../System.Private.CoreLib.Shared.projitems | 1 + .../src/System/Reflection/FieldAccessor.cs | 508 ++++++++++++++++++ .../System.Reflection.Tests/FieldInfoTests.cs | 293 +++++++++- .../src/System/Reflection/RuntimeFieldInfo.cs | 4 +- 18 files changed, 975 insertions(+), 249 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Reflection/FieldAccessor.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs index 6a8aaf6898b834..9bd2298eb9357a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs @@ -18,46 +18,18 @@ internal sealed unsafe class RtFieldInfo : RuntimeFieldInfo, IRuntimeFieldInfo // lazy caching private string? m_name; private RuntimeType? m_fieldType; - private InvocationFlags m_invocationFlags; - internal InvocationFlags InvocationFlags - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (m_invocationFlags & InvocationFlags.Initialized) != 0 ? - m_invocationFlags : InitializeInvocationFlags(); - } + private FieldAccessor? m_fieldAccessor; - [MethodImpl(MethodImplOptions.NoInlining)] - private InvocationFlags InitializeInvocationFlags() + internal FieldAccessor FieldAccessor { - Type? declaringType = DeclaringType; - - InvocationFlags invocationFlags = 0; - - // first take care of all the NO_INVOKE cases - if (declaringType != null && declaringType.ContainsGenericParameters) - { - invocationFlags |= InvocationFlags.NoInvoke; - } - - // If the invocationFlags are still 0, then - // this should be an usable field, determine the other flags - if (invocationFlags == 0) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - if ((m_fieldAttributes & FieldAttributes.InitOnly) != 0) - invocationFlags |= InvocationFlags.SpecialField; - - if ((m_fieldAttributes & FieldAttributes.HasFieldRVA) != 0) - invocationFlags |= InvocationFlags.SpecialField; - - // find out if the field type is one of the following: Primitive, Enum or Pointer - Type fieldType = FieldType; - if (fieldType.IsPointer || fieldType.IsEnum || fieldType.IsPrimitive) - invocationFlags |= InvocationFlags.FieldSpecialCast; + m_fieldAccessor ??= new FieldAccessor(this); + return m_fieldAccessor; } - - // must be last to avoid threading problems - return m_invocationFlags = invocationFlags | InvocationFlags.Initialized; } + #endregion #region Constructor @@ -75,28 +47,6 @@ internal RtFieldInfo( #endregion #region Internal Members - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CheckConsistency(object? target) - { - // only test instance fields - if ((m_fieldAttributes & FieldAttributes.Static) != FieldAttributes.Static) - { - if (!m_declaringType.IsInstanceOfType(target)) - { - if (target == null) - { - throw new TargetException(SR.RFLCT_Targ_StatFldReqTarg); - } - else - { - throw new ArgumentException( - SR.Format(SR.Arg_FieldDeclTarget, - Name, m_declaringType, target.GetType())); - } - } - } - } - internal override bool CacheEquals(object? o) { return o is RtFieldInfo m && m.m_fieldHandle == m_fieldHandle; @@ -131,36 +81,7 @@ public override int GetHashCode() => #region FieldInfo Overrides [DebuggerStepThrough] [DebuggerHidden] - public override object? GetValue(object? obj) - { - InvocationFlags invocationFlags = InvocationFlags; - RuntimeType? declaringType = DeclaringType as RuntimeType; - - if ((invocationFlags & InvocationFlags.NoInvoke) != 0) - { - if (declaringType != null && DeclaringType!.ContainsGenericParameters) - throw new InvalidOperationException(SR.Arg_UnboundGenField); - - throw new FieldAccessException(); - } - - CheckConsistency(obj); - - RuntimeType fieldType = (RuntimeType)FieldType; - - bool domainInitialized = false; - if (declaringType == null) - { - return RuntimeFieldHandle.GetValue(this, obj, fieldType, null, ref domainInitialized); - } - else - { - domainInitialized = declaringType.DomainInitialized; - object? retVal = RuntimeFieldHandle.GetValue(this, obj, fieldType, declaringType, ref domainInitialized); - declaringType.DomainInitialized = domainInitialized; - return retVal; - } - } + public override object? GetValue(object? obj) => FieldAccessor.GetValue(obj); public override object GetRawConstantValue() { throw new InvalidOperationException(); } @@ -180,45 +101,7 @@ public override int GetHashCode() => [DebuggerStepThrough] [DebuggerHidden] public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) - { - InvocationFlags invocationFlags = InvocationFlags; - RuntimeType? declaringType = DeclaringType as RuntimeType; - - if ((invocationFlags & InvocationFlags.NoInvoke) != 0) - { - if (declaringType != null && declaringType.ContainsGenericParameters) - throw new InvalidOperationException(SR.Arg_UnboundGenField); - - throw new FieldAccessException(); - } - - CheckConsistency(obj); - - RuntimeType fieldType = (RuntimeType)FieldType; - if (value is null) - { - if (fieldType.IsActualValueType) - { - fieldType.CheckValue(ref value, binder, culture, invokeAttr); - } - } - else if (!ReferenceEquals(value.GetType(), fieldType)) - { - fieldType.CheckValue(ref value, binder, culture, invokeAttr); - } - - bool domainInitialized = false; - if (declaringType is null) - { - RuntimeFieldHandle.SetValue(this, obj, value, fieldType, m_fieldAttributes, null, ref domainInitialized); - } - else - { - domainInitialized = declaringType.DomainInitialized; - RuntimeFieldHandle.SetValue(this, obj, value, fieldType, m_fieldAttributes, declaringType, ref domainInitialized); - declaringType.DomainInitialized = domainInitialized; - } - } + => FieldAccessor.SetValue(obj, value, invokeAttr, binder, culture); [DebuggerStepThrough] [DebuggerHidden] diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs index dab1d07145dbd9..a314edaab2a15f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs @@ -11,7 +11,7 @@ internal abstract class RuntimeFieldInfo : FieldInfo #region Private Data Members private readonly BindingFlags m_bindingFlags; protected readonly RuntimeTypeCache m_reflectedTypeCache; - protected readonly RuntimeType m_declaringType; + protected internal readonly RuntimeType m_declaringType; #endregion #region Constructor diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 73b9bb167f7a7f..244312cf59809c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1087,6 +1087,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa private object? m_d; private int m_b; private object? m_e; + private object? m_f; private RuntimeFieldHandleInternal m_fieldHandle; #pragma warning restore 414, 169, IDE0044 @@ -1189,17 +1190,26 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field) return type; } + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool IsFastPathSupported(RtFieldInfo field); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern int GetInstanceFieldOffset(RtFieldInfo field); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int GetToken(RtFieldInfo field); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object? GetValue(RtFieldInfo field, object? instance, RuntimeType fieldType, RuntimeType? declaringType, ref bool domainInitialized); + internal static extern object? GetValue(RtFieldInfo field, object? instance, RuntimeType fieldType, RuntimeType? declaringType, ref bool isClassInitialized); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object? GetValueDirect(RtFieldInfo field, RuntimeType fieldType, void* pTypedRef, RuntimeType? contextType); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void SetValue(RtFieldInfo field, object? obj, object? value, RuntimeType fieldType, FieldAttributes fieldAttr, RuntimeType? declaringType, ref bool domainInitialized); + internal static extern void SetValue(RtFieldInfo field, object? obj, object? value, RuntimeType fieldType, RuntimeType? declaringType, ref bool isClassInitialized); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void SetValueDirect(RtFieldInfo field, RuntimeType fieldType, void* pTypedRef, object? value, RuntimeType? contextType); diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 1329f1f7140b43..c74d76388b91a9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -1438,7 +1438,6 @@ internal T[] GetMemberList(MemberListType listType, string? name, CacheType cach private string? m_toString; private string? m_namespace; private readonly bool m_isGlobal; - private bool m_bIsDomainInitialized; private MemberInfoCache? m_methodInfoCache; private MemberInfoCache? m_constructorInfoCache; private MemberInfoCache? m_fieldInfoCache; @@ -1523,12 +1522,6 @@ internal Type[] FunctionPointerReturnAndParameterTypes } } - internal bool DomainInitialized - { - get => m_bIsDomainInitialized; - set => m_bIsDomainInitialized = value; - } - internal string? GetName(TypeNameKind kind) { switch (kind) @@ -1935,12 +1928,6 @@ internal object? GenericCache set => Cache.GenericCache = value; } - internal bool DomainInitialized - { - get => Cache.DomainInitialized; - set => Cache.DomainInitialized = value; - } - internal static FieldInfo GetFieldInfo(IRuntimeFieldInfo fieldHandle) { return GetFieldInfo(RuntimeFieldHandle.GetApproxDeclaringType(fieldHandle), fieldHandle); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index ffc2badf3e802d..bd4a2090166522 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -337,13 +337,13 @@ DEFINE_FIELD(RT_TYPE_HANDLE, M_TYPE, m_type) DEFINE_CLASS(TYPE_NAME_PARSER, Reflection, TypeNameParser) DEFINE_METHOD(TYPE_NAME_PARSER, GET_TYPE_HELPER, GetTypeHelper, SM_Type_CharPtr_RuntimeAssembly_Bool_Bool_RetRuntimeType) -DEFINE_CLASS_U(Reflection, RtFieldInfo, NoClass) -DEFINE_FIELD_U(m_fieldHandle, ReflectFieldObject, m_pFD) +DEFINE_CLASS_U(Reflection, RtFieldInfo, NoClass) +DEFINE_FIELD_U(m_fieldHandle, ReflectFieldObject, m_pFD) DEFINE_CLASS(RT_FIELD_INFO, Reflection, RtFieldInfo) DEFINE_FIELD(RT_FIELD_INFO, HANDLE, m_fieldHandle) -DEFINE_CLASS_U(System, RuntimeFieldInfoStub, ReflectFieldObject) -DEFINE_FIELD_U(m_fieldHandle, ReflectFieldObject, m_pFD) +DEFINE_CLASS_U(System, RuntimeFieldInfoStub, ReflectFieldObject) +DEFINE_FIELD_U(m_fieldHandle, ReflectFieldObject, m_pFD) DEFINE_CLASS(STUBFIELDINFO, System, RuntimeFieldInfoStub) #if FOR_ILLINK DEFINE_METHOD(STUBFIELDINFO, CTOR, .ctor, IM_RetVoid) diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 1ff1218e42944d..1af6a5055e6c33 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -242,6 +242,9 @@ FCFuncStart(gCOMFieldHandleNewFuncs) FCFuncElement("GetStaticFieldForGenericType", RuntimeFieldHandle::GetStaticFieldForGenericType) FCFuncElement("AcquiresContextFromThis", RuntimeFieldHandle::AcquiresContextFromThis) FCFuncElement("GetLoaderAllocator", RuntimeFieldHandle::GetLoaderAllocator) + FCFuncElement("IsFastPathSupported", RuntimeFieldHandle::IsFastPathSupported) + FCFuncElement("GetInstanceFieldOffset", RuntimeFieldHandle::GetInstanceFieldOffset) + FCFuncElement("GetStaticFieldAddress", RuntimeFieldHandle::GetStaticFieldAddress) FCFuncEnd() FCFuncStart(gCOMModuleHandleFuncs) diff --git a/src/coreclr/vm/field.cpp b/src/coreclr/vm/field.cpp index b2973d8b4c66fd..c2eab291cc42b0 100644 --- a/src/coreclr/vm/field.cpp +++ b/src/coreclr/vm/field.cpp @@ -180,8 +180,8 @@ void* FieldDesc::GetStaticAddress(void *base) void* ret = GetStaticAddressHandle(base); // Get the handle - // For value classes, the handle points at an OBJECTREF - // which holds the boxed value class, so dereference and unbox. + // For value classes, the handle points at an OBJECTREF + // which holds the boxed value class, so dereference and unbox. if (GetFieldType() == ELEMENT_TYPE_VALUETYPE && !IsRVA()) { OBJECTREF obj = ObjectToOBJECTREF(*(Object**) ret); @@ -211,11 +211,10 @@ MethodTable * FieldDesc::GetExactDeclaringType(MethodTable * ownerOrSubType) #endif // #ifndef DACCESS_COMPILE - // static value classes are actually stored in their boxed form. - // this means that their address moves. +// Static value classes are actually stored in their boxed form. +// This means that their address moves. PTR_VOID FieldDesc::GetStaticAddressHandle(PTR_VOID base) { - CONTRACTL { INSTANCE_CHECK; @@ -255,7 +254,6 @@ PTR_VOID FieldDesc::GetStaticAddressHandle(PTR_VOID base) } #endif // FEATURE_METADATA_UPDATER - if (IsRVA()) { Module* pModule = GetModule(); @@ -270,12 +268,10 @@ PTR_VOID FieldDesc::GetStaticAddressHandle(PTR_VOID base) PTR_VOID ret = PTR_VOID(dac_cast(base) + GetOffset()); - return ret; } - // These routines encapsulate the operation of getting and setting // fields. void FieldDesc::GetInstanceField(OBJECTREF o, VOID * pOutVal) diff --git a/src/coreclr/vm/field.h b/src/coreclr/vm/field.h index e2324787febaf5..c37fa4244dadf9 100644 --- a/src/coreclr/vm/field.h +++ b/src/coreclr/vm/field.h @@ -285,6 +285,14 @@ class FieldDesc SetOffset(FIELD_OFFSET_NEW_ENC); } + BOOL IsCollectible() + { + LIMITED_METHOD_DAC_CONTRACT; + + LoaderAllocator *pLoaderAllocator = GetApproxEnclosingMethodTable()->GetLoaderAllocator(); + return pLoaderAllocator->IsCollectible(); + } + // Was this field added by EnC? // If this is true, then this object is an instance of EnCFieldDesc BOOL IsEnCNew() @@ -518,7 +526,7 @@ class FieldDesc } } - VOID CheckRunClassInitThrowing() + void CheckRunClassInitThrowing() { CONTRACTL { diff --git a/src/coreclr/vm/invokeutil.cpp b/src/coreclr/vm/invokeutil.cpp index eb8462ed16f245..7d0c8f80becdb7 100644 --- a/src/coreclr/vm/invokeutil.cpp +++ b/src/coreclr/vm/invokeutil.cpp @@ -741,14 +741,14 @@ void InvokeUtil::ValidateObjectTarget(FieldDesc *pField, TypeHandle enclosingTyp // SetValidField // Given an target object, a value object and a field this method will set the field -// on the target object. The field must be validate before calling this. +// on the target object. The field must be validated before calling this. void InvokeUtil::SetValidField(CorElementType fldType, TypeHandle fldTH, FieldDesc *pField, OBJECTREF *target, OBJECTREF *valueObj, TypeHandle declaringType, - CLR_BOOL *pDomainInitialized) { + CLR_BOOL *pIsClassInitialized) { CONTRACTL { THROWS; GC_TRIGGERS; @@ -786,19 +786,18 @@ void InvokeUtil::SetValidField(CorElementType fldType, pDeclMT = pField->GetModule()->GetGlobalMethodTable(); } - if (*pDomainInitialized == FALSE) + if (*pIsClassInitialized == FALSE) { EX_TRY { pDeclMT->EnsureInstanceActive(); pDeclMT->CheckRunClassInitThrowing(); - - *pDomainInitialized = TRUE; + *pIsClassInitialized = pDeclMT->IsClassInited(); } EX_CATCH_THROWABLE(&Throwable); } #ifdef _DEBUG - else if (*pDomainInitialized == TRUE && !declaringType.IsNull()) + else if (*pIsClassInitialized == TRUE && !declaringType.IsNull()) CONSISTENCY_CHECK(declaringType.GetMethodTable()->CheckActivated()); #endif @@ -973,9 +972,7 @@ void InvokeUtil::SetValidField(CorElementType fldType, // GetFieldValue // This method will return an ARG_SLOT containing the value of the field. -// GetFieldValue -// This method will return an ARG_SLOT containing the value of the field. -OBJECTREF InvokeUtil::GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJECTREF* target, TypeHandle declaringType, CLR_BOOL *pDomainInitialized) { +OBJECTREF InvokeUtil::GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJECTREF* target, TypeHandle declaringType, CLR_BOOL *pIsClassInitialized) { CONTRACTL { THROWS; GC_TRIGGERS; @@ -999,7 +996,7 @@ OBJECTREF InvokeUtil::GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJ { pDeclMT = declaringType.GetMethodTable(); - // We don't allow getting the field just so we don't have more specical + // We don't allow getting the field just so we don't have more special // cases than we need to. Then we need at least the throw check to ensure // we don't allow data corruption. if (Nullable::IsNullableType(pDeclMT)) @@ -1013,23 +1010,21 @@ OBJECTREF InvokeUtil::GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJ pDeclMT = pField->GetModule()->GetGlobalMethodTable(); } - if (*pDomainInitialized == FALSE) + if (*pIsClassInitialized == FALSE) { EX_TRY { pDeclMT->EnsureInstanceActive(); pDeclMT->CheckRunClassInitThrowing(); - - *pDomainInitialized = TRUE; + *pIsClassInitialized = pDeclMT->IsClassInited(); } EX_CATCH_THROWABLE(&Throwable); } #ifdef _DEBUG - else if (*pDomainInitialized == TRUE && !declaringType.IsNull()) + else if (*pIsClassInitialized == TRUE && !declaringType.IsNull()) CONSISTENCY_CHECK(declaringType.GetMethodTable()->CheckActivated()); #endif - if(Throwable != NULL) { GCPROTECT_BEGIN(Throwable); @@ -1084,7 +1079,7 @@ OBJECTREF InvokeUtil::GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJ case ELEMENT_TYPE_VALUETYPE: { - // Value classes require createing a boxed version of the field and then + // Value classes require creating a boxed version of the field and then // copying from the source... // Allocate an object to return... _ASSERTE(!fieldType.IsTypeDesc()); diff --git a/src/coreclr/vm/invokeutil.h b/src/coreclr/vm/invokeutil.h index 0bd1577c7a19d3..b288c475aae07e 100644 --- a/src/coreclr/vm/invokeutil.h +++ b/src/coreclr/vm/invokeutil.h @@ -138,9 +138,9 @@ class InvokeUtil // SetValidField // Given an target object, a value object and a field this method will set the field // on the target object. The field must be validate before calling this. - static void SetValidField(CorElementType fldType, TypeHandle fldTH, FieldDesc* pField, OBJECTREF* target, OBJECTREF* value, TypeHandle declaringType, CLR_BOOL *pDomainInitialized); + static void SetValidField(CorElementType fldType, TypeHandle fldTH, FieldDesc* pField, OBJECTREF* target, OBJECTREF* value, TypeHandle declaringType, CLR_BOOL *pIsClassInitialized); - static OBJECTREF GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJECTREF* target, TypeHandle declaringType, CLR_BOOL *pDomainInitialized); + static OBJECTREF GetFieldValue(FieldDesc* pField, TypeHandle fieldType, OBJECTREF* target, TypeHandle declaringType, CLR_BOOL *pIsClassInitialized); // ValidateObjectTarget // This method will validate the Object/Target relationship diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 91bbc95304ce1e..915e45deca0636 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1130,6 +1130,7 @@ class ReflectFieldObject : public BaseObjectWithCachedData INT32 m_empty2; OBJECTREF m_empty3; OBJECTREF m_empty4; + OBJECTREF m_empty5; FieldDesc * m_pFD; public: diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index e12ade403c6b5c..fb5cd978b85fdf 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -25,8 +25,9 @@ #include "dbginterface.h" #include "argdestination.h" -FCIMPL5(Object*, RuntimeFieldHandle::GetValue, ReflectFieldObject *pFieldUNSAFE, Object *instanceUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, ReflectClassBaseObject *pDeclaringTypeUNSAFE, CLR_BOOL *pDomainInitialized) { - CONTRACTL { +FCIMPL5(Object*, RuntimeFieldHandle::GetValue, ReflectFieldObject *pFieldUNSAFE, Object *instanceUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, ReflectClassBaseObject *pDeclaringTypeUNSAFE, CLR_BOOL *pIsClassInitialized) { + CONTRACTL + { FCALL_CHECK; } CONTRACTL_END; @@ -50,22 +51,11 @@ FCIMPL5(Object*, RuntimeFieldHandle::GetValue, ReflectFieldObject *pFieldUNSAFE, TypeHandle fieldType = gc.pFieldType->GetType(); TypeHandle declaringType = (gc.pDeclaringType != NULL) ? gc.pDeclaringType->GetType() : TypeHandle(); - Assembly *pAssem; - if (declaringType.IsNull()) - { - // global field - pAssem = gc.refField->GetField()->GetModule()->GetAssembly(); - } - else - { - pAssem = declaringType.GetAssembly(); - } - OBJECTREF rv = NULL; // not protected HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); // There can be no GC after this until the Object is returned. - rv = InvokeUtil::GetFieldValue(gc.refField->GetField(), fieldType, &gc.target, declaringType, pDomainInitialized); + rv = InvokeUtil::GetFieldValue(gc.refField->GetField(), fieldType, &gc.target, declaringType, pIsClassInitialized); HELPER_METHOD_FRAME_END(); return OBJECTREFToObject(rv); @@ -73,7 +63,8 @@ FCIMPL5(Object*, RuntimeFieldHandle::GetValue, ReflectFieldObject *pFieldUNSAFE, FCIMPLEND FCIMPL2(FC_BOOL_RET, ReflectionInvocation::CanValueSpecialCast, ReflectClassBaseObject *pValueTypeUNSAFE, ReflectClassBaseObject *pTargetTypeUNSAFE) { - CONTRACTL { + CONTRACTL + { FCALL_CHECK; PRECONDITION(CheckPointer(pValueTypeUNSAFE)); PRECONDITION(CheckPointer(pTargetTypeUNSAFE)); @@ -126,7 +117,8 @@ FCIMPLEND /// Allocate the value type and copy the optional value into it. /// FCIMPL2(Object*, ReflectionInvocation::AllocateValueType, ReflectClassBaseObject *pTargetTypeUNSAFE, Object *valueUNSAFE) { - CONTRACTL { + CONTRACTL + { FCALL_CHECK; PRECONDITION(CheckPointer(pTargetTypeUNSAFE)); PRECONDITION(CheckPointer(valueUNSAFE, NULL_OK)); @@ -169,8 +161,9 @@ FCIMPL2(Object*, ReflectionInvocation::AllocateValueType, ReflectClassBaseObject } FCIMPLEND -FCIMPL7(void, RuntimeFieldHandle::SetValue, ReflectFieldObject *pFieldUNSAFE, Object *targetUNSAFE, Object *valueUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, DWORD attr, ReflectClassBaseObject *pDeclaringTypeUNSAFE, CLR_BOOL *pDomainInitialized) { - CONTRACTL { +FCIMPL6(void, RuntimeFieldHandle::SetValue, ReflectFieldObject *pFieldUNSAFE, Object *targetUNSAFE, Object *valueUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, ReflectClassBaseObject *pDeclaringTypeUNSAFE, CLR_BOOL *pIsClassInitialized) { + CONTRACTL + { FCALL_CHECK; } CONTRACTL_END; @@ -195,24 +188,13 @@ FCIMPL7(void, RuntimeFieldHandle::SetValue, ReflectFieldObject *pFieldUNSAFE, Ob TypeHandle fieldType = gc.fieldType->GetType(); TypeHandle declaringType = gc.declaringType != NULL ? gc.declaringType->GetType() : TypeHandle(); - Assembly *pAssem; - if (declaringType.IsNull()) - { - // global field - pAssem = gc.refField->GetField()->GetModule()->GetAssembly(); - } - else - { - pAssem = declaringType.GetAssembly(); - } - FC_GC_POLL_NOT_NEEDED(); FieldDesc* pFieldDesc = gc.refField->GetField(); HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); - InvokeUtil::SetValidField(fieldType.GetVerifierCorElementType(), fieldType, pFieldDesc, &gc.target, &gc.value, declaringType, pDomainInitialized); + InvokeUtil::SetValidField(fieldType.GetVerifierCorElementType(), fieldType, pFieldDesc, &gc.target, &gc.value, declaringType, pIsClassInitialized); HELPER_METHOD_FRAME_END(); } @@ -225,7 +207,8 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_CreateInstanceForAnotherGenericParam QCall::ObjectHandleOnStack pInstantiatedObject ) { - CONTRACTL{ + CONTRACTL + { QCALL_CHECK; PRECONDITION(!pTypeHandle.AsTypeHandle().IsNull()); PRECONDITION(cInstArray >= 0); @@ -310,7 +293,8 @@ FCIMPLEND static OBJECTREF InvokeArrayConstructor(TypeHandle th, PVOID* args, int argCnt) { - CONTRACTL { + CONTRACTL + { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; @@ -344,7 +328,8 @@ static OBJECTREF InvokeArrayConstructor(TypeHandle th, PVOID* args, int argCnt) static BOOL IsActivationNeededForMethodInvoke(MethodDesc * pMD) { - CONTRACTL { + CONTRACTL + { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; @@ -875,7 +860,8 @@ struct SkipStruct { // This method is called by the GetMethod function and will crawl backward // up the stack for integer methods. static StackWalkAction SkipMethods(CrawlFrame* frame, VOID* data) { - CONTRACTL { + CONTRACTL + { NOTHROW; GC_NOTRIGGER; MODE_ANY; @@ -936,8 +922,9 @@ FCIMPL1(ReflectMethodObject*, RuntimeMethodHandle::GetCurrentMethod, StackCrawlM } FCIMPLEND -static OBJECTREF DirectObjectFieldGet(FieldDesc *pField, TypeHandle fieldType, TypeHandle enclosingType, TypedByRef *pTarget, CLR_BOOL *pDomainInitialized) { - CONTRACTL { +static OBJECTREF DirectObjectFieldGet(FieldDesc *pField, TypeHandle fieldType, TypeHandle enclosingType, TypedByRef *pTarget, CLR_BOOL *pIsClassInitialized) { + CONTRACTL + { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; @@ -954,13 +941,14 @@ static OBJECTREF DirectObjectFieldGet(FieldDesc *pField, TypeHandle fieldType, T } InvokeUtil::ValidateObjectTarget(pField, enclosingType, &objref); - refRet = InvokeUtil::GetFieldValue(pField, fieldType, &objref, enclosingType, pDomainInitialized); + refRet = InvokeUtil::GetFieldValue(pField, fieldType, &objref, enclosingType, pIsClassInitialized); GCPROTECT_END(); return refRet; } FCIMPL4(Object*, RuntimeFieldHandle::GetValueDirect, ReflectFieldObject *pFieldUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, TypedByRef *pTarget, ReflectClassBaseObject *pDeclaringTypeUNSAFE) { - CONTRACTL { + CONTRACTL + { FCALL_CHECK; } CONTRACTL_END; @@ -994,9 +982,9 @@ FCIMPL4(Object*, RuntimeFieldHandle::GetValueDirect, ReflectFieldObject *pFieldU _ASSERTE(gc.refDeclaringType == NULL || !gc.refDeclaringType->GetType().IsTypeDesc()); MethodTable *pEnclosingMT = (gc.refDeclaringType != NULL ? gc.refDeclaringType->GetType() : TypeHandle()).AsMethodTable(); - CLR_BOOL domainInitialized = FALSE; + CLR_BOOL isClassInitialized = FALSE; if (pField->IsStatic() || !targetType.IsValueType()) { - refRet = DirectObjectFieldGet(pField, fieldType, TypeHandle(pEnclosingMT), pTarget, &domainInitialized); + refRet = DirectObjectFieldGet(pField, fieldType, TypeHandle(pEnclosingMT), pTarget, &isClassInitialized); goto lExit; } @@ -1059,8 +1047,9 @@ lExit: ; } FCIMPLEND -static void DirectObjectFieldSet(FieldDesc *pField, TypeHandle fieldType, TypeHandle enclosingType, TypedByRef *pTarget, OBJECTREF *pValue, CLR_BOOL *pDomainInitialized) { - CONTRACTL { +static void DirectObjectFieldSet(FieldDesc *pField, TypeHandle fieldType, TypeHandle enclosingType, TypedByRef *pTarget, OBJECTREF *pValue, CLR_BOOL *pIsClassInitialized) { + CONTRACTL + { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; @@ -1078,12 +1067,13 @@ static void DirectObjectFieldSet(FieldDesc *pField, TypeHandle fieldType, TypeHa // Validate the target/fld type relationship InvokeUtil::ValidateObjectTarget(pField, enclosingType, &objref); - InvokeUtil::SetValidField(pField->GetFieldType(), fieldType, pField, &objref, pValue, enclosingType, pDomainInitialized); + InvokeUtil::SetValidField(pField->GetFieldType(), fieldType, pField, &objref, pValue, enclosingType, pIsClassInitialized); GCPROTECT_END(); } FCIMPL5(void, RuntimeFieldHandle::SetValueDirect, ReflectFieldObject *pFieldUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, TypedByRef *pTarget, Object *valueUNSAFE, ReflectClassBaseObject *pContextTypeUNSAFE) { - CONTRACTL { + CONTRACTL + { FCALL_CHECK; } CONTRACTL_END; @@ -1124,9 +1114,9 @@ FCIMPL5(void, RuntimeFieldHandle::SetValueDirect, ReflectFieldObject *pFieldUNSA // Verify that the value passed can be widened into the target InvokeUtil::ValidField(fieldType, &gc.oValue); - CLR_BOOL domainInitialized = FALSE; + CLR_BOOL isClassInitialized = FALSE; if (pField->IsStatic() || !targetType.IsValueType()) { - DirectObjectFieldSet(pField, fieldType, TypeHandle(pEnclosingMT), pTarget, &gc.oValue, &domainInitialized); + DirectObjectFieldSet(pField, fieldType, TypeHandle(pEnclosingMT), pTarget, &gc.oValue, &isClassInitialized); goto lExit; } @@ -1243,6 +1233,85 @@ lExit: ; } FCIMPLEND +static bool IsFastPathSupportedHelper(FieldDesc* pFieldDesc) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(CheckPointer(pFieldDesc)); + } + CONTRACTL_END; + + return !pFieldDesc->IsThreadStatic() && + !pFieldDesc->IsEnCNew() && + !(pFieldDesc->IsCollectible() && pFieldDesc->IsStatic()); +} + +FCIMPL1(FC_BOOL_RET, RuntimeFieldHandle::IsFastPathSupported, ReflectFieldObject *pFieldUNSAFE) +{ + FCALL_CONTRACT; + + REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE); + _ASSERTE(refField != NULL); + + FieldDesc* pFieldDesc = refField->GetField(); + return IsFastPathSupportedHelper(pFieldDesc) ? TRUE : FALSE; +} +FCIMPLEND + +FCIMPL1(INT32, RuntimeFieldHandle::GetInstanceFieldOffset, ReflectFieldObject *pFieldUNSAFE) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(CheckPointer(pFieldUNSAFE)); + } + CONTRACTL_END; + + REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE); + _ASSERTE(refField != NULL); + + FieldDesc* pFieldDesc = refField->GetField(); + _ASSERTE(!pFieldDesc->IsStatic()); + + // IsFastPathSupported needs to checked before calling this method. + _ASSERTE(IsFastPathSupportedHelper(pFieldDesc)); + + return pFieldDesc->GetOffset(); +} +FCIMPLEND + +FCIMPL1(void*, RuntimeFieldHandle::GetStaticFieldAddress, ReflectFieldObject *pFieldUNSAFE) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(CheckPointer(pFieldUNSAFE)); + } + CONTRACTL_END; + + REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE); + _ASSERTE(refField != NULL); + + FieldDesc* pFieldDesc = refField->GetField(); + _ASSERTE(pFieldDesc->IsStatic()); + + // IsFastPathSupported needs to checked before calling this method. + _ASSERTE(IsFastPathSupportedHelper(pFieldDesc)); + + PTR_BYTE base = 0; + if (!pFieldDesc->IsRVA()) + { + // For RVA the base is ignored and offset is used. + base = pFieldDesc->GetBase(); + } + + return PTR_VOID(base + pFieldDesc->GetOffset()); +} +FCIMPLEND + extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD) { QCALL_CONTRACT; @@ -1313,7 +1382,8 @@ static void PrepareMethodHelper(MethodDesc * pMD) // It does not walk a subset of callgraph to provide CER guarantees. extern "C" void QCALLTYPE ReflectionInvocation_PrepareMethod(MethodDesc *pMD, TypeHandle *pInstantiation, UINT32 cInstantiation) { - CONTRACTL { + CONTRACTL + { QCALL_CHECK; PRECONDITION(pMD != NULL); PRECONDITION(CheckPointer(pInstantiation, NULL_OK)); @@ -1366,7 +1436,8 @@ extern "C" void QCALLTYPE ReflectionInvocation_PrepareMethod(MethodDesc *pMD, Ty // was prepared prior to the Combine. FCIMPL1(void, ReflectionInvocation::PrepareDelegate, Object* delegateUNSAFE) { - CONTRACTL { + CONTRACTL + { FCALL_CHECK; PRECONDITION(CheckPointer(delegateUNSAFE, NULL_OK)); } @@ -1427,7 +1498,8 @@ FCIMPLEND FCIMPL4(void, ReflectionInvocation::MakeTypedReference, TypedByRef * value, Object* targetUNSAFE, ArrayBase* fldsUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE) { - CONTRACTL { + CONTRACTL + { FCALL_CHECK; PRECONDITION(CheckPointer(targetUNSAFE)); PRECONDITION(CheckPointer(fldsUNSAFE)); @@ -1699,7 +1771,8 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo( BOOL* pfCtorIsPublic ) { - CONTRACTL{ + CONTRACTL + { QCALL_CHECK; PRECONDITION(CheckPointer(ppfnAllocator)); PRECONDITION(CheckPointer(pvAllocatorFirstArg)); @@ -1818,7 +1891,8 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo( FCIMPL1(Object*, RuntimeTypeHandle::AllocateComObject, void* pClassFactory) { - CONTRACTL{ + CONTRACTL + { FCALL_CHECK; PRECONDITION(CheckPointer(pClassFactory)); } @@ -1867,7 +1941,8 @@ extern "C" void QCALLTYPE ReflectionSerialization_GetCreateUninitializedObjectIn PCODE* ppfnAllocator, void** pvAllocatorFirstArg) { - CONTRACTL{ + CONTRACTL + { QCALL_CHECK; PRECONDITION(CheckPointer(ppfnAllocator)); PRECONDITION(CheckPointer(pvAllocatorFirstArg)); diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index 694a25a30623da..ec7864ad2bc499 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -292,10 +292,13 @@ extern "C" void QCALLTYPE RuntimeMethodHandle_Destroy(MethodDesc * pMethod); class RuntimeFieldHandle { public: - static FCDECL5(Object*, GetValue, ReflectFieldObject *pFieldUNSAFE, Object *instanceUNSAFE, ReflectClassBaseObject *pFieldType, ReflectClassBaseObject *pDeclaringType, CLR_BOOL *pDomainInitialized); - static FCDECL7(void, SetValue, ReflectFieldObject *pFieldUNSAFE, Object *targetUNSAFE, Object *valueUNSAFE, ReflectClassBaseObject *pFieldType, DWORD attr, ReflectClassBaseObject *pDeclaringType, CLR_BOOL *pDomainInitialized); + static FCDECL5(Object*, GetValue, ReflectFieldObject *pFieldUNSAFE, Object *instanceUNSAFE, ReflectClassBaseObject *pFieldType, ReflectClassBaseObject *pDeclaringType, CLR_BOOL *pIsClassInitialized); + static FCDECL6(void, SetValue, ReflectFieldObject *pFieldUNSAFE, Object *targetUNSAFE, Object *valueUNSAFE, ReflectClassBaseObject *pFieldType, ReflectClassBaseObject *pDeclaringType, CLR_BOOL *pIsClassInitialized); static FCDECL4(Object*, GetValueDirect, ReflectFieldObject *pFieldUNSAFE, ReflectClassBaseObject *pFieldType, TypedByRef *pTarget, ReflectClassBaseObject *pDeclaringType); static FCDECL5(void, SetValueDirect, ReflectFieldObject *pFieldUNSAFE, ReflectClassBaseObject *pFieldType, TypedByRef *pTarget, Object *valueUNSAFE, ReflectClassBaseObject *pContextType); + static FCDECL1(FC_BOOL_RET, IsFastPathSupported, ReflectFieldObject *pField); + static FCDECL1(INT32, GetInstanceFieldOffset, ReflectFieldObject *pField); + static FCDECL1(void*, GetStaticFieldAddress, ReflectFieldObject *pField); static FCDECL1(StringObject*, GetName, ReflectFieldObject *pFieldUNSAFE); static FCDECL1(LPCUTF8, GetUtf8Name, FieldDesc *pField); diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index bf27c2f870db02..09348ec9d3ce43 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4298,6 +4298,9 @@ This operation is not available because the reflection support was disabled at compile time. + + Cannot set initonly static field '{0}' after type '{1}' is initialized. + This AssemblyBuilder instance doesn't support saving. Use AssemblyBuilder.DefinePersistedAssembly to create an AssemblyBuilder instance that supports saving. diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 94089d1018d8d1..c9e500d22af6b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -702,6 +702,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldAccessor.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldAccessor.cs new file mode 100644 index 00000000000000..2370d8fff263a7 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldAccessor.cs @@ -0,0 +1,508 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Reflection +{ + internal sealed class FieldAccessor + { + private readonly RtFieldInfo _fieldInfo; + private IntPtr _addressOrOffset; + private unsafe MethodTable* _methodTable; + private FieldAccessorType _fieldAccessType; + + internal FieldAccessor(FieldInfo fieldInfo) + { + _fieldInfo = (RtFieldInfo)fieldInfo; + Debug.Assert(_fieldInfo.m_declaringType != null); + + if (_fieldInfo.m_declaringType.ContainsGenericParameters || + _fieldInfo.m_declaringType.IsNullableOfT) + { + _fieldAccessType = FieldAccessorType.NoInvoke; + } + else + { + _fieldAccessType = FieldAccessorType.SlowPathUntilClassInitialized; + } + } + + private void Initialize() + { + if (!RuntimeFieldHandle.IsFastPathSupported(_fieldInfo)) + { + // Currently this is true for [ThreadStatic] cases, for fields added from EnC, and for fields on unloadable types. + _fieldAccessType = FieldAccessorType.SlowPath; + return; + } + + RuntimeType fieldType = (RuntimeType)_fieldInfo.FieldType; + + unsafe + { + if (_fieldInfo.IsStatic) + { + _addressOrOffset = RuntimeFieldHandle.GetStaticFieldAddress(_fieldInfo); + + if (fieldType.IsValueType) + { + if (fieldType.IsEnum) + { + _fieldAccessType = GetPrimitiveAccessorTypeForStatic(fieldType.GetEnumUnderlyingType()); + _methodTable = (MethodTable*)fieldType.TypeHandle.Value; + } + else if (RuntimeTypeHandle.GetCorElementType(fieldType) == CorElementType.ELEMENT_TYPE_VALUETYPE) + { + // The runtime stores non-primitive value types as a boxed value. + _fieldAccessType = FieldAccessorType.StaticValueTypeBoxed; + _methodTable = (MethodTable*)fieldType.TypeHandle.Value; + } + else + { + _fieldAccessType = GetPrimitiveAccessorTypeForStatic(fieldType); + _methodTable = (MethodTable*)fieldType.TypeHandle.Value; + } + } + else if (fieldType.IsPointer) + { + _fieldAccessType = FieldAccessorType.StaticPointerType; + } + else if (fieldType.IsFunctionPointer) + { + _fieldAccessType = GetIntPtrAccessorTypeForStatic(); + _methodTable = (MethodTable*)typeof(IntPtr).TypeHandle.Value; + } + else + { + _fieldAccessType = FieldAccessorType.StaticReferenceType; + } + } + else + { + _addressOrOffset = RuntimeFieldHandle.GetInstanceFieldOffset(_fieldInfo); + + if (fieldType.IsEnum) + { + _fieldAccessType = GetPrimitiveAccessorTypeForInstance(fieldType.GetEnumUnderlyingType()); + _methodTable = (MethodTable*)fieldType.TypeHandle.Value; + } + else if (fieldType.IsValueType) + { + _fieldAccessType = GetPrimitiveAccessorTypeForInstance(fieldType); + _methodTable = (MethodTable*)fieldType.TypeHandle.Value; + } + else if (fieldType.IsPointer) + { + _fieldAccessType = FieldAccessorType.InstancePointerType; + } + else if (fieldType.IsFunctionPointer) + { + _fieldAccessType = GetIntPtrAccessorTypeForInstance(); + _methodTable = (MethodTable*)typeof(IntPtr).TypeHandle.Value; + } + else + { + _fieldAccessType = FieldAccessorType.InstanceReferenceType; + } + } + } + } + + public object? GetValue(object? obj) + { + bool isClassInitialized; + + unsafe + { + switch (_fieldAccessType) + { + case FieldAccessorType.InstanceReferenceType: + VerifyTarget(obj); + Debug.Assert(obj != null); + return Volatile.Read(ref Unsafe.As(ref Unsafe.AddByteOffset(ref obj.GetRawData(), _addressOrOffset))); + + case FieldAccessorType.InstanceValueType: + case FieldAccessorType.InstanceValueTypeSize1: + case FieldAccessorType.InstanceValueTypeSize2: + case FieldAccessorType.InstanceValueTypeSize4: + case FieldAccessorType.InstanceValueTypeSize8: + VerifyTarget(obj); + Debug.Assert(obj != null); + return RuntimeHelpers.Box( + _methodTable, + ref Unsafe.AddByteOffset(ref obj.GetRawData(), _addressOrOffset)); + + case FieldAccessorType.InstancePointerType: + VerifyTarget(obj); + Debug.Assert(obj != null); + return Pointer.Box( + (void*)Unsafe.As(ref Unsafe.AddByteOffset(ref obj.GetRawData(), _addressOrOffset)), + _fieldInfo.FieldType); + + case FieldAccessorType.StaticReferenceType: + return Volatile.Read(ref Unsafe.As(ref *(IntPtr*)_addressOrOffset)); + + case FieldAccessorType.StaticValueType: + case FieldAccessorType.StaticValueTypeSize1: + case FieldAccessorType.StaticValueTypeSize2: + case FieldAccessorType.StaticValueTypeSize4: + case FieldAccessorType.StaticValueTypeSize8: + return RuntimeHelpers.Box(_methodTable, ref Unsafe.AsRef(_addressOrOffset.ToPointer())); + + case FieldAccessorType.StaticValueTypeBoxed: + // Re-box the value. + return RuntimeHelpers.Box( + _methodTable, + ref Unsafe.As(ref *(IntPtr*)_addressOrOffset).GetRawData()); + + case FieldAccessorType.StaticPointerType: + return Pointer.Box((void*)Unsafe.As( + ref Unsafe.AsRef(_addressOrOffset.ToPointer())), _fieldInfo.FieldType); + + case FieldAccessorType.SlowPathUntilClassInitialized: + if (!IsStatic()) + { + VerifyTarget(obj); + } + + isClassInitialized = false; + object? ret = RuntimeFieldHandle.GetValue(_fieldInfo, obj, (RuntimeType)_fieldInfo.FieldType, _fieldInfo.m_declaringType, ref isClassInitialized); + if (isClassInitialized) + { + Initialize(); + } + + return ret; + + case FieldAccessorType.SlowPath: + if (!IsStatic()) + { + VerifyTarget(obj); + } + + isClassInitialized = true; + return RuntimeFieldHandle.GetValue(_fieldInfo, obj, (RuntimeType)_fieldInfo.FieldType, _fieldInfo.m_declaringType, ref isClassInitialized); + + case FieldAccessorType.NoInvoke: + if (_fieldInfo.DeclaringType is not null && _fieldInfo.DeclaringType.ContainsGenericParameters) + throw new InvalidOperationException(SR.Arg_UnboundGenField); + + if (_fieldInfo.DeclaringType is not null && ((RuntimeType)_fieldInfo.FieldType).IsNullableOfT) + throw new NotSupportedException(); + + throw new FieldAccessException(); + + default: + Debug.Assert(false, "Unknown enum value"); + return null; + } + } + } + + public void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) + { + bool isClassInitialized; + + unsafe + { + switch (_fieldAccessType) + { + case FieldAccessorType.InstanceReferenceType: + VerifyInstanceField(obj, ref value, invokeAttr, binder, culture); + Debug.Assert(obj != null); + Volatile.Write( + ref Unsafe.As(ref Unsafe.AddByteOffset(ref obj.GetRawData(), _addressOrOffset)), + value); + return; + + case FieldAccessorType.InstanceValueTypeSize1: + VerifyInstanceField(obj, ref value, invokeAttr, binder, culture); + Debug.Assert(obj != null); + Volatile.Write( + ref Unsafe.AddByteOffset(ref obj.GetRawData(), _addressOrOffset), + value!.GetRawData()); + return; + + case FieldAccessorType.InstanceValueTypeSize2: + VerifyInstanceField(obj, ref value, invokeAttr, binder, culture); + Debug.Assert(obj != null); + Volatile.Write( + ref Unsafe.As(ref Unsafe.AddByteOffset(ref obj.GetRawData(), _addressOrOffset)), + Unsafe.As(ref value!.GetRawData())); + return; + + case FieldAccessorType.InstanceValueTypeSize4: + VerifyInstanceField(obj, ref value, invokeAttr, binder, culture); + Debug.Assert(obj != null); + Volatile.Write( + ref Unsafe.As(ref Unsafe.AddByteOffset(ref obj.GetRawData(), _addressOrOffset)), + Unsafe.As(ref value!.GetRawData())); + return; + + case FieldAccessorType.InstanceValueTypeSize8: + VerifyInstanceField(obj, ref value, invokeAttr, binder, culture); + Debug.Assert(obj != null); + Volatile.Write( + ref Unsafe.As(ref Unsafe.AddByteOffset(ref obj.GetRawData(), _addressOrOffset)), + Unsafe.As(ref value!.GetRawData())); + return; + + case FieldAccessorType.StaticReferenceType: + VerifyStaticField(ref value, invokeAttr, binder, culture); + Volatile.Write(ref Unsafe.As(ref *(IntPtr*)_addressOrOffset), value); + return; + + case FieldAccessorType.StaticValueTypeSize1: + VerifyStaticField(ref value, invokeAttr, binder, culture); + Volatile.Write( + ref Unsafe.AsRef(_addressOrOffset.ToPointer()), + value!.GetRawData()); + return; + + case FieldAccessorType.StaticValueTypeSize2: + VerifyStaticField(ref value, invokeAttr, binder, culture); + Volatile.Write( + ref Unsafe.AsRef(_addressOrOffset.ToPointer()), + Unsafe.As(ref value!.GetRawData())); + return; + + case FieldAccessorType.StaticValueTypeSize4: + VerifyStaticField(ref value, invokeAttr, binder, culture); + Volatile.Write( + ref Unsafe.AsRef(_addressOrOffset.ToPointer()), + Unsafe.As(ref value!.GetRawData())); + return; + + case FieldAccessorType.StaticValueTypeSize8: + VerifyStaticField(ref value, invokeAttr, binder, culture); + Volatile.Write( + ref Unsafe.AsRef(_addressOrOffset.ToPointer()), + Unsafe.As(ref value!.GetRawData())); + return; + + case FieldAccessorType.SlowPathUntilClassInitialized: + if (IsStatic()) + { + VerifyStaticField(ref value, invokeAttr, binder, culture); + } + else + { + VerifyInstanceField(obj, ref value, invokeAttr, binder, culture); + } + + isClassInitialized = false; + RuntimeFieldHandle.SetValue(_fieldInfo, obj, value, (RuntimeType)_fieldInfo.FieldType, _fieldInfo.m_declaringType, ref isClassInitialized); + if (isClassInitialized) + { + Initialize(); + } + + return; + + case FieldAccessorType.NoInvoke: + if (_fieldInfo.DeclaringType is not null && _fieldInfo.DeclaringType.ContainsGenericParameters) + throw new InvalidOperationException(SR.Arg_UnboundGenField); + + throw new FieldAccessException(); + } + } + + // All other cases use the slow path. + if (IsStatic()) + { + VerifyStaticField(ref value, invokeAttr, binder, culture); + } + else + { + VerifyInstanceField(obj, ref value, invokeAttr, binder, culture); + } + + isClassInitialized = true; + RuntimeFieldHandle.SetValue(_fieldInfo, obj, value, (RuntimeType)_fieldInfo.FieldType, _fieldInfo.m_declaringType, ref isClassInitialized); + } + + private bool IsStatic() => (_fieldInfo.Attributes & FieldAttributes.Static) == FieldAttributes.Static; + + private void VerifyStaticField(ref object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) + { + VerifyInitOnly(); + CheckValue(ref value, invokeAttr, binder, culture); + } + + private void VerifyInstanceField(object? obj, ref object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) + { + VerifyTarget(obj); + CheckValue(ref value, invokeAttr, binder, culture); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void VerifyTarget(object? target) + { + Debug.Assert(!IsStatic()); + + if (!_fieldInfo.m_declaringType.IsInstanceOfType(target)) + { + if (target == null) + { + ThrowHelperTargetException(); + } + else + { + ThrowHelperArgumentException(target, _fieldInfo); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void CheckValue(ref object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) + { + if (value is null) + { + if (((RuntimeType)_fieldInfo.FieldType).IsActualValueType) + { + ((RuntimeType)_fieldInfo.FieldType).CheckValue(ref value, binder, culture, invokeAttr); + } + } + else if (!ReferenceEquals(value.GetType(), _fieldInfo.FieldType)) + { + ((RuntimeType)_fieldInfo.FieldType).CheckValue(ref value, binder, culture, invokeAttr); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void VerifyInitOnly() + { + Debug.Assert(IsStatic()); + + if ((_fieldInfo.Attributes & FieldAttributes.InitOnly) == FieldAttributes.InitOnly && + _fieldAccessType != FieldAccessorType.SlowPathUntilClassInitialized) + { + ThrowHelperFieldAccessException(_fieldInfo.Name, _fieldInfo.DeclaringType?.FullName); + } + } + + /// + /// Currently we only optimize for primitive types and not all value types. Primitive types support atomic write operations, are + /// not boxed by the runtime when stored as a static field, and don't need special nullable, GC or alignment checks. + /// + private static FieldAccessorType GetPrimitiveAccessorTypeForInstance(Type fieldType) + { + FieldAccessorType accessorType = FieldAccessorType.InstanceValueType; + + if (fieldType == typeof(byte) || + fieldType == typeof(sbyte) || + fieldType == typeof(bool)) + accessorType = FieldAccessorType.InstanceValueTypeSize1; + else if (fieldType == typeof(short) || + fieldType == typeof(ushort) || + fieldType == typeof(char)) + accessorType = FieldAccessorType.InstanceValueTypeSize2; + else if (fieldType == typeof(int) || + fieldType == typeof(uint) || + fieldType == typeof(float)) + accessorType = FieldAccessorType.InstanceValueTypeSize4; + else if (fieldType == typeof(long) || + fieldType == typeof(ulong) || + fieldType == typeof(double)) + accessorType = FieldAccessorType.InstanceValueTypeSize8; + else if (fieldType == typeof(IntPtr) || + fieldType == typeof(UIntPtr)) + accessorType = GetIntPtrAccessorTypeForInstance(); + + return accessorType; + } + + private static FieldAccessorType GetPrimitiveAccessorTypeForStatic(Type fieldType) + { + FieldAccessorType accessorType = FieldAccessorType.StaticValueType; + + if (fieldType == typeof(byte) || + fieldType == typeof(sbyte) || + fieldType == typeof(bool)) + accessorType = FieldAccessorType.StaticValueTypeSize1; + else if (fieldType == typeof(short) || + fieldType == typeof(ushort) || + fieldType == typeof(char)) + accessorType = FieldAccessorType.StaticValueTypeSize2; + else if (fieldType == typeof(int) || + fieldType == typeof(uint) || + fieldType == typeof(float)) + accessorType = FieldAccessorType.StaticValueTypeSize4; + else if (fieldType == typeof(long) || + fieldType == typeof(ulong) || + fieldType == typeof(double)) + accessorType = FieldAccessorType.StaticValueTypeSize8; + else if (fieldType == typeof(IntPtr) || + fieldType == typeof(UIntPtr)) + accessorType = GetIntPtrAccessorTypeForStatic(); + + return accessorType; + } + + private static FieldAccessorType GetIntPtrAccessorTypeForInstance() + { + FieldAccessorType accessorType = FieldAccessorType.InstanceValueType; + + if (IntPtr.Size == 4) + { + accessorType = FieldAccessorType.InstanceValueTypeSize4; + } + else if (IntPtr.Size == 8) + { + accessorType = FieldAccessorType.InstanceValueTypeSize8; + } + + return accessorType; + } + + private static FieldAccessorType GetIntPtrAccessorTypeForStatic() + { + FieldAccessorType accessorType = FieldAccessorType.StaticValueType; + + if (IntPtr.Size == 4) + { + accessorType = FieldAccessorType.StaticValueTypeSize4; + } + else if (IntPtr.Size == 8) + { + accessorType = FieldAccessorType.StaticValueTypeSize8; + } + + return accessorType; + } + + private static void ThrowHelperTargetException() => throw new TargetException(SR.RFLCT_Targ_StatFldReqTarg); + + private static void ThrowHelperArgumentException(object target, FieldInfo fieldInfo) => + throw new ArgumentException(SR.Format(SR.Arg_FieldDeclTarget, fieldInfo.Name, fieldInfo.DeclaringType, target.GetType())); + + private static void ThrowHelperFieldAccessException(string fieldName, string? declaringTypeName) => + throw new FieldAccessException(SR.Format(SR.RFLCT_CannotSetInitonlyStaticField, fieldName, declaringTypeName)); + + private enum FieldAccessorType + { + InstanceReferenceType, + InstanceValueType, + InstanceValueTypeSize1, + InstanceValueTypeSize2, + InstanceValueTypeSize4, + InstanceValueTypeSize8, + InstancePointerType, + StaticReferenceType, + StaticValueType, + StaticValueTypeSize1, + StaticValueTypeSize2, + StaticValueTypeSize4, + StaticValueTypeSize8, + StaticValueTypeBoxed, + StaticPointerType, + SlowPathUntilClassInitialized, + SlowPath, + NoInvoke, + } + } +} diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/FieldInfoTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/FieldInfoTests.cs index 5f107d9b44e585..d854e19c71ade7 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/FieldInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/FieldInfoTests.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Xunit; @@ -29,7 +31,7 @@ public void SetValue_ConstantField_ThrowsFieldAccessException(string field, obje [Fact] public void SetValue_ReadonlyField() { - FieldInfo fieldInfo = typeof(FieldInfoTests).GetTypeInfo().GetDeclaredField("readonlyIntField"); + FieldInfo fieldInfo = typeof(FieldInfoTests).GetTypeInfo().GetDeclaredField(nameof(readonlyIntField)); FieldInfoTests myInstance = new FieldInfoTests(); object current = fieldInfo.GetValue(myInstance); @@ -55,11 +57,39 @@ public static void CustomAttributes(Type type, string expectedToString) public static IEnumerable GetValue_TestData() { - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.s_intField), new FieldInfoTests(), 100 }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.s_intField), null, 100 }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.intField), new FieldInfoTests(), 101 }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.s_stringField), new FieldInfoTests(), "static" }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.stringField), new FieldInfoTests(), "non static" }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_boolField), null, true }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_intField), new FieldInfoTests(), 100 }; // Non-null 'obj' ignored. + yield return new object[] { typeof(FieldInfoTests), nameof(s_intField), null, 100 }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_stringField), null, "static" }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_myStruct), null, new MyStruct() }; + + yield return new object[] { typeof(FieldInfoTests), nameof(boolField), new FieldInfoTests(), true }; + yield return new object[] { typeof(FieldInfoTests), nameof(intField), new FieldInfoTests(), 101 }; + yield return new object[] { typeof(FieldInfoTests), nameof(stringField), new FieldInfoTests(), "non static" }; + yield return new object[] { typeof(FieldInfoTests), nameof(_myStruct), new FieldInfoTests(), new MyStruct() }; + + yield return new object[] { typeof(FieldInfoTests), nameof(shortEnumField), new FieldInfoTests(), default(ShortEnum) }; + yield return new object[] { typeof(FieldInfoTests), nameof(intEnumField), new FieldInfoTests(), default(IntEnum) }; + yield return new object[] { typeof(FieldInfoTests), nameof(longEnumField), new FieldInfoTests(), default(LongEnum) }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_shortEnumField), null, default(ShortEnum) }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_intEnumField), null, default(IntEnum) }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_longEnumField), null, default(LongEnum) }; + + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_boolField), null, true }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_intField), null, 100 }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_intField), null, 100 }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_constIntField), null, 102 }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_stringField), null, "static" }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_readonlyStringField), null, "readonlyStatic" }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_objectField), null, MyStruct.s_objectField }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_intPtr), null, MyStruct.s_intPtrForComparison }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_rvaIntField), null, new int[] { 1, 2, 3 } }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.threadStatic_intField), null, 100 }; + + yield return new object[] { typeof(MyStruct), nameof(MyStruct.stringField), new MyStruct(), "non static" }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.intField), new MyStruct(), 101 }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.intPtr), new MyStruct(), MyStruct.intPtrForComparison }; + yield return new object[] { typeof(MyStruct_OnlyPrimitiveTypes), nameof(MyStruct_OnlyPrimitiveTypes.intField), new MyStruct_OnlyPrimitiveTypes(), 101 }; } [Theory] @@ -68,6 +98,76 @@ public void GetValue(Type type, string name, object obj, object expected) { FieldInfo fieldInfo = GetField(type, name); Assert.Equal(expected, fieldInfo.GetValue(obj)); + + // Perform a second time to rule out cases of slow-path vs. fast-path. + Assert.Equal(expected, fieldInfo.GetValue(obj)); + } + + public static IEnumerable GetValue_TestData_WithFunctionPointers() + { + yield return new object[] { typeof(MyStructWithFunctionPointers), nameof(MyStructWithFunctionPointers.s_fcnPtr), null, (IntPtr)45 }; + yield return new object[] { typeof(MyStructWithFunctionPointers), nameof(MyStructWithFunctionPointers.fcnPtr), new MyStructWithFunctionPointers(), (IntPtr)44 }; + } + + [Theory] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97833", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] + [MemberData(nameof(GetValue_TestData_WithFunctionPointers))] + public void GetValueWithFunctionPointers(Type type, string name, object obj, object expected) + { + FieldInfo fieldInfo = GetField(type, name); + Assert.Equal(expected, fieldInfo.GetValue(obj)); + + // Perform a second time to rule out cases of slow-path vs. fast-path. + Assert.Equal(expected, fieldInfo.GetValue(obj)); + } + + [Fact] + public void GetAndSetValueTypeFromStatic() + { + FieldInfo fieldInfo = GetField(typeof(FieldInfoTests), nameof(s_myStruct_GetAndSet)); + s_myStruct_GetAndSet.intField = 10; + object obj = fieldInfo.GetValue(null); + Assert.Equal(10, ((MyStruct)obj).intField); + s_myStruct_GetAndSet.intField = 11; + + // Make sure the previously boxed value didn't change. The runtime boxes non-primitive value types internally. + Assert.Equal(10, ((MyStruct)obj).intField); + + obj = fieldInfo.GetValue(null); + Assert.Equal(11, ((MyStruct)obj).intField); + } + + [Fact] + public void ClassInitializerCalledOnceOnException() + { + TestThrows(); + for (int j = 0; j < 100; j++) GC.Collect(); // Encourage the type to unload. + TestThrows(); + + for (int j = 0; j < 100; j++) GC.Collect(); // Encourage the type to unload. + InitializerNotCalledAfterThrow(); + + static void TestThrows() + { + FieldInfo fi = typeof(MyTypeThatThrowsInClassInitializer).GetField(nameof(MyTypeThatThrowsInClassInitializer.s_field)); + for (int i = 0; i < 3; i++) + { + // The actual exception may be TargetInvocationException or TypeInitializationException; there is no guarantee on when + // exactly the class initializer is called (e.g. it could happen before the GetValue\SetValue operation actually runs). + Assert.ThrowsAny(() => fi.GetValue(null)); + Assert.ThrowsAny(() => fi.SetValue(null, 100)); + } + } + + static void InitializerNotCalledAfterThrow() + { + // Setting this stops the class initializer's code from throwing, but the runtime caches the previous exception so it never runs. + SettingsForMyTypeThatThrowsInClassInitializer.s_shouldThrow = false; + + FieldInfo fi = typeof(MyTypeThatThrowsInClassInitializer).GetField(nameof(MyTypeThatThrowsInClassInitializer.s_field)); + Assert.ThrowsAny(() => fi.GetValue(null)); + Assert.ThrowsAny(() => fi.SetValue(null, 100)); + } } public static IEnumerable GetValue_Invalid_TestData() @@ -82,18 +182,42 @@ public void GetValue_Invalid(Type type, string name, object obj, Type exceptionT { FieldInfo fieldInfo = GetField(type, name); Assert.Throws(exceptionType, () => fieldInfo.GetValue(obj)); + + // Perform a second time to rule out cases of slow-path vs. fast-path. + Assert.Throws(exceptionType, () => fieldInfo.GetValue(obj)); } public static IEnumerable SetValue_TestData() { - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.s_intField), new FieldInfoTests(), 1000, 1000 }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.s_intField), null, 1000, 1000 }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.intField), new FieldInfoTests(), 1000, 1000 }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.s_stringField), new FieldInfoTests(), "new", "new" }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.stringField), new FieldInfoTests(), "new", "new" }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.shortEnumField), new FieldInfoTests(), (byte)1, (ShortEnum)1 }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.intEnumField), new FieldInfoTests(), (short)2, (IntEnum)2 }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.longEnumField), new FieldInfoTests(), (int)3, (LongEnum)3 }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_boolField_Set), null, true, true }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_boolField_Set), null, false, false }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_intField_Set), new FieldInfoTests(), 1000, 1000 }; // Non-null 'obj' ignored. + yield return new object[] { typeof(FieldInfoTests), nameof(s_intField_Set), null, 1001, 1001 }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_stringField_Set), null, "new", "new" }; + yield return new object[] { typeof(FieldInfoTests), nameof(s_myStruct_Set), null, s_myStruct_Set, s_myStruct_Set }; + + yield return new object[] { typeof(FieldInfoTests), nameof(boolField), new FieldInfoTests(), true, true }; + yield return new object[] { typeof(FieldInfoTests), nameof(boolField), new FieldInfoTests(), false, false }; + yield return new object[] { typeof(FieldInfoTests), nameof(stringField), new FieldInfoTests(), "new", "new" }; + yield return new object[] { typeof(FieldInfoTests), nameof(shortEnumField), new FieldInfoTests(), (byte)1, (ShortEnum)1 }; + yield return new object[] { typeof(FieldInfoTests), nameof(intEnumField), new FieldInfoTests(), (short)2, (IntEnum)2 }; + yield return new object[] { typeof(FieldInfoTests), nameof(longEnumField), new FieldInfoTests(), (int)3, (LongEnum)3 }; + yield return new object[] { typeof(FieldInfoTests), nameof(_myStruct), new FieldInfoTests(), s_myStruct_Set, s_myStruct_Set }; + + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_boolField_Set), null, true, true }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_boolField_Set), null, false, false }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_intField_Set), null, 1001, 1001 }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_stringField_Set), null, "new", "new" }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_objectField_Set), null, MyStruct.s_objectField, MyStruct.s_objectField }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_intPtr_Set), null, MyStruct.s_intPtrForComparison, MyStruct.s_intPtrForComparison }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.threadStatic_intField_Set), null, 100, 100 }; + + yield return new object[] { typeof(MyStruct), nameof(MyStruct.boolField), new MyStruct(), true, true }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.boolField), new MyStruct(), false, false }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.intField), new MyStruct(), 1002, 1002 }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.stringField), new MyStruct(), "new", "new" }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.objectField), new MyStruct(), MyStruct.s_objectField, MyStruct.s_objectField }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.intPtr), new MyStruct(), MyStruct.s_intPtrForComparison, MyStruct.s_intPtrForComparison }; } [Theory] @@ -106,6 +230,38 @@ public void SetValue(Type type, string name, object obj, object value, object ex { fieldInfo.SetValue(obj, value); Assert.Equal(expected, fieldInfo.GetValue(obj)); + + // Perform a second time to rule out cases of slow-path vs. fast-path. + fieldInfo.SetValue(obj, value); + Assert.Equal(expected, fieldInfo.GetValue(obj)); + } + finally + { + fieldInfo.SetValue(obj, original); + } + } + + public static IEnumerable SetValue_TestData_FunctionPointers() + { + yield return new object[] { typeof(MyStructWithFunctionPointers), nameof(MyStructWithFunctionPointers.s_fcnPtr_Set), null, (IntPtr)201, (IntPtr)201 }; + yield return new object[] { typeof(MyStructWithFunctionPointers), nameof(MyStructWithFunctionPointers.fcnPtr), new MyStructWithFunctionPointers(), (IntPtr)200, (IntPtr)200 }; + } + + [Theory] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97833", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] + [MemberData(nameof(SetValue_TestData_FunctionPointers))] + public void SetValueWithFunctionPointers(Type type, string name, object obj, object value, object expected) + { + FieldInfo fieldInfo = GetField(type, name); + object original = fieldInfo.GetValue(obj); + try + { + fieldInfo.SetValue(obj, value); + Assert.Equal(expected, fieldInfo.GetValue(obj)); + + // Perform a second time to rule out cases of slow-path vs. fast-path. + fieldInfo.SetValue(obj, value); + Assert.Equal(expected, fieldInfo.GetValue(obj)); } finally { @@ -115,17 +271,23 @@ public void SetValue(Type type, string name, object obj, object value, object ex public static IEnumerable SetValue_Invalid_TestData() { - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.stringField), null, "new", typeof(TargetException) }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.stringField), new object(), "new", typeof(ArgumentException) }; - yield return new object[] { typeof(FieldInfoTests), nameof(FieldInfoTests.stringField), new FieldInfoTests(), 100, typeof(ArgumentException) }; + yield return new object[] { typeof(FieldInfoTests), nameof(stringField), null, "new", typeof(TargetException) }; + yield return new object[] { typeof(FieldInfoTests), nameof(stringField), new object(), "new", typeof(ArgumentException) }; + yield return new object[] { typeof(FieldInfoTests), nameof(stringField), new FieldInfoTests(), 100, typeof(ArgumentException) }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_constIntField), null, 100, typeof(FieldAccessException) }; + yield return new object[] { typeof(MyStruct), nameof(MyStruct.s_rvaIntField), null, new int[] { 3, 4, 5 }, typeof(FieldAccessException) }; } [Theory] + [ActiveIssue("https://github.com/dotnet/runtime/issues/97829", TestRuntimes.Mono)] [MemberData(nameof(SetValue_Invalid_TestData))] public void SetValue_Invalid(Type type, string name, object obj, object value, Type exceptionType) { FieldInfo fieldInfo = GetField(type, name); Assert.Throws(exceptionType, () => fieldInfo.SetValue(obj, value)); + + // Perform a second time to rule out cases of slow-path vs. fast-path. + Assert.Throws(exceptionType, () => fieldInfo.SetValue(obj, value)); } [Theory] @@ -239,6 +401,7 @@ public void IsPrivate(Type type, string name, bool expected) [Theory] [InlineData(typeof(FieldInfoTests), nameof(FieldInfoTests.readonlyIntField), true)] + [InlineData(typeof(FieldInfoTests), nameof(FieldInfoTests.s_readonlyIntField), true)] [InlineData(typeof(FieldInfoTests), nameof(FieldInfoTests.intField), false)] public void IsInitOnly(Type type, string name, bool expected) { @@ -462,18 +625,32 @@ private static FieldInfo GetField(Type type, string name) public const long ConstInt64Field = 1000; public const byte ConstByteField = 0; + public static bool s_boolField = true; + public static bool s_boolField_Set = false; public static int s_intField = 100; + public static int s_intField_Set = 0; public static string s_stringField = "static"; + public static string s_stringField_Set = "static"; + public static readonly int s_readonlyIntField = 100; + public bool boolField = true; public int intField = 101; public string stringField = "non static"; - public enum ShortEnum : short {} - public enum IntEnum {} - public enum LongEnum : long {} + public MyStruct _myStruct = new MyStruct(); + public static MyStruct s_myStruct = new MyStruct(); + public static MyStruct s_myStruct_Set = new MyStruct(); + public static MyStruct s_myStruct_GetAndSet = new MyStruct(); + + public enum ShortEnum : short { } + public enum IntEnum { } + public enum LongEnum : long { } public ShortEnum shortEnumField; public IntEnum intEnumField; public LongEnum longEnumField; + public static ShortEnum s_shortEnumField; + public static IntEnum s_intEnumField; + public static LongEnum s_longEnumField; private int privateIntField = 1; private string privateStringField = "privateStringField"; @@ -586,5 +763,81 @@ public static void SetValueDirect_GetValueDirectRoundDataTest(object value) Assert.Equal(value, result); } + + + public struct MyStruct_OnlyPrimitiveTypes + { + public int intField = 101; + + public MyStruct_OnlyPrimitiveTypes() + { + } + } + + public struct MyStruct + { + public static bool s_boolField = true; + public static bool s_boolField_Set = false; + public static int s_intField = 100; + public static int s_intField_Set = 0; + [ThreadStatic] public static int threadStatic_intField = 100; + [ThreadStatic] public static int threadStatic_intField_Set = 0; + public static string s_stringField = "static"; + public static readonly string s_readonlyStringField = "readonlyStatic"; + public static string s_stringField_Set = null; + public static object s_objectField = new MyClass1(); + public static object s_objectField_Set = null; + + // This does not report FieldAttributes.HasFieldRVA since Roslyn wraps the .data with generated helper class. + public static readonly int[] s_rvaIntField = [1, 2, 3]; + + public unsafe static object intPtrForComparison = Pointer.Box((void*)42, typeof(int*)); + public unsafe static int* s_intPtr = (int*)43; + public unsafe static int* s_intPtr_Set = (int*)0; + public unsafe static object s_intPtrForComparison = Pointer.Box((void*)43, typeof(int*)); + public bool boolField = true; + public int intField = 101; + public object objectField = null; + public string stringField = "non static"; + public const int s_constIntField = 102; + public unsafe int* intPtr = (int*)42; + + public MyStruct() { } + } + + public struct MyStructWithFunctionPointers + { + public unsafe static delegate* s_fcnPtr = (delegate*)45; + public unsafe static delegate* s_fcnPtr_Set = (delegate*)0; + public unsafe delegate* fcnPtr = (delegate*)44; + + public MyStructWithFunctionPointers() { } + } + + public class MyTypeThatThrowsInClassInitializer + { + public static int s_field; + + static MyTypeThatThrowsInClassInitializer() + { + FieldInfo fi = typeof(MyTypeThatThrowsInClassInitializer).GetField(nameof(s_field)); + + // Ensure that the runtime doesn't treat this type has having been initialized due to successful GetValue(). + for (int i = 0; i < 3; i++) + { + fi.GetValue(null); + } + + if (SettingsForMyTypeThatThrowsInClassInitializer.s_shouldThrow) + { + throw new Exception(); + } + } + } + + public static class SettingsForMyTypeThatThrowsInClassInitializer + { + public static bool s_shouldThrow = true; + } } } diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs index 33162acb597819..7811adcf631c05 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs @@ -97,8 +97,8 @@ internal override void CheckConsistency(object target) [DebuggerHidden] internal override void UnsafeSetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) { - bool domainInitialized = false; - RuntimeFieldHandle.SetValue(this, obj, value, null, Attributes, null, ref domainInitialized); + bool isClassInitialized = false; + RuntimeFieldHandle.SetValue(this, obj, value, null, Attributes, null, ref isClassInitialized); } [DebuggerStepThrough] From b2e93f8f6b4d05f8ff1fbb079ceae446eb59a397 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Wed, 14 Feb 2024 22:22:00 +0100 Subject: [PATCH 038/158] Handle large imm values in ARMEmitter.EmitLDR (#98433) --- .../Target_ARM/ARMEmitter.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs index 8562927f838782..0b5db9e65bf04f 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_ARM/ARMEmitter.cs @@ -121,9 +121,41 @@ public void EmitLDR(Register destination, Register source) // ldr.w reg, [reg, #offset] // reg range: [0..PC] // offset range: [-255..4095] + // + // for offset >= 4096 we do an expansion into: + // add.w destination, source, #const + // ldr.w destination, [destination, #offset] public void EmitLDR(Register destination, Register source, int offset) { Debug.Assert(IsValidReg(destination) && IsValidReg(source)); + + if (offset >= 0x1000) + { + uint constVal = (uint)offset & ~0xfffu; + uint mask32 = 0xff; + uint imm8 = 0; + int encode = 31; // 11111 + + do + { + mask32 <<= 1; + if ((constVal & ~mask32) == 0) + { + imm8 = (constVal & mask32) >> (32 - encode); + break; + } + encode--; + } while (encode >= 8); + + Debug.Assert(encode >= 8); + Debug.Assert((imm8 & 0x80) > 0); + Builder.EmitShort((short)(0xF100 + (byte)source + (((byte)encode & 0x10) << 6))); + Builder.EmitShort((short)((((byte)encode & 0xE) << 11) + ((byte)destination << 8) + (((byte)encode & 1) << 7) + (imm8 & 0x7f))); + + offset = (int)(offset & 0xfffu); + source = destination; + } + Debug.Assert(offset >= -255 && offset <= 4095); if (offset >= 0) { From 9844faa61ee47f746caa8e298a3117ea6e720e7f Mon Sep 17 00:00:00 2001 From: Badre BSAILA <54767641+pedrobsaila@users.noreply.github.com> Date: Wed, 14 Feb 2024 22:38:52 +0100 Subject: [PATCH 039/158] Breaking a string on many lines produce incorrect result if followed by a call (#96948) --- .../Microsoft/CSharp/CSharpCodeGenerator.cs | 19 ++++++-- .../Compiler/CSharpCodeGeneratorTests.cs | 48 +++++++++++++++---- .../Schema/Import/SurrogateTests.cs | 4 +- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs b/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs index 095b76eb7deb53..49b8bd42d6513c 100644 --- a/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs +++ b/src/libraries/System.CodeDom/src/Microsoft/CSharp/CSharpCodeGenerator.cs @@ -96,6 +96,7 @@ private string QuoteSnippetStringCStyle(string value) b.Append('\"'); + bool isStringMultiline = false; int i = 0; while (i < value.Length) { @@ -144,16 +145,26 @@ private string QuoteSnippetStringCStyle(string value) b.Append(value[++i]); } - b.Append("\" +"); - b.Append(Environment.NewLine); - b.Append(indentObj.IndentationString); - b.Append('\"'); + if (i != value.Length - 1) + { + b.Append("\" +"); + b.Append(Environment.NewLine); + b.Append(indentObj.IndentationString); + b.Append('\"'); + isStringMultiline = true; + } } ++i; } b.Append('\"'); + if (isStringMultiline) + { + b.Insert(0, '('); + b.Append(')'); + } + return b.ToString(); } diff --git a/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CSharpCodeGeneratorTests.cs b/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CSharpCodeGeneratorTests.cs index f20162b7c34a5d..58e7f3fc3ed22b 100644 --- a/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CSharpCodeGeneratorTests.cs +++ b/src/libraries/System.CodeDom/tests/System/CodeDom/Compiler/CSharpCodeGeneratorTests.cs @@ -539,11 +539,11 @@ public static IEnumerable GenerateCodeFromExpression_TestData() yield return new object[] { new CodePrimitiveExpression("\uDC00"), null, "\"\uDC00\"" }; yield return new object[] { new CodePrimitiveExpression("\uD800"), null, "\"\uD800\"" }; yield return new object[] { new CodePrimitiveExpression("01234567890123456789012345678901234567890123456789012345678901234567890123456789"), null, $"\"01234567890123456789012345678901234567890123456789012345678901234567890123456789\"" }; - yield return new object[] { new CodePrimitiveExpression("01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800"), null, $"\"01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800\" +{nl} \"\"" }; - yield return new object[] { new CodePrimitiveExpression("01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800\uDC00"), null, $"\"01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800\uDC00\" +{nl} \"\"" }; - yield return new object[] { new CodePrimitiveExpression("01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800a"), null, $"\"01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800\" +{nl} \"a\"" }; - yield return new object[] { new CodePrimitiveExpression("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"), null, $"\"012345678901234567890123456789012345678901234567890123456789012345678901234567890\" +{nl} \"123456789\"" }; - yield return new object[] { new CodePrimitiveExpression("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"), customOptions, $"\"012345678901234567890123456789012345678901234567890123456789012345678901234567890\" +{nl}$\"123456789\"" }; + yield return new object[] { new CodePrimitiveExpression("01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800"), null, $"\"01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800\"" }; + yield return new object[] { new CodePrimitiveExpression("01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800\uDC00"), null, $"\"01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800\uDC00\"" }; + yield return new object[] { new CodePrimitiveExpression("01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800a"), null, $"(\"01234567890123456789012345678901234567890123456789012345678901234567890123456789\uD800\" +{nl} \"a\")" }; + yield return new object[] { new CodePrimitiveExpression("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"), null, $"(\"012345678901234567890123456789012345678901234567890123456789012345678901234567890\" +{nl} \"123456789\")" }; + yield return new object[] { new CodePrimitiveExpression("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"), customOptions, $"(\"012345678901234567890123456789012345678901234567890123456789012345678901234567890\" +{nl}$\"123456789\")" }; yield return new object[] { new CodePrimitiveExpression(new string('a', 256)), null, $"@\"{new string('a', 256)}\"" }; yield return new object[] { new CodePrimitiveExpression("\"" + new string('a', 254) + "\""), null, $"@\"\"\"{new string('a', 254)}\"\"\"" }; yield return new object[] { new CodePrimitiveExpression("\"" + new string('a', 1498) + "\""), null, $"@\"\"\"{new string('a', 1498)}\"\"\"" }; @@ -918,7 +918,7 @@ public static IEnumerable GenerateCodeFromStatement_TestData() new CodeConditionStatement( new CodePrimitiveExpression(1), new CodeExpressionStatement(new CodePrimitiveExpression(new string('a', 82))) - ), null, $"if (1) {{{nl} \"{new string('a', 81)}\" +{nl} \"a\";{nl}}}{nl}" + ), null, $"if (1) {{{nl} (\"{new string('a', 81)}\" +{nl} \"a\");{nl}}}{nl}" }; yield return new object[] { @@ -928,7 +928,7 @@ public static IEnumerable GenerateCodeFromStatement_TestData() new CodePrimitiveExpression(2), new CodeExpressionStatement(new CodePrimitiveExpression(new string('a', 82))) ) - ), null, $"if (1) {{{nl} if (2) {{{nl} \"{new string('a', 81)}\" +{nl} \"a\";{nl} }}{nl}}}{nl}" + ), null, $"if (1) {{{nl} if (2) {{{nl} (\"{new string('a', 81)}\" +{nl} \"a\");{nl} }}{nl}}}{nl}" }; yield return new object[] { @@ -941,7 +941,7 @@ public static IEnumerable GenerateCodeFromStatement_TestData() new CodeExpressionStatement(new CodePrimitiveExpression(new string('a', 82))) ) ) - ), null, $"if (1) {{{nl} if (2) {{{nl} if (3) {{{nl} \"{new string('a', 81)}\" +{nl} \"a\";{nl} }}{nl} }}{nl}}}{nl}" + ), null, $"if (1) {{{nl} if (2) {{{nl} if (3) {{{nl} (\"{new string('a', 81)}\" +{nl} \"a\");{nl} }}{nl} }}{nl}}}{nl}" }; yield return new object[] { @@ -957,7 +957,7 @@ public static IEnumerable GenerateCodeFromStatement_TestData() ) ) ) - ), null, $"if (1) {{{nl} if (2) {{{nl} if (3) {{{nl} if (4) {{{nl} \"{new string('a', 81)}\" +{nl} \"a\";{nl} }}{nl} }}{nl} }}{nl}}}{nl}" + ), null, $"if (1) {{{nl} if (2) {{{nl} if (3) {{{nl} if (4) {{{nl} (\"{new string('a', 81)}\" +{nl} \"a\");{nl} }}{nl} }}{nl} }}{nl}}}{nl}" }; yield return new object[] @@ -1308,6 +1308,7 @@ public static IEnumerable GenerateCodeFromStatement_TestData() [Theory] [MemberData(nameof(GenerateCodeFromStatement_TestData))] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework has different string breakup handling")] public void GenerateCodeFromStatement_Invoke_Success(CodeStatement e, CodeGeneratorOptions o, string expected) { ICodeGenerator generator = GetGenerator(); @@ -2708,6 +2709,35 @@ public void ValidateIdentifier_InvokeInvalid_ThrowsArgumentException(string valu AssertExtensions.Throws("value", null, () => generator.ValidateIdentifier(value)); } + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework has different string breakup handling")] + public void LineBreaksShouldPreserveTheWholeStringAsOneValue() + { + CodeStatement e = new CodeAssignStatement( + new CodeFieldReferenceExpression + { + FieldName = "Value", + TargetObject = new CodeTypeReferenceExpression("PF") + }, + new CodeMethodInvokeExpression + { + Parameters = + { + new CodePrimitiveExpression('|') + }, + Method = new CodeMethodReferenceExpression + { + MethodName = "MethodName", + TargetObject = new CodePrimitiveExpression(new string('*', 82)) + } + } + ); + ICodeGenerator generator = GetGenerator(); + var writer = new StringWriter(); + generator.GenerateCodeFromStatement(e, writer, new CodeGeneratorOptions()); + AssertEqualLong("PF.Value = (\"*********************************************************************************\" +" + writer.NewLine + " \"*\").MethodName('|');" + writer.NewLine, writer.ToString()); + } + private static ICodeGenerator GetGenerator() { #pragma warning disable 0618 diff --git a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs index 35d64bde333ba5..0f7f015caa7c69 100644 --- a/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs +++ b/src/libraries/System.Runtime.Serialization.Schema/tests/System/Runtime/Serialization/Schema/Import/SurrogateTests.cs @@ -59,7 +59,7 @@ public void DefaultScenario() Assert.Contains(@"public partial class SerializableSquare : object, System.Runtime.Serialization.IExtensibleDataObject", code); Assert.Contains(@"namespace System.Runtime.Serialization.Schema.Tests.DataContracts", code); - Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name\s*=\s*""SerializableNode"", Namespace\s*=\s*""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""\s*\+\s*"".DataContracts""\)\]\s*public partial class SerializableNode : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name\s*=\s*""SerializableNode"", Namespace\s*=\s*\(""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""\s*\+\s*"".DataContracts""\)\)\]\s*public partial class SerializableNode : object, System.Runtime.Serialization.IExtensibleDataObject", code); Assert.Matches(@"\[System.Xml.Serialization.XmlSchemaProviderAttribute\(""ExportSchema""\)\]\s*\[System.Xml.Serialization.XmlRootAttribute\(ElementName\s*=\s*""XmlSerializerPersonElement"", Namespace\s*=\s*""""\)\]\s*public partial class XmlSerializerPerson : object, System.Xml.Serialization.IXmlSerializable", code); } @@ -92,7 +92,7 @@ public void WithReferencedType() Assert.Matches(@"\[System.Runtime.Serialization.DataMemberAttribute\(\)\]\s*public System.Runtime.Serialization.Schema.Tests.DataContracts.SerializableCircle Circle", code); Assert.Contains(@"namespace System.Runtime.Serialization.Schema.Tests.DataContracts", code); - Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name\s*=\s*""SerializableNode"", Namespace\s*=\s*""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""\s*\+\s*"".DataContracts""\)\]\s*public partial class SerializableNode : object, System.Runtime.Serialization.IExtensibleDataObject", code); + Assert.Matches(@"\[System.Runtime.Serialization.DataContractAttribute\(Name\s*=\s*""SerializableNode"", Namespace\s*=\s*\(""http://schemas.datacontract.org/2004/07/System.Runtime.Serialization.Schema.Tests""\s*\+\s*"".DataContracts""\)\)\]\s*public partial class SerializableNode : object, System.Runtime.Serialization.IExtensibleDataObject", code); Assert.Matches(@"\[System.Xml.Serialization.XmlSchemaProviderAttribute\(""ExportSchema""\)\]\s*\[System.Xml.Serialization.XmlRootAttribute\(ElementName\s*=\s*""XmlSerializerPersonElement"", Namespace\s*=\s*""""\)\]\s*public partial class XmlSerializerPerson : object, System.Xml.Serialization.IXmlSerializable", code); Assert.DoesNotContain(@"public partial class SerializableSquare : object, System.Runtime.Serialization.IExtensibleDataObject", code); } From 1eebab21dba4b7476d04cf047b2e32beeff9adde Mon Sep 17 00:00:00 2001 From: Christiaan de Ridder Date: Wed, 14 Feb 2024 22:54:54 +0100 Subject: [PATCH 040/158] CallSiteValidator doesn't cache results of the walk (#96254) --- .../src/ServiceLookup/CallSiteValidator.cs | 33 +++++++---- .../ServiceProviderValidationTests.cs | 59 +++++++++++++++++++ 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs index e272c8a3d722b9..433b53b5cebe6b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs @@ -10,21 +10,15 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup internal sealed class CallSiteValidator : CallSiteVisitor { // Keys are services being resolved via GetService, values - first scoped service in their call site tree - private readonly ConcurrentDictionary _scopedServices = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _scopedServices = new ConcurrentDictionary(); - public void ValidateCallSite(ServiceCallSite callSite) - { - Type? scoped = VisitCallSite(callSite, default); - if (scoped != null) - { - _scopedServices[callSite.Cache.Key] = scoped; - } - } + public void ValidateCallSite(ServiceCallSite callSite) => VisitCallSite(callSite, default); public void ValidateResolution(ServiceCallSite callSite, IServiceScope scope, IServiceScope rootScope) { if (ReferenceEquals(scope, rootScope) - && _scopedServices.TryGetValue(callSite.Cache.Key, out Type? scopedService)) + && _scopedServices.TryGetValue(callSite.Cache.Key, out Type? scopedService) + && scopedService != null) { Type serviceType = callSite.ServiceType; if (serviceType == scopedService) @@ -42,6 +36,25 @@ public void ValidateResolution(ServiceCallSite callSite, IServiceScope scope, IS } } + protected override Type? VisitCallSite(ServiceCallSite callSite, CallSiteValidatorState argument) + { + // First, check if we have encountered this call site before to prevent visiting call site trees that have already been visited + // If firstScopedServiceInCallSiteTree is null there are no scoped dependencies in this service's call site tree + // If firstScopedServiceInCallSiteTree has a value, it contains the first scoped service in this service's call site tree + if (_scopedServices.TryGetValue(callSite.Cache.Key, out Type? firstScopedServiceInCallSiteTree)) + { + return firstScopedServiceInCallSiteTree; + } + + // Walk the tree + Type? scoped = base.VisitCallSite(callSite, argument); + + // Store the result for each visited service + _scopedServices[callSite.Cache.Key] = scoped; + + return scoped; + } + protected override Type? VisitConstructor(ConstructorCallSite constructorCallSite, CallSiteValidatorState state) { Type? result = null; diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderValidationTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderValidationTests.cs index 8780312c2e8ff6..a5ee249bd4705e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderValidationTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderValidationTests.cs @@ -180,6 +180,65 @@ public void GetService_DoesNotThrow_WhenScopeFactoryIsInjectedIntoSingleton() Assert.NotNull(result); } + [Fact] + public void GetService_DoesNotThrow_WhenGetServiceForServiceWithMultipleImplementationScopesWhereLastIsNotScoped() + { + // Arrange + var serviceCollection = new ServiceCollection(); + serviceCollection.AddScoped(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + var serviceProvider = serviceCollection.BuildServiceProvider(true); + + + // Act + Assert + var exception = Assert.Throws(() => serviceProvider.GetService(typeof(IEnumerable))); + Assert.Equal($"Cannot resolve scoped service '{typeof(IEnumerable)}' from root provider.", exception.Message); + + var result = serviceProvider.GetService(typeof(IBar)); + Assert.NotNull(result); + } + + + [Fact] + public void GetService_Throws_WhenGetServiceForServiceWithMultipleImplementationScopesWhereLastIsScoped() + { + // Arrange + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton(); + serviceCollection.AddScoped(); + serviceCollection.AddSingleton(); + var serviceProvider = serviceCollection.BuildServiceProvider(true); + + + // Act + Assert + var exception = Assert.Throws(() => serviceProvider.GetService(typeof(IEnumerable))); + Assert.Equal($"Cannot resolve scoped service '{typeof(IEnumerable)}' from root provider.", exception.Message); + + exception = Assert.Throws(() => serviceProvider.GetService(typeof(IBar))); + Assert.Equal($"Cannot resolve scoped service '{typeof(IBar)}' from root provider.", exception.Message); + } + + [Fact] + public void GetService_DoesNotThrow_WhenGetServiceForNonScopedImplementationWithMultipleImplementationScopesWhereLastIsScoped() + { + // Arrange + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddScoped(); + serviceCollection.AddSingleton(); + var serviceProvider = serviceCollection.BuildServiceProvider(true); + + + // Act + Assert + var exception = Assert.Throws(() => serviceProvider.GetService(typeof(IEnumerable))); + Assert.Equal($"Cannot resolve scoped service '{typeof(IEnumerable)}' from root provider.", exception.Message); + + var result = serviceProvider.GetService(typeof(Bar)); + Assert.NotNull(result); + } + [Fact] public void BuildServiceProvider_ValidateOnBuild_ThrowsForUnresolvableServices() { From 72ee969ab5ade8b238c46596a7f118ae4c07619c Mon Sep 17 00:00:00 2001 From: Buyaa Namnan Date: Wed, 14 Feb 2024 14:13:29 -0800 Subject: [PATCH 041/158] Fix more bugs found with ILVerify (#98393) * Fix more bugs found with ILVerify * Revert changes on ConstructorOnTypeBuilderInstantiation.ContainsGenericParameters, check generic parameters instead, add related test * Remove mono ContainsGenericParameters implementation that throwing with the new test --- .../Emit/MethodOnTypeBuilderInstantiation.cs | 15 +++-- .../Reflection/Emit/MethodBuilderImpl.cs | 16 ++++- .../Reflection/Emit/ModuleBuilderImpl.cs | 14 ++-- .../AssemblySaveEventBuilderTests.cs | 64 +++++++++++++++++++ .../AssemblySaveILGeneratorTests.cs | 29 +++++++++ .../AssemblySaveTypeBuilderAPIsTests.cs | 17 +++++ .../AssemblySaveTypeBuilderTests.cs | 43 +++++++++++++ .../MethodOnTypeBuilderInstantiation.Mono.cs | 19 ------ 8 files changed, 184 insertions(+), 33 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.cs index a1256cf7faab64..bfa9ef0359c502 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.cs @@ -68,13 +68,6 @@ public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? bind throw new NotSupportedException(); } public override CallingConventions CallingConvention => _method.CallingConvention; -#if !MONO - public override MethodInfo GetGenericMethodDefinition() { return _method; } - public override bool IsGenericMethodDefinition => _method.IsGenericMethodDefinition; - public override Type[] GetGenericArguments() - { - return _method.GetGenericArguments(); - } public override bool ContainsGenericParameters { get @@ -101,6 +94,14 @@ public override bool ContainsGenericParameters return false; } } +#if !MONO + public override MethodInfo GetGenericMethodDefinition() { return _method; } + public override bool IsGenericMethodDefinition => _method.IsGenericMethodDefinition; + public override Type[] GetGenericArguments() + { + return _method.GetGenericArguments(); + } + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] public override MethodInfo MakeGenericMethod(params Type[] typeArgs) { diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs index fe6b7c5e3f1b34..5881f753eb8fe5 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/MethodBuilderImpl.cs @@ -252,7 +252,21 @@ protected override void SetSignatureCore(Type? returnType, Type[]? returnTypeReq public override int MetadataToken => _handle == default ? 0 : MetadataTokens.GetToken(_handle); public override RuntimeMethodHandle MethodHandle => throw new NotSupportedException(SR.NotSupported_DynamicModule); public override Type? ReflectedType => DeclaringType; - public override ParameterInfo ReturnParameter { get => throw new NotImplementedException(); } + public override ParameterInfo ReturnParameter + { + get + { + if (_parameterBuilders == null || _parameterBuilders[0] == null) + { + return new ParameterInfoWrapper(new ParameterBuilderImpl(this, 0, ParameterAttributes.Retval, null), _returnType); + } + else + { + return new ParameterInfoWrapper(_parameterBuilders[0], _returnType); + } + } + } + public override Type ReturnType => _returnType; public override ICustomAttributeProvider ReturnTypeCustomAttributes { get => throw new NotImplementedException(); } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index de6cb832bb96e9..b95e74c7846c33 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -538,6 +538,7 @@ private EntityHandle GetMemberReferenceHandle(MemberInfo memberInfo) MetadataSignatureHelper.GetFieldSignature(field.FieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this)); break; case ConstructorInfo ctor: + ctor = (ConstructorInfo)GetOriginalMemberIfConstructedType(ctor); memberHandle = AddMemberReference(ctor.Name, GetTypeHandle(memberInfo.DeclaringType!), MetadataSignatureHelper.GetConstructorSignature(ctor.GetParameters(), this)); break; case MethodInfo method: @@ -606,8 +607,9 @@ internal static SignatureCallingConvention GetSignatureConvention(CallingConvent private static MemberInfo GetOriginalMemberIfConstructedType(MethodBase methodBase) { Type declaringType = methodBase.DeclaringType!; - if (declaringType.IsConstructedGenericType && !methodBase.ContainsGenericParameters && - declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl) + if (declaringType.IsConstructedGenericType && + declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl && + !ContainsNotBakedTypeBuilder(declaringType.GetGenericArguments())) { return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(methodBase); } @@ -890,14 +892,14 @@ private EntityHandle GetHandleForMember(MemberInfo member) } private static bool IsConstructedFromNotBakedTypeBuilder(Type type) => type.IsConstructedGenericType && - (type.GetGenericTypeDefinition() is TypeBuilderImpl tb && tb._handle == default || + (type.GetGenericTypeDefinition() is TypeBuilderImpl || ContainsNotBakedTypeBuilder(type.GetGenericArguments())); private static bool ContainsNotBakedTypeBuilder(Type[] genericArguments) { foreach (Type type in genericArguments) { - if (type is TypeBuilderImpl tb && tb._handle == default) + if (type is TypeBuilderImpl || type is GenericTypeParameterBuilderImpl) { return true; } @@ -964,8 +966,8 @@ internal EntityHandle TryGetMethodHandle(MethodInfo method) private static bool IsArrayMethodFromNotBakedTypeBuilder(MethodInfo method) => method is ArrayMethod arrayMethod && arrayMethod.DeclaringType!.GetElementType() is TypeBuilderImpl tb && tb._handle == default; - private static bool IsConstructedMethodFromNotBakedMethodBuilder(MethodInfo method) => - method.IsConstructedGenericMethod && method.GetGenericMethodDefinition() is MethodBuilderImpl mb && mb._handle == default; + private static bool IsConstructedMethodFromNotBakedMethodBuilder(MethodInfo method) => method.IsConstructedGenericMethod && + (method.GetGenericMethodDefinition() is MethodBuilderImpl mb && mb._handle == default || ContainsNotBakedTypeBuilder(method.GetGenericArguments())); internal EntityHandle TryGetMethodHandle(MethodInfo method, Type[] optionalParameterTypes) { diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEventBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEventBuilderTests.cs index d7c03fa49fc2b2..65250fb332003e 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEventBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveEventBuilderTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using Xunit; namespace System.Reflection.Emit.Tests @@ -95,5 +96,68 @@ public void Set_WhenTypeAlreadyCreated_ThrowsInvalidOperationException() Assert.Throws(() => eventBuilder.AddOtherMethod(method)); Assert.Throws(() => eventBuilder.SetCustomAttribute(customAttrBuilder)); } + + [Fact] + public void ReferenceEventInIL() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + TypeBuilder delegateType = ab.GetDynamicModule("MyModule").DefineType("OnMissingString", TypeAttributes.Public | TypeAttributes.Sealed, typeof(MulticastDelegate)); + delegateType.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + typeof(void), [typeof(string)]).SetImplementationFlags(MethodImplAttributes.Runtime); + delegateType.DefineMethod("BeginInvoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + typeof(IAsyncResult), [typeof(string), typeof(AsyncCallback), typeof(object)]).SetImplementationFlags(MethodImplAttributes.Runtime); + delegateType.DefineMethod("EndInvoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + typeof(void), [typeof(IAsyncResult)]).SetImplementationFlags(MethodImplAttributes.Runtime); + delegateType.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, [typeof(object), typeof(IntPtr)]). + SetImplementationFlags(MethodImplAttributes.Runtime); + MethodInfo combineMethod = typeof(Delegate).GetMethod("Combine", [typeof(Delegate), typeof(Delegate)]); + MethodInfo interlockedGenericMethod = typeof(Interlocked).GetMethods(BindingFlags.Public | BindingFlags.Static). + Where(m => m.Name == "CompareExchange" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1).First().MakeGenericMethod(delegateType); + EventBuilder eventBuilder = type.DefineEvent("MissingString", EventAttributes.SpecialName, delegateType); + FieldBuilder field = type.DefineField("MissingString", delegateType, FieldAttributes.Private); + MethodBuilder addMethod = type.DefineMethod("add_MissingString", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, [delegateType]); + ILGenerator addIL = addMethod.GetILGenerator(); + addIL.DeclareLocal(delegateType); + addIL.DeclareLocal(delegateType); + addIL.DeclareLocal(delegateType); + Label loop = addIL.DefineLabel(); + addIL.Emit(OpCodes.Ldarg_0); + addIL.Emit(OpCodes.Ldfld, field); + addIL.Emit(OpCodes.Stloc_0); + addIL.MarkLabel(loop); + addIL.Emit(OpCodes.Ldloc_0); + addIL.Emit(OpCodes.Stloc_1); + addIL.Emit(OpCodes.Ldloc_1); + addIL.Emit(OpCodes.Ldarg_1); + addIL.Emit(OpCodes.Call, combineMethod); + addIL.Emit(OpCodes.Castclass, delegateType); + addIL.Emit(OpCodes.Stloc_2); + addIL.Emit(OpCodes.Ldarg_0); + addIL.Emit(OpCodes.Ldflda, field); + addIL.Emit(OpCodes.Ldloc_2); + addIL.Emit(OpCodes.Ldloc_1); + addIL.Emit(OpCodes.Call, interlockedGenericMethod); + addIL.Emit(OpCodes.Stloc_0); + addIL.Emit(OpCodes.Ldloc_0); + addIL.Emit(OpCodes.Ldloc_1); + addIL.Emit(OpCodes.Bne_Un_S, loop); + addIL.Emit(OpCodes.Ret); + eventBuilder.SetAddOnMethod(addMethod); + + delegateType.CreateType(); + type.CreateType(); + ab.Save(file.Path); + + using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver())) + { + Assembly assemblyFromDisk = mlc.LoadFromAssemblyPath(file.Path); + Type typeFromDisk = assemblyFromDisk.Modules.First().GetType("MyType"); + EventInfo eventFromDisk = typeFromDisk.GetEvent("MissingString"); + Assert.Equal(addMethod.Name, eventFromDisk.AddMethod.Name); + Assert.Equal(delegateType.FullName, eventFromDisk.EventHandlerType.FullName); } + } + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs index c2fd512b85e82e..2fd95fa874cbed 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveILGeneratorTests.cs @@ -2503,5 +2503,34 @@ public void ReferenceNestedGenericTypeWithConstructedTypeBuilderParameterInIL() } } } + + [Fact] + public void ConstructorOfGenericTypeReferencedCorrectly() + { + using (TempFile file = TempFile.Create()) + { + AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + FieldBuilder field = type.DefineField("Field", typeof(int?), FieldAttributes.Public); + ConstructorBuilder ctor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Type.EmptyTypes); + ILGenerator ctorIL = ctor.GetILGenerator(); + ctorIL.Emit(OpCodes.Ldarg_0); + ctorIL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); + ctorIL.Emit(OpCodes.Ldarg_0); + ctorIL.Emit(OpCodes.Ldc_I4_1); + ctorIL.Emit(OpCodes.Newobj, typeof(int?).GetConstructor([typeof(int)])); + ctorIL.Emit(OpCodes.Stfld, field); + ctorIL.Emit(OpCodes.Ret); + type.CreateType(); + ab.Save(file.Path); + + TestAssemblyLoadContext tlc = new TestAssemblyLoadContext(); + Type typeFromDisk = tlc.LoadFromAssemblyPath(file.Path).GetType("MyType"); + FieldInfo fieldFromDisk = typeFromDisk.GetField("Field"); + object obj = Activator.CreateInstance(typeFromDisk); + Assert.Equal(typeof(int?), fieldFromDisk.FieldType); + Assert.Equal(1, fieldFromDisk.GetValue(obj)); + tlc.Unload(); + } + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs index 4b04b03acd2a53..ae84e78cfc24f9 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderAPIsTests.cs @@ -892,5 +892,22 @@ public void AbstractBaseMethodImplementationReturnsDifferentType() Assert.IsType(typeFromDisk, obj); } } + + [Fact] + public void TestContainsGenericParametersOnMethodCtorOfConstructedGenericType() + { + AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder tb); + GenericTypeParameterBuilder[] typeParameters = tb.DefineGenericParameters("T"); + ConstructorBuilder constructorBuilder = tb.DefineDefaultConstructor(MethodAttributes.Public); + MethodBuilder methodBuilder = tb.DefineMethod("Method", MethodAttributes.Public); + + Type instantiatedTypeBuilder1 = tb.MakeGenericType(typeof(List<>).GetGenericArguments()[0]); + Assert.True(TypeBuilder.GetConstructor(instantiatedTypeBuilder1, constructorBuilder).ContainsGenericParameters); + Assert.True(TypeBuilder.GetMethod(instantiatedTypeBuilder1, methodBuilder).ContainsGenericParameters); + + Type instantiatedTypeBuilder2 = tb.MakeGenericType(typeof(int)); + Assert.False(TypeBuilder.GetConstructor(instantiatedTypeBuilder2, constructorBuilder).ContainsGenericParameters); + Assert.False(TypeBuilder.GetMethod(instantiatedTypeBuilder2, methodBuilder).ContainsGenericParameters); + } } } diff --git a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index 6489fe2715faf1..eb7976a007866a 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -606,6 +606,49 @@ public void SaveMultipleGenericTypeParametersToEnsureSortingWorks() } } } + + [Fact] + public void MethodBuilderGetParametersReturnParameterTest() + { + AssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilderAndTypeBuilder(out TypeBuilder type); + MethodBuilder method1 = type.DefineMethod("Method1", MethodAttributes.Public, typeof(long), [typeof(int), typeof(string)]); + MethodBuilder method2 = type.DefineMethod("Method2", MethodAttributes.Static); + MethodBuilder method3 = type.DefineMethod("Method1", MethodAttributes.Public, typeof(int), [typeof(string)]); + method1.DefineParameter(0, ParameterAttributes.Retval, null); + method1.DefineParameter(1, ParameterAttributes.None, "index"); + method1.DefineParameter(2, ParameterAttributes.Out, "outParam"); + ParameterBuilder pb = method3.DefineParameter(1, ParameterAttributes.Optional, "name"); + pb.SetConstant("defaultName"); + //type.CreateType(); + + ParameterInfo[] params1 = method1.GetParameters(); + Assert.Equal(2, params1.Length); + Assert.Equal("index", params1[0].Name); + Assert.Equal(typeof(int), params1[0].ParameterType); + Assert.Equal("outParam", params1[1].Name); + Assert.Equal(typeof(string), params1[1].ParameterType); + Assert.Equal(ParameterAttributes.Out, params1[1].Attributes); + Assert.True(params1[1].IsOut); + Assert.Equal(typeof(long), method1.ReturnParameter.ParameterType); + Assert.Null(method1.ReturnParameter.Name); + Assert.True(method1.ReturnParameter.IsRetval); + + Assert.Empty(method2.GetParameters()); + Assert.Equal(typeof(void), method2.ReturnParameter.ParameterType); + Assert.Null(method2.ReturnParameter.Name); + Assert.True(method2.ReturnParameter.IsRetval); + + ParameterInfo[] params3 = method3.GetParameters(); + Assert.Equal(1, params3.Length); + Assert.Equal("name", params3[0].Name); + Assert.Equal(typeof(string), params3[0].ParameterType); + Assert.True(params3[0].HasDefaultValue); + Assert.Equal("defaultName", params3[0].DefaultValue); + + Assert.Equal(typeof(int), method3.ReturnParameter.ParameterType); + Assert.Null(method3.ReturnParameter.Name); + Assert.True(method3.ReturnParameter.IsRetval); + } } // Test Types diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.Mono.cs index f0c730920df848..3219d0bedce539 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/MethodOnTypeBuilderInstantiation.Mono.cs @@ -66,25 +66,6 @@ public override Type[] GetGenericArguments() return result; } - public override bool ContainsGenericParameters - { - get - { - if (_method.ContainsGenericParameters) - return true; - if (!_method.IsGenericMethodDefinition) - throw new NotSupportedException(); - if (_typeArguments == null) - return true; - foreach (Type t in _typeArguments) - { - if (t.ContainsGenericParameters) - return true; - } - return false; - } - } - public override bool IsGenericMethodDefinition => _method.IsGenericMethodDefinition && _typeArguments == null; public override MethodInfo GetGenericMethodDefinition() { return _genericMethodDefinition ?? _method; } From c409d186e7cecd4af59f94af1a888cc93ca4cd77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 15 Feb 2024 07:21:49 +0900 Subject: [PATCH 042/158] Build linux-arm and linux-musl-arm NAOT runtime packs (#98414) --- eng/pipelines/runtime-official.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/eng/pipelines/runtime-official.yml b/eng/pipelines/runtime-official.yml index d8ad9c571806af..ba4964eca0eb2c 100644 --- a/eng/pipelines/runtime-official.yml +++ b/eng/pipelines/runtime-official.yml @@ -310,8 +310,10 @@ extends: - iossimulator_arm64 - ios_arm64 - linux_x64 + - linux_arm - linux_arm64 - linux_musl_x64 + - linux_musl_arm - linux_musl_arm64 - linux_bionic_x64 - linux_bionic_arm64 From 09dfaf1f852a81ebd95edb1499cf0f83f3584eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 15 Feb 2024 07:23:02 +0900 Subject: [PATCH 043/158] Reject attempts to invoke uninstantiated methods (#98408) Fixes #95245. --- .../Runtime/MethodInfos/RuntimeNamedMethodInfo.cs | 2 +- .../tests/System.Reflection.Tests/MethodInfoTests.cs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs index 68554ad271685e..a0a726a4607edf 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs @@ -309,7 +309,7 @@ protected sealed override MethodBaseInvoker UncachedMethodInvoker if (invoker != null) return invoker; - return GetUncachedMethodInvoker(Array.Empty(), this); + return GetUncachedMethodInvoker(GenericTypeParameters, this); } } diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInfoTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInfoTests.cs index 341fb7fcb9de11..c0f95b43425565 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodInfoTests.cs @@ -300,6 +300,12 @@ public void ContainsGenericParameters(Type type, string name, bool expected) Assert.Equal(expected, GetMethod(type, name).ContainsGenericParameters); } + [Fact] + public void InvokeUninstantiatedGenericMethod() + { + Assert.Throws(() => GetMethod(typeof(MI_SubClass), nameof(MI_SubClass.StaticGenericMethod)).Invoke(null, [null])); + } + [Fact] public void GetHashCodeTest() { From fbe2b0691f999d4efee73eed3576979bb3b6fe94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 15 Feb 2024 07:23:59 +0900 Subject: [PATCH 044/158] Turn unreachable basic blocks into an infinite loop (#98319) Instead of padding unreachable basic blocks with nops followed by `br $-2`, we now turn unreachable basic blocks into _just_ `br $-2`, no nops (and matching basic block size) needed. This means offsets within the method body can shuffle around and we fix them up. --- .../Compiler/SubstitutedILProvider.cs | 196 ++++++++++++++---- .../SmokeTests/TrimmingBehaviors/Dataflow.cs | 30 +++ 2 files changed, 188 insertions(+), 38 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs index d2d614f761c08f..975df4243c2192 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/SubstitutedILProvider.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; @@ -106,9 +107,12 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) // The "seek backwards to find what feeds the comparison" only works for a couple known instructions // (load constant, call). It can't e.g. skip over arguments to the call. // - // Last step is a sweep - we replace the tail of all unreachable blocks with "br $-2" - // and nop out the rest. If the basic block is smaller than 2 bytes, we don't touch it. - // We also eliminate any EH records that correspond to the stubbed out basic block. + // We then do a pass to compute the offsets of instructions in a new instruction stream, where + // the unreachable basic blocks are replaced with an infinite loop (`br $-2`). + // Because of this rewriting, all the offsets will shift. + // + // Last step is the actual rewriting: we copy instructions from the source basic block, remapping + // offsets for jumps, and replacing parts that are unreachable with the infinite loop. // // We also attempt to rewrite calls to SR.SomeResourceString accessors with string // literals looked up from the managed resources. @@ -224,9 +228,7 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) // // We also do another round of basic block marking to mark beginning of visible basic blocks // after dead branch elimination. This allows us to limit the number of potential small basic blocks - // that are not interesting (because no code jumps to them anymore), but could prevent us from - // finishing the process. Unreachable basic blocks smaller than 2 bytes abort the substitution - // inlining process because we can't neutralize them (turn them into an infinite loop). + // that are not interesting (because no code jumps to them anymore). offsetsToVisit.Push(0); while (offsetsToVisit.TryPop(out int offset)) { @@ -270,7 +272,7 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) offsetsToVisit.Push(ehRegion.HandlerOffset); // RyuJIT is going to look at this basic block even though it's unreachable. - // Consider it visible so that we replace the tail with an endless loop. + // Consider it visible so that we replace it with an endless loop. int handlerEnd = ehRegion.HandlerOffset + ehRegion.HandlerLength; if (handlerEnd < flags.Length) flags[handlerEnd] |= OpcodeFlags.VisibleBasicBlockStart; @@ -398,7 +400,7 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) } } - // Now sweep unreachable basic blocks by replacing them with nops + // Now sweep unreachable basic blocks bool hasUnmarkedInstruction = false; foreach (var flag in flags) { @@ -412,54 +414,168 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) if (!hasUnmarkedInstruction && !hasGetResourceStringCall) return method; - byte[] newBody = (byte[])methodBytes.Clone(); - int position = 0; - while (position < newBody.Length) - { - Debug.Assert((flags[position] & OpcodeFlags.InstructionStart) != 0); - Debug.Assert((flags[position] & OpcodeFlags.VisibleBasicBlockStart) != 0); + // Maps instruction offsets in original method body to offsets in rewritten method body. + var offsetMap = new int[methodBytes.Length]; +#if DEBUG + Array.Fill(offsetMap, -1); +#endif + int srcPos = 0; + int dstPos = 0; - bool erase = (flags[position] & OpcodeFlags.Mark) == 0; + // Compute a map from original instruction offset to new instruction offsets + while (srcPos < flags.Length) + { + bool marked = (flags[srcPos] & OpcodeFlags.Mark) != 0; - int basicBlockStart = position; + // Go over all bytes in a single visible basic block + int lastInstructionPos = srcPos; do { - if (erase) - newBody[position] = (byte)ILOpCode.Nop; - position++; - } while (position < newBody.Length && (flags[position] & OpcodeFlags.VisibleBasicBlockStart) == 0); - - // If we had to nop out this basic block, we need to neutralize it by appending - // an infinite loop ("br $-2"). - // We append instead of prepend because RyuJIT's importer has trouble with junk unreachable bytes. - if (erase) + if ((flags[srcPos] & OpcodeFlags.InstructionStart) != 0) + { + lastInstructionPos = srcPos; + offsetMap[srcPos] = dstPos; + } + + if (marked) + { + dstPos++; + } + + srcPos++; + } while (srcPos < flags.Length && (flags[srcPos] & OpcodeFlags.VisibleBasicBlockStart) == 0); + + if (marked) { - if (position - basicBlockStart < 2) + // This was a marked visible basic block. If it ended in a short-form branch instruction, + // we need to do a size adjustment because the rewritten code doesn't use short-form + // instructions. + var reader = new ILReader(methodBytes, lastInstructionPos); + if (reader.HasNext) { - // We cannot neutralize the basic block, so better leave the method alone. - // The control would fall through to the next basic block. - return method; + ILOpcode opcode = reader.ReadILOpcode(); + int adjustment = opcode switch + { + >= ILOpcode.br_s and <= ILOpcode.blt_un_s => 3, + ILOpcode.leave_s => 3, + _ => 0, + }; + dstPos += adjustment; } + } + else + { + // This is a dead visible basic block. We're going to emit `br_s $-2`: reserve 2 bytes. + dstPos += 2; + } + } + + // Now generate the new body + var newBody = new byte[dstPos]; + srcPos = 0; + dstPos = 0; + while (srcPos < flags.Length) + { + Debug.Assert((flags[srcPos] & OpcodeFlags.InstructionStart) != 0); + Debug.Assert((flags[srcPos] & OpcodeFlags.VisibleBasicBlockStart) != 0); + + bool marked = (flags[srcPos] & OpcodeFlags.Mark) != 0; + + if (!marked) + { + // Dead basic block: emit endless loop and skip the rest of the original code. + newBody[dstPos++] = (byte)ILOpCode.Br_s; + newBody[dstPos++] = unchecked((byte)-2); - newBody[position - 2] = (byte)ILOpCode.Br_s; - newBody[position - 1] = unchecked((byte)-2); + do + { + srcPos++; + } + while (srcPos < flags.Length && (flags[srcPos] & OpcodeFlags.VisibleBasicBlockStart) == 0); + } + else + { + // Live basic block: copy the original bytes + int lastInstructionPos = srcPos; + do + { + if ((flags[srcPos] & OpcodeFlags.InstructionStart) != 0) + lastInstructionPos = srcPos; + + newBody[dstPos++] = methodBytes[srcPos++]; + } + while (srcPos < flags.Length && (flags[srcPos] & OpcodeFlags.VisibleBasicBlockStart) == 0); + + // If the basic block ended in a branch, we need to map the target offset to new offset. + // We'll also rewrite short-form instructions to their long forms. + var reader = new ILReader(methodBytes, lastInstructionPos); + if (reader.HasNext) + { + ILOpcode opcode = reader.ReadILOpcode(); + + if (opcode == ILOpcode.switch_) + { + uint count = reader.ReadILUInt32(); + int srcJmpBase = reader.Offset + (int)(4 * count); + int dstJmpBase = dstPos; + dstPos -= (int)(sizeof(uint) * count); + for (uint i = 0; i < count; i++) + { + int dest = offsetMap[(int)reader.ReadILUInt32() + srcJmpBase]; + BinaryPrimitives.WriteUInt32LittleEndian(new Span(newBody, dstPos, sizeof(uint)), (uint)(dest - dstJmpBase)); + dstPos += sizeof(uint); + } + } + else if (opcode is >= ILOpcode.br_s and <= ILOpcode.blt_un || opcode is ILOpcode.leave or ILOpcode.leave_s) + { + int dest = offsetMap[reader.ReadBranchDestination(opcode)]; + + if (opcode is >= ILOpcode.br_s and <= ILOpcode.blt_un_s || opcode == ILOpcode.leave_s) + { + dstPos -= 2; + if (opcode == ILOpcode.leave_s) + opcode = ILOpcode.leave; + else + opcode += ILOpcode.br - ILOpcode.br_s; + } + else + { + dstPos -= 5; + } + + newBody[dstPos++] = (byte)opcode; + BinaryPrimitives.WriteUInt32LittleEndian(new Span(newBody, dstPos, sizeof(uint)), (uint)(dest - (dstPos + sizeof(uint)))); + dstPos += sizeof(uint); + } + } } } // EH regions with unmarked handlers belong to unmarked basic blocks // Need to eliminate them because they're not usable. + // The rest need to have their offsets and lengths remapped. ArrayBuilder newEHRegions = default(ArrayBuilder); foreach (ILExceptionRegion ehRegion in ehRegions) { if ((flags[ehRegion.HandlerOffset] & OpcodeFlags.Mark) != 0) { - newEHRegions.Add(ehRegion); + int tryOffset = offsetMap[ehRegion.TryOffset]; + int handlerOffset = offsetMap[ehRegion.HandlerOffset]; + + var newEhRegion = new ILExceptionRegion( + ehRegion.Kind, + tryOffset, + offsetMap[ehRegion.TryOffset + ehRegion.TryLength] - tryOffset, + handlerOffset, + offsetMap[ehRegion.HandlerOffset + ehRegion.HandlerLength] - handlerOffset, + ehRegion.ClassToken, + ehRegion.Kind == ILExceptionRegionKind.Filter ? offsetMap[ehRegion.FilterOffset] : -1); + + newEHRegions.Add(newEhRegion); } } - // Existing debug information might not match new instruction boundaries (plus there's little point - // in generating debug information for NOPs) - generate new debug information by filtering - // out the sequence points associated with nopped out instructions. + // Remap debug information as well. MethodDebugInformation debugInfo = method.GetDebugInfo(); IEnumerable oldSequencePoints = debugInfo?.GetSequencePoints(); if (oldSequencePoints != null) @@ -469,7 +585,9 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) { if (sequencePoint.Offset < flags.Length && (flags[sequencePoint.Offset] & OpcodeFlags.Mark) != 0) { - sequencePoints.Add(sequencePoint); + ILSequencePoint newSequencePoint = new ILSequencePoint( + offsetMap[sequencePoint.Offset], sequencePoint.Document, sequencePoint.LineNumber); + sequencePoints.Add(newSequencePoint); } } @@ -486,11 +604,13 @@ public MethodIL GetMethodILWithInlinedSubstitutions(MethodIL method) // of a MethodIL and we're making a new one here. It just has to be unique to the MethodIL. int tokenRid = ecmaMethodIL.Module.MetadataReader.GetHeapSize(HeapIndex.UserString); - for (int offset = 0; offset < flags.Length; offset++) + for (int srcOffset = 0; srcOffset < flags.Length; srcOffset++) { - if ((flags[offset] & OpcodeFlags.GetResourceStringCall) == 0) + if ((flags[srcOffset] & OpcodeFlags.GetResourceStringCall) == 0) continue; + int offset = offsetMap[srcOffset]; + Debug.Assert(newBody[offset] == (byte)ILOpcode.call); var getter = (EcmaMethod)method.GetObject(new ILReader(newBody, offset + 1).ReadILToken()); diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs index fe050ada673edd..5efcd99d9ca643 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; #pragma warning disable 649 // 'blah' is never assigned to #pragma warning disable 169 // 'blah' is never used @@ -26,6 +27,7 @@ public static int Run() TestDynamicDependencyWithGenerics.Run(); TestObjectGetTypeDataflow.Run(); TestMarshalIntrinsics.Run(); + Regression97758.Run(); return 100; } @@ -631,6 +633,34 @@ static void SanityTest() } } } + + class Regression97758 + { + class Foo + { + public static void Trigger() + { + typeof(Bar).GetConstructor([]).Invoke([]); + + if (typeof(T).IsValueType && (object)default(T) == null) + { + if (!RuntimeFeature.IsDynamicCodeCompiled) + return; + + Unreachable(); + } + + static void Unreachable() { } + } + } + + class Bar { } + + public static void Run() + { + Foo.Trigger(); + } + } } static class Assert From aff061bab1b6d9ccd5731bd16fa8e89ad82ab75a Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 14 Feb 2024 15:00:43 -0800 Subject: [PATCH 045/158] Make sure ScopeStack doesn't box to IDisposable (#98396) We allocate every time we encounter a using (ScopeStack.PushScope...) statement, which adds up to be a lot (~10mb in hello world, one of the most allocated objects). Instead, we can return the struct type directly so no boxing occurs. --- src/tools/illink/src/linker/BannedSymbols.txt | 2 + .../src/linker/Linker.Steps/MarkScopeStack.cs | 19 ++++++- .../src/linker/Linker.Steps/MarkStep.cs | 54 +++++++++---------- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/tools/illink/src/linker/BannedSymbols.txt b/src/tools/illink/src/linker/BannedSymbols.txt index ada1bd0b4e1054..f7e6db3c773b69 100644 --- a/src/tools/illink/src/linker/BannedSymbols.txt +++ b/src/tools/illink/src/linker/BannedSymbols.txt @@ -10,3 +10,5 @@ P:Mono.Cecil.Cil.MethodBody.Instructions;Use LinkContext.GetMethodIL or BannedAp P:Mono.Cecil.Cil.MethodBody.ExceptionHandlers;Use LinkContext.GetMethodIL or BannedApiExtensions.ExceptionHandlers(Mono.Linker.LinkContext) instead P:Mono.Cecil.Cil.MethodBody.Variables;Use LinkContext.GetMethodIL or BannedApiExtensions.Variables(Mono.Linker.LinkContext) instead M:Mono.Linker.Steps.ILProvider/MethodIL.Create;Use ILProvider GetMethodIL instead +M:Mono.Linker.Steps.MarkScopeStack.PushScope;Use PushLocalScope instead to avoid boxing +M:Mono.Linker.Steps.MarkScopeStack.PopToParent;Use PopToParentScope instead to avoid boxing diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkScopeStack.cs b/src/tools/illink/src/linker/Linker.Steps/MarkScopeStack.cs index 00f5edec5379bf..ab1ad448754fcc 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkScopeStack.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkScopeStack.cs @@ -22,7 +22,7 @@ public Scope (in MessageOrigin origin) readonly Stack _scopeStack; - readonly struct LocalScope : IDisposable + internal readonly struct LocalScope : IDisposable { readonly MessageOrigin _origin; readonly MarkScopeStack _scopeStack; @@ -51,7 +51,7 @@ public void Dispose () } } - readonly struct ParentScope : IDisposable + internal readonly struct ParentScope : IDisposable { readonly Scope _parentScope; readonly Scope _childScope; @@ -78,6 +78,21 @@ public MarkScopeStack () _scopeStack = new Stack (); } + internal LocalScope PushLocalScope (in MessageOrigin origin) + { + return new LocalScope (origin, this); + } + + internal LocalScope PushLocalScope (in Scope scope) + { + return new LocalScope (scope, this); + } + + internal ParentScope PopToParentScope () + { + return new ParentScope (this); + } + public IDisposable PushScope (in MessageOrigin origin) { return new LocalScope (origin, this); diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index bb5e95e0a38dd0..c7eb071913f59a 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -297,7 +297,7 @@ bool ProcessInternalsVisibleAttributes () Debug.Assert (attr.Provider is ModuleDefinition or AssemblyDefinition); var assembly = (provider is ModuleDefinition module) ? module.Assembly : provider as AssemblyDefinition; - using var assemblyScope = ScopeStack.PushScope (new MessageOrigin (assembly)); + using var assemblyScope = ScopeStack.PushLocalScope (new MessageOrigin (assembly)); if (!Annotations.IsMarked (attr.Attribute) && IsInternalsVisibleAttributeAssemblyMarked (attr.Attribute)) { MarkCustomAttribute (attr.Attribute, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, attr.Provider)); @@ -362,7 +362,7 @@ internal void MarkEntireType (TypeDefinition type, in DependencyInfo reason) // Prevent cases where there's nothing on the stack (can happen when marking entire assemblies) // In which case we would generate warnings with no source (hard to debug) - using var _ = ScopeStack.CurrentScope.Origin.Provider == null ? ScopeStack.PushScope (new MessageOrigin (type)) : null; + using MarkScopeStack.LocalScope? _ = ScopeStack.CurrentScope.Origin.Provider == null ? ScopeStack.PushLocalScope (new MessageOrigin (type)) : null; if (!_entireTypesMarked.Add (type)) return; @@ -451,7 +451,7 @@ bool MarkFullyPreservedAssemblies () // Setup empty scope - there has to be some scope setup since we're doing marking below // but there's no "origin" right now (command line is the origin really) - using var localScope = ScopeStack.PushScope (new MessageOrigin ((ICustomAttributeProvider?) null)); + using var localScope = ScopeStack.PushLocalScope (new MessageOrigin ((ICustomAttributeProvider?) null)); // Beware: this works on loaded assemblies, not marked assemblies, so it should not be tied to marking. // We could further optimize this to only iterate through assemblies if the last mark iteration loaded @@ -488,7 +488,7 @@ bool ProcessPrimaryQueue () bool ProcessMarkedPending () { - using var emptyScope = ScopeStack.PushScope (new MessageOrigin (null as ICustomAttributeProvider)); + using var emptyScope = ScopeStack.PushLocalScope (new MessageOrigin (null as ICustomAttributeProvider)); bool marked = false; foreach (var pending in Annotations.GetMarkedPending ()) { @@ -498,7 +498,7 @@ bool ProcessMarkedPending () if (Annotations.IsProcessed (pending.Key)) continue; - using var localScope = ScopeStack.PushScope (pending.Value); + using var localScope = ScopeStack.PushLocalScope (pending.Value); switch (pending.Key) { case TypeDefinition type: @@ -572,7 +572,7 @@ protected virtual void EnqueueMethod (MethodDefinition method, in DependencyInfo void ProcessVirtualMethods () { foreach ((var method, var scope) in _virtual_methods) { - using (ScopeStack.PushScope (scope)) { + using (ScopeStack.PushLocalScope (scope)) { ProcessVirtualMethod (method); } } @@ -597,7 +597,7 @@ void ProcessMarkedTypesWithInterfaces () // UnusedInterfaces optimization is turned off mark all interface implementations bool unusedInterfacesOptimizationEnabled = Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type); - using (ScopeStack.PushScope (scope)) { + using (ScopeStack.PushLocalScope (scope)) { if (Annotations.IsInstantiated (type) || Annotations.IsRelevantToVariantCasting (type) || !unusedInterfacesOptimizationEnabled) { MarkInterfaceImplementations (type); @@ -685,7 +685,7 @@ void ProcessPendingBodies () for (int i = 0; i < _unreachableBodies.Count; i++) { (var body, var scope) = _unreachableBodies[i]; if (Annotations.IsInstantiated (body.Method.DeclaringType)) { - using (ScopeStack.PushScope (scope)) + using (ScopeStack.PushLocalScope (scope)) MarkMethodBody (body); _unreachableBodies.RemoveAt (i--); @@ -874,7 +874,7 @@ void MarkCustomAttributes (ICustomAttributeProvider provider, in DependencyInfo return; IMemberDefinition providerMember = (IMemberDefinition) provider; ; - using (ScopeStack.PushScope (new MessageOrigin (providerMember))) + using (ScopeStack.PushLocalScope (new MessageOrigin (providerMember))) foreach (var dynamicDependency in Annotations.GetLinkerAttributes (providerMember)) MarkDynamicDependency (dynamicDependency, providerMember); } @@ -1407,7 +1407,7 @@ protected virtual void MarkAssembly (AssemblyDefinition assembly, DependencyInfo if (CheckProcessed (assembly)) return; - using var assemblyScope = ScopeStack.PushScope (new MessageOrigin (assembly)); + using var assemblyScope = ScopeStack.PushLocalScope (new MessageOrigin (assembly)); EmbeddedXmlInfo.ProcessDescriptors (assembly, Context); @@ -1537,7 +1537,7 @@ bool ProcessLazyAttributes () Debug.Assert (provider is ModuleDefinition or AssemblyDefinition); var assembly = (provider is ModuleDefinition module) ? module.Assembly : provider as AssemblyDefinition; - using var assemblyScope = ScopeStack.PushScope (new MessageOrigin (assembly)); + using var assemblyScope = ScopeStack.PushLocalScope (new MessageOrigin (assembly)); var resolved = Context.Resolve (customAttribute.Constructor); if (resolved == null) { @@ -1607,7 +1607,7 @@ bool ProcessLateMarkedAttributes () } markOccurred = true; - using (ScopeStack.PushScope (scope)) { + using (ScopeStack.PushLocalScope (scope)) { MarkCustomAttribute (customAttribute, reason); } } @@ -1788,7 +1788,7 @@ void MarkField (FieldDefinition field, in DependencyInfo reason, in MessageOrigi // Use the original scope for marking the declaring type - it provides better warning message location MarkType (field.DeclaringType, new DependencyInfo (DependencyKind.DeclaringType, field)); - using var fieldScope = ScopeStack.PushScope (new MessageOrigin (field)); + using var fieldScope = ScopeStack.PushLocalScope (new MessageOrigin (field)); MarkType (field.FieldType, new DependencyInfo (DependencyKind.FieldType, field)); MarkCustomAttributes (field, new DependencyInfo (DependencyKind.CustomAttribute, field)); MarkMarshalSpec (field, new DependencyInfo (DependencyKind.FieldMarshalSpec, field)); @@ -2007,7 +2007,7 @@ internal void MarkStaticConstructorVisibleToReflection (TypeDefinition type, in if (reference == null) return null; - using var localScope = origin.HasValue ? ScopeStack.PushScope (origin.Value) : null; + using MarkScopeStack.LocalScope? localScope = origin.HasValue ? ScopeStack.PushLocalScope (origin.Value) : null; (reference, reason) = GetOriginalType (reference, reason); @@ -2053,7 +2053,7 @@ internal void MarkStaticConstructorVisibleToReflection (TypeDefinition type, in if (type.Scope is ModuleDefinition module) MarkModule (module, new DependencyInfo (DependencyKind.ScopeOfType, type)); - using var typeScope = ScopeStack.PushScope (new MessageOrigin (type)); + using var typeScope = ScopeStack.PushLocalScope (new MessageOrigin (type)); foreach (Action handleMarkType in MarkContext.MarkTypeActions) handleMarkType (type); @@ -2141,7 +2141,7 @@ internal void MarkStaticConstructorVisibleToReflection (TypeDefinition type, in } } if (ShouldMarkTypeStaticConstructor (type) && reason.Kind != DependencyKind.TriggersCctorForCalledMethod) { - using (ScopeStack.PopToParent ()) + using (ScopeStack.PopToParentScope ()) MarkStaticConstructor (type, new DependencyInfo (DependencyKind.CctorForType, type), ScopeStack.CurrentScope.Origin); } } @@ -2440,7 +2440,7 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency if (property.Name != property_name) continue; - using (ScopeStack.PushScope (new MessageOrigin (property))) { + using (ScopeStack.PushLocalScope (new MessageOrigin (property))) { // This marks methods directly without reporting the property. MarkMethod (property.GetMethod, reason, ScopeStack.CurrentScope.Origin); MarkMethod (property.SetMethod, reason, ScopeStack.CurrentScope.Origin); @@ -2804,7 +2804,7 @@ void MarkGenericArguments (IGenericInstance instance) // The only two implementations of IGenericInstance both derive from MemberReference Debug.Assert (instance is MemberReference); - using var _ = ScopeStack.CurrentScope.Origin.Provider == null ? ScopeStack.PushScope (new MessageOrigin (((MemberReference) instance).Resolve ())) : null; + using MarkScopeStack.LocalScope? _ = ScopeStack.CurrentScope.Origin.Provider == null ? ScopeStack.PushLocalScope (new MessageOrigin (((MemberReference) instance).Resolve ())) : null; var scanner = new GenericArgumentDataFlow (Context, this, ScopeStack.CurrentScope.Origin); scanner.ProcessGenericArgumentDataFlow (parameter, argument); } @@ -2832,7 +2832,7 @@ void MarkGenericArguments (IGenericInstance instance) void ApplyPreserveInfo (TypeDefinition type) { - using var typeScope = ScopeStack.PushScope (new MessageOrigin (type)); + using var typeScope = ScopeStack.PushLocalScope (new MessageOrigin (type)); if (Annotations.TryGetPreserve (type, out TypePreserve preserve)) { if (!Annotations.SetAppliedPreserve (type, preserve)) @@ -3203,8 +3203,8 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo throw new InternalErrorException ($"Unsupported method dependency {reason.Kind}"); #endif ScopeStack.AssertIsEmpty (); - using var parentScope = ScopeStack.PushScope (new MarkScopeStack.Scope (origin)); - using var methodScope = ScopeStack.PushScope (new MessageOrigin (method)); + using var parentScope = ScopeStack.PushLocalScope (new MarkScopeStack.Scope (origin)); + using var methodScope = ScopeStack.PushLocalScope (new MessageOrigin (method)); bool markedForCall = reason.Kind == DependencyKind.DirectCall || @@ -3360,7 +3360,7 @@ protected virtual void MarkRequirementsForInstantiatedTypes (TypeDefinition type Annotations.MarkInstantiated (type); - using var typeScope = ScopeStack.PushScope (new MessageOrigin (type)); + using var typeScope = ScopeStack.PushLocalScope (new MessageOrigin (type)); MarkInterfaceImplementations (type); @@ -3450,7 +3450,7 @@ bool MarkDisablePrivateReflectionAttribute () if (disablePrivateReflection == null) throw new LinkerFatalErrorException (MessageContainer.CreateErrorMessage (null, DiagnosticId.CouldNotFindType, "System.Runtime.CompilerServices.DisablePrivateReflectionAttribute")); - using (ScopeStack.PushScope (new MessageOrigin (null as ICustomAttributeProvider))) { + using (ScopeStack.PushLocalScope (new MessageOrigin (null as ICustomAttributeProvider))) { MarkType (disablePrivateReflection, DependencyInfo.DisablePrivateReflectionRequirement); var ctor = MarkMethodIf (disablePrivateReflection.Methods, MethodDefinitionExtensions.IsDefaultConstructor, new DependencyInfo (DependencyKind.DisablePrivateReflectionRequirement, disablePrivateReflection), ScopeStack.CurrentScope.Origin); @@ -3570,7 +3570,7 @@ protected internal void MarkProperty (PropertyDefinition prop, in DependencyInfo if (!Annotations.MarkProcessed (prop, reason)) return; - using var propertyScope = ScopeStack.PushScope (new MessageOrigin (prop)); + using var propertyScope = ScopeStack.PushLocalScope (new MessageOrigin (prop)); // Consider making this more similar to MarkEvent method? MarkCustomAttributes (prop, new DependencyInfo (DependencyKind.CustomAttribute, prop)); @@ -3582,7 +3582,7 @@ protected internal virtual void MarkEvent (EventDefinition evt, in DependencyInf if (!Annotations.MarkProcessed (evt, reason)) return; - using var eventScope = ScopeStack.PushScope (new MessageOrigin (evt)); + using var eventScope = ScopeStack.PushLocalScope (new MessageOrigin (evt)); MarkCustomAttributes (evt, new DependencyInfo (DependencyKind.CustomAttribute, evt)); @@ -3681,7 +3681,7 @@ bool MarkAndCheckRequiresReflectionMethodBodyScanner (MethodIL methodIL) requiresReflectionMethodBodyScanner = ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForMethodBody (Context, methodIL.Method); - using var _ = ScopeStack.PushScope (new MessageOrigin (methodIL.Method)); + using var _ = ScopeStack.PushLocalScope (new MessageOrigin (methodIL.Method)); foreach (Instruction instruction in methodIL.Instructions) MarkInstruction (instruction, methodIL.Method, ref requiresReflectionMethodBodyScanner); @@ -3832,7 +3832,7 @@ protected internal virtual void MarkInterfaceImplementation (InterfaceImplementa return; Annotations.MarkProcessed (iface, reason ?? new DependencyInfo (DependencyKind.InterfaceImplementationOnType, ScopeStack.CurrentScope.Origin.Provider)); - using var localScope = origin.HasValue ? ScopeStack.PushScope (origin.Value) : null; + using MarkScopeStack.LocalScope? localScope = origin.HasValue ? ScopeStack.PushLocalScope (origin.Value) : null; // Blame the type that has the interfaceimpl, expecting the type itself to get marked for other reasons. MarkCustomAttributes (iface, new DependencyInfo (DependencyKind.CustomAttribute, iface)); From c7253b133feefa26d3bb5f07ddcd7563efd511b4 Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Wed, 14 Feb 2024 23:23:54 -0500 Subject: [PATCH 046/158] JIT ARM64 SVE: Add FZ_2A, HG_2A, GZ_3A, GV_3A, GY_3*, DV_4A (#98310) Part of #94549. Implements the following encodings: IF_SVE_FZ_2A IF_SVE_HG_2A (SVE2) IF_SVE_GZ_3A IF_SVE_GV_3A IF_SVE_GY_3B_D (SVE2) IF_SVE_GY_3A (SVE2) IF_SVE_DV_4A (SVE2) --- src/coreclr/jit/codegenarm64test.cpp | 75 ++++++ src/coreclr/jit/emitarm64.cpp | 387 +++++++++++++++++++++++++-- src/coreclr/jit/emitarm64.h | 3 + 3 files changed, 439 insertions(+), 26 deletions(-) diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 9e537de5877b2f..ac75834f317e05 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -5422,6 +5422,19 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R(INS_sve_ursqrte, EA_SCALABLE, REG_V3, REG_P0, REG_V5, INS_OPTS_SCALABLE_S); // URSQRTE .S, /M, .S + // IF_SVE_FZ_2A + theEmitter->emitIns_R_R(INS_sve_sqcvtn, EA_SCALABLE, REG_V0, REG_V1); // SQCVTN .H, {.S-.S } + theEmitter->emitIns_R_R(INS_sve_sqcvtun, EA_SCALABLE, REG_V6, REG_V7); // SQCVTUN .H, {.S-.S } + theEmitter->emitIns_R_R(INS_sve_uqcvtn, EA_SCALABLE, REG_V14, REG_V15); // UQCVTN .H, {.S-.S } + +#ifdef ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED + // IF_SVE_HG_2A + theEmitter->emitIns_R_R(INS_sve_bfcvtn, EA_SCALABLE, REG_V0, REG_V1); // BFCVTN .B, {.H-.H } + theEmitter->emitIns_R_R(INS_sve_fcvtn, EA_SCALABLE, REG_V2, REG_V3); // FCVTN .B, {.H-.H } + theEmitter->emitIns_R_R(INS_sve_fcvtnb, EA_SCALABLE, REG_V6, REG_V7); // FCVTNB .B, {.S-.S } + theEmitter->emitIns_R_R(INS_sve_fcvtnt, EA_SCALABLE, REG_V14, REG_V15); // FCVTNT .B, {.S-.S } +#endif // ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED + // IF_SVE_GA_2A theEmitter->emitIns_R_R_I(INS_sve_sqrshrn, EA_SCALABLE, REG_V0, REG_V0, 5, INS_OPTS_SCALABLE_H); // SQRSHRN .H, {.S-.S }, # @@ -5699,6 +5712,16 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R_I(INS_sve_bfmls, EA_SCALABLE, REG_V7, REG_V8, REG_V7, 7, INS_OPTS_SCALABLE_H); // BFMLS .H, .H, .H[] + // IF_SVE_GV_3A + theEmitter->emitIns_R_R_R_I_I(INS_sve_fcmla, EA_SCALABLE, REG_V0, REG_V1, REG_V0, 0, 0, + INS_OPTS_SCALABLE_S); // FCMLA .S, .S, .S[], + theEmitter->emitIns_R_R_R_I_I(INS_sve_fcmla, EA_SCALABLE, REG_V2, REG_V3, REG_V5, 1, 90, + INS_OPTS_SCALABLE_S); // FCMLA .S, .S, .S[], + theEmitter->emitIns_R_R_R_I_I(INS_sve_fcmla, EA_SCALABLE, REG_V4, REG_V5, REG_V10, 0, 180, + INS_OPTS_SCALABLE_S); // FCMLA .S, .S, .S[], + theEmitter->emitIns_R_R_R_I_I(INS_sve_fcmla, EA_SCALABLE, REG_V6, REG_V7, REG_V15, 1, 270, + INS_OPTS_SCALABLE_S); // FCMLA .S, .S, .S[], + // IF_SVE_GX_3A theEmitter->emitIns_R_R_R_I(INS_sve_fmul, EA_SCALABLE, REG_V0, REG_V2, REG_V1, 0, INS_OPTS_SCALABLE_S); // FMUL .S, .S, .S[] @@ -5739,6 +5762,46 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R_I(INS_sve_bfdot, EA_SCALABLE, REG_V12, REG_V14, REG_V7, 3, INS_OPTS_SCALABLE_H); // BFDOT .S, .H, .H[] +#ifdef ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED + // IF_SVE_GY_3A + theEmitter->emitIns_R_R_R_I(INS_sve_fdot, EA_SCALABLE, REG_V0, REG_V2, REG_V1, + 1); // FDOT .H, .B, .B[] + theEmitter->emitIns_R_R_R_I(INS_sve_fdot, EA_SCALABLE, REG_V4, REG_V6, REG_V3, + 3); // FDOT .H, .B, .B[] + theEmitter->emitIns_R_R_R_I(INS_sve_fdot, EA_SCALABLE, REG_V8, REG_V10, REG_V5, + 5); // FDOT .H, .B, .B[] + theEmitter->emitIns_R_R_R_I(INS_sve_fdot, EA_SCALABLE, REG_V12, REG_V14, REG_V7, + 7); // FDOT .H, .B, .B[] + + // IF_SVE_GY_3B_D + theEmitter->emitIns_R_R_R_I(INS_sve_fdot, EA_SCALABLE, REG_V0, REG_V2, REG_V1, 0, + INS_OPTS_SCALABLE_B); // FDOT .S, .B, .B[] + theEmitter->emitIns_R_R_R_I(INS_sve_fdot, EA_SCALABLE, REG_V4, REG_V6, REG_V3, 1, + INS_OPTS_SCALABLE_B); // FDOT .S, .B, .B[] + theEmitter->emitIns_R_R_R_I(INS_sve_fdot, EA_SCALABLE, REG_V8, REG_V10, REG_V5, 2, + INS_OPTS_SCALABLE_B); // FDOT .S, .B, .B[] + theEmitter->emitIns_R_R_R_I(INS_sve_fdot, EA_SCALABLE, REG_V12, REG_V14, REG_V7, 3, + INS_OPTS_SCALABLE_B); // FDOT .S, .B, .B[] +#endif // ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED + + // IF_SVE_GZ_3A + theEmitter->emitIns_R_R_R_I(INS_sve_bfmlalb, EA_SCALABLE, REG_V0, REG_V1, REG_V0, 0, + INS_OPTS_SCALABLE_H); // BFMLALB .S, .H, .H[] + theEmitter->emitIns_R_R_R_I(INS_sve_bfmlalt, EA_SCALABLE, REG_V2, REG_V3, REG_V1, 1, + INS_OPTS_SCALABLE_H); // BFMLALT .S, .H, .H[] + theEmitter->emitIns_R_R_R_I(INS_sve_bfmlslb, EA_SCALABLE, REG_V4, REG_V5, REG_V2, 2, + INS_OPTS_SCALABLE_H); // BFMLSLB .S, .H, .H[] + theEmitter->emitIns_R_R_R_I(INS_sve_bfmlslt, EA_SCALABLE, REG_V6, REG_V7, REG_V3, 3, + INS_OPTS_SCALABLE_H); // BFMLSLT .S, .H, .H[] + theEmitter->emitIns_R_R_R_I(INS_sve_fmlalb, EA_SCALABLE, REG_V8, REG_V9, REG_V4, 4, + INS_OPTS_SCALABLE_H); // FMLALB .S, .H, .H[] + theEmitter->emitIns_R_R_R_I(INS_sve_fmlalt, EA_SCALABLE, REG_V10, REG_V11, REG_V5, 5, + INS_OPTS_SCALABLE_H); // FMLALT .S, .H, .H[] + theEmitter->emitIns_R_R_R_I(INS_sve_fmlslb, EA_SCALABLE, REG_V12, REG_V13, REG_V6, 6, + INS_OPTS_SCALABLE_H); // FMLSLB .S, .H, .H[] + theEmitter->emitIns_R_R_R_I(INS_sve_fmlslt, EA_SCALABLE, REG_V14, REG_V15, REG_V7, 7, + INS_OPTS_SCALABLE_H); // FMLSLT .S, .H, .H[] + // IF_SVE_HE_3A theEmitter->emitIns_R_R_R(INS_sve_faddv, EA_2BYTE, REG_V21, REG_P7, REG_V7, INS_OPTS_SCALABLE_H); // FADDV , , . @@ -5825,6 +5888,18 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R(INS_sve_whilewr, EA_8BYTE, REG_P7, REG_R14, REG_R15, INS_OPTS_SCALABLE_D); // WHILEWR ., , +#ifdef ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED + // IF_SVE_DV_4A + theEmitter->emitIns_R_R_R_R_I(INS_sve_psel, EA_SCALABLE, REG_P0, REG_P1, REG_P2, REG_R12, 15, + INS_OPTS_SCALABLE_B); // PSEL , , .[, ] + theEmitter->emitIns_R_R_R_R_I(INS_sve_psel, EA_SCALABLE, REG_P3, REG_P4, REG_P5, REG_R13, 7, + INS_OPTS_SCALABLE_H); // PSEL , , .[, ] + theEmitter->emitIns_R_R_R_R_I(INS_sve_psel, EA_SCALABLE, REG_P6, REG_P7, REG_P8, REG_R14, 3, + INS_OPTS_SCALABLE_S); // PSEL , , .[, ] + theEmitter->emitIns_R_R_R_R_I(INS_sve_psel, EA_SCALABLE, REG_P9, REG_P10, REG_P11, REG_R15, 1, + INS_OPTS_SCALABLE_D); // PSEL , , .[, ] +#endif // ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED + // IF_SVE_DW_2A theEmitter->emitIns_R_R_I(INS_sve_pext, EA_SCALABLE, REG_P0, REG_P8, 0, INS_OPTS_SCALABLE_B); // PEXT ., [] diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 53923701301757..5a9285e3b49018 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1165,16 +1165,17 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isVectorRegister(id->idReg3())); // mmmmm/aaaaa break; - case IF_SVE_EG_3A: // ...........iimmm ......nnnnnddddd -- SVE two-way dot product (indexed) - case IF_SVE_EY_3A: // ...........iimmm ......nnnnnddddd -- SVE integer dot product (indexed) - case IF_SVE_EZ_3A: // ...........iimmm ......nnnnnddddd -- SVE mixed sign dot product (indexed) - case IF_SVE_FD_3B: // ...........iimmm ......nnnnnddddd -- SVE2 integer multiply (indexed) - case IF_SVE_FF_3B: // ...........iimmm ......nnnnnddddd -- SVE2 integer multiply-add (indexed) - case IF_SVE_FI_3B: // ...........iimmm ......nnnnnddddd -- SVE2 saturating multiply high (indexed) - case IF_SVE_GU_3A: // ...........iimmm ......nnnnnddddd -- SVE floating-point multiply-add (indexed) - case IF_SVE_GX_3A: // ...........iimmm ......nnnnnddddd -- SVE floating-point multiply (indexed) - case IF_SVE_GY_3B: // ...........iimmm ......nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) - case IF_SVE_FK_3B: // ...........iimmm ......nnnnnddddd -- SVE2 saturating multiply-add high (indexed) + case IF_SVE_EG_3A: // ...........iimmm ......nnnnnddddd -- SVE two-way dot product (indexed) + case IF_SVE_EY_3A: // ...........iimmm ......nnnnnddddd -- SVE integer dot product (indexed) + case IF_SVE_EZ_3A: // ...........iimmm ......nnnnnddddd -- SVE mixed sign dot product (indexed) + case IF_SVE_FD_3B: // ...........iimmm ......nnnnnddddd -- SVE2 integer multiply (indexed) + case IF_SVE_FF_3B: // ...........iimmm ......nnnnnddddd -- SVE2 integer multiply-add (indexed) + case IF_SVE_FI_3B: // ...........iimmm ......nnnnnddddd -- SVE2 saturating multiply high (indexed) + case IF_SVE_GU_3A: // ...........iimmm ......nnnnnddddd -- SVE floating-point multiply-add (indexed) + case IF_SVE_GX_3A: // ...........iimmm ......nnnnnddddd -- SVE floating-point multiply (indexed) + case IF_SVE_GY_3B: // ...........iimmm ......nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) + case IF_SVE_GY_3B_D: // ...........iimmm ......nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) + case IF_SVE_FK_3B: // ...........iimmm ......nnnnnddddd -- SVE2 saturating multiply-add high (indexed) assert(insOptsScalableStandard(id->idInsOpt())); assert(isVectorRegister(id->idReg1())); // ddddd assert(isVectorRegister(id->idReg2())); // nnnnn @@ -1190,9 +1191,11 @@ void emitter::emitInsSanityCheck(instrDesc* id) case IF_SVE_FH_3A: // ...........iimmm ....i.nnnnnddddd -- SVE2 saturating multiply (indexed) case IF_SVE_FI_3A: // .........i.iimmm ......nnnnnddddd -- SVE2 saturating multiply high (indexed) case IF_SVE_FJ_3A: // ...........iimmm ....i.nnnnnddddd -- SVE2 saturating multiply-add (indexed) + case IF_SVE_FK_3A: // .........i.iimmm ......nnnnnddddd -- SVE2 saturating multiply-add high (indexed) case IF_SVE_GU_3C: // .........i.iimmm ......nnnnnddddd -- SVE floating-point multiply-add (indexed) case IF_SVE_GX_3C: // .........i.iimmm ......nnnnnddddd -- SVE floating-point multiply (indexed) - case IF_SVE_FK_3A: // .........i.iimmm ......nnnnnddddd -- SVE2 saturating multiply-add high (indexed) + case IF_SVE_GY_3A: // ...........iimmm ....i.nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) + case IF_SVE_GZ_3A: // ...........iimmm ....i.nnnnnddddd -- SVE floating-point multiply-add long (indexed) assert(insOptsScalableStandard(id->idInsOpt())); assert(isVectorRegister(id->idReg1())); // ddddd assert(isVectorRegister(id->idReg2())); // nnnnn @@ -1564,6 +1567,18 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isValidGeneralDatasize(id->idOpSize())); // x break; + case IF_SVE_FZ_2A: // ................ ......nnnn.ddddd -- SME2 multi-vec extract narrow + assert(insOptsNone(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isLowVectorRegister(id->idReg2())); // nnnn + break; + + case IF_SVE_HG_2A: // ................ ......nnnn.ddddd -- SVE2 FP8 downconverts + assert(insOptsNone(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isLowVectorRegister(id->idReg2())); // nnnn + break; + case IF_SVE_GD_2A: // .........x.xx... ......nnnnnddddd -- SVE2 saturating extract narrow assert(insOptsScalableStandard(id->idInsOpt())); assert(isVectorRegister(id->idReg1())); // nnnnn @@ -1683,6 +1698,42 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx break; + case IF_SVE_DV_4A: // ........ix.xxxvv ..NNNN.MMMM.DDDD -- SVE broadcast predicate element + { + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isPredicateRegister(id->idReg1())); // DDDD + assert(isPredicateRegister(id->idReg2())); // NNNN + assert(isPredicateRegister(id->idReg3())); // MMMM + assert(isGeneralRegister(id->idReg4())); // vv + assert((REG_R12 <= id->idReg4()) && (id->idReg4() <= REG_R15)); + const ssize_t imm = emitGetInsSC(id); + + switch (id->idInsOpt()) + { + case INS_OPTS_SCALABLE_B: + assert(isValidUimm4(imm)); + break; + + case INS_OPTS_SCALABLE_H: + assert(isValidUimm3(imm)); + break; + + case INS_OPTS_SCALABLE_S: + assert(isValidUimm2(imm)); + break; + + case INS_OPTS_SCALABLE_D: + assert(isValidImm1(imm)); + break; + + default: + unreached(); + break; + } + + break; + } + case IF_SVE_DW_2B: // ........xx...... .......iNNN.DDDD -- SVE extract mask predicate from predicate-as-counter assert(isValidImm1(emitGetInsSC(id))); // i @@ -1791,6 +1842,7 @@ void emitter::emitInsSanityCheck(instrDesc* id) case IF_SVE_FA_3B: // ...........immmm ....rrnnnnnddddd -- SVE2 complex integer dot product (indexed) case IF_SVE_FB_3B: // ...........immmm ....rrnnnnnddddd -- SVE2 complex integer multiply-add (indexed) case IF_SVE_FC_3B: // ...........immmm ....rrnnnnnddddd -- SVE2 complex saturating multiply-add (indexed) + case IF_SVE_GV_3A: // ...........immmm ....rrnnnnnddddd -- SVE floating-point complex multiply-add (indexed) assert(insOptsScalableStandard(id->idInsOpt())); assert(isVectorRegister(id->idReg1())); // ddddd assert(isVectorRegister(id->idReg2())); // nnnnn @@ -8505,6 +8557,26 @@ void emitter::emitIns_R_R(instruction ins, fmt = IF_SVE_DS_2A; break; + case INS_sve_sqcvtn: + case INS_sve_uqcvtn: + case INS_sve_sqcvtun: + assert(insOptsNone(opt)); + assert(isVectorRegister(reg1)); // ddddd + assert(isLowVectorRegister(reg2)); // nnnn + fmt = IF_SVE_FZ_2A; + break; + + case INS_sve_fcvtn: + case INS_sve_bfcvtn: + case INS_sve_fcvtnt: + case INS_sve_fcvtnb: + unreached(); // TODO-SVE: Not yet supported. + assert(insOptsNone(opt)); + assert(isVectorRegister(reg1)); // ddddd + assert(isLowVectorRegister(reg2)); // nnnn + fmt = IF_SVE_HG_2A; + break; + case INS_sve_sqxtnb: case INS_sve_sqxtnt: case INS_sve_uqxtnb: @@ -12349,7 +12421,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, break; case INS_sve_bfmul: - assert(opt = INS_OPTS_SCALABLE_H); + assert(opt == INS_OPTS_SCALABLE_H); assert(isVectorRegister(reg1)); // ddddd assert(isVectorRegister(reg2)); // nnnnn assert(isVectorRegister(reg3)); // mmm @@ -12359,8 +12431,37 @@ void emitter::emitIns_R_R_R_I(instruction ins, break; case INS_sve_fdot: + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isVectorRegister(reg3)); // mmm + assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); + assert(isValidUimm2(imm)); // ii + + if (opt == INS_OPTS_SCALABLE_B) + { + unreached(); // TODO-SVE: Not yet supported. + assert(isValidUimm2(imm)); // ii + fmt = IF_SVE_GY_3B_D; + } + else if (opt == INS_OPTS_SCALABLE_H) + { + assert(isValidUimm2(imm)); // ii + fmt = IF_SVE_GY_3B; + } + else + { + unreached(); // TODO-SVE: Not yet supported. + assert(insOptsNone(opt)); + assert(isValidUimm3(imm)); // i ii + + // Simplify emitDispInsHelp logic by setting insOpt + opt = INS_OPTS_SCALABLE_B; + fmt = IF_SVE_GY_3A; + } + break; + case INS_sve_bfdot: - assert(opt = INS_OPTS_SCALABLE_H); + assert(opt == INS_OPTS_SCALABLE_H); assert(isVectorRegister(reg1)); // ddddd assert(isVectorRegister(reg2)); // nnnnn assert(isVectorRegister(reg3)); // mmm @@ -12547,6 +12648,23 @@ void emitter::emitIns_R_R_R_I(instruction ins, fmt = IF_SVE_GP_3A; break; + case INS_sve_fmlalb: + case INS_sve_fmlalt: + case INS_sve_fmlslb: + case INS_sve_fmlslt: + case INS_sve_bfmlalb: + case INS_sve_bfmlalt: + case INS_sve_bfmlslb: + case INS_sve_bfmlslt: + assert(opt == INS_OPTS_SCALABLE_H); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isVectorRegister(reg3)); // mmm + assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); + assert(isValidUimm3(imm)); // ii i + fmt = IF_SVE_GZ_3A; + break; + default: unreached(); break; @@ -12791,6 +12909,19 @@ void emitter::emitIns_R_R_R_I_I(instruction ins, } break; + case INS_sve_fcmla: + assert(opt == INS_OPTS_SCALABLE_S); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isLowVectorRegister(reg3)); // mmmm + assert(isValidImm1(imm1)); // i + assert(isValidRot(imm2)); // rr + + // Convert imm2 from rotation value (0-270) to bitwise representation (0-3) + imm = (imm1 << 2) | emitEncodeRotationImm0_to_270(imm2); + fmt = IF_SVE_GV_3A; + break; + default: unreached(); break; @@ -14212,6 +14343,41 @@ void emitter::emitIns_R_R_R_R_I(instruction ins, fmt = IF_SVE_GT_4A; break; + case INS_sve_psel: + unreached(); // TODO-SVE: Not yet supported. + assert(insOptsScalableStandard(opt)); + assert(isPredicateRegister(reg1)); // DDDD + assert(isPredicateRegister(reg2)); // NNNN + assert(isPredicateRegister(reg3)); // MMMM + assert(isGeneralRegister(reg4)); // vv + assert((REG_R12 <= reg4) && (reg4 <= REG_R15)); + + switch (opt) + { + case INS_OPTS_SCALABLE_B: + assert(isValidUimm4(imm)); + break; + + case INS_OPTS_SCALABLE_H: + assert(isValidUimm3(imm)); + break; + + case INS_OPTS_SCALABLE_S: + assert(isValidUimm2(imm)); + break; + + case INS_OPTS_SCALABLE_D: + assert(isValidImm1(imm)); + break; + + default: + unreached(); + break; + } + + fmt = IF_SVE_DV_4A; + break; + default: unreached(); break; @@ -16238,7 +16404,7 @@ void emitter::emitIns_Call(EmitCallType callType, { assert(isVectorRegister(reg)); emitter::code_t ureg = (emitter::code_t)reg - (emitter::code_t)REG_V0; - assert((ureg >= 0) && (ureg <= 31)); + assert((ureg >= 0) && (ureg <= 15)); return ureg << 6; } @@ -17199,6 +17365,53 @@ void emitter::emitIns_Call(EmitCallType callType, return 0; } +/***************************************************************************** + * + * Returns the encoding for the field 'i1:tszh:tszl' at bit locations '23-22:20-18'. + */ + +/*static*/ emitter::code_t emitter::insEncodeSveElemsize_tszh_tszl_and_imm(const insOpts opt, const ssize_t imm) +{ + code_t encoding = 0; + + switch (opt) + { + case INS_OPTS_SCALABLE_B: + assert(isValidUimm4(imm)); + encoding = 0x040000; // set the bit at location 18 + // encode immediate at location 23-22:20-19 + encoding |= ((imm & 0b1100) << 22); + encoding |= ((imm & 0b11) << 19); + break; + + case INS_OPTS_SCALABLE_H: + assert(isValidUimm3(imm)); + encoding = 0x080000; // set the bit at location 19 + // encode immediate at location 23-22:20 + encoding |= ((imm & 0b110) << 22); + encoding |= ((imm & 1) << 20); + break; + + case INS_OPTS_SCALABLE_S: + assert(isValidUimm2(imm)); + encoding = 0x100000; // set the bit at location 20 + encoding |= (imm << 22); // encode immediate at location 23:22 + break; + + case INS_OPTS_SCALABLE_D: + assert(isValidImm1(imm)); + encoding = 0x400000; // set the bit at location 22 + encoding |= (imm << 23); // encode immediate at location 23 + break; + + default: + assert(!"Invalid size for vector register"); + break; + } + + return encoding; +} + /***************************************************************************** * * Returns the encoding to select the elemsize for an Arm64 SVE vector instruction plus an immediate. @@ -17643,6 +17856,10 @@ void emitter::emitIns_Call(EmitCallType callType, assert((regpos == 1) || (regpos == 2)); return ((regpos == 2) ? PREDICATE_ZERO : PREDICATE_SIZED); + case IF_SVE_DV_4A: + assert((regpos >= 1) && (regpos <= 3)); + return ((regpos == 3) ? PREDICATE_SIZED : PREDICATE_NONE); + case IF_SVE_ID_2A: case IF_SVE_JG_2A: return PREDICATE_NONE; @@ -21470,16 +21687,17 @@ BYTE* emitter::emitOutput_InstrSve(BYTE* dst, instrDesc* id) dst += emitOutput_Instr(dst, code); break; - case IF_SVE_EG_3A: // ...........iimmm ......nnnnnddddd -- SVE two-way dot product (indexed) - case IF_SVE_EY_3A: // ...........iimmm ......nnnnnddddd -- SVE integer dot product (indexed) - case IF_SVE_EZ_3A: // ...........iimmm ......nnnnnddddd -- SVE mixed sign dot product (indexed) - case IF_SVE_FD_3B: // ...........iimmm ......nnnnnddddd -- SVE2 integer multiply (indexed) - case IF_SVE_FF_3B: // ...........iimmm ......nnnnnddddd -- SVE2 integer multiply-add (indexed) - case IF_SVE_FI_3B: // ...........iimmm ......nnnnnddddd -- SVE2 saturating multiply high (indexed) - case IF_SVE_GU_3A: // ...........iimmm ......nnnnnddddd -- SVE floating-point multiply-add (indexed) - case IF_SVE_GX_3A: // ...........iimmm ......nnnnnddddd -- SVE floating-point multiply (indexed) - case IF_SVE_GY_3B: // ...........iimmm ......nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) - case IF_SVE_FK_3B: // ...........iimmm ......nnnnnddddd -- SVE2 saturating multiply-add high (indexed) + case IF_SVE_EG_3A: // ...........iimmm ......nnnnnddddd -- SVE two-way dot product (indexed) + case IF_SVE_EY_3A: // ...........iimmm ......nnnnnddddd -- SVE integer dot product (indexed) + case IF_SVE_EZ_3A: // ...........iimmm ......nnnnnddddd -- SVE mixed sign dot product (indexed) + case IF_SVE_FD_3B: // ...........iimmm ......nnnnnddddd -- SVE2 integer multiply (indexed) + case IF_SVE_FF_3B: // ...........iimmm ......nnnnnddddd -- SVE2 integer multiply-add (indexed) + case IF_SVE_FI_3B: // ...........iimmm ......nnnnnddddd -- SVE2 saturating multiply high (indexed) + case IF_SVE_GU_3A: // ...........iimmm ......nnnnnddddd -- SVE floating-point multiply-add (indexed) + case IF_SVE_GX_3A: // ...........iimmm ......nnnnnddddd -- SVE floating-point multiply (indexed) + case IF_SVE_GY_3B: // ...........iimmm ......nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) + case IF_SVE_GY_3B_D: // ...........iimmm ......nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) + case IF_SVE_FK_3B: // ...........iimmm ......nnnnnddddd -- SVE2 saturating multiply-add high (indexed) code = emitInsCodeSve(ins, fmt); code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd code |= insEncodeReg_V_9_to_5(id->idReg2()); // nnnnn @@ -21508,6 +21726,8 @@ BYTE* emitter::emitOutput_InstrSve(BYTE* dst, instrDesc* id) case IF_SVE_FG_3A: // ...........iimmm ....i.nnnnnddddd -- SVE2 integer multiply-add long (indexed) case IF_SVE_FH_3A: // ...........iimmm ....i.nnnnnddddd -- SVE2 saturating multiply (indexed) case IF_SVE_FJ_3A: // ...........iimmm ....i.nnnnnddddd -- SVE2 saturating multiply-add (indexed) + case IF_SVE_GY_3A: // ...........iimmm ....i.nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) + case IF_SVE_GZ_3A: // ...........iimmm ....i.nnnnnddddd -- SVE floating-point multiply-add long (indexed) imm = emitGetInsSC(id); code = emitInsCodeSve(ins, fmt); code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd @@ -21730,6 +21950,14 @@ BYTE* emitter::emitOutput_InstrSve(BYTE* dst, instrDesc* id) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_FZ_2A: // ................ ......nnnn.ddddd -- SME2 multi-vec extract narrow + case IF_SVE_HG_2A: // ................ ......nnnn.ddddd -- SVE2 FP8 downconverts + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= insEncodeReg_V_9_to_6(id->idReg2()); // nnnn + dst += emitOutput_Instr(dst, code); + break; + case IF_SVE_GD_2A: // .........x.xx... ......nnnnnddddd -- SVE2 saturating extract narrow code = emitInsCodeSve(ins, fmt); code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd @@ -21886,6 +22114,7 @@ BYTE* emitter::emitOutput_InstrSve(BYTE* dst, instrDesc* id) case IF_SVE_FA_3B: // ...........immmm ....rrnnnnnddddd -- SVE2 complex integer dot product (indexed) case IF_SVE_FB_3B: // ...........immmm ....rrnnnnnddddd -- SVE2 complex integer multiply-add (indexed) case IF_SVE_FC_3B: // ...........immmm ....rrnnnnnddddd -- SVE2 complex saturating multiply-add (indexed) + case IF_SVE_GV_3A: // ...........immmm ....rrnnnnnddddd -- SVE floating-point complex multiply-add (indexed) { const ssize_t imm = emitGetInsSC(id); const ssize_t rot = (imm & 0b11); @@ -21933,6 +22162,16 @@ BYTE* emitter::emitOutput_InstrSve(BYTE* dst, instrDesc* id) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_DV_4A: // ........ix.xxxvv ..NNNN.MMMM.DDDD -- SVE broadcast predicate element + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_3_to_0(id->idReg1()); // DDDD + code |= insEncodeReg_P_13_to_10(id->idReg2()); // NNNN + code |= insEncodeReg_P_8_to_5(id->idReg3()); // MMMM + code |= insEncodeReg_R_17_to_16(id->idReg4()); // vv + code |= insEncodeSveElemsize_tszh_tszl_and_imm(id->idInsOpt(), emitGetInsSC(id)); // ix xx + dst += emitOutput_Instr(dst, code); + break; + case IF_SVE_IH_3A: // ............iiii ...gggnnnnnttttt -- SVE contiguous load (quadwords, scalar plus // immediate) case IF_SVE_IH_3A_A: // ............iiii ...gggnnnnnttttt -- SVE contiguous load (quadwords, scalar plus @@ -24908,9 +25147,11 @@ void emitter::emitDispInsHelp( case IF_SVE_EG_3A: // ...........iimmm ......nnnnnddddd -- SVE two-way dot product (indexed) case IF_SVE_FG_3A: // ...........iimmm ....i.nnnnnddddd -- SVE2 integer multiply-add long (indexed) case IF_SVE_FJ_3A: // ...........iimmm ....i.nnnnnddddd -- SVE2 saturating multiply-add (indexed) + case IF_SVE_GZ_3A: // ...........iimmm ....i.nnnnnddddd -- SVE floating-point multiply-add long (indexed) // .S, .B, .B[] - case IF_SVE_EY_3A: // ...........iimmm ......nnnnnddddd -- SVE integer dot product (indexed) - case IF_SVE_EZ_3A: // ...........iimmm ......nnnnnddddd -- SVE mixed sign dot product (indexed) + case IF_SVE_EY_3A: // ...........iimmm ......nnnnnddddd -- SVE integer dot product (indexed) + case IF_SVE_EZ_3A: // ...........iimmm ......nnnnnddddd -- SVE mixed sign dot product (indexed) + case IF_SVE_GY_3B_D: // ...........iimmm ......nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) // .S, .H, .H[] case IF_SVE_FE_3A: // ...........iimmm ....i.nnnnnddddd -- SVE2 integer multiply long (indexed) case IF_SVE_FH_3A: // ...........iimmm ....i.nnnnnddddd -- SVE2 saturating multiply (indexed) @@ -24946,6 +25187,14 @@ void emitter::emitDispInsHelp( emitDispElementIndex(emitGetInsSC(id), false); // ii break; + // .H, .B, .B[] + case IF_SVE_GY_3A: // ...........iimmm ....i.nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) + emitDispSveReg(id->idReg1(), INS_OPTS_SCALABLE_H, true); // ddddd + emitDispSveReg(id->idReg2(), id->idInsOpt(), true); // nnnnn + emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmm + emitDispElementIndex(emitGetInsSC(id), false); // iii + break; + // .H, .H, .H[] case IF_SVE_FD_3A: // .........i.iimmm ......nnnnnddddd -- SVE2 integer multiply (indexed) case IF_SVE_FI_3A: // .........i.iimmm ......nnnnnddddd -- SVE2 saturating multiply high (indexed) @@ -25157,6 +25406,26 @@ void emitter::emitDispInsHelp( emitDispReg(id->idReg2(), id->idOpSize(), false); // mmmmm break; + // .H, {.S-.S } + case IF_SVE_FZ_2A: // ................ ......nnnn.ddddd -- SME2 multi-vec extract narrow + { + emitDispSveReg(id->idReg1(), INS_OPTS_SCALABLE_H, true); + const unsigned baseRegNum = id->idReg2() - REG_FP_FIRST; + const regNumber regNum = (regNumber)((baseRegNum * 2) + REG_FP_FIRST); + emitDispSveConsecutiveRegList(regNum, 2, INS_OPTS_SCALABLE_S, false); + break; + } + + // .B, {.H-.H } + case IF_SVE_HG_2A: // ................ ......nnnn.ddddd -- SVE2 FP8 downconverts + { + emitDispSveReg(id->idReg1(), INS_OPTS_SCALABLE_B, true); + const unsigned baseRegNum = id->idReg2() - REG_FP_FIRST; + const regNumber regNum = (regNumber)((baseRegNum * 2) + REG_FP_FIRST); + emitDispSveConsecutiveRegList(regNum, 2, INS_OPTS_SCALABLE_H, false); + break; + } + // ., . case IF_SVE_GD_2A: // .........x.xx... ......nnnnnddddd -- SVE2 saturating extract narrow emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd @@ -25225,6 +25494,17 @@ void emitter::emitDispInsHelp( emitDispReg(id->idReg3(), id->idOpSize(), false); // mmmmm break; + // , , .[, ] + case IF_SVE_DV_4A: // ........ix.xxxvv ..NNNN.MMMM.DDDD -- SVE broadcast predicate element + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt, 1), id->idInsOpt(), true); // DDDD + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt, 2), id->idInsOpt(), true); // NNNN + emitDispPredicateReg(id->idReg3(), insGetPredicateType(fmt, 3), id->idInsOpt(), false); // MMMM + printf("["); + emitDispReg(id->idReg4(), EA_4BYTE, true); // vv + emitDispImm(emitGetInsSC(id), false); // ix xx + printf("]"); + break; + // ., [] case IF_SVE_DW_2A: // ........xx...... ......iiNNN.DDDD -- SVE extract mask predicate from predicate-as-counter emitDispPredicateReg(id->idReg1(), PREDICATE_SIZED, id->idInsOpt(), true); // DDDD @@ -25369,6 +25649,8 @@ void emitter::emitDispInsHelp( case IF_SVE_FC_3A: // ...........iimmm ....rrnnnnnddddd -- SVE2 complex saturating multiply-add (indexed) // SQRDCMLAH .S, .S, .S[], case IF_SVE_FC_3B: // ...........immmm ....rrnnnnnddddd -- SVE2 complex saturating multiply-add (indexed) + // FCMLA .S, .S, .S[], + case IF_SVE_GV_3A: // ...........immmm ....rrnnnnnddddd -- SVE floating-point complex multiply-add (indexed) { const ssize_t imm = emitGetInsSC(id); const ssize_t rot = (imm & 0b11); @@ -28289,6 +28571,30 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insThroughput = PERFSCORE_THROUGHPUT_1C; break; + case IF_SVE_GZ_3A: // ...........iimmm ....i.nnnnnddddd -- SVE floating-point multiply-add long (indexed) + switch (ins) + { + case INS_sve_fmlalb: + case INS_sve_fmlalt: + case INS_sve_fmlslb: + case INS_sve_fmlslt: + case INS_sve_bfmlalb: + case INS_sve_bfmlalt: + result.insThroughput = PERFSCORE_THROUGHPUT_2C; + result.insLatency = PERFSCORE_LATENCY_4C; + break; + case INS_sve_bfmlslb: + case INS_sve_bfmlslt: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + default: + // all other instructions + perfScoreUnhandledInstruction(id, &result); + break; + } + break; + case IF_SVE_EZ_3A: // ...........iimmm ......nnnnnddddd -- SVE mixed sign dot product (indexed) result.insLatency = PERFSCORE_LATENCY_3C; result.insThroughput = PERFSCORE_THROUGHPUT_2C; @@ -28647,6 +28953,34 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insLatency = PERFSCORE_LATENCY_1C; break; + case IF_SVE_DV_4A: // ........ix.xxxvv ..NNNN.MMMM.DDDD -- SVE broadcast predicate element + case IF_SVE_FZ_2A: // ................ ......nnnn.ddddd -- SME2 multi-vec extract narrow + case IF_SVE_GY_3A: // ...........iimmm ....i.nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) + case IF_SVE_GY_3B_D: // ...........iimmm ......nnnnnddddd -- SVE BFloat16 floating-point dot product (indexed) + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + + case IF_SVE_HG_2A: // ................ ......nnnn.ddddd -- SVE2 FP8 downconverts + switch (ins) + { + case INS_sve_fcvtnt: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_3C; + break; + case INS_sve_fcvtn: + case INS_sve_bfcvtn: + case INS_sve_fcvtnb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + default: + // all other instructions + perfScoreUnhandledInstruction(id, &result); + break; + } + break; + // Not available in Arm Neoverse N2 Software Optimization Guide. case IF_SVE_AG_3A: // ........xx...... ...gggnnnnnddddd -- SVE bitwise logical reduction (quadwords) case IF_SVE_AJ_3A: // ........xx...... ...gggnnnnnddddd -- SVE integer add reduction (quadwords) @@ -29396,6 +29730,7 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insLatency = PERFSCORE_LATENCY_3C; break; + case IF_SVE_GV_3A: // ...........immmm ....rrnnnnnddddd -- SVE floating-point complex multiply-add (indexed) case IF_SVE_GT_4A: // ........xx.mmmmm .rrgggnnnnnddddd -- SVE floating-point complex multiply-add (predicated) result.insThroughput = PERFSCORE_THROUGHPUT_2C; result.insLatency = PERFSCORE_LATENCY_5C; diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index e12e405b8e21bc..f224008c4f64f7 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -533,6 +533,9 @@ static code_t insEncodeSveElemsize_sz_21(emitAttr size); // This specifically encodes the field 'tszh:tszl' at bit locations '22:20-19'. static code_t insEncodeSveElemsize_tszh_22_tszl_20_to_19(emitAttr size); +// Returns the encoding for the field 'i1:tszh:tszl' at bit locations '23-22:20-18'. +static code_t insEncodeSveElemsize_tszh_tszl_and_imm(const insOpts opt, const ssize_t imm); + // Returns the encoding to select the constant values 90 or 270 for an Arm64 SVE vector instruction // This specifically encode the field 'rot' at bit location '16'. static code_t insEncodeSveImm90_or_270_rot(ssize_t imm); From 741c0c891dfedf74f71bd5da57e1a101ede09df2 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Thu, 15 Feb 2024 08:28:33 +0100 Subject: [PATCH 047/158] Fix exceptions thrown by LDiv/LMod helpers (#98474) --- .../Internal/Runtime/CompilerHelpers/MathHelpers.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs index d7195891320f00..7175ea9c00cbe2 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs @@ -164,6 +164,8 @@ public static long LMod(long i, long j) { if (j == 0) return ThrowLngDivByZero(); + else if (j == -1 && i == long.MinValue) + return ThrowLngOvf(); else return RhpLMod(i, j); } @@ -189,7 +191,7 @@ public static long LDiv(long i, long j) if (j == 0) return ThrowLngDivByZero(); else if (j == -1 && i == long.MinValue) - return ThrowLngArithExc(); + return ThrowLngOvf(); else return RhpLDiv(i, j); } @@ -205,12 +207,6 @@ private static ulong ThrowULngDivByZero() { throw new DivideByZeroException(); } - - [MethodImpl(MethodImplOptions.NoInlining)] - private static long ThrowLngArithExc() - { - throw new ArithmeticException(); - } #endif // TARGET_64BIT [RuntimeExport("Dbl2IntOvf")] From 96e5e6e50919a4ad4c12bec2d48b310d8578550b Mon Sep 17 00:00:00 2001 From: David Mason Date: Thu, 15 Feb 2024 00:24:18 -0800 Subject: [PATCH 048/158] Fix deadlock when we request a ReJIT due to an inlined method (#98400) --- src/coreclr/vm/jitinterface.cpp | 35 +++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index f2f7d229d546f1..20c8321a7c4960 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -8241,17 +8241,32 @@ void CEEInfo::reportInliningDecision (CORINFO_METHOD_HANDLE inlinerHnd, if (CORProfilerEnableRejit()) { - // If ReJIT is enabled, there is a chance that a race happened where the profiler - // requested a ReJIT on a method, but before the ReJIT occurred an inlining happened. - // If we end up reporting an inlining on a method with non-default IL it means the race - // happened and we need to manually request ReJIT for it since it was missed. - CodeVersionManager* pCodeVersionManager = pCallee->GetCodeVersionManager(); - CodeVersionManager::LockHolder codeVersioningLockHolder; - ILCodeVersion ilVersion = pCodeVersionManager->GetActiveILCodeVersion(pCallee); - if (ilVersion.GetRejitState() != ILCodeVersion::kStateActive || !ilVersion.HasDefaultIL()) + ModuleID modId = 0; + mdMethodDef methodDef = mdMethodDefNil; + BOOL shouldCallReJIT = FALSE; + + { + // If ReJIT is enabled, there is a chance that a race happened where the profiler + // requested a ReJIT on a method, but before the ReJIT occurred an inlining happened. + // If we end up reporting an inlining on a method with non-default IL it means the race + // happened and we need to manually request ReJIT for it since it was missed. + CodeVersionManager* pCodeVersionManager = pCallee->GetCodeVersionManager(); + CodeVersionManager::LockHolder codeVersioningLockHolder; + ILCodeVersion ilVersion = pCodeVersionManager->GetActiveILCodeVersion(pCallee); + if (ilVersion.GetRejitState() != ILCodeVersion::kStateActive || !ilVersion.HasDefaultIL()) + { + shouldCallReJIT = TRUE; + modId = reinterpret_cast(pCaller->GetModule()); + methodDef = pCaller->GetMemberDef(); + // Do Not call RequestReJIT inside this scope, calling RequestReJIT while holding the CodeVersionManager lock + // will cause deadlocks with other threads calling RequestReJIT since it tries to obtain the CodeVersionManager lock + } + } + + if (shouldCallReJIT) { - ModuleID modId = reinterpret_cast(pCaller->GetModule()); - mdMethodDef methodDef = pCaller->GetMemberDef(); + _ASSERTE(modId != 0); + _ASSERTE(methodDef != mdMethodDefNil); ReJitManager::RequestReJIT(1, &modId, &methodDef, static_cast(0)); } } From 988958fec561240b665b28913dfadd8c14f5fb43 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 15 Feb 2024 10:31:44 +0100 Subject: [PATCH 049/158] JIT: Simplify formatting strings for various "reason" fields (#98434) Add a `Compiler::printfAlloc` that makes it simpler to create more insightful strings to be stored in `LclVarDsc` and other places. --- src/coreclr/jit/compiler.cpp | 25 ++++++++++++++++++ src/coreclr/jit/compiler.h | 2 ++ src/coreclr/jit/decomposelongs.cpp | 17 +++--------- src/coreclr/jit/lclvars.cpp | 8 ++---- src/coreclr/jit/promotion.cpp | 9 ++----- src/coreclr/jit/rangecheck.cpp | 42 +++++++++++++----------------- src/coreclr/jit/rangecheck.h | 17 ++++-------- 7 files changed, 58 insertions(+), 62 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index f48c3a76943daf..e182debb628fb4 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -10615,6 +10615,31 @@ const char* Compiler::devirtualizationDetailToString(CORINFO_DEVIRTUALIZATION_DE return "undefined"; } } + +//------------------------------------------------------------------------------ +// printfAlloc: printf a string and allocate the result in CMK_DebugOnly +// memory. +// +// Arguments: +// format - Format string +// +// Returns: +// Allocated string. +// +const char* Compiler::printfAlloc(const char* format, ...) +{ + char str[512]; + va_list args; + va_start(args, format); + int result = vsprintf_s(str, ArrLen(str), format, args); + va_end(args); + assert((result >= 0) && ((unsigned)result < ArrLen(str))); + + char* resultStr = new (this, CMK_DebugOnly) char[result + 1]; + memcpy(resultStr, str, (unsigned)result + 1); + return resultStr; +} + #endif // defined(DEBUG) #if TRACK_ENREG_STATS diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 26ade9a4683f0d..ed2a7dac333b91 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9988,6 +9988,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX const char* devirtualizationDetailToString(CORINFO_DEVIRTUALIZATION_DETAIL detail); + const char* printfAlloc(const char* format, ...); + #endif // DEBUG // clang-format off diff --git a/src/coreclr/jit/decomposelongs.cpp b/src/coreclr/jit/decomposelongs.cpp index c7b28b15a5c63e..2f05779f9ff137 100644 --- a/src/coreclr/jit/decomposelongs.cpp +++ b/src/coreclr/jit/decomposelongs.cpp @@ -2171,20 +2171,11 @@ void DecomposeLongs::TryPromoteLongVar(unsigned lclNum) // Grab the temp for the field local. CLANG_FORMAT_COMMENT_ANCHOR; -#ifdef DEBUG - char buf[200]; - sprintf_s(buf, sizeof(buf), "%s V%02u.%s (fldOffset=0x%x)", "field", lclNum, index == 0 ? "lo" : "hi", - index * 4); - - // We need to copy 'buf' as lvaGrabTemp() below caches a copy to its argument. - size_t len = strlen(buf) + 1; - char* bufp = m_compiler->getAllocator(CMK_DebugOnly).allocate(len); - strcpy_s(bufp, len, buf); -#endif - // Lifetime of field locals might span multiple BBs, so they are long lifetime temps. - unsigned fieldLclNum = m_compiler->lvaGrabTemp(false DEBUGARG(bufp)); - varDsc = m_compiler->lvaGetDesc(lclNum); + unsigned fieldLclNum = m_compiler->lvaGrabTemp( + false DEBUGARG(m_compiler->printfAlloc("%s V%02u.%s (fldOffset=0x%x)", "field", lclNum, + index == 0 ? "lo" : "hi", index * 4))); + varDsc = m_compiler->lvaGetDesc(lclNum); LclVarDsc* fieldVarDsc = m_compiler->lvaGetDesc(fieldLclNum); fieldVarDsc->lvType = TYP_INT; diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 1cf64495ec0ecb..55787580cc1df7 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2357,16 +2357,12 @@ void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum) // Now grab the temp for the field local. #ifdef DEBUG - char buf[200]; char fieldNameBuffer[128]; const char* fieldName = compiler->eeGetFieldName(pFieldInfo->diagFldHnd, false, fieldNameBuffer, sizeof(fieldNameBuffer)); - sprintf_s(buf, sizeof(buf), "field V%02u.%s (fldOffset=0x%x)", lclNum, fieldName, pFieldInfo->fldOffset); - // We need to copy 'buf' as lvaGrabTemp() below caches a copy to its argument. - size_t len = strlen(buf) + 1; - char* bufp = compiler->getAllocator(CMK_DebugOnly).allocate(len); - strcpy_s(bufp, len, buf); + const char* bufp = + compiler->printfAlloc("field V%02u.%s (fldOffset=0x%x)", lclNum, fieldName, pFieldInfo->fldOffset); if (index > 0) { diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index e11d0619905e76..f66748633a69e3 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -1219,13 +1219,8 @@ class LocalsUseVisitor : public GenTreeVisitor for (Replacement& rep : reps) { #ifdef DEBUG - char buf[32]; - sprintf_s(buf, sizeof(buf), "V%02u.[%03u..%03u)", agg->LclNum, rep.Offset, - rep.Offset + genTypeSize(rep.AccessType)); - size_t len = strlen(buf) + 1; - char* bufp = new (m_compiler, CMK_DebugOnly) char[len]; - strcpy_s(bufp, len, buf); - rep.Description = bufp; + rep.Description = m_compiler->printfAlloc("V%02u.[%03u..%03u)", agg->LclNum, rep.Offset, + rep.Offset + genTypeSize(rep.AccessType)); #endif rep.LclNum = m_compiler->lvaGrabTemp(false DEBUGARG(rep.Description)); diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 6ac1c1c01646d8..475df2d659cabc 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -96,7 +96,7 @@ bool RangeCheck::BetweenBounds(Range& range, GenTree* upper, int arrSize) #ifdef DEBUG if (m_pCompiler->verbose) { - printf("%s BetweenBounds <%d, ", range.ToString(m_pCompiler->getAllocatorDebugOnly()), 0); + printf("%s BetweenBounds <%d, ", range.ToString(m_pCompiler), 0); Compiler::printTreeID(upper); printf(">\n"); } @@ -359,7 +359,7 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree* return; } - JITDUMP("Range value %s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP("Range value %s\n", range.ToString(m_pCompiler)); m_pSearchPath->RemoveAll(); Widen(block, treeIndex, &range); @@ -917,7 +917,7 @@ void RangeCheck::MergeEdgeAssertions(ValueNum normalLclVN, ASSERT_VALARG_TP asse break; } JITDUMP("The range after edge merging:"); - JITDUMP(pRange->ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP(pRange->ToString(m_pCompiler)); JITDUMP("\n"); } } @@ -1007,7 +1007,7 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool if (icon >= 0) { Range range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, icon)); - JITDUMP("Limit range to %s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP("Limit range to %s\n", range.ToString(m_pCompiler)); return range; } // Generalized range computation not implemented for these operators @@ -1068,32 +1068,28 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool if (binop->OperIs(GT_ADD)) { r = RangeOps::Add(op1Range, op2Range); - JITDUMP("BinOp add ranges %s %s = %s\n", op1Range.ToString(m_pCompiler->getAllocatorDebugOnly()), - op2Range.ToString(m_pCompiler->getAllocatorDebugOnly()), - r.ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP("BinOp add ranges %s %s = %s\n", op1Range.ToString(m_pCompiler), op2Range.ToString(m_pCompiler), + r.ToString(m_pCompiler)); } else if (binop->OperIs(GT_MUL)) { r = RangeOps::Multiply(op1Range, op2Range); - JITDUMP("BinOp multiply ranges %s %s = %s\n", op1Range.ToString(m_pCompiler->getAllocatorDebugOnly()), - op2Range.ToString(m_pCompiler->getAllocatorDebugOnly()), - r.ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP("BinOp multiply ranges %s %s = %s\n", op1Range.ToString(m_pCompiler), op2Range.ToString(m_pCompiler), + r.ToString(m_pCompiler)); } else if (binop->OperIs(GT_LSH)) { // help the next step a bit, convert the LSH rhs to a multiply Range convertedOp2Range = RangeOps::ConvertShiftToMultiply(op2Range); r = RangeOps::Multiply(op1Range, convertedOp2Range); - JITDUMP("BinOp multiply ranges %s %s = %s\n", op1Range.ToString(m_pCompiler->getAllocatorDebugOnly()), - convertedOp2Range.ToString(m_pCompiler->getAllocatorDebugOnly()), - r.ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP("BinOp multiply ranges %s %s = %s\n", op1Range.ToString(m_pCompiler), + convertedOp2Range.ToString(m_pCompiler), r.ToString(m_pCompiler)); } else if (binop->OperIs(GT_RSH)) { r = RangeOps::ShiftRight(op1Range, op2Range); - JITDUMP("Right shift range: %s >> %s = %s\n", op1Range.ToString(m_pCompiler->getAllocatorDebugOnly()), - op2Range.ToString(m_pCompiler->getAllocatorDebugOnly()), - r.ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP("Right shift range: %s >> %s = %s\n", op1Range.ToString(m_pCompiler), op2Range.ToString(m_pCompiler), + r.ToString(m_pCompiler)); } return r; } @@ -1265,9 +1261,8 @@ bool RangeCheck::DoesBinOpOverflow(BasicBlock* block, GenTreeOp* binop) return true; } - JITDUMP("Checking bin op overflow %s %s %s\n", GenTree::OpName(binop->OperGet()), - op1Range->ToString(m_pCompiler->getAllocatorDebugOnly()), - op2Range->ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP("Checking bin op overflow %s %s %s\n", GenTree::OpName(binop->OperGet()), op1Range->ToString(m_pCompiler), + op2Range->ToString(m_pCompiler)); if (binop->OperIs(GT_ADD)) { @@ -1482,16 +1477,15 @@ Range RangeCheck::ComputeRange(BasicBlock* block, GenTree* expr, bool monIncreas assert(!argRange.LowerLimit().IsUndef()); assert(!argRange.UpperLimit().IsUndef()); MergeAssertion(block, use.GetNode(), &argRange DEBUGARG(indent + 1)); - JITDUMP("Merging ranges %s %s:", range.ToString(m_pCompiler->getAllocatorDebugOnly()), - argRange.ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP("Merging ranges %s %s:", range.ToString(m_pCompiler), argRange.ToString(m_pCompiler)); range = RangeOps::Merge(range, argRange, monIncreasing); - JITDUMP("%s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP("%s\n", range.ToString(m_pCompiler)); } } else if (varTypeIsSmall(expr)) { range = GetRangeFromType(expr->TypeGet()); - JITDUMP("%s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly())); + JITDUMP("%s\n", range.ToString(m_pCompiler)); } else if (expr->OperIs(GT_COMMA)) { @@ -1546,7 +1540,7 @@ Range RangeCheck::GetRange(BasicBlock* block, GenTree* expr, bool monIncreasing { Indent(indent); JITDUMP(" %s Range [%06d] => %s\n", (pRange == nullptr) ? "Computed" : "Cached", Compiler::dspTreeID(expr), - range.ToString(m_pCompiler->getAllocatorDebugOnly())); + range.ToString(m_pCompiler)); Indent(indent); JITDUMP("}\n", expr); } diff --git a/src/coreclr/jit/rangecheck.h b/src/coreclr/jit/rangecheck.h index 8e63147c312258..098e1cc62b0d7a 100644 --- a/src/coreclr/jit/rangecheck.h +++ b/src/coreclr/jit/rangecheck.h @@ -209,10 +209,8 @@ struct Limit return false; } #ifdef DEBUG - const char* ToString(CompAllocator alloc) + const char* ToString(Compiler* comp) { - unsigned size = 64; - char* buf = alloc.allocate(size); switch (type) { case keUndef: @@ -225,12 +223,10 @@ struct Limit return "Dependent"; case keBinOpArray: - sprintf_s(buf, size, FMT_VN " + %d", vn, cns); - return buf; + return comp->printfAlloc(FMT_VN " + %d", vn, cns); case keConstant: - sprintf_s(buf, size, "%d", cns); - return buf; + return comp->printfAlloc("%d", cns); } unreached(); } @@ -265,12 +261,9 @@ struct Range } #ifdef DEBUG - char* ToString(CompAllocator alloc) + const char* ToString(Compiler* comp) { - size_t size = 64; - char* buf = alloc.allocate(size); - sprintf_s(buf, size, "<%s, %s>", lLimit.ToString(alloc), uLimit.ToString(alloc)); - return buf; + return comp->printfAlloc("<%s, %s>", lLimit.ToString(comp), uLimit.ToString(comp)); } #endif }; From c34474e0ae4549b664148e929720a7b6dbf4f37b Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Thu, 15 Feb 2024 12:52:33 +0100 Subject: [PATCH 050/158] [browser] Move reflection from JS to C# (#98391) --- .../src/Interop/Browser/Interop.Runtime.cs | 7 +- .../src/Resources/Strings.resx | 4 +- .../JavaScript/Interop/JavaScriptExports.cs | 101 +++---- .../Interop/JavaScriptImports.Generated.cs | 4 + .../JavaScript/JSFunctionBinding.cs | 24 +- .../JavaScript/JSHostImplementation.cs | 210 +++++++++++++- src/mono/browser/runtime/corebindings.c | 20 +- src/mono/browser/runtime/cwraps.ts | 10 +- src/mono/browser/runtime/debug.ts | 6 +- src/mono/browser/runtime/driver.c | 82 +----- src/mono/browser/runtime/exports-binding.ts | 2 - src/mono/browser/runtime/exports-internal.ts | 2 + src/mono/browser/runtime/gc-lock.ts | 13 +- src/mono/browser/runtime/invoke-cs.ts | 269 ++++++------------ src/mono/browser/runtime/invoke-js.ts | 2 + .../browser/runtime/jiterpreter-tables.ts | 3 + src/mono/browser/runtime/managed-exports.ts | 77 +++-- src/mono/browser/runtime/marshal-to-cs.ts | 13 +- src/mono/browser/runtime/marshal-to-js.ts | 3 + src/mono/browser/runtime/roots.ts | 26 +- src/mono/browser/runtime/run.ts | 34 +-- src/mono/browser/runtime/runtime.c | 46 +-- src/mono/browser/runtime/strings.ts | 4 +- 23 files changed, 471 insertions(+), 491 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 01888e365ee0cb..000dd103d21890 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Reflection; using System.Runtime.CompilerServices; internal static partial class Interop @@ -25,8 +26,6 @@ internal static unsafe partial class Runtime public static extern void InvokeJSFunctionSend(nint targetNativeTID, nint functionHandle, nint data); #endif - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern unsafe void BindCSFunction(in string fully_qualified_name, int signature_hash, void* signature, out int is_exception, out object result); [MethodImpl(MethodImplOptions.InternalCall)] public static extern void ResolveOrRejectPromise(nint data); #if FEATURE_WASM_MANAGED_THREADS @@ -65,7 +64,7 @@ internal static unsafe partial class Runtime [MethodImpl(MethodImplOptions.InternalCall)] public static extern void CancelPromise(nint gcHandle); #endif - - + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern void SetEntryAssembly(Assembly assembly, int entryPointMetadataToken); } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx index 9cb71cc66223ad..e77e419997996d 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx @@ -138,8 +138,8 @@ Managed entrypoint handle is not set. - - Cannot resolve managed entrypoint handle. + + Cannot resolve managed entrypoint {0} in assembly {1}. Return type '{0}' from main method in not supported. diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs index f4a6efb3c333f3..9ec4a2400cd211 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs @@ -1,14 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using static System.Runtime.InteropServices.JavaScript.JSHostImplementation; namespace System.Runtime.InteropServices.JavaScript { @@ -18,13 +15,14 @@ namespace System.Runtime.InteropServices.JavaScript internal static unsafe partial class JavaScriptExports { // the marshaled signature is: - // Task? CallEntrypoint(MonoMethod* entrypointPtr, string[] args) + // Task? CallEntrypoint(string mainAssemblyName, string[] args, bool waitForDebugger) public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() ref JSMarshalerArgument arg_result = ref arguments_buffer[1]; // initialized by caller in alloc_stack_frame() ref JSMarshalerArgument arg_1 = ref arguments_buffer[2]; // initialized and set by caller ref JSMarshalerArgument arg_2 = ref arguments_buffer[3]; // initialized and set by caller + ref JSMarshalerArgument arg_3 = ref arguments_buffer[4]; // initialized and set by caller try { #if FEATURE_WASM_MANAGED_THREADS @@ -32,62 +30,12 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer) arg_exc.AssertCurrentThreadContext(); #endif - arg_1.ToManaged(out IntPtr entrypointPtr); - if (entrypointPtr == IntPtr.Zero) - { - throw new MissingMethodException(SR.MissingManagedEntrypointHandle); - } + arg_1.ToManaged(out string? mainAssemblyName); + arg_2.ToManaged(out string?[]? args); + arg_3.ToManaged(out bool waitForDebugger); - RuntimeMethodHandle methodHandle = GetMethodHandleFromIntPtr(entrypointPtr); - // this would not work for generic types. But Main() could not be generic, so we are fine. - MethodInfo? method = MethodBase.GetMethodFromHandle(methodHandle) as MethodInfo; - if (method == null) - { - throw new InvalidOperationException(SR.CannotResolveManagedEntrypointHandle); - } + Task? result = JSHostImplementation.CallEntrypoint(mainAssemblyName, args, waitForDebugger); - arg_2.ToManaged(out string?[]? args); - object[] argsToPass = System.Array.Empty(); - Task? result = null; - var parameterInfos = method.GetParameters(); - if (parameterInfos.Length > 0 && parameterInfos[0].ParameterType == typeof(string[])) - { - argsToPass = new object[] { args ?? System.Array.Empty() }; - } - if (method.ReturnType == typeof(void)) - { - method.Invoke(null, argsToPass); - } - else if (method.ReturnType == typeof(int)) - { - int intResult = (int)method.Invoke(null, argsToPass)!; - result = Task.FromResult(intResult); - } - else if (method.ReturnType == typeof(Task)) - { - Task methodResult = (Task)method.Invoke(null, argsToPass)!; - TaskCompletionSource tcs = new TaskCompletionSource(); - result = tcs.Task; - methodResult.ContinueWith((t) => - { - if (t.IsFaulted) - { - tcs.SetException(t.Exception!); - } - else - { - tcs.SetResult(0); - } - }, TaskScheduler.Default); - } - else if (method.ReturnType == typeof(Task)) - { - result = (Task)method.Invoke(null, argsToPass)!; - } - else - { - throw new InvalidOperationException(SR.Format(SR.ReturnTypeNotSupportedForMain, method.ReturnType.FullName)); - } arg_result.ToJS(result, (ref JSMarshalerArgument arg, int value) => { arg.ToJS(value); @@ -95,10 +43,7 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer) } catch (Exception ex) { - if (ex is TargetInvocationException refEx && refEx.InnerException != null) - ex = refEx.InnerException; - - arg_exc.ToJS(ex); + Environment.FailFast("CallEntrypoint: Unexpected synchronous failure. " + ex); } } @@ -163,7 +108,7 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments } catch (Exception ex) { - arg_exc.ToJS(ex); + Environment.FailFast("ReleaseJSOwnedObjectByGCHandle: Unexpected synchronous failure. " + ex); } } @@ -188,7 +133,7 @@ public static void CallDelegate(JSMarshalerArgument* arguments_buffer) #endif GCHandle callback_gc_handle = (GCHandle)arg_1.slot.GCHandle; - if (callback_gc_handle.Target is ToManagedCallback callback) + if (callback_gc_handle.Target is JSHostImplementation.ToManagedCallback callback) { // arg_2, arg_3, arg_4, arg_res are processed by the callback callback(arguments_buffer); @@ -219,7 +164,7 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer) // when we arrive here, we are on the thread which owns the proxies var ctx = arg_exc.AssertCurrentThreadContext(); var holder = ctx.GetPromiseHolder(arg_1.slot.GCHandle); - ToManagedCallback callback; + JSHostImplementation.ToManagedCallback callback; #if FEATURE_WASM_MANAGED_THREADS lock (ctx) @@ -260,7 +205,7 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer) } catch (Exception ex) { - arg_exc.ToJS(ex); + Environment.FailFast("CompleteTask: Unexpected synchronous failure. " + ex); } } @@ -318,6 +263,30 @@ public static void InstallMainSynchronizationContext(JSMarshalerArgument* argume #endif + // the marshaled signature is: + // Task BindAssemblyExports(string assemblyName) + public static void BindAssemblyExports(JSMarshalerArgument* arguments_buffer) + { + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() + ref JSMarshalerArgument arg_result = ref arguments_buffer[1]; // used as return value + ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller + try + { + string? assemblyName; + // when we arrive here, we are on the thread which owns the proxies + arg_exc.AssertCurrentThreadContext(); + arg_1.ToManaged(out assemblyName); + + var result = JSHostImplementation.BindAssemblyExports(assemblyName); + + arg_result.ToJS(result); + } + catch (Exception ex) + { + Environment.FailFast("BindAssemblyExports: Unexpected synchronous failure. " + ex); + } + } + [MethodImpl(MethodImplOptions.NoInlining)] // profiler needs to find it executed under this name public static void StopProfile() { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs index 8c5c3782a2f379..2e41abd76c5c1e 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs @@ -45,6 +45,10 @@ internal static unsafe partial class JavaScriptImports [JSImport("INTERNAL.dynamic_import")] // TODO: the continuation should be running on deputy or TP in MT public static partial Task DynamicImport(string moduleName, string moduleUrl); + + [JSImport("INTERNAL.mono_wasm_bind_cs_function")] + public static partial void BindCSFunction(IntPtr monoMethod, string assemblyName, string namespaceName, string shortClassName, string methodName, int signatureHash, IntPtr signature); + #if FEATURE_WASM_MANAGED_THREADS [JSImport("INTERNAL.thread_available")] // TODO: the continuation should be running on deputy or TP in MT diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs index acbd4a92ef9042..ae432cafc323cc 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs @@ -191,10 +191,11 @@ public static JSFunctionBinding BindManagedFunction(string fullyQualifiedName, i { if (RuntimeInformation.OSArchitecture != Architecture.Wasm) throw new PlatformNotSupportedException(); -#if FEATURE_WASM_MANAGED_THREADS - JSProxyContext.AssertIsInteropThread(); -#endif - return BindManagedFunctionImpl(fullyQualifiedName, signatureHash, signatures); + + // this could be called by assembly module initializer from Net7 code-gen + // on wrong thread, in which case we will bind it to UI thread + + return JSHostImplementation.BindManagedFunction(fullyQualifiedName, signatureHash, signatures); } #if !DEBUG @@ -407,21 +408,6 @@ internal static unsafe JSFunctionBinding BindJSImportImpl(string functionName, s return signature; } - internal static unsafe JSFunctionBinding BindManagedFunctionImpl(string fullyQualifiedName, int signatureHash, ReadOnlySpan signatures) - { - var signature = JSHostImplementation.GetMethodSignature(signatures, null, null); - - Interop.Runtime.BindCSFunction(fullyQualifiedName, signatureHash, signature.Header, out int isException, out object exceptionMessage); - if (isException != 0) - { - throw new JSException((string)exceptionMessage); - } - - JSHostImplementation.FreeMethodSignatureBuffer(signature); - - return signature; - } - #if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs index 3035781eb730f1..7268951b68828b 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs @@ -33,14 +33,6 @@ public static bool GetTaskResultDynamic(Task task, out object? value) throw new InvalidOperationException(); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RuntimeMethodHandle GetMethodHandleFromIntPtr(IntPtr ptr) - { - var temp = new IntPtrAndHandle { ptr = ptr }; - return temp.methodHandle; - } - /// /// Gets the MethodInfo for the Task{T}.Result property getter. /// @@ -208,6 +200,176 @@ public static void LoadSatelliteAssembly(byte[] dllBytes) AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(dllBytes)); } + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Dynamic access from JavaScript")] + [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Dynamic access from JavaScript")] + public static Task? CallEntrypoint(string? assemblyName, string?[]? args, bool waitForDebugger) + { + try + { + if (string.IsNullOrEmpty(assemblyName)) + { + throw new MissingMethodException(SR.MissingManagedEntrypointHandle); + } + if (!assemblyName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)) + { + assemblyName += ".dll"; + } + Assembly mainAssembly = Assembly.LoadFrom(assemblyName); + + MethodInfo? method = mainAssembly.EntryPoint; + if (method == null) + { + throw new InvalidOperationException(string.Format(SR.CannotResolveManagedEntrypoint, "Main", assemblyName)); + } + if (method.IsSpecialName) + { + // we are looking for the original async method, rather than for the compiler generated wrapper like
+ // because we need to yield to browser event loop + var type = method.DeclaringType!; + var name = method.Name; + var asyncName = name + "$"; + method = type.GetMethod(asyncName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + if (method == null) + { + asyncName = name.Substring(1, name.Length - 2); + method = type.GetMethod(asyncName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + } + if (method == null) + { + throw new InvalidOperationException(string.Format(SR.CannotResolveManagedEntrypoint, asyncName, assemblyName)); + } + } + + Interop.Runtime.SetEntryAssembly(mainAssembly, waitForDebugger ? method.MetadataToken : 0); + + object[] argsToPass = System.Array.Empty(); + Task? result = null; + var parameterInfos = method.GetParameters(); + if (parameterInfos.Length > 0 && parameterInfos[0].ParameterType == typeof(string[])) + { + argsToPass = new object[] { args ?? System.Array.Empty() }; + } + if (method.ReturnType == typeof(void)) + { + method.Invoke(null, argsToPass); + } + else if (method.ReturnType == typeof(int)) + { + int intResult = (int)method.Invoke(null, argsToPass)!; + result = Task.FromResult(intResult); + } + else if (method.ReturnType == typeof(Task)) + { + Task methodResult = (Task)method.Invoke(null, argsToPass)!; + TaskCompletionSource tcs = new TaskCompletionSource(); + result = tcs.Task; + methodResult.ContinueWith((t) => + { + if (t.IsFaulted) + { + tcs.SetException(t.Exception!); + } + else + { + tcs.SetResult(0); + } + }, TaskScheduler.Default); + } + else if (method.ReturnType == typeof(Task)) + { + result = (Task)method.Invoke(null, argsToPass)!; + } + else + { + throw new InvalidOperationException(SR.Format(SR.ReturnTypeNotSupportedForMain, method.ReturnType.FullName)); + } + return result; + } + catch (Exception ex) + { + if (ex is TargetInvocationException refEx && refEx.InnerException != null) + ex = refEx.InnerException; + return Task.FromException(ex); + } + } + + private static string GeneratedInitializerClassName = "System.Runtime.InteropServices.JavaScript.__GeneratedInitializer"; + private static string GeneratedInitializerMethodName = "__Register_"; + + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Dynamic access from JavaScript")] + [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Dynamic access from JavaScript")] + public static Task BindAssemblyExports(string? assemblyName) + { + try + { + if (string.IsNullOrEmpty(assemblyName)) + { + throw new MissingMethodException("Missing assembly name"); + } + if (!assemblyName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)) + { + assemblyName += ".dll"; + } + + Assembly assembly = Assembly.LoadFrom(assemblyName); + Type? type = assembly.GetType(GeneratedInitializerClassName); + if (type == null) + { + foreach (var module in assembly.Modules) + { + RuntimeHelpers.RunModuleConstructor(module.ModuleHandle); + } + } + else + { + MethodInfo? methodInfo = type.GetMethod(GeneratedInitializerMethodName, BindingFlags.NonPublic | BindingFlags.Static); + methodInfo?.Invoke(null, []); + } + + return Task.CompletedTask; + } + catch (Exception ex) + { + if (ex is TargetInvocationException refEx && refEx.InnerException != null) + ex = refEx.InnerException; + return Task.FromException(ex); + } + } + + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "TODO https://github.com/dotnet/runtime/issues/98366")] + [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "TODO https://github.com/dotnet/runtime/issues/98366")] + public static unsafe JSFunctionBinding BindManagedFunction(string fullyQualifiedName, int signatureHash, ReadOnlySpan signatures) + { + if (string.IsNullOrEmpty(fullyQualifiedName)) + { + throw new ArgumentNullException(nameof(fullyQualifiedName)); + } + + var signature = GetMethodSignature(signatures, null, null); + var (assemblyName, className, nameSpace, shortClassName, methodName) = ParseFQN(fullyQualifiedName); + + Assembly assembly = Assembly.LoadFrom(assemblyName + ".dll"); + Type? type = assembly.GetType(className); + if (type == null) + { + throw new InvalidOperationException("Class not found " + className); + } + var wrapper_name = $"__Wrapper_{methodName}_{signatureHash}"; + var methodInfo = type.GetMethod(wrapper_name, BindingFlags.NonPublic | BindingFlags.Static); + if (methodInfo == null) + { + throw new InvalidOperationException("Method not found " + wrapper_name); + } + + var monoMethod = GetIntPtrFromMethodHandle(methodInfo.MethodHandle); + + JavaScriptImports.BindCSFunction(monoMethod, assemblyName, nameSpace, shortClassName, methodName, signatureHash, (IntPtr)signature.Header); + + FreeMethodSignatureBuffer(signature); + + return signature; + } + #if FEATURE_WASM_MANAGED_THREADS [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "external_eventloop")] private static extern ref bool GetThreadExternalEventloop(Thread @this); @@ -218,5 +380,37 @@ public static void SetHasExternalEventLoop(Thread thread) } #endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IntPtr GetIntPtrFromMethodHandle(RuntimeMethodHandle methodHandle) + { + var temp = new IntPtrAndHandle { methodHandle = methodHandle }; + return temp.ptr; + } + + + public static (string assemblyName, string className, string nameSpace, string shortClassName, string methodName) ParseFQN(string fqn) + { + var assembly = fqn.Substring(fqn.IndexOf('[') + 1, fqn.IndexOf(']') - 1).Trim(); + fqn = fqn.Substring(fqn.IndexOf(']') + 1).Trim(); + var methodName = fqn.Substring(fqn.IndexOf(':') + 1); + var className = fqn.Substring(0, fqn.IndexOf(':')).Trim(); + + var nameSpace = ""; + var shortClassName = className; + var idx = fqn.LastIndexOf("."); + if (idx != -1) + { + nameSpace = fqn.Substring(0, idx); + shortClassName = className.Substring(idx + 1); + } + + if (string.IsNullOrEmpty(assembly)) + throw new InvalidOperationException("No assembly name specified " + fqn); + if (string.IsNullOrEmpty(className)) + throw new InvalidOperationException("No class name specified " + fqn); + if (string.IsNullOrEmpty(methodName)) + throw new InvalidOperationException("No method name specified " + fqn); + return (assembly, className, nameSpace, shortClassName, methodName); + } } } diff --git a/src/mono/browser/runtime/corebindings.c b/src/mono/browser/runtime/corebindings.c index 2814b0da915d06..68e8c6c7097660 100644 --- a/src/mono/browser/runtime/corebindings.c +++ b/src/mono/browser/runtime/corebindings.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "wasm-config.h" @@ -22,9 +23,12 @@ extern void mono_wasm_release_cs_owned_object (int js_handle); extern void mono_wasm_resolve_or_reject_promise (void *data); extern void mono_wasm_cancel_promise (int task_holder_gc_handle); extern void mono_wasm_console_clear (); +extern void mono_wasm_set_entrypoint_breakpoint (int entry_point_metadata_token); typedef void (*background_job_cb)(void); +void mono_wasm_set_entry_assembly (MonoReflectionAssembly* ref_assembly, int entry_point_metadata_token); + #ifndef DISABLE_THREADS void mono_wasm_release_cs_owned_object_post (pthread_t target_tid, int js_handle); void mono_wasm_resolve_or_reject_promise_post (pthread_t target_tid, void *data); @@ -32,7 +36,6 @@ void mono_wasm_cancel_promise_post (pthread_t target_tid, int task_holder_gc_han extern void mono_wasm_install_js_worker_interop (int context_gc_handle); extern void mono_wasm_uninstall_js_worker_interop (); -extern void mono_wasm_bind_cs_function (MonoString **fully_qualified_name, int signature_hash, void* signatures, int *is_exception, MonoObject **result); extern void mono_wasm_invoke_import_async (void* args, void* signature); void mono_wasm_invoke_import_async_post (pthread_t target_tid, void* args, void* signature); extern void mono_wasm_invoke_import_sync (void* args, void* signature); @@ -43,7 +46,6 @@ extern void mono_threads_wasm_async_run_in_target_thread_vi (pthread_t target_th extern void mono_threads_wasm_async_run_in_target_thread_vii (pthread_t target_thread, void (*func) (gpointer, gpointer), gpointer user_data1, gpointer user_data2); extern void mono_threads_wasm_sync_run_in_target_thread_vii (pthread_t target_thread, void (*func) (gpointer, gpointer), gpointer user_data1, gpointer user_data2); #else -extern void mono_wasm_bind_cs_function (MonoString **fully_qualified_name, int signature_hash, void* signatures, int *is_exception, MonoObject **result); extern void mono_wasm_bind_js_import (void *signature, int *is_exception, MonoObject **result); extern void mono_wasm_invoke_js_import (int function_handle, void *args); extern void mono_wasm_invoke_js_function (int function_js_handle, void *args); @@ -75,7 +77,6 @@ void bindings_initialize_internals (void) mono_add_internal_call ("Interop/Runtime::ResolveOrRejectPromisePost", mono_wasm_resolve_or_reject_promise_post); mono_add_internal_call ("Interop/Runtime::InstallWebWorkerInterop", mono_wasm_install_js_worker_interop); mono_add_internal_call ("Interop/Runtime::UninstallWebWorkerInterop", mono_wasm_uninstall_js_worker_interop); - mono_add_internal_call ("Interop/Runtime::BindCSFunction", mono_wasm_bind_cs_function); mono_add_internal_call ("Interop/Runtime::InvokeJSImportSync", mono_wasm_invoke_import_sync); mono_add_internal_call ("Interop/Runtime::InvokeJSImportSyncSend", mono_wasm_invoke_import_sync_send); mono_add_internal_call ("Interop/Runtime::InvokeJSImportAsyncPost", mono_wasm_invoke_import_async_post); @@ -83,14 +84,15 @@ void bindings_initialize_internals (void) mono_add_internal_call ("Interop/Runtime::InvokeJSFunctionSend", mono_wasm_invoke_js_function_send); mono_add_internal_call ("Interop/Runtime::CancelPromise", mono_wasm_cancel_promise); mono_add_internal_call ("Interop/Runtime::CancelPromisePost", mono_wasm_cancel_promise_post); + mono_add_internal_call ("Interop/Runtime::SetEntryAssembly", mono_wasm_set_entry_assembly); #else mono_add_internal_call ("Interop/Runtime::ReleaseCSOwnedObject", mono_wasm_release_cs_owned_object); mono_add_internal_call ("Interop/Runtime::ResolveOrRejectPromise", mono_wasm_resolve_or_reject_promise); - mono_add_internal_call ("Interop/Runtime::BindCSFunction", mono_wasm_bind_cs_function); mono_add_internal_call ("Interop/Runtime::BindJSImport", mono_wasm_bind_js_import); mono_add_internal_call ("Interop/Runtime::InvokeJSImport", mono_wasm_invoke_js_import); mono_add_internal_call ("Interop/Runtime::InvokeJSFunction", mono_wasm_invoke_js_function); mono_add_internal_call ("Interop/Runtime::CancelPromise", mono_wasm_cancel_promise); + mono_add_internal_call ("Interop/Runtime::SetEntryAssembly", mono_wasm_set_entry_assembly); #endif /* DISABLE_THREADS */ mono_add_internal_call ("Interop/JsGlobalization::ChangeCaseInvariant", mono_wasm_change_case_invariant); @@ -106,6 +108,16 @@ void bindings_initialize_internals (void) mono_add_internal_call ("System.ConsolePal::Clear", mono_wasm_console_clear); } +void mono_wasm_set_entry_assembly (MonoReflectionAssembly* ref_assembly, int entry_point_metadata_token) +{ + MonoAssembly *assembly = mono_reflection_assembly_get_assembly (ref_assembly); + mono_domain_ensure_entry_assembly (mono_get_root_domain (), assembly); + if (entry_point_metadata_token != 0) + { + mono_wasm_set_entrypoint_breakpoint (entry_point_metadata_token); + } +} + #ifndef DISABLE_THREADS void mono_wasm_release_cs_owned_object_post (pthread_t target_tid, int js_handle) diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index 7c38a34ee4e852..65e245585a671b 100644 --- a/src/mono/browser/runtime/cwraps.ts +++ b/src/mono/browser/runtime/cwraps.ts @@ -48,12 +48,9 @@ const fn_signatures: SigLine[] = [ [true, "mono_wasm_assembly_load", "number", ["string"]], [true, "mono_wasm_assembly_find_class", "number", ["number", "string", "string"]], - [true, "mono_wasm_runtime_run_module_cctor", "void", ["number"]], [true, "mono_wasm_assembly_find_method", "number", ["number", "string", "number"]], - [false, "mono_wasm_invoke_method_ref", "void", ["number", "number", "number", "number", "number"]], [true, "mono_wasm_string_from_utf16_ref", "void", ["number", "number", "number"]], [true, "mono_wasm_intern_string_ref", "void", ["number"]], - [true, "mono_wasm_assembly_get_entry_point", "number", ["number", "number"]], [false, "mono_wasm_exit", "void", ["number"]], [false, "mono_wasm_abort", "void", []], @@ -64,7 +61,7 @@ const fn_signatures: SigLine[] = [ [() => !runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler, "mono_wasm_profiler_init_aot", "void", ["string"]], [true, "mono_wasm_profiler_init_browser", "void", ["number"]], [false, "mono_wasm_exec_regression", "number", ["number", "string"]], - [false, "mono_wasm_invoke_method", "number", ["number", "number", "number"]], + [false, "mono_wasm_invoke_method", "void", ["number", "number"]], [true, "mono_wasm_write_managed_pointer_unsafe", "void", ["number", "number"]], [true, "mono_wasm_copy_managed_pointer", "void", ["number", "number"]], [true, "mono_wasm_i52_to_f64", "number", ["number", "number"]], @@ -171,9 +168,7 @@ export interface t_Cwraps { mono_wasm_assembly_load(name: string): MonoAssembly; mono_wasm_assembly_find_class(assembly: MonoAssembly, namespace: string, name: string): MonoClass; mono_wasm_assembly_find_method(klass: MonoClass, name: string, args: number): MonoMethod; - mono_wasm_invoke_method_ref(method: MonoMethod, this_arg: MonoObjectRef, params: VoidPtr, out_exc: MonoObjectRef, out_result: MonoObjectRef): void; mono_wasm_string_from_utf16_ref(str: CharPtr, len: number, result: MonoObjectRef): void; - mono_wasm_assembly_get_entry_point(assembly: MonoAssembly, idx: number): MonoMethod; mono_wasm_intern_string_ref(strRef: MonoStringRef): void; mono_wasm_exit(exit_code: number): void; @@ -181,14 +176,13 @@ export interface t_Cwraps { mono_wasm_getenv(name: string): CharPtr; mono_wasm_set_main_args(argc: number, argv: VoidPtr): void; mono_wasm_exec_regression(verbose_level: number, image: string): number; - mono_wasm_invoke_method(method: MonoMethod, args: JSMarshalerArguments, fail: MonoStringRef): number; + mono_wasm_invoke_method(method: MonoMethod, args: JSMarshalerArguments): void; mono_wasm_write_managed_pointer_unsafe(destination: VoidPtr | MonoObjectRef, pointer: ManagedPointer): void; mono_wasm_copy_managed_pointer(destination: VoidPtr | MonoObjectRef, source: VoidPtr | MonoObjectRef): void; mono_wasm_i52_to_f64(source: VoidPtr, error: Int32Ptr): number; mono_wasm_u52_to_f64(source: VoidPtr, error: Int32Ptr): number; mono_wasm_f64_to_i52(destination: VoidPtr, value: number): I52Error; mono_wasm_f64_to_u52(destination: VoidPtr, value: number): I52Error; - mono_wasm_runtime_run_module_cctor(assembly: MonoAssembly): void; mono_wasm_method_get_name(method: MonoMethod): CharPtr; mono_wasm_method_get_full_name(method: MonoMethod): CharPtr; mono_wasm_gc_lock(): void; diff --git a/src/mono/browser/runtime/debug.ts b/src/mono/browser/runtime/debug.ts index 76562bc8f24930..acc467e4b6d3e1 100644 --- a/src/mono/browser/runtime/debug.ts +++ b/src/mono/browser/runtime/debug.ts @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import BuildConfiguration from "consts:configuration"; -import { INTERNAL, Module, runtimeHelpers } from "./globals"; +import { INTERNAL, Module, loaderHelpers, runtimeHelpers } from "./globals"; import { toBase64StringImpl } from "./base64"; import cwraps from "./cwraps"; import { VoidPtr, CharPtr } from "./types/emscripten"; @@ -158,9 +158,9 @@ export function mono_wasm_debugger_attached(): void { cwraps.mono_wasm_set_is_debugger_attached(true); } -export function mono_wasm_set_entrypoint_breakpoint(assembly_name: CharPtr, entrypoint_method_token: number): void { +export function mono_wasm_set_entrypoint_breakpoint(entrypoint_method_token: number): void { //keep these assignments, these values are used by BrowserDebugProxy - _assembly_name_str = utf8ToString(assembly_name).concat(".dll"); + _assembly_name_str = loaderHelpers.config.mainAssemblyName + ".dll"; _entrypoint_method_token = entrypoint_method_token; //keep this console.assert, otherwise optimization will remove the assignments // eslint-disable-next-line no-console diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c index 215eaca4dfe85e..3f47245a71efa6 100644 --- a/src/mono/browser/runtime/driver.c +++ b/src/mono/browser/runtime/driver.c @@ -227,92 +227,30 @@ mono_wasm_load_runtime (int debug_level) bindings_initialize_internals(); } -EMSCRIPTEN_KEEPALIVE int -mono_wasm_invoke_method (MonoMethod *method, void* args, MonoString **out_exc) +EMSCRIPTEN_KEEPALIVE void +mono_wasm_invoke_method (MonoMethod *method, void* args) { PVOLATILE(MonoObject) temp_exc = NULL; void *invoke_args[1] = { args }; - int is_err = 0; MONO_ENTER_GC_UNSAFE; mono_runtime_invoke (method, NULL, args ? invoke_args : NULL, (MonoObject **)&temp_exc); // this failure is unlikely because it would be runtime error, not application exception. // the application exception is passed inside JSMarshalerArguments `args` - if (temp_exc && out_exc) { + // so, if that happens, we should abort the runtime + if (temp_exc) { PVOLATILE(MonoObject) exc2 = NULL; - store_volatile((MonoObject**)out_exc, (MonoObject*)mono_object_to_string ((MonoObject*)temp_exc, (MonoObject **)&exc2)); - if (exc2) - store_volatile((MonoObject**)out_exc, (MonoObject*)mono_string_new (root_domain, "Exception Double Fault")); - is_err = 1; - } - MONO_EXIT_GC_UNSAFE; - return is_err; -} - -EMSCRIPTEN_KEEPALIVE MonoMethod* -mono_wasm_assembly_get_entry_point (MonoAssembly *assembly, int auto_insert_breakpoint) -{ - MonoImage *image; - MonoMethod *method; - - MONO_ENTER_GC_UNSAFE; - image = mono_assembly_get_image (assembly); - uint32_t entry = mono_image_get_entry_point (image); - if (!entry) - goto end; - - mono_domain_ensure_entry_assembly (root_domain, assembly); - method = mono_get_method (image, entry, NULL); - - /* - * If the entry point looks like a compiler generated wrapper around - * an async method in the form "" then try to look up the async methods - * "$" and "Name" it could be wrapping. We do this because the generated - * sync wrapper will call task.GetAwaiter().GetResult() when we actually want - * to yield to the host runtime. - */ - if (mono_method_get_flags (method, NULL) & 0x0800 /* METHOD_ATTRIBUTE_SPECIAL_NAME */) { - const char *name = mono_method_get_name (method); - int name_length = strlen (name); - - if ((*name != '<') || (name [name_length - 1] != '>')) - goto end; - - MonoClass *klass = mono_method_get_class (method); - assert(klass); - char *async_name = malloc (name_length + 2); - snprintf (async_name, name_length + 2, "%s$", name); - - // look for "$" - MonoMethodSignature *sig = mono_method_get_signature (method, image, mono_method_get_token (method)); - MonoMethod *async_method = mono_class_get_method_from_name (klass, async_name, mono_signature_get_param_count (sig)); - if (async_method != NULL) { - free (async_name); - method = async_method; - goto end; + store_volatile((MonoObject**)temp_exc, (MonoObject*)mono_object_to_string ((MonoObject*)temp_exc, (MonoObject **)&exc2)); + if (exc2) { + mono_wasm_trace_logger ("jsinterop", "critical", "mono_wasm_invoke_method unexpected double fault", 1, NULL); + } else { + mono_wasm_trace_logger ("jsinterop", "critical", mono_string_to_utf8((MonoString*)temp_exc), 1, NULL); } - - // look for "Name" by trimming the first and last character of "" - async_name [name_length - 1] = '\0'; - async_method = mono_class_get_method_from_name (klass, async_name + 1, mono_signature_get_param_count (sig)); - - free (async_name); - if (async_method != NULL) - method = async_method; + abort (); } - - end: MONO_EXIT_GC_UNSAFE; - if (auto_insert_breakpoint) - { - MonoAssemblyName *aname = mono_assembly_get_name (assembly); - const char *name = mono_assembly_name_get_name (aname); - if (name != NULL) - mono_wasm_set_entrypoint_breakpoint(name, mono_method_get_token (method)); - } - return method; } EMSCRIPTEN_KEEPALIVE void diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts index cb286789879b67..17aadf3780e431 100644 --- a/src/mono/browser/runtime/exports-binding.ts +++ b/src/mono/browser/runtime/exports-binding.ts @@ -5,7 +5,6 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_set_entrypoint_breakpoint, mono_wasm_fire_debugger_agent_message_with_data, mono_wasm_fire_debugger_agent_message_with_data_to_pause } from "./debug"; import { mono_wasm_release_cs_owned_object } from "./gc-handles"; -import { mono_wasm_bind_cs_function } from "./invoke-cs"; import { mono_wasm_bind_js_import, mono_wasm_invoke_js_function, mono_wasm_invoke_import_async, mono_wasm_invoke_import_sync, mono_wasm_invoke_js_import } from "./invoke-js"; import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter"; import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry"; @@ -92,7 +91,6 @@ export const mono_wasm_imports = [ mono_wasm_bind_js_import, mono_wasm_invoke_js_function, mono_wasm_invoke_js_import, - mono_wasm_bind_cs_function, mono_wasm_resolve_or_reject_promise, mono_wasm_cancel_promise, mono_wasm_change_case_invariant, diff --git a/src/mono/browser/runtime/exports-internal.ts b/src/mono/browser/runtime/exports-internal.ts index f431298cb1f5af..5fe5773f975777 100644 --- a/src/mono/browser/runtime/exports-internal.ts +++ b/src/mono/browser/runtime/exports-internal.ts @@ -20,6 +20,7 @@ import { mono_wasm_get_func_id_to_name_mappings } from "./logging"; import { MonoObject, MonoObjectNull } from "./types/internal"; import { monoStringToStringUnsafe } from "./strings"; import { thread_available } from "./pthreads/browser"; +import { mono_wasm_bind_cs_function } from "./invoke-cs"; export function export_internal(): any { return { @@ -57,6 +58,7 @@ export function export_internal(): any { get_dotnet_instance: () => exportedRuntimeAPI, dynamic_import, thread_available, + mono_wasm_bind_cs_function, // BrowserWebSocket ws_wasm_create, diff --git a/src/mono/browser/runtime/gc-lock.ts b/src/mono/browser/runtime/gc-lock.ts index 876e6a2eb58693..ea373eecc0447d 100644 --- a/src/mono/browser/runtime/gc-lock.ts +++ b/src/mono/browser/runtime/gc-lock.ts @@ -1,11 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import WasmEnableThreads from "consts:wasmEnableThreads"; import { ENVIRONMENT_IS_PTHREAD } from "./globals"; import cwraps from "./cwraps"; -let locked = false; +export let gc_locked = false; export function mono_wasm_gc_lock(): void { - if (locked) { + if (gc_locked) { throw new Error("GC is already locked"); } if (WasmEnableThreads) { @@ -14,11 +17,11 @@ export function mono_wasm_gc_lock(): void { } cwraps.mono_wasm_gc_lock(); } - locked = true; + gc_locked = true; } export function mono_wasm_gc_unlock(): void { - if (!locked) { + if (!gc_locked) { throw new Error("GC is not locked"); } if (WasmEnableThreads) { @@ -27,5 +30,5 @@ export function mono_wasm_gc_unlock(): void { } cwraps.mono_wasm_gc_unlock(); } - locked = false; + gc_locked = false; } diff --git a/src/mono/browser/runtime/invoke-cs.ts b/src/mono/browser/runtime/invoke-cs.ts index d45063ab30bbb6..1b0338180ebffe 100644 --- a/src/mono/browser/runtime/invoke-cs.ts +++ b/src/mono/browser/runtime/invoke-cs.ts @@ -6,131 +6,101 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { bind_arg_marshal_to_cs } from "./marshal-to-cs"; -import { marshal_exception_to_js, bind_arg_marshal_to_js, end_marshal_task_to_js } from "./marshal-to-js"; +import { bind_arg_marshal_to_js, end_marshal_task_to_js } from "./marshal-to-js"; import { - get_arg, get_sig, get_signature_argument_count, is_args_exception, - bound_cs_function_symbol, get_signature_version, alloc_stack_frame, get_signature_type, set_args_context, + get_sig, get_signature_argument_count, + bound_cs_function_symbol, get_signature_version, alloc_stack_frame, get_signature_type, } from "./marshal"; -import { mono_wasm_new_external_root, mono_wasm_new_root } from "./roots"; -import { monoStringToString } from "./strings"; -import { MonoObjectRef, MonoStringRef, MonoString, MonoObject, MonoMethod, JSMarshalerArguments, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, VoidPtrNull, MonoObjectRefNull, MonoObjectNull, MarshalerType, MonoAssembly } from "./types/internal"; -import { Int32Ptr } from "./types/emscripten"; +import { MonoMethod, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, MarshalerType, MonoAssembly } from "./types/internal"; import cwraps from "./cwraps"; -import { assert_js_interop, wrap_error_root, wrap_no_error_root } from "./invoke-js"; +import { assert_js_interop } from "./invoke-js"; import { startMeasure, MeasuredBlock, endMeasure } from "./profiler"; +import { bind_assembly_exports, invoke_sync_method } from "./managed-exports"; import { mono_log_debug } from "./logging"; const _assembly_cache_by_name = new Map(); -export function mono_wasm_bind_cs_function(fully_qualified_name: MonoStringRef, signature_hash: number, signature: JSFunctionSignature, is_exception: Int32Ptr, result_address: MonoObjectRef): void { - assert_js_interop(); - const fqn_root = mono_wasm_new_external_root(fully_qualified_name), resultRoot = mono_wasm_new_external_root(result_address); +export function mono_wasm_bind_cs_function(method: MonoMethod, assemblyName: string, namespaceName: string, shortClassName: string, methodName: string, signatureHash: number, signature: JSFunctionSignature): void { + const fullyQualifiedName = `[${assemblyName}] ${namespaceName}.${shortClassName}:${methodName}`; const mark = startMeasure(); - try { - const version = get_signature_version(signature); - mono_assert(version === 2, () => `Signature version ${version} mismatch.`); - - const args_count = get_signature_argument_count(signature); - const js_fqn = monoStringToString(fqn_root)!; - mono_assert(js_fqn, "fully_qualified_name must be string"); - - mono_log_debug(`Binding [JSExport] ${js_fqn}`); - - const { assembly, namespace, classname, methodname } = parseFQN(js_fqn); - - const asm = assembly_load(assembly); - if (!asm) - throw new Error("Could not find assembly: " + assembly); - - const klass = cwraps.mono_wasm_assembly_find_class(asm, namespace, classname); - if (!klass) - throw new Error("Could not find class: " + namespace + ":" + classname + " in assembly " + assembly); - - const wrapper_name = `__Wrapper_${methodname}_${signature_hash}`; - const method = cwraps.mono_wasm_assembly_find_method(klass, wrapper_name, -1); - if (!method) - throw new Error(`Could not find method: ${wrapper_name} in ${klass} [${assembly}]`); - - const arg_marshalers: (BoundMarshalerToCs)[] = new Array(args_count); - for (let index = 0; index < args_count; index++) { - const sig = get_sig(signature, index + 2); - const marshaler_type = get_signature_type(sig); - const arg_marshaler = bind_arg_marshal_to_cs(sig, marshaler_type, index + 2); - mono_assert(arg_marshaler, "ERR43: argument marshaler must be resolved"); - arg_marshalers[index] = arg_marshaler; - } + mono_log_debug(`Binding [JSExport] ${assemblyName}.${shortClassName} from ${assemblyName} assembly`); + const version = get_signature_version(signature); + mono_assert(version === 2, () => `Signature version ${version} mismatch.`); - const res_sig = get_sig(signature, 1); - let res_marshaler_type = get_signature_type(res_sig); - const is_async = res_marshaler_type == MarshalerType.Task; - if (is_async) { - res_marshaler_type = MarshalerType.TaskPreCreated; - } - const res_converter = bind_arg_marshal_to_js(res_sig, res_marshaler_type, 1); - - const closure: BindingClosure = { - method, - fqn: js_fqn, - args_count, - arg_marshalers, - res_converter, - is_async, - isDisposed: false, - }; - let bound_fn: Function; - // void - if (args_count == 0 && !res_converter) { - bound_fn = bind_fn_0V(closure); - } - else if (args_count == 1 && !res_converter) { - bound_fn = bind_fn_1V(closure); - } - else if (is_async && args_count == 1 && res_converter) { - bound_fn = bind_fn_1RA(closure); - } - else if (is_async && args_count == 2 && res_converter) { - bound_fn = bind_fn_2RA(closure); - } - else if (args_count == 1 && res_converter) { - bound_fn = bind_fn_1R(closure); - } - else if (args_count == 2 && res_converter) { - bound_fn = bind_fn_2R(closure); - } - else { - bound_fn = bind_fn(closure); - } - // this is just to make debugging easier. - // It's not CSP compliant and possibly not performant, that's why it's only enabled in debug builds - // in Release configuration, it would be a trimmed by rollup - if (BuildConfiguration === "Debug" && !runtimeHelpers.cspPolicy) { - try { - bound_fn = new Function("fn", "return (function JSExport_" + methodname + "(){ return fn.apply(this, arguments)});")(bound_fn); - } - catch (ex) { - runtimeHelpers.cspPolicy = true; - } - } + const args_count = get_signature_argument_count(signature); - (bound_fn)[bound_cs_function_symbol] = closure; + const arg_marshalers: (BoundMarshalerToCs)[] = new Array(args_count); + for (let index = 0; index < args_count; index++) { + const sig = get_sig(signature, index + 2); + const marshaler_type = get_signature_type(sig); + const arg_marshaler = bind_arg_marshal_to_cs(sig, marshaler_type, index + 2); + mono_assert(arg_marshaler, "ERR43: argument marshaler must be resolved"); + arg_marshalers[index] = arg_marshaler; + } - _walk_exports_to_set_function(assembly, namespace, classname, methodname, signature_hash, bound_fn); - endMeasure(mark, MeasuredBlock.bindCsFunction, js_fqn); - wrap_no_error_root(is_exception, resultRoot); + const res_sig = get_sig(signature, 1); + let res_marshaler_type = get_signature_type(res_sig); + const is_async = res_marshaler_type == MarshalerType.Task; + if (is_async) { + res_marshaler_type = MarshalerType.TaskPreCreated; + } + const res_converter = bind_arg_marshal_to_js(res_sig, res_marshaler_type, 1); + + const closure: BindingClosure = { + method, + fullyQualifiedName, + args_count, + arg_marshalers, + res_converter, + is_async, + isDisposed: false, + }; + let bound_fn: Function; + // void + if (args_count == 0 && !res_converter) { + bound_fn = bind_fn_0V(closure); + } + else if (args_count == 1 && !res_converter) { + bound_fn = bind_fn_1V(closure); + } + else if (is_async && args_count == 1 && res_converter) { + bound_fn = bind_fn_1RA(closure); } - catch (ex: any) { - Module.err(ex.toString()); - wrap_error_root(is_exception, ex, resultRoot); - } finally { - resultRoot.release(); - fqn_root.release(); + else if (is_async && args_count == 2 && res_converter) { + bound_fn = bind_fn_2RA(closure); } + else if (args_count == 1 && res_converter) { + bound_fn = bind_fn_1R(closure); + } + else if (args_count == 2 && res_converter) { + bound_fn = bind_fn_2R(closure); + } + else { + bound_fn = bind_fn(closure); + } + + // this is just to make debugging easier. + // It's not CSP compliant and possibly not performant, that's why it's only enabled in debug builds + // in Release configuration, it would be a trimmed by rollup + if (BuildConfiguration === "Debug" && !runtimeHelpers.cspPolicy) { + try { + bound_fn = new Function("fn", "return (function JSExport_" + methodName + "(){ return fn.apply(this, arguments)});")(bound_fn); + } + catch (ex) { + runtimeHelpers.cspPolicy = true; + } + } + + (bound_fn)[bound_cs_function_symbol] = closure; + + _walk_exports_to_set_function(assemblyName, namespaceName, shortClassName, methodName, signatureHash, bound_fn); + endMeasure(mark, MeasuredBlock.bindCsFunction, fullyQualifiedName); } function bind_fn_0V(closure: BindingClosure) { const method = closure.method; - const fqn = closure.fqn; + const fqn = closure.fullyQualifiedName; if (!WasmEnableThreads) (closure) = null; return function bound_fn_0V() { const mark = startMeasure(); @@ -151,7 +121,7 @@ function bind_fn_0V(closure: BindingClosure) { function bind_fn_1V(closure: BindingClosure) { const method = closure.method; const marshaler1 = closure.arg_marshalers[0]!; - const fqn = closure.fqn; + const fqn = closure.fullyQualifiedName; if (!WasmEnableThreads) (closure) = null; return function bound_fn_1V(arg1: any) { const mark = startMeasure(); @@ -175,7 +145,7 @@ function bind_fn_1R(closure: BindingClosure) { const method = closure.method; const marshaler1 = closure.arg_marshalers[0]!; const res_converter = closure.res_converter!; - const fqn = closure.fqn; + const fqn = closure.fullyQualifiedName; if (!WasmEnableThreads) (closure) = null; return function bound_fn_1R(arg1: any) { const mark = startMeasure(); @@ -202,7 +172,7 @@ function bind_fn_1RA(closure: BindingClosure) { const method = closure.method; const marshaler1 = closure.arg_marshalers[0]!; const res_converter = closure.res_converter!; - const fqn = closure.fqn; + const fqn = closure.fullyQualifiedName; if (!WasmEnableThreads) (closure) = null; return function bound_fn_1R(arg1: any) { const mark = startMeasure(); @@ -235,7 +205,7 @@ function bind_fn_2R(closure: BindingClosure) { const marshaler1 = closure.arg_marshalers[0]!; const marshaler2 = closure.arg_marshalers[1]!; const res_converter = closure.res_converter!; - const fqn = closure.fqn; + const fqn = closure.fullyQualifiedName; if (!WasmEnableThreads) (closure) = null; return function bound_fn_2R(arg1: any, arg2: any) { const mark = startMeasure(); @@ -264,7 +234,7 @@ function bind_fn_2RA(closure: BindingClosure) { const marshaler1 = closure.arg_marshalers[0]!; const marshaler2 = closure.arg_marshalers[1]!; const res_converter = closure.res_converter!; - const fqn = closure.fqn; + const fqn = closure.fullyQualifiedName; if (!WasmEnableThreads) (closure) = null; return function bound_fn_2R(arg1: any, arg2: any) { const mark = startMeasure(); @@ -298,7 +268,7 @@ function bind_fn(closure: BindingClosure) { const arg_marshalers = closure.arg_marshalers; const res_converter = closure.res_converter; const method = closure.method; - const fqn = closure.fqn; + const fqn = closure.fullyQualifiedName; const is_async = closure.is_async; if (!WasmEnableThreads) (closure) = null; return function bound_fn(...js_args: any[]) { @@ -339,7 +309,7 @@ function bind_fn(closure: BindingClosure) { } type BindingClosure = { - fqn: string, + fullyQualifiedName: string, args_count: number, method: MonoMethod, arg_marshalers: (BoundMarshalerToCs)[], @@ -348,23 +318,6 @@ type BindingClosure = { isDisposed: boolean, } -export function invoke_sync_method(method: MonoMethod, args: JSMarshalerArguments): void { - assert_js_interop(); - const fail_root = mono_wasm_new_root(); - try { - set_args_context(args); - const fail = cwraps.mono_wasm_invoke_method(method, args, fail_root.address); - if (fail) runtimeHelpers.nativeAbort("ERR24: Unexpected error: " + monoStringToString(fail_root)); - if (is_args_exception(args)) { - const exc = get_arg(args, 0); - throw marshal_exception_to_js(exc); - } - } - finally { - fail_root.release(); - } -} - export const exportsByAssembly: Map = new Map(); function _walk_exports_to_set_function(assembly: string, namespace: string, classname: string, methodname: string, signature_hash: number, fn: Function): void { const parts = `${namespace}.${classname}`.replace(/\//g, ".").split("."); @@ -399,66 +352,12 @@ export async function mono_wasm_get_assembly_exports(assembly: string): Promise< assert_js_interop(); const result = exportsByAssembly.get(assembly); if (!result) { - const mark = startMeasure(); - const asm = assembly_load(assembly); - if (!asm) - throw new Error("Could not find assembly: " + assembly); - - const klass = cwraps.mono_wasm_assembly_find_class(asm, runtimeHelpers.runtime_interop_namespace, "__GeneratedInitializer"); - if (klass) { - const method = cwraps.mono_wasm_assembly_find_method(klass, "__Register_", -1); - if (method) { - const outException = mono_wasm_new_root(); - const outResult = mono_wasm_new_root(); - try { - cwraps.mono_wasm_invoke_method_ref(method, MonoObjectRefNull, VoidPtrNull, outException.address, outResult.address); - if (outException.value !== MonoObjectNull) { - const msg = monoStringToString(outResult)!; - throw new Error(msg); - } - } - finally { - outException.release(); - outResult.release(); - } - } - } else { - mono_assert(!WasmEnableThreads, () => `JSExport with multi-threading enabled is not supported with assembly ${assembly} as it was generated with the .NET 7 SDK`); - // this needs to stay here for compatibility with assemblies generated in Net7 - // it doesn't have the __GeneratedInitializer class - cwraps.mono_wasm_runtime_run_module_cctor(asm); - } - endMeasure(mark, MeasuredBlock.getAssemblyExports, assembly); + await bind_assembly_exports(assembly); } return exportsByAssembly.get(assembly) || {}; } -export function parseFQN(fqn: string) - : { assembly: string, namespace: string, classname: string, methodname: string } { - const assembly = fqn.substring(fqn.indexOf("[") + 1, fqn.indexOf("]")).trim(); - fqn = fqn.substring(fqn.indexOf("]") + 1).trim(); - - const methodname = fqn.substring(fqn.indexOf(":") + 1); - fqn = fqn.substring(0, fqn.indexOf(":")).trim(); - - let namespace = ""; - let classname = fqn; - if (fqn.indexOf(".") != -1) { - const idx = fqn.lastIndexOf("."); - namespace = fqn.substring(0, idx); - classname = fqn.substring(idx + 1); - } - - if (!assembly.trim()) - throw new Error("No assembly name specified " + fqn); - if (!classname.trim()) - throw new Error("No class name specified " + fqn); - if (!methodname.trim()) - throw new Error("No method name specified " + fqn); - return { assembly, namespace, classname, methodname }; -} - export function assembly_load(name: string): MonoAssembly { if (_assembly_cache_by_name.has(name)) return _assembly_cache_by_name.get(name); diff --git a/src/mono/browser/runtime/invoke-js.ts b/src/mono/browser/runtime/invoke-js.ts index ccd7e294a3f883..5323407eefa0fe 100644 --- a/src/mono/browser/runtime/invoke-js.ts +++ b/src/mono/browser/runtime/invoke-js.ts @@ -449,6 +449,8 @@ export function wrap_error_root(is_exception: Int32Ptr | null, ex: any, result: } // to set out parameters of icalls +// TODO replace it with replace it with UTF8 char*, no GC root needed +// https://github.com/dotnet/runtime/issues/98365 export function wrap_no_error_root(is_exception: Int32Ptr | null, result?: WasmRoot): void { if (is_exception) { receiveWorkerHeapViews(); diff --git a/src/mono/browser/runtime/jiterpreter-tables.ts b/src/mono/browser/runtime/jiterpreter-tables.ts index 71a3eb86c9f014..0ab6ce675267be 100644 --- a/src/mono/browser/runtime/jiterpreter-tables.ts +++ b/src/mono/browser/runtime/jiterpreter-tables.ts @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + import { WasmOpcode, WasmSimdOpcode, JiterpSpecialOpcode } from "./jiterpreter-opcodes"; diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts index 4a95d0ca362a55..9a140ac0f6c70a 100644 --- a/src/mono/browser/runtime/managed-exports.ts +++ b/src/mono/browser/runtime/managed-exports.ts @@ -3,15 +3,14 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; -import { GCHandle, GCHandleNull, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal"; +import { GCHandle, GCHandleNull, JSMarshalerArguments, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal"; import cwraps from "./cwraps"; import { runtimeHelpers, Module, loaderHelpers, mono_assert } from "./globals"; import { JavaScriptMarshalerArgSize, alloc_stack_frame, get_arg, get_arg_gc_handle, is_args_exception, set_arg_intptr, set_arg_type, set_gc_handle } from "./marshal"; -import { invoke_sync_method } from "./invoke-cs"; -import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs"; +import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_bool_to_cs, marshal_exception_to_cs, marshal_string_to_cs } from "./marshal-to-cs"; import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begin_marshal_task_to_js, marshal_exception_to_js } from "./marshal-to-js"; import { do_not_force_dispose } from "./gc-handles"; -import { assert_c_interop } from "./invoke-js"; +import { assert_c_interop, assert_js_interop } from "./invoke-js"; import { mono_wasm_main_thread_ptr } from "./pthreads/shared"; import { _zero_region } from "./memory"; @@ -19,18 +18,21 @@ const managedExports: ManagedExports = {} as any; export function init_managed_exports(): void { const exports_fqn_asm = "System.Runtime.InteropServices.JavaScript"; + // TODO https://github.com/dotnet/runtime/issues/98366 runtimeHelpers.runtime_interop_module = cwraps.mono_wasm_assembly_load(exports_fqn_asm); if (!runtimeHelpers.runtime_interop_module) throw "Can't find bindings module assembly: " + exports_fqn_asm; - runtimeHelpers.runtime_interop_namespace = "System.Runtime.InteropServices.JavaScript"; + runtimeHelpers.runtime_interop_namespace = exports_fqn_asm; runtimeHelpers.runtime_interop_exports_classname = "JavaScriptExports"; + // TODO https://github.com/dotnet/runtime/issues/98366 runtimeHelpers.runtime_interop_exports_class = cwraps.mono_wasm_assembly_find_class(runtimeHelpers.runtime_interop_module, runtimeHelpers.runtime_interop_namespace, runtimeHelpers.runtime_interop_exports_classname); if (!runtimeHelpers.runtime_interop_exports_class) throw "Can't find " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + " class"; managedExports.InstallMainSynchronizationContext = WasmEnableThreads ? get_method("InstallMainSynchronizationContext") : undefined; managedExports.CallEntrypoint = get_method("CallEntrypoint"); + managedExports.BindAssemblyExports = get_method("BindAssemblyExports"); managedExports.ReleaseJSOwnedObjectByGCHandle = get_method("ReleaseJSOwnedObjectByGCHandle"); managedExports.CompleteTask = get_method("CompleteTask"); managedExports.CallDelegate = get_method("CallDelegate"); @@ -39,26 +41,23 @@ export function init_managed_exports(): void { managedExports.LoadLazyAssembly = get_method("LoadLazyAssembly"); } -// the marshaled signature is: Task? CallEntrypoint(MonoMethod* entrypointPtr, string[] args) -export async function call_entry_point(entry_point: MonoMethod, program_args?: string[]): Promise { +// the marshaled signature is: Task? CallEntrypoint(string mainAssemblyName, string[] args) +export function call_entry_point(main_assembly_name: string, program_args: string[] | undefined, waitForDebugger: boolean): Promise { loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); try { - Module.runtimeKeepalivePush(); - const args = alloc_stack_frame(4); + const args = alloc_stack_frame(5); const res = get_arg(args, 1); const arg1 = get_arg(args, 2); const arg2 = get_arg(args, 3); - marshal_intptr_to_cs(arg1, entry_point); - if (program_args && program_args.length == 0) { - program_args = undefined; - } - marshal_array_to_cs_impl(arg2, program_args, MarshalerType.String); + const arg3 = get_arg(args, 4); + marshal_string_to_cs(arg1, main_assembly_name); + marshal_array_to_cs_impl(arg2, program_args && !program_args.length ? undefined : program_args, MarshalerType.String); + marshal_bool_to_cs(arg3, waitForDebugger); // because this is async, we could pre-allocate the promise let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated, marshal_int32_to_js); - // NOTE: at the moment this is synchronous call on the same thread and therefore we could marshal (null) result synchronously invoke_sync_method(managedExports.CallEntrypoint, args); // in case the C# side returned synchronously @@ -68,10 +67,10 @@ export async function call_entry_point(entry_point: MonoMethod, program_args?: s promise = Promise.resolve(0); } (promise as any)[do_not_force_dispose] = true; // prevent disposing the task in forceDisposeProxies() - return await promise; + + return promise; } finally { - Module.runtimeKeepalivePop();// after await promise ! - Module.stackRestore(sp); + Module.stackRestore(sp); // synchronously } } @@ -219,8 +218,7 @@ export function install_main_synchronization_context(): GCHandle { const arg1 = get_arg(args, 2); const arg2 = get_arg(args, 3); set_arg_intptr(arg1, mono_wasm_main_thread_ptr() as any); - const fail = cwraps.mono_wasm_invoke_method(managedExports.InstallMainSynchronizationContext!, args, 0 as any); - if (fail) runtimeHelpers.nativeAbort("ERR24: Unexpected error"); + cwraps.mono_wasm_invoke_method(managedExports.InstallMainSynchronizationContext!, args); if (is_args_exception(args)) { const exc = get_arg(args, 0); throw marshal_exception_to_js(exc); @@ -231,7 +229,45 @@ export function install_main_synchronization_context(): GCHandle { } } +export function invoke_sync_method(method: MonoMethod, args: JSMarshalerArguments): void { + assert_js_interop(); + cwraps.mono_wasm_invoke_method(method, args as any); + if (is_args_exception(args)) { + const exc = get_arg(args, 0); + throw marshal_exception_to_js(exc); + } +} + +// the marshaled signature is: Task BindAssemblyExports(string assemblyName) +export function bind_assembly_exports(assemblyName: string): Promise { + loaderHelpers.assert_runtime_running(); + const sp = Module.stackSave(); + try { + const args = alloc_stack_frame(3); + const res = get_arg(args, 1); + const arg1 = get_arg(args, 2); + marshal_string_to_cs(arg1, assemblyName); + + // because this is async, we could pre-allocate the promise + let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated); + + invoke_sync_method(managedExports.BindAssemblyExports, args); + + // in case the C# side returned synchronously + promise = end_marshal_task_to_js(args, marshal_int32_to_js, promise); + + if (promise === null || promise === undefined) { + promise = Promise.resolve(); + } + return promise; + } finally { + Module.stackRestore(sp); // synchronously + } +} + + function get_method(method_name: string): MonoMethod { + // TODO https://github.com/dotnet/runtime/issues/98366 const res = cwraps.mono_wasm_assembly_find_method(runtimeHelpers.runtime_interop_exports_class, method_name, -1); if (!res) throw "Can't find method " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + "." + method_name; @@ -242,6 +278,7 @@ type ManagedExports = { InstallMainSynchronizationContext: MonoMethod | undefined, entry_point: MonoMethod, CallEntrypoint: MonoMethod, + BindAssemblyExports: MonoMethod, ReleaseJSOwnedObjectByGCHandle: MonoMethod, CompleteTask: MonoMethod, CallDelegate: MonoMethod, diff --git a/src/mono/browser/runtime/marshal-to-cs.ts b/src/mono/browser/runtime/marshal-to-cs.ts index 63657a122195e7..c1b663044c8b84 100644 --- a/src/mono/browser/runtime/marshal-to-cs.ts +++ b/src/mono/browser/runtime/marshal-to-cs.ts @@ -25,6 +25,7 @@ import { TypedArray } from "./types/emscripten"; import { addUnsettledPromise, settleUnsettledPromise } from "./pthreads/shared/eventloop"; import { mono_log_debug } from "./logging"; import { complete_task } from "./managed-exports"; +import { gc_locked } from "./gc-lock"; export const jsinteropDoc = "For more information see https://aka.ms/dotnet-wasm-jsinterop"; @@ -33,7 +34,7 @@ export function initialize_marshalers_to_cs(): void { js_to_cs_marshalers.set(MarshalerType.Array, marshal_array_to_cs); js_to_cs_marshalers.set(MarshalerType.Span, _marshal_span_to_cs); js_to_cs_marshalers.set(MarshalerType.ArraySegment, _marshal_array_segment_to_cs); - js_to_cs_marshalers.set(MarshalerType.Boolean, _marshal_bool_to_cs); + js_to_cs_marshalers.set(MarshalerType.Boolean, marshal_bool_to_cs); js_to_cs_marshalers.set(MarshalerType.Byte, _marshal_byte_to_cs); js_to_cs_marshalers.set(MarshalerType.Char, _marshal_char_to_cs); js_to_cs_marshalers.set(MarshalerType.Int16, _marshal_int16_to_cs); @@ -45,7 +46,7 @@ export function initialize_marshalers_to_cs(): void { js_to_cs_marshalers.set(MarshalerType.IntPtr, marshal_intptr_to_cs); js_to_cs_marshalers.set(MarshalerType.DateTime, _marshal_date_time_to_cs); js_to_cs_marshalers.set(MarshalerType.DateTimeOffset, _marshal_date_time_offset_to_cs); - js_to_cs_marshalers.set(MarshalerType.String, _marshal_string_to_cs); + js_to_cs_marshalers.set(MarshalerType.String, marshal_string_to_cs); js_to_cs_marshalers.set(MarshalerType.Exception, marshal_exception_to_cs); js_to_cs_marshalers.set(MarshalerType.JSException, marshal_exception_to_cs); js_to_cs_marshalers.set(MarshalerType.JSObject, marshal_js_object_to_cs); @@ -97,7 +98,7 @@ export function get_marshaler_to_cs_by_type(marshaler_type: MarshalerType): Mars return converter; } -function _marshal_bool_to_cs(arg: JSMarshalerArgument, value: any): void { +export function marshal_bool_to_cs(arg: JSMarshalerArgument, value: any): void { if (value === null || value === undefined) { set_arg_type(arg, MarshalerType.None); } @@ -219,7 +220,7 @@ function _marshal_date_time_offset_to_cs(arg: JSMarshalerArgument, value: Date): } } -function _marshal_string_to_cs(arg: JSMarshalerArgument, value: string) { +export function marshal_string_to_cs(arg: JSMarshalerArgument, value: string) { if (value === null || value === undefined) { set_arg_type(arg, MarshalerType.None); } @@ -556,17 +557,19 @@ export function marshal_array_to_cs_impl(arg: JSMarshalerArgument, value: Array< mono_check(Array.isArray(value), "Value is not an Array"); _zero_region(buffer_ptr, buffer_length); if (!WasmEnableJsInteropByValue) { + mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when creating a GC root"); cwraps.mono_wasm_register_root(buffer_ptr, buffer_length, "marshal_array_to_cs"); } for (let index = 0; index < length; index++) { const element_arg = get_arg(buffer_ptr, index); - _marshal_string_to_cs(element_arg, value[index]); + marshal_string_to_cs(element_arg, value[index]); } } else if (element_type == MarshalerType.Object) { mono_check(Array.isArray(value), "Value is not an Array"); _zero_region(buffer_ptr, buffer_length); if (!WasmEnableJsInteropByValue) { + mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when creating a GC root"); cwraps.mono_wasm_register_root(buffer_ptr, buffer_length, "marshal_array_to_cs"); } for (let index = 0; index < length; index++) { diff --git a/src/mono/browser/runtime/marshal-to-js.ts b/src/mono/browser/runtime/marshal-to-js.ts index e40c83899bc07f..d3987586b01d4d 100644 --- a/src/mono/browser/runtime/marshal-to-js.ts +++ b/src/mono/browser/runtime/marshal-to-js.ts @@ -22,6 +22,7 @@ import { TypedArray } from "./types/emscripten"; import { get_marshaler_to_cs_by_type, jsinteropDoc, marshal_exception_to_cs } from "./marshal-to-cs"; import { localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory"; import { call_delegate } from "./managed-exports"; +import { gc_locked } from "./gc-lock"; export function initialize_marshalers_to_js(): void { if (cs_to_js_marshalers.size == 0) { @@ -494,6 +495,7 @@ function _marshal_array_to_js_impl(arg: JSMarshalerArgument, element_type: Marsh result[index] = marshal_string_to_js(element_arg); } if (!WasmEnableJsInteropByValue) { + mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when disposing a GC root"); cwraps.mono_wasm_deregister_root(buffer_ptr); } } @@ -504,6 +506,7 @@ function _marshal_array_to_js_impl(arg: JSMarshalerArgument, element_type: Marsh result[index] = _marshal_cs_object_to_js(element_arg); } if (!WasmEnableJsInteropByValue) { + mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when disposing a GC root"); cwraps.mono_wasm_deregister_root(buffer_ptr); } } diff --git a/src/mono/browser/runtime/roots.ts b/src/mono/browser/runtime/roots.ts index 21f9f18077b7c7..24958a22e08458 100644 --- a/src/mono/browser/runtime/roots.ts +++ b/src/mono/browser/runtime/roots.ts @@ -1,11 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import WasmEnableThreads from "consts:wasmEnableThreads"; + import cwraps from "./cwraps"; -import { Module } from "./globals"; +import { Module, mono_assert } from "./globals"; import { VoidPtr, ManagedPointer, NativePointer } from "./types/emscripten"; import { MonoObjectRef, MonoObjectRefNull, MonoObject, is_nullish, WasmRoot, WasmRootBuffer } from "./types/internal"; import { _zero_region, localHeapViewU32 } from "./memory"; +import { gc_locked } from "./gc-lock"; const maxScratchRoots = 8192; let _scratch_root_buffer: WasmRootBuffer | null = null; @@ -36,25 +39,6 @@ export function mono_wasm_new_root_buffer(capacity: number, name?: string): Wasm return new WasmRootBufferImpl(offset, capacity, true, name); } -/** - * Creates a root buffer object representing an existing allocation in the native heap and registers - * the allocation with the GC. The caller is responsible for managing the lifetime of the allocation. - */ -export function mono_wasm_new_root_buffer_from_pointer(offset: VoidPtr, capacity: number, name?: string): WasmRootBuffer { - if (capacity <= 0) - throw new Error("capacity >= 1"); - - capacity = capacity | 0; - - const capacityBytes = capacity * 4; - if ((offset % 4) !== 0) - throw new Error("Unaligned offset"); - - _zero_region(offset, capacityBytes); - - return new WasmRootBufferImpl(offset, capacity, false, name); -} - /** * Allocates a WasmRoot pointing to a root provided and controlled by external code. Typicaly on managed stack. * Releasing this root will not de-allocate the root space. You still need to call .release(). @@ -188,6 +172,7 @@ export class WasmRootBufferImpl implements WasmRootBuffer { this.__offset32 = offset >>> 2; this.__count = capacity; this.length = capacity; + mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when creating a GC root"); this.__handle = cwraps.mono_wasm_register_root(offset, capacityBytes, name || "noname"); this.__ownsAllocation = ownsAllocation; } @@ -247,6 +232,7 @@ export class WasmRootBufferImpl implements WasmRootBuffer { release(): void { if (this.__offset && this.__ownsAllocation) { + mono_assert(!WasmEnableThreads || !gc_locked, "GC must not be locked when disposing a GC root"); cwraps.mono_wasm_deregister_root(this.__offset); _zero_region(this.__offset, this.__count * 4); Module._free(this.__offset); diff --git a/src/mono/browser/runtime/run.ts b/src/mono/browser/runtime/run.ts index 932d851973b752..8760aab6514cad 100644 --- a/src/mono/browser/runtime/run.ts +++ b/src/mono/browser/runtime/run.ts @@ -3,13 +3,11 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; -import { ENVIRONMENT_IS_NODE, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; +import { ENVIRONMENT_IS_NODE, Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { mono_wasm_wait_for_debugger } from "./debug"; import { mono_wasm_set_main_args } from "./startup"; import cwraps from "./cwraps"; import { mono_log_info } from "./logging"; -import { assert_js_interop } from "./invoke-js"; -import { assembly_load } from "./invoke-cs"; import { cancelThreads } from "./pthreads/browser"; import { call_entry_point } from "./managed-exports"; @@ -58,36 +56,26 @@ export async function mono_run_main(main_assembly_name?: string, args?: string[] } mono_wasm_set_main_args(main_assembly_name, args); + loaderHelpers.config.mainAssemblyName = main_assembly_name; + if (runtimeHelpers.waitForDebugger == -1) { mono_log_info("waiting for debugger..."); await mono_wasm_wait_for_debugger(); } - const method = find_entry_point(main_assembly_name); - const res = await call_entry_point(method, args); + try { + Module.runtimeKeepalivePush(); - // one more timer loop before we return, so that any remaining queued calls could run - await new Promise(resolve => globalThis.setTimeout(resolve, 0)); + // one more timer loop before we return, so that any remaining queued calls could run + await new Promise(resolve => globalThis.setTimeout(resolve, 0)); - return res; + return await call_entry_point(main_assembly_name, args, runtimeHelpers.waitForDebugger == 1); + } finally { + Module.runtimeKeepalivePop();// after await promise ! + } } -export function find_entry_point(assembly: string) { - loaderHelpers.assert_runtime_running(); - assert_js_interop(); - const asm = assembly_load(assembly); - if (!asm) - throw new Error("Could not find assembly: " + assembly); - let auto_set_breakpoint = 0; - if (runtimeHelpers.waitForDebugger == 1) - auto_set_breakpoint = 1; - - const method = cwraps.mono_wasm_assembly_get_entry_point(asm, auto_set_breakpoint); - if (!method) - throw new Error("Could not find entry point for assembly: " + assembly); - return method; -} export function nativeExit(code: number) { if (WasmEnableThreads) { diff --git a/src/mono/browser/runtime/runtime.c b/src/mono/browser/runtime/runtime.c index 73e64f0a8a3fea..9030c4a52ee556 100644 --- a/src/mono/browser/runtime/runtime.c +++ b/src/mono/browser/runtime/runtime.c @@ -318,6 +318,7 @@ mono_wasm_load_runtime_common (int debug_level, MonoLogCallback log_callback, co return domain; } +// TODO https://github.com/dotnet/runtime/issues/98366 EMSCRIPTEN_KEEPALIVE MonoAssembly* mono_wasm_assembly_load (const char *name) { @@ -331,6 +332,7 @@ mono_wasm_assembly_load (const char *name) return res; } +// TODO https://github.com/dotnet/runtime/issues/98366 EMSCRIPTEN_KEEPALIVE MonoClass* mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, const char *name) { @@ -342,21 +344,7 @@ mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, co return result; } -extern int mono_runtime_run_module_cctor (MonoImage *image, MonoError *error); - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_runtime_run_module_cctor (MonoAssembly *assembly) -{ - assert (assembly); - MonoError error; - MONO_ENTER_GC_UNSAFE; - MonoImage *image = mono_assembly_get_image (assembly); - if (!mono_runtime_run_module_cctor(image, &error)) { - //g_print ("Failed to run module constructor due to %s\n", mono_error_get_message (error)); - } - MONO_EXIT_GC_UNSAFE; -} - +// TODO https://github.com/dotnet/runtime/issues/98366 EMSCRIPTEN_KEEPALIVE MonoMethod* mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int arguments) { @@ -367,31 +355,3 @@ mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int argument MONO_EXIT_GC_UNSAFE; return result; } - -EMSCRIPTEN_KEEPALIVE void -mono_wasm_invoke_method_ref (MonoMethod *method, MonoObject **this_arg_in, void *params[], MonoObject **_out_exc, MonoObject **out_result) -{ - PPVOLATILE(MonoObject) out_exc = _out_exc; - PVOLATILE(MonoObject) temp_exc = NULL; - if (out_exc) - *out_exc = NULL; - else - out_exc = &temp_exc; - - MONO_ENTER_GC_UNSAFE; - if (out_result) { - *out_result = NULL; - PVOLATILE(MonoObject) invoke_result = mono_runtime_invoke (method, this_arg_in ? *this_arg_in : NULL, params, (MonoObject **)out_exc); - store_volatile(out_result, invoke_result); - } else { - mono_runtime_invoke (method, this_arg_in ? *this_arg_in : NULL, params, (MonoObject **)out_exc); - } - - if (*out_exc && out_result) { - PVOLATILE(MonoObject) exc2 = NULL; - store_volatile(out_result, (MonoObject*)mono_object_to_string (*out_exc, (MonoObject **)&exc2)); - if (exc2) - store_volatile(out_result, (MonoObject*)mono_string_new (mono_get_root_domain (), "Exception Double Fault")); - } - MONO_EXIT_GC_UNSAFE; -} diff --git a/src/mono/browser/runtime/strings.ts b/src/mono/browser/runtime/strings.ts index 9058f29d0bf2a9..ae73fe57ff50d0 100644 --- a/src/mono/browser/runtime/strings.ts +++ b/src/mono/browser/runtime/strings.ts @@ -31,6 +31,8 @@ export function strings_init(): void { } mono_wasm_string_decoder_buffer = Module._malloc(12); } + if (!mono_wasm_string_root) + mono_wasm_string_root = mono_wasm_new_root(); } export function stringToUTF8(str: string): Uint8Array { @@ -257,8 +259,6 @@ let mono_wasm_string_root: any; export function monoStringToStringUnsafe(mono_string: MonoString): string | null { if (mono_string === MonoStringNull) return null; - if (!mono_wasm_string_root) - mono_wasm_string_root = mono_wasm_new_root(); mono_wasm_string_root.value = mono_string; const result = monoStringToString(mono_wasm_string_root); From 72c488e37818e18557b47292f519489ea60af2f8 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Thu, 15 Feb 2024 12:56:07 +0100 Subject: [PATCH 051/158] JIT: Discard simple dominated CSEs that will never be defs (#98417) Be a bit smarter when indexing CSEs if we see the same value twice in the same basic block and the second one has more exceptions than the first one. In that case the first will never be a candidate def for the second, so it is always better to just consider the second one to be the def. This is particularly relevant with passthrough nodes like `CASTCLASS(x, y)`, where `CASTCLASS` gets the same VN as its first operand, but with more exceptions on it. Previously we would consider x to be the def; this naturally considers the full `CASTCLASS(x, y)` to be the def, which promises more exceptions. One thing to note is that `GT_COMMA` would benefit from this handling too, but it is already special cased such that we create a separate candidate for the `GT_COMMA` (that includes the exception set of op1) and its op2 (that doesn't). That would also be a potential way to improve the situation here, but it requires special casing all such cases. The fix here is very simplistic and doesn't handle many cases. In particular we could have seen x previously in another basic block, in which case we run into the same issue again. I think switching CSE to be dominator tree based and generating multiple candidates instead would be a better general solution; hopefully we can work towards that. If not, this at least alleviates some of the problem. --- src/coreclr/jit/optcse.cpp | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index 6442100082eb76..99d03f5958054c 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -609,10 +609,45 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt) treeStmtLst* newElem; - /* Have we started the list of matching nodes? */ + // Have we started the list of matching nodes? if (hashDsc->csdTreeList == nullptr) { + // This is the second time we see this value. Handle cases + // where the first value dominates the second one and we can + // already prove that the first one is _not_ going to be a + // valid def for the second one, due to the second one having + // more exceptions. This happens for example in code like + // CASTCLASS(x, y) where the "CASTCLASS" just adds exceptions + // on top of "x". In those cases it is always better to let the + // second value be the def. + // It also happens for GT_COMMA, but that one is special cased + // above; this handling is a less special-casey version of the + // GT_COMMA handling above. However, it is quite limited since + // it only handles the def/use being in the same block. + if (compCurBB == hashDsc->csdBlock) + { + GenTree* prevTree = hashDsc->csdTree; + ValueNum prevVnLib = prevTree->GetVN(VNK_Liberal); + if (prevVnLib != vnLib) + { + ValueNum prevExceptionSet = vnStore->VNExceptionSet(prevVnLib); + ValueNum curExceptionSet = vnStore->VNExceptionSet(vnLib); + if ((prevExceptionSet != curExceptionSet) && + vnStore->VNExcIsSubset(curExceptionSet, prevExceptionSet)) + { + JITDUMP("Skipping CSE candidate for tree [%06u]; tree [%06u] is a better candidate with " + "more exceptions\n", + prevTree->gtTreeID, tree->gtTreeID); + prevTree->gtCSEnum = 0; + hashDsc->csdStmt = stmt; + hashDsc->csdTree = tree; + tree->gtCSEnum = (signed char)hashDsc->csdIndex; + return hashDsc->csdIndex; + } + } + } + // Create the new element based upon the matching hashDsc element. newElem = new (this, CMK_TreeStatementList) treeStmtLst; From 963626276e11bf5587aaed69826b62682b05d9c4 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Thu, 15 Feb 2024 13:32:52 +0100 Subject: [PATCH 052/158] [wasm] Fix "bump-chrome-pr.env" update. (#98480) * Fix "bump-chrome-pr.env" update. * Fix: do not overwrite the .env file (emptying it) when there was no update. --- eng/testing/bump-chrome-version.proj | 11 ++----- .../WasmBuildTasks/UpdateChromeVersions.cs | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/eng/testing/bump-chrome-version.proj b/eng/testing/bump-chrome-version.proj index 7bb437919b08d4..334e37bfd32ba7 100644 --- a/eng/testing/bump-chrome-version.proj +++ b/eng/testing/bump-chrome-version.proj @@ -4,6 +4,7 @@ $(RepositoryEngineeringDir)testing\ChromeVersions.props + $(RepositoryEngineeringDir)testing\bump-chrome-pr.env @@ -13,19 +14,13 @@ Channel="$(ChromeChannel)" MaxMajorVersionsToCheck="1" IntermediateOutputPath="$(ArtifactsObjDir)" - ChromeVersionsPath="$(ChromeVersionsPath)"> + ChromeVersionsPath="$(ChromeVersionsPath)" + EnvVarsForPRPath="$(EnvVarsForPRPath)"> - - - - - - - diff --git a/src/tasks/WasmBuildTasks/UpdateChromeVersions.cs b/src/tasks/WasmBuildTasks/UpdateChromeVersions.cs index 998a888c384449..bdf44cdfed474d 100644 --- a/src/tasks/WasmBuildTasks/UpdateChromeVersions.cs +++ b/src/tasks/WasmBuildTasks/UpdateChromeVersions.cs @@ -44,6 +44,9 @@ public partial class UpdateChromeVersions : MBU.Task [Required, NotNull] public string ChromeVersionsPath { get; set; } = string.Empty; + [Required, NotNull] + public string EnvVarsForPRPath { get; set; } = string.Empty; + public int MaxMajorVersionsToCheck { get; set; } = 2; // start at the branch position found in all.json, and try to @@ -70,15 +73,21 @@ private async Task ExecuteInternalAsync() XmlDocument chromeVersionsXmlDoc = new XmlDocument(); chromeVersionsXmlDoc.Load(ChromeVersionsPath); var osInfo = OSIdentifiers.Zip(OSPrefixes, (num, str) => new { Identifier = num, Prefix = str }); + List versions = new(); foreach (var info in osInfo) { (ChromeVersionSpec version, string baseUrl) = await FindVersionFromChromiumDash(info.Prefix, info.Identifier).ConfigureAwait(false); + versions.Add(version); bool hasMajorChanges = AreVersionsChanged(chromeVersionsXmlDoc, version, baseUrl); if (hasMajorChanges) { VersionsChanged = UpdateChromeVersionsFile(chromeVersionsXmlDoc, version, baseUrl); } } + if (VersionsChanged) + { + UpdateEnvVarsForPRFile(versions); + } return !Log.HasLoggedErrors; } catch (LogAsErrorException laee) @@ -137,6 +146,26 @@ private bool UpdateChromeVersionsFile(XmlDocument xmlDoc, ChromeVersionSpec vers return true; } + private void UpdateEnvVarsForPRFile(List versions) + { + using StreamWriter writer = new StreamWriter(EnvVarsForPRPath); + foreach (var version in versions) + { + if (string.Equals(version.os, "Linux", StringComparison.OrdinalIgnoreCase)) + { + writer.WriteLine($"CHROME_LINUX_VER={version.version}"); + } + else if (string.Equals(version.os, "Windows", StringComparison.OrdinalIgnoreCase)) + { + writer.WriteLine($"CHROME_WIN_VER={version.version}"); + } + else + { + throw new Exception($"UpdateEnvVarsForPRFile task was used with unknown OS: {version.os}"); + } + } + } + private static void UpdateNodeValue(XmlDocument xmlDoc, string nodeName, string newValue) { XmlNode? node = xmlDoc.SelectSingleNode($"/Project/PropertyGroup/{nodeName}"); From bccbf2ea1b45a8d8013308f562084c3f3ad16ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 15 Feb 2024 14:02:43 +0100 Subject: [PATCH 053/158] [browser] Use standard xunit test runner on multithreaded runtime (#91317) --- eng/testing/tests.browser.targets | 14 ++++-- eng/testing/tests.wasi.targets | 4 +- .../tests/WasmTestRunner/WasmTestRunner.cs | 47 ++++++++++--------- .../System.Collections.Immutable.Tests.csproj | 2 +- .../System.Net.Http.Functional.Tests.csproj | 2 +- .../System.Net.WebSockets.Client.Tests.csproj | 2 +- .../tests/System.Private.Xml.Tests.csproj | 2 +- ...me.InteropServices.JavaScript.Tests.csproj | 2 +- ...ileSystem.DisabledFileLocking.Tests.csproj | 3 ++ .../System.IO.FileSystem.Tests.csproj | 3 ++ .../System.Threading.Tasks.Tests.csproj | 1 + .../System.Text.Json.Tests.csproj | 2 +- src/mono/browser/README.md | 14 ++++++ src/mono/wasm/sln/WasmBuild.sln | 8 +++- 14 files changed, 73 insertions(+), 33 deletions(-) diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets index df305affa4b4c4..d27fa412d490b1 100644 --- a/eng/testing/tests.browser.targets +++ b/eng/testing/tests.browser.targets @@ -91,7 +91,7 @@ $(WasmTestAppArgs) -backgroundExec <_AppArgs Condition="'$(WasmTestAppArgs)' != ''">$(_AppArgs) $(WasmTestAppArgs) - $(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=1 + $(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=true $(WasmXHarnessMonoArgs) --setenv=IsBrowserThreadingSupported=true @@ -108,9 +108,11 @@ <_XHarnessArgs Condition="'$(_UseWasmSymbolicator)' == 'true'" >$(_XHarnessArgs) --symbolicator WasmSymbolicator.dll,Microsoft.WebAssembly.Internal.SymbolicatorWrapperForXHarness <_XHarnessArgs Condition="'$(_WasmBrowserPathForTests)' != ''" >$(_XHarnessArgs) "--browser-path=$(_WasmBrowserPathForTests)" <_XHarnessArgs Condition="'$(WasmXHarnessTestsTimeout)' != ''" >$(_XHarnessArgs) "--timeout=$(WasmXHarnessTestsTimeout)" + <_XHarnessArgs Condition="'$(WasmXHarnessVerbosity)' != ''" >$(_XHarnessArgs) --verbosity=$(WasmXHarnessVerbosity) <_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli) - + <_AppArgs Condition="'$(WasmEnableThreads)' == 'true'">$(_AppArgs) -threads + $HARNESS_RUNNER $(_XHarnessArgs) %24XHARNESS_ARGS %24WasmXHarnessArgs -- $(WasmXHarnessMonoArgs) %24WasmXHarnessMonoArgs $(_AppArgs) %24WasmTestAppArgs %HARNESS_RUNNER% $(_XHarnessArgs) %XHARNESS_ARGS% %WasmXHarnessArgs% -- $(WasmXHarnessMonoArgs) %WasmXHarnessMonoArgs% $(_AppArgs) %WasmTestAppArgs% @@ -147,6 +149,12 @@ PrepareForWasmBuildApp;$(WasmNestedPublishAppDependsOn) + + + + + + $(BundleDir) @@ -197,7 +205,7 @@ diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index 8516230d7e6da6..d147fea218fe4a 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -31,7 +31,7 @@ <_AppArgs Condition="'$(WasmTestAppArgs)' != ''">$(_AppArgs) -- $(WasmTestAppArgs) - $(WasmXHarnessMonoArgs) --env=XHARNESS_LOG_TEST_START=1 + $(WasmXHarnessMonoArgs) --env=XHARNESS_LOG_TEST_START=true @@ -51,7 +51,7 @@ <_InvariantGlobalization Condition="'$(InvariantGlobalization)' == 'true'">--env=DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true - + $HARNESS_RUNNER $(_XHarnessArgs) %24XHARNESS_ARGS %24WasmXHarnessArgs -- $(WasmXHarnessMonoArgs) %24WasmXHarnessMonoArgs $(_InvariantGlobalization) %24_InvariantGlobalization $(_AppArgs) %24WasmTestAppArgs %HARNESS_RUNNER% $(_XHarnessArgs) %XHARNESS_ARGS% %WasmXHarnessArgs% -- $(WasmXHarnessMonoArgs) %WasmXHarnessMonoArgs% $(_InvariantGlobalization) %_InvariantGlobalization% $(_AppArgs) %WasmTestAppArgs% diff --git a/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs b/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs index 326cd7b10e918a..2eb1c1e440c33b 100644 --- a/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs +++ b/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs @@ -5,20 +5,25 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.DotNet.XHarness.TestRunners.Common; - using Microsoft.DotNet.XHarness.TestRunners.Xunit; -public class SimpleWasmTestRunner : WasmApplicationEntryPoint +public class WasmTestRunner : WasmApplicationEntryPoint { + // TODO: Set max threads for run in parallel + // protected override int? MaxParallelThreads => RunInParallel ? 8 : base.MaxParallelThreads; + public static async Task Main(string[] args) { if (args.Length == 0) { - Console.WriteLine ($"No args given"); + Console.WriteLine($"No args given"); return -1; } - var testAssembly = args[0]; + var runner = new WasmTestRunner(); + + runner.TestAssembly = args[0]; + var excludedTraits = new List(); var includedTraits = new List(); var includedNamespaces = new List(); @@ -26,7 +31,6 @@ public static async Task Main(string[] args) var includedMethods = new List(); var backgroundExec = false; var untilFailed = false; - var minimumLogLevel = MinimumLogLevel.Info; for (int i = 1; i < args.Length; i++) { @@ -34,23 +38,23 @@ public static async Task Main(string[] args) switch (option) { case "-notrait": - excludedTraits.Add (args[i + 1]); + excludedTraits.Add(args[i + 1]); i++; break; case "-trait": - includedTraits.Add (args[i + 1]); + includedTraits.Add(args[i + 1]); i++; break; case "-namespace": - includedNamespaces.Add (args[i + 1]); + includedNamespaces.Add(args[i + 1]); i++; break; case "-class": - includedClasses.Add (args[i + 1]); + includedClasses.Add(args[i + 1]); i++; break; case "-method": - includedMethods.Add (args[i + 1]); + includedMethods.Add(args[i + 1]); i++; break; case "-backgroundExec": @@ -59,8 +63,14 @@ public static async Task Main(string[] args) case "-untilFailed": untilFailed = true; break; + case "-threads": + runner.IsThreadless = false; + // TODO: Enable run in parallel + // runner.RunInParallel = true; + // Console.WriteLine($"Running in parallel with {runner.MaxParallelThreads} threads."); + break; case "-verbosity": - minimumLogLevel = Enum.Parse(args[i + 1]); + runner.MinimumLogLevel = Enum.Parse(args[i + 1]); i++; break; default: @@ -68,16 +78,11 @@ public static async Task Main(string[] args) } } - var runner = new SimpleWasmTestRunner() - { - TestAssembly = testAssembly, - ExcludedTraits = excludedTraits, - IncludedTraits = includedTraits, - IncludedNamespaces = includedNamespaces, - IncludedClasses = includedClasses, - IncludedMethods = includedMethods, - MinimumLogLevel = minimumLogLevel - }; + runner.ExcludedTraits = excludedTraits; + runner.IncludedTraits = includedTraits; + runner.IncludedNamespaces = includedNamespaces; + runner.IncludedClasses = includedClasses; + runner.IncludedMethods = includedMethods; if (OperatingSystem.IsBrowser()) { diff --git a/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj b/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj index 4286b79bbd414b..839c10352bb4f4 100644 --- a/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj +++ b/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj @@ -6,7 +6,7 @@ - --setenv=XHARNESS_LOG_TEST_START=true + true 01:15:00 diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index d195737dfeb008..26cc220014655f 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -24,7 +24,7 @@ $(TestArchiveRoot)browserornodejs/ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ $(DefineConstants);TARGET_BROWSER - --setenv=XHARNESS_LOG_TEST_START=true + true 01:15:00 diff --git a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj index 3ccc01e5e63f5b..7bb14097848cb5 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj +++ b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj @@ -13,7 +13,7 @@ $(TestArchiveRoot)browserornodejs/ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ $(DefineConstants);TARGET_BROWSER - --setenv=XHARNESS_LOG_TEST_START=true + true 01:15:00 diff --git a/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj b/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj index 3983ac03f42067..30fe5d7a261a46 100644 --- a/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj +++ b/src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj @@ -14,7 +14,7 @@ $(TestArchiveRoot)browserornodejs/ $(TestArchiveTestsRoot)$(OSPlatformConfig)/ $(DefineConstants);TARGET_BROWSER - --setenv=XHARNESS_LOG_TEST_START=true + true diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj index f390f63deaeb67..14a51e460d913d 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj @@ -13,7 +13,7 @@ true - $(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=true + true diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj index 8f474ca378fc9d..62425e583e9145 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/DisabledFileLockingTests/System.IO.FileSystem.DisabledFileLocking.Tests.csproj @@ -7,6 +7,9 @@ --working-dir=/test-dir + + + diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/System.IO.FileSystem.Tests.csproj index 738960903f5045..c2d3f67d0d556c 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/System.IO.FileSystem.Tests.csproj @@ -6,6 +6,9 @@ --working-dir=/test-dir + + + diff --git a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj index eb1b13766e64f9..19521db08c3331 100644 --- a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj @@ -3,6 +3,7 @@ true true $(NetCoreAppCurrent) + true diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 8bec6eca8c7ea1..ddb87ab85f5745 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -18,7 +18,7 @@ true - --setenv=XHARNESS_LOG_TEST_START=true + true 01:15:00 diff --git a/src/mono/browser/README.md b/src/mono/browser/README.md index 5ee64fcd4724d2..4fb494a86b199d 100644 --- a/src/mono/browser/README.md +++ b/src/mono/browser/README.md @@ -117,10 +117,24 @@ The wrapper script used to actually run these tests, accepts: ### Using a local build of xharness +XHarness consists of two pieces for WASM + +#### 1. CLI/host + * set `XHARNESS_CLI_PATH=/path/to/xharness/artifacts/bin/Microsoft.DotNet.XHarness.CLI/Debug/net7.0/Microsoft.DotNet.XHarness.CLI.dll` **Note:** Additional msbuild arguments can be passed with: `make .. MSBUILD_ARGS="/p:a=b"` +#### 2. Test runner running inside of the browser + +All library tests are hosted by `WasmTestRunner.csproj`. The project references XHarness nuget for running tests using Xunit. To make changes and iterate quickly + +- Add property `$(RestoreAdditionalProjectSources);LOCAL_CLONE_OF_XHARNESS\artifacts\packages\Debug\Shipping` in `WasmTestRunner.csproj`. +- Set environment variable in your terminal `$env:NUGET_PACKAGES="$pwd\.nuget"` (so that nuget packages are restored to local folder `.nuget`) +- Run "Pack" in the XHarness solution in Visual Studio on `Microsoft.DotNet.XHarness.TestRunners.Common` or `Microsoft.DotNet.XHarness.TestRunners.Xunit` based on your changes (it will generate a nuget package in `LOCAL_CLONE_OF_XHARNESS\artifacts\packages\Debug\Shipping`). +- Build WasmTestRunner `.\dotnet.cmd build -c Debug .\src\libraries\Common\tests\WasmTestRunner\WasmTestRunner.csproj`. +- If you need to iterate, delete Xunit or Common nuget cache `rm -r .\.nuget\microsoft.dotnet.xharness.testrunners.xunit\` or `rm -r .\.nuget\microsoft.dotnet.xharness.testrunners.common\`. + ### Symbolicating traces Exceptions thrown after the runtime starts get symbolicating from js itself. Exceptions before that, like asserts containing native traces get symbolicated by xharness using `src/mono/wasm/symbolicator`. diff --git a/src/mono/wasm/sln/WasmBuild.sln b/src/mono/wasm/sln/WasmBuild.sln index da3a82af643fb0..b84f2d7ba8c848 100755 --- a/src/mono/wasm/sln/WasmBuild.sln +++ b/src/mono/wasm/sln/WasmBuild.sln @@ -25,7 +25,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplyUpdateReferencedAssemb EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Sdk.WebAssembly.Pack.Tasks", "..\..\..\tasks\Microsoft.NET.Sdk.WebAssembly.Pack.Tasks\Microsoft.NET.Sdk.WebAssembly.Pack.Tasks.csproj", "{5EEC2925-2021-4830-B7E9-72BB8B2C283D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wasi.Build.Tests", "..\..\wasi\Wasi.Build.Tests\Wasi.Build.Tests.csproj", "{3A3AEAE5-0110-45D3-89B0-B82AC430535C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasi.Build.Tests", "..\..\wasi\Wasi.Build.Tests\Wasi.Build.Tests.csproj", "{3A3AEAE5-0110-45D3-89B0-B82AC430535C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WasmTestRunner", "..\..\..\libraries\Common\tests\WasmTestRunner\WasmTestRunner.csproj", "{2BBE4AA8-5424-44AB-933C-66554B688872}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -81,6 +83,10 @@ Global {3A3AEAE5-0110-45D3-89B0-B82AC430535C}.Debug|Any CPU.Build.0 = Debug|Any CPU {3A3AEAE5-0110-45D3-89B0-B82AC430535C}.Release|Any CPU.ActiveCfg = Release|Any CPU {3A3AEAE5-0110-45D3-89B0-B82AC430535C}.Release|Any CPU.Build.0 = Release|Any CPU + {2BBE4AA8-5424-44AB-933C-66554B688872}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2BBE4AA8-5424-44AB-933C-66554B688872}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2BBE4AA8-5424-44AB-933C-66554B688872}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2BBE4AA8-5424-44AB-933C-66554B688872}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 64204a583ec9563b67b4e9d06538bbf46dcb29ca Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 15 Feb 2024 08:14:03 -0500 Subject: [PATCH 054/158] Make SocketsHttpHandler's content encoding comparison case-insensitive (#98475) --- .../Net/Http/HttpClientHandlerTest.Decompression.cs | 8 ++++++-- .../Net/Http/SocketsHttpHandler/DecompressionHandler.cs | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs index 439cadf32ae620..4da9edc2ca2147 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Decompression.cs @@ -28,7 +28,7 @@ public abstract class HttpClientHandler_Decompression_Test : HttpClientHandlerTe public HttpClientHandler_Decompression_Test(ITestOutputHelper output) : base(output) { } public static IEnumerable DecompressedResponse_MethodSpecified_DecompressedContentReturned_MemberData() => - from compressionName in new[] { "gzip", "zlib", "deflate", "br" } + from compressionName in new[] { "gzip", "GZIP", "zlib", "ZLIB", "deflate", "DEFLATE", "br", "BR" } from all in new[] { false, true } from copyTo in new[] { false, true } from contentLength in new[] { 0, 1, 12345 } @@ -40,7 +40,7 @@ public static IEnumerable DecompressedResponse_MethodSpecified_Decompr public async Task DecompressedResponse_MethodSpecified_DecompressedContentReturned(string compressionName, bool all, bool useCopyTo, int contentLength) { if (IsWinHttpHandler && - (compressionName == "br" || compressionName == "zlib")) + (compressionName is "br" or "BR" or "zlib" or "ZLIB")) { // brotli and zlib not supported on WinHttpHandler return; @@ -52,17 +52,20 @@ public async Task DecompressedResponse_MethodSpecified_DecompressedContentReturn switch (compressionName) { case "gzip": + case "GZIP": compress = s => new GZipStream(s, CompressionLevel.Optimal, leaveOpen: true); methods = all ? DecompressionMethods.GZip : _all; break; #if !NETFRAMEWORK case "br": + case "BR": compress = s => new BrotliStream(s, CompressionLevel.Optimal, leaveOpen: true); methods = all ? DecompressionMethods.Brotli : _all; break; case "zlib": + case "ZLIB": compress = s => new ZLibStream(s, CompressionLevel.Optimal, leaveOpen: true); methods = all ? DecompressionMethods.Deflate : _all; encodingName = "deflate"; @@ -70,6 +73,7 @@ public async Task DecompressedResponse_MethodSpecified_DecompressedContentReturn #endif case "deflate": + case "DEFLATE": compress = s => new DeflateStream(s, CompressionLevel.Optimal, leaveOpen: true); methods = all ? DecompressionMethods.Deflate : _all; break; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/DecompressionHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/DecompressionHandler.cs index 939a29e06f393e..1ea55ac07f6144 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/DecompressionHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/DecompressionHandler.cs @@ -78,15 +78,15 @@ internal override async ValueTask SendAsync(HttpRequestMess last = encoding; } - if (GZipEnabled && last == Gzip) + if (GZipEnabled && string.Equals(last, Gzip, StringComparison.OrdinalIgnoreCase)) { response.Content = new GZipDecompressedContent(response.Content); } - else if (DeflateEnabled && last == Deflate) + else if (DeflateEnabled && string.Equals(last, Deflate, StringComparison.OrdinalIgnoreCase)) { response.Content = new DeflateDecompressedContent(response.Content); } - else if (BrotliEnabled && last == Brotli) + else if (BrotliEnabled && string.Equals(last, Brotli, StringComparison.OrdinalIgnoreCase)) { response.Content = new BrotliDecompressedContent(response.Content); } From fa3dccb7e8ba5703007c308bb9916a36cf826ba6 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Thu, 15 Feb 2024 15:46:53 +0200 Subject: [PATCH 055/158] [mono][interp] Free var_values table later (#98482) It contains, among other things, ref count information for vars. This is also used late in the codgen process, for peephole optimization when generating marvin block intrinsic. --- src/mono/mono/mini/interp/transform.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index d1584c31d496bf..98be733a89b121 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -9022,12 +9022,6 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG if (td->optimized) { MONO_TIME_TRACK (mono_interp_stats.optimize_time, interp_optimize_code (td)); interp_alloc_offsets (td); - // Offset allocator uses computed ref counts from var values. We have to free this - // table later here. - if (td->var_values != NULL) { - g_free (td->var_values); - td->var_values = NULL; - } interp_squash_initlocals (td); #if HOST_BROWSER if (mono_interp_opt & INTERP_OPT_JITERPRETER) @@ -9037,6 +9031,15 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG generate_compacted_code (rtm, td); + if (td->optimized) { + // Offset allocator and compacted code generation use computed ref counts + // from var values. We have to free this table later here. + if (td->var_values != NULL) { + g_free (td->var_values); + td->var_values = NULL; + } + } + if (td->total_locals_size >= G_MAXUINT16) { if (td->disable_inlining && td->optimized) { char *name = mono_method_get_full_name (method); From ad7ca4062298a3fbafccd3bd938183cf291a8014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 15 Feb 2024 16:06:46 +0100 Subject: [PATCH 056/158] [browser][wbt] Print build info and ensure WasmEnableThreads is true in Blazor MT WBT (#98490) --- .../Blazor/SimpleMultiThreadedTests.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs index c28f18a3c57469..5f1e1711151c09 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/SimpleMultiThreadedTests.cs @@ -48,7 +48,17 @@ public async Task BlazorPublishRunTest(string config, bool aot) string projectFile = CreateWasmTemplateProject(id, "blazorwasm"); AddItemsPropertiesToProject(projectFile, "true"); // if (aot) - // AddItemsPropertiesToProject(projectFile, "true"); + // AddItemsPropertiesToProject(projectFile, "true"); + + File.WriteAllText( + Path.Combine(Path.GetDirectoryName(projectFile)!, "wwwroot", id + ".lib.module.js"), + """ + export function onRuntimeReady({ runtimeBuildInfo }) { + console.log('Runtime is ready: ' + JSON.stringify(runtimeBuildInfo)); + console.log(`WasmEnableThreads=${runtimeBuildInfo.wasmEnableThreads}`); + } + """ + ); BlazorPublish(new BlazorBuildOptions( id, @@ -57,6 +67,7 @@ public async Task BlazorPublishRunTest(string config, bool aot) : (config == "Release" ? NativeFilesType.Relinked : NativeFilesType.FromRuntimePack), RuntimeType: RuntimeVariant.MultiThreaded)); + bool hasEmittedWasmEnableThreads = false; StringBuilder errorOutput = new(); await BlazorRunForPublishWithWebServer( runOptions: new BlazorRunOptions( @@ -64,6 +75,9 @@ await BlazorRunForPublishWithWebServer( ExtraArgs: "--web-server-use-cors --web-server-use-cop", OnConsoleMessage: (message) => { + if (message.Text.Contains("WasmEnableThreads=true")) + hasEmittedWasmEnableThreads = true; + if (message.Type == "error") errorOutput.AppendLine(message.Text); }, @@ -74,5 +88,8 @@ await BlazorRunForPublishWithWebServer( if (errorOutput.Length > 0) throw new XunitException($"Errors found in browser console output:\n{errorOutput}"); + + if (!hasEmittedWasmEnableThreads) + throw new XunitException($"The test didn't emit expected message 'WasmEnableThreads=true'"); } } From 0810dc702a8de73c9021eace0d1c1f79f573eb2f Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 15 Feb 2024 10:12:50 -0500 Subject: [PATCH 057/158] Make all StreamWriter.Null operations nops (#98473) --- .../src/System/IO/StreamWriter.cs | 79 +++++++++++++++- .../src/System/IO/TextWriter.cs | 2 + .../Stream/Stream.NullTests.cs | 93 +++++++++++++++++-- 3 files changed, 166 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs index df0c9e95cc6c05..6eaa2c47cae179 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -27,7 +28,7 @@ public class StreamWriter : TextWriter private const int MinBufferSize = 128; // Bit bucket - Null has no backing store. Non closable. - public static new readonly StreamWriter Null = new StreamWriter(Stream.Null, UTF8NoBOM, MinBufferSize, leaveOpen: true); + public static new readonly StreamWriter Null = new NullStreamWriter(); private readonly Stream _stream; private readonly Encoding _encoding; @@ -159,6 +160,15 @@ public StreamWriter(string path, Encoding encoding, FileStreamOptions options) { } + private StreamWriter() + { + Debug.Assert(GetType() == typeof(NullStreamWriter)); + _stream = Stream.Null; + _encoding = UTF8NoBOM; + _encoder = null!; + _charBuffer = Array.Empty(); + } + private static FileStream ValidateArgsAndOpenPath(string path, Encoding encoding, FileStreamOptions options) { ArgumentException.ThrowIfNullOrEmpty(path); @@ -973,5 +983,72 @@ private void ThrowIfDisposed() void ThrowObjectDisposedException() => throw new ObjectDisposedException(GetType().Name, SR.ObjectDisposed_WriterClosed); } + + private sealed class NullStreamWriter : StreamWriter + { + public override bool AutoFlush { get => false; set { } } + [AllowNull] + public override string NewLine { get => base.NewLine; set { } } + public override IFormatProvider FormatProvider => CultureInfo.InvariantCulture; + + // To avoid all unnecessary overhead in the base, and to ensure StreamWriter's uninitialized state is never touched, + // override all methods as pure nops. + public override void Close() { } + protected override void Dispose(bool disposing) { } + public override ValueTask DisposeAsync() => default; + public override void Flush() { } + public override Task FlushAsync() => Task.CompletedTask; + public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; + public override void Write(char value) { } + public override void Write(char[]? buffer) { } + public override void Write(char[] buffer, int index, int count) { } + public override void Write(ReadOnlySpan buffer) { } + public override void Write(bool value) { } + public override void Write(int value) { } + public override void Write(uint value) { } + public override void Write(long value) { } + public override void Write(ulong value) { } + public override void Write(float value) { } + public override void Write(double value) { } + public override void Write(decimal value) { } + public override void Write(string? value) { } + public override void Write(object? value) { } + public override void Write(StringBuilder? value) { } + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) { } + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) { } + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) { } + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) { } + public override Task WriteAsync(char value) => Task.CompletedTask; + public override Task WriteAsync(string? value) => Task.CompletedTask; + public override Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default) => Task.CompletedTask; + public override Task WriteAsync(char[] buffer, int index, int count) => Task.CompletedTask; + public override Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => Task.CompletedTask; + public override void WriteLine() { } + public override void WriteLine(char value) { } + public override void WriteLine(char[]? buffer) { } + public override void WriteLine(char[] buffer, int index, int count) { } + public override void WriteLine(ReadOnlySpan buffer) { } + public override void WriteLine(bool value) { } + public override void WriteLine(int value) { } + public override void WriteLine(uint value) { } + public override void WriteLine(long value) { } + public override void WriteLine(ulong value) { } + public override void WriteLine(float value) { } + public override void WriteLine(double value) { } + public override void WriteLine(decimal value) { } + public override void WriteLine(string? value) { } + public override void WriteLine(StringBuilder? value) { } + public override void WriteLine(object? value) { } + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) { } + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) { } + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) { } + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) { } + public override Task WriteLineAsync(char value) => Task.CompletedTask; + public override Task WriteLineAsync(string? value) => Task.CompletedTask; + public override Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default) => Task.CompletedTask; + public override Task WriteLineAsync(char[] buffer, int index, int count) => Task.CompletedTask; + public override Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => Task.CompletedTask; + public override Task WriteLineAsync() => Task.CompletedTask; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs index 874638ee34fd87..289ec5fac7a5e8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs @@ -672,6 +672,8 @@ internal NullTextWriter() { } public override IFormatProvider FormatProvider => CultureInfo.InvariantCulture; public override Encoding Encoding => Encoding.Unicode; + [AllowNull] + public override string NewLine { get => base.NewLine; set { } } // To avoid all unnecessary overhead in the base, override all Flush/Write methods as pure nops. diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/Stream/Stream.NullTests.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/Stream/Stream.NullTests.cs index efe700c1e9c753..86ebfba5efbefb 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/Stream/Stream.NullTests.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/Stream/Stream.NullTests.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Globalization; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using Xunit; @@ -196,14 +198,93 @@ public static async Task TestCanceledNullTextReaderAsync(TextReader input) [MemberData(nameof(NullWriters))] public static void TextNullTextWriter(TextWriter output) { - output.Flush(); + // Use every method to make sure they don't throw + + output.Close(); output.Dispose(); + Assert.True(output.DisposeAsync().IsCompletedSuccessfully); + + Assert.NotNull(output.Encoding); + Assert.Same(CultureInfo.InvariantCulture, output.FormatProvider); + Assert.Equal(Environment.NewLine, output.NewLine); + output.NewLine = "hello"; + Assert.Equal(Environment.NewLine, output.NewLine); - output.WriteLine(decimal.MinValue); - output.WriteLine(Math.PI); - output.WriteLine(); output.Flush(); - output.Dispose(); + Assert.True(output.FlushAsync().IsCompletedSuccessfully); + Assert.True(output.FlushAsync(CancellationToken.None).IsCompletedSuccessfully); + Assert.True(output.FlushAsync(new CancellationToken(true)).IsCompletedSuccessfully); + + output.Write('a'); + output.Write((char[])null); + output.Write(new char[] { 'b', 'c' }); + output.Write(42m); + output.Write(43d); + output.Write(44f); + output.Write(45); + output.Write(46L); + output.Write(DayOfWeek.Monday); + output.Write((string)null); + output.Write("Tuesday"); + output.Write((StringBuilder)null); + output.Write(new StringBuilder("Wednesday")); + output.Write(47u); + output.Write(48ul); + output.Write("Thursday".AsSpan()); + output.Write(" {0} ", "Friday"); + output.Write(" {0}{1} ", "Saturday", "Sunday"); + output.Write(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); + output.Write(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); + output.WriteLine(); + output.WriteLine(true); + output.WriteLine('a'); + output.WriteLine((char[])null); + output.WriteLine(new char[] { 'b', 'c' }); + output.WriteLine(42m); + output.WriteLine(43d); + output.WriteLine(44f); + output.WriteLine(45); + output.WriteLine(46L); + output.WriteLine(DayOfWeek.Monday); + output.WriteLine((string)null); + output.WriteLine("Tuesday"); + output.WriteLine((StringBuilder)null); + output.WriteLine(new StringBuilder("Wednesday")); + output.WriteLine(47u); + output.WriteLine(48ul); + output.WriteLine("Thursday".AsSpan()); + output.WriteLine(" {0} ", "Friday"); + output.WriteLine(" {0}{1} ", "Saturday", "Sunday"); + output.WriteLine(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); + output.WriteLine(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); + Assert.True(output.WriteAsync('a').IsCompletedSuccessfully); + Assert.True(output.WriteAsync((char[])null).IsCompletedSuccessfully); + Assert.True(output.WriteAsync(new char[] { 'b', 'c' }).IsCompletedSuccessfully); + Assert.True(output.WriteAsync((string)null).IsCompletedSuccessfully); + Assert.True(output.WriteAsync("Tuesday").IsCompletedSuccessfully); + Assert.True(output.WriteAsync((StringBuilder)null).IsCompletedSuccessfully); + Assert.True(output.WriteAsync(new StringBuilder("Wednesday")).IsCompletedSuccessfully); + Assert.True(output.WriteLineAsync().IsCompletedSuccessfully); + Assert.True(output.WriteLineAsync('a').IsCompletedSuccessfully); + Assert.True(output.WriteLineAsync((char[])null).IsCompletedSuccessfully); + Assert.True(output.WriteLineAsync(new char[] { 'b', 'c' }).IsCompletedSuccessfully); + Assert.True(output.WriteLineAsync((string)null).IsCompletedSuccessfully); + Assert.True(output.WriteLineAsync("Tuesday").IsCompletedSuccessfully); + Assert.True(output.WriteLineAsync((StringBuilder)null).IsCompletedSuccessfully); + Assert.True(output.WriteLineAsync(new StringBuilder("Wednesday")).IsCompletedSuccessfully); + + if (output is StreamWriter sw) + { + Assert.False(sw.AutoFlush); + sw.AutoFlush = true; + Assert.False(sw.AutoFlush); + + Assert.Same(Stream.Null, sw.BaseStream); + } + + // Use some parallelism in an attempt to validate statelessness + string longLine = new string('#', 100_000); + Parallel.For(0, 25, i => output.WriteLine(longLine)); } [Theory] @@ -248,7 +329,6 @@ public static IEnumerable NullReaders { yield return new object[] { TextReader.Null }; yield return new object[] { StreamReader.Null }; - yield return new object[] { StringReader.Null }; } } @@ -258,7 +338,6 @@ public static IEnumerable NullWriters { yield return new object[] { TextWriter.Null }; yield return new object[] { StreamWriter.Null }; - yield return new object[] { StringWriter.Null }; } } From 6bb3103067b0f1b1c3129abe6d336ed4840eb374 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 09:56:50 -0600 Subject: [PATCH 058/158] Update dependencies from https://github.com/dotnet/emsdk build 20240214.5 (#98491) Microsoft.SourceBuild.Intermediate.emsdk , Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-preview.2.24113.2 -> To Version 9.0.0-preview.2.24114.5 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.24105.1 -> To Version 16.0.5-alpha.1.24112.1 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 132 ++++++++++++++++++++-------------------- eng/Versions.props | 64 +++++++++---------- 2 files changed, 98 insertions(+), 98 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 375dfe3baa91a2..25d402e9588994 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -12,73 +12,73 @@ https://github.com/dotnet/wcf 7f504aabb1988e9a093c1e74d8040bd52feb2f01 - + https://github.com/dotnet/emsdk - 4bbafbd3474cb3c927f792a9b6749fe89277ef7d + 8afd92448d03a80001c9cac5f2acb53b336263a4 - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d https://github.com/dotnet/command-line-api @@ -100,14 +100,14 @@ 93dcb576e191a965008eae9b622527436653873f - + https://github.com/dotnet/emsdk - 4bbafbd3474cb3c927f792a9b6749fe89277ef7d + 8afd92448d03a80001c9cac5f2acb53b336263a4 - + https://github.com/dotnet/emsdk - 4bbafbd3474cb3c927f792a9b6749fe89277ef7d + 8afd92448d03a80001c9cac5f2acb53b336263a4 @@ -258,61 +258,61 @@ https://github.com/dotnet/runtime-assets b5ac2d9031d4b2dc40683b31de86b05a20b670af - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/llvm-project - cb7d881de3674394a5f98d167bfb58f9aff9768b + 9885e5aecc176ca701fc3527877d608bf7ccfb7d https://github.com/dotnet/runtime diff --git a/eng/Versions.props b/eng/Versions.props index f7f28f795968eb..839e980577cb53 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -109,14 +109,14 @@ 9.0.0-preview.2.24111.9 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 6.0.0 1.1.1 @@ -225,39 +225,39 @@ 2.2.3 9.0.0-alpha.1.24067.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 - 9.0.0-preview.2.24113.2 + 9.0.0-preview.2.24114.5 $(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportVersion) - 9.0.0-preview.2.24113.2 + 9.0.0-preview.2.24114.5 1.1.87-gba258badda 1.0.0-v3.14.0.5722 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 - 16.0.5-alpha.1.24105.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 + 16.0.5-alpha.1.24112.1 3.1.7 1.0.406601 From eab4b76713187675f756713826c887594caf1ca1 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 10:00:18 -0600 Subject: [PATCH 059/158] [main] Update dependencies from dotnet/roslyn (#98421) * Update dependencies from https://github.com/dotnet/roslyn build 20240214.1 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-2.24112.13 -> To Version 4.10.0-2.24114.1 * Update dependencies from https://github.com/dotnet/roslyn build 20240214.13 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.10.0-2.24112.13 -> To Version 4.10.0-2.24114.13 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 16 ++++++++-------- eng/Versions.props | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 25d402e9588994..0bc2d69f0e0856 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -392,18 +392,18 @@ https://github.com/dotnet/runtime-assets b5ac2d9031d4b2dc40683b31de86b05a20b670af - + https://github.com/dotnet/roslyn - 892d5f01f7f41dfe7fcd82522276c5628255ef74 + 77372c66fd54927312b5b0a2e399e192f74445c9 - + https://github.com/dotnet/roslyn - 892d5f01f7f41dfe7fcd82522276c5628255ef74 + 77372c66fd54927312b5b0a2e399e192f74445c9 - + https://github.com/dotnet/roslyn - 892d5f01f7f41dfe7fcd82522276c5628255ef74 + 77372c66fd54927312b5b0a2e399e192f74445c9 https://github.com/dotnet/roslyn-analyzers @@ -414,9 +414,9 @@ 68c643b4667c6808bd21910ef32f7e2f7bd776c5 - + https://github.com/dotnet/roslyn - 892d5f01f7f41dfe7fcd82522276c5628255ef74 + 77372c66fd54927312b5b0a2e399e192f74445c9 diff --git a/eng/Versions.props b/eng/Versions.props index 839e980577cb53..29ede69058eb3c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -42,9 +42,9 @@ Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure they do not break the local dev experience. --> - 4.10.0-2.24112.13 - 4.10.0-2.24112.13 - 4.10.0-2.24112.13 + 4.10.0-2.24114.13 + 4.10.0-2.24114.13 + 4.10.0-2.24114.13 - true + leg only, so we also take DotNetBuildSourceOnly into account. --> + true - true + true @@ -94,7 +94,7 @@ net8.0 - $(NetCoreAppCurrent) + $(NetCoreAppCurrent) - - - + + + 8.0.0 @@ -176,7 +176,7 @@ $([MSBuild]::NormalizePath('$(MonoTargetsTasksDir)', 'MonoTargetsTasks.dll')) $([MSBuild]::NormalizePath('$(TestExclusionListTasksDir)', 'TestExclusionListTasks.dll')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'coreclr', '$(TargetOS).$(TargetArchitecture).$(RuntimeConfiguration)')) - $(CoreCLRToolPath) + $(CoreCLRToolPath) $(WASMTIME_PATH) $([MSBuild]::NormalizeDirectory($(ArtifactsObjDir), 'wasmtime')) true @@ -184,7 +184,7 @@ - false + false true @@ -298,8 +298,8 @@ - true - false + true + false true true @@ -380,7 +380,7 @@ portable true - true + true false Properties diff --git a/Directory.Build.targets b/Directory.Build.targets index 43564d362554b1..f731eedc390c36 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -21,7 +21,7 @@ When .NET gets built from source, make the SDK aware there are bootstrap packages for Microsoft.NETCore.App.Runtime. and Microsoft.NETCore.App.Crossgen2.. --> - + %(RuntimePackRuntimeIdentifiers);$(PackageRID) @@ -88,7 +88,7 @@ - $(SystemReflectionMetadataLoadContextVersion) + $(SystemReflectionMetadataLoadContextVersion) - false + false $(RunAnalyzers) diff --git a/eng/DiaSymReaderNative.targets b/eng/DiaSymReaderNative.targets index caa482f4b6e804..ac8dc7e36a06d5 100644 --- a/eng/DiaSymReaderNative.targets +++ b/eng/DiaSymReaderNative.targets @@ -18,7 +18,7 @@ package can't be referenced directly but rather has to have it's assets manually copied out. This logic is responsible for doing that. --> - + PreserveNewest false diff --git a/eng/DotNetBuild.props b/eng/DotNetBuild.props index fbad52bdac7d6b..8527ec175ad88b 100644 --- a/eng/DotNetBuild.props +++ b/eng/DotNetBuild.props @@ -38,9 +38,9 @@ - + $(InnerBuildArgs) $(FlagParameterPrefix)arch $(TargetArch) $(InnerBuildArgs) $(FlagParameterPrefix)os $(TargetOS) $(InnerBuildArgs) $(FlagParameterPrefix)cross @@ -63,8 +63,7 @@ $(InnerBuildArgs) $(FlagParameterPrefix)s clr.nativeaotlibs+clr.nativeaotruntime+libs+packs /p:BuildNativeAOTRuntimePack=true /p:SkipLibrariesNativeRuntimePackages=true - $(InnerBuildArgs) /p:ArcadeBuildFromSource=true - $(InnerBuildArgs) /p:ArcadeBuildVertical=true + $(InnerBuildArgs) /p:DotNetBuildRepo=true $(InnerBuildArgs) /p:OfficialBuildId=$(OfficialBuildId) $(InnerBuildArgs) /p:ContinuousIntegrationBuild=$(ContinuousIntegrationBuild) $(InnerBuildArgs) /p:PortableBuild=$(PortableBuild) diff --git a/eng/Subsets.props b/eng/Subsets.props index 9088b48f285de6..cd4532699b10e3 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -40,7 +40,7 @@ mono+libs+packs mono+libs+host+packs - clr+libs+tools+host+packs + clr+libs+tools+host+packs @@ -85,13 +85,13 @@ tools.illink host.native+host.tools+host.pkg - $(DefaultHostSubsets)+host.pretest+host.tests + $(DefaultHostSubsets)+host.pretest+host.tests host.native packs.product - $(DefaultPacksSubsets)+packs.tests - $(DefaultPacksSubsets)+packs.installers + $(DefaultPacksSubsets)+packs.tests + $(DefaultPacksSubsets)+packs.installers $(DefaultPacksSubsets)+mono.manifests @@ -341,7 +341,7 @@ $(CoreClrProjectRoot)tools\aot\ILCompiler\repro\repro.csproj; $(CoreClrProjectRoot)tools\r2rtest\R2RTest.csproj; $(CoreClrProjectRoot)tools\PdbChecker\PdbChecker.csproj; - $(CoreClrProjectRoot)tools\AssemblyChecker\AssemblyChecker.csproj" Category="clr" Condition="'$(DotNetBuildFromSource)' != 'true'"/> + $(CoreClrProjectRoot)tools\AssemblyChecker\AssemblyChecker.csproj" Category="clr" Condition="'$(DotNetBuildSourceOnly)' != 'true'"/> @@ -355,11 +355,11 @@ + Test="true" Category="clr" Condition="'$(DotNetBuildSourceOnly)' != 'true'"/> + Test="true" Category="clr" Condition="'$(DotNetBuildSourceOnly)' != 'true' and '$(NativeAotSupported)' == 'true'"/> + Test="true" Category="clr" Condition="'$(DotNetBuildSourceOnly)' != 'true' and '$(NativeAotSupported)' == 'true'"/> @@ -393,7 +393,7 @@ - + diff --git a/eng/Tools.props b/eng/Tools.props index 01cae1f2b23036..3baa40f4f32e04 100644 --- a/eng/Tools.props +++ b/eng/Tools.props @@ -11,7 +11,7 @@ - + diff --git a/eng/Versions.props b/eng/Versions.props index 29ede69058eb3c..ea03f538ca3c84 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -26,8 +26,8 @@ true true - - true + + true diff --git a/eng/codeOptimization.targets b/eng/codeOptimization.targets index d42a39e3dfd2ae..5f990a983476d7 100644 --- a/eng/codeOptimization.targets +++ b/eng/codeOptimization.targets @@ -30,7 +30,7 @@ - true + true diff --git a/eng/nativepgo.targets b/eng/nativepgo.targets index 9f5984efdb6332..99344e20b8e2ba 100644 --- a/eng/nativepgo.targets +++ b/eng/nativepgo.targets @@ -9,7 +9,7 @@ <_NativeOptimizationDataPackageTarget Condition="'$(TargetOS)' == 'windows'">windows_nt-$(TargetArchitecture.ToLower()) - + diff --git a/eng/packaging.targets b/eng/packaging.targets index fde0ccb624b89e..554e705e1887b7 100644 --- a/eng/packaging.targets +++ b/eng/packaging.targets @@ -4,12 +4,12 @@ true + '$(DotNetBuildSourceOnly)' != 'true'">true true + '$(DotNetBuildSourceOnly)' == 'true'">true $(ApiCompatNetCoreAppBaselineVersion) $(BeforePack);IncludeAnalyzersInPackage;AddNETStandardCompatErrorFileForPackaging @@ -39,7 +39,7 @@ true + '$(DotNetBuildSourceOnly)' == 'true'">true false @@ -307,7 +307,7 @@ diff --git a/eng/resolveContract.targets b/eng/resolveContract.targets index bd6fc7de7fc5c3..fb93fcd09e9a67 100644 --- a/eng/resolveContract.targets +++ b/eng/resolveContract.targets @@ -76,7 +76,7 @@ all the inputs available, some suppressions might only apply to one or the other and hence unnecessary suppressions can't be determined. Disable the validation under source build as that might use an out-of-date SDK and not the ApiCompat.Task package. --> - + true true @@ -137,7 +137,7 @@ Version="$(MicrosoftDotNetGenApiVersion)" PrivateAssets="all" IsImplicitlyDefined="true" - Condition="'$(DotNetBuildFromSource)' != 'true'" /> + Condition="'$(DotNetBuildSourceOnly)' != 'true'" /> diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 9db169fb6dae06..f240269e0ad7ff 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -16,7 +16,7 @@ false Debug;Release;Checked false - $(NoWarn);CS8524 + $(NoWarn);CS8524 @@ -24,7 +24,7 @@ - + all contentfiles diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj index 268e2e6b1248df..663dadfd936f53 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj @@ -9,7 +9,7 @@ x64;x86 AnyCPU false - true + true $(DefineConstants);DISABLE_UNMANAGED_PDB_SYMBOLS - false + false false false diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompiler.props b/src/coreclr/tools/aot/ILCompiler/ILCompiler.props index 6b419773911a8d..70994509d40075 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompiler.props +++ b/src/coreclr/tools/aot/ILCompiler/ILCompiler.props @@ -47,10 +47,10 @@ true - true + true - + diff --git a/src/coreclr/tools/r2rdump/R2RDump.csproj b/src/coreclr/tools/r2rdump/R2RDump.csproj index 103d8160d0b532..3f5a93c94fbeb9 100644 --- a/src/coreclr/tools/r2rdump/R2RDump.csproj +++ b/src/coreclr/tools/r2rdump/R2RDump.csproj @@ -15,7 +15,7 @@ false - + diff --git a/src/installer/pkg/projects/Directory.Build.props b/src/installer/pkg/projects/Directory.Build.props index 2917964afb8aa3..a07b360efc07a6 100644 --- a/src/installer/pkg/projects/Directory.Build.props +++ b/src/installer/pkg/projects/Directory.Build.props @@ -35,7 +35,7 @@ true - false + false dotnet-crossgen2 crossgen2 - linux-x64;linux-musl-x64;linux-arm;linux-musl-arm;linux-arm64;linux-musl-arm64;freebsd-x64;freebsd-arm64;osx-x64;osx-arm64;win-x64;win-x86;win-arm64 + linux-x64;linux-musl-x64;linux-arm;linux-musl-arm;linux-arm64;linux-musl-arm64;freebsd-x64;freebsd-arm64;osx-x64;osx-arm64;win-x64;win-x86;win-arm64 false tools/ true @@ -21,7 +21,7 @@ false - false + false diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj index 988b59bcecec60..1f6b55038026d4 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj @@ -19,7 +19,7 @@ true - true + true diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj index 085cade3966b96..28d8bcddd18515 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/Microsoft.Extensions.Logging.Abstractions.csproj @@ -44,7 +44,7 @@ Microsoft.Extensions.Logging.Abstractions.NullLogger + Condition="'$(DotNetBuildSourceOnly)' != 'true'" /> diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj index 958cf0e65df499..24aa038645aecc 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj @@ -23,7 +23,7 @@ - + diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index 40351864618018..16d83c57fe466d 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -161,7 +161,7 @@ + '$(DotNetBuildSourceOnly)' != 'true'"> + '$(DotNetBuildSourceOnly)' != 'true'"> - + diff --git a/src/libraries/oob-all.proj b/src/libraries/oob-all.proj index 07b063fab4094f..b8d20924522d7d 100644 --- a/src/libraries/oob-all.proj +++ b/src/libraries/oob-all.proj @@ -29,7 +29,7 @@ + Condition="'$(DotNetBuildSourceOnly)' == 'true'" /> diff --git a/src/libraries/oob-src.proj b/src/libraries/oob-src.proj index bb571ebc35f199..48bc4897a26790 100644 --- a/src/libraries/oob-src.proj +++ b/src/libraries/oob-src.proj @@ -37,7 +37,7 @@ + Condition="'$(DotNetBuildSourceOnly)' == 'true'" /> diff --git a/src/libraries/oob.proj b/src/libraries/oob.proj index 34823cd1ba4fe3..d49efa6e616815 100644 --- a/src/libraries/oob.proj +++ b/src/libraries/oob.proj @@ -13,7 +13,7 @@ + Condition="'$(DotNetBuildSourceOnly)' != 'true' and '$(ApiCompatValidateAssemblies)' != 'false'" /> diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index ec9b4a7706a62b..42afa2ece4fba1 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -99,8 +99,8 @@ - - + + diff --git a/src/mono/nuget/mono-packages.proj b/src/mono/nuget/mono-packages.proj index 025c6666db7121..10add92c17efcf 100644 --- a/src/mono/nuget/mono-packages.proj +++ b/src/mono/nuget/mono-packages.proj @@ -30,7 +30,7 @@ - + diff --git a/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj b/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj index 4e91a40280ca46..50249f07c44e07 100644 --- a/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj +++ b/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj @@ -2,7 +2,7 @@ $(NetCoreAppToolCurrent) - $(TargetFrameworks);net8.0 + $(TargetFrameworks);net8.0 enable $(NoWarn),CA1050,CA1850 diff --git a/src/tasks/tasks.proj b/src/tasks/tasks.proj index ab54991791ba7c..504f3caad09474 100644 --- a/src/tasks/tasks.proj +++ b/src/tasks/tasks.proj @@ -6,7 +6,7 @@ + Condition="'$(DotNetBuildSourceOnly)' == 'true'" /> true diff --git a/src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csproj b/src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csproj index 3771de91b840df..5dd13cb7772158 100644 --- a/src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csproj +++ b/src/tools/illink/src/ILLink.CodeFix/ILLink.CodeFixProvider.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj b/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj index 80fd6a902203c6..7db05c1cde9afe 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj @@ -7,7 +7,7 @@ false false Latest - $(NoWarn);CS8524 + $(NoWarn);CS8524 cs @@ -16,9 +16,9 @@ - - - + + + all contentfiles diff --git a/src/tools/illink/src/linker/Mono.Linker.csproj b/src/tools/illink/src/linker/Mono.Linker.csproj index e5cdbb86c5eb76..d1a4bdbf0570ca 100644 --- a/src/tools/illink/src/linker/Mono.Linker.csproj +++ b/src/tools/illink/src/linker/Mono.Linker.csproj @@ -15,7 +15,7 @@ true false - $(NoWarn);CS8524 + $(NoWarn);CS8524 $(MSBuildThisFileDirectory)ref\Mono.Linker.csproj Major false @@ -76,7 +76,7 @@ - + all contentfiles @@ -84,7 +84,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 3c0acaa8b27dfe99d218669081a537ad39d8f092 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 15 Feb 2024 12:56:02 -0500 Subject: [PATCH 062/158] Vectorize TensorPrimitives.Tan (#98299) * Vectorize TensorPrimitives.Tan * Address PR feedback * Temporarily disable vectorization for TanPi. --------- Co-authored-by: Eirik Tsarpalis --- .../netcore/TensorPrimitives.netcore.cs | 519 +++++++++++++++--- 1 file changed, 433 insertions(+), 86 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs index dec6446cd7653b..065c0f4fd08aaf 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs @@ -16068,18 +16068,21 @@ public static Vector512 Invoke(Vector512 x) public static Vector128 Invoke(Vector128 x) { - Vector128 uxMasked = Vector128.Abs(x).AsUInt32(); - if (Vector128.GreaterThanAny(uxMasked, Vector128.Create(MaxVectorizedValue))) + Vector128 uxMasked = Vector128.Abs(x); + if (Vector128.GreaterThanAny(uxMasked.AsUInt32(), Vector128.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector128 r = uxMasked.AsSingle(); Vector128 almHuge = Vector128.Create(AlmHuge); - Vector128 dn = ((r + Vector128.Create(float.Pi / 2)) * Vector128.Create(1 / float.Pi)) + almHuge; + Vector128 dn = MultiplyAddEstimateOperator.Invoke(uxMasked + Vector128.Create(float.Pi / 2), Vector128.Create(1 / float.Pi), almHuge); Vector128 odd = dn.AsUInt32() << 31; dn = dn - almHuge - Vector128.Create(0.5f); - Vector128 f = r + (dn * Vector128.Create(-float.Pi)) + (dn * Vector128.Create(Pi_Tail1)) + (dn * Vector128.Create(Pi_Tail2)); + + Vector128 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(-float.Pi), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(Pi_Tail1), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(Pi_Tail2), f); // POLY_EVAL_ODD_9 Vector128 f2 = f * f; @@ -16095,18 +16098,21 @@ public static Vector128 Invoke(Vector128 x) public static Vector256 Invoke(Vector256 x) { - Vector256 uxMasked = Vector256.Abs(x).AsUInt32(); - if (Vector256.GreaterThanAny(uxMasked, Vector256.Create(MaxVectorizedValue))) + Vector256 uxMasked = Vector256.Abs(x); + if (Vector256.GreaterThanAny(uxMasked.AsUInt32(), Vector256.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector256 r = uxMasked.AsSingle(); Vector256 almHuge = Vector256.Create(AlmHuge); - Vector256 dn = ((r + Vector256.Create(float.Pi / 2)) * Vector256.Create(1 / float.Pi)) + almHuge; + Vector256 dn = MultiplyAddEstimateOperator.Invoke(uxMasked + Vector256.Create(float.Pi / 2), Vector256.Create(1 / float.Pi), almHuge); Vector256 odd = dn.AsUInt32() << 31; dn = dn - almHuge - Vector256.Create(0.5f); - Vector256 f = r + (dn * Vector256.Create(-float.Pi)) + (dn * Vector256.Create(Pi_Tail1)) + (dn * Vector256.Create(Pi_Tail2)); + + Vector256 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(-float.Pi), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(Pi_Tail1), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(Pi_Tail2), f); // POLY_EVAL_ODD_9 Vector256 f2 = f * f; @@ -16122,18 +16128,21 @@ public static Vector256 Invoke(Vector256 x) public static Vector512 Invoke(Vector512 x) { - Vector512 uxMasked = Vector512.Abs(x).AsUInt32(); - if (Vector512.GreaterThanAny(uxMasked, Vector512.Create(MaxVectorizedValue))) + Vector512 uxMasked = Vector512.Abs(x); + if (Vector512.GreaterThanAny(uxMasked.AsUInt32(), Vector512.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector512 r = uxMasked.AsSingle(); Vector512 almHuge = Vector512.Create(AlmHuge); - Vector512 dn = ((r + Vector512.Create(float.Pi / 2)) * Vector512.Create(1 / float.Pi)) + almHuge; + Vector512 dn = MultiplyAddEstimateOperator.Invoke(uxMasked + Vector512.Create(float.Pi / 2), Vector512.Create(1 / float.Pi), almHuge); Vector512 odd = dn.AsUInt32() << 31; dn = dn - almHuge - Vector512.Create(0.5f); - Vector512 f = r + (dn * Vector512.Create(-float.Pi)) + (dn * Vector512.Create(Pi_Tail1)) + (dn * Vector512.Create(Pi_Tail2)); + + Vector512 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(-float.Pi), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(Pi_Tail1), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(Pi_Tail2), f); // POLY_EVAL_ODD_9 Vector512 f2 = f * f; @@ -16171,18 +16180,21 @@ public static Vector512 Invoke(Vector512 x) public static Vector128 Invoke(Vector128 x) { - Vector128 uxMasked = Vector128.Abs(x).AsUInt64(); - if (Vector128.GreaterThanAny(uxMasked, Vector128.Create(MaxVectorizedValue))) + Vector128 uxMasked = Vector128.Abs(x); + if (Vector128.GreaterThanAny(uxMasked.AsUInt64(), Vector128.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector128 r = uxMasked.AsDouble(); Vector128 almHuge = Vector128.Create(AlmHuge); - Vector128 dn = (r * Vector128.Create(1 / double.Pi)) + Vector128.Create(double.Pi / 2) + almHuge; + Vector128 dn = (uxMasked * Vector128.Create(1 / double.Pi)) + Vector128.Create(double.Pi / 2) + almHuge; Vector128 odd = dn.AsUInt64() << 63; dn = dn - almHuge - Vector128.Create(0.5); - Vector128 f = r + (dn * Vector128.Create(-double.Pi)) + (dn * Vector128.Create(Pi_Tail2)) + (dn * Vector128.Create(Pi_Tail3)); + + Vector128 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(-double.Pi), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(Pi_Tail2), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(Pi_Tail3), f); // POLY_EVAL_ODD_17 Vector128 f2 = f * f; @@ -16203,18 +16215,21 @@ public static Vector128 Invoke(Vector128 x) public static Vector256 Invoke(Vector256 x) { - Vector256 uxMasked = Vector256.Abs(x).AsUInt64(); - if (Vector256.GreaterThanAny(uxMasked, Vector256.Create(MaxVectorizedValue))) + Vector256 uxMasked = Vector256.Abs(x); + if (Vector256.GreaterThanAny(uxMasked.AsUInt64(), Vector256.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector256 r = uxMasked.AsDouble(); Vector256 almHuge = Vector256.Create(AlmHuge); - Vector256 dn = (r * Vector256.Create(1 / double.Pi)) + Vector256.Create(double.Pi / 2) + almHuge; + Vector256 dn = (uxMasked * Vector256.Create(1 / double.Pi)) + Vector256.Create(double.Pi / 2) + almHuge; Vector256 odd = dn.AsUInt64() << 63; dn = dn - almHuge - Vector256.Create(0.5); - Vector256 f = r + (dn * Vector256.Create(-double.Pi)) + (dn * Vector256.Create(Pi_Tail2)) + (dn * Vector256.Create(Pi_Tail3)); + + Vector256 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(-double.Pi), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(Pi_Tail2), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(Pi_Tail3), f); // POLY_EVAL_ODD_17 Vector256 f2 = f * f; @@ -16235,18 +16250,21 @@ public static Vector256 Invoke(Vector256 x) public static Vector512 Invoke(Vector512 x) { - Vector512 uxMasked = Vector512.Abs(x).AsUInt64(); - if (Vector512.GreaterThanAny(uxMasked, Vector512.Create(MaxVectorizedValue))) + Vector512 uxMasked = Vector512.Abs(x); + if (Vector512.GreaterThanAny(uxMasked.AsUInt64(), Vector512.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector512 r = uxMasked.AsDouble(); Vector512 almHuge = Vector512.Create(AlmHuge); - Vector512 dn = (r * Vector512.Create(1 / double.Pi)) + Vector512.Create(double.Pi / 2) + almHuge; + Vector512 dn = (uxMasked * Vector512.Create(1 / double.Pi)) + Vector512.Create(double.Pi / 2) + almHuge; Vector512 odd = dn.AsUInt64() << 63; dn = dn - almHuge - Vector512.Create(0.5); - Vector512 f = r + (dn * Vector512.Create(-double.Pi)) + (dn * Vector512.Create(Pi_Tail2)) + (dn * Vector512.Create(Pi_Tail3)); + + Vector512 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(-double.Pi), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(Pi_Tail2), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(Pi_Tail3), f); // POLY_EVAL_ODD_17 Vector512 f2 = f * f; @@ -16526,20 +16544,21 @@ public static Vector512 Invoke(Vector512 x) public static Vector128 Invoke(Vector128 x) { - Vector128 sign = x.AsUInt32() & Vector128.Create(~SignMask); - Vector128 uxMasked = Vector128.Abs(x).AsUInt32(); - - if (Vector128.GreaterThanAny(uxMasked, Vector128.Create(MaxVectorizedValue))) + Vector128 uxMasked = Vector128.Abs(x); + if (Vector128.GreaterThanAny(uxMasked.AsUInt32(), Vector128.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector128 r = uxMasked.AsSingle(); Vector128 almHuge = Vector128.Create(AlmHuge); - Vector128 dn = (r * Vector128.Create(1 / float.Pi)) + almHuge; + Vector128 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector128.Create(1 / float.Pi), almHuge); Vector128 odd = dn.AsUInt32() << 31; dn -= almHuge; - Vector128 f = r + (dn * Vector128.Create(-float.Pi)) + (dn * Vector128.Create(Pi_Tail1)) + (dn * Vector128.Create(Pi_Tail2)); + + Vector128 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(-float.Pi), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(Pi_Tail1), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(Pi_Tail2), f); // POLY_EVAL_ODD_9 Vector128 f2 = f * f; @@ -16550,25 +16569,26 @@ public static Vector128 Invoke(Vector128 x) Vector128 a3 = MultiplyAddEstimateOperator.Invoke(a2, f4, a1); Vector128 poly = f * a3; - return (poly.AsUInt32() ^ sign ^ odd).AsSingle(); + return (poly.AsUInt32() ^ (x.AsUInt32() & Vector128.Create(~SignMask)) ^ odd).AsSingle(); } public static Vector256 Invoke(Vector256 x) { - Vector256 sign = x.AsUInt32() & Vector256.Create(~SignMask); - Vector256 uxMasked = Vector256.Abs(x).AsUInt32(); - - if (Vector256.GreaterThanAny(uxMasked, Vector256.Create(MaxVectorizedValue))) + Vector256 uxMasked = Vector256.Abs(x); + if (Vector256.GreaterThanAny(uxMasked.AsUInt32(), Vector256.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector256 r = uxMasked.AsSingle(); Vector256 almHuge = Vector256.Create(AlmHuge); - Vector256 dn = (r * Vector256.Create(1 / float.Pi)) + almHuge; + Vector256 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector256.Create(1 / float.Pi), almHuge); Vector256 odd = dn.AsUInt32() << 31; dn -= almHuge; - Vector256 f = r + (dn * Vector256.Create(-float.Pi)) + (dn * Vector256.Create(Pi_Tail1)) + (dn * Vector256.Create(Pi_Tail2)); + + Vector256 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(-float.Pi), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(Pi_Tail1), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(Pi_Tail2), f); // POLY_EVAL_ODD_9 Vector256 f2 = f * f; @@ -16579,25 +16599,26 @@ public static Vector256 Invoke(Vector256 x) Vector256 a3 = MultiplyAddEstimateOperator.Invoke(a2, f4, a1); Vector256 poly = f * a3; - return (poly.AsUInt32() ^ sign ^ odd).AsSingle(); + return (poly.AsUInt32() ^ (x.AsUInt32() & Vector256.Create(~SignMask)) ^ odd).AsSingle(); } public static Vector512 Invoke(Vector512 x) { - Vector512 sign = x.AsUInt32() & Vector512.Create(~SignMask); - Vector512 uxMasked = Vector512.Abs(x).AsUInt32(); - - if (Vector512.GreaterThanAny(uxMasked, Vector512.Create(MaxVectorizedValue))) + Vector512 uxMasked = Vector512.Abs(x); + if (Vector512.GreaterThanAny(uxMasked.AsUInt32(), Vector512.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector512 r = uxMasked.AsSingle(); Vector512 almHuge = Vector512.Create(AlmHuge); - Vector512 dn = (r * Vector512.Create(1 / float.Pi)) + almHuge; + Vector512 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector512.Create(1 / float.Pi), almHuge); Vector512 odd = dn.AsUInt32() << 31; dn -= almHuge; - Vector512 f = r + (dn * Vector512.Create(-float.Pi)) + (dn * Vector512.Create(Pi_Tail1)) + (dn * Vector512.Create(Pi_Tail2)); + + Vector512 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(-float.Pi), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(Pi_Tail1), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(Pi_Tail2), f); // POLY_EVAL_ODD_9 Vector512 f2 = f * f; @@ -16608,7 +16629,7 @@ public static Vector512 Invoke(Vector512 x) Vector512 a3 = MultiplyAddEstimateOperator.Invoke(a2, f4, a1); Vector512 poly = f * a3; - return (poly.AsUInt32() ^ sign ^ odd).AsSingle(); + return (poly.AsUInt32() ^ (x.AsUInt32() & Vector512.Create(~SignMask)) ^ odd).AsSingle(); } } @@ -16635,20 +16656,17 @@ public static Vector512 Invoke(Vector512 x) public static Vector128 Invoke(Vector128 x) { - Vector128 sign = x.AsUInt64() & Vector128.Create(~SignMask); - Vector128 uxMasked = Vector128.Abs(x).AsUInt64(); - - if (Vector128.GreaterThanAny(uxMasked, Vector128.Create(MaxVectorizedValue))) + Vector128 uxMasked = Vector128.Abs(x); + if (Vector128.GreaterThanAny(uxMasked.AsUInt64(), Vector128.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector128 r = uxMasked.AsDouble(); Vector128 almHuge = Vector128.Create(AlmHuge); - Vector128 dn = (r * Vector128.Create(1 / double.Pi)) + almHuge; + Vector128 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector128.Create(1 / double.Pi), almHuge); Vector128 odd = dn.AsUInt64() << 63; dn -= almHuge; - Vector128 f = r - (dn * Vector128.Create(double.Pi)) - (dn * Vector128.Create(Pi_Tail1)) - (dn * Vector128.Create(Pi_Tail2)); + Vector128 f = uxMasked - (dn * Vector128.Create(double.Pi)) - (dn * Vector128.Create(Pi_Tail1)) - (dn * Vector128.Create(Pi_Tail2)); // POLY_EVAL_ODD_17 Vector128 f2 = f * f; @@ -16664,25 +16682,22 @@ public static Vector128 Invoke(Vector128 x) Vector128 b2 = MultiplyAddEstimateOperator.Invoke(f10, a3, f14 * a4); Vector128 poly = MultiplyAddEstimateOperator.Invoke(f, b1 + b2, f); - return (poly.AsUInt64() ^ sign ^ odd).AsDouble(); + return (poly.AsUInt64() ^ (x.AsUInt64() & Vector128.Create(~SignMask)) ^ odd).AsDouble(); } public static Vector256 Invoke(Vector256 x) { - Vector256 sign = x.AsUInt64() & Vector256.Create(~SignMask); - Vector256 uxMasked = Vector256.Abs(x).AsUInt64(); - - if (Vector256.GreaterThanAny(uxMasked, Vector256.Create(MaxVectorizedValue))) + Vector256 uxMasked = Vector256.Abs(x); + if (Vector256.GreaterThanAny(uxMasked.AsUInt64(), Vector256.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector256 r = uxMasked.AsDouble(); Vector256 almHuge = Vector256.Create(AlmHuge); - Vector256 dn = (r * Vector256.Create(1 / double.Pi)) + almHuge; + Vector256 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector256.Create(1 / double.Pi), almHuge); Vector256 odd = dn.AsUInt64() << 63; dn -= almHuge; - Vector256 f = r - (dn * Vector256.Create(double.Pi)) - (dn * Vector256.Create(Pi_Tail1)) - (dn * Vector256.Create(Pi_Tail2)); + Vector256 f = uxMasked - (dn * Vector256.Create(double.Pi)) - (dn * Vector256.Create(Pi_Tail1)) - (dn * Vector256.Create(Pi_Tail2)); // POLY_EVAL_ODD_17 Vector256 f2 = f * f; @@ -16698,25 +16713,22 @@ public static Vector256 Invoke(Vector256 x) Vector256 b2 = MultiplyAddEstimateOperator.Invoke(f10, a3, f14 * a4); Vector256 poly = MultiplyAddEstimateOperator.Invoke(f, b1 + b2, f); - return (poly.AsUInt64() ^ sign ^ odd).AsDouble(); + return (poly.AsUInt64() ^ (x.AsUInt64() & Vector256.Create(~SignMask)) ^ odd).AsDouble(); } public static Vector512 Invoke(Vector512 x) { - Vector512 sign = x.AsUInt64() & Vector512.Create(~SignMask); - Vector512 uxMasked = Vector512.Abs(x).AsUInt64(); - - if (Vector512.GreaterThanAny(uxMasked, Vector512.Create(MaxVectorizedValue))) + Vector512 uxMasked = Vector512.Abs(x); + if (Vector512.GreaterThanAny(uxMasked.AsUInt64(), Vector512.Create(MaxVectorizedValue))) { return ApplyScalar(x); } - Vector512 r = uxMasked.AsDouble(); Vector512 almHuge = Vector512.Create(AlmHuge); - Vector512 dn = (r * Vector512.Create(1 / double.Pi)) + almHuge; + Vector512 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector512.Create(1 / double.Pi), almHuge); Vector512 odd = dn.AsUInt64() << 63; dn -= almHuge; - Vector512 f = r - (dn * Vector512.Create(double.Pi)) - (dn * Vector512.Create(Pi_Tail1)) - (dn * Vector512.Create(Pi_Tail2)); + Vector512 f = uxMasked - (dn * Vector512.Create(double.Pi)) - (dn * Vector512.Create(Pi_Tail1)) - (dn * Vector512.Create(Pi_Tail2)); // POLY_EVAL_ODD_17 Vector512 f2 = f * f; @@ -16732,7 +16744,7 @@ public static Vector512 Invoke(Vector512 x) Vector512 b2 = MultiplyAddEstimateOperator.Invoke(f10, a3, f14 * a4); Vector512 poly = MultiplyAddEstimateOperator.Invoke(f, b1 + b2, f); - return (poly.AsUInt64() ^ sign ^ odd).AsDouble(); + return (poly.AsUInt64() ^ (x.AsUInt64() & Vector512.Create(~SignMask)) ^ odd).AsDouble(); } } @@ -16910,22 +16922,357 @@ public static Vector512 Invoke(Vector512 t) internal readonly struct TanOperator : IUnaryOperator where T : ITrigonometricFunctions { - public static bool Vectorizable => false; // TODO: Vectorize + // This code is based on `vrs4_tan` and `vrd2_tan` from amd/aocl-libm-ose + // Copyright (C) 2019-2020 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + // Implementation notes from amd/aocl-libm-ose: + // -------------------------------------------- + // A given x is reduced into the form: + // |x| = (N * π/2) + F + // Where N is an integer obtained using: + // N = round(x * 2/π) + // And F is a fraction part lying in the interval + // [-π/4, +π/4]; + // obtained as F = |x| - (N * π/2) + // Thus tan(x) is given by + // tan(x) = tan((N * π/2) + F) = tan(F) + // when N is even, = -cot(F) = -1/tan(F) + // when N is odd, tan(F) is approximated using a polynomial + // obtained from Remez approximation from Sollya. + + public static bool Vectorizable => typeof(T) == typeof(float) || typeof(T) == typeof(double); + public static T Invoke(T x) => T.Tan(x); - public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); - public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); - public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + + public static Vector128 Invoke(Vector128 x) + { + if (typeof(T) == typeof(float)) + { + return TanOperatorSingle.Invoke(x.AsSingle()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(double)); + return TanOperatorDouble.Invoke(x.AsDouble()).As(); + } + } + + public static Vector256 Invoke(Vector256 x) + { + if (typeof(T) == typeof(float)) + { + return TanOperatorSingle.Invoke(x.AsSingle()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(double)); + return TanOperatorDouble.Invoke(x.AsDouble()).As(); + } + } + + public static Vector512 Invoke(Vector512 x) + { + if (typeof(T) == typeof(float)) + { + return TanOperatorSingle.Invoke(x.AsSingle()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(double)); + return TanOperatorDouble.Invoke(x.AsDouble()).As(); + } + } + } + + /// float.Tan(x) + internal readonly struct TanOperatorSingle : IUnaryOperator + { + internal const uint SignMask = 0x7FFFFFFFu; + internal const uint MaxVectorizedValue = 0x49800000u; + private const float AlmHuge = 1.2582912e7f; + private const float Pi_Tail2 = 4.371139e-8f; + private const float Pi_Tail3 = 1.7151245e-15f; + private const float C1 = 0.33333358f; + private const float C2 = 0.13332522f; + private const float C3 = 0.05407107f; + private const float C4 = 0.021237267f; + private const float C5 = 0.010932301f; + private const float C6 = -1.5722344e-5f; + private const float C7 = 0.0044221194f; + + public static bool Vectorizable => true; + + public static float Invoke(float x) => float.Tan(x); + + public static Vector128 Invoke(Vector128 x) + { + Vector128 uxMasked = Vector128.Abs(x); + if (Vector128.GreaterThanAny(uxMasked.AsUInt32(), Vector128.Create(MaxVectorizedValue))) + { + return ApplyScalar(x); + } + + Vector128 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector128.Create(2 / float.Pi), Vector128.Create(AlmHuge)); + Vector128 odd = dn.AsUInt32() << 31; + dn -= Vector128.Create(AlmHuge); + + Vector128 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(-float.Pi / 2), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(Pi_Tail2), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector128.Create(Pi_Tail3), f); + + // POLY_EVAL_ODD_15 + Vector128 f2 = f * f; + Vector128 f4 = f2 * f2; + Vector128 f8 = f4 * f4; + Vector128 f12 = f8 * f4; + Vector128 a1 = MultiplyAddEstimateOperator.Invoke(Vector128.Create(C2), f2, Vector128.Create(C1)); + Vector128 a2 = MultiplyAddEstimateOperator.Invoke(Vector128.Create(C4), f2, Vector128.Create(C3)); + Vector128 a3 = MultiplyAddEstimateOperator.Invoke(Vector128.Create(C6), f2, Vector128.Create(C5)); + Vector128 b1 = MultiplyAddEstimateOperator.Invoke(a2, f4, a1); + Vector128 b2 = MultiplyAddEstimateOperator.Invoke(f8, a3, f12 * Vector128.Create(C7)); + Vector128 poly = MultiplyAddEstimateOperator.Invoke(f * f2, b1 + b2, f); + + Vector128 result = (poly.AsUInt32() ^ (x.AsUInt32() & Vector128.Create(~SignMask))).AsSingle(); + return Vector128.ConditionalSelect(Vector128.Equals(odd, Vector128.Zero).AsSingle(), + result, + Vector128.Create(-1f) / result); + } + + public static Vector256 Invoke(Vector256 x) + { + Vector256 uxMasked = Vector256.Abs(x); + if (Vector256.GreaterThanAny(uxMasked.AsUInt32(), Vector256.Create(MaxVectorizedValue))) + { + return ApplyScalar(x); + } + + Vector256 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector256.Create(2 / float.Pi), Vector256.Create(AlmHuge)); + Vector256 odd = dn.AsUInt32() << 31; + dn -= Vector256.Create(AlmHuge); + + Vector256 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(-float.Pi / 2), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(Pi_Tail2), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector256.Create(Pi_Tail3), f); + + // POLY_EVAL_ODD_15 + Vector256 f2 = f * f; + Vector256 f4 = f2 * f2; + Vector256 f8 = f4 * f4; + Vector256 f12 = f8 * f4; + Vector256 a1 = MultiplyAddEstimateOperator.Invoke(Vector256.Create(C2), f2, Vector256.Create(C1)); + Vector256 a2 = MultiplyAddEstimateOperator.Invoke(Vector256.Create(C4), f2, Vector256.Create(C3)); + Vector256 a3 = MultiplyAddEstimateOperator.Invoke(Vector256.Create(C6), f2, Vector256.Create(C5)); + Vector256 b1 = MultiplyAddEstimateOperator.Invoke(a2, f4, a1); + Vector256 b2 = MultiplyAddEstimateOperator.Invoke(f8, a3, f12 * Vector256.Create(C7)); + Vector256 poly = MultiplyAddEstimateOperator.Invoke(f * f2, b1 + b2, f); + + Vector256 result = (poly.AsUInt32() ^ (x.AsUInt32() & Vector256.Create(~SignMask))).AsSingle(); + return Vector256.ConditionalSelect(Vector256.Equals(odd, Vector256.Zero).AsSingle(), + result, + Vector256.Create(-1f) / result); + } + + public static Vector512 Invoke(Vector512 x) + { + Vector512 uxMasked = Vector512.Abs(x); + if (Vector512.GreaterThanAny(uxMasked.AsUInt32(), Vector512.Create(MaxVectorizedValue))) + { + return ApplyScalar(x); + } + + Vector512 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector512.Create(2 / float.Pi), Vector512.Create(AlmHuge)); + Vector512 odd = dn.AsUInt32() << 31; + dn -= Vector512.Create(AlmHuge); + + Vector512 f = uxMasked; + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(-float.Pi / 2), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(Pi_Tail2), f); + f = MultiplyAddEstimateOperator.Invoke(dn, Vector512.Create(Pi_Tail3), f); + + // POLY_EVAL_ODD_15 + Vector512 f2 = f * f; + Vector512 f4 = f2 * f2; + Vector512 f8 = f4 * f4; + Vector512 f12 = f8 * f4; + Vector512 a1 = MultiplyAddEstimateOperator.Invoke(Vector512.Create(C2), f2, Vector512.Create(C1)); + Vector512 a2 = MultiplyAddEstimateOperator.Invoke(Vector512.Create(C4), f2, Vector512.Create(C3)); + Vector512 a3 = MultiplyAddEstimateOperator.Invoke(Vector512.Create(C6), f2, Vector512.Create(C5)); + Vector512 b1 = MultiplyAddEstimateOperator.Invoke(a2, f4, a1); + Vector512 b2 = MultiplyAddEstimateOperator.Invoke(f8, a3, f12 * Vector512.Create(C7)); + Vector512 poly = MultiplyAddEstimateOperator.Invoke(f * f2, b1 + b2, f); + + Vector512 result = (poly.AsUInt32() ^ (x.AsUInt32() & Vector512.Create(~SignMask))).AsSingle(); + return Vector512.ConditionalSelect(Vector512.Equals(odd, Vector512.Zero).AsSingle(), + result, + Vector512.Create(-1f) / result); + } + } + + /// double.Tan(x) + internal readonly struct TanOperatorDouble : IUnaryOperator + { + internal const ulong SignMask = 0x7FFFFFFFFFFFFFFFul; + internal const ulong MaxVectorizedValue = 0x4160000000000000ul; + private const double AlmHuge = 6.755399441055744e15; + private const double HalfPi2 = 6.123233995736766E-17; + private const double HalfPi3 = -1.4973849048591698E-33; + private const double C1 = 0.33333333333332493; + private const double C3 = 0.133333333334343; + private const double C5 = 0.0539682539203796; + private const double C7 = 0.02186948972198256; + private const double C9 = 0.008863217894198291; + private const double C11 = 0.003592298593761111; + private const double C13 = 0.0014547086183165365; + private const double C15 = 5.952456856028558E-4; + private const double C17 = 2.2190741289936845E-4; + private const double C19 = 1.3739809957985104E-4; + private const double C21 = -2.7500197359895707E-5; + private const double C23 = 9.038741690184683E-5; + private const double C25 = -4.534076545538694E-5; + private const double C27 = 2.0966522562190197E-5; + + public static bool Vectorizable => true; + + public static double Invoke(double x) => double.Tan(x); + + public static Vector128 Invoke(Vector128 x) + { + Vector128 uxMasked = Vector128.Abs(x); + if (Vector128.GreaterThanAny(uxMasked.AsUInt64(), Vector128.Create(MaxVectorizedValue))) + { + return ApplyScalar(x); + } + + Vector128 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector128.Create(2 / double.Pi), Vector128.Create(AlmHuge)); + Vector128 odd = dn.AsUInt64() << 63; + dn -= Vector128.Create(AlmHuge); + Vector128 f = uxMasked.AsDouble() - (dn * (double.Pi / 2)) - (dn * HalfPi2) - (dn * HalfPi3); + + // POLY_EVAL_ODD_29 + Vector128 g = f * f; + Vector128 g2 = g * g; + Vector128 g3 = g * g2; + Vector128 g5 = g3 * g2; + Vector128 g7 = g5 * g2; + Vector128 g9 = g7 * g2; + Vector128 g11 = g9 * g2; + Vector128 g13 = g11 * g2; + Vector128 a1 = MultiplyAddEstimateOperator.Invoke(Vector128.Create(C3), g, Vector128.Create(C1)); + Vector128 a2 = MultiplyAddEstimateOperator.Invoke(Vector128.Create(C7), g, Vector128.Create(C5)); + Vector128 a3 = MultiplyAddEstimateOperator.Invoke(Vector128.Create(C11), g, Vector128.Create(C9)); + Vector128 a4 = MultiplyAddEstimateOperator.Invoke(Vector128.Create(C15), g, Vector128.Create(C13)); + Vector128 a5 = MultiplyAddEstimateOperator.Invoke(Vector128.Create(C19), g, Vector128.Create(C17)); + Vector128 a6 = MultiplyAddEstimateOperator.Invoke(Vector128.Create(C23), g, Vector128.Create(C21)); + Vector128 a7 = MultiplyAddEstimateOperator.Invoke(Vector128.Create(C27), g, Vector128.Create(C25)); + Vector128 b1 = MultiplyAddEstimateOperator.Invoke(g, a1, g3 * a2); + Vector128 b2 = MultiplyAddEstimateOperator.Invoke(g5, a3, g7 * a4); + Vector128 b3 = MultiplyAddEstimateOperator.Invoke(g9, a5, g11 * a6); + Vector128 q = MultiplyAddEstimateOperator.Invoke(g13, a7, b1 + b2 + b3); + Vector128 poly = MultiplyAddEstimateOperator.Invoke(f, q, f); + + Vector128 result = (poly.AsUInt64() ^ (x.AsUInt64() & Vector128.Create(~SignMask))).AsDouble(); + return Vector128.ConditionalSelect(Vector128.Equals(odd, Vector128.Zero).AsDouble(), + result, + Vector128.Create(-1.0) / result); + } + + public static Vector256 Invoke(Vector256 x) + { + Vector256 uxMasked = Vector256.Abs(x); + if (Vector256.GreaterThanAny(uxMasked.AsUInt64(), Vector256.Create(MaxVectorizedValue))) + { + return ApplyScalar(x); + } + + Vector256 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector256.Create(2 / double.Pi), Vector256.Create(AlmHuge)); + Vector256 odd = dn.AsUInt64() << 63; + dn -= Vector256.Create(AlmHuge); + Vector256 f = uxMasked.AsDouble() - (dn * (double.Pi / 2)) - (dn * HalfPi2) - (dn * HalfPi3); + + // POLY_EVAL_ODD_29 + Vector256 g = f * f; + Vector256 g2 = g * g; + Vector256 g3 = g * g2; + Vector256 g5 = g3 * g2; + Vector256 g7 = g5 * g2; + Vector256 g9 = g7 * g2; + Vector256 g11 = g9 * g2; + Vector256 g13 = g11 * g2; + Vector256 a1 = MultiplyAddEstimateOperator.Invoke(Vector256.Create(C3), g, Vector256.Create(C1)); + Vector256 a2 = MultiplyAddEstimateOperator.Invoke(Vector256.Create(C7), g, Vector256.Create(C5)); + Vector256 a3 = MultiplyAddEstimateOperator.Invoke(Vector256.Create(C11), g, Vector256.Create(C9)); + Vector256 a4 = MultiplyAddEstimateOperator.Invoke(Vector256.Create(C15), g, Vector256.Create(C13)); + Vector256 a5 = MultiplyAddEstimateOperator.Invoke(Vector256.Create(C19), g, Vector256.Create(C17)); + Vector256 a6 = MultiplyAddEstimateOperator.Invoke(Vector256.Create(C23), g, Vector256.Create(C21)); + Vector256 a7 = MultiplyAddEstimateOperator.Invoke(Vector256.Create(C27), g, Vector256.Create(C25)); + Vector256 b1 = MultiplyAddEstimateOperator.Invoke(g, a1, g3 * a2); + Vector256 b2 = MultiplyAddEstimateOperator.Invoke(g5, a3, g7 * a4); + Vector256 b3 = MultiplyAddEstimateOperator.Invoke(g9, a5, g11 * a6); + Vector256 q = MultiplyAddEstimateOperator.Invoke(g13, a7, b1 + b2 + b3); + Vector256 poly = MultiplyAddEstimateOperator.Invoke(f, q, f); + + Vector256 result = (poly.AsUInt64() ^ (x.AsUInt64() & Vector256.Create(~SignMask))).AsDouble(); + return Vector256.ConditionalSelect(Vector256.Equals(odd, Vector256.Zero).AsDouble(), + result, + Vector256.Create(-1.0) / result); + } + + public static Vector512 Invoke(Vector512 x) + { + Vector512 uxMasked = Vector512.Abs(x); + if (Vector512.GreaterThanAny(uxMasked.AsUInt64(), Vector512.Create(MaxVectorizedValue))) + { + return ApplyScalar(x); + } + + Vector512 dn = MultiplyAddEstimateOperator.Invoke(uxMasked, Vector512.Create(2 / double.Pi), Vector512.Create(AlmHuge)); + Vector512 odd = dn.AsUInt64() << 63; + dn -= Vector512.Create(AlmHuge); + Vector512 f = uxMasked.AsDouble() - (dn * (double.Pi / 2)) - (dn * HalfPi2) - (dn * HalfPi3); + + // POLY_EVAL_ODD_29 + Vector512 g = f * f; + Vector512 g2 = g * g; + Vector512 g3 = g * g2; + Vector512 g5 = g3 * g2; + Vector512 g7 = g5 * g2; + Vector512 g9 = g7 * g2; + Vector512 g11 = g9 * g2; + Vector512 g13 = g11 * g2; + Vector512 a1 = MultiplyAddEstimateOperator.Invoke(Vector512.Create(C3), g, Vector512.Create(C1)); + Vector512 a2 = MultiplyAddEstimateOperator.Invoke(Vector512.Create(C7), g, Vector512.Create(C5)); + Vector512 a3 = MultiplyAddEstimateOperator.Invoke(Vector512.Create(C11), g, Vector512.Create(C9)); + Vector512 a4 = MultiplyAddEstimateOperator.Invoke(Vector512.Create(C15), g, Vector512.Create(C13)); + Vector512 a5 = MultiplyAddEstimateOperator.Invoke(Vector512.Create(C19), g, Vector512.Create(C17)); + Vector512 a6 = MultiplyAddEstimateOperator.Invoke(Vector512.Create(C23), g, Vector512.Create(C21)); + Vector512 a7 = MultiplyAddEstimateOperator.Invoke(Vector512.Create(C27), g, Vector512.Create(C25)); + Vector512 b1 = MultiplyAddEstimateOperator.Invoke(g, a1, g3 * a2); + Vector512 b2 = MultiplyAddEstimateOperator.Invoke(g5, a3, g7 * a4); + Vector512 b3 = MultiplyAddEstimateOperator.Invoke(g9, a5, g11 * a6); + Vector512 q = MultiplyAddEstimateOperator.Invoke(g13, a7, b1 + b2 + b3); + Vector512 poly = MultiplyAddEstimateOperator.Invoke(f, q, f); + + Vector512 result = (poly.AsUInt64() ^ (x.AsUInt64() & Vector512.Create(~SignMask))).AsDouble(); + return Vector512.ConditionalSelect(Vector512.Equals(odd, Vector512.Zero).AsDouble(), + result, + Vector512.Create(-1.0) / result); + } } /// T.TanPi(x) internal readonly struct TanPiOperator : IUnaryOperator where T : ITrigonometricFunctions { - public static bool Vectorizable => TanOperator.Vectorizable; + public static bool Vectorizable => false; public static T Invoke(T x) => T.TanPi(x); - public static Vector128 Invoke(Vector128 x) => TanOperator.Invoke(x * Vector128.Create(T.Pi)); - public static Vector256 Invoke(Vector256 x) => TanOperator.Invoke(x * Vector256.Create(T.Pi)); - public static Vector512 Invoke(Vector512 x) => TanOperator.Invoke(x * Vector512.Create(T.Pi)); + public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); + public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); + public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); } /// T.Tanh(x) From f0c06c578b64fbacc69992f633b9cc28cc0e877f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 11:57:09 -0600 Subject: [PATCH 063/158] [main] Update dependencies from dotnet/cecil, dotnet/hotreload-utils, dotnet/icu, dotnet/xharness (#98351) * Update dependencies from https://github.com/dotnet/icu build 20240212.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-preview.2.24105.1 -> To Version 9.0.0-preview.2.24112.1 * Update dependencies from https://github.com/dotnet/xharness build 20240212.4 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.24112.1 -> To Version 9.0.0-prerelease.24112.4 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240212.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.24105.1 -> To Version 9.0.0-alpha.0.24112.1 * Update dependencies from https://github.com/dotnet/cecil build 20240212.1 Microsoft.SourceBuild.Intermediate.cecil , Microsoft.DotNet.Cecil From Version 0.11.4-alpha.24105.1 -> To Version 0.11.4-alpha.24112.1 * Update dependencies from https://github.com/dotnet/icu build 20240212.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-preview.2.24105.1 -> To Version 9.0.0-preview.2.24112.1 * Update dependencies from https://github.com/dotnet/xharness build 20240212.4 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.24112.1 -> To Version 9.0.0-prerelease.24112.4 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240212.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.24105.1 -> To Version 9.0.0-alpha.0.24112.1 * Update dependencies from https://github.com/dotnet/cecil build 20240212.1 Microsoft.SourceBuild.Intermediate.cecil , Microsoft.DotNet.Cecil From Version 0.11.4-alpha.24105.1 -> To Version 0.11.4-alpha.24112.1 * Update dependencies from https://github.com/dotnet/icu build 20240212.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-preview.2.24105.1 -> To Version 9.0.0-preview.2.24112.1 * Update dependencies from https://github.com/dotnet/xharness build 20240212.4 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.24112.1 -> To Version 9.0.0-prerelease.24112.4 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240212.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.24105.1 -> To Version 9.0.0-alpha.0.24112.1 * Update dependencies from https://github.com/dotnet/cecil build 20240212.1 Microsoft.SourceBuild.Intermediate.cecil , Microsoft.DotNet.Cecil From Version 0.11.4-alpha.24105.1 -> To Version 0.11.4-alpha.24112.1 --------- Co-authored-by: dotnet-maestro[bot] --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 28 ++++++++++++++-------------- eng/Versions.props | 12 ++++++------ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 9413c0e0fbbbab..2663dda99de3fe 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "9.0.0-prerelease.24112.1", + "version": "9.0.0-prerelease.24112.4", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 0bc2d69f0e0856..509075938ac11c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/icu - 0664e5d5a59ae8ad8fe0cc1e82ed8a32baac43f7 + 1b9c03a1103cf622ee2f7850d16aa1095a719e56 https://github.com/dotnet/msquic @@ -90,14 +90,14 @@ a045dd54a4c44723c215d992288160eb1401bb7f - + https://github.com/dotnet/cecil - 93dcb576e191a965008eae9b622527436653873f + ca7e93445acbd94bfa696c16fa039f2a6130f2cb - + https://github.com/dotnet/cecil - 93dcb576e191a965008eae9b622527436653873f + ca7e93445acbd94bfa696c16fa039f2a6130f2cb @@ -352,17 +352,17 @@ https://github.com/dotnet/runtime 9699f39112b2aea89a05a74199baf9049db85537 - + https://github.com/dotnet/xharness - 2f3f51a1adec18475563c8a49fd9b668ae9f2f31 + 11ae3663fe3de366ea3566d7ae9b4731adee2ca3 - + https://github.com/dotnet/xharness - 2f3f51a1adec18475563c8a49fd9b668ae9f2f31 + 11ae3663fe3de366ea3566d7ae9b4731adee2ca3 - + https://github.com/dotnet/xharness - 2f3f51a1adec18475563c8a49fd9b668ae9f2f31 + 11ae3663fe3de366ea3566d7ae9b4731adee2ca3 https://github.com/dotnet/arcade @@ -384,9 +384,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization db9f1c2362565f3ef41c8e8feb5ed49ab11a6459 - + https://github.com/dotnet/hotreload-utils - c3893319be301c7acdd167c276a5178c1004f10f + 465874b5842702bf69bbb6bacd94a52d8ea2a073 https://github.com/dotnet/runtime-assets diff --git a/eng/Versions.props b/eng/Versions.props index ea03f538ca3c84..5c4fb67c97abfc 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -187,10 +187,10 @@ 1.4.0 17.4.0-preview-20220707-01 - 9.0.0-prerelease.24112.1 - 9.0.0-prerelease.24112.1 - 9.0.0-prerelease.24112.1 - 9.0.0-alpha.0.24105.1 + 9.0.0-prerelease.24112.4 + 9.0.0-prerelease.24112.4 + 9.0.0-prerelease.24112.4 + 9.0.0-alpha.0.24112.1 3.12.0 4.5.0 6.0.0 @@ -216,11 +216,11 @@ 8.0.0-preview-20230918.1 - 0.11.4-alpha.24105.1 + 0.11.4-alpha.24112.1 9.0.0-preview.2.24111.9 - 9.0.0-preview.2.24105.1 + 9.0.0-preview.2.24112.1 2.2.3 9.0.0-alpha.1.24067.1 From 3848f566d5e326e3b094ccb92f0a91069cd41058 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Thu, 15 Feb 2024 12:08:20 -0600 Subject: [PATCH 064/158] Require the workload for wasm threading (#98498) --- .../WorkloadManifest.targets.in | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in index 86b8c527efa08c..8d15ecdc96dd0e 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Current.Manifest/WorkloadManifest.targets.in @@ -54,6 +54,7 @@ <_WasmNativeWorkloadNeeded Condition=" + '$(WasmEnableThreads)' == 'true' or '$(_WasmPropertiesDifferFromRuntimePackThusNativeBuildNeeded)' == 'true' or '$(RunAOTCompilation)' == 'true' or '$(WasmBuildNative)' == 'true' or From bbb97a76942d9ba95e20e7006b21325266a1baa1 Mon Sep 17 00:00:00 2001 From: Andrew J Said Date: Thu, 15 Feb 2024 19:28:22 +0000 Subject: [PATCH 065/158] Reintroduce case sensitive comparison optimization for FrozenDictionary in some cases (#95232) * Reintroduce case sensitive comparison optimization for FrozenDictionary in some cases * Renamed a few symbols and added comments for additional clarity --------- Co-authored-by: Stephen Toub --- .../Collections/Frozen/String/KeyAnalyzer.cs | 50 ++++++++++++------- .../tests/Frozen/KeyAnalyzerTests.cs | 13 +++-- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/KeyAnalyzer.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/KeyAnalyzer.cs index ddea9bfbe76999..9f6094edb97763 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/KeyAnalyzer.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/KeyAnalyzer.cs @@ -37,7 +37,7 @@ public static AnalysisResults Analyze( AnalysisResults results; if (minLength == 0 || !TryUseSubstring(uniqueStrings, ignoreCase, minLength, maxLength, out results)) { - results = CreateAnalysisResults(uniqueStrings, ignoreCase, minLength, maxLength, 0, 0, isSubstring: false, static (s, _, _) => s.AsSpan()); + results = CreateAnalysisResults(uniqueStrings, ignoreCase, minLength, maxLength, 0, 0, static (s, _, _) => s.AsSpan()); } return results; @@ -77,7 +77,7 @@ private static bool TryUseSubstring(ReadOnlySpan uniqueStrings, bool ign if (HasSufficientUniquenessFactor(set, uniqueStrings, acceptableNonUniqueCount)) { results = CreateAnalysisResults( - uniqueStrings, ignoreCase, minLength, maxLength, index, count, isSubstring: true, + uniqueStrings, ignoreCase, minLength, maxLength, index, count, static (string s, int index, int count) => s.AsSpan(index, count)); return true; } @@ -101,7 +101,7 @@ private static bool TryUseSubstring(ReadOnlySpan uniqueStrings, bool ign if (HasSufficientUniquenessFactor(set, uniqueStrings, acceptableNonUniqueCount)) { results = CreateAnalysisResults( - uniqueStrings, ignoreCase, minLength, maxLength, comparer.Index, count, isSubstring: true, + uniqueStrings, ignoreCase, minLength, maxLength, comparer.Index, count, static (string s, int index, int count) => s.AsSpan(s.Length + index, count)); return true; } @@ -115,7 +115,7 @@ private static bool TryUseSubstring(ReadOnlySpan uniqueStrings, bool ign } private static AnalysisResults CreateAnalysisResults( - ReadOnlySpan uniqueStrings, bool ignoreCase, int minLength, int maxLength, int index, int count, bool isSubstring, GetSpan getSubstringSpan) + ReadOnlySpan uniqueStrings, bool ignoreCase, int minLength, int maxLength, int index, int count, GetSpan getHashString) { // Start off by assuming all strings are ASCII bool allAsciiIfIgnoreCase = true; @@ -125,30 +125,42 @@ private static AnalysisResults CreateAnalysisResults( // substrings are ASCII, so we check each. if (ignoreCase) { - // Further, if the ASCII keys (in their entirety) don't contain any letters, then we can - // actually perform the comparison as case-sensitive even if case-insensitive - // was requested, as there's nothing that would compare equally to the substring - // other than the substring itself. - bool canSwitchIgnoreCaseHashToCaseSensitive = !isSubstring; + // Further, if the ASCII keys (in their entirety) don't contain any letters, + // then we can actually perform the comparison as case-sensitive even if + // case-insensitive was requested, as there's nothing that would compare + // equally to the key other than the key itself. + bool canSwitchIgnoreCaseHashToCaseSensitive = true; - foreach (string s in uniqueStrings) + foreach (string uniqueString in uniqueStrings) { - // Get the span for the substring. - ReadOnlySpan substring = getSubstringSpan(s, index, count); + // Get a span representing the slice of the uniqueString which will be hashed. + ReadOnlySpan hashString = getHashString(uniqueString, index, count); - // If the substring isn't ASCII, bail out to return the results. - if (!IsAllAscii(substring)) + // If the slice isn't ASCII, bail out to return the results. + if (!IsAllAscii(hashString)) { allAsciiIfIgnoreCase = false; canSwitchIgnoreCaseHashToCaseSensitive = false; break; } - // All substrings so far are still ASCII only. If this one contains any ASCII - // letters, mark that we can't switch to case-sensitive. - if (canSwitchIgnoreCaseHashToCaseSensitive && ContainsAnyLetters(substring)) + // The hash string is ASCII only. We disable the switch to + // case sensitive if by examining the entire uniqueString we + // find that it is not ASCII, or that it contains ASCII letters. + if (canSwitchIgnoreCaseHashToCaseSensitive) { - canSwitchIgnoreCaseHashToCaseSensitive = false; + // If count is 0 then uniqueString equals hashString, + // and as we have just checked that IsAllAscii(hashString) is true + // then we know IsAllAscii(uniqueString) must be true, + // so we can skip the check. + if (count > 0 && !IsAllAscii(uniqueString.AsSpan())) + { + canSwitchIgnoreCaseHashToCaseSensitive = false; + } + else if (ContainsAnyAsciiLetters(uniqueString.AsSpan())) + { + canSwitchIgnoreCaseHashToCaseSensitive = false; + } } } @@ -207,7 +219,7 @@ internal static unsafe bool IsAllAscii(ReadOnlySpan s) #if NET8_0_OR_GREATER private static readonly SearchValues s_asciiLetters = SearchValues.Create("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); #endif - internal static bool ContainsAnyLetters(ReadOnlySpan s) + internal static bool ContainsAnyAsciiLetters(ReadOnlySpan s) { Debug.Assert(IsAllAscii(s)); diff --git a/src/libraries/System.Collections.Immutable/tests/Frozen/KeyAnalyzerTests.cs b/src/libraries/System.Collections.Immutable/tests/Frozen/KeyAnalyzerTests.cs index 31f6007b72442a..00a16bf3cef54d 100644 --- a/src/libraries/System.Collections.Immutable/tests/Frozen/KeyAnalyzerTests.cs +++ b/src/libraries/System.Collections.Immutable/tests/Frozen/KeyAnalyzerTests.cs @@ -107,6 +107,13 @@ public static void LeftHandCaseInsensitive() Assert.Equal(0, r.HashIndex); Assert.Equal(1, r.HashCount); + r = RunAnalysis(new[] { "0001", "0002", "0003", "0004", "0005", "0006" }, true); + Assert.False(r.RightJustifiedSubstring); + Assert.False(r.IgnoreCase); + Assert.True(r.AllAsciiIfIgnoreCase); + Assert.Equal(3, r.HashIndex); + Assert.Equal(1, r.HashCount); + } [Fact] @@ -226,9 +233,9 @@ public static void IsAllAscii() [Fact] public static void ContainsAnyLetters() { - Assert.True(KeyAnalyzer.ContainsAnyLetters("abc".AsSpan())); - Assert.True(KeyAnalyzer.ContainsAnyLetters("ABC".AsSpan())); - Assert.False(KeyAnalyzer.ContainsAnyLetters("123".AsSpan())); + Assert.True(KeyAnalyzer.ContainsAnyAsciiLetters("abc".AsSpan())); + Assert.True(KeyAnalyzer.ContainsAnyAsciiLetters("ABC".AsSpan())); + Assert.False(KeyAnalyzer.ContainsAnyAsciiLetters("123".AsSpan())); // note, must only pass ASCII to ContainsAnyLetters, anything else is a // Debug.Assert and would not have been called in the actual implementation } From 0272fcc6bfc09165a397b6dff56607bef794dd2a Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 15 Feb 2024 20:55:35 +0100 Subject: [PATCH 066/158] Fold "cast(cast(obj, cls), cls)" to "cast(obj, cls)" (#98337) Co-authored-by: Andy Ayers --- src/coreclr/jit/assertionprop.cpp | 145 +++++++++++++++++++++++------- src/coreclr/jit/compiler.h | 11 ++- src/coreclr/jit/gentree.cpp | 35 ++++++++ src/coreclr/jit/importer.cpp | 3 +- src/coreclr/jit/morph.cpp | 18 +--- src/coreclr/jit/valuenum.cpp | 7 ++ 6 files changed, 167 insertions(+), 52 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 13ddd674096062..fb317faea42c13 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2664,7 +2664,104 @@ AssertionIndex Compiler::optAssertionIsSubtype(GenTree* tree, GenTree* methodTab } //------------------------------------------------------------------------------ -// optVNConstantPropOnTree: Substitutes tree with an evaluated constant while +// optVNBasedFoldExpr_Call: Folds given call using VN to a simpler tree. +// +// Arguments: +// block - The block containing the tree. +// parent - The parent node of the tree. +// call - The call to fold +// +// Return Value: +// Returns a new tree or nullptr if nothing is changed. +// +GenTree* Compiler::optVNBasedFoldExpr_Call(BasicBlock* block, GenTree* parent, GenTreeCall* call) +{ + switch (call->GetHelperNum()) + { + // + // Fold "CAST(IsInstanceOf(obj, cls), cls)" to "IsInstanceOf(obj, cls)" + // where CAST is either ISINST or CASTCLASS. + // + case CORINFO_HELP_CHKCASTARRAY: + case CORINFO_HELP_CHKCASTANY: + case CORINFO_HELP_CHKCASTINTERFACE: + case CORINFO_HELP_CHKCASTCLASS: + case CORINFO_HELP_ISINSTANCEOFARRAY: + case CORINFO_HELP_ISINSTANCEOFCLASS: + case CORINFO_HELP_ISINSTANCEOFANY: + case CORINFO_HELP_ISINSTANCEOFINTERFACE: + { + GenTree* castClsArg = call->gtArgs.GetUserArgByIndex(0)->GetNode(); + GenTree* castObjArg = call->gtArgs.GetUserArgByIndex(1)->GetNode(); + ValueNum castClsArgVN = castClsArg->gtVNPair.GetConservative(); + ValueNum castObjArgVN = castObjArg->gtVNPair.GetConservative(); + + if ((castObjArg->gtFlags & GTF_ALL_EFFECT) != 0) + { + // It won't be trivial to properly extract side-effects from the call node. + // Ideally, we only need side effects from the castClsArg argument as the call itself + // won't throw any exceptions. But we should not forget about the EarlyNode (setup args) + return nullptr; + } + + VNFuncApp funcApp; + if (vnStore->GetVNFunc(castObjArgVN, &funcApp) && (funcApp.m_func == VNF_IsInstanceOf)) + { + ValueNum innerCastClsVN = funcApp.m_args[0]; + if (innerCastClsVN == castClsArgVN) + { + // The outer cast is redundant, remove it and preserve its side effects + // We do ignoreRoot here because the actual cast node never throws any exceptions. + GenTree* result = gtWrapWithSideEffects(castObjArg, call, GTF_ALL_EFFECT, true); + fgSetTreeSeq(result); + return result; + } + } + } + break; + + default: + break; + } + + return nullptr; +} + +//------------------------------------------------------------------------------ +// optVNBasedFoldExpr: Folds given tree using VN to a constant or a simpler tree. +// +// Arguments: +// block - The block containing the tree. +// parent - The parent node of the tree. +// tree - The tree to fold. +// +// Return Value: +// Returns a new tree or nullptr if nothing is changed. +// +GenTree* Compiler::optVNBasedFoldExpr(BasicBlock* block, GenTree* parent, GenTree* tree) +{ + // First, attempt to fold it to a constant if possible. + GenTree* foldedToCns = optVNBasedFoldConstExpr(block, parent, tree); + if (foldedToCns != nullptr) + { + return foldedToCns; + } + + switch (tree->OperGet()) + { + case GT_CALL: + return optVNBasedFoldExpr_Call(block, parent, tree->AsCall()); + + // We can add more VN-based foldings here. + + default: + break; + } + return nullptr; +} + +//------------------------------------------------------------------------------ +// optVNBasedFoldConstExpr: Substitutes tree with an evaluated constant while // managing side-effects. // // Arguments: @@ -2691,7 +2788,7 @@ AssertionIndex Compiler::optAssertionIsSubtype(GenTree* tree, GenTree* methodTab // the relop will evaluate to "true" or "false" statically, then the side-effects // will be put into new statements, presuming the JTrue will be folded away. // -GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* parent, GenTree* tree) +GenTree* Compiler::optVNBasedFoldConstExpr(BasicBlock* block, GenTree* parent, GenTree* tree) { if (tree->OperGet() == GT_JTRUE) { @@ -5109,21 +5206,10 @@ GenTree* Compiler::optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCal unsigned index = optAssertionIsSubtype(arg1, arg2, assertions); if (index != NO_ASSERTION_INDEX) { -#ifdef DEBUG - if (verbose) - { - printf("\nDid VN based subtype prop for index #%02u in " FMT_BB ":\n", index, compCurBB->bbNum); - gtDispTree(call, nullptr, nullptr, true); - } -#endif - GenTree* list = nullptr; - gtExtractSideEffList(call, &list, GTF_SIDE_EFFECT, true); - if (list != nullptr) - { - arg1 = gtNewOperNode(GT_COMMA, call->TypeGet(), list, arg1); - fgSetTreeSeq(arg1); - } + JITDUMP("\nDid VN based subtype prop for index #%02u in " FMT_BB ":\n", index, compCurBB->bbNum); + DISPTREE(call); + arg1 = gtWrapWithSideEffects(arg1, call, GTF_SIDE_EFFECT, true); return optAssertionProp_Update(arg1, call, stmt); } @@ -6301,8 +6387,8 @@ GenTree* Compiler::optVNConstantPropOnJTrue(BasicBlock* block, GenTree* test) } //------------------------------------------------------------------------------ -// optVNConstantPropCurStmt -// Performs constant prop on the current statement's tree nodes. +// optVNBasedFoldCurStmt: Performs VN-based folding +// on the current statement's tree nodes using VN. // // Assumption: // This function is called as part of a post-order tree walk. @@ -6316,17 +6402,12 @@ GenTree* Compiler::optVNConstantPropOnJTrue(BasicBlock* block, GenTree* test) // Return Value: // Returns the standard visitor walk result. // -// Description: -// Checks if a node is an R-value and evaluates to a constant. If the node -// evaluates to constant, then the tree is replaced by its side effects and -// the constant node. -// -Compiler::fgWalkResult Compiler::optVNConstantPropCurStmt(BasicBlock* block, - Statement* stmt, - GenTree* parent, - GenTree* tree) +Compiler::fgWalkResult Compiler::optVNBasedFoldCurStmt(BasicBlock* block, + Statement* stmt, + GenTree* parent, + GenTree* tree) { - // Don't perform const prop on expressions marked with GTF_DONT_CSE + // Don't try and fold expressions marked with GTF_DONT_CSE // TODO-ASG: delete. if (!tree->CanCSE()) { @@ -6414,8 +6495,8 @@ Compiler::fgWalkResult Compiler::optVNConstantPropCurStmt(BasicBlock* block, return WALK_CONTINUE; } - // Perform the constant propagation - GenTree* newTree = optVNConstantPropOnTree(block, parent, tree); + // Perform the VN-based folding: + GenTree* newTree = optVNBasedFoldExpr(block, parent, tree); if (newTree == nullptr) { @@ -6428,7 +6509,7 @@ Compiler::fgWalkResult Compiler::optVNConstantPropCurStmt(BasicBlock* block, optAssertionProp_Update(newTree, tree, stmt); - JITDUMP("After constant propagation on [%06u]:\n", tree->gtTreeID); + JITDUMP("After VN-based fold of [%06u]:\n", tree->gtTreeID); DBEXEC(VERBOSE, gtDispStmt(stmt)); return WALK_CONTINUE; @@ -6496,7 +6577,7 @@ Compiler::fgWalkResult Compiler::optVNAssertionPropCurStmtVisitor(GenTree** ppTr pThis->optVnNonNullPropCurStmt(pData->block, pData->stmt, *ppTree); - return pThis->optVNConstantPropCurStmt(pData->block, pData->stmt, data->parent, *ppTree); + return pThis->optVNBasedFoldCurStmt(pData->block, pData->stmt, data->parent, *ppTree); } /***************************************************************************** diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index ed2a7dac333b91..ab93a8044bb54c 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3518,6 +3518,11 @@ class Compiler GenTreeFlags GenTreeFlags = GTF_SIDE_EFFECT, bool ignoreRoot = false); + GenTree* gtWrapWithSideEffects(GenTree* tree, + GenTree* sideEffectsSource, + GenTreeFlags sideEffectsFlags = GTF_SIDE_EFFECT, + bool ignoreRoot = false); + bool gtSplitTree( BasicBlock* block, Statement* stmt, GenTree* splitPoint, Statement** firstNewStmt, GenTree*** splitPointUse); @@ -7722,9 +7727,11 @@ class Compiler public: void optVnNonNullPropCurStmt(BasicBlock* block, Statement* stmt, GenTree* tree); - fgWalkResult optVNConstantPropCurStmt(BasicBlock* block, Statement* stmt, GenTree* parent, GenTree* tree); + fgWalkResult optVNBasedFoldCurStmt(BasicBlock* block, Statement* stmt, GenTree* parent, GenTree* tree); GenTree* optVNConstantPropOnJTrue(BasicBlock* block, GenTree* test); - GenTree* optVNConstantPropOnTree(BasicBlock* block, GenTree* parent, GenTree* tree); + GenTree* optVNBasedFoldConstExpr(BasicBlock* block, GenTree* parent, GenTree* tree); + GenTree* optVNBasedFoldExpr(BasicBlock* block, GenTree* parent, GenTree* tree); + GenTree* optVNBasedFoldExpr_Call(BasicBlock* block, GenTree* parent, GenTreeCall* call); GenTree* optExtractSideEffListFromConst(GenTree* tree); AssertionIndex GetAssertionCount() diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 6d41f802ef3d45..99a90db42a8fb5 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -17132,6 +17132,41 @@ bool Compiler::gtSplitTree( return splitter.MadeChanges; } +//------------------------------------------------------------------------ +// gtWrapWithSideEffects: Extracts side effects from sideEffectSource (if any) +// and wraps the input tree with a COMMA node with them. +// +// Arguments: +// tree - the expression tree to wrap with side effects (if any) +// it has to be either a side effect free subnode of sideEffectsSource +// or any tree outside sideEffectsSource's hierarchy +// sideEffectsSource - the expression tree to extract side effects from +// sideEffectsFlags - side effect flags to be considered +// ignoreRoot - ignore side effects on the expression root node +// +// Return Value: +// The original tree wrapped with a COMMA node that contains the side effects +// or just the tree itself if sideEffectSource has no side effects. +// +GenTree* Compiler::gtWrapWithSideEffects(GenTree* tree, + GenTree* sideEffectsSource, + GenTreeFlags sideEffectsFlags, + bool ignoreRoot) +{ + GenTree* sideEffects = nullptr; + gtExtractSideEffList(sideEffectsSource, &sideEffects, sideEffectsFlags, ignoreRoot); + if (sideEffects != nullptr) + { + // TODO: assert if tree is a subnode of sideEffectsSource and the tree has its own side effects + // otherwise the resulting COMMA might have some side effects to be duplicated + // It should be possible to be smarter here and allow such cases by extracting the side effects + // properly for this particular case. For now, caller is responsible for avoiding such cases. + + tree = gtNewOperNode(GT_COMMA, tree->TypeGet(), sideEffects, tree); + } + return tree; +} + //------------------------------------------------------------------------ // gtExtractSideEffList: Extracts side effects from the given expression. // diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 2e79a604390203..b3d035b26d96bc 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1677,8 +1677,7 @@ GenTree* Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken return slotPtrTree; } - slotPtrTree = gtNewIndir(TYP_I_IMPL, slotPtrTree, GTF_IND_NONFAULTING); - slotPtrTree->gtFlags &= ~GTF_GLOB_REF; // TODO-Bug?: this is a quirk. Can we mark this indirection invariant? + slotPtrTree = gtNewIndir(TYP_I_IMPL, slotPtrTree, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); return slotPtrTree; } diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 312fc8300966a6..ebf5244dedc43f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -9653,22 +9653,8 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA } else { - GenTree* op1SideEffects = nullptr; - gtExtractSideEffList(op1, &op1SideEffects, GTF_ALL_EFFECT); - if (op1SideEffects != nullptr) - { - DEBUG_DESTROY_NODE(tree); - // Keep side-effects of op1 - tree = gtNewOperNode(GT_COMMA, TYP_INT, op1SideEffects, gtNewIconNode(0)); - JITDUMP("false with side effects:\n") - DISPTREE(tree); - } - else - { - JITDUMP("false\n"); - DEBUG_DESTROY_NODE(tree, op1); - tree = gtNewIconNode(0); - } + JITDUMP("false\n"); + tree = gtWrapWithSideEffects(gtNewIconNode(0), op1, GTF_ALL_EFFECT); } INDEBUG(tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); return tree; diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 8bb7c308207aff..977e984865aac7 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -13098,6 +13098,13 @@ bool Compiler::fgValueNumberHelperCall(GenTreeCall* call) vnStore->VNPairForFunc(TYP_REF, VNF_OverflowExc, vnStore->VNPForVoid())); break; + case CORINFO_HELP_CHKCASTINTERFACE: + case CORINFO_HELP_CHKCASTARRAY: + case CORINFO_HELP_CHKCASTCLASS: + case CORINFO_HELP_CHKCASTANY: + // InvalidCastExc for these is set in VNForCast + break; + default: // Setup vnpExc with the information that multiple different exceptions // could be generated by this helper From 617edb195797189c2c27e64735002da7886ac204 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 15 Feb 2024 12:03:19 -0800 Subject: [PATCH 067/158] X509Chain.Build should throw when an internal error occurs Chain building can sometimes return nonsensical values when an internal error is encountered. This changes the behavior so that chain building will throw instead of returning nonsensical values. --- .../TestUtilities/System/AssertExtensions.cs | 14 +++++ .../SignedCms/SignedCmsTests.netcoreapp.cs | 13 +++- .../src/Resources/Strings.resx | 3 + .../X509Certificates/X509Chain.cs | 61 ++++++++++++++----- .../tests/X509Certificates/ChainTests.cs | 52 ++++++++++++++++ .../tests/X509Certificates/TestData.cs | 49 +++++++++++++++ 6 files changed, 174 insertions(+), 18 deletions(-) diff --git a/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs b/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs index 32dd05facb0313..d74b25c120aec5 100644 --- a/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs +++ b/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs @@ -400,6 +400,20 @@ public static void GreaterThanOrEqualTo(T actual, T greaterThanOrEqualTo, str throw new XunitException(AddOptionalUserMessage($"Expected: {actual} to be greater than or equal to {greaterThanOrEqualTo}", userMessage)); } + /// + /// Validate that a given enum value has the expected flag set. + /// + /// The enum type. + /// The flag which should be present in . + /// The value which should contain the flag . + public static void HasFlag(T expected, T actual, string userMessage = null) where T : Enum + { + if (!actual.HasFlag(expected)) + { + throw new XunitException(AddOptionalUserMessage($"Expected: Value {actual} (of enum type {typeof(T).FullName}) to have the flag {expected} set.", userMessage)); + } + } + // NOTE: Consider using SequenceEqual below instead, as it will give more useful information about what // the actual differences are, especially for large arrays/spans. /// diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs index e5ef61d996a88b..2e1911ba11d3ea 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/SignedCms/SignedCmsTests.netcoreapp.cs @@ -399,7 +399,10 @@ public static void AddSigner_RSA_EphemeralKey() { ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 }); SignedCms cms = new SignedCms(content, false); - CmsSigner signer = new CmsSigner(certWithEphemeralKey); + CmsSigner signer = new CmsSigner(certWithEphemeralKey) + { + IncludeOption = X509IncludeOption.EndCertOnly + }; cms.ComputeSignature(signer); } } @@ -429,7 +432,8 @@ public static void AddSigner_DSA_EphemeralKey() SignedCms cms = new SignedCms(content, false); CmsSigner signer = new CmsSigner(certWithEphemeralKey) { - DigestAlgorithm = new Oid(Oids.Sha1, Oids.Sha1) + DigestAlgorithm = new Oid(Oids.Sha1, Oids.Sha1), + IncludeOption = X509IncludeOption.EndCertOnly }; cms.ComputeSignature(signer); } @@ -458,7 +462,10 @@ public static void AddSigner_ECDSA_EphemeralKey() { ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 }); SignedCms cms = new SignedCms(content, false); - CmsSigner signer = new CmsSigner(certWithEphemeralKey); + CmsSigner signer = new CmsSigner(certWithEphemeralKey) + { + IncludeOption = X509IncludeOption.EndCertOnly + }; cms.ComputeSignature(signer); } } diff --git a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx index e447937b7b6c39..8f39a254b6cc56 100644 --- a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx @@ -672,6 +672,9 @@ Certificate '{0}' is corrupted. + + An unknown chain building error occurred. + The certificate export operation failed. diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Chain.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Chain.cs index 50eaba6eb91b95..72707a4216650b 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Chain.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Chain.cs @@ -137,26 +137,57 @@ internal bool Build(X509Certificate2 certificate, bool throwOnException) chainPolicy.UrlRetrievalTimeout, chainPolicy.DisableCertificateDownloads); - if (_pal == null) - return false; - - _chainElements = new X509ChainElementCollection(_pal.ChainElements!); - - Exception? verificationException; - bool? verified = _pal.Verify(chainPolicy.VerificationFlags, out verificationException); - if (!verified.HasValue) + bool success = false; + if (_pal is not null) { - if (throwOnException) - { - throw verificationException!; - } - else + _chainElements = new X509ChainElementCollection(_pal.ChainElements!); + + Exception? verificationException; + bool? verified = _pal.Verify(chainPolicy.VerificationFlags, out verificationException); + if (!verified.HasValue) { - verified = false; + if (throwOnException) + { + throw verificationException!; + } + else + { + verified = false; + } } + + success = verified.Value; + } + + // There are two reasons success might be false here. + // + // The most common reason is that we built the chain but the chain appears to run + // afoul of policy. This is represented by BuildChain returning a non-null object + // and storing potential policy violations in the chain structure. The public Build + // method returns false to the caller, and the caller can inspect the ChainStatus + // and ChainElements properties and evaluate the failure reason against app-level + // policies. If the caller does not care about these policy violations, they can + // choose to ignore them and to treat chain building as successful. + // + // The other type of failure is that BuildChain simply can't build the chain at all. + // Perhaps something within the certificate is not valid or is unsupported, or perhaps + // there's an internal failure within the OS layer we're invoking, etc. Whatever the + // reason, we're not meaningfully able to initialize the ChainStatus property, which + // means callers may observe a non-empty list of policy violations. Depending on the + // caller's logic, they might incorrectly interpret this as there being no policy + // violations at all, which means they might treat this condition as success. + // + // To avoid callers misinterpreting this latter condition as success, we'll throw an + // exception, which matches general .NET API behavior when a method cannot complete + // its objective. If throwOnException is false, it means the caller explicitly wants + // to suppress exceptions and normalize them to a false return value. + + if (!success && throwOnException && _pal?.ChainStatus is not { Length: > 0 }) + { + throw new CryptographicException(SR.Cryptography_X509_ChainBuildingFailed); } - return verified.Value; + return success; } } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/ChainTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/ChainTests.cs index 60b16a3757f718..775c05b7fc7eec 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/ChainTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/ChainTests.cs @@ -1269,6 +1269,58 @@ public static void BuildChainForSelfSignedSha3Certificate() } } + [Fact] + public static void BuildChainForSelfSignedCertificate_WithSha256RsaSignature() + { + using (ChainHolder chainHolder = new ChainHolder()) + using (X509Certificate2 cert = new X509Certificate2(TestData.SelfSignedCertSha256RsaBytes)) + { + X509Chain chain = chainHolder.Chain; + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + chain.ChainPolicy.VerificationTime = cert.NotBefore.AddHours(2); + + // No custom root of trust store means that this self-signed cert will at + // minimum be marked UntrustedRoot. + + Assert.False(chain.Build(cert)); + AssertExtensions.HasFlag(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags()); + } + } + + [Fact] + public static void BuildChainForSelfSignedCertificate_WithUnknownOidSignature() + { + using (ChainHolder chainHolder = new ChainHolder()) + using (X509Certificate2 cert = new X509Certificate2(TestData.SelfSignedCertDummyOidBytes)) + { + X509Chain chain = chainHolder.Chain; + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + chain.ChainPolicy.VerificationTime = cert.NotBefore.AddHours(2); + + // This tests a self-signed cert whose signature block contains a garbage signing alg OID. + // Some platforms return NotSignatureValid to indicate that they cannot understand the + // signature block. Other platforms return PartialChain to indicate that they think the + // bad signature block might correspond to some unknown, untrusted signer. Yet other + // platforms simply fail the operation; e.g., Windows's CertGetCertificateChain API returns + // NTE_BAD_ALGID, which we bubble up as CryptographicException. + + if (PlatformDetection.UsesAppleCrypto) + { + Assert.False(chain.Build(cert)); + AssertExtensions.HasFlag(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + } + else if (PlatformDetection.IsOpenSslSupported) + { + Assert.False(chain.Build(cert)); + AssertExtensions.HasFlag(X509ChainStatusFlags.NotSignatureValid, chain.AllStatusFlags()); + } + else + { + Assert.ThrowsAny(() => chain.Build(cert)); + } + } + } + internal static X509ChainStatusFlags AllStatusFlags(this X509Chain chain) { return chain.ChainStatus.Aggregate( diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs index 6f62e7a067163e..0d7633f9992ef2 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/TestData.cs @@ -4225,6 +4225,55 @@ internal static DSAParameters GetDSA1024Params() "021A0414A57105D833610A6D07EBFBE51E5486CD3F8BCE0D0414DB32290CC077" + "37E9D9446E37F104FA876C861C0102022710").HexToByteArray(); + // Used for chain building tests (CN=Test chain building) + internal static readonly byte[] SelfSignedCertSha256RsaBytes = ( + "308202BD308201A5A003020102020900EF79C10DFD657168300D06092A864886F70D0101" + + "0B0500301E311C301A060355040313135465737420636861696E206275696C64696E6730" + + "1E170D3231313031333231353735335A170D3232313031333231353735335A301E311C30" + + "1A060355040313135465737420636861696E206275696C64696E6730820122300D06092A" + + "864886F70D01010105000382010F003082010A0282010100E3B5BBF862313DEAA9172788" + + "278B26A3EAB61B9B0326F5CEA91B1A6C6DFD156836A2363BFAC5B0F4A78F4CFF5A11F35A" + + "831C6D7935D1DFD13DD81DA29AA0645CBA9F4D20BF991C625E6D61CF396C15914DEE41F6" + + "1190E97B52BFF7AE52B79FD0E2EEE3319EC23C30D27A52A2E8A963557B12BEC0664ADEF9" + + "3C520B587EC5DABFBC70980DB7473414B4B6BF982EA9AA0969F2A76AA085464AE78DFB2B" + + "F04BDE7192874679193119C2AABEC04D360F61925921660BF09A0489B7C53464F5FC35B8" + + "612F5B993D544475C20AC46CD350A34551FEA0ACBD138D8B72F79052BF0EB3BD794A426C" + + "0117CB77B4F311FFD1C628F8E438E5474509AD51FA035558771546310203010001300D06" + + "092A864886F70D01010B050003820101000A12CE2FC3DC854113D179725E9D9ADD013A42" + + "D66340CEA7A465D54EC357AD8FED1828862D8B5C32EB3D21FC8B26A7CFA9D9FB36F593CC" + + "6AD30C25C96E8100C3F07B1B51430245EE995864749C53B409260B4040705654710C236F" + + "D9B7DE3F3BE5E6E5047712C5E506419106A57C5290BB206A97F6A3FCC4B4C83E25C3FC6D" + + "2BAB03B941374086265EE08A90A8C72A63A4053044B9FA3ABD5ED5785CFDDB15A6A327BD" + + "C0CC2B115B9D33BD6E528E35670E5A6A8D9CF52199F8D693315C60D9ADAD54EF7FDCED36" + + "0C8C79E84D42AB5CB6355A70951B1ABF1F2B3FB8BEB7E3A8D6BA2293C0DB8C86B0BB060F" + + "0D6DB9939E88B998662A27F092634BBF21F58EEAAA").HexToByteArray(); + + // This is nearly identical to the cert in Pkcs7SelfSignedCertSha256RsaBytes, + // but we've replaced the OID (1.2.840.113549.1.1.11 sha256RSA) with a dummy OID + // 1.3.9999.1234.5678.1234. The cert should load properly into an X509Certificate2 + // object but will cause chain building to fail. + internal static readonly byte[] SelfSignedCertDummyOidBytes = ( + "308202BD308201A5A003020102020900EF79C10DFD657168300D06092A864886F70D0101" + + "0B0500301E311C301A060355040313135465737420636861696E206275696C64696E6730" + + "1E170D3231313031333231353735335A170D3232313031333231353735335A301E311C30" + + "1A060355040313135465737420636861696E206275696C64696E6730820122300D06092A" + + "864886F70D01010105000382010F003082010A0282010100E3B5BBF862313DEAA9172788" + + "278B26A3EAB61B9B0326F5CEA91B1A6C6DFD156836A2363BFAC5B0F4A78F4CFF5A11F35A" + + "831C6D7935D1DFD13DD81DA29AA0645CBA9F4D20BF991C625E6D61CF396C15914DEE41F6" + + "1190E97B52BFF7AE52B79FD0E2EEE3319EC23C30D27A52A2E8A963557B12BEC0664ADEF9" + + "3C520B587EC5DABFBC70980DB7473414B4B6BF982EA9AA0969F2A76AA085464AE78DFB2B" + + "F04BDE7192874679193119C2AABEC04D360F61925921660BF09A0489B7C53464F5FC35B8" + + "612F5B993D544475C20AC46CD350A34551FEA0ACBD138D8B72F79052BF0EB3BD794A426C" + + "0117CB77B4F311FFD1C628F8E438E5474509AD51FA035558771546310203010001300D06" + + "092BCE0F8952AC2E8952050003820101000A12CE2FC3DC854113D179725E9D9ADD013A42" + + "D66340CEA7A465D54EC357AD8FED1828862D8B5C32EB3D21FC8B26A7CFA9D9FB36F593CC" + + "6AD30C25C96E8100C3F07B1B51430245EE995864749C53B409260B4040705654710C236F" + + "D9B7DE3F3BE5E6E5047712C5E506419106A57C5290BB206A97F6A3FCC4B4C83E25C3FC6D" + + "2BAB03B941374086265EE08A90A8C72A63A4053044B9FA3ABD5ED5785CFDDB15A6A327BD" + + "C0CC2B115B9D33BD6E528E35670E5A6A8D9CF52199F8D693315C60D9ADAD54EF7FDCED36" + + "0C8C79E84D42AB5CB6355A70951B1ABF1F2B3FB8BEB7E3A8D6BA2293C0DB8C86B0BB060F" + + "0D6DB9939E88B998662A27F092634BBF21F58EEAAA").HexToByteArray(); + internal static readonly byte[] EmptyPkcs7 = "300B06092A864886F70D010702".HexToByteArray(); } } From dc7f7033f22ca5369abaf9049f848b74ed2ba200 Mon Sep 17 00:00:00 2001 From: Kunal Pathak Date: Thu, 15 Feb 2024 13:03:55 -0800 Subject: [PATCH 068/158] [NativeAOT] Inline TLS access for linux/arm64 (#97910) * wip * hacky version * more fix * missing build error * wip * fixes * fix * Add IMAGE_REL_AARCH64_TLSDESC_CALL * Use GT_CALL to emit everything * fix the loading of slow helper * fix build error * adjust the endif * fix the slow helper relocation * cleanup * fix iiaAddr() for TLS, add IsTlsIconHandle() * add comments * jit format * Remove EmitInlineTLSAccess() * update guid * fix for sharedlibrary * misc changes * Review feedback --- src/coreclr/inc/corinfo.h | 10 ++ src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/codegenarm64.cpp | 5 + src/coreclr/jit/codegenarmarch.cpp | 46 +++++++ src/coreclr/jit/emitarm64.cpp | 115 ++++++++++++++++-- src/coreclr/jit/emitarm64.h | 5 + src/coreclr/jit/gentree.h | 10 ++ src/coreclr/jit/helperexpansion.cpp | 83 +++++++++---- src/coreclr/jit/lsraarmarch.cpp | 15 +++ .../Compiler/DependencyAnalysis/Relocation.cs | 5 + .../tools/Common/JitInterface/CorInfoImpl.cs | 13 ++ .../Target_ARM64/ARM64ReadyToRunHelperNode.cs | 97 +-------------- .../Target_X64/X64ReadyToRunHelperNode.cs | 32 +---- .../JitInterface/CorInfoImpl.RyuJit.cs | 5 +- 14 files changed, 281 insertions(+), 170 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 2da2a737bccc32..86680d6e20c91e 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -3372,8 +3372,18 @@ class ICorDynamicInfo : public ICorStaticInfo #define IMAGE_REL_BASED_REL32 0x10 #define IMAGE_REL_BASED_THUMB_BRANCH24 0x13 #define IMAGE_REL_SECREL 0x104 + +// Linux x64 +// GD model #define IMAGE_REL_TLSGD 0x105 +// Linux arm64 +// TLSDESC (dynamic) +#define IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21 0x107 +#define IMAGE_REL_AARCH64_TLSDESC_LD64_LO12 0x108 +#define IMAGE_REL_AARCH64_TLSDESC_ADD_LO12 0x109 +#define IMAGE_REL_AARCH64_TLSDESC_CALL 0x10A + // The identifier for ARM32-specific PC-relative address // computation corresponds to the following instruction // sequence: diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 5d63f9df9a80d4..6355fc20dd0fd5 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* b8a05f18-503e-47e4-9193-931c50b151d1 */ - 0xb8a05f18, - 0x503e, - 0x47e4, - {0x91, 0x93, 0x93, 0x1c, 0x50, 0xb1, 0x51, 0xd1} +constexpr GUID JITEEVersionIdentifier = { /* 0fb71692-0ee6-4914-88a8-6446e45f23e8 */ + 0x0fb71692, + 0x0ee6, + 0x4914, + {0x88, 0xa8, 0x64, 0x46, 0xe4, 0x5f, 0x23, 0xe8} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 4efe4a235f6a7d..3883b20118ad8a 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -2333,6 +2333,11 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre if (con->ImmedValNeedsReloc(compiler)) { attr = EA_SET_FLG(attr, EA_CNS_RELOC_FLG); + if (tree->IsTlsIconHandle()) + { + // no need to generate because we generate it as part of GT_CALL + break; + } } if (targetType == TYP_BYREF) diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index cb04cf702f2b94..92e99471956f0d 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -3623,6 +3623,44 @@ void CodeGen::genCallInstruction(GenTreeCall* call) // assert(genIsValidIntReg(target->GetRegNum())); +#ifdef TARGET_ARM64 + bool isTlsHandleTarget = + compiler->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && TargetOS::IsUnix && target->IsTlsIconHandle(); + + if (isTlsHandleTarget) + { + assert(call->gtFlags & GTF_TLS_GET_ADDR); + emitter* emitter = GetEmitter(); + emitAttr attr = (emitAttr)(EA_CNS_TLSGD_RELOC | EA_CNS_RELOC_FLG | retSize); + GenTreeIntCon* iconNode = target->AsIntCon(); + methHnd = (CORINFO_METHOD_HANDLE)iconNode->gtIconVal; + retSize = EA_SET_FLG(retSize, EA_CNS_TLSGD_RELOC); + + // For NativeAOT, linux/arm64, linker wants the following pattern, so we will generate + // it as part of the call. Generating individual instructions is tricky to get it + // correct in the format the way linker needs. Also, we might end up spilling or + // reloading a register, which can break the pattern. + // + // mrs x1, tpidr_el0 + // adrp x0, :tlsdesc:tlsRoot ; R_AARCH64_TLSDESC_ADR_PAGE21 + // ldr x2, [x0] ; R_AARCH64_TLSDESC_LD64_LO12 + // add x0, x0, #0 ; R_AARCH64_TLSDESC_ADD_LO12 + // blr x2 ; R_AARCH64_TLSDESC_CALL + // add x0, x1, x0 + // We guaranteed in LSRA that r0, r1 and r2 are assigned to this node. + + // mrs + emitter->emitIns_R(INS_mrs_tpid0, attr, REG_R1); + + // adrp + // ldr + // add + emitter->emitIns_Adrp_Ldr_Add(attr, REG_R0, target->GetRegNum(), + (ssize_t)methHnd DEBUGARG(iconNode->gtTargetHandle) + DEBUGARG(iconNode->gtFlags)); + } +#endif + // clang-format off genEmitCall(emitter::EC_INDIR_R, methHnd, @@ -3633,6 +3671,14 @@ void CodeGen::genCallInstruction(GenTreeCall* call) di, target->GetRegNum(), call->IsFastTailCall()); + +#ifdef TARGET_ARM64 + if (isTlsHandleTarget) + { + // add x0, x1, x0 + GetEmitter()->emitIns_R_R_R(INS_add, EA_8BYTE, REG_R0, REG_R1, REG_R0); + } +#endif // clang-format on } else diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 5a9285e3b49018..60282237609798 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -215,7 +215,15 @@ void emitter::emitInsSanityCheck(instrDesc* id) break; case IF_BR_1B: // BR_1B ................ ......nnnnn..... Rn - assert(isGeneralRegister(id->idReg3())); + if (emitComp->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && id->idIsTlsGD()) + { + assert(isGeneralRegister(id->idReg1())); + assert(id->idAddr()->iiaAddr != nullptr); + } + else + { + assert(isGeneralRegister(id->idReg3())); + } break; case IF_LS_1A: // LS_1A .X......iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB) @@ -227,7 +235,7 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isIntegerRegister(id->idReg1()) || // ZR isVectorRegister(id->idReg1())); assert(isIntegerRegister(id->idReg2())); // SP - assert(emitGetInsSC(id) == 0); + assert((emitGetInsSC(id) == 0) || (id->idIsTlsGD())); assert(insOptsNone(id->idInsOpt())); break; @@ -9542,7 +9550,7 @@ void emitter::emitIns_R_R_I(instruction ins, reg2 = encodingSPtoZR(reg2); ssize_t mask = (1 << scale) - 1; // the mask of low bits that must be zero to encode the immediate - if (imm == 0) + if (imm == 0 || EA_IS_CNS_TLSGD_RELOC(attr)) { assert(insOptsNone(opt)); // PRE/POST Index doesn't make sense with an immediate of zero @@ -9649,7 +9657,11 @@ void emitter::emitIns_R_R_I(instruction ins, id->idReg1(reg1); id->idReg2(reg2); - + if (EA_IS_CNS_TLSGD_RELOC(attr)) + { + assert(imm != 0); + id->idSetTlsGD(); + } dispIns(id); appendToCurIG(id); } @@ -15396,6 +15408,61 @@ void emitter::emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNu NYI("emitIns_R_AR"); } +// This generates code to populate the access for TLS on linux +void emitter::emitIns_Adrp_Ldr_Add(emitAttr attr, + regNumber reg1, + regNumber reg2, + ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) +{ + assert(emitComp->IsTargetAbi(CORINFO_NATIVEAOT_ABI)); + assert(TargetOS::IsUnix); + assert(EA_IS_RELOC(attr)); + assert(EA_IS_CNS_TLSGD_RELOC(attr)); + + emitAttr size = EA_SIZE(attr); + insFormat fmt = IF_DI_1E; + bool needAdd = false; + instrDescJmp* id = emitNewInstrJmp(); + + // adrp + id->idIns(INS_adrp); + id->idInsFmt(fmt); + id->idInsOpt(INS_OPTS_NONE); + id->idOpSize(size); + id->idAddr()->iiaAddr = (BYTE*)addr; + id->idReg1(reg1); + id->idSetIsDspReloc(); + id->idSetTlsGD(); + +#ifdef DEBUG + id->idDebugOnlyInfo()->idMemCookie = targetHandle; + id->idDebugOnlyInfo()->idFlags = gtFlags; +#endif + + dispIns(id); + appendToCurIG(id); + + // ldr + emitIns_R_R_I(INS_ldr, attr, reg2, reg1, (ssize_t)addr); + + // add + fmt = IF_DI_2A; + instrDesc* addId = emitNewInstr(attr); + assert(id->idIsReloc()); + + addId->idIns(INS_add); + addId->idInsFmt(fmt); + addId->idInsOpt(INS_OPTS_NONE); + addId->idOpSize(size); + addId->idAddr()->iiaAddr = (BYTE*)addr; + addId->idReg1(reg1); + addId->idReg2(reg1); + addId->idSetTlsGD(); + + dispIns(addId); + appendToCurIG(addId); +} + // This computes address from the immediate which is relocatable. void emitter::emitIns_R_AI(instruction ins, emitAttr attr, @@ -15909,8 +15976,21 @@ void emitter::emitIns_Call(EmitCallType callType, id->idIns(ins); id->idInsFmt(fmt); - id->idReg3(ireg); assert(xreg == REG_NA); + if (emitComp->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && EA_IS_CNS_TLSGD_RELOC(retSize)) + { + // For NativeAOT linux/arm64, we need to also record the relocation of methHnd. + // Since we do not have space to embed it in instrDesc, we store the register in + // reg1 and instead use the `iiaAdd` to store the method handle. Likewise, during + // emitOutputInstr, we retrieve the register from reg1 for this specific case. + id->idSetTlsGD(); + id->idReg1(ireg); + id->idAddr()->iiaAddr = (BYTE*)methHnd; + } + else + { + id->idReg3(ireg); + } } else { @@ -20106,10 +20186,19 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) assert(insOptsNone(id->idInsOpt())); assert((ins == INS_br_tail) || (ins == INS_blr)); code = emitInsCode(ins, fmt); - code |= insEncodeReg_Rn(id->idReg3()); // nnnnn - sz = id->idIsLargeCall() ? sizeof(instrDescCGCA) : sizeof(instrDesc); + if (emitComp->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && id->idIsTlsGD()) + { + emitRecordRelocation(odst, (CORINFO_METHOD_HANDLE)id->idAddr()->iiaAddr, + IMAGE_REL_AARCH64_TLSDESC_CALL); + code |= insEncodeReg_Rn(id->idReg1()); // nnnnn + } + else + { + code |= insEncodeReg_Rn(id->idReg3()); // nnnnn + } dst += emitOutputCall(ig, dst, id, code); + sz = id->idIsLargeCall() ? sizeof(instrDescCGCA) : sizeof(instrDesc); break; case IF_LS_1A: // LS_1A XX...V..iiiiiiii iiiiiiiiiiittttt Rt PC imm(1MB) @@ -20138,6 +20227,10 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) } code |= insEncodeReg_Rn(id->idReg2()); // nnnnn dst += emitOutput_Instr(dst, code); + if (id->idIsTlsGD()) + { + emitRecordRelocation(odst, (void*)emitGetInsSC(id), IMAGE_REL_AARCH64_TLSDESC_LD64_LO12); + } break; case IF_LS_2B: // LS_2B .X.......Xiiiiii iiiiiinnnnnttttt Rt Rn imm(0-4095) @@ -20403,7 +20496,8 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) code = emitInsCode(ins, fmt); code |= insEncodeReg_Rd(id->idReg1()); // ddddd dst += emitOutput_Instr(dst, code); - emitRecordRelocation(odst, id->idAddr()->iiaAddr, IMAGE_REL_ARM64_PAGEBASE_REL21); + emitRecordRelocation(odst, id->idAddr()->iiaAddr, id->idIsTlsGD() ? IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21 + : IMAGE_REL_ARM64_PAGEBASE_REL21); } else { @@ -20446,7 +20540,8 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) { assert(sz == sizeof(instrDesc)); assert(id->idAddr()->iiaAddr != nullptr); - emitRecordRelocation(odst, id->idAddr()->iiaAddr, IMAGE_REL_ARM64_PAGEOFFSET_12A); + emitRecordRelocation(odst, id->idAddr()->iiaAddr, id->idIsTlsGD() ? IMAGE_REL_AARCH64_TLSDESC_ADD_LO12 + : IMAGE_REL_ARM64_PAGEOFFSET_12A); } break; @@ -23962,7 +24057,7 @@ void emitter::emitDispInsHelp( case IF_LS_2A: // LS_2A .X.......X...... ......nnnnnttttt Rt Rn assert(insOptsNone(id->idInsOpt())); - assert(emitGetInsSC(id) == 0); + assert((emitGetInsSC(id) == 0) || id->idIsTlsGD()); emitDispReg(id->idReg1(), emitInsTargetRegSize(id), true); emitDispAddrRI(id->idReg2(), id->idInsOpt(), 0); break; diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index f224008c4f64f7..99b36bd4ec5341 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -1476,6 +1476,11 @@ void emitIns_I_AR(instruction ins, emitAttr attr, int val, regNumber reg, int of void emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, int offs); +void emitIns_Adrp_Ldr_Add(emitAttr attr, + regNumber reg1, + regNumber reg2, + ssize_t addr DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); + void emitIns_R_AI(instruction ins, emitAttr attr, regNumber ireg, diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 49486d48750f48..98d0039111b6ac 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2234,6 +2234,16 @@ struct GenTree return (gtOper == GT_CNS_INT) ? (gtFlags & GTF_ICON_HDL_MASK) : GTF_EMPTY; } + bool IsTlsIconHandle() + { + if (IsIconHandle()) + { + GenTreeFlags tlsFlags = (GTF_ICON_TLSGD_OFFSET | GTF_ICON_TLS_HDL); + return ((gtFlags & tlsFlags) == tlsFlags); + } + return false; + } + // Mark this node as no longer being a handle; clear its GTF_ICON_*_HDL bits. void ClearIconHandleMask() { diff --git a/src/coreclr/jit/helperexpansion.cpp b/src/coreclr/jit/helperexpansion.cpp index eba1849389f7e9..52b7d3d68cbce9 100644 --- a/src/coreclr/jit/helperexpansion.cpp +++ b/src/coreclr/jit/helperexpansion.cpp @@ -576,8 +576,8 @@ bool Compiler::fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, St // use(tlsRoot); // ... - GenTree* tlsRootAddr = nullptr; - CORINFO_CONST_LOOKUP tlsRootObject = threadStaticInfo.tlsRootObject; + GenTree* tlsRootAddr = nullptr; + CORINFO_GENERIC_HANDLE tlsRootObject = threadStaticInfo.tlsRootObject.handle; if (TargetOS::IsWindows) { @@ -598,7 +598,7 @@ bool Compiler::fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, St tlsValue = gtNewIndir(TYP_I_IMPL, tlsValue, GTF_IND_NONFAULTING | GTF_IND_INVARIANT); // This resolves to an offset which is TYP_INT - GenTree* tlsRootOffset = gtNewIconNode((size_t)tlsRootObject.handle, TYP_INT); + GenTree* tlsRootOffset = gtNewIconNode((size_t)tlsRootObject, TYP_INT); tlsRootOffset->gtFlags |= GTF_ICON_SECREL_OFFSET; // Add the tlsValue and tlsRootOffset to produce tlsRootAddr. @@ -606,34 +606,63 @@ bool Compiler::fgExpandThreadLocalAccessForCallNativeAOT(BasicBlock** pBlock, St } else if (TargetOS::IsUnix) { - // Code sequence to access thread local variable on linux/x64: - // data16 - // lea rdi, 0x7FE5C418CD28 ; tlsRootObject - // data16 data16 - // call _tls_get_addr - // - // This sequence along with `data16` prefix is expected by the linker so it - // will patch these with TLS access. - GenTree* tls_get_addr_val = - gtNewIconHandleNode((size_t)threadStaticInfo.tlsGetAddrFtnPtr.handle, GTF_ICON_FTN_ADDR); - tls_get_addr_val->SetContained(); + if (TargetArchitecture::IsX64) + { + // Code sequence to access thread local variable on linux/x64: + // data16 + // lea rdi, 0x7FE5C418CD28 ; tlsRootObject + // data16 data16 + // call _tls_get_addr + // + // This sequence along with `data16` prefix is expected by the linker so it + // will patch these with TLS access. + GenTree* tls_get_addr_val = + gtNewIconHandleNode((size_t)threadStaticInfo.tlsGetAddrFtnPtr.handle, GTF_ICON_FTN_ADDR); + tls_get_addr_val->SetContained(); + + GenTreeCall* tlsRefCall = gtNewIndCallNode(tls_get_addr_val, TYP_I_IMPL); + tlsRefCall->gtFlags |= GTF_TLS_GET_ADDR; + + // This is an indirect call which takes an argument. + // Populate and set the ABI appropriately. + assert(tlsRootObject != 0); + GenTree* tlsArg = gtNewIconNode((size_t)tlsRootObject, TYP_I_IMPL); + tlsArg->gtFlags |= GTF_ICON_TLSGD_OFFSET; + tlsRefCall->gtArgs.PushBack(this, NewCallArg::Primitive(tlsArg)); + + fgMorphArgs(tlsRefCall); + + tlsRefCall->gtFlags |= GTF_EXCEPT | (tls_get_addr_val->gtFlags & GTF_GLOB_EFFECT); + tlsRootAddr = tlsRefCall; + } + else if (TargetArchitecture::IsArm64) + { + /* + x0 = adrp :tlsdesc:tlsRoot ; 1st parameter + x0 += tlsdesc_lo12:tlsRoot ; update 1st parameter - // GenTreeCall* tlsRefCall = gtNewCallNode(CT_ tls_get_addr_val, TYP_I_IMPL); - GenTreeCall* tlsRefCall = gtNewIndCallNode(tls_get_addr_val, TYP_I_IMPL); - tlsRefCall->gtFlags |= GTF_TLS_GET_ADDR; - // // + x1 = tpidr_el0 ; 2nd parameter - // This is an indirect call which takes an argument. - // Populate and set the ABI appropriately. - assert(tlsRootObject.handle != 0); - GenTree* tlsArg = gtNewIconNode((size_t)tlsRootObject.handle, TYP_I_IMPL); - tlsArg->gtFlags |= GTF_ICON_TLSGD_OFFSET; - tlsRefCall->gtArgs.PushBack(this, NewCallArg::Primitive(tlsArg)); + x2 = [x0] ; call + blr x2 - fgMorphArgs(tlsRefCall); + */ - tlsRefCall->gtFlags |= GTF_EXCEPT | (tls_get_addr_val->gtFlags & GTF_GLOB_EFFECT); - tlsRootAddr = tlsRefCall; + GenTree* tlsRootOffset = gtNewIconHandleNode((size_t)tlsRootObject, GTF_ICON_TLS_HDL); + tlsRootOffset->gtFlags |= GTF_ICON_TLSGD_OFFSET; + + GenTree* tlsCallIndir = gtCloneExpr(tlsRootOffset); + GenTreeCall* tlsRefCall = gtNewIndCallNode(tlsCallIndir, TYP_I_IMPL); + tlsRefCall->gtFlags |= GTF_TLS_GET_ADDR; + fgMorphArgs(tlsRefCall); + + tlsRefCall->gtFlags |= GTF_EXCEPT | (tlsCallIndir->gtFlags & GTF_GLOB_EFFECT); + tlsRootAddr = tlsRefCall; + } + else + { + unreached(); + } } else { diff --git a/src/coreclr/jit/lsraarmarch.cpp b/src/coreclr/jit/lsraarmarch.cpp index 8b1305caec52ae..a0f27ba65b3058 100644 --- a/src/coreclr/jit/lsraarmarch.cpp +++ b/src/coreclr/jit/lsraarmarch.cpp @@ -368,6 +368,21 @@ int LinearScan::BuildCall(GenTreeCall* call) if (ctrlExpr != nullptr) { +#ifdef TARGET_ARM64 + if (compiler->IsTargetAbi(CORINFO_NATIVEAOT_ABI) && TargetOS::IsUnix && (call->gtArgs.CountArgs() == 0) && + ctrlExpr->IsTlsIconHandle()) + { + // For NativeAOT linux/arm64, we generate the needed code as part of + // call node because the generated code has to be in specific format + // that linker can patch. As such, the code needs specific registers + // that we will attach to this node to guarantee that they are available + // during generating this node. + assert(call->gtFlags & GTF_TLS_GET_ADDR); + newRefPosition(REG_R0, currentLoc, RefTypeFixedReg, nullptr, genRegMask(REG_R0)); + newRefPosition(REG_R1, currentLoc, RefTypeFixedReg, nullptr, genRegMask(REG_R1)); + ctrlExprCandidates = genRegMask(REG_R2); + } +#endif BuildUse(ctrlExpr, ctrlExprCandidates); srcCount++; } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index 3d2441891fbfed..7585ca75fa8b4a 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -487,8 +487,11 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v case RelocType.IMAGE_REL_TPOFF: case RelocType.IMAGE_REL_SYMBOL_SIZE: case RelocType.IMAGE_REL_FILE_ABSOLUTE: + case RelocType.IMAGE_REL_AARCH64_TLSDESC_CALL: *(int*)location = (int)value; break; + case RelocType.IMAGE_REL_AARCH64_TLSDESC_LD64_LO12: + break; case RelocType.IMAGE_REL_BASED_DIR64: *(long*)location = value; break; @@ -503,9 +506,11 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v PutArm64Rel28((uint*)location, value); break; case RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: + case RelocType.IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21: PutArm64Rel21((uint*)location, (int)value); break; case RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: + case RelocType.IMAGE_REL_AARCH64_TLSDESC_ADD_LO12: PutArm64Rel12((uint*)location, (int)value); break; case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC: diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 62529c5b443c9b..2e72a18def7713 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3879,6 +3879,11 @@ private static RelocType GetRelocType(TargetArchitecture targetArchitecture, ush const ushort IMAGE_REL_ARM64_BRANCH26 = 3; const ushort IMAGE_REL_ARM64_PAGEBASE_REL21 = 4; const ushort IMAGE_REL_ARM64_PAGEOFFSET_12A = 6; + const ushort IMAGE_REL_ARM64_TLSDESC_ADR_PAGE21 = 0x107; + const ushort IMAGE_REL_ARM64_TLSDESC_LD64_LO12 = 0x108; + const ushort IMAGE_REL_ARM64_TLSDESC_ADD_LO12 = 0x109; + const ushort IMAGE_REL_ARM64_TLSDESC_CALL = 0x10A; + switch (fRelocType) { @@ -3888,6 +3893,14 @@ private static RelocType GetRelocType(TargetArchitecture targetArchitecture, ush return RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21; case IMAGE_REL_ARM64_PAGEOFFSET_12A: return RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A; + case IMAGE_REL_ARM64_TLSDESC_ADR_PAGE21: + return RelocType.IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21; + case IMAGE_REL_ARM64_TLSDESC_ADD_LO12: + return RelocType.IMAGE_REL_AARCH64_TLSDESC_ADD_LO12; + case IMAGE_REL_ARM64_TLSDESC_LD64_LO12: + return RelocType.IMAGE_REL_AARCH64_TLSDESC_LD64_LO12; + case IMAGE_REL_ARM64_TLSDESC_CALL: + return RelocType.IMAGE_REL_AARCH64_TLSDESC_CALL; default: Debug.Fail("Invalid RelocType: " + fRelocType); return 0; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs index 6d94afdc3ee4be..7f672fca6fa151 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_ARM64/ARM64ReadyToRunHelperNode.cs @@ -74,26 +74,7 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, ISortableSymbolNode index = factory.TypeThreadStaticIndex(target); if (index is TypeThreadStaticIndexNode ti && ti.IsInlined) { - if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) - { - EmitInlineTLSAccess(factory, ref encoder); - } - else - { - // First arg: unused address of the TypeManager - // encoder.EmitMOV(encoder.TargetRegister.Arg0, (ushort)0); - - // Second arg: ~0 (index of inlined storage) - encoder.EmitMVN(encoder.TargetRegister.Arg1, 0); - - encoder.EmitMOV(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); - encoder.EmitSUB(encoder.TargetRegister.Arg2, NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); - encoder.EmitLDR(encoder.TargetRegister.Arg3, encoder.TargetRegister.Arg2); - encoder.EmitCMP(encoder.TargetRegister.Arg3, 0); - - encoder.EmitJNE(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); - EmitInlineTLSAccess(factory, ref encoder); - } + throw new NotImplementedException(); } else { @@ -226,81 +207,5 @@ protected override void EmitCode(NodeFactory factory, ref ARM64Emitter encoder, throw new NotImplementedException(); } } - - // emits code that results in ThreadStaticBase referenced in X0. - // may trash volatile registers. (there are calls to the slow helper and possibly to the platform's TLS support) - private static void EmitInlineTLSAccess(NodeFactory factory, ref ARM64Emitter encoder) - { - ISymbolNode getInlinedThreadStaticBaseSlow = factory.HelperEntrypoint(HelperEntrypoint.GetInlinedThreadStaticBaseSlow); - ISymbolNode tlsRoot = factory.TlsRoot; - // IsSingleFileCompilation is not enough to guarantee that we can use "Initial Executable" optimizations. - // we need a special compiler flag analogous to /GA. Just assume "false" for now. - // bool isInitialExecutable = factory.CompilationModuleGroup.IsSingleFileCompilation; - bool isInitialExecutable = false; - - if (factory.Target.OperatingSystem == TargetOS.Linux) - { - if (isInitialExecutable) - { - // mrs x0, tpidr_el0 - encoder.Builder.EmitUInt(0xd53bd040); - - // add x0, x0, #:tprel_hi12:tlsRoot, lsl #12 - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_HI12); - encoder.Builder.EmitUInt(0x91400000); - - // add x1, x0, #:tprel_lo12_nc:tlsRoot, lsl #0 - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_LO12_NC); - encoder.Builder.EmitUInt(0x91000001); - } - else - { - // stp x29, x30, [sp, -16]! - encoder.Builder.EmitUInt(0xa9bf7bfd); - // mov x29, sp - encoder.Builder.EmitUInt(0x910003fd); - - // mrs x1, tpidr_el0 - encoder.Builder.EmitUInt(0xd53bd041); - - // adrp x0, :tlsdesc:tlsRoot - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21); - encoder.Builder.EmitUInt(0x90000000); - - // ldr x2, [x0, #:tlsdesc_lo12:tlsRoot] - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSDESC_LD64_LO12); - encoder.Builder.EmitUInt(0xf9400002); - - // add x0, x0, :tlsdesc_lo12:tlsRoot - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSDESC_ADD_LO12); - encoder.Builder.EmitUInt(0x91000000); - - // blr :tlsdesc_call:tlsRoot:x2 - encoder.Builder.EmitReloc(tlsRoot, RelocType.IMAGE_REL_AARCH64_TLSDESC_CALL); - encoder.Builder.EmitUInt(0xd63f0040); - - // add x1, x1, x0 - encoder.Builder.EmitUInt(0x8b000021); - - // ldp x29, x30, [sp], 16 - encoder.Builder.EmitUInt(0xa8c17bfd); - } - - encoder.EmitLDR(Register.X0, Register.X1); - - // here we have: - // X1: addr, X0: storage - // if the storage is already allocated, just return, otherwise do slow path. - - encoder.EmitCMP(Register.X0, 0); - encoder.EmitRETIfNotEqual(); - encoder.EmitMOV(Register.X0, Register.X1); - encoder.EmitJMP(getInlinedThreadStaticBaseSlow); - } - else - { - throw new NotImplementedException(); - } - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs index 66265e9d71bb49..ab20cf1c892fa3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -74,26 +74,7 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo ISortableSymbolNode index = factory.TypeThreadStaticIndex(target); if (index is TypeThreadStaticIndexNode ti && ti.IsInlined) { - if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) - { - EmitInlineTLSAccess(factory, ref encoder); - } - else - { - // First arg: unused address of the TypeManager - // encoder.EmitMOV(encoder.TargetRegister.Arg0, 0); - - // Second arg: -1 (index of inlined storage) - encoder.EmitMOV(encoder.TargetRegister.Arg1, -1); - - encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); - - AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg2, null, 0, 0, AddrModeSize.Int64); - encoder.EmitCMP(ref initialized, 0); - encoder.EmitJNE(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); - - EmitInlineTLSAccess(factory, ref encoder); - } + throw new NotImplementedException(); } else { @@ -225,16 +206,5 @@ protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bo throw new NotImplementedException(); } } - - // emits code that results in ThreadStaticBase referenced in RAX. - // may trash volatile registers. (there are calls to the slow helper and possibly to platform's TLS support) - private static void EmitInlineTLSAccess(NodeFactory factory, ref X64Emitter encoder) - { - // For factory.Target.IsApplePlatform - // movq _\Var @TLVP(% rip), % rdi - // callq * (% rdi) - - throw new NotImplementedException(); - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 14676fd8fe917f..4495e0322d4a78 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -2177,7 +2177,10 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET } else if (field.IsThreadStatic) { - if ((MethodBeingCompiled.Context.Target.IsWindows || MethodBeingCompiled.Context.Target.OperatingSystem == TargetOS.Linux) && MethodBeingCompiled.Context.Target.Architecture == TargetArchitecture.X64) + var target = MethodBeingCompiled.Context.Target; + if ((target.IsWindows && target.Architecture is TargetArchitecture.X64) || + ((target.OperatingSystem == TargetOS.Linux) && + (target.Architecture is TargetArchitecture.X64 or TargetArchitecture.ARM64))) { ISortableSymbolNode index = _compilation.NodeFactory.TypeThreadStaticIndex((MetadataType)field.OwningType); if (index is TypeThreadStaticIndexNode ti) From c2eae1b2163e00866237836cea3608eb5737d080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 16 Feb 2024 06:09:47 +0900 Subject: [PATCH 069/158] Avoid double writes to PDB in NAOT publish (#98342) We currently let the publish logic do its things unaware of what PublishAot will do and then copy over native bits. In general, I wonder if we should switch to the same mechanism that R2R, ILLink, or Single file publishing use - have SDK [be aware of the magic target](https://github.com/dotnet/sdk/blob/95025c62f4e260540c1257627616641e9670501e/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Publish.targets#L453-L460) and then modify ResolvedFileToPublish items as needed. --- .../BuildIntegration/Microsoft.NETCore.Native.Publish.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets index c407a8de6f16dd..e9a4424e819a23 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Publish.targets @@ -24,6 +24,7 @@ <_ResolvedCopyLocalPublishAssets Remove="@(_AssembliesToSkipPublish)" /> <_ResolvedCopyLocalPublishAssets Include="@(_LinkedResolvedAssemblies)" /> + <_DebugSymbolsIntermediatePath Remove="@(_DebugSymbolsIntermediatePath)" /> From 8cb1f79fd66850e233a4b756aa0b5407e64f1105 Mon Sep 17 00:00:00 2001 From: Jeremy Barton Date: Thu, 15 Feb 2024 13:13:33 -0800 Subject: [PATCH 070/158] Change GetNameInfo early return to linear flow --- .../libs/System.Security.Cryptography.Native/openssl.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native/openssl.c b/src/native/libs/System.Security.Cryptography.Native/openssl.c index 73822045895425..9e57a8413ca373 100644 --- a/src/native/libs/System.Security.Cryptography.Native/openssl.c +++ b/src/native/libs/System.Security.Cryptography.Native/openssl.c @@ -668,13 +668,11 @@ BIO* CryptoNative_GetX509NameInfo(X509* x509, int32_t nameType, int32_t forIssue if (sizeof(szOidUpn) == cchLocalOid && 0 == strncmp(localOid, szOidUpn, sizeof(szOidUpn))) { - // OTHERNAME->ASN1_TYPE->union.field - if (!value->value) + if (value->value) { - return NULL; + // OTHERNAME->ASN1_TYPE->union.field + str = value->value->value.asn1_string; } - - str = value->value->value.asn1_string; } } From 4d37918070377e2518970bf7ed3df7e89c40e2de Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Thu, 15 Feb 2024 22:58:53 +0100 Subject: [PATCH 071/158] Fix debug info emitted for VLT_STK_REG/VLT_FPSTK variable locations (#98479) --- .../Compiler/ObjectWriter/Dwarf/DwarfInfo.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfo.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfo.cs index b1408e3eae3bb0..fa02122c9a8793 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfo.cs @@ -510,7 +510,6 @@ private static void DumpVarLocation(DwarfExpressionBuilder e, VarLoc loc) break; case VarLocType.VLT_STK: case VarLocType.VLT_STK2: - case VarLocType.VLT_FPSTK: case VarLocType.VLT_STK_BYREF: e.OpBReg(loc.B, loc.C); if (loc.LocationType == VarLocType.VLT_STK_BYREF) @@ -531,11 +530,16 @@ private static void DumpVarLocation(DwarfExpressionBuilder e, VarLoc loc) e.OpPiece(); break; case VarLocType.VLT_STK_REG: - e.OpBReg(loc.C, loc.D); + e.OpBReg(loc.B, loc.C); e.OpPiece(); - e.OpReg(loc.B); + e.OpReg(loc.D); e.OpPiece(); break; + case VarLocType.VLT_FPSTK: + // On ARM32 this is used to encode actual registers. This may be wrong for x86 + // which we don't support anyway. + e.OpReg(loc.B); + break; default: // Unsupported Debug.Assert(loc.LocationType != VarLocType.VLT_FIXED_VA); From f2d5b2f84fbac54b0a43e167a0f0c7d5da1aa3c2 Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Thu, 15 Feb 2024 14:36:04 -0800 Subject: [PATCH 072/158] Fix problem with fgExpandQmarkForCastInstOf (#97493) This function expands a nested qmark representing a cast/isinst into multiple blocks of flow. I found a problem, with JitOptRepeat, that exists in non-JitOptRepeat compiles but apparently doesn't lead to problems. The result of the qmark is assigned to a variable. This variable is multiply used during the expansion of the qmarks. This causes problems with subsequent optimizations if the variable already has a known exact type in the importer due to lvaSetClass/lvaUpdateClass. While the variable might have that known type after the full qmark evaluation, it is not guaranteed to have that type in the middle of the evaluation. In particular, the qmark expansion for isinst converts types that don't match the type being tested into null, so the "known type" isn't valid until the value is null. The solution is to create a new temp for the intermediate results, and assign the final result at the end of the expansion. This leads to quite a lot of downstream churn and diffs. E.g., the `fgOptimizeUncondBranchToSimpleCond` optimization doesn't kick in any more on these expansions. --- src/coreclr/jit/gentree.cpp | 4 +-- src/coreclr/jit/morph.cpp | 64 ++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 99a90db42a8fb5..6434cc78c20111 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -19005,7 +19005,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetArrayElementClassHandle(GenTree* array) CORINFO_CLASS_HANDLE Compiler::gtGetFieldClassHandle(CORINFO_FIELD_HANDLE fieldHnd, bool* pIsExact, bool* pIsNonNull) { - CORINFO_CLASS_HANDLE fieldClass = nullptr; + CORINFO_CLASS_HANDLE fieldClass = NO_CLASS_HANDLE; CorInfoType fieldCorType = info.compCompHnd->getFieldType(fieldHnd, &fieldClass); if (fieldCorType == CORINFO_TYPE_CLASS) @@ -19019,7 +19019,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetFieldClassHandle(CORINFO_FIELD_HANDLE fieldH #if DEBUG char fieldNameBuffer[128]; char classNameBuffer[128]; - JITDUMP("Querying runtime about current class of field %s (declared as %s)\n", + JITDUMP("\nQuerying runtime about current class of field %s (declared as %s)\n", eeGetFieldName(fieldHnd, true, fieldNameBuffer, sizeof(fieldNameBuffer)), eeGetClassName(fieldClass, classNameBuffer, sizeof(classNameBuffer))); #endif // DEBUG diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index ebf5244dedc43f..0c44bbad973ea6 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -14517,12 +14517,11 @@ GenTreeQmark* Compiler::fgGetTopLevelQmark(GenTree* expr, GenTree** ppDst /* = N // // Notes: // -// For a castclass helper call, -// Importer creates the following tree: +// For a castclass helper call, importer creates the following tree: // tmp = (op1 == null) ? op1 : ((*op1 == (cse = op2, cse)) ? op1 : helper()); // // This method splits the qmark expression created by the importer into the -// following blocks: (block, asg, cond1, cond2, helper, remainder) +// following blocks: (block, asg, cond1, cond2, helper, remainder). // Notice that op1 is the result for both the conditions. So we coalesce these // assignments into a single block instead of two blocks resulting a nested diamond. // @@ -14533,17 +14532,24 @@ GenTreeQmark* Compiler::fgGetTopLevelQmark(GenTree* expr, GenTree** ppDst /* = N // block-->asg-->cond1--+-->cond2--+-->helper--+-->remainder // // We expect to achieve the following codegen: -// mov rsi, rdx tmp = op1 // asgBlock -// test rsi, rsi goto skip if tmp == null ? // cond1Block +// mov rsi, rdx tmp2 = op1 // asgBlock +// test rsi, rsi goto skip if tmp2 == null ? // cond1Block // je SKIP -// mov rcx, 0x76543210 cns = op2 // cond2Block -// cmp qword ptr [rsi], rcx goto skip if *tmp == op2 +// mov rcx, 0x76543210 cns = op2 // cond2Block +// cmp qword ptr [rsi], rcx goto skip if *tmp2 == op2 // je SKIP -// call CORINFO_HELP_CHKCASTCLASS_SPECIAL tmp = helper(cns, tmp) // helperBlock +// call CORINFO_HELP_CHKCASTCLASS_SPECIAL tmp2 = helper(cns, tmp2) // helperBlock // mov rsi, rax -// SKIP: // remainderBlock +// SKIP: // remainderBlock +// mov rdi, rsi tmp = tmp2 // tmp has the result. // +// Note that we can't use `tmp` during the computation of the result: we must create a new temp, +// and only assign `tmp` to the final value. This is because `tmp` may already have been annotated +// via lvaSetClass/lvaUpdateClass as having a known type. This is only true after the full expansion, +// where any other type gets converted to null. If we used `tmp` during the expansion, then it would +// appear to subsequent optimizations that cond2Block (where the type is checked) is unnecessary. +// bool Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt) { #ifdef DEBUG @@ -14585,10 +14591,10 @@ bool Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt) } else { - // This is a rare case that arises when we are doing minopts and encounter isinst of null - // gtFoldExpr was still is able to optimize away part of the tree (but not all). + // This is a rare case that arises when we are doing minopts and encounter isinst of null. + // gtFoldExpr was still able to optimize away part of the tree (but not all). // That means it does not match our pattern. - + // // Rather than write code to handle this case, just fake up some nodes to make it match the common // case. Synthesize a comparison that is always true, and for the result-on-true, use the // entire subtree we expected to be the nested question op. @@ -14604,7 +14610,7 @@ bool Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt) // block ... asgBlock ... cond1Block ... cond2Block ... helperBlock ... remainderBlock // // We need to remember flags that exist on 'block' that we want to propagate to 'remainderBlock', - // if they are going to be cleared by fgSplitBlockAfterStatement(). We currently only do this only + // if they are going to be cleared by fgSplitBlockAfterStatement(). We currently only do this // for the GC safe point bit, the logic being that if 'block' was marked gcsafe, then surely // remainderBlock will still be GC safe. BasicBlockFlags propagateFlags = block->GetFlagsRaw() & BBF_GC_SAFE_POINT; @@ -14669,6 +14675,7 @@ bool Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt) // { // [weight 0.5 * <100 - likelihood of FastType>] // } + // } // cond2Block->inheritWeightPercentage(cond1Block, 50); helperBlock->inheritWeightPercentage(cond2Block, nestedQmarkElseLikelihood); @@ -14683,14 +14690,12 @@ bool Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt) jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo()); fgInsertStmtAtEnd(cond2Block, jmpStmt); - unsigned dstLclNum = dst->AsLclVarCommon()->GetLclNum(); + unsigned tmp2 = lvaGrabTemp(false DEBUGARG("CastInstOf QMark result")); + lvaGetDesc(tmp2)->lvType = dst->TypeGet(); - // AsgBlock should get tmp = op1. - GenTree* trueExprStore = - dst->OperIs(GT_STORE_LCL_FLD) - ? gtNewStoreLclFldNode(dstLclNum, dst->TypeGet(), dst->AsLclFld()->GetLclOffs(), trueExpr) - : gtNewStoreLclVarNode(dstLclNum, trueExpr)->AsLclVarCommon(); - Statement* trueStmt = fgNewStmtFromTree(trueExprStore, stmt->GetDebugInfo()); + // AsgBlock should get tmp2 = op1. + GenTree* trueExprStore = gtNewStoreLclVarNode(tmp2, trueExpr)->AsLclVarCommon(); + Statement* trueStmt = fgNewStmtFromTree(trueExprStore, stmt->GetDebugInfo()); fgInsertStmtAtEnd(asgBlock, trueStmt); // Since we are adding helper in the JTRUE false path, reverse the cond2 and add the helper. @@ -14706,14 +14711,21 @@ bool Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt) } else { - GenTree* helperExprStore = - dst->OperIs(GT_STORE_LCL_FLD) - ? gtNewStoreLclFldNode(dstLclNum, dst->TypeGet(), dst->AsLclFld()->GetLclOffs(), true2Expr) - : gtNewStoreLclVarNode(dstLclNum, true2Expr)->AsLclVarCommon(); - Statement* helperStmt = fgNewStmtFromTree(helperExprStore, stmt->GetDebugInfo()); + GenTree* helperExprStore = gtNewStoreLclVarNode(tmp2, true2Expr)->AsLclVarCommon(); + Statement* helperStmt = fgNewStmtFromTree(helperExprStore, stmt->GetDebugInfo()); fgInsertStmtAtEnd(helperBlock, helperStmt); } + // RemainderBlock should get tmp = tmp2. + GenTree* tmp2CopyLcl = gtNewLclvNode(tmp2, dst->TypeGet()); + unsigned dstLclNum = dst->AsLclVarCommon()->GetLclNum(); + GenTree* resultCopy = + dst->OperIs(GT_STORE_LCL_FLD) + ? gtNewStoreLclFldNode(dstLclNum, dst->TypeGet(), dst->AsLclFld()->GetLclOffs(), tmp2CopyLcl) + : gtNewStoreLclVarNode(dstLclNum, tmp2CopyLcl)->AsLclVarCommon(); + Statement* resultCopyStmt = fgNewStmtFromTree(resultCopy, stmt->GetDebugInfo()); + fgInsertStmtAtBeg(remainderBlock, resultCopyStmt); + // Finally remove the nested qmark stmt. fgRemoveStmt(block, stmt); @@ -15425,7 +15437,7 @@ PhaseStatus Compiler::fgRetypeImplicitByRefArgs() (nonCallAppearances <= varDsc->lvFieldCnt)); #ifdef DEBUG - // Above is a profitability heurisic; either value of + // Above is a profitability heuristic; either value of // undoPromotion should lead to correct code. So, // under stress, make different decisions at times. if (compStressCompile(STRESS_BYREF_PROMOTION, 25)) From aeecdb8c00e1a673838b22168c264e896d9446e8 Mon Sep 17 00:00:00 2001 From: Ruihan-Yin <107431934+Ruihan-Yin@users.noreply.github.com> Date: Thu, 15 Feb 2024 14:40:28 -0800 Subject: [PATCH 073/158] Expose AVX512F embedded rounding intrinsics. (#97415) * Expose embedded rounding related scalar intrinsic APIs * Expose embedded rounding related arithmatic intrinsic APIs * Ensure the new APIs are properly lowered * Bug fixes * Expose embedded rounding casting APIs * Expose arithmetic embedded rounding unit tests * Add a test template for embedded rounding APIs, this will be enough to cover all the binary APIs including vector and scalar operations. * Add template for unary ops * Expose all the embedded rounding unit tests generated by the templates * Expose embedded rounding casting APIs unit tests * Expose handwritten unit tests for embedded rounding APIs with special input arg lists. * Bug fixes: 1. ConvertToVector256Int32/UInt32 use special code gen path, adding a fallback path when embedded rounding is activated and the control byte is not constant. * Bug fix: Fix wrong data type in the API definition. * formatting * Update API documents for embedded rounding APIs. * resolve conflicts with #97569 * formatting * bug fix and remove un-needed SAE related intrinsics * resolve comments: 1. update the arg lists for genHWIntrinsic_R_RM * resolve comments: Add jumptable fallback to non-table driven embedded rounding intrinsics. * resolve comments: 1. remove some redundent checks on embedded rounding intrinsics * Bug fix: pass the correct operand GenTree node, when emitting the fallback for embedded rounding intrinsics. * formatting * revert an unexpected change. * 1.Resolve comments: 2. Added FMA intrinsics with embedded rounding and unit tests. * Expose the rest of embedded rounding APIs * formatting * Ensure the control byte local is assigned to the correct register. --- src/coreclr/jit/codegen.h | 20 +- src/coreclr/jit/emitxarch.cpp | 28 +- src/coreclr/jit/emitxarch.h | 11 +- src/coreclr/jit/gentree.cpp | 89 ++ src/coreclr/jit/gentree.h | 1 + src/coreclr/jit/hwintrinsic.h | 15 - src/coreclr/jit/hwintrinsiccodegenxarch.cpp | 265 ++++-- src/coreclr/jit/hwintrinsiclistxarch.h | 67 +- src/coreclr/jit/lowerxarch.cpp | 31 +- src/coreclr/jit/lsraxarch.cpp | 14 +- .../X86/Avx512DQ.PlatformNotSupported.cs | 41 +- .../System/Runtime/Intrinsics/X86/Avx512DQ.cs | 42 + .../X86/Avx512F.PlatformNotSupported.cs | 350 +++++++- .../System/Runtime/Intrinsics/X86/Avx512F.cs | 340 +++++++- .../ref/System.Runtime.Intrinsics.cs | 75 +- .../GenerateHWIntrinsicTests_X86.cs | 187 +++++ .../Shared/SimpleBinOpEmbRounding.template | 71 +- .../Shared/SimpleTernOpEmbRounding.template | 392 +++++++++ .../Shared/SimpleUnaryOpEmbRounding.template | 341 ++++++++ .../Avx512F/Avx512F_handwritten_r.csproj | 2 + .../Avx512F/Avx512F_handwritten_ro.csproj | 2 + .../Avx512F/EmbeddedRounding.Double.cs | 716 ++++++++++++++++ .../Avx512F/EmbeddedRounding.Single.cs | 784 ++++++++++++++++++ 23 files changed, 3737 insertions(+), 147 deletions(-) create mode 100644 src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleTernOpEmbRounding.template create mode 100644 src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleUnaryOpEmbRounding.template create mode 100644 src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/EmbeddedRounding.Double.cs create mode 100644 src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/EmbeddedRounding.Single.cs diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index aee8ecc0c4e590..afd9e42a9d2cdd 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -976,13 +976,23 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #ifdef FEATURE_HW_INTRINSICS void genHWIntrinsic(GenTreeHWIntrinsic* node); #if defined(TARGET_XARCH) - void genHWIntrinsic_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, regNumber reg, GenTree* rmOp); + void genHWIntrinsic_R_RM(GenTreeHWIntrinsic* node, + instruction ins, + emitAttr attr, + regNumber reg, + GenTree* rmOp, + insOpts instOptions = INS_OPTS_NONE); void genHWIntrinsic_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival); void genHWIntrinsic_R_R_RM(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, insOpts instOptions); void genHWIntrinsic_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival); void genHWIntrinsic_R_R_RM_R(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr); - void genHWIntrinsic_R_R_R_RM( - instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg, GenTree* op3); + void genHWIntrinsic_R_R_R_RM(instruction ins, + emitAttr attr, + regNumber targetReg, + regNumber op1Reg, + regNumber op2Reg, + GenTree* op3, + insOpts instOptions = INS_OPTS_NONE); void genHWIntrinsic_R_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival); void genBaseIntrinsic(GenTreeHWIntrinsic* node); @@ -994,7 +1004,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void genAvxFamilyIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions); void genAESIntrinsic(GenTreeHWIntrinsic* node); void genBMI1OrBMI2Intrinsic(GenTreeHWIntrinsic* node, insOpts instOptions); - void genFMAIntrinsic(GenTreeHWIntrinsic* node); + void genFMAIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions); void genPermuteVar2x(GenTreeHWIntrinsic* node); void genLZCNTIntrinsic(GenTreeHWIntrinsic* node); void genPCLMULQDQIntrinsic(GenTreeHWIntrinsic* node); @@ -1008,6 +1018,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX regNumber baseReg, regNumber offsReg, HWIntrinsicSwitchCaseBody emitSwCase); + + void genNonTableDrivenHWIntrinsicsJumpTableFallback(GenTreeHWIntrinsic* node, GenTree* lastOp); #endif // defined(TARGET_XARCH) #ifdef TARGET_ARM64 diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index d5dc2fd9530a62..9e80da3bfaf9ab 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -6565,7 +6565,7 @@ void emitter::emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regN * Add an instruction with two register operands. */ -void emitter::emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2) +void emitter::emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, insOpts instOptions) { if (IsMovInstruction(ins)) { @@ -6587,6 +6587,13 @@ void emitter::emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNum id->idReg1(reg1); id->idReg2(reg2); + if ((instOptions & INS_OPTS_EVEX_b_MASK) != INS_OPTS_NONE) + { + // if EVEX.b needs to be set in this path, then it should be embedded rounding. + assert(UseEvexEncoding()); + id->idSetEvexbContext(instOptions); + } + UNATIVE_OFFSET sz = emitInsSizeRR(id); id->idCodeSize(sz); @@ -8545,20 +8552,32 @@ void emitter::emitIns_SIMD_R_R_R_C(instruction ins, // op1Reg -- The register of the first operand // op2Reg -- The register of the second operand // op3Reg -- The register of the second operand +// instOptions - The options that modify how the instruction is generated // -void emitter::emitIns_SIMD_R_R_R_R( - instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg, regNumber op3Reg) +void emitter::emitIns_SIMD_R_R_R_R(instruction ins, + emitAttr attr, + regNumber targetReg, + regNumber op1Reg, + regNumber op2Reg, + regNumber op3Reg, + insOpts instOptions) { if (IsFMAInstruction(ins) || IsPermuteVar2xInstruction(ins) || IsAVXVNNIInstruction(ins)) { assert(UseSimdEncoding()); + if (instOptions != INS_OPTS_NONE) + { + // insOpts is currently available only in EVEX encoding. + assert(UseEvexEncoding()); + } + // Ensure we aren't overwriting op2 or op3 assert((op2Reg != targetReg) || (op1Reg == targetReg)); assert((op3Reg != targetReg) || (op1Reg == targetReg)); emitIns_Mov(INS_movaps, attr, targetReg, op1Reg, /* canSkip */ true); - emitIns_R_R_R(ins, attr, targetReg, op2Reg, op3Reg); + emitIns_R_R_R(ins, attr, targetReg, op2Reg, op3Reg, instOptions); } else if (UseSimdEncoding()) { @@ -11659,6 +11678,7 @@ void emitter::emitDispIns( default: { printf("%s, %s", emitRegName(id->idReg1(), attr), emitRegName(id->idReg2(), attr)); + emitDispEmbRounding(id); break; } } diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 2877346ab4fd36..4554a892201f95 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -635,7 +635,7 @@ void emitIns_R_I(instruction ins, void emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regNumber srgReg, bool canSkip); -void emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2); +void emitIns_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, insOpts instOptions = INS_OPTS_NONE); void emitIns_R_R_I(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, int ival); @@ -839,8 +839,13 @@ void emitIns_SIMD_R_R_R_C(instruction ins, regNumber op2Reg, CORINFO_FIELD_HANDLE fldHnd, int offs); -void emitIns_SIMD_R_R_R_R( - instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg, regNumber op3Reg); +void emitIns_SIMD_R_R_R_R(instruction ins, + emitAttr attr, + regNumber targetReg, + regNumber op1Reg, + regNumber op2Reg, + regNumber op3Reg, + insOpts instOptions = INS_OPTS_NONE); void emitIns_SIMD_R_R_R_S( instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg, int varx, int offs); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 6434cc78c20111..ad68efef307b77 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -26484,6 +26484,95 @@ bool GenTreeHWIntrinsic::OperIsBitwiseHWIntrinsic() const return Oper == GT_AND || Oper == GT_OR || Oper == GT_XOR || Oper == GT_AND_NOT; } +//------------------------------------------------------------------------ +// OperIsEmbRoundingEnabled: Is this HWIntrinsic a node with embedded rounding feature. +// +// Return Value: +// Whether "this" is a node with embedded rounding feature. +// +bool GenTreeHWIntrinsic::OperIsEmbRoundingEnabled() const +{ +#if defined(TARGET_XARCH) + NamedIntrinsic intrinsicId = GetHWIntrinsicId(); + + if (!HWIntrinsicInfo::IsEmbRoundingCompatible(intrinsicId)) + { + return false; + } + + size_t numArgs = GetOperandCount(); + switch (intrinsicId) + { + // these intrinsics only have the embedded rounding enabled implementation. + case NI_AVX512F_AddScalar: + case NI_AVX512F_DivideScalar: + case NI_AVX512F_MultiplyScalar: + case NI_AVX512F_SubtractScalar: + case NI_AVX512F_SqrtScalar: + { + return true; + } + + case NI_AVX512F_FusedMultiplyAdd: + case NI_AVX512F_FusedMultiplyAddScalar: + case NI_AVX512F_FusedMultiplyAddNegated: + case NI_AVX512F_FusedMultiplyAddNegatedScalar: + case NI_AVX512F_FusedMultiplyAddSubtract: + case NI_AVX512F_FusedMultiplySubtract: + case NI_AVX512F_FusedMultiplySubtractAdd: + case NI_AVX512F_FusedMultiplySubtractNegated: + case NI_AVX512F_FusedMultiplySubtractNegatedScalar: + case NI_AVX512F_FusedMultiplySubtractScalar: + { + return numArgs == 4; + } + + case NI_AVX512F_Add: + case NI_AVX512F_Divide: + case NI_AVX512F_Multiply: + case NI_AVX512F_Subtract: + + case NI_AVX512F_Scale: + case NI_AVX512F_ScaleScalar: + + case NI_AVX512F_ConvertScalarToVector128Single: +#if defined(TARGET_AMD64) + case NI_AVX512F_X64_ConvertScalarToVector128Double: + case NI_AVX512F_X64_ConvertScalarToVector128Single: +#endif // TARGET_AMD64 + { + return numArgs == 3; + } + + case NI_AVX512F_Sqrt: + case NI_AVX512F_ConvertToInt32: + case NI_AVX512F_ConvertToUInt32: + case NI_AVX512F_ConvertToVector256Int32: + case NI_AVX512F_ConvertToVector256Single: + case NI_AVX512F_ConvertToVector256UInt32: + case NI_AVX512F_ConvertToVector512Single: + case NI_AVX512F_ConvertToVector512UInt32: + case NI_AVX512F_ConvertToVector512Int32: +#if defined(TARGET_AMD64) + case NI_AVX512F_X64_ConvertToInt64: + case NI_AVX512F_X64_ConvertToUInt64: +#endif // TARGET_AMD64 + case NI_AVX512DQ_ConvertToVector256Single: + case NI_AVX512DQ_ConvertToVector512Double: + case NI_AVX512DQ_ConvertToVector512Int64: + case NI_AVX512DQ_ConvertToVector512UInt64: + { + return numArgs == 2; + } + + default: + unreached(); + } +#else // !TARGET_XARCH + return false; +#endif // TARGET_XARCH +} + //------------------------------------------------------------------------------ // OperRequiresAsgFlag : Check whether the operation requires GTF_ASG flag regardless // of the children's flags. diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 98d0039111b6ac..1bd7ce27004fe6 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -6387,6 +6387,7 @@ struct GenTreeHWIntrinsic : public GenTreeJitIntrinsic bool OperIsBroadcastScalar() const; bool OperIsCreateScalarUnsafe() const; bool OperIsBitwiseHWIntrinsic() const; + bool OperIsEmbRoundingEnabled() const; bool OperRequiresAsgFlag() const; bool OperRequiresCallFlag() const; diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h index 4df1aace5287a4..15256ea22e93b3 100644 --- a/src/coreclr/jit/hwintrinsic.h +++ b/src/coreclr/jit/hwintrinsic.h @@ -607,21 +607,6 @@ struct HWIntrinsicInfo HWIntrinsicFlag flags = lookupFlags(id); return (flags & HW_Flag_EmbMaskingIncompatible) == 0; } - - static size_t EmbRoundingArgPos(NamedIntrinsic id) - { - // This helper function returns the expected position, - // where the embedded rounding control argument should be. - assert(IsEmbRoundingCompatible(id)); - switch (id) - { - case NI_AVX512F_Add: - return 3; - - default: - unreached(); - } - } #endif // TARGET_XARCH static bool CanBenefitFromConstantProp(NamedIntrinsic id) diff --git a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp index 6e85d98fea1223..5e44772e7115ac 100644 --- a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp @@ -253,57 +253,82 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) } } - if (HWIntrinsicInfo::IsEmbRoundingCompatible(intrinsicId)) + if (node->OperIsEmbRoundingEnabled()) { - assert(isTableDriven); - size_t expectedArgNum = HWIntrinsicInfo::EmbRoundingArgPos(intrinsicId); + GenTree* lastOp = node->Op(numArgs); - if (numArgs == expectedArgNum) - { - GenTree* lastOp = node->Op(numArgs); - - // Now that we've extracted the rounding mode, we'll remove the - // last operand, adjust the arg count, and continue. This allows - // us to reuse all the existing logic without having to add new - // specialized handling everywhere. + // Now that we've extracted the rounding mode, we'll remove the + // last operand, adjust the arg count, and continue. This allows + // us to reuse all the existing logic without having to add new + // specialized handling everywhere. - switch (numArgs) + switch (numArgs) + { + case 2: { - case 3: - { - numArgs = 2; - node->ResetHWIntrinsicId(intrinsicId, compiler, node->Op(1), node->Op(2)); - break; - } - - default: - { - unreached(); - } + numArgs = 1; + node->ResetHWIntrinsicId(intrinsicId, compiler, node->Op(1)); + break; } - if (lastOp->isContained()) + case 3: { - assert(lastOp->IsCnsIntOrI()); + numArgs = 2; + node->ResetHWIntrinsicId(intrinsicId, compiler, node->Op(1), node->Op(2)); + break; + } - int8_t mode = static_cast(lastOp->AsIntCon()->IconValue()); - instOptions = AddEmbRoundingMode(instOptions, mode); + case 4: + { + numArgs = 3; + node->ResetHWIntrinsicId(intrinsicId, compiler, node->Op(1), node->Op(2), node->Op(3)); + break; } - else + + default: { - var_types baseType = node->GetSimdBaseType(); + unreached(); + } + } - instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType); - assert(ins != INS_invalid); + if (lastOp->isContained()) + { + assert(lastOp->IsCnsIntOrI()); + + int8_t mode = static_cast(lastOp->AsIntCon()->IconValue()); + instOptions = AddEmbRoundingMode(instOptions, mode); + } + else + { + var_types baseType = node->GetSimdBaseType(); - emitAttr simdSize = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->GetSimdSize())); - assert(simdSize != 0); + instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType); + assert(ins != INS_invalid); - genConsumeMultiOpOperands(node); - genConsumeRegs(lastOp); + emitAttr simdSize = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->GetSimdSize())); + assert(simdSize != 0); + genConsumeMultiOpOperands(node); + genConsumeRegs(lastOp); + + if (isTableDriven) + { switch (numArgs) { + case 1: + { + regNumber targetReg = node->GetRegNum(); + GenTree* rmOp = node->Op(1); + auto emitSwCase = [&](int8_t i) { + insOpts newInstOptions = AddEmbRoundingMode(instOptions, i); + genHWIntrinsic_R_RM(node, ins, simdSize, targetReg, rmOp, newInstOptions); + }; + regNumber baseReg = node->ExtractTempReg(); + regNumber offsReg = node->GetSingleTempReg(); + genHWIntrinsicJumpTableFallback(intrinsicId, lastOp->GetRegNum(), baseReg, offsReg, + emitSwCase); + break; + } case 2: { auto emitSwCase = [&](int8_t i) { @@ -322,10 +347,15 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) unreached(); } } - - genProduceReg(node); - return; } + else + { + // There are a few embedded rounding intrinsics that need to be emitted with special handling. + genNonTableDrivenHWIntrinsicsJumpTableFallback(node, lastOp); + } + + genProduceReg(node); + return; } } } @@ -396,7 +426,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) } else { - genHWIntrinsic_R_RM(node, ins, simdSize, targetReg, op1); + genHWIntrinsic_R_RM(node, ins, simdSize, targetReg, op1, instOptions); } } break; @@ -715,7 +745,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) genBMI1OrBMI2Intrinsic(node, instOptions); break; case InstructionSet_FMA: - genFMAIntrinsic(node); + genFMAIntrinsic(node, instOptions); break; case InstructionSet_LZCNT: case InstructionSet_LZCNT_X64: @@ -749,13 +779,23 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) // attr - The emit attribute for the instruction being generated // reg - The register // rmOp - The register/memory operand node -// +// instOptions - the existing intOpts void CodeGen::genHWIntrinsic_R_RM( - GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, regNumber reg, GenTree* rmOp) + GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, regNumber reg, GenTree* rmOp, insOpts instOptions) { emitter* emit = GetEmitter(); OperandDesc rmOpDesc = genOperandDesc(rmOp); + if (((instOptions & INS_OPTS_EVEX_b_MASK) != 0) && (rmOpDesc.GetKind() == OperandKind::Reg)) + { + // As embedded rounding only appies in R_R case, we can skip other checks for different paths. + regNumber op1Reg = rmOp->GetRegNum(); + assert(op1Reg != REG_NA); + + emit->emitIns_R_R(ins, attr, reg, op1Reg, instOptions); + return; + } + if (rmOpDesc.IsContained()) { assert(HWIntrinsicInfo::SupportsContainment(node->GetHWIntrinsicId())); @@ -1081,9 +1121,15 @@ void CodeGen::genHWIntrinsic_R_R_RM_R(GenTreeHWIntrinsic* node, instruction ins, // op1Reg - The register of the first operand // op2Reg - The register of the second operand // op3 - The third operand +// instOptions - The options that modify how the instruction is generated // -void CodeGen::genHWIntrinsic_R_R_R_RM( - instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg, GenTree* op3) +void CodeGen::genHWIntrinsic_R_R_R_RM(instruction ins, + emitAttr attr, + regNumber targetReg, + regNumber op1Reg, + regNumber op2Reg, + GenTree* op3, + insOpts instOptions) { assert(targetReg != REG_NA); assert(op1Reg != REG_NA); @@ -1092,6 +1138,16 @@ void CodeGen::genHWIntrinsic_R_R_R_RM( emitter* emit = GetEmitter(); OperandDesc op3Desc = genOperandDesc(op3); + if (((instOptions & INS_OPTS_EVEX_b_MASK) != 0) && (op3Desc.GetKind() == OperandKind::Reg)) + { + // As embedded rounding only appies in R_R case, we can skip other checks for different paths. + regNumber op3Reg = op3->GetRegNum(); + assert(op3Reg != REG_NA); + + emit->emitIns_SIMD_R_R_R_R(ins, attr, targetReg, op1Reg, op2Reg, op3Desc.GetReg(), instOptions); + return; + } + switch (op3Desc.GetKind()) { case OperandKind::ClsVar: @@ -1285,6 +1341,113 @@ void CodeGen::genHWIntrinsicJumpTableFallback(NamedIntrinsic intrinsi genDefineTempLabel(switchTableEnd); } +void CodeGen::genNonTableDrivenHWIntrinsicsJumpTableFallback(GenTreeHWIntrinsic* node, GenTree* lastOp) +{ + NamedIntrinsic intrinsicId = node->GetHWIntrinsicId(); + HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsicId); + + assert(HWIntrinsicInfo::IsEmbRoundingCompatible(intrinsicId)); + assert(!lastOp->isContained()); + assert(!genIsTableDrivenHWIntrinsic(intrinsicId, category)); + + var_types baseType = node->GetSimdBaseType(); + emitAttr attr = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->GetSimdSize())); + var_types targetType = node->TypeGet(); + instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType); + regNumber targetReg = node->GetRegNum(); + + insOpts instOptions = INS_OPTS_NONE; + switch (intrinsicId) + { + case NI_AVX512F_ConvertToVector256Int32: + case NI_AVX512F_ConvertToVector256UInt32: + { + // This intrinsic has several overloads, only the ones with floating number inputs should reach this part. + assert(varTypeIsFloating(baseType)); + GenTree* rmOp = node->Op(1); + auto emitSwCase = [&](int8_t i) { + insOpts newInstOptions = AddEmbRoundingMode(instOptions, i); + genHWIntrinsic_R_RM(node, ins, attr, targetReg, rmOp, newInstOptions); + }; + regNumber baseReg = node->ExtractTempReg(); + regNumber offsReg = node->GetSingleTempReg(); + genHWIntrinsicJumpTableFallback(intrinsicId, lastOp->GetRegNum(), baseReg, offsReg, emitSwCase); + break; + } + + case NI_AVX512F_ConvertToInt32: + case NI_AVX512F_ConvertToUInt32: +#if defined(TARGET_AMD64) + case NI_AVX512F_X64_ConvertToInt64: + case NI_AVX512F_X64_ConvertToUInt64: +#endif // TARGET_AMD64 + { + assert(varTypeIsFloating(baseType)); + attr = emitTypeSize(targetType); + GenTree* rmOp = node->Op(1); + + auto emitSwCase = [&](int8_t i) { + insOpts newInstOptions = AddEmbRoundingMode(instOptions, i); + genHWIntrinsic_R_RM(node, ins, attr, targetReg, rmOp, newInstOptions); + }; + regNumber baseReg = node->ExtractTempReg(); + regNumber offsReg = node->GetSingleTempReg(); + genHWIntrinsicJumpTableFallback(intrinsicId, lastOp->GetRegNum(), baseReg, offsReg, emitSwCase); + break; + } + + case NI_AVX512F_X64_ConvertScalarToVector128Single: + case NI_AVX512F_X64_ConvertScalarToVector128Double: + { + assert(varTypeIsLong(baseType)); + auto emitSwCase = [&](int8_t i) { + insOpts newInstOptions = AddEmbRoundingMode(instOptions, i); + genHWIntrinsic_R_R_RM(node, ins, EA_8BYTE, newInstOptions); + }; + regNumber baseReg = node->ExtractTempReg(); + regNumber offsReg = node->GetSingleTempReg(); + genHWIntrinsicJumpTableFallback(intrinsicId, lastOp->GetRegNum(), baseReg, offsReg, emitSwCase); + break; + } + + case NI_AVX512F_FusedMultiplyAdd: + case NI_AVX512F_FusedMultiplyAddScalar: + case NI_AVX512F_FusedMultiplyAddNegated: + case NI_AVX512F_FusedMultiplyAddNegatedScalar: + case NI_AVX512F_FusedMultiplyAddSubtract: + case NI_AVX512F_FusedMultiplySubtract: + case NI_AVX512F_FusedMultiplySubtractAdd: + case NI_AVX512F_FusedMultiplySubtractNegated: + case NI_AVX512F_FusedMultiplySubtractNegatedScalar: + case NI_AVX512F_FusedMultiplySubtractScalar: + { + // For FMA intrinsics, since it is not possible to get any contained operand in this case: embedded rounding + // is limited in register-to-register form, and the control byte is dynamic, we don't need to do any swap. + assert(HWIntrinsicInfo::IsFmaIntrinsic(intrinsicId)); + + GenTree* op1 = node->Op(1); + GenTree* op2 = node->Op(2); + GenTree* op3 = node->Op(3); + + regNumber op1Reg = op1->GetRegNum(); + regNumber op2Reg = op2->GetRegNum(); + + auto emitSwCase = [&](int8_t i) { + insOpts newInstOptions = AddEmbRoundingMode(instOptions, i); + genHWIntrinsic_R_R_R_RM(ins, attr, targetReg, op1Reg, op2Reg, op3, newInstOptions); + }; + regNumber baseReg = node->ExtractTempReg(); + regNumber offsReg = node->GetSingleTempReg(); + genHWIntrinsicJumpTableFallback(intrinsicId, lastOp->GetRegNum(), baseReg, offsReg, emitSwCase); + break; + } + + default: + unreached(); + break; + } +} + //------------------------------------------------------------------------ // genBaseIntrinsic: Generates the code for a base hardware intrinsic node // @@ -1988,7 +2151,7 @@ void CodeGen::genAvxFamilyIntrinsic(GenTreeHWIntrinsic* node, insOpts instOption if (HWIntrinsicInfo::IsFmaIntrinsic(intrinsicId)) { - genFMAIntrinsic(node); + genFMAIntrinsic(node, instOptions); return; } @@ -2550,8 +2713,10 @@ void CodeGen::genAvxFamilyIntrinsic(GenTreeHWIntrinsic* node, insOpts instOption break; } + case NI_AVX512F_ConvertToInt32: case NI_AVX512F_ConvertToUInt32: case NI_AVX512F_ConvertToUInt32WithTruncation: + case NI_AVX512F_X64_ConvertToInt64: case NI_AVX512F_X64_ConvertToUInt64: case NI_AVX512F_X64_ConvertToUInt64WithTruncation: { @@ -2559,7 +2724,7 @@ void CodeGen::genAvxFamilyIntrinsic(GenTreeHWIntrinsic* node, insOpts instOption emitAttr attr = emitTypeSize(targetType); instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType); - genHWIntrinsic_R_RM(node, ins, attr, targetReg, node->Op(1)); + genHWIntrinsic_R_RM(node, ins, attr, targetReg, op1, instOptions); break; } @@ -2571,7 +2736,7 @@ void CodeGen::genAvxFamilyIntrinsic(GenTreeHWIntrinsic* node, insOpts instOption if (varTypeIsFloating(baseType)) { instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType); - genHWIntrinsic_R_RM(node, ins, attr, targetReg, op1); + genHWIntrinsic_R_RM(node, ins, attr, targetReg, op1, instOptions); break; } FALLTHROUGH; @@ -2623,7 +2788,7 @@ void CodeGen::genAvxFamilyIntrinsic(GenTreeHWIntrinsic* node, insOpts instOption case NI_AVX512F_X64_ConvertScalarToVector128Double: case NI_AVX512F_X64_ConvertScalarToVector128Single: { - assert(baseType == TYP_ULONG); + assert(baseType == TYP_ULONG || baseType == TYP_LONG); instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType); genHWIntrinsic_R_R_RM(node, ins, EA_8BYTE, instOptions); break; @@ -2773,7 +2938,7 @@ void CodeGen::genBMI1OrBMI2Intrinsic(GenTreeHWIntrinsic* node, insOpts instOptio // Arguments: // node - The hardware intrinsic node // -void CodeGen::genFMAIntrinsic(GenTreeHWIntrinsic* node) +void CodeGen::genFMAIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions) { NamedIntrinsic intrinsicId = node->GetHWIntrinsicId(); assert(HWIntrinsicInfo::IsFmaIntrinsic(intrinsicId)); @@ -2880,7 +3045,7 @@ void CodeGen::genFMAIntrinsic(GenTreeHWIntrinsic* node) } assert(ins != INS_invalid); - genHWIntrinsic_R_R_R_RM(ins, attr, targetReg, emitOp1->GetRegNum(), emitOp2->GetRegNum(), emitOp3); + genHWIntrinsic_R_R_R_RM(ins, attr, targetReg, emitOp1->GetRegNum(), emitOp2->GetRegNum(), emitOp3, instOptions); genProduceReg(node); } diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h index 65df8c14c053e2..3093c9ff71a566 100644 --- a/src/coreclr/jit/hwintrinsiclistxarch.h +++ b/src/coreclr/jit/hwintrinsiclistxarch.h @@ -836,6 +836,7 @@ HARDWARE_INTRINSIC(AVX2, Xor, // AVX512F Intrinsics HARDWARE_INTRINSIC(AVX512F, Abs, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_pabsd, INS_invalid, INS_vpabsq, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(AVX512F, Add, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_paddd, INS_paddd, INS_paddq, INS_paddq, INS_addps, INS_addpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative|HW_Flag_EmbBroadcastCompatible|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, AddScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_addss, INS_addsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, AlignRight32, 64, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_valignd, INS_valignd, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_IMM, HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, AlignRight64, 64, 3, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_valignq, INS_valignq, INS_invalid, INS_invalid}, HW_Category_IMM, HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, And, 64, 2, true, {INS_pand, INS_pand, INS_pand, INS_pand, INS_pand, INS_pand, INS_vpandq, INS_vpandq, INS_andps, INS_andpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative|HW_Flag_EmbBroadcastCompatible) @@ -856,9 +857,10 @@ HARDWARE_INTRINSIC(AVX512F, CompareNotLessThan, HARDWARE_INTRINSIC(AVX512F, CompareNotLessThanOrEqual, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcmpps, INS_vcmppd}, HW_Category_SimpleSIMD, HW_Flag_ReturnsPerElementMask|HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(AVX512F, CompareOrdered, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcmpps, INS_vcmppd}, HW_Category_SimpleSIMD, HW_Flag_ReturnsPerElementMask|HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(AVX512F, CompareUnordered, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcmpps, INS_vcmppd}, HW_Category_SimpleSIMD, HW_Flag_ReturnsPerElementMask|HW_Flag_SpecialImport|HW_Flag_NoCodeGen) -HARDWARE_INTRINSIC(AVX512F, ConvertScalarToVector128Double, 16, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtusi2sd32, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromSecondArg|HW_Flag_CopyUpperBits) -HARDWARE_INTRINSIC(AVX512F, ConvertScalarToVector128Single, 16, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtusi2ss32, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromSecondArg|HW_Flag_CopyUpperBits) -HARDWARE_INTRINSIC(AVX512F, ConvertToUInt32, 16, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtss2usi, INS_vcvtsd2usi}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(AVX512F, ConvertScalarToVector128Double, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtsi2sd32, INS_vcvtusi2sd32, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromSecondArg|HW_Flag_CopyUpperBits) +HARDWARE_INTRINSIC(AVX512F, ConvertScalarToVector128Single, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtsi2ss32, INS_vcvtusi2ss32, INS_invalid, INS_invalid, INS_invalid, INS_cvtsd2ss}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromSecondArg|HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, ConvertToInt32, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtss2si, INS_cvtsd2si}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, ConvertToUInt32, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtss2usi, INS_vcvtsd2usi}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, ConvertToUInt32WithTruncation, 16, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvttss2usi32, INS_vcvttsd2usi}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(AVX512F, ConvertToVector128Byte, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovdb, INS_vpmovdb, INS_vpmovqb, INS_vpmovqb, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(AVX512F, ConvertToVector128ByteWithSaturation, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovusdb, INS_invalid, INS_vpmovusqb, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) @@ -870,36 +872,41 @@ HARDWARE_INTRINSIC(AVX512F, ConvertToVector128UInt16, HARDWARE_INTRINSIC(AVX512F, ConvertToVector128UInt16WithSaturation, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovusqw, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(AVX512F, ConvertToVector256Int16, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovdw, INS_vpmovdw, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(AVX512F, ConvertToVector256Int16WithSaturation, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovsdw, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) -HARDWARE_INTRINSIC(AVX512F, ConvertToVector256Int32, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovqd, INS_vpmovqd, INS_invalid, INS_cvtpd2dq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(AVX512F, ConvertToVector256Int32, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovqd, INS_vpmovqd, INS_invalid, INS_cvtpd2dq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, ConvertToVector256Int32WithSaturation, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovsqd, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(AVX512F, ConvertToVector256Int32WithTruncation, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvttpd2dq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(AVX512F, ConvertToVector256Single, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtpd2ps}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(AVX512F, ConvertToVector256Single, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtpd2ps}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, ConvertToVector256UInt16, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovdw, INS_vpmovdw, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(AVX512F, ConvertToVector256UInt16WithSaturation, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovusdw, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) -HARDWARE_INTRINSIC(AVX512F, ConvertToVector256UInt32, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovqd, INS_vpmovqd, INS_invalid, INS_vcvtpd2udq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(AVX512F, ConvertToVector256UInt32, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovqd, INS_vpmovqd, INS_invalid, INS_vcvtpd2udq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, ConvertToVector256UInt32WithSaturation, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpmovusqd, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(AVX512F, ConvertToVector256UInt32WithTruncation, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvttpd2udq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(AVX512F, ConvertToVector512Double, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtdq2pd, INS_vcvtudq2pd, INS_invalid, INS_invalid, INS_cvtps2pd, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(AVX512F, ConvertToVector512Int32, 64, 1, true, {INS_pmovsxbd, INS_pmovzxbd, INS_pmovsxwd, INS_pmovzxwd, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtps2dq, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(AVX512F, ConvertToVector512Int32, 64, -1, false, {INS_pmovsxbd, INS_pmovzxbd, INS_pmovsxwd, INS_pmovzxwd, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtps2dq, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, ConvertToVector512Int32WithTruncation, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvttps2dq, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(AVX512F, ConvertToVector512Int64, 64, 1, true, {INS_pmovsxbq, INS_pmovzxbq, INS_pmovsxwq, INS_pmovzxwq, INS_pmovsxdq, INS_pmovzxdq, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(AVX512F, ConvertToVector512Single, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtdq2ps, INS_vcvtudq2ps, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(AVX512F, ConvertToVector512UInt32, 64, 1, true, {INS_pmovsxbd, INS_pmovzxbd, INS_pmovsxwd, INS_pmovzxwd, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtps2udq, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(AVX512F, ConvertToVector512Single, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtdq2ps, INS_vcvtudq2ps, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, ConvertToVector512UInt32, 64, -1, false, {INS_pmovsxbd, INS_pmovzxbd, INS_pmovsxwd, INS_pmovzxwd, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtps2udq, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, ConvertToVector512UInt32WithTruncation, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvttps2udq, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(AVX512F, ConvertToVector512UInt64, 64, 1, true, {INS_pmovsxbq, INS_pmovzxbq, INS_pmovsxwq, INS_pmovzxwq, INS_pmovsxdq, INS_pmovzxdq, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(AVX512F, Divide, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_divps, INS_divpd}, HW_Category_SimpleSIMD, HW_Flag_EmbBroadcastCompatible) +HARDWARE_INTRINSIC(AVX512F, Divide, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_divps, INS_divpd}, HW_Category_SimpleSIMD, HW_Flag_EmbBroadcastCompatible|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, DivideScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_divss, INS_divsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, DuplicateEvenIndexed, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movsldup, INS_movddup}, HW_Category_SimpleSIMD, HW_Flag_NoFlag) HARDWARE_INTRINSIC(AVX512F, DuplicateOddIndexed, 64, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_movshdup, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoFlag) HARDWARE_INTRINSIC(AVX512F, ExtractVector128, 64, 2, true, {INS_vextracti128, INS_vextracti128, INS_vextracti128, INS_vextracti128, INS_vextracti128, INS_vextracti128, INS_vextracti128, INS_vextracti128, INS_vextractf128, INS_vextractf128}, HW_Category_IMM, HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, ExtractVector256, 64, 2, true, {INS_vextracti64x4, INS_vextracti64x4, INS_vextracti64x4, INS_vextracti64x4, INS_vextracti64x4, INS_vextracti64x4, INS_vextracti64x4, INS_vextracti64x4, INS_vextractf64x4, INS_vextractf64x4}, HW_Category_IMM, HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, Fixup, 64, 4, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfixupimmps, INS_vfixupimmpd}, HW_Category_IMM, HW_Flag_SpecialImport|HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, FixupScalar, 16, 4, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfixupimmss, INS_vfixupimmsd}, HW_Category_IMM, HW_Flag_SpecialImport|HW_Flag_FullRangeIMM|HW_Flag_CopyUpperBits) -HARDWARE_INTRINSIC(AVX512F, FusedMultiplyAdd, 64, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfmadd213ps, INS_vfmadd213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic) -HARDWARE_INTRINSIC(AVX512F, FusedMultiplyAddNegated, 64, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfnmadd213ps, INS_vfnmadd213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic) -HARDWARE_INTRINSIC(AVX512F, FusedMultiplyAddSubtract, 64, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfmaddsub213ps, INS_vfmaddsub213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic) -HARDWARE_INTRINSIC(AVX512F, FusedMultiplySubtract, 64, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfmsub213ps, INS_vfmsub213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic) -HARDWARE_INTRINSIC(AVX512F, FusedMultiplySubtractAdd, 64, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfmsubadd213ps, INS_vfmsubadd213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic) -HARDWARE_INTRINSIC(AVX512F, FusedMultiplySubtractNegated, 64, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfnmsub213ps, INS_vfnmsub213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic) +HARDWARE_INTRINSIC(AVX512F, FusedMultiplyAdd, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfmadd213ps, INS_vfmadd213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, FusedMultiplyAddScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfmadd213ss, INS_vfmadd213sd}, HW_Category_SIMDScalar, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic|HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, FusedMultiplyAddNegated, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfnmadd213ps, INS_vfnmadd213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, FusedMultiplyAddNegatedScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfnmadd213ss, INS_vfnmadd213sd}, HW_Category_SIMDScalar, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic|HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, FusedMultiplyAddSubtract, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfmaddsub213ps, INS_vfmaddsub213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, FusedMultiplySubtract, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfmsub213ps, INS_vfmsub213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, FusedMultiplySubtractScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfmsub213ss, INS_vfmsub213sd}, HW_Category_SIMDScalar, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic|HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, FusedMultiplySubtractAdd, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfmsubadd213ps, INS_vfmsubadd213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, FusedMultiplySubtractNegated, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfnmsub213ps, INS_vfnmsub213pd}, HW_Category_SimpleSIMD, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, FusedMultiplySubtractNegatedScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vfnmsub213ss, INS_vfnmsub213sd}, HW_Category_SIMDScalar, HW_Flag_SpecialCodeGen|HW_Flag_FmaIntrinsic|HW_Flag_RmwIntrinsic|HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, GetExponent, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vgetexpps, INS_vgetexppd}, HW_Category_SimpleSIMD, HW_Flag_NoFlag) HARDWARE_INTRINSIC(AVX512F, GetExponentScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vgetexpss, INS_vgetexpsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits) HARDWARE_INTRINSIC(AVX512F, GetMantissa, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vgetmantps, INS_vgetmantpd}, HW_Category_IMM, HW_Flag_NoFlag) @@ -911,7 +918,8 @@ HARDWARE_INTRINSIC(AVX512F, LoadAlignedVector512NonTemporal, HARDWARE_INTRINSIC(AVX512F, LoadVector512, 64, 1, true, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_vmovdqu64, INS_vmovdqu64, INS_movups, INS_movupd}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(AVX512F, Max, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_pmaxsd, INS_pmaxud, INS_vpmaxsq, INS_vpmaxuq, INS_maxps, INS_maxpd}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative|HW_Flag_EmbBroadcastCompatible) HARDWARE_INTRINSIC(AVX512F, Min, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_pminsd, INS_pminud, INS_vpminsq, INS_vpminuq, INS_minps, INS_minpd}, HW_Category_SimpleSIMD, HW_Flag_MaybeCommutative|HW_Flag_EmbBroadcastCompatible) -HARDWARE_INTRINSIC(AVX512F, Multiply, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_pmuldq, INS_pmuludq, INS_mulps, INS_mulpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative|HW_Flag_EmbBroadcastCompatible) +HARDWARE_INTRINSIC(AVX512F, Multiply, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_pmuldq, INS_pmuludq, INS_mulps, INS_mulpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative|HW_Flag_EmbBroadcastCompatible|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, MultiplyScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_mulss, INS_mulsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, MultiplyLow, 64, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_pmulld, INS_pmulld, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_Commutative|HW_Flag_EmbBroadcastCompatible) HARDWARE_INTRINSIC(AVX512F, Or, 64, 2, true, {INS_por, INS_por, INS_por, INS_por, INS_por, INS_por, INS_vporq, INS_vporq, INS_orps, INS_orpd}, HW_Category_SimpleSIMD, HW_Flag_Commutative|HW_Flag_EmbBroadcastCompatible) HARDWARE_INTRINSIC(AVX512F, Permute2x64, 64, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpermilpd}, HW_Category_IMM, HW_Flag_FullRangeIMM) @@ -933,8 +941,8 @@ HARDWARE_INTRINSIC(AVX512F, RotateRight, HARDWARE_INTRINSIC(AVX512F, RotateRightVariable, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vprorvd, INS_vprorvd, INS_vprorvq, INS_vprorvq, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoFlag) HARDWARE_INTRINSIC(AVX512F, RoundScale, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vrndscaleps, INS_vrndscalepd}, HW_Category_IMM, HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, RoundScaleScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vrndscaless, INS_vrndscalesd}, HW_Category_IMM, HW_Flag_FullRangeIMM|HW_Flag_CopyUpperBits) -HARDWARE_INTRINSIC(AVX512F, Scale, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vscalefps, INS_vscalefpd}, HW_Category_SimpleSIMD, HW_Flag_NoFlag) -HARDWARE_INTRINSIC(AVX512F, ScaleScalar, 16, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vscalefss, INS_vscalefsd}, HW_Category_SimpleSIMD, HW_Flag_CopyUpperBits) +HARDWARE_INTRINSIC(AVX512F, Scale, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vscalefps, INS_vscalefpd}, HW_Category_SimpleSIMD, HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, ScaleScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vscalefss, INS_vscalefsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, ShiftLeftLogical, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_pslld, INS_pslld, INS_psllq, INS_psllq, INS_invalid, INS_invalid}, HW_Category_IMM, HW_Flag_MaybeIMM|HW_Flag_NoJmpTableIMM|HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, ShiftLeftLogicalVariable, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpsllvd, INS_vpsllvd, INS_vpsllvq, INS_vpsllvq, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_EmbBroadcastCompatible) HARDWARE_INTRINSIC(AVX512F, ShiftRightArithmetic, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_psrad, INS_invalid, INS_vpsraq, INS_invalid, INS_invalid, INS_invalid}, HW_Category_IMM, HW_Flag_MaybeIMM|HW_Flag_NoJmpTableIMM|HW_Flag_FullRangeIMM) @@ -943,11 +951,13 @@ HARDWARE_INTRINSIC(AVX512F, ShiftRightLogical, HARDWARE_INTRINSIC(AVX512F, ShiftRightLogicalVariable, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vpsrlvd, INS_vpsrlvd, INS_vpsrlvq, INS_vpsrlvq, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_EmbBroadcastCompatible) HARDWARE_INTRINSIC(AVX512F, Shuffle, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_pshufd, INS_pshufd, INS_invalid, INS_invalid, INS_shufps, INS_shufpd}, HW_Category_IMM, HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, Shuffle4x128, 64, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vshufi32x4, INS_vshufi32x4, INS_vshufi64x2, INS_vshufi64x2, INS_vshuff32x4, INS_vshuff64x2}, HW_Category_IMM, HW_Flag_FullRangeIMM) -HARDWARE_INTRINSIC(AVX512F, Sqrt, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sqrtps, INS_sqrtpd}, HW_Category_SimpleSIMD, HW_Flag_NoFlag) +HARDWARE_INTRINSIC(AVX512F, Sqrt, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sqrtps, INS_sqrtpd}, HW_Category_SimpleSIMD, HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, SqrtScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sqrtss, INS_sqrtsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, Store, 64, 2, true, {INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_movdqu, INS_vmovdqu64, INS_vmovdqu64, INS_movups, INS_movupd}, HW_Category_Helper, HW_Flag_SpecialImport|HW_Flag_BaseTypeFromSecondArg|HW_Flag_NoCodeGen) HARDWARE_INTRINSIC(AVX512F, StoreAligned, 64, 2, true, {INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_movdqa, INS_vmovdqa64, INS_vmovdqa64, INS_movaps, INS_movapd}, HW_Category_MemoryStore, HW_Flag_BaseTypeFromSecondArg) HARDWARE_INTRINSIC(AVX512F, StoreAlignedNonTemporal, 64, 2, true, {INS_movntdq, INS_movntdq, INS_movntdq, INS_movntdq, INS_movntdq, INS_movntdq, INS_movntdq, INS_movntdq, INS_movntps, INS_movntpd}, HW_Category_MemoryStore, HW_Flag_BaseTypeFromSecondArg) -HARDWARE_INTRINSIC(AVX512F, Subtract, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_psubd, INS_psubd, INS_psubq, INS_psubq, INS_subps, INS_subpd}, HW_Category_SimpleSIMD, HW_Flag_EmbBroadcastCompatible) +HARDWARE_INTRINSIC(AVX512F, Subtract, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_psubd, INS_psubd, INS_psubq, INS_psubq, INS_subps, INS_subpd}, HW_Category_SimpleSIMD, HW_Flag_EmbBroadcastCompatible|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F, SubtractScalar, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_subss, INS_subsd}, HW_Category_SIMDScalar, HW_Flag_CopyUpperBits|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F, UnpackHigh, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_punpckhdq, INS_punpckhdq, INS_punpckhqdq, INS_punpckhqdq, INS_unpckhps, INS_unpckhpd}, HW_Category_SimpleSIMD, HW_Flag_NoFlag) HARDWARE_INTRINSIC(AVX512F, TernaryLogic, 64, 4, true, {INS_vpternlogd, INS_vpternlogd, INS_vpternlogd, INS_vpternlogd, INS_vpternlogd, INS_vpternlogd, INS_vpternlogq, INS_vpternlogq, INS_vpternlogd, INS_vpternlogq}, HW_Category_IMM, HW_Flag_SpecialImport|HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512F, UnpackLow, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_punpckldq, INS_punpckldq, INS_punpcklqdq, INS_punpcklqdq, INS_unpcklps, INS_unpcklpd}, HW_Category_SimpleSIMD, HW_Flag_NoFlag) @@ -1013,9 +1023,10 @@ HARDWARE_INTRINSIC(AVX512F_VL, TernaryLogic, // {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // AVX512F.X64 Intrinsics -HARDWARE_INTRINSIC(AVX512F_X64, ConvertScalarToVector128Double, 16, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtusi2sd64, INS_invalid, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromSecondArg|HW_Flag_CopyUpperBits|HW_Flag_SpecialCodeGen) -HARDWARE_INTRINSIC(AVX512F_X64, ConvertScalarToVector128Single, 16, 2, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtusi2ss64, INS_invalid, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromSecondArg|HW_Flag_CopyUpperBits|HW_Flag_SpecialCodeGen) -HARDWARE_INTRINSIC(AVX512F_X64, ConvertToUInt64, 16, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtss2usi, INS_vcvtsd2usi}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(AVX512F_X64, ConvertScalarToVector128Double, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtsi2sd64, INS_vcvtusi2sd64, INS_invalid, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromSecondArg|HW_Flag_CopyUpperBits|HW_Flag_SpecialCodeGen|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F_X64, ConvertScalarToVector128Single, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtsi2ss64, INS_vcvtusi2ss64, INS_invalid, INS_invalid}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromSecondArg|HW_Flag_CopyUpperBits|HW_Flag_SpecialCodeGen|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F_X64, ConvertToInt64, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_cvtss2si, INS_cvtsd2si}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512F_X64, ConvertToUInt64, 16, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtss2usi, INS_vcvtsd2usi}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512F_X64, ConvertToUInt64WithTruncation, 16, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvttss2usi64, INS_vcvttsd2usi}, HW_Category_SIMDScalar, HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** @@ -1121,11 +1132,11 @@ HARDWARE_INTRINSIC(AVX512DQ, AndNot, HARDWARE_INTRINSIC(AVX512DQ, BroadcastPairScalarToVector512, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vbroadcasti32x2, INS_vbroadcasti32x2, INS_invalid, INS_invalid, INS_vbroadcastf32x2, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoFlag) HARDWARE_INTRINSIC(AVX512DQ, BroadcastVector128ToVector512, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vbroadcasti64x2, INS_vbroadcasti64x2, INS_invalid, INS_vbroadcastf64x2}, HW_Category_MemoryLoad, HW_Flag_NoFlag) HARDWARE_INTRINSIC(AVX512DQ, BroadcastVector256ToVector512, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vbroadcasti32x8, INS_vbroadcasti32x8, INS_invalid, INS_invalid, INS_vbroadcastf32x8, INS_invalid}, HW_Category_MemoryLoad, HW_Flag_NoFlag) -HARDWARE_INTRINSIC(AVX512DQ, ConvertToVector256Single, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtqq2ps, INS_vcvtuqq2ps, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(AVX512DQ, ConvertToVector512Double, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtqq2pd, INS_vcvtuqq2pd, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(AVX512DQ, ConvertToVector512Int64, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtps2qq, INS_vcvtpd2qq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(AVX512DQ, ConvertToVector256Single, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtqq2ps, INS_vcvtuqq2ps, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512DQ, ConvertToVector512Double, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtqq2pd, INS_vcvtuqq2pd, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbRoundingCompatible) +HARDWARE_INTRINSIC(AVX512DQ, ConvertToVector512Int64, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtps2qq, INS_vcvtpd2qq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512DQ, ConvertToVector512Int64WithTruncation, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvttps2qq, INS_vcvttpd2qq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) -HARDWARE_INTRINSIC(AVX512DQ, ConvertToVector512UInt64, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtps2uqq, INS_vcvtpd2uqq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) +HARDWARE_INTRINSIC(AVX512DQ, ConvertToVector512UInt64, 64, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvtps2uqq, INS_vcvtpd2uqq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbRoundingCompatible) HARDWARE_INTRINSIC(AVX512DQ, ConvertToVector512UInt64WithTruncation, 64, 1, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vcvttps2uqq, INS_vcvttpd2uqq}, HW_Category_SimpleSIMD, HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(AVX512DQ, ExtractVector128, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vextracti64x2, INS_vextracti64x2, INS_invalid, INS_vextractf64x2}, HW_Category_IMM, HW_Flag_FullRangeIMM) HARDWARE_INTRINSIC(AVX512DQ, ExtractVector256, 64, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_vextracti32x8, INS_vextracti32x8, INS_invalid, INS_invalid, INS_vextractf32x8, INS_invalid}, HW_Category_IMM, HW_Flag_FullRangeIMM) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index a7ba98cad90d0e..6709a57a49e8d7 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -1068,29 +1068,24 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) NamedIntrinsic intrinsicId = node->GetHWIntrinsicId(); - if (HWIntrinsicInfo::IsEmbRoundingCompatible(intrinsicId)) + if (node->OperIsEmbRoundingEnabled()) { - size_t numArgs = node->GetOperandCount(); - size_t expectedArgNum = HWIntrinsicInfo::EmbRoundingArgPos(intrinsicId); + size_t numArgs = node->GetOperandCount(); + GenTree* lastOp = node->Op(numArgs); + uint8_t mode = 0xFF; - if (numArgs == expectedArgNum) + if (lastOp->IsCnsIntOrI()) { - GenTree* lastOp = node->Op(numArgs); - uint8_t mode = 0xFF; - - if (lastOp->IsCnsIntOrI()) - { - // Mark the constant as contained since it's specially encoded - MakeSrcContained(node, lastOp); + // Mark the constant as contained since it's specially encoded + MakeSrcContained(node, lastOp); - mode = static_cast(lastOp->AsIntCon()->IconValue()); - } + mode = static_cast(lastOp->AsIntCon()->IconValue()); + } - if ((mode & 0x03) != 0x00) - { - // Embedded rounding only works for register-to-register operations, so skip containment - return node->gtNext; - } + if ((mode & 0x03) != 0x00) + { + // Embedded rounding only works for register-to-register operations, so skip containment + return node->gtNext; } } diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index f43febae6c3a42..cb2b82d5d8ce92 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -2164,8 +2164,7 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou } } - if (HWIntrinsicInfo::IsEmbRoundingCompatible(intrinsicId) && - numArgs == HWIntrinsicInfo::EmbRoundingArgPos(intrinsicId) && !lastOp->IsCnsIntOrI()) + if (intrinsicTree->OperIsEmbRoundingEnabled() && !lastOp->IsCnsIntOrI()) { buildInternalIntRegisterDefForNode(intrinsicTree); buildInternalIntRegisterDefForNode(intrinsicTree); @@ -2402,13 +2401,17 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou case NI_FMA_MultiplySubtractNegatedScalar: case NI_FMA_MultiplySubtractScalar: case NI_AVX512F_FusedMultiplyAdd: + case NI_AVX512F_FusedMultiplyAddScalar: case NI_AVX512F_FusedMultiplyAddNegated: + case NI_AVX512F_FusedMultiplyAddNegatedScalar: case NI_AVX512F_FusedMultiplyAddSubtract: case NI_AVX512F_FusedMultiplySubtract: + case NI_AVX512F_FusedMultiplySubtractScalar: case NI_AVX512F_FusedMultiplySubtractAdd: case NI_AVX512F_FusedMultiplySubtractNegated: + case NI_AVX512F_FusedMultiplySubtractNegatedScalar: { - assert(numArgs == 3); + assert((numArgs == 3) || (intrinsicTree->OperIsEmbRoundingEnabled())); assert(isRMW); assert(HWIntrinsicInfo::IsFmaIntrinsic(intrinsicId)); @@ -2506,6 +2509,11 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou srcCount += BuildDelayFreeUses(emitOp2, emitOp1); srcCount += emitOp3->isContained() ? BuildOperandUses(emitOp3) : BuildDelayFreeUses(emitOp3, emitOp1); + if (intrinsicTree->OperIsEmbRoundingEnabled() && !intrinsicTree->Op(4)->IsCnsIntOrI()) + { + srcCount += BuildOperandUses(intrinsicTree->Op(4)); + } + buildUses = false; break; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512DQ.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512DQ.PlatformNotSupported.cs index 6bac345ed9d304..305e7333dbc527 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512DQ.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512DQ.PlatformNotSupported.cs @@ -322,7 +322,16 @@ internal X64() { } /// VCVTUQQ2PS ymm1 {k1}{z}, zmm2/m512/m64bcst /// public static Vector256 ConvertToVector256Single(Vector512 value) { throw new PlatformNotSupportedException(); } - + /// + /// __m256 _mm512_cvt_roundepi64_ps (__m512i a, int r) + /// VCVTQQ2PS ymm1, zmm2 {er} + /// + public static Vector256 ConvertToVector256Single(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m256 _mm512_cvt_roundepu64_ps (__m512i a, int r) + /// VCVTUQQ2PS ymm1, zmm2 {er} + /// + public static Vector256 ConvertToVector256Single(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512d _mm512_cvtepi64_pd (__m512i a) /// VCVTQQ2PD zmm1 {k1}{z}, zmm2/m512/m64bcst @@ -334,6 +343,16 @@ internal X64() { } /// public static Vector512 ConvertToVector512Double(Vector512 value) { throw new PlatformNotSupportedException(); } /// + /// __m512d _mm512_cvt_roundepi64_pd (__m512i a, int r) + /// VCVTQQ2PD zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Double(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m512d _mm512_cvt_roundepu64_pd (__m512i a, int r) + /// VCVTUQQ2PD zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Double(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512i _mm512_cvtps_epi64 (__m512 a) /// VCVTPS2QQ zmm1 {k1}{z}, ymm2/m256/m32bcst{er} /// @@ -344,6 +363,16 @@ internal X64() { } /// public static Vector512 ConvertToVector512Int64(Vector512 value) { throw new PlatformNotSupportedException(); } /// + /// __m512i _mm512_cvt_roundps_epi64 (__m512 a, int r) + /// VCVTPS2QQ zmm1, ymm2 {er} + /// + public static Vector512 ConvertToVector512Int64(Vector256 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m512i _mm512_cvt_roundpd_epi64 (__m512d a, int r) + /// VCVTPD2QQ zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Int64(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512i _mm512_cvttps_epi64 (__m512 a) /// VCVTTPS2QQ zmm1 {k1}{z}, ymm2/m256/m32bcst{er} /// @@ -364,6 +393,16 @@ internal X64() { } /// public static Vector512 ConvertToVector512UInt64(Vector512 value) { throw new PlatformNotSupportedException(); } /// + /// __m512i _mm512_cvt_roundps_epu64 (__m512 a, int r) + /// VCVTPS2UQQ zmm1 {k1}{z}, ymm2/m256/m32bcst{er} + /// + public static Vector512 ConvertToVector512UInt64(Vector256 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m512i _mm512_cvt_roundpd_epu64 (__m512d a, int r) + /// VCVTPD2UQQ zmm1 {k1}{z}, zmm2/m512/m64bcst{er} + /// + public static Vector512 ConvertToVector512UInt64(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512i _mm512_cvttps_epu64 (__m512 a) /// VCVTTPS2UQQ zmm1 {k1}{z}, ymm2/m256/m32bcst{er} /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512DQ.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512DQ.cs index 97898da3ff6edf..403a851dbd1fe9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512DQ.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512DQ.cs @@ -323,6 +323,16 @@ internal X64() { } /// VCVTUQQ2PS ymm1 {k1}{z}, zmm2/m512/m64bcst /// public static Vector256 ConvertToVector256Single(Vector512 value) => ConvertToVector256Single(value); + /// + /// __m256 _mm512_cvt_roundepi64_ps (__m512i a, int r) + /// VCVTQQ2PS ymm1, zmm2 {er} + /// + public static Vector256 ConvertToVector256Single(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector256Single(value, mode); + /// + /// __m256 _mm512_cvt_roundepu64_ps (__m512i a, int r) + /// VCVTUQQ2PS ymm1, zmm2 {er} + /// + public static Vector256 ConvertToVector256Single(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector256Single(value, mode); /// /// __m512d _mm512_cvtepi64_pd (__m512i a) @@ -334,6 +344,17 @@ internal X64() { } /// VCVTUQQ2PD zmm1 {k1}{z}, zmm2/m512/m64bcst /// public static Vector512 ConvertToVector512Double(Vector512 value) => ConvertToVector512Double(value); + /// + /// __m512d _mm512_cvt_roundepi64_pd (__m512i a, int r) + /// VCVTQQ2PD zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Double(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector512Double(value, mode); + /// + /// __m512d _mm512_cvt_roundepu64_pd (__m512i a, int r) + /// VCVTUQQ2PD zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Double(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector512Double(value, mode); + /// /// __m512i _mm512_cvtps_epi64 (__m512 a) /// VCVTPS2QQ zmm1 {k1}{z}, ymm2/m256/m32bcst{er} @@ -344,6 +365,17 @@ internal X64() { } /// VCVTPD2QQ zmm1 {k1}{z}, zmm2/m512/m64bcst{er} /// public static Vector512 ConvertToVector512Int64(Vector512 value) => ConvertToVector512Int64(value); + /// + /// __m512i _mm512_cvt_roundps_epi64 (__m512 a, int r) + /// VCVTPS2QQ zmm1, ymm2 {er} + /// + public static Vector512 ConvertToVector512Int64(Vector256 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector512Int64(value, mode); + /// + /// __m512i _mm512_cvt_roundpd_epi64 (__m512d a, int r) + /// VCVTPD2QQ zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Int64(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector512Int64(value, mode); + /// /// __m512i _mm512_cvttps_epi64 (__m512 a) /// VCVTTPS2QQ zmm1 {k1}{z}, ymm2/m256/m32bcst{er} @@ -365,6 +397,16 @@ internal X64() { } /// public static Vector512 ConvertToVector512UInt64(Vector512 value) => ConvertToVector512UInt64(value); /// + /// __m512i _mm512_cvt_roundps_epu64 (__m512 a, int r) + /// VCVTPS2UQQ zmm1 {k1}{z}, ymm2/m256/m32bcst{er} + /// + public static Vector512 ConvertToVector512UInt64(Vector256 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector512UInt64(value, mode); + /// + /// __m512i _mm512_cvt_roundpd_epu64 (__m512d a, int r) + /// VCVTPD2UQQ zmm1 {k1}{z}, zmm2/m512/m64bcst{er} + /// + public static Vector512 ConvertToVector512UInt64(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector512UInt64(value, mode); + /// /// __m512i _mm512_cvttps_epu64 (__m512 a) /// VCVTTPS2UQQ zmm1 {k1}{z}, ymm2/m256/m32bcst{er} /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.PlatformNotSupported.cs index 5e6bc6e2023ec4..249d3af06fbbdc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.PlatformNotSupported.cs @@ -1261,19 +1261,54 @@ internal X64() { } public static new bool IsSupported { [Intrinsic] get { return false; } } + /// + /// __m128 _mm_cvt_roundi64_ss (__m128 a, __int64 b, int rounding) + /// VCVTSI2SS xmm1, xmm2, r64 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, ulong value) { throw new PlatformNotSupportedException(); } /// /// __m128 _mm_cvtsi64_ss (__m128 a, __int64 b) /// VCVTUSI2SS xmm1, xmm2, r/m64 /// This intrinsic is only available on 64-bit processes /// - public static Vector128 ConvertScalarToVector128Single(Vector128 upper, ulong value) { throw new PlatformNotSupportedException(); } + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, long value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_cvt_roundu64_ss (__m128 a, unsigned __int64 b, int rounding) + /// VCVTUSI2SS xmm1, xmm2, r64 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, ulong value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m128d _mm_cvtsi64_sd (__m128d a, __int64 b) /// VCVTUSI2SD xmm1, xmm2, r/m64 /// This intrinsic is only available on 64-bit processes /// public static Vector128 ConvertScalarToVector128Double(Vector128 upper, ulong value) { throw new PlatformNotSupportedException(); } - + /// + /// __m128d _mm_cvt_roundsi64_sd (__m128d a, __int64 b, int rounding) + /// VCVTSI2SD xmm1, xmm2, r64 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static Vector128 ConvertScalarToVector128Double(Vector128 upper, long value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_cvt_roundu64_sd (__m128d a, unsigned __int64 b, int rounding) + /// VCVTUSI2SD xmm1, xmm2, r64 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static Vector128 ConvertScalarToVector128Double(Vector128 upper, ulong value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __int64 _mm_cvt_roundss_i64 (__m128 a, int rounding) + /// VCVTSS2SI r64, xmm1 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static long ConvertToInt64(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __int64 _mm_cvt_roundsd_i64 (__m128d a, int rounding) + /// VCVTSD2SI r64, xmm1 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static long ConvertToInt64(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// unsigned __int64 _mm_cvtss_u64 (__m128 a) /// VCVTSS2USI r64, xmm1/m32{er} @@ -1281,11 +1316,23 @@ internal X64() { } /// public static ulong ConvertToUInt64(Vector128 value) { throw new PlatformNotSupportedException(); } /// + /// unsigned __int64 _mm_cvt_roundss_u64 (__m128 a, int rounding) + /// VCVTSS2USI r64, xmm1 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static ulong ConvertToUInt64(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// unsigned __int64 _mm_cvtsd_u64 (__m128d a) /// VCVTSD2USI r64, xmm1/m64{er} /// This intrinsic is only available on 64-bit processes /// public static ulong ConvertToUInt64(Vector128 value) { throw new PlatformNotSupportedException(); } + /// + /// unsigned __int64 _mm_cvt_roundsd_u64 (__m128d a, int rounding) + /// VCVTSD2USI r64, xmm1 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static ulong ConvertToUInt64(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// unsigned __int64 _mm_cvttss_u64 (__m128 a) @@ -1338,8 +1385,8 @@ internal X64() { } /// public static Vector512 Add(Vector512 left, Vector512 right) { throw new PlatformNotSupportedException(); } /// - /// __m512d _mm512_add_pd (__m512d a, __m512d b) - /// VADDPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} + /// __m512d _mm512_add_round_pd (__m512d a, __m512d b, int rounding) + /// VADDPD zmm1, zmm2, zmm3 {er} /// public static Vector512 Add(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// @@ -1347,7 +1394,21 @@ internal X64() { } /// VADDPS zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst{er} /// public static Vector512 Add(Vector512 left, Vector512 right) { throw new PlatformNotSupportedException(); } - + /// + /// __m512 _mm512_add_round_ps (__m512 a, __m512 b, int rounding) + /// VADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Add(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_add_round_sd (__m128d a, __m128d b, int rounding) + /// VADDSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 AddScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_add_round_ss (__m128 a, __m128 b, int rounding) + /// VADDSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 AddScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512i _mm512_alignr_epi32 (__m512i a, __m512i b, const int count) /// VALIGND zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst, imm8 @@ -1832,22 +1893,56 @@ internal X64() { } /// public static Vector128 ConvertScalarToVector128Single(Vector128 upper, uint value) { throw new PlatformNotSupportedException(); } /// + /// __m128 _mm_cvt_roundi32_ss (__m128 a, int b, int rounding) + /// VCVTUSI2SS xmm1, xmm2, r32 {er} + /// + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, uint value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertScalarToVector128Single(upper, value, mode); + /// + /// __m128 _mm_cvt_roundi32_ss (__m128 a, int b, int rounding) + /// VCVTSI2SS xmm1, xmm2, r32 {er} + /// + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, int value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_cvt_roundsd_ss (__m128 a, __m128d b, int rounding) + /// VCVTSD2SS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode){ throw new PlatformNotSupportedException(); } + /// /// __m128d _mm_cvtsi32_sd (__m128d a, int b) /// VCVTUSI2SD xmm1, xmm2, r/m32 /// public static Vector128 ConvertScalarToVector128Double(Vector128 upper, uint value) { throw new PlatformNotSupportedException(); } - + /// + /// int _mm_cvt_roundss_i32 (__m128 a, int rounding) + /// VCVTSS2SIK r32, xmm1 {er} + /// + public static int ConvertToInt32(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// int _mm_cvt_roundsd_i32 (__m128d a, int rounding) + /// VCVTSD2SI r32, xmm1 {er} + /// + public static int ConvertToInt32(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// unsigned int _mm_cvtss_u32 (__m128 a) /// VCVTSS2USI r32, xmm1/m32{er} /// public static uint ConvertToUInt32(Vector128 value) { throw new PlatformNotSupportedException(); } /// + /// unsigned int _mm_cvt_roundss_u32 (__m128 a, int rounding) + /// VCVTSS2USI r32, xmm1 {er} + /// + public static uint ConvertToUInt32(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// unsigned int _mm_cvtsd_u32 (__m128d a) /// VCVTSD2USI r32, xmm1/m64{er} /// public static uint ConvertToUInt32(Vector128 value) { throw new PlatformNotSupportedException(); } /// + /// unsigned int _mm_cvt_roundsd_u32 (__m128d a, int rounding) + /// VCVTSD2USI r32, xmm1 {er} + /// + public static uint ConvertToUInt32(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// unsigned int _mm_cvttss_u32 (__m128 a) /// VCVTTSS2USI r32, xmm1/m32{er} /// @@ -1974,6 +2069,11 @@ internal X64() { } /// public static Vector256 ConvertToVector256Int32(Vector512 value) { throw new PlatformNotSupportedException(); } /// + /// __m256i _mm512_cvt_roundpd_epi32 (__m512d a, int rounding) + /// VCVTPD2DQ ymm1, zmm2 {er} + /// + public static Vector256 ConvertToVector256Int32(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m256i _mm512_cvtepi64_epi32 (__m512i a) /// VPMOVQD ymm1/m256 {k1}{z}, zmm2 /// @@ -2000,7 +2100,11 @@ internal X64() { } /// VCVTPD2PS ymm1 {k1}{z}, zmm2/m512/m64bcst{er} /// public static Vector256 ConvertToVector256Single(Vector512 value) { throw new PlatformNotSupportedException(); } - + /// + /// __m256 _mm512_cvt_roundpd_ps (__m512d a, int rounding) + /// VCVTPD2PS ymm1, zmm2 {er} + /// + public static Vector256 ConvertToVector256Single(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m256i _mm512_cvtepi32_epi16 (__m512i a) /// VPMOVDW ymm1/m256 {k1}{z}, zmm2 @@ -2023,6 +2127,11 @@ internal X64() { } /// public static Vector256 ConvertToVector256UInt32(Vector512 value) { throw new PlatformNotSupportedException(); } /// + ///__m256i _mm512_cvt_roundpd_epu32 (__m512d a, int rounding) + /// VCVTPD2UDQ ymm1, zmm2 {er} + /// + public static Vector256 ConvertToVector256UInt32(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m256i _mm512_cvtepi64_epi32 (__m512i a) /// VPMOVQD ymm1/m256 {k1}{z}, zmm2 /// @@ -2064,6 +2173,16 @@ internal X64() { } /// public static Vector512 ConvertToVector512Int32(Vector128 value) { throw new PlatformNotSupportedException(); } /// + /// __m512 _mm512_cvt_roundepi32_ps (__m512i a, int rounding) + /// VCVTDQ2PS zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Single(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m512 _mm512_cvt_roundepi32_ps (__m512i a, int rounding) + /// VCVTUDQ2PS zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Single(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512i _mm512_cvtepu8_epi32 (__m128i a) /// VPMOVZXBD zmm1 {k1}{z}, xmm2/m128 /// @@ -2084,6 +2203,11 @@ internal X64() { } /// public static Vector512 ConvertToVector512Int32(Vector512 value) { throw new PlatformNotSupportedException(); } /// + /// __m512i _mm512_cvt_roundps_epi32 (__m512 a, int rounding) + /// VCVTPS2DQ zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Int32(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512i _mm512_cvttps_epi32 (__m512 a) /// VCVTTPS2DQ zmm1 {k1}{z}, zmm2/m512/m32bcst{sae} /// @@ -2154,6 +2278,11 @@ internal X64() { } /// public static Vector512 ConvertToVector512UInt32(Vector512 value) { throw new PlatformNotSupportedException(); } /// + /// __m512i _mm512_cvt_roundps_epu32 (__m512 a, int rounding) + /// VCVTPS2UDQ zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512UInt32(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512i _mm512_cvttps_epu32 (__m512 a) /// VCVTTPS2UDQ zmm1 {k1}{z}, zmm2/m512/m32bcst{er} /// @@ -2199,7 +2328,26 @@ internal X64() { } /// VDIVPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} /// public static Vector512 Divide(Vector512 left, Vector512 right) { throw new PlatformNotSupportedException(); } - + /// + /// __m512 _mm512_div_round_ps (__m512 a, __m512 b, int rounding) + /// VDIVPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Divide(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m512d _mm512_div_round_pd (__m512d a, __m512d b, int rounding) + /// VDIVPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Divide(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_div_round_sd (__m128d a, __m128d b, int rounding) + /// VDIVSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 DivideScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_div_round_ss (__m128 a, __m128 b, int rounding) + /// VDIVSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 DivideScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512 _mm512_moveldup_ps (__m512 a) /// VMOVSLDUP zmm1 {k1}{z}, zmm2/m512 @@ -2345,11 +2493,31 @@ internal X64() { } /// VFMADDPS zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst /// public static Vector512 FusedMultiplyAdd(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } + /// + /// __m512 _mm512_fmadd_round_ps (__m512 a, __m512 b, __m512 c, int r) + /// VFMADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAdd(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512d _mm512_fmadd_pd (__m512d a, __m512d b, __m512d c) /// VFMADDPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst /// public static Vector512 FusedMultiplyAdd(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } + /// + /// __m512d _mm512_fmadd_round_pd (__m512d a, __m512d b, __m512d c, int r) + /// VFMADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAdd(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_fmadd_round_ss (__m128 a, __m128 b, __m128 c, int r) + /// VFMADDSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplyAddScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_fmadd_round_sd (__m128d a, __m128d b, __m128d c, int r) + /// VFMADDSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplyAddScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512 _mm512_fmaddsub_ps (__m512 a, __m512 b, __m512 c) @@ -2357,10 +2525,20 @@ internal X64() { } /// public static Vector512 FusedMultiplyAddSubtract(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } /// + /// __m512 _mm512_fmaddsub_ps (__m512 a, __m512 b, __m512 c, int c) + /// VFMADDSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAddSubtract(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512d _mm512_fmaddsub_pd (__m512d a, __m512d b, __m512d c) /// VFMADDSUBPD zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst /// public static Vector512 FusedMultiplyAddSubtract(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } + /// + /// __m512d _mm512_fmaddsub_pd (__m512d a, __m512d b, __m512d c, int c) + /// VFMADDSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAddSubtract(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512 _mm512_fmsub_ps (__m512 a, __m512 b, __m512 c) @@ -2368,21 +2546,50 @@ internal X64() { } /// public static Vector512 FusedMultiplySubtract(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } /// + /// __m512 _mm512_fmsub_round_ps (__m512 a, __m512 b, __m512 c, int r) + /// VFMSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtract(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512d _mm512_fmsub_pd (__m512d a, __m512d b, __m512d c) /// VFMSUBPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst /// public static Vector512 FusedMultiplySubtract(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } - + /// + /// __m512d _mm512_fmsub_round_pd (__m512d a, __m512d b, __m512d c, int r) + /// VFMSUBPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtract(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_fmsub_round_ss (__m128 a, __m128 b, __m128 c, int r) + /// VFMSUBSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplySubtractScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_fmsub_round_sd (__m128d a, __m128d b, __m128d c, int r) + /// VFMSUBSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplySubtractScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512 _mm512_fmsubadd_ps (__m512 a, __m512 b, __m512 c) /// VFMSUBADDPS zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst /// public static Vector512 FusedMultiplySubtractAdd(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } /// + /// __m512 _mm512_fmsubadd_round_ps (__m512 a, __m512 b, __m512 c) + /// VFMSUBADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtractAdd(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512d _mm512_fmsubadd_pd (__m512d a, __m512d b, __m512d c) /// VFMSUBADDPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst /// public static Vector512 FusedMultiplySubtractAdd(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } + /// + /// __m512d _mm512_fmsubadd_round_ps (__m512d a, __m512d b, __m512d c) + /// VFMSUBADDPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtractAdd(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512 _mm512_fnmadd_ps (__m512 a, __m512 b, __m512 c) @@ -2390,21 +2597,60 @@ internal X64() { } /// public static Vector512 FusedMultiplyAddNegated(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } /// + /// __m512 _mm512_fnmadd_round_ps (__m512 a, __m512 b, __m512 c, int r) + /// VFNMADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAddNegated(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512d _mm512_fnmadd_pd (__m512d a, __m512d b, __m512d c) /// VFNMADDPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst /// public static Vector512 FusedMultiplyAddNegated(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } - + /// + /// __m512d _mm512_fnmadd_round_pdd (__m512d a, __m512d b, __m512d c, int r) + /// VFNMADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAddNegated(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_fnmadd_round_ss (__m128 a, __m128 b, __m128 c, int r) + /// VFNMADDSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplyAddNegatedScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_fnmadd_round_sd (__m128d a, __m128d b, __m128d c, int r) + /// VFNMADDSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplyAddNegatedScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512 _mm512_fnmsub_ps (__m512 a, __m512 b, __m512 c) /// VFNMSUBPS zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst /// public static Vector512 FusedMultiplySubtractNegated(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } /// + /// __m512 _mm512_fnmsub_round_ps (__m512 a, __m512 b, __m512 c, int r) + /// VFNMSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtractNegated(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// /// __m512d _mm512_fnmsub_pd (__m512d a, __m512d b, __m512d c) /// VFNMSUBPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst /// public static Vector512 FusedMultiplySubtractNegated(Vector512 a, Vector512 b, Vector512 c) { throw new PlatformNotSupportedException(); } +/// + /// __m512d _mm512_fnmsub_round_pd (__m512d a, __m512d b, __m512d c, int r) + /// VFNMSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtractNegated(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_fnmsub_round_ss (__m128 a, __m128 b, __m128 c, int r) + /// VFNMSUBSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplySubtractNegatedScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_fnmsub_round_sd (__m128d a, __m128d b, __m128d c, int r) + /// VFNMSUBSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplySubtractNegatedScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512 _mm512_getexp_ps (__m512 a) @@ -2801,7 +3047,26 @@ internal X64() { } /// VMULPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} /// public static Vector512 Multiply(Vector512 left, Vector512 right) { throw new PlatformNotSupportedException(); } - + /// + /// __m512 _mm512_mul_round_ps (__m512 a, __m512 b, int rounding) + /// VMULPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Multiply(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m512d _mm512_mul_round_pd (__m512d a, __m512d b, int rounding) + /// VMULPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Multiply(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_mul_round_ss (__m128 a, __m128 b, int rounding) + /// VMULSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 MultiplyScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_mul_round_sd (__m128d a, __m128d b, int rounding) + /// VMULSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 MultiplyScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512i _mm512_mullo_epi32 (__m512i a, __m512i b) /// VPMULLD zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst @@ -3160,7 +3425,16 @@ internal X64() { } /// VSCALEFPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} /// public static Vector512 Scale(Vector512 left, Vector512 right) { throw new PlatformNotSupportedException(); } - + /// + /// __m512 _mm512_scalef_round_ps (__m512 a, __m512 b, int rounding) + /// VSCALEFPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Scale(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m512d _mm512_scalef_round_pd (__m512d a, __m512d b, int rounding) + /// VSCALEFPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Scale(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m128 _mm_scalef_ss (__m128 a, __m128 b) /// VSCALEFSS xmm1 {k1}{z}, xmm2, xmm3/m32{er} @@ -3171,7 +3445,16 @@ internal X64() { } /// VSCALEFSD xmm1 {k1}{z}, xmm2, xmm3/m64{er} /// public static Vector128 ScaleScalar(Vector128 left, Vector128 right) { throw new PlatformNotSupportedException(); } - + /// + /// __m128 _mm_scalef_round_ss (__m128 a, __m128 b) + /// VSCALEFSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 ScaleScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_scalef_round_sd (__m128d a, __m128d b) + /// VSCALEFSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 ScaleScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512i _mm512_sll_epi32 (__m512i a, __m128i count) /// VPSLLD zmm1 {k1}{z}, zmm2, xmm3/m128 @@ -3394,7 +3677,26 @@ internal X64() { } /// VSQRTPD zmm1 {k1}{z}, zmm2/m512/m64bcst{er} /// public static Vector512 Sqrt(Vector512 value) { throw new PlatformNotSupportedException(); } - + /// + /// __m512 _mm512_sqrt_round_ps (__m512 a, int rounding) + /// VSQRTPS zmm1, zmm2 {er} + /// + public static Vector512 Sqrt(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m512d _mm512_sqrt_round_pd (__m512d a, int rounding) + /// VSQRTPD zmm1, zmm2 {er} + /// + public static Vector512 Sqrt(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_sqrt_round_ss (__m128 a, __m128 b, int rounding) + /// VSQRTSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 SqrtScalar(Vector128 upper, Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_sqrt_round_sd (__m128d a, __m128d b, int rounding) + /// VSQRTSD xmm1, xmm2 xmm3 {er} + /// + public static Vector128 SqrtScalar(Vector128 upper, Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// void _mm512_storeu_si512 (__m512i * mem_addr, __m512i a) /// VMOVDQU32 m512 {k1}{z}, zmm1 @@ -3578,6 +3880,26 @@ internal X64() { } /// VSUBPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} /// public static Vector512 Subtract(Vector512 left, Vector512 right) { throw new PlatformNotSupportedException(); } + /// + /// __m512 _mm512_sub_round_ps (__m512 a, __m512 b, int rounding) + /// VSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Subtract(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m512d _mm512_sub_round_pd (__m512d a, __m512d b, int rounding) + /// VSUBPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Subtract(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128 _mm_sub_round_ss (__m128 a, __m128 b, int rounding) + /// VSUBSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 SubtractScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } + /// + /// __m128d _mm_sub_round_sd (__m128d a, __m128d b, int rounding) + /// VSUBSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 SubtractScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) { throw new PlatformNotSupportedException(); } /// /// __m512i _mm512_ternarylogic_si512 (__m512i a, __m512i b, __m512i c, int imm) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.cs index fa0ebeaa816230..3227da13b99cc6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/Avx512F.cs @@ -1262,6 +1262,12 @@ internal X64() { } public static new bool IsSupported { get => IsSupported; } + /// + /// __m128 _mm_cvt_roundi64_ss (__m128 a, __int64 b, int rounding) + /// VCVTSI2SS xmm1, xmm2, r64 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, long value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertScalarToVector128Single(upper, value, mode); /// /// __m128 _mm_cvtsi64_ss (__m128 a, __int64 b) /// VCVTUSI2SS xmm1, xmm2, r/m64 @@ -1269,12 +1275,42 @@ internal X64() { } /// public static Vector128 ConvertScalarToVector128Single(Vector128 upper, ulong value) => ConvertScalarToVector128Single(upper, value); /// + /// __m128 _mm_cvt_roundu64_ss (__m128 a, unsigned __int64 b, int rounding) + /// VCVTUSI2SS xmm1, xmm2, r64 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, ulong value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertScalarToVector128Single(upper, value, mode); + /// + /// __m128d _mm_cvt_roundsi64_sd (__m128d a, __int64 b, int rounding) + /// VCVTSI2SD xmm1, xmm2, r64 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static Vector128 ConvertScalarToVector128Double(Vector128 upper, long value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertScalarToVector128Double(upper, value, mode); + /// /// __m128d _mm_cvtsi64_sd (__m128d a, __int64 b) /// VCVTUSI2SD xmm1, xmm2, r/m64 /// This intrinsic is only available on 64-bit processes /// public static Vector128 ConvertScalarToVector128Double(Vector128 upper, ulong value) => ConvertScalarToVector128Double(upper, value); + /// + /// __m128d _mm_cvt_roundu64_sd (__m128d a, unsigned __int64 b, int rounding) + /// VCVTUSI2SD xmm1, xmm2, r64 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static Vector128 ConvertScalarToVector128Double(Vector128 upper, ulong value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertScalarToVector128Double(upper, value, mode); + /// + /// __int64 _mm_cvt_roundss_i64 (__m128 a, int rounding) + /// VCVTSS2SI r64, xmm1 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static long ConvertToInt64(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToInt64(value, mode); + /// + /// __int64 _mm_cvt_roundsd_i64 (__m128d a, int rounding) + /// VCVTSD2SI r64, xmm1 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static long ConvertToInt64(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToInt64(value, mode); /// /// unsigned __int64 _mm_cvtss_u64 (__m128 a) /// VCVTSS2USI r64, xmm1/m32{er} @@ -1282,11 +1318,23 @@ internal X64() { } /// public static ulong ConvertToUInt64(Vector128 value) => ConvertToUInt64(value); /// + /// unsigned __int64 _mm_cvt_roundss_u64 (__m128 a, int rounding) + /// VCVTSS2USI r64, xmm1 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static ulong ConvertToUInt64(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToUInt64(value, mode); + /// /// unsigned __int64 _mm_cvtsd_u64 (__m128d a) /// VCVTSD2USI r64, xmm1/m64{er} /// This intrinsic is only available on 64-bit processes /// public static ulong ConvertToUInt64(Vector128 value) => ConvertToUInt64(value); + /// + /// unsigned __int64 _mm_cvt_roundsd_u64 (__m128d a, int rounding) + /// VCVTSD2USI r64, xmm1 {er} + /// This intrinsic is only available on 64-bit processes + /// + public static ulong ConvertToUInt64(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToUInt64(value, mode); /// /// unsigned __int64 _mm_cvttss_u64 (__m128 a) @@ -1339,8 +1387,8 @@ internal X64() { } /// public static Vector512 Add(Vector512 left, Vector512 right) => Add(left, right); /// - /// __m512d _mm512_add_pd (__m512d a, __m512d b) - /// VADDPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} + /// __m512d _mm512_add_round_pd (__m512d a, __m512d b, int rounding) + /// VADDPD zmm1, zmm2, zmm3 {er} /// public static Vector512 Add(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Add(left, right, mode); /// @@ -1348,7 +1396,21 @@ internal X64() { } /// VADDPS zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst{er} /// public static Vector512 Add(Vector512 left, Vector512 right) => Add(left, right); - + /// + /// __m512 _mm512_add_round_ps (__m512 a, __m512 b, int rounding) + /// VADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Add(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Add(left, right, mode); + /// + /// __m128 _mm_add_round_ss (__m128 a, __m128 b, int rounding) + /// VADDSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 AddScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => AddScalar(left, right, mode); + /// + /// __m128d _mm_add_round_sd (__m128d a, __m128d b, int rounding) + /// VADDSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 AddScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => AddScalar(left, right, mode); /// /// __m512i _mm512_alignr_epi32 (__m512i a, __m512i b, const int count) /// VALIGND zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst, imm8 @@ -1833,22 +1895,57 @@ internal X64() { } /// public static Vector128 ConvertScalarToVector128Single(Vector128 upper, uint value) => ConvertScalarToVector128Single(upper, value); /// + /// __m128 _mm_cvt_roundi32_ss (__m128 a, int b, int rounding) + /// VCVTUSI2SS xmm1, xmm2, r32 {er} + /// + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, uint value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertScalarToVector128Single(upper, value, mode); + /// + /// __m128 _mm_cvt_roundi32_ss (__m128 a, int b, int rounding) + /// VCVTSI2SS xmm1, xmm2, r32 {er} + /// + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, int value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertScalarToVector128Single(upper, value, mode); + /// + /// __m128 _mm_cvt_roundsd_ss (__m128 a, __m128d b, int rounding) + /// VCVTSD2SS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 ConvertScalarToVector128Single(Vector128 upper, Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertScalarToVector128Single(upper, value, mode); + /// /// __m128d _mm_cvtsi32_sd (__m128d a, int b) /// VCVTUSI2SD xmm1, xmm2, r/m32 /// public static Vector128 ConvertScalarToVector128Double(Vector128 upper, uint value) => ConvertScalarToVector128Double(upper, value); + /// + /// int _mm_cvt_roundss_i32 (__m128 a, int rounding) + /// VCVTSS2SIK r32, xmm1 {er} + /// + public static int ConvertToInt32(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToInt32(value, mode); + /// + /// int _mm_cvt_roundsd_i32 (__m128d a, int rounding) + /// VCVTSD2SI r32, xmm1 {er} + /// + public static int ConvertToInt32(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToInt32(value, mode); /// /// unsigned int _mm_cvtss_u32 (__m128 a) /// VCVTSS2USI r32, xmm1/m32{er} /// public static uint ConvertToUInt32(Vector128 value) => ConvertToUInt32(value); /// + /// unsigned int _mm_cvt_roundss_u32 (__m128 a, int rounding) + /// VCVTSS2USI r32, xmm1 {er} + /// + public static uint ConvertToUInt32(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToUInt32(value, mode); + /// /// unsigned int _mm_cvtsd_u32 (__m128d a) /// VCVTSD2USI r32, xmm1/m64{er} /// public static uint ConvertToUInt32(Vector128 value) => ConvertToUInt32(value); /// + /// unsigned int _mm_cvt_roundsd_u32 (__m128d a, int rounding) + /// VCVTSD2USI r32, xmm1 {er} + /// + public static uint ConvertToUInt32(Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToUInt32(value, mode); + /// /// unsigned int _mm_cvttss_u32 (__m128 a) /// VCVTTSS2USI r32, xmm1/m32{er} /// @@ -1975,6 +2072,11 @@ internal X64() { } /// public static Vector256 ConvertToVector256Int32(Vector512 value) => ConvertToVector256Int32(value); /// + /// __m256i _mm512_cvt_roundpd_epi32 (__m512d a, int rounding) + /// VCVTPD2DQ ymm1, zmm2 {er} + /// + public static Vector256 ConvertToVector256Int32(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector256Int32(value, mode); + /// /// __m256i _mm512_cvtepi64_epi32 (__m512i a) /// VPMOVQD ymm1/m256 {k1}{z}, zmm2 /// @@ -2001,6 +2103,11 @@ internal X64() { } /// VCVTPD2PS ymm1 {k1}{z}, zmm2/m512/m64bcst{er} /// public static Vector256 ConvertToVector256Single(Vector512 value) => ConvertToVector256Single(value); + /// + /// __m256 _mm512_cvt_roundpd_ps (__m512d a, int rounding) + /// VCVTPD2PS ymm1, zmm2 {er} + /// + public static Vector256 ConvertToVector256Single(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector256Single(value, mode); /// /// __m256i _mm512_cvtepi32_epi16 (__m512i a) @@ -2024,6 +2131,11 @@ internal X64() { } /// public static Vector256 ConvertToVector256UInt32(Vector512 value) => ConvertToVector256UInt32(value); /// + ///__m256i _mm512_cvt_roundpd_epu32 (__m512d a, int rounding) + /// VCVTPD2UDQ ymm1, zmm2 {er} + /// + public static Vector256 ConvertToVector256UInt32(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector256UInt32(value, mode); + /// /// __m256i _mm512_cvtepi64_epi32 (__m512i a) /// VPMOVQD ymm1/m256 {k1}{z}, zmm2 /// @@ -2085,6 +2197,11 @@ internal X64() { } /// public static Vector512 ConvertToVector512Int32(Vector512 value) => ConvertToVector512Int32(value); /// + /// __m512i _mm512_cvt_roundps_epi32 (__m512 a, int rounding) + /// VCVTPS2DQ zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Int32(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector512Int32(value, mode); + /// /// __m512i _mm512_cvttps_epi32 (__m512 a) /// VCVTTPS2DQ zmm1 {k1}{z}, zmm2/m512/m32bcst{sae} /// @@ -2125,11 +2242,21 @@ internal X64() { } /// public static Vector512 ConvertToVector512Single(Vector512 value) => ConvertToVector512Single(value); /// + /// __m512 _mm512_cvt_roundepi32_ps (__m512i a, int rounding) + /// VCVTDQ2PS zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Single(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector512Single(value, mode); + /// /// __m512 _mm512_cvtepu32_ps (__m512i a) /// VCVTUDQ2PS zmm1 {k1}{z}, zmm2/m512/m32bcst{er} /// public static Vector512 ConvertToVector512Single(Vector512 value) => ConvertToVector512Single(value); /// + /// __m512 _mm512_cvt_roundepi32_ps (__m512i a, int rounding) + /// VCVTUDQ2PS zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512Single(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector512Single(value, mode); + /// /// __m512i _mm512_cvtepi8_epi32 (__m128i a) /// VPMOVSXBD zmm1 {k1}{z}, xmm2/m128 /// @@ -2155,6 +2282,11 @@ internal X64() { } /// public static Vector512 ConvertToVector512UInt32(Vector512 value) => ConvertToVector512UInt32(value); /// + /// __m512i _mm512_cvt_roundps_epu32 (__m512 a, int rounding) + /// VCVTPS2UDQ zmm1, zmm2 {er} + /// + public static Vector512 ConvertToVector512UInt32(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ConvertToVector512UInt32(value, mode); + /// /// __m512i _mm512_cvttps_epu32 (__m512 a) /// VCVTTPS2UDQ zmm1 {k1}{z}, zmm2/m512/m32bcst{er} /// @@ -2200,7 +2332,26 @@ internal X64() { } /// VDIVPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} /// public static Vector512 Divide(Vector512 left, Vector512 right) => Divide(left, right); - + /// + /// __m512 _mm512_div_round_ps (__m512 a, __m512 b, int rounding) + /// VDIVPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Divide(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Divide(left, right, mode); + /// + /// __m512d _mm512_div_round_pd (__m512d a, __m512d b, int rounding) + /// VDIVPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Divide(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Divide(left, right, mode); + /// + /// __m128 _mm_div_round_ss (__m128 a, __m128 b, int rounding) + /// VDIVSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 DivideScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => DivideScalar(left, right, mode); + /// + /// __m128d _mm_div_round_sd (__m128d a, __m128d b, int rounding) + /// VDIVSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 DivideScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => DivideScalar(left, right, mode); /// /// __m512 _mm512_moveldup_ps (__m512 a) /// VMOVSLDUP zmm1 {k1}{z}, zmm2/m512 @@ -2347,10 +2498,30 @@ internal X64() { } /// public static Vector512 FusedMultiplyAdd(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplyAdd(a, b, c); /// + /// __m512 _mm512_fmadd_round_ps (__m512 a, __m512 b, __m512 c, int r) + /// VFMADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAdd(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplyAdd(a, b, c, mode); + /// /// __m512d _mm512_fmadd_pd (__m512d a, __m512d b, __m512d c) /// VFMADDPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst /// public static Vector512 FusedMultiplyAdd(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplyAdd(a, b, c); + /// + /// __m512d _mm512_fmadd_round_pd (__m512d a, __m512d b, __m512d c, int r) + /// VFMADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAdd(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplyAdd(a, b, c, mode); + /// + /// __m128 _mm_fmadd_round_ss (__m128 a, __m128 b, __m128 c, int r) + /// VFMADDSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplyAddScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplyAddScalar(a, b, c, mode); + /// + /// __m128d _mm_fmadd_round_sd (__m128d a, __m128d b, __m128d c, int r) + /// VFMADDSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplyAddScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplyAddScalar(a, b, c, mode); /// /// __m512 _mm512_fmaddsub_ps (__m512 a, __m512 b, __m512 c) @@ -2358,10 +2529,20 @@ internal X64() { } /// public static Vector512 FusedMultiplyAddSubtract(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplyAddSubtract(a, b, c); /// + /// __m512 _mm512_fmaddsub_ps (__m512 a, __m512 b, __m512 c, int c) + /// VFMADDSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAddSubtract(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplyAddSubtract(a, b, c, mode); + /// /// __m512d _mm512_fmaddsub_pd (__m512d a, __m512d b, __m512d c) /// VFMADDSUBPD zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst /// public static Vector512 FusedMultiplyAddSubtract(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplyAddSubtract(a, b, c); + /// + /// __m512d _mm512_fmaddsub_pd (__m512d a, __m512d b, __m512d c, int c) + /// VFMADDSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAddSubtract(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplyAddSubtract(a, b, c, mode); /// /// __m512 _mm512_fmsub_ps (__m512 a, __m512 b, __m512 c) @@ -2369,10 +2550,30 @@ internal X64() { } /// public static Vector512 FusedMultiplySubtract(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplySubtract(a, b, c); /// + /// __m512 _mm512_fmsub_round_ps (__m512 a, __m512 b, __m512 c, int r) + /// VFMSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtract(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplySubtract(a, b, c, mode); + /// /// __m512d _mm512_fmsub_pd (__m512d a, __m512d b, __m512d c) /// VFMSUBPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst /// public static Vector512 FusedMultiplySubtract(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplySubtract(a, b, c); + /// + /// __m512d _mm512_fmsub_round_pd (__m512d a, __m512d b, __m512d c, int r) + /// VFMSUBPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtract(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplySubtract(a, b, c, mode); + /// + /// __m128 _mm_fmsub_round_ss (__m128 a, __m128 b, __m128 c, int r) + /// VFMSUBSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplySubtractScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplySubtractScalar(a, b, c, mode); + /// + /// __m128d _mm_fmsub_round_sd (__m128d a, __m128d b, __m128d c, int r) + /// VFMSUBSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplySubtractScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplySubtractScalar(a, b, c, mode); /// /// __m512 _mm512_fmsubadd_ps (__m512 a, __m512 b, __m512 c) @@ -2380,10 +2581,20 @@ internal X64() { } /// public static Vector512 FusedMultiplySubtractAdd(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplySubtractAdd(a, b, c); /// + /// __m512 _mm512_fmsubadd_round_ps (__m512 a, __m512 b, __m512 c) + /// VFMSUBADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtractAdd(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplySubtractAdd(a, b, c, mode); + /// /// __m512d _mm512_fmsubadd_pd (__m512d a, __m512d b, __m512d c) /// VFMSUBADDPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst /// public static Vector512 FusedMultiplySubtractAdd(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplySubtractAdd(a, b, c); + /// + /// __m512d _mm512_fmsubadd_round_ps (__m512d a, __m512d b, __m512d c) + /// VFMSUBADDPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtractAdd(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplySubtractAdd(a, b, c, mode); /// /// __m512 _mm512_fnmadd_ps (__m512 a, __m512 b, __m512 c) @@ -2391,10 +2602,30 @@ internal X64() { } /// public static Vector512 FusedMultiplyAddNegated(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplyAddNegated(a, b, c); /// + /// __m512 _mm512_fnmadd_round_ps (__m512 a, __m512 b, __m512 c, int r) + /// VFNMADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAddNegated(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplyAddNegated(a, b, c, mode); + /// /// __m512d _mm512_fnmadd_pd (__m512d a, __m512d b, __m512d c) /// VFNMADDPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst /// public static Vector512 FusedMultiplyAddNegated(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplyAddNegated(a, b, c); + /// + /// __m512d _mm512_fnmadd_round_pdd (__m512d a, __m512d b, __m512d c, int r) + /// VFNMADDPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplyAddNegated(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplyAddNegated(a, b, c, mode); + /// + /// __m128 _mm_fnmadd_round_ss (__m128 a, __m128 b, __m128 c, int r) + /// VFNMADDSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplyAddNegatedScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplyAddNegatedScalar(a, b, c, mode); + /// + /// __m128d _mm_fnmadd_round_sd (__m128d a, __m128d b, __m128d c, int r) + /// VFNMADDSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplyAddNegatedScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplyAddNegatedScalar(a, b, c, mode); /// /// __m512 _mm512_fnmsub_ps (__m512 a, __m512 b, __m512 c) @@ -2402,10 +2633,30 @@ internal X64() { } /// public static Vector512 FusedMultiplySubtractNegated(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplySubtractNegated(a, b, c); /// + /// __m512 _mm512_fnmsub_round_ps (__m512 a, __m512 b, __m512 c, int r) + /// VFNMSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtractNegated(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplySubtractNegated(a, b, c, mode); + /// /// __m512d _mm512_fnmsub_pd (__m512d a, __m512d b, __m512d c) /// VFNMSUBPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst /// public static Vector512 FusedMultiplySubtractNegated(Vector512 a, Vector512 b, Vector512 c) => FusedMultiplySubtractNegated(a, b, c); + /// + /// __m512d _mm512_fnmsub_round_pd (__m512d a, __m512d b, __m512d c, int r) + /// VFNMSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 FusedMultiplySubtractNegated(Vector512 a, Vector512 b, Vector512 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplySubtractNegated(a, b, c, mode); + /// + /// __m128 _mm_fnmsub_round_ss (__m128 a, __m128 b, __m128 c, int r) + /// VFNMSUBSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplySubtractNegatedScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplySubtractNegatedScalar(a, b, c, mode); + /// + /// __m128d _mm_fnmsub_round_sd (__m128d a, __m128d b, __m128d c, int r) + /// VFNMSUBSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 FusedMultiplySubtractNegatedScalar(Vector128 a, Vector128 b, Vector128 c, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => FusedMultiplySubtractNegatedScalar(a, b, c, mode); /// /// __m512 _mm512_getexp_ps (__m512 a) @@ -2802,7 +3053,26 @@ internal X64() { } /// VMULPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} /// public static Vector512 Multiply(Vector512 left, Vector512 right) => Multiply(left, right); - + /// + /// __m512 _mm512_mul_round_ps (__m512 a, __m512 b, int rounding) + /// VMULPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Multiply(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Multiply(left, right, mode); + /// + /// __m512d _mm512_mul_round_pd (__m512d a, __m512d b, int rounding) + /// VMULPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Multiply(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Multiply(left, right, mode); + /// + /// __m128 _mm_mul_round_ss (__m128 a, __m128 b, int rounding) + /// VMULSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 MultiplyScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => MultiplyScalar(left, right, mode); + /// + /// __m128d _mm_mul_round_sd (__m128d a, __m128d b, int rounding) + /// VMULSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 MultiplyScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => MultiplyScalar(left, right, mode); /// /// __m512i _mm512_mullo_epi32 (__m512i a, __m512i b) /// VPMULLD zmm1 {k1}{z}, zmm2, zmm3/m512/m32bcst @@ -3161,6 +3431,16 @@ internal X64() { } /// VSCALEFPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} /// public static Vector512 Scale(Vector512 left, Vector512 right) => Scale(left, right); + /// + /// __m512 _mm512_scalef_round_ps (__m512 a, __m512 b, int rounding) + /// VSCALEFPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Scale(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Scale(left, right, mode); + /// + /// __m512d _mm512_scalef_round_pd (__m512d a, __m512d b, int rounding) + /// VSCALEFPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Scale(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Scale(left, right, mode); /// /// __m128 _mm_scalef_ss (__m128 a, __m128 b) @@ -3172,6 +3452,16 @@ internal X64() { } /// VSCALEFSD xmm1 {k1}{z}, xmm2, xmm3/m64{er} /// public static Vector128 ScaleScalar(Vector128 left, Vector128 right) => ScaleScalar(left, right); + /// + /// __m128 _mm_scalef_round_ss (__m128 a, __m128 b) + /// VSCALEFSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 ScaleScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ScaleScalar(left, right, mode); + /// + /// __m128d _mm_scalef_round_sd (__m128d a, __m128d b) + /// VSCALEFSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 ScaleScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => ScaleScalar(left, right, mode); /// /// __m512i _mm512_sll_epi32 (__m512i a, __m128i count) @@ -3394,6 +3684,26 @@ internal X64() { } /// VSQRTPD zmm1 {k1}{z}, zmm2/m512/m64bcst{er} /// public static Vector512 Sqrt(Vector512 value) => Sqrt(value); + /// + /// __m512 _mm512_sqrt_round_ps (__m512 a, int rounding) + /// VSQRTPS zmm1, zmm2 {er} + /// + public static Vector512 Sqrt(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Sqrt(value, mode); + /// + /// __m512d _mm512_sqrt_round_pd (__m512d a, int rounding) + /// VSQRTPD zmm1, zmm2 {er} + /// + public static Vector512 Sqrt(Vector512 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Sqrt(value, mode); + /// + /// __m128 _mm_sqrt_round_ss (__m128 a, __m128 b, int rounding) + /// VSQRTSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 SqrtScalar(Vector128 upper, Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => SqrtScalar(upper, value, mode); + /// + /// __m128d _mm_sqrt_round_sd (__m128d a, __m128d b, int rounding) + /// VSQRTSD xmm1, xmm2 xmm3 {er} + /// + public static Vector128 SqrtScalar(Vector128 upper, Vector128 value, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => SqrtScalar(upper, value, mode); /// /// void _mm512_storeu_si512 (__m512i * mem_addr, __m512i a) @@ -3578,6 +3888,26 @@ internal X64() { } /// VSUBPD zmm1 {k1}{z}, zmm2, zmm3/m512/m64bcst{er} /// public static Vector512 Subtract(Vector512 left, Vector512 right) => Subtract(left, right); + /// + /// __m512 _mm512_sub_round_ps (__m512 a, __m512 b, int rounding) + /// VSUBPS zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Subtract(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Subtract(left, right, mode); + /// + /// __m512d _mm512_sub_round_pd (__m512d a, __m512d b, int rounding) + /// VSUBPD zmm1, zmm2, zmm3 {er} + /// + public static Vector512 Subtract(Vector512 left, Vector512 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => Subtract(left, right, mode); + /// + /// __m128 _mm_sub_round_ss (__m128 a, __m128 b, int rounding) + /// VSUBSS xmm1, xmm2, xmm3 {er} + /// + public static Vector128 SubtractScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => SubtractScalar(left, right, mode); + /// + /// __m128d _mm_sub_round_sd (__m128d a, __m128d b, int rounding) + /// VSUBSD xmm1, xmm2, xmm3 {er} + /// + public static Vector128 SubtractScalar(Vector128 left, Vector128 right, [ConstantExpected(Max = FloatRoundingMode.ToZero)] FloatRoundingMode mode) => SubtractScalar(left, right, mode); /// /// __m512i _mm512_ternarylogic_si512 (__m512i a, __m512i b, __m512i c, int imm) diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index ed5bd64642997c..48beef8f254073 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -5107,10 +5107,18 @@ internal Avx512DQ() { } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Double(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int64(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int64(System.Runtime.Intrinsics.Vector256 value) { throw null; } + public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Single(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Single(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Double(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Double(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int64(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int64(System.Runtime.Intrinsics.Vector256 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int64WithTruncation(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int64WithTruncation(System.Runtime.Intrinsics.Vector256 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt64(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt64(System.Runtime.Intrinsics.Vector256 value) { throw null; } + public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt64(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt64(System.Runtime.Intrinsics.Vector256 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt64WithTruncation(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt64WithTruncation(System.Runtime.Intrinsics.Vector256 value) { throw null; } public static new System.Runtime.Intrinsics.Vector128 ExtractVector128(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte index) { throw null; } @@ -5205,8 +5213,11 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Add(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 AddScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 AddScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 AlignRight32(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte mask) { throw null; } public static System.Runtime.Intrinsics.Vector512 AlignRight32(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte mask) { throw null; } public static System.Runtime.Intrinsics.Vector512 AlignRight64(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte mask) { throw null; } @@ -5297,8 +5308,15 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector512 CompareUnordered(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Double(System.Runtime.Intrinsics.Vector128 upper, uint value) { throw null; } public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Single(System.Runtime.Intrinsics.Vector128 upper, uint value) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Single(System.Runtime.Intrinsics.Vector128 upper, int value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Single(System.Runtime.Intrinsics.Vector128 upper, uint value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Single(System.Runtime.Intrinsics.Vector128 upper, System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static int ConvertToInt32(System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static int ConvertToInt32(System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static uint ConvertToUInt32(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static uint ConvertToUInt32(System.Runtime.Intrinsics.Vector128 value) { throw null; } + public static uint ConvertToUInt32(System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static uint ConvertToUInt32(System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static uint ConvertToUInt32WithTruncation(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static uint ConvertToUInt32WithTruncation(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static System.Runtime.Intrinsics.Vector128 ConvertToVector128Byte(System.Runtime.Intrinsics.Vector512 value) { throw null; } @@ -5323,15 +5341,18 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Int16(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Int16WithSaturation(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Int32(System.Runtime.Intrinsics.Vector512 value) { throw null; } + public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Int32(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Int32(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Int32(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Int32WithSaturation(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Int32WithTruncation(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Single(System.Runtime.Intrinsics.Vector512 value) { throw null; } + public static System.Runtime.Intrinsics.Vector256 ConvertToVector256Single(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256UInt16(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256UInt16(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256UInt16WithSaturation(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256UInt32(System.Runtime.Intrinsics.Vector512 value) { throw null; } + public static System.Runtime.Intrinsics.Vector256 ConvertToVector256UInt32(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256UInt32(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256UInt32(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector256 ConvertToVector256UInt32WithSaturation(System.Runtime.Intrinsics.Vector512 value) { throw null; } @@ -5343,6 +5364,7 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int32(System.Runtime.Intrinsics.Vector256 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int32(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int32(System.Runtime.Intrinsics.Vector512 value) { throw null; } + public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int32(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int32(System.Runtime.Intrinsics.Vector256 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int32WithTruncation(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int64(System.Runtime.Intrinsics.Vector128 value) { throw null; } @@ -5352,11 +5374,14 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int64(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Int64(System.Runtime.Intrinsics.Vector256 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Single(System.Runtime.Intrinsics.Vector512 value) { throw null; } + public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Single(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Single(System.Runtime.Intrinsics.Vector512 value) { throw null; } + public static System.Runtime.Intrinsics.Vector512 ConvertToVector512Single(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt32(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt32(System.Runtime.Intrinsics.Vector256 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt32(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt32(System.Runtime.Intrinsics.Vector512 value) { throw null; } + public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt32(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt32(System.Runtime.Intrinsics.Vector256 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt32WithTruncation(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt64(System.Runtime.Intrinsics.Vector128 value) { throw null; } @@ -5367,6 +5392,10 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector512 ConvertToVector512UInt64(System.Runtime.Intrinsics.Vector256 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 Divide(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Divide(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Divide(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Divide(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 DivideScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 DivideScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 DuplicateEvenIndexed(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 DuplicateEvenIndexed(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 DuplicateOddIndexed(System.Runtime.Intrinsics.Vector512 value) { throw null; } @@ -5395,17 +5424,37 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector128 FixupScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, System.Runtime.Intrinsics.Vector128 table, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte control) { throw null; } public static System.Runtime.Intrinsics.Vector128 FixupScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, System.Runtime.Intrinsics.Vector128 table, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte control) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAdd(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAdd(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAdd(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAdd(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 FusedMultiplyAddScalar(System.Runtime.Intrinsics.Vector128 a, System.Runtime.Intrinsics.Vector128 b, System.Runtime.Intrinsics.Vector128 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 FusedMultiplyAddScalar(System.Runtime.Intrinsics.Vector128 a, System.Runtime.Intrinsics.Vector128 b, System.Runtime.Intrinsics.Vector128 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAddNegated(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAddNegated(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAddNegated(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAddNegated(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 FusedMultiplyAddNegatedScalar(System.Runtime.Intrinsics.Vector128 a, System.Runtime.Intrinsics.Vector128 b, System.Runtime.Intrinsics.Vector128 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 FusedMultiplyAddNegatedScalar(System.Runtime.Intrinsics.Vector128 a, System.Runtime.Intrinsics.Vector128 b, System.Runtime.Intrinsics.Vector128 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAddSubtract(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAddSubtract(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAddSubtract(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplyAddSubtract(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtract(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtract(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtract(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtract(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 FusedMultiplySubtractScalar(System.Runtime.Intrinsics.Vector128 a, System.Runtime.Intrinsics.Vector128 b, System.Runtime.Intrinsics.Vector128 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 FusedMultiplySubtractScalar(System.Runtime.Intrinsics.Vector128 a, System.Runtime.Intrinsics.Vector128 b, System.Runtime.Intrinsics.Vector128 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtractAdd(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtractAdd(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtractAdd(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtractAdd(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtractNegated(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtractNegated(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtractNegated(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c) { throw null; } + public static System.Runtime.Intrinsics.Vector512 FusedMultiplySubtractNegated(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 FusedMultiplySubtractNegatedScalar(System.Runtime.Intrinsics.Vector128 a, System.Runtime.Intrinsics.Vector128 b, System.Runtime.Intrinsics.Vector128 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 FusedMultiplySubtractNegatedScalar(System.Runtime.Intrinsics.Vector128 a, System.Runtime.Intrinsics.Vector128 b, System.Runtime.Intrinsics.Vector128 c, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 GetExponent(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 GetExponent(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector128 GetExponentScalar(System.Runtime.Intrinsics.Vector128 value) { throw null; } @@ -5482,6 +5531,10 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector512 Multiply(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Multiply(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Multiply(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Multiply(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Multiply(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 MultiplyScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 MultiplyScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 MultiplyLow(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 MultiplyLow(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Or(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } @@ -5547,8 +5600,12 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector128 RoundScaleScalar(System.Runtime.Intrinsics.Vector128 upper, System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte control) { throw null; } public static System.Runtime.Intrinsics.Vector512 Scale(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Scale(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Scale(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Scale(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector128 ScaleScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 ScaleScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ScaleScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ScaleScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 ShiftLeftLogical(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte count) { throw null; } public static System.Runtime.Intrinsics.Vector512 ShiftLeftLogical(System.Runtime.Intrinsics.Vector512 value, System.Runtime.Intrinsics.Vector128 count) { throw null; } public static System.Runtime.Intrinsics.Vector512 ShiftLeftLogical(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte count) { throw null; } @@ -5591,6 +5648,10 @@ internal Avx512F() { } public static System.Runtime.Intrinsics.Vector512 Shuffle4x128(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte control) { throw null; } public static System.Runtime.Intrinsics.Vector512 Sqrt(System.Runtime.Intrinsics.Vector512 value) { throw null; } public static System.Runtime.Intrinsics.Vector512 Sqrt(System.Runtime.Intrinsics.Vector512 value) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Sqrt(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Sqrt(System.Runtime.Intrinsics.Vector512 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 SqrtScalar(System.Runtime.Intrinsics.Vector128 upper, System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 SqrtScalar(System.Runtime.Intrinsics.Vector128 upper, System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static unsafe void Store(byte* address, System.Runtime.Intrinsics.Vector512 source) { } public static unsafe void Store(double* address, System.Runtime.Intrinsics.Vector512 source) { } public static unsafe void Store(short* address, System.Runtime.Intrinsics.Vector512 source) { } @@ -5627,6 +5688,10 @@ public static unsafe void StoreAlignedNonTemporal(ulong* address, System.Runtime public static System.Runtime.Intrinsics.Vector512 Subtract(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Subtract(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } public static System.Runtime.Intrinsics.Vector512 Subtract(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Subtract(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector512 Subtract(System.Runtime.Intrinsics.Vector512 left, System.Runtime.Intrinsics.Vector512 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 SubtractScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 SubtractScalar(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static System.Runtime.Intrinsics.Vector512 TernaryLogic(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte control) { throw null; } public static System.Runtime.Intrinsics.Vector512 TernaryLogic(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte control) { throw null; } public static System.Runtime.Intrinsics.Vector512 TernaryLogic(System.Runtime.Intrinsics.Vector512 a, System.Runtime.Intrinsics.Vector512 b, System.Runtime.Intrinsics.Vector512 c, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute] byte control) { throw null; } @@ -5900,9 +5965,17 @@ internal VL() { } internal X64() { } public static new bool IsSupported { get { throw null; } } public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Double(System.Runtime.Intrinsics.Vector128 upper, ulong value) { throw null; } - public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Single(System.Runtime.Intrinsics.Vector128 upper, ulong value) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Single(System.Runtime.Intrinsics.Vector128 upper, ulong value) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Double(System.Runtime.Intrinsics.Vector128 upper, ulong value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Single(System.Runtime.Intrinsics.Vector128 upper, ulong value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Double(System.Runtime.Intrinsics.Vector128 upper, long value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static System.Runtime.Intrinsics.Vector128 ConvertScalarToVector128Single(System.Runtime.Intrinsics.Vector128 upper, long value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static long ConvertToInt64(System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static long ConvertToInt64(System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static ulong ConvertToUInt64(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static ulong ConvertToUInt64(System.Runtime.Intrinsics.Vector128 value) { throw null; } + public static ulong ConvertToUInt64(System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } + public static ulong ConvertToUInt64(System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpected(Max = System.Runtime.Intrinsics.X86.FloatRoundingMode.ToZero)] System.Runtime.Intrinsics.X86.FloatRoundingMode mode) { throw null; } public static ulong ConvertToUInt64WithTruncation(System.Runtime.Intrinsics.Vector128 value) { throw null; } public static ulong ConvertToUInt64WithTruncation(System.Runtime.Intrinsics.Vector128 value) { throw null; } } diff --git a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs index 6e284032934299..20b8942132da02 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs @@ -1522,6 +1522,93 @@ ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Add", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Add", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Add", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Add", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Add", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Add", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Subtract", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Subtract", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Subtract", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Subtract", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Subtract", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Subtract", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Multiply", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Multiply", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Multiply", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Multiply", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Multiply", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Multiply", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Divide", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Divide", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Divide", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Divide", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Divide", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Divide", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Scale", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Scale", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Scale", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Scale", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Scale", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Scale", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Sqrt", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.DoubleToUInt64Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Sqrt", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.DoubleToUInt64Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Sqrt", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.DoubleToUInt64Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Sqrt", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Sqrt", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "Sqrt", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Int32", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Int32", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Int32", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Single", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Single", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Single", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Single", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Single", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Single", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Int32", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Int32", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Int32", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512UInt32", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512UInt32", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512UInt32", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256UInt32", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256UInt32", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256UInt32", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAdd", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAdd", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAdd", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAdd", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAdd", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAdd", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegated", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegated", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegated", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegated", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegated", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegated", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddSubtract", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddSubtract", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddSubtract", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddSubtract", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddSubtract", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddSubtract", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtract", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtract", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtract", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtract", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtract", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtract", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractAdd", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractAdd", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractAdd", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractAdd", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractAdd", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractAdd", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegated", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegated", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegated", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegated", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegated", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegated", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector512", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), }; (string templateFileName, Dictionary templateData)[] Avx512F_ScalarUpperInputs = new [] @@ -1544,6 +1631,69 @@ ("ImmBinOpTest.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "RoundScaleScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Imm"] = "2", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits(MathF.Ceiling(right[0]))", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(result[i]) != BitConverter.SingleToInt32Bits(left[i])"}), ("ImmBinOpTest.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "RoundScaleScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Imm"] = "3", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateFirstResult"] = "BitConverter.DoubleToInt64Bits(result[0]) != BitConverter.DoubleToInt64Bits((right[0] > 0) ? Math.Floor(right[0]) : Math.Ceiling(right[0]))", ["ValidateRemainingResults"] = "BitConverter.DoubleToInt64Bits(result[i]) != BitConverter.DoubleToInt64Bits(left[i])"}), ("ImmBinOpTest.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "RoundScaleScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Imm"] = "3", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(result[0]) != BitConverter.SingleToInt32Bits((right[0] > 0) ? MathF.Floor(right[0]) : MathF.Ceiling(right[0]))", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(result[i]) != BitConverter.SingleToInt32Bits(left[i])"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "AddScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "AddScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "AddScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "AddScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "AddScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "AddScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "DivideScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "DivideScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "DivideScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "DivideScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "DivideScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "DivideScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "MultiplyScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "MultiplyScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "MultiplyScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "MultiplyScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "MultiplyScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "MultiplyScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SubtractScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SubtractScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SubtractScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SubtractScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SubtractScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SubtractScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SqrtScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SqrtScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SqrtScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SqrtScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SqrtScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "SqrtScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ScaleScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ScaleScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ScaleScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ScaleScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ScaleScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ScaleScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertScalarToVector128Single", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "1.0", ["FixedInput2"] = "15.0"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertScalarToVector128Single", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "1.0", ["FixedInput2"] = "15.0"}), + ("SimpleBinOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertScalarToVector128Single", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "1.0", ["FixedInput2"] = "15.0"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegatedScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegatedScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegatedScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegatedScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegatedScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplyAddNegatedScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegatedScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegatedScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegatedScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Double", ["LargestVectorSize"] = "16", ["CastingMethod"] = "DoubleToUInt64Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegatedScalar", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegatedScalar", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), + ("SimpleTernOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512F", ["LoadIsa"] = "Avx512F", ["Method"] = "FusedMultiplySubtractNegatedScalar", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Single", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "Single", ["LargestVectorSize"] = "16", ["CastingMethod"] = "SingleToUInt32Bits", ["FixedInput1"] = "0.05", ["FixedInput2"] = "0.45", ["FixedInput3"] = "0.75"}), }; (string templateFileName, Dictionary templateData)[] Avx512F_VL_Vector128Inputs = new [] @@ -2210,6 +2360,30 @@ ("ImmUnOpTest.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ReduceScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["Imm"] = "16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ValidateFirstResult"] = "BitConverter.SingleToInt32Bits(Avx512Verify.Reduce(firstOp[0], 1)) != BitConverter.SingleToInt32Bits(result[0])", ["ValidateRemainingResults"] = "BitConverter.SingleToInt32Bits(firstOp[i]) != BitConverter.SingleToInt32Bits(result[i])"}), ("SimpleBinOpTest.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "Xor", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateFirstResult"] = "(BitConverter.DoubleToInt64Bits(left[0]) ^ BitConverter.DoubleToInt64Bits(right[0])) != BitConverter.DoubleToInt64Bits(result[0])", ["ValidateRemainingResults"] = "(BitConverter.DoubleToInt64Bits(left[i]) ^ BitConverter.DoubleToInt64Bits(right[i])) != BitConverter.DoubleToInt64Bits(result[i])"}), ("SimpleBinOpTest.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "Xor", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Single", ["Op2VectorType"] = "Vector512", ["Op2BaseType"] = "Single", ["LargestVectorSize"] = "64", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateFirstResult"] = "(BitConverter.SingleToInt32Bits(left[0]) ^ BitConverter.SingleToInt32Bits(right[0])) != BitConverter.SingleToInt32Bits(result[0])", ["ValidateRemainingResults"] = "(BitConverter.SingleToInt32Bits(left[i]) ^ BitConverter.SingleToInt32Bits(right[i])) != BitConverter.SingleToInt32Bits(result[i])"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Single", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Single", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Single", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Single", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Single", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector256Single", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector256", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.SingleToUInt32Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Double", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.DoubleToUInt64Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Double", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.DoubleToUInt64Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Double", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.DoubleToUInt64Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Double", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.DoubleToUInt64Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Double", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.DoubleToUInt64Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Double", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "64", ["CastingMethod"] = "BitConverter.DoubleToUInt64Bits", ["FixedInput"] = "10"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Int64", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Int64", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Int64", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Int64", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Int64", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512Int64", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512UInt64", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512UInt64", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512UInt64", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector512", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512UInt64", ["RoundingMode"] = "ToNegativeInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512UInt64", ["RoundingMode"] = "ToPositiveInfinity", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), + ("SimpleUnaryOpEmbRounding.template", new Dictionary { ["Isa"] = "Avx512DQ", ["LoadIsa"] = "Avx512F", ["Method"] = "ConvertToVector512UInt64", ["RoundingMode"] = "ToZero", ["RetVectorType"] = "Vector512", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector256", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "64", ["CastingMethod"] = "(ulong)", ["FixedInput"] = "29.37"}), }; (string templateFileName, Dictionary templateData)[] Avx512DQ_ScalarUpperInputs = new [] @@ -2634,12 +2808,25 @@ void ProcessInput(StreamWriter testListFile, string groupName, (string templateF testName += ".Tuple3Op"; suffix += "Tuple3Op"; } + else if (input.templateFileName == "SimpleTernOpEmbRounding.template") + { + testName += ".EmbeddedRounding"; + testName += $".{input.templateData["RoundingMode"]}"; + suffix += "EmbeddedRounding"; + } else if (input.templateFileName == "SimpleBinOpEmbRounding.template") { testName += ".EmbeddedRounding"; testName += $".{input.templateData["RoundingMode"]}"; suffix += "EmbeddedRounding"; } + else if (input.templateFileName == "SimpleUnaryOpEmbRounding.template") + { + testName +=$".{input.templateData["Op1BaseType"]}"; + testName += ".EmbeddedRounding"; + testName += $".{input.templateData["RoundingMode"]}"; + suffix += "EmbeddedRounding"; + } var fileName = Path.Combine(outputDirectory, $"{testName}.cs"); diff --git a/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpEmbRounding.template b/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpEmbRounding.template index da78721c596d01..3b6ab6a4de20ba 100644 --- a/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpEmbRounding.template +++ b/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleBinOpEmbRounding.template @@ -173,7 +173,7 @@ namespace JIT.HardwareIntrinsics.X86 { TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); - var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2VectorType}<{Op2BaseType}>) , typeof(FloatRoundingMode)}) + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2VectorType}<{Op2BaseType}>), typeof(FloatRoundingMode)}) .Invoke(null, new object[] { Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr), @@ -307,9 +307,78 @@ namespace JIT.HardwareIntrinsics.X86 private static Dictionary<(string, string, string), ulong[]> binaryEmbRoundingAnswerTable = new Dictionary<(string, string, string), ulong[]> { + {("Double", "ConvertScalarToVector128Double", "ToNegativeInfinity"), new ulong[] {0x402e000000000000, 0x3ff0000000000000}}, + {("Single", "ConvertScalarToVector128Single", "ToNegativeInfinity"), new ulong[] {0x41700000, 0x3f800000, 0x3f800000, 0x3f800000}}, + {("Double", "ConvertScalarToVector128Double", "ToPositiveInfinity"), new ulong[] {0x402e000000000000, 0x3ff0000000000000}}, + {("Single", "ConvertScalarToVector128Single", "ToPositiveInfinity"), new ulong[] {0x41700000, 0x3f800000, 0x3f800000, 0x3f800000}}, + {("Double", "ConvertScalarToVector128Double", "ToZero"), new ulong[] {0x402e000000000000, 0x3ff0000000000000}}, + {("Single", "ConvertScalarToVector128Single", "ToZero"), new ulong[] {0x41700000, 0x3f800000, 0x3f800000, 0x3f800000}}, + {("Double", "AddScalar", "ToNegativeInfinity"), new ulong[] {0x3fe0000000000000, 0x3fa999999999999a}}, + {("Single", "AddScalar", "ToNegativeInfinity"), new ulong[] {0x3effffff, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "AddScalar", "ToPositiveInfinity"), new ulong[] {0x3fe0000000000001, 0x3fa999999999999a}}, + {("Single", "AddScalar", "ToPositiveInfinity"), new ulong[] {0x3f000000, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "AddScalar", "ToZero"), new ulong[] {0x3fe0000000000000, 0x3fa999999999999a}}, + {("Single", "AddScalar", "ToZero"), new ulong[] {0x3effffff, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "DivideScalar", "ToNegativeInfinity"), new ulong[] {0x3fbc71c71c71c71c, 0x3fa999999999999a}}, + {("Single", "DivideScalar", "ToNegativeInfinity"), new ulong[] {0x3de38e39, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "DivideScalar", "ToPositiveInfinity"), new ulong[] {0x3fbc71c71c71c71d, 0x3fa999999999999a}}, + {("Single", "DivideScalar", "ToPositiveInfinity"), new ulong[] {0x3de38e3a, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "DivideScalar", "ToZero"), new ulong[] {0x3fbc71c71c71c71c, 0x3fa999999999999a}}, + {("Single", "DivideScalar", "ToZero"), new ulong[] {0x3de38e39, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "MultiplyScalar", "ToNegativeInfinity"), new ulong[] {0x3f970a3d70a3d70a, 0x3fa999999999999a}}, + {("Single", "MultiplyScalar", "ToNegativeInfinity"), new ulong[] {0x3cb851eb, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "MultiplyScalar", "ToPositiveInfinity"), new ulong[] {0x3f970a3d70a3d70b, 0x3fa999999999999a}}, + {("Single", "MultiplyScalar", "ToPositiveInfinity"), new ulong[] {0x3cb851ec, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "MultiplyScalar", "ToZero"), new ulong[] {0x3f970a3d70a3d70a, 0x3fa999999999999a}}, + {("Single", "MultiplyScalar", "ToZero"), new ulong[] {0x3cb851eb, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "SubtractScalar", "ToNegativeInfinity"), new ulong[] {0xbfd999999999999a, 0x3fa999999999999a}}, + {("Single", "SubtractScalar", "ToNegativeInfinity"), new ulong[] {0xbecccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "SubtractScalar", "ToPositiveInfinity"), new ulong[] {0xbfd9999999999999, 0x3fa999999999999a}}, + {("Single", "SubtractScalar", "ToPositiveInfinity"), new ulong[] {0xbecccccc, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "SubtractScalar", "ToZero"), new ulong[] {0xbfd9999999999999, 0x3fa999999999999a}}, + {("Single", "SubtractScalar", "ToZero"), new ulong[] {0xbecccccc, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "SqrtScalar", "ToNegativeInfinity"), new ulong[] {0x3fe5775c544ff262, 0x3fa999999999999a}}, + {("Single", "SqrtScalar", "ToNegativeInfinity"), new ulong[] {0x3f2bbae2, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "SqrtScalar", "ToPositiveInfinity"), new ulong[] {0x3fe5775c544ff263, 0x3fa999999999999a}}, + {("Single", "SqrtScalar", "ToPositiveInfinity"), new ulong[] {0x3f2bbae3, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "SqrtScalar", "ToZero"), new ulong[] {0x3fe5775c544ff262, 0x3fa999999999999a}}, + {("Single", "SqrtScalar", "ToZero"), new ulong[] {0x3f2bbae2, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, {("Double", "Add", "ToNegativeInfinity"), new ulong[] {0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000}}, + {("Single", "Add", "ToNegativeInfinity"), new ulong[] {0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff}}, {("Double", "Add", "ToPositiveInfinity"), new ulong[] {0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001, 0x3fe0000000000001}}, + {("Single", "Add", "ToPositiveInfinity"), new ulong[] {0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000}}, {("Double", "Add", "ToZero"), new ulong[] {0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000, 0x3fe0000000000000}}, + {("Single", "Add", "ToZero"), new ulong[] {0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff, 0x3effffff}}, + {("Double", "Divide", "ToNegativeInfinity"), new ulong[] {0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c}}, + {("Single", "Divide", "ToNegativeInfinity"), new ulong[] {0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39}}, + {("Double", "Divide", "ToPositiveInfinity"), new ulong[] {0x3fbc71c71c71c71d, 0x3fbc71c71c71c71d, 0x3fbc71c71c71c71d, 0x3fbc71c71c71c71d, 0x3fbc71c71c71c71d, 0x3fbc71c71c71c71d, 0x3fbc71c71c71c71d, 0x3fbc71c71c71c71d}}, + {("Single", "Divide", "ToPositiveInfinity"), new ulong[] {0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a, 0x3de38e3a}}, + {("Double", "Divide", "ToZero"), new ulong[] {0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c, 0x3fbc71c71c71c71c}}, + {("Single", "Divide", "ToZero"), new ulong[] {0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39, 0x3de38e39}}, + {("Double", "Multiply", "ToNegativeInfinity"), new ulong[] {0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a}}, + {("Single", "Multiply", "ToNegativeInfinity"), new ulong[] {0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb}}, + {("Double", "Multiply", "ToPositiveInfinity"), new ulong[] {0x3f970a3d70a3d70b, 0x3f970a3d70a3d70b, 0x3f970a3d70a3d70b, 0x3f970a3d70a3d70b, 0x3f970a3d70a3d70b, 0x3f970a3d70a3d70b, 0x3f970a3d70a3d70b, 0x3f970a3d70a3d70b}}, + {("Single", "Multiply", "ToPositiveInfinity"), new ulong[] {0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec, 0x3cb851ec}}, + {("Double", "Multiply", "ToZero"), new ulong[] {0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a, 0x3f970a3d70a3d70a}}, + {("Single", "Multiply", "ToZero"), new ulong[] {0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb, 0x3cb851eb}}, + {("Double", "Subtract", "ToNegativeInfinity"), new ulong[] {0xbfd999999999999a, 0xbfd999999999999a, 0xbfd999999999999a, 0xbfd999999999999a, 0xbfd999999999999a, 0xbfd999999999999a, 0xbfd999999999999a, 0xbfd999999999999a}}, + {("Single", "Subtract", "ToNegativeInfinity"), new ulong[] {0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd, 0xbecccccd}}, + {("Double", "Subtract", "ToPositiveInfinity"), new ulong[] {0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999}}, + {("Single", "Subtract", "ToPositiveInfinity"), new ulong[] {0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc}}, + {("Double", "Subtract", "ToZero"), new ulong[] {0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999, 0xbfd9999999999999}}, + {("Single", "Subtract", "ToZero"), new ulong[] {0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc, 0xbecccccc}}, + {("Double", "Scale", "ToNegativeInfinity"), new ulong[] {0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a}}, + {("Single", "Scale", "ToNegativeInfinity"), new ulong[] {0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "Scale", "ToPositiveInfinity"), new ulong[] {0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a}}, + {("Single", "Scale", "ToPositiveInfinity"), new ulong[] {0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "Scale", "ToZero"), new ulong[] {0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a, 0x3fa999999999999a}}, + {("Single", "Scale", "ToZero"), new ulong[] {0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "ScaleScalar", "ToNegativeInfinity"), new ulong[] {0x3fa999999999999a, 0x3fa999999999999a}}, + {("Single", "ScaleScalar", "ToNegativeInfinity"), new ulong[] {0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "ScaleScalar", "ToPositiveInfinity"), new ulong[] {0x3fa999999999999a, 0x3fa999999999999a}}, + {("Single", "ScaleScalar", "ToPositiveInfinity"), new ulong[] {0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "ScaleScalar", "ToZero"), new ulong[] {0x3fa999999999999a, 0x3fa999999999999a}}, + {("Single", "ScaleScalar", "ToZero"), new ulong[] {0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, }; } } diff --git a/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleTernOpEmbRounding.template b/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleTernOpEmbRounding.template new file mode 100644 index 00000000000000..b302aa267a8209 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleTernOpEmbRounding.template @@ -0,0 +1,392 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Collections.Generic; +using System.Runtime.Intrinsics.X86; +using Xunit; + + +namespace JIT.HardwareIntrinsics.X86 +{ + public static partial class Program + { + [Fact] + public static void {Method}{RetBaseType}{RoundingMode}() + { + var test = new TernaryOpTest__{Method}{RetBaseType}{RoundingMode}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + + // Validates basic functionality works, using LoadAligned + test.RunBasicScenario_LoadAligned(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class TernaryOpTest__{Method}{RetBaseType}{RoundingMode} + { + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld1; + public {Op2VectorType}<{Op2BaseType}> _fld2; + public {Op2VectorType}<{Op3BaseType}> _fld3; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = ({Op1BaseType}){FixedInput1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = ({Op1BaseType}){FixedInput2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref testStruct._fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + for (var i = 0; i < Op3ElementCount; i++) { _data3[i] = ({Op1BaseType}){FixedInput3}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op3VectorType}<{Op3BaseType}>, byte>(ref testStruct._fld3), ref Unsafe.As<{Op3BaseType}, byte>(ref _data3[0]), (uint)Unsafe.SizeOf<{Op3VectorType}<{Op3BaseType}>>()); + + return testStruct; + } + + public void RunStructFldScenario(TernaryOpTest__{Method}{RetBaseType}{RoundingMode} testClass) + { + var result = {Isa}.{Method}(_fld1, _fld2, _fld3, FloatRoundingMode.{RoundingMode}); + + Unsafe.Write(testClass._dataTable.outArrayPtr, result); + testClass.ValidateResult(_fld1, _fld2, _fld3, testClass._dataTable.outArrayPtr); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int Op1ElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int Op2ElementCount = Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>() / sizeof({Op2BaseType}); + private static readonly int Op3ElementCount = Unsafe.SizeOf<{Op3VectorType}<{Op3BaseType}>>() / sizeof({Op3BaseType}); + private static readonly int RetElementCount = Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>() / sizeof({RetBaseType}); + + private static {Op1BaseType}[] _data1 = new {Op1BaseType}[Op1ElementCount]; + private static {Op2BaseType}[] _data2 = new {Op2BaseType}[Op2ElementCount]; + private static {Op3BaseType}[] _data3 = new {Op3BaseType}[Op3ElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _fld1; + private {Op2VectorType}<{Op2BaseType}> _fld2; + private {Op3VectorType}<{Op3BaseType}> _fld3; + + private SimpleTernaryOpTest__DataTable<{RetBaseType}, {Op1BaseType}, {Op2BaseType}, {Op3BaseType}> _dataTable; + + public TernaryOpTest__{Method}{RetBaseType}{RoundingMode}() + { + Succeeded = true; + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = ({Op1BaseType}){FixedInput1}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = ({Op1BaseType}){FixedInput2}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2VectorType}<{Op2BaseType}>, byte>(ref _fld2), ref Unsafe.As<{Op2BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + for (var i = 0; i < Op3ElementCount; i++) { _data3[i] = ({Op1BaseType}){FixedInput3}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op3VectorType}<{Op3BaseType}>, byte>(ref _fld3), ref Unsafe.As<{Op3BaseType}, byte>(ref _data3[0]), (uint)Unsafe.SizeOf<{Op3VectorType}<{Op3BaseType}>>()); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = ({Op1BaseType}){FixedInput1}; } + for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = ({Op1BaseType}){FixedInput2}; } + for (var i = 0; i < Op3ElementCount; i++) { _data3[i] = ({Op1BaseType}){FixedInput3}; } + _dataTable = new SimpleTernaryOpTest__DataTable<{RetBaseType}, {Op1BaseType}, {Op2BaseType}, {Op3BaseType}>(_data1, _data2, _data3, new {RetBaseType}[RetElementCount], LargestVectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead)); + + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr), + Unsafe.Read<{Op3VectorType}<{Op3BaseType}>>(_dataTable.inArray3Ptr), + FloatRoundingMode.{RoundingMode} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.inArray3Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + var result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.Load{Op2VectorType}(({Op2BaseType}*)(_dataTable.inArray2Ptr)), + {LoadIsa}.Load{Op3VectorType}(({Op3BaseType}*)(_dataTable.inArray3Ptr)), + FloatRoundingMode.{RoundingMode} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.inArray3Ptr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_LoadAligned)); + + var result = {Isa}.{Method}( + {LoadIsa}.LoadAligned{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArray1Ptr)), + {LoadIsa}.LoadAligned{Op2VectorType}(({Op2BaseType}*)(_dataTable.inArray2Ptr)), + {LoadIsa}.LoadAligned{Op3VectorType}(({Op3BaseType}*)(_dataTable.inArray3Ptr)), + FloatRoundingMode.{RoundingMode} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.inArray3Ptr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof({Op2VectorType}<{Op2BaseType}>), typeof({Op3VectorType}<{Op3BaseType}>), typeof(FloatRoundingMode) }) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr), + Unsafe.Read<{Op3VectorType}<{Op3BaseType}>>(_dataTable.inArray3Ptr), + FloatRoundingMode.{RoundingMode} + }); + + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.inArray3Ptr, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead)); + + var op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr); + var op2 = Unsafe.Read<{Op2VectorType}<{Op2BaseType}>>(_dataTable.inArray2Ptr); + var op3 = Unsafe.Read<{Op3VectorType}<{Op3BaseType}>>(_dataTable.inArray3Ptr); + var result = {Isa}.{Method}(op1, op2, op3, FloatRoundingMode.{RoundingMode}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(op1, op2, op3, _dataTable.outArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + var result = {Isa}.{Method}(_fld1, _fld2, _fld3, FloatRoundingMode.{RoundingMode}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _fld2, _fld3, _dataTable.outArrayPtr); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + var result = {Isa}.{Method}(test._fld1, test._fld2, test._fld3, FloatRoundingMode.{RoundingMode}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, test._fld2, test._fld3, _dataTable.outArrayPtr); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + public void RunUnsupportedScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario)); + + bool succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> op1, {Op2VectorType}<{Op2BaseType}> op2, {Op3VectorType}<{Op3BaseType}> op3, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {Op2BaseType}[] inArray2 = new {Op2BaseType}[Op2ElementCount]; + {Op3BaseType}[] inArray3 = new {Op3BaseType}[Op3ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), op1); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), op2); + Unsafe.WriteUnaligned(ref Unsafe.As<{Op3BaseType}, byte>(ref inArray3[0]), op3); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray1, inArray2, inArray3, outArray, method); + } + + private void ValidateResult(void* op1, void* op2, void* op3, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {Op2BaseType}[] inArray2 = new {Op2BaseType}[Op2ElementCount]; + {Op3BaseType}[] inArray3 = new {Op3BaseType}[Op3ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), ref Unsafe.AsRef(op1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op2BaseType}, byte>(ref inArray2[0]), ref Unsafe.AsRef(op2), (uint)Unsafe.SizeOf<{Op2VectorType}<{Op2BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op3BaseType}, byte>(ref inArray3[0]), ref Unsafe.AsRef(op3), (uint)Unsafe.SizeOf<{Op3VectorType}<{Op3BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray1, inArray2, inArray3, outArray, method); + } + + private void ValidateResult({Op1BaseType}[] firstOp, {Op2BaseType}[] secondOp, {Op3BaseType}[] thirdOp, {RetBaseType}[] result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + for (int i = 0; i < result.Length; i++) + { + ulong[] answerTable = TernaryEmbRoundingAnswerTable[("{RetBaseType}", "{Method}", "{RoundingMode}")]; + + if (BitConverter.{CastingMethod}(result[i]) != answerTable[i]) + { + succeeded = false; + Console.WriteLine("Avx512 {Method} Embedded rounding failed on {RetBaseType} with {RoundingMode}:"); + foreach (var item in result) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>, {Op2VectorType}<{Op2BaseType}>, {Op3VectorType}<{Op3BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" firstOp: ({string.Join(", ", firstOp)})"); + TestLibrary.TestFramework.LogInformation($"secondOp: ({string.Join(", ", secondOp)})"); + TestLibrary.TestFramework.LogInformation($" thirdOp: ({string.Join(", ", thirdOp)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private static Dictionary<(string, string, string), ulong[]> TernaryEmbRoundingAnswerTable = new Dictionary<(string, string, string), ulong[]> + { + {("Double", "FusedMultiplyAdd", "ToNegativeInfinity"), new ulong[] {0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8}}, + {("Single", "FusedMultiplyAdd", "ToNegativeInfinity"), new ulong[] {0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f}}, + {("Double", "FusedMultiplyAdd", "ToPositiveInfinity"), new ulong[] {0x3fe8b851eb851eb9, 0x3fe8b851eb851eb9, 0x3fe8b851eb851eb9, 0x3fe8b851eb851eb9, 0x3fe8b851eb851eb9, 0x3fe8b851eb851eb9, 0x3fe8b851eb851eb9, 0x3fe8b851eb851eb9}}, + {("Single", "FusedMultiplyAdd", "ToPositiveInfinity"), new ulong[] {0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290, 0x3f45c290}}, + {("Double", "FusedMultiplyAdd", "ToZero"), new ulong[] {0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8, 0x3fe8b851eb851eb8}}, + {("Single", "FusedMultiplyAdd", "ToZero"), new ulong[] {0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f, 0x3f45c28f}}, + {("Double", "FusedMultiplyAddNegated", "ToNegativeInfinity"), new ulong[] {0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147}}, + {("Single", "FusedMultiplyAddNegated", "ToNegativeInfinity"), new ulong[] {0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70}}, + {("Double", "FusedMultiplyAddNegated", "ToPositiveInfinity"), new ulong[] {0x3fe747ae147ae148, 0x3fe747ae147ae148, 0x3fe747ae147ae148, 0x3fe747ae147ae148, 0x3fe747ae147ae148, 0x3fe747ae147ae148, 0x3fe747ae147ae148, 0x3fe747ae147ae148}}, + {("Single", "FusedMultiplyAddNegated", "ToPositiveInfinity"), new ulong[] {0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71, 0x3f3a3d71}}, + {("Double", "FusedMultiplyAddNegated", "ToZero"), new ulong[] {0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147, 0x3fe747ae147ae147}}, + {("Single", "FusedMultiplyAddNegated", "ToZero"), new ulong[] {0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70, 0x3f3a3d70}}, + {("Double", "FusedMultiplyAddSubtract", "ToNegativeInfinity"), new ulong[] {0xbfe747ae147ae148, 0x3fe8b851eb851eb8, 0xbfe747ae147ae148, 0x3fe8b851eb851eb8, 0xbfe747ae147ae148, 0x3fe8b851eb851eb8, 0xbfe747ae147ae148, 0x3fe8b851eb851eb8}}, + {("Single", "FusedMultiplyAddSubtract", "ToNegativeInfinity"), new ulong[] {0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f}}, + {("Double", "FusedMultiplyAddSubtract", "ToPositiveInfinity"), new ulong[] {0xbfe747ae147ae147, 0x3fe8b851eb851eb9, 0xbfe747ae147ae147, 0x3fe8b851eb851eb9, 0xbfe747ae147ae147, 0x3fe8b851eb851eb9, 0xbfe747ae147ae147, 0x3fe8b851eb851eb9}}, + {("Single", "FusedMultiplyAddSubtract", "ToPositiveInfinity"), new ulong[] {0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290}}, + {("Double", "FusedMultiplyAddSubtract", "ToZero"), new ulong[] {0xbfe747ae147ae147, 0x3fe8b851eb851eb8, 0xbfe747ae147ae147, 0x3fe8b851eb851eb8, 0xbfe747ae147ae147, 0x3fe8b851eb851eb8, 0xbfe747ae147ae147, 0x3fe8b851eb851eb8}}, + {("Single", "FusedMultiplyAddSubtract", "ToZero"), new ulong[] {0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f}}, + {("Double", "FusedMultiplySubtract", "ToNegativeInfinity"), new ulong[] {0xbfe747ae147ae148, 0xbfe747ae147ae148, 0xbfe747ae147ae148, 0xbfe747ae147ae148, 0xbfe747ae147ae148, 0xbfe747ae147ae148, 0xbfe747ae147ae148, 0xbfe747ae147ae148}}, + {("Single", "FusedMultiplySubtract", "ToNegativeInfinity"), new ulong[] {0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71, 0xbf3a3d71}}, + {("Double", "FusedMultiplySubtract", "ToPositiveInfinity"), new ulong[] {0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147}}, + {("Single", "FusedMultiplySubtract", "ToPositiveInfinity"), new ulong[] {0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70}}, + {("Double", "FusedMultiplySubtract", "ToZero"), new ulong[] {0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147, 0xbfe747ae147ae147}}, + {("Single", "FusedMultiplySubtract", "ToZero"), new ulong[] {0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70, 0xbf3a3d70}}, + {("Double", "FusedMultiplySubtractAdd", "ToNegativeInfinity"), new ulong[] {0x3fe8b851eb851eb8, 0xbfe747ae147ae148, 0x3fe8b851eb851eb8, 0xbfe747ae147ae148, 0x3fe8b851eb851eb8, 0xbfe747ae147ae148, 0x3fe8b851eb851eb8, 0xbfe747ae147ae148}}, + {("Single", "FusedMultiplySubtractAdd", "ToNegativeInfinity"), new ulong[] {0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71, 0x3f45c28f, 0xbf3a3d71}}, + {("Double", "FusedMultiplySubtractAdd", "ToPositiveInfinity"), new ulong[] {0x3fe8b851eb851eb9, 0xbfe747ae147ae147, 0x3fe8b851eb851eb9, 0xbfe747ae147ae147, 0x3fe8b851eb851eb9, 0xbfe747ae147ae147, 0x3fe8b851eb851eb9, 0xbfe747ae147ae147}}, + {("Single", "FusedMultiplySubtractAdd", "ToPositiveInfinity"), new ulong[] {0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70, 0x3f45c290, 0xbf3a3d70}}, + {("Double", "FusedMultiplySubtractAdd", "ToZero"), new ulong[] {0x3fe8b851eb851eb8, 0xbfe747ae147ae147, 0x3fe8b851eb851eb8, 0xbfe747ae147ae147, 0x3fe8b851eb851eb8, 0xbfe747ae147ae147, 0x3fe8b851eb851eb8, 0xbfe747ae147ae147}}, + {("Single", "FusedMultiplySubtractAdd", "ToZero"), new ulong[] {0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70, 0x3f45c28f, 0xbf3a3d70}}, + {("Double", "FusedMultiplySubtractNegated", "ToNegativeInfinity"), new ulong[] {0xbfe8b851eb851eb9, 0xbfe8b851eb851eb9, 0xbfe8b851eb851eb9, 0xbfe8b851eb851eb9, 0xbfe8b851eb851eb9, 0xbfe8b851eb851eb9, 0xbfe8b851eb851eb9, 0xbfe8b851eb851eb9}}, + {("Single", "FusedMultiplySubtractNegated", "ToNegativeInfinity"), new ulong[] {0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290, 0xbf45c290}}, + {("Double", "FusedMultiplySubtractNegated", "ToPositiveInfinity"), new ulong[] {0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8}}, + {("Single", "FusedMultiplySubtractNegated", "ToPositiveInfinity"), new ulong[] {0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f}}, + {("Double", "FusedMultiplySubtractNegated", "ToZero"), new ulong[] {0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8, 0xbfe8b851eb851eb8}}, + {("Single", "FusedMultiplySubtractNegated", "ToZero"), new ulong[] {0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f, 0xbf45c28f}}, + {("Double", "FusedMultiplyAddScalar", "ToNegativeInfinity"), new ulong[] {0x3fe8b851eb851eb8, 0x3fa999999999999a}}, + {("Single", "FusedMultiplyAddScalar", "ToNegativeInfinity"), new ulong[] {0x3f45c28f, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplyAddScalar", "ToPositiveInfinity"), new ulong[] {0x3fe8b851eb851eb9, 0x3fa999999999999a}}, + {("Single", "FusedMultiplyAddScalar", "ToPositiveInfinity"), new ulong[] {0x3f45c290, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplyAddScalar", "ToZero"), new ulong[] {0x3fe8b851eb851eb8, 0x3fa999999999999a}}, + {("Single", "FusedMultiplyAddScalar", "ToZero"), new ulong[] {0x3f45c28f, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplyAddNegatedScalar", "ToNegativeInfinity"), new ulong[] {0x3fe747ae147ae147, 0x3fa999999999999a}}, + {("Single", "FusedMultiplyAddNegatedScalar", "ToNegativeInfinity"), new ulong[] {0x3f3a3d70, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplyAddNegatedScalar", "ToPositiveInfinity"), new ulong[] {0x3fe747ae147ae148, 0x3fa999999999999a}}, + {("Single", "FusedMultiplyAddNegatedScalar", "ToPositiveInfinity"), new ulong[] {0x3f3a3d71, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplyAddNegatedScalar", "ToZero"), new ulong[] {0x3fe747ae147ae147, 0x3fa999999999999a}}, + {("Single", "FusedMultiplyAddNegatedScalar", "ToZero"), new ulong[] {0x3f3a3d70, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplySubtractNegatedScalar", "ToNegativeInfinity"), new ulong[] {0xbfe8b851eb851eb9, 0x3fa999999999999a}}, + {("Single", "FusedMultiplySubtractNegatedScalar", "ToNegativeInfinity"), new ulong[] {0xbf45c290, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplySubtractNegatedScalar", "ToPositiveInfinity"), new ulong[] {0xbfe8b851eb851eb8, 0x3fa999999999999a}}, + {("Single", "FusedMultiplySubtractNegatedScalar", "ToPositiveInfinity"), new ulong[] {0xbf45c28f, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplySubtractNegatedScalar", "ToZero"), new ulong[] {0xbfe8b851eb851eb8, 0x3fa999999999999a}}, + {("Single", "FusedMultiplySubtractNegatedScalar", "ToZero"), new ulong[] {0xbf45c28f, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplySubtractScalar", "ToNegativeInfinity"), new ulong[] {0xbfe747ae147ae148, 0x3fa999999999999a}}, + {("Single", "FusedMultiplySubtractScalar", "ToNegativeInfinity"), new ulong[] {0xbf3a3d71, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplySubtractScalar", "ToPositiveInfinity"), new ulong[] {0xbfe747ae147ae147, 0x3fa999999999999a}}, + {("Single", "FusedMultiplySubtractScalar", "ToPositiveInfinity"), new ulong[] {0xbf3a3d70, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + {("Double", "FusedMultiplySubtractScalar", "ToZero"), new ulong[] {0xbfe747ae147ae147, 0x3fa999999999999a}}, + {("Single", "FusedMultiplySubtractScalar", "ToZero"), new ulong[] {0xbf3a3d70, 0x3d4ccccd, 0x3d4ccccd, 0x3d4ccccd}}, + }; + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleUnaryOpEmbRounding.template b/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleUnaryOpEmbRounding.template new file mode 100644 index 00000000000000..3464db01af8d2f --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/X86/Shared/SimpleUnaryOpEmbRounding.template @@ -0,0 +1,341 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/****************************************************************************** + * This file is auto-generated from a template file by the GenerateTests.csx * + * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make * + * changes, please update the corresponding template and run according to the * + * directions listed in the file. * + ******************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using Xunit; + +namespace JIT.HardwareIntrinsics.X86 +{ + public static partial class Program + { + [Fact] + public static void {Method}{Op1BaseType}to{RetBaseType}{RoundingMode}() + { + var test = new UnaryOpTest__{Method}{Op1BaseType}to{RetBaseType}{RoundingMode}(); + + if (test.IsSupported) + { + // Validates basic functionality works, using Unsafe.Read + test.RunBasicScenario_UnsafeRead(); + + if ({LoadIsa}.IsSupported) + { + // Validates basic functionality works, using Load + test.RunBasicScenario_Load(); + + // Validates basic functionality works, using LoadAligned + test.RunBasicScenario_LoadAligned(); + } + + // Validates calling via reflection works, using Unsafe.Read + test.RunReflectionScenario_UnsafeRead(); + + // Validates passing a local works, using Unsafe.Read + test.RunLclVarScenario_UnsafeRead(); + + // Validates passing an instance member of a class works + test.RunClassFldScenario(); + + // Validates passing the field of a local struct works + test.RunStructLclFldScenario(); + + // Validates passing an instance member of a struct works + test.RunStructFldScenario(); + } + else + { + // Validates we throw on unsupported hardware + test.RunUnsupportedScenario(); + } + + if (!test.Succeeded) + { + throw new Exception("One or more scenarios did not complete as expected."); + } + } + } + + public sealed unsafe class UnaryOpTest__{Method}{Op1BaseType}to{RetBaseType}{RoundingMode} + { + private struct TestStruct + { + public {Op1VectorType}<{Op1BaseType}> _fld1; + + public static TestStruct Create() + { + var testStruct = new TestStruct(); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = ({Op1BaseType}){FixedInput}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + return testStruct; + } + + public void RunStructFldScenario(UnaryOpTest__{Method}{Op1BaseType}to{RetBaseType}{RoundingMode} testClass) + { + var result = {Isa}.{Method}(_fld1, FloatRoundingMode.{RoundingMode}); + + Unsafe.Write(testClass._dataTable.outArrayPtr, result); + testClass.ValidateResult(_fld1, testClass._dataTable.outArrayPtr); + } + } + + private static readonly int LargestVectorSize = {LargestVectorSize}; + + private static readonly int Op1ElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int RetElementCount = Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>() / sizeof({RetBaseType}); + + private static {Op1BaseType}[] _data1 = new {Op1BaseType}[Op1ElementCount]; + + private {Op1VectorType}<{Op1BaseType}> _fld1; + + private SimpleUnaryOpTest__DataTable<{RetBaseType}, {Op1BaseType}> _dataTable; + + public UnaryOpTest__{Method}{Op1BaseType}to{RetBaseType}{RoundingMode}() + { + Succeeded = true; + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = ({Op1BaseType}){FixedInput}; } + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + + for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = ({Op1BaseType}){FixedInput}; } + _dataTable = new SimpleUnaryOpTest__DataTable<{RetBaseType}, {Op1BaseType}>(_data1, new {RetBaseType}[RetElementCount], LargestVectorSize); + } + + public bool IsSupported => {Isa}.IsSupported; + + public bool Succeeded { get; set; } + + public void RunBasicScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead)); + + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArrayPtr), + FloatRoundingMode.{RoundingMode} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_Load() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); + + var result = {Isa}.{Method}( + {LoadIsa}.Load{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArrayPtr)), + FloatRoundingMode.{RoundingMode} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunBasicScenario_LoadAligned() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_LoadAligned)); + + var result = {Isa}.{Method}( + {LoadIsa}.LoadAligned{Op1VectorType}(({Op1BaseType}*)(_dataTable.inArrayPtr)), + FloatRoundingMode.{RoundingMode} + ); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunReflectionScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead)); + + var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1VectorType}<{Op1BaseType}>), typeof(FloatRoundingMode) }) + .Invoke(null, new object[] { + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArrayPtr), + FloatRoundingMode.{RoundingMode} + }); + + Unsafe.Write(_dataTable.outArrayPtr, ({RetVectorType}<{RetBaseType}>)(result)); + ValidateResult(_dataTable.inArrayPtr, _dataTable.outArrayPtr); + } + + public void RunLclVarScenario_UnsafeRead() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead)); + + var op1 = Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArrayPtr); + var result = {Isa}.{Method}(op1, FloatRoundingMode.{RoundingMode}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(op1, _dataTable.outArrayPtr); + } + + public void RunClassFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario)); + + var result = {Isa}.{Method}(_fld1, FloatRoundingMode.{RoundingMode}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(_fld1, _dataTable.outArrayPtr); + } + + public void RunStructLclFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario)); + + var test = TestStruct.Create(); + var result = {Isa}.{Method}(test._fld1, FloatRoundingMode.{RoundingMode}); + + Unsafe.Write(_dataTable.outArrayPtr, result); + ValidateResult(test._fld1, _dataTable.outArrayPtr); + } + + public void RunStructFldScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario)); + + var test = TestStruct.Create(); + test.RunStructFldScenario(this); + } + + public void RunUnsupportedScenario() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario)); + + bool succeeded = false; + + try + { + RunBasicScenario_UnsafeRead(); + } + catch (PlatformNotSupportedException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + + private void ValidateResult({Op1VectorType}<{Op1BaseType}> op1, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), op1); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray1, outArray, method); + } + + private void ValidateResult(void* op1, void* result, [CallerMemberName] string method = "") + { + {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; + + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), ref Unsafe.AsRef(op1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); + Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); + + ValidateResult(inArray1, outArray, method); + } + + private void ValidateResult({Op1BaseType}[] firstOp, {RetBaseType}[] result, [CallerMemberName] string method = "") + { + bool succeeded = true; + + for (int i = 0; i < result.Length; i++) + { + ulong[] answerTable = unaryEmbRoundingAnswerTable[("{Isa}", "{Op1BaseType}", "{RetBaseType}", "{Method}", "{RoundingMode}")]; + + if ({CastingMethod}(result[i]) != answerTable[i]) + { + succeeded = false; + Console.WriteLine("Avx512 {Method} Embedded rounding failed on {RetBaseType} with {RoundingMode}:"); + foreach (var item in result) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + if (!succeeded) + { + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($" firstOp: ({string.Join(", ", firstOp)})"); + TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})"); + TestLibrary.TestFramework.LogInformation(string.Empty); + + Succeeded = false; + } + } + + private static Dictionary<(string, string, string, string, string), ulong[]> unaryEmbRoundingAnswerTable = new Dictionary<(string, string, string, string, string), ulong[]> + { + {("Avx512F", "Double", "UInt32", "ConvertToVector256UInt32", "ToNegativeInfinity"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512F", "Double", "UInt32", "ConvertToVector256UInt32", "ToPositiveInfinity"), new ulong[] {0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}}, + {("Avx512F", "Double", "UInt32", "ConvertToVector256UInt32", "ToZero"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512F", "Single", "UInt32", "ConvertToVector512UInt32", "ToNegativeInfinity"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512F", "Single", "UInt32", "ConvertToVector512UInt32", "ToPositiveInfinity"), new ulong[] {0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}}, + {("Avx512F", "Single", "UInt32", "ConvertToVector512UInt32", "ToZero"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512F", "Single", "Int32", "ConvertToVector512Int32", "ToNegativeInfinity"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512F", "Single", "Int32", "ConvertToVector512Int32", "ToPositiveInfinity"), new ulong[] {0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}}, + {("Avx512F", "Single", "Int32", "ConvertToVector512Int32", "ToZero"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512F", "Double", "Int32", "ConvertToVector256Int32", "ToNegativeInfinity"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512F", "Double", "Int32", "ConvertToVector256Int32", "ToPositiveInfinity"), new ulong[] {0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}}, + {("Avx512F", "Double", "Int32", "ConvertToVector256Int32", "ToZero"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512F", "Int32", "Single", "ConvertToVector512Single", "ToNegativeInfinity"), new ulong[] {0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000}}, + {("Avx512F", "Int32", "Single", "ConvertToVector512Single", "ToPositiveInfinity"), new ulong[] {0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000}}, + {("Avx512F", "Int32", "Single", "ConvertToVector512Single", "ToZero"), new ulong[] {0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000, 0x41e80000}}, + {("Avx512F", "Double", "Single", "ConvertToVector256Single", "ToNegativeInfinity"), new ulong[] {0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2}}, + {("Avx512F", "Double", "Single", "ConvertToVector256Single", "ToPositiveInfinity"), new ulong[] {0x41eaf5c3, 0x41eaf5c3, 0x41eaf5c3, 0x41eaf5c3, 0x41eaf5c3, 0x41eaf5c3, 0x41eaf5c3, 0x41eaf5c3}}, + {("Avx512F", "Double", "Single", "ConvertToVector256Single", "ToZero"), new ulong[] {0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2, 0x41eaf5c2}}, + {("Avx512F", "Double", "Double", "Sqrt", "ToNegativeInfinity"), new ulong[] {0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec}}, + {("Avx512F", "Single", "Single", "Sqrt", "ToNegativeInfinity"), new ulong[] {0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd}}, + {("Avx512F", "Double", "Double", "Sqrt", "ToPositiveInfinity"), new ulong[] {0x4015ad79b34092ed, 0x4015ad79b34092ed, 0x4015ad79b34092ed, 0x4015ad79b34092ed, 0x4015ad79b34092ed, 0x4015ad79b34092ed, 0x4015ad79b34092ed, 0x4015ad79b34092ed}}, + {("Avx512F", "Single", "Single", "Sqrt", "ToPositiveInfinity"), new ulong[] {0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce, 0x40ad6bce}}, + {("Avx512F", "Double", "Double", "Sqrt", "ToZero"), new ulong[] {0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec, 0x4015ad79b34092ec}}, + {("Avx512F", "Single", "Single", "Sqrt", "ToZero"), new ulong[] {0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd, 0x40ad6bcd}}, + {("Avx512DQ", "Int64", "Double", "ConvertToVector512Double", "ToNegativeInfinity"), new ulong[] {0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000}}, + {("Avx512DQ", "Int64", "Double", "ConvertToVector512Double", "ToPositiveInfinity"), new ulong[] {0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000}}, + {("Avx512DQ", "Int64", "Double", "ConvertToVector512Double", "ToZero"), new ulong[] {0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000}}, + {("Avx512DQ", "Int64", "Single", "ConvertToVector256Single", "ToNegativeInfinity"), new ulong[] {0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000}}, + {("Avx512DQ", "Int64", "Single", "ConvertToVector256Single", "ToPositiveInfinity"), new ulong[] {0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000}}, + {("Avx512DQ", "Int64", "Single", "ConvertToVector256Single", "ToZero"), new ulong[] {0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000}}, + {("Avx512DQ", "UInt64", "Double", "ConvertToVector512Double", "ToNegativeInfinity"), new ulong[] {0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000}}, + {("Avx512DQ", "UInt64", "Double", "ConvertToVector512Double", "ToPositiveInfinity"), new ulong[] {0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000}}, + {("Avx512DQ", "UInt64", "Double", "ConvertToVector512Double", "ToZero"), new ulong[] {0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000, 0x4024000000000000}}, + {("Avx512DQ", "UInt64", "Single", "ConvertToVector256Single", "ToNegativeInfinity"), new ulong[] {0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000}}, + {("Avx512DQ", "UInt64", "Single", "ConvertToVector256Single", "ToPositiveInfinity"), new ulong[] {0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000}}, + {("Avx512DQ", "UInt64", "Single", "ConvertToVector256Single", "ToZero"), new ulong[] {0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000, 0x41200000}}, + {("Avx512DQ", "Double", "Int64", "ConvertToVector512Int64", "ToNegativeInfinity"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512DQ", "Double", "Int64", "ConvertToVector512Int64", "ToPositiveInfinity"), new ulong[] {0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}}, + {("Avx512DQ", "Double", "Int64", "ConvertToVector512Int64", "ToZero"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512DQ", "Single", "Int64", "ConvertToVector512Int64", "ToNegativeInfinity"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512DQ", "Single", "Int64", "ConvertToVector512Int64", "ToPositiveInfinity"), new ulong[] {0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}}, + {("Avx512DQ", "Single", "Int64", "ConvertToVector512Int64", "ToZero"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512DQ", "Double", "UInt64", "ConvertToVector512UInt64", "ToNegativeInfinity"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512DQ", "Double", "UInt64", "ConvertToVector512UInt64", "ToPositiveInfinity"), new ulong[] {0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}}, + {("Avx512DQ", "Double", "UInt64", "ConvertToVector512UInt64", "ToZero"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512DQ", "Single", "UInt64", "ConvertToVector512UInt64", "ToNegativeInfinity"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + {("Avx512DQ", "Single", "UInt64", "ConvertToVector512UInt64", "ToPositiveInfinity"), new ulong[] {0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}}, + {("Avx512DQ", "Single", "UInt64", "ConvertToVector512UInt64", "ToZero"), new ulong[] {0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d}}, + }; + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_handwritten_r.csproj b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_handwritten_r.csproj index fd0d21f01bc549..89f2c83fc5a0c4 100644 --- a/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_handwritten_r.csproj +++ b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_handwritten_r.csproj @@ -44,5 +44,7 @@ + + diff --git a/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_handwritten_ro.csproj b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_handwritten_ro.csproj index 082d6f4ee197a3..03ffa05da0d30e 100644 --- a/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_handwritten_ro.csproj +++ b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_handwritten_ro.csproj @@ -44,5 +44,7 @@ + + diff --git a/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/EmbeddedRounding.Double.cs b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/EmbeddedRounding.Double.cs new file mode 100644 index 00000000000000..1c01761fe92e72 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/EmbeddedRounding.Double.cs @@ -0,0 +1,716 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; +using Xunit; + +namespace IntelHardwareIntrinsicTest._Avx512F +{ + public partial class Program + { + [Fact] + public static unsafe void ConvertToInt32EmbeddedRounding_Double() + { + int testResult = 1; + int answerTable_ToNegativeInfinity = -1; + int answerTable_ToPositiveInfinity = 0; + int answerTable_ToZero = 0; + if (Avx512F.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45, -0.45); + int res = Avx512F.ConvertToInt32(inputVec, FloatRoundingMode.ToNegativeInfinity); + + if (res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on double with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.ConvertToInt32(inputVec, FloatRoundingMode.ToPositiveInfinity); + + if (res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on double with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.ConvertToInt32(inputVec, FloatRoundingMode.ToZero); + + if (res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on double with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToUInt32EmbeddedRounding_Double() + { + int testResult = 1; + uint answerTable_ToNegativeInfinity = 4294967295; + uint answerTable_ToPositiveInfinity = 0; + uint answerTable_ToZero = 0; + if (Avx512F.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45, -0.45); + uint res = Avx512F.ConvertToUInt32(inputVec, FloatRoundingMode.ToNegativeInfinity); + + if (res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on double with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.ConvertToUInt32(inputVec, FloatRoundingMode.ToPositiveInfinity); + + if (res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on double with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.ConvertToUInt32(inputVec, FloatRoundingMode.ToZero); + + if (res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on double with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToInt64EmbeddedRounding_Double() + { + int testResult = 1; + long answerTable_ToNegativeInfinity = -1; + long answerTable_ToPositiveInfinity = 0; + long answerTable_ToZero = 0; + if (Avx512F.X64.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45, -0.45); + long res = Avx512F.X64.ConvertToInt64(inputVec, FloatRoundingMode.ToNegativeInfinity); + + if (res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on double with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.X64.ConvertToInt64(inputVec, FloatRoundingMode.ToPositiveInfinity); + + if (res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on double with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.X64.ConvertToInt64(inputVec, FloatRoundingMode.ToZero); + + if (res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on double with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToUInt64EmbeddedRounding_Double() + { + int testResult = 1; + ulong answerTable_ToNegativeInfinity = 18446744073709551615; + ulong answerTable_ToPositiveInfinity = 0; + ulong answerTable_ToZero = 0; + if (Avx512F.X64.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45, -0.45); + ulong res = Avx512F.X64.ConvertToUInt64(inputVec, FloatRoundingMode.ToNegativeInfinity); + + if (res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on double with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.X64.ConvertToUInt64(inputVec, FloatRoundingMode.ToPositiveInfinity); + + if (res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on double with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.X64.ConvertToUInt64(inputVec, FloatRoundingMode.ToZero); + + if (res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on double with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertScalarToVector128DoubleInt64EmbeddedRounding_Double() + { + int testResult = 1; + ulong[] answerTable_ToNegativeInfinity = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + ulong[] answerTable_ToPositiveInfinity = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + ulong[] answerTable_ToZero = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + if (Avx512F.X64.IsSupported) + { + using (TestTable doubleTable = new TestTable(new double[2] { -1.0f, -1.0f}, new double[2])) + { + var upper = Unsafe.Read>(doubleTable.inArrayPtr); + long value = 15; + var vd3 = Avx512F.X64.ConvertScalarToVector128Double(upper, value, FloatRoundingMode.ToNegativeInfinity); + Unsafe.Write(doubleTable.outArrayPtr, vd3); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on Int64 input with ToNegativeInfinity:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = Avx512F.X64.ConvertScalarToVector128Double(upper, value, FloatRoundingMode.ToPositiveInfinity); + Unsafe.Write(doubleTable.outArrayPtr, vd3); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToPositiveInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on Int64 input with ToPositiveInfinity:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = Avx512F.X64.ConvertScalarToVector128Double(upper, value, FloatRoundingMode.ToZero); + Unsafe.Write(doubleTable.outArrayPtr, vd3); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToZero[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on Int64 input with ToZero:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertScalarToVector128DoubleUInt64EmbeddedRounding_Double() + { + int testResult = 1; + ulong[] answerTable_ToNegativeInfinity = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + ulong[] answerTable_ToPositiveInfinity = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + ulong[] answerTable_ToZero = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + if (Avx512F.X64.IsSupported) + { + using (TestTable doubleTable = new TestTable(new double[2] { -1.0f, -1.0f}, new double[2])) + { + var upper = Unsafe.Read>(doubleTable.inArrayPtr); + ulong value = 15; + var vd3 = Avx512F.X64.ConvertScalarToVector128Double(upper, value, FloatRoundingMode.ToNegativeInfinity); + Unsafe.Write(doubleTable.outArrayPtr, vd3); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on UInt64 input with ToNegativeInfinity:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = Avx512F.X64.ConvertScalarToVector128Double(upper, value, FloatRoundingMode.ToPositiveInfinity); + Unsafe.Write(doubleTable.outArrayPtr, vd3); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToPositiveInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on UInt64 input with ToPositiveInfinity:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = Avx512F.X64.ConvertScalarToVector128Double(upper, value, FloatRoundingMode.ToZero); + Unsafe.Write(doubleTable.outArrayPtr, vd3); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on UInt64 input with ToZero:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToInt32EmbeddedRoundingReflection_Double() + { + int testResult = 1; + int answerTable_ToNegativeInfinity = -1; + int answerTable_ToPositiveInfinity = 0; + int answerTable_ToZero = 0; + if (Avx512F.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45, -0.45); + var res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToNegativeInfinity + }); + + if ((int)res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on double with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToPositiveInfinity + }); + + if ((int)res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on double with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToZero + }); + + if ((int)res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on double with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToUInt32EmbeddedRoundingReflection_Double() + { + int testResult = 1; + uint answerTable_ToNegativeInfinity = 4294967295; + uint answerTable_ToPositiveInfinity = 0; + uint answerTable_ToZero = 0; + if (Avx512F.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45, -0.45); + var res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToUInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToNegativeInfinity + }); + + if ((uint)res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on double with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToUInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToPositiveInfinity + }); + + if ((uint)res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on double with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToUInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToZero + }); + + if ((uint)res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on double with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToInt64EmbeddedRoundingReflection_Double() + { + int testResult = 1; + long answerTable_ToNegativeInfinity = -1; + long answerTable_ToPositiveInfinity = 0; + long answerTable_ToZero = 0; + if (Avx512F.X64.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45, -0.45); + + var res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToNegativeInfinity + }); + + if ((long)res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on double with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToPositiveInfinity + }); + + if ((long)res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on double with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToZero + }); + + if ((long)res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on double with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToUInt64EmbeddedRoundingReflection_Double() + { + int testResult = 1; + ulong answerTable_ToNegativeInfinity = 18446744073709551615; + ulong answerTable_ToPositiveInfinity = 0; + ulong answerTable_ToZero = 0; + if (Avx512F.X64.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45, -0.45); + + var res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToUInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToNegativeInfinity + }); + + if ((ulong)res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on double with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToUInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToPositiveInfinity + }); + + if ((ulong)res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on double with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToUInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToZero + }); + + if ((ulong)res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on double with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertScalarToVector128DoubleInt64EmbeddedRoundingReflection_Double() + { + int testResult = 1; + ulong[] answerTable_ToNegativeInfinity = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + ulong[] answerTable_ToPositiveInfinity = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + ulong[] answerTable_ToZero = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + if (Avx512F.X64.IsSupported) + { + using (TestTable doubleTable = new TestTable(new double[2] { -1.0f, -1.0f}, new double[2])) + { + long value = 15; + var vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Double), new Type[] { typeof(Vector128), typeof(long), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(doubleTable.inArrayPtr), + value, + FloatRoundingMode.ToNegativeInfinity + }); + + Unsafe.Write(doubleTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on Int64 input with ToNegativeInfinity:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Double), new Type[] { typeof(Vector128), typeof(long), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(doubleTable.inArrayPtr), + value, + FloatRoundingMode.ToPositiveInfinity + }); + + Unsafe.Write(doubleTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToPositiveInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on Int64 input with ToPositiveInfinity:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Double), new Type[] { typeof(Vector128), typeof(long), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(doubleTable.inArrayPtr), + value, + FloatRoundingMode.ToZero + }); + + Unsafe.Write(doubleTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToZero[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on Int64 input with ToZero:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertScalarToVector128DoubleUInt64EmbeddedRoundingReflection_Double() + { + int testResult = 1; + ulong[] answerTable_ToNegativeInfinity = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + ulong[] answerTable_ToPositiveInfinity = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + ulong[] answerTable_ToZero = new ulong[2] {0x402e000000000000, 0xbff0000000000000}; + if (Avx512F.X64.IsSupported) + { + using (TestTable doubleTable = new TestTable(new double[2] { -1.0f, -1.0f}, new double[2])) + { + ulong value = 15; + var vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Double), new Type[] { typeof(Vector128), typeof(ulong), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(doubleTable.inArrayPtr), + value, + FloatRoundingMode.ToNegativeInfinity + }); + + Unsafe.Write(doubleTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on UInt64 input with ToNegativeInfinity:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Double), new Type[] { typeof(Vector128), typeof(ulong), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(doubleTable.inArrayPtr), + value, + FloatRoundingMode.ToPositiveInfinity + }); + + Unsafe.Write(doubleTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToPositiveInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on UInt64 input with ToPositiveInfinity:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Double), new Type[] { typeof(Vector128), typeof(ulong), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(doubleTable.inArrayPtr), + value, + FloatRoundingMode.ToZero + }); + + Unsafe.Write(doubleTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < doubleTable.outArray.Length; i++) + { + if (BitConverter.DoubleToUInt64Bits(doubleTable.outArray[i]) != answerTable_ToZero[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Double Embedded rounding failed on UInt64 input with ToZero:"); + foreach (var item in doubleTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + } + } + Assert.Equal(1, testResult); + } + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/EmbeddedRounding.Single.cs b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/EmbeddedRounding.Single.cs new file mode 100644 index 00000000000000..945ebbfe62ea7b --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/EmbeddedRounding.Single.cs @@ -0,0 +1,784 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; +using Xunit; + +namespace IntelHardwareIntrinsicTest._Avx512F +{ + public partial class Program + { + [Fact] + public static unsafe void ConvertToInt32EmbeddedRounding_Single() + { + int testResult = 1; + int answerTable_ToNegativeInfinity = -1; + int answerTable_ToPositiveInfinity = 0; + int answerTable_ToZero = 0; + if (Avx512F.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45f, -0.45f, -0.45f, -0.45f); + int res = Avx512F.ConvertToInt32(inputVec, FloatRoundingMode.ToNegativeInfinity); + + if (res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on float with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.ConvertToInt32(inputVec, FloatRoundingMode.ToPositiveInfinity); + + if (res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on float with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.ConvertToInt32(inputVec, FloatRoundingMode.ToZero); + + if (res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on float with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToUInt32EmbeddedRounding_Single() + { + int testResult = 1; + uint answerTable_ToNegativeInfinity = 4294967295; + uint answerTable_ToPositiveInfinity = 0; + uint answerTable_ToZero = 0; + if (Avx512F.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45f, -0.45f, -0.45f, -0.45f); + uint res = Avx512F.ConvertToUInt32(inputVec, FloatRoundingMode.ToNegativeInfinity); + + if (res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on float with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.ConvertToUInt32(inputVec, FloatRoundingMode.ToPositiveInfinity); + + if (res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on float with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.ConvertToUInt32(inputVec, FloatRoundingMode.ToZero); + + if (res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on float with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertScalarToVector128SingleInt32EmbeddedRounding_Single() + { + int testResult = 1; + uint[] answerTable_ToNegativeInfinity = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + uint[] answerTable_ToPositiveInfinity = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + uint[] answerTable_ToZero = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + if (Avx512F.IsSupported) + { + using (TestTable floatTable = new TestTable(new float[4] { -1.0f, -1.0f, -1.0f, -1.0f }, new float[4])) + { + var upper = Unsafe.Read>(floatTable.inArrayPtr); + int value = 15; + var vd3 = Avx512F.ConvertScalarToVector128Single(upper, value, FloatRoundingMode.ToNegativeInfinity); + Unsafe.Write(floatTable.outArrayPtr, vd3); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on Int32 input with ToNegativeInfinity:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = Avx512F.ConvertScalarToVector128Single(upper, value, FloatRoundingMode.ToPositiveInfinity); + Unsafe.Write(floatTable.outArrayPtr, vd3); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToPositiveInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on Int32 input with ToPositiveInfinity:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = Avx512F.ConvertScalarToVector128Single(upper, value, FloatRoundingMode.ToZero); + Unsafe.Write(floatTable.outArrayPtr, vd3); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on Int32 input with ToZero:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToInt64EmbeddedRounding_Single() + { + int testResult = 1; + long answerTable_ToNegativeInfinity = -1; + long answerTable_ToPositiveInfinity = 0; + long answerTable_ToZero = 0; + if (Avx512F.X64.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45f, -0.45f, -0.45f, -0.45f); + long res = Avx512F.X64.ConvertToInt64(inputVec, FloatRoundingMode.ToNegativeInfinity); + + if (res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on float with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.X64.ConvertToInt64(inputVec, FloatRoundingMode.ToPositiveInfinity); + + if (res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on float with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.X64.ConvertToInt64(inputVec, FloatRoundingMode.ToZero); + + if (res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on float with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToUInt64EmbeddedRounding_Single() + { + int testResult = 1; + ulong answerTable_ToNegativeInfinity = 18446744073709551615; + ulong answerTable_ToPositiveInfinity = 0; + ulong answerTable_ToZero = 0; + if (Avx512F.X64.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45f, -0.45f, -0.45f, -0.45f); + ulong res = Avx512F.X64.ConvertToUInt64(inputVec, FloatRoundingMode.ToNegativeInfinity); + + if (res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on float with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.X64.ConvertToUInt64(inputVec, FloatRoundingMode.ToPositiveInfinity); + + if (res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on float with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = Avx512F.X64.ConvertToUInt64(inputVec, FloatRoundingMode.ToZero); + + if (res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on float with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertScalarToVector128SingleInt64EmbeddedRounding_Single() + { + int testResult = 1; + uint[] answerTable_ToNegativeInfinity = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000,}; + uint[] answerTable_ToPositiveInfinity = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + uint[] answerTable_ToZero = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + if (Avx512F.X64.IsSupported) + { + using (TestTable floatTable = new TestTable(new float[4] { -1.0f, -1.0f, -1.0f, -1.0f }, new float[4])) + { + var upper = Unsafe.Read>(floatTable.inArrayPtr); + long value = 15; + var vd3 = Avx512F.X64.ConvertScalarToVector128Single(upper, value, FloatRoundingMode.ToNegativeInfinity); + Unsafe.Write(floatTable.outArrayPtr, vd3); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on Int64 input with ToNegativeInfinity:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = Avx512F.X64.ConvertScalarToVector128Single(upper, value, FloatRoundingMode.ToPositiveInfinity); + Unsafe.Write(floatTable.outArrayPtr, vd3); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToPositiveInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on Int64 input with ToPositiveInfinity:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = Avx512F.X64.ConvertScalarToVector128Single(upper, value, FloatRoundingMode.ToZero); + Unsafe.Write(floatTable.outArrayPtr, vd3); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on Int64 input with ToZero:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertScalarToVector128SingleUInt64EmbeddedRounding_Single() + { + int testResult = 1; + uint[] answerTable_ToNegativeInfinity = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000,}; + uint[] answerTable_ToPositiveInfinity = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + uint[] answerTable_ToZero = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + if (Avx512F.X64.IsSupported) + { + using (TestTable floatTable = new TestTable(new float[4] { -1.0f, -1.0f, -1.0f, -1.0f }, new float[4])) + { + var upper = Unsafe.Read>(floatTable.inArrayPtr); + ulong value = 15; + var vd3 = Avx512F.X64.ConvertScalarToVector128Single(upper, value, FloatRoundingMode.ToNegativeInfinity); + Unsafe.Write(floatTable.outArrayPtr, vd3); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on UInt64 input with ToNegativeInfinity:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = Avx512F.X64.ConvertScalarToVector128Single(upper, value, FloatRoundingMode.ToPositiveInfinity); + Unsafe.Write(floatTable.outArrayPtr, vd3); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToPositiveInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on UInt64 input with ToPositiveInfinity:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = Avx512F.X64.ConvertScalarToVector128Single(upper, value, FloatRoundingMode.ToZero); + Unsafe.Write(floatTable.outArrayPtr, vd3); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on UInt64 input with ToZero:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToInt32EmbeddedRoundingReflection_Single() + { + int testResult = 1; + int answerTable_ToNegativeInfinity = -1; + int answerTable_ToPositiveInfinity = 0; + int answerTable_ToZero = 0; + if (Avx512F.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45f, -0.45f, -0.45f, -0.45f); + var res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToNegativeInfinity + }); + + if ((int)res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on float with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToPositiveInfinity + }); + + if ((int)res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on float with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToZero + }); + + if ((int)res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToInt32 Embedded rounding failed on float with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToUInt32EmbeddedRoundingReflection_Single() + { + int testResult = 1; + uint answerTable_ToNegativeInfinity = 4294967295; + uint answerTable_ToPositiveInfinity = 0; + uint answerTable_ToZero = 0; + if (Avx512F.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45f, -0.45f, -0.45f, -0.45f); + var res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToUInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToNegativeInfinity + }); + + if ((uint)res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on float with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToUInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToPositiveInfinity + }); + + if ((uint)res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on float with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F).GetMethod(nameof(Avx512F.ConvertToUInt32), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToZero + }); + + if ((uint)res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToUInt32 Embedded rounding failed on float with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToInt64EmbeddedRoundingReflection_Single() + { + int testResult = 1; + long answerTable_ToNegativeInfinity = -1; + long answerTable_ToPositiveInfinity = 0; + long answerTable_ToZero = 0; + if (Avx512F.X64.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45f, -0.45f, -0.45f, -0.45f); + + var res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToNegativeInfinity + }); + + if ((long)res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on float with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToPositiveInfinity + }); + + if ((long)res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on float with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToZero + }); + + if ((long)res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToInt64 Embedded rounding failed on float with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertToUInt64EmbeddedRoundingReflection_Single() + { + int testResult = 1; + ulong answerTable_ToNegativeInfinity = 18446744073709551615; + ulong answerTable_ToPositiveInfinity = 0; + ulong answerTable_ToZero = 0; + if (Avx512F.X64.IsSupported) + { + Vector128 inputVec = Vector128.Create(-0.45f, -0.45f, -0.45f, -0.45f); + + var res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToUInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToNegativeInfinity + }); + + if ((ulong)res != answerTable_ToNegativeInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on float with ToNegativeInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToUInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToPositiveInfinity + }); + + if ((ulong)res != answerTable_ToPositiveInfinity) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on float with ToPositiveInfinity:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + + res = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertToUInt64), new Type[] { typeof(Vector128) , typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + inputVec, + FloatRoundingMode.ToZero + }); + + if ((ulong)res != answerTable_ToZero) + { + Console.WriteLine("Avx512 ConvertToUInt64 Embedded rounding failed on float with ToZero:"); + Console.Write(res); + Console.WriteLine(); + Assert.Fail(""); + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertScalarToVector128SingleInt64EmbeddedRoundingReflection_Single() + { + int testResult = 1; + uint[] answerTable_ToNegativeInfinity = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000,}; + uint[] answerTable_ToPositiveInfinity = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + uint[] answerTable_ToZero = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + if (Avx512F.X64.IsSupported) + { + using (TestTable floatTable = new TestTable(new float[4] { -1.0f, -1.0f, -1.0f, -1.0f}, new float[4])) + { + long value = 15; + var vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Single), new Type[] { typeof(Vector128), typeof(long), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(floatTable.inArrayPtr), + value, + FloatRoundingMode.ToNegativeInfinity + }); + + Unsafe.Write(floatTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on Int64 input with ToNegativeInfinity:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Single), new Type[] { typeof(Vector128), typeof(long), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(floatTable.inArrayPtr), + value, + FloatRoundingMode.ToPositiveInfinity + }); + + Unsafe.Write(floatTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToPositiveInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on Int64 input with ToPositiveInfinity:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Single), new Type[] { typeof(Vector128), typeof(long), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(floatTable.inArrayPtr), + value, + FloatRoundingMode.ToZero + }); + + Unsafe.Write(floatTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToZero[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on Int64 input with ToZero:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + } + } + Assert.Equal(1, testResult); + } + + [Fact] + public static unsafe void ConvertScalarToVector128SingleUInt64EmbeddedRoundingReflection_Single() + { + int testResult = 1; + uint[] answerTable_ToNegativeInfinity = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000,}; + uint[] answerTable_ToPositiveInfinity = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + uint[] answerTable_ToZero = new uint[4] {0x41700000, 0xbf800000, 0xbf800000, 0xbf800000}; + if (Avx512F.X64.IsSupported) + { + using (TestTable floatTable = new TestTable(new float[4] { -1.0f, -1.0f, -1.0f, -1.0f}, new float[4])) + { + ulong value = 15; + var vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Single), new Type[] { typeof(Vector128), typeof(ulong), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(floatTable.inArrayPtr), + value, + FloatRoundingMode.ToNegativeInfinity + }); + + Unsafe.Write(floatTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToNegativeInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on UInt64 input with ToNegativeInfinity:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Single), new Type[] { typeof(Vector128), typeof(ulong), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(floatTable.inArrayPtr), + value, + FloatRoundingMode.ToPositiveInfinity + }); + + Unsafe.Write(floatTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToPositiveInfinity[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on UInt64 input with ToPositiveInfinity:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + + vd3 = typeof(Avx512F.X64).GetMethod(nameof(Avx512F.X64.ConvertScalarToVector128Single), new Type[] { typeof(Vector128), typeof(ulong), typeof(FloatRoundingMode)}) + .Invoke(null, new object[] { + Unsafe.Read>(floatTable.inArrayPtr), + value, + FloatRoundingMode.ToZero + }); + + Unsafe.Write(floatTable.outArrayPtr, (Vector128)(vd3)); + + for (int i = 0; i < floatTable.outArray.Length; i++) + { + if (BitConverter.SingleToUInt32Bits(floatTable.outArray[i]) != answerTable_ToZero[i]) + { + Console.WriteLine("Avx512 ConvertScalarToVector128Single Embedded rounding failed on UInt64 input with ToZero:"); + foreach (var item in floatTable.outArray) + { + Console.Write(item + ", "); + } + Console.WriteLine(); + Assert.Fail(""); + } + } + } + } + Assert.Equal(1, testResult); + } + } +} From 22c5e9f99635121780d53c95243e08ab78d64735 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 15 Feb 2024 15:43:50 -0800 Subject: [PATCH 074/158] Remove C FILE PAL wrappers (#98238) --- .../debug/createdump/crashinfounix.cpp | 4 +- .../debug/createdump/createdumppal.cpp | 20 - .../dlls/mscordac/mscordac_unixexports.src | 10 +- src/coreclr/ilasm/main.cpp | 3 +- src/coreclr/ildasm/dasm.cpp | 5 + src/coreclr/ildasm/dis.cpp | 9 +- src/coreclr/jit/compiler.cpp | 4 +- src/coreclr/pal/inc/pal.h | 158 ++--- src/coreclr/pal/inc/pal_assert.h | 2 +- src/coreclr/pal/inc/rt/palrt.h | 8 +- src/coreclr/pal/src/CMakeLists.txt | 3 - src/coreclr/pal/src/cruntime/file.cpp | 665 ------------------ src/coreclr/pal/src/cruntime/filecrt.cpp | 378 ---------- src/coreclr/pal/src/cruntime/misc.cpp | 30 +- src/coreclr/pal/src/cruntime/printfcpp.cpp | 84 --- src/coreclr/pal/src/cruntime/wchar.cpp | 48 ++ src/coreclr/pal/src/file/file.cpp | 85 ++- src/coreclr/pal/src/include/pal/cruntime.h | 115 --- src/coreclr/pal/src/include/pal/file.h | 89 --- src/coreclr/pal/src/include/pal/file.hpp | 34 - src/coreclr/pal/src/include/pal/palinternal.h | 21 +- src/coreclr/pal/src/include/pal/printfcpp.hpp | 46 -- .../pal/src/include/pal/threadsusp.hpp | 1 - src/coreclr/pal/src/init/pal.cpp | 17 - src/coreclr/pal/src/locale/unicode.cpp | 8 +- src/coreclr/pal/src/misc/fmtmessage.cpp | 2 - src/coreclr/pal/src/misc/perftrace.cpp | 43 +- src/coreclr/pal/tests/palsuite/CMakeLists.txt | 29 - .../palsuite/c_runtime/errno/test1/test1.cpp | 42 -- .../palsuite/c_runtime/errno/test2/test2.cpp | 75 -- .../palsuite/c_runtime/ferror/test1/test1.cpp | 73 -- .../palsuite/c_runtime/ferror/test1/testfile | 1 - .../palsuite/c_runtime/ferror/test2/test2.cpp | 68 -- .../palsuite/c_runtime/ferror/test2/testfile | 1 - .../palsuite/c_runtime/fflush/test1/test1.cpp | 79 --- .../palsuite/c_runtime/fgets/test1/test1.cpp | 101 --- .../palsuite/c_runtime/fgets/test2/test2.cpp | 96 --- .../palsuite/c_runtime/fgets/test3/test3.cpp | 72 -- .../palsuite/c_runtime/fopen/test1/test1.cpp | 81 --- .../palsuite/c_runtime/fopen/test2/test2.cpp | 64 -- .../palsuite/c_runtime/fopen/test3/test3.cpp | 65 -- .../palsuite/c_runtime/fopen/test4/test4.cpp | 81 --- .../palsuite/c_runtime/fopen/test5/test5.cpp | 77 -- .../palsuite/c_runtime/fopen/test6/test6.cpp | 130 ---- .../palsuite/c_runtime/fopen/test7/test7.cpp | 116 --- .../palsuite/c_runtime/fputs/test1/test1.cpp | 99 --- .../palsuite/c_runtime/fputs/test2/test2.cpp | 87 --- .../palsuite/c_runtime/fread/test1/test1.cpp | 134 ---- .../palsuite/c_runtime/fread/test1/testfile | 1 - .../palsuite/c_runtime/fread/test2/test2.cpp | 142 ---- .../palsuite/c_runtime/fread/test2/testfile | 1 - .../palsuite/c_runtime/fread/test3/test3.cpp | 130 ---- .../palsuite/c_runtime/fread/test3/testfile | 1 - .../palsuite/c_runtime/fseek/test1/test1.cpp | 192 ----- .../palsuite/c_runtime/ftell/test1/ftell.cpp | 144 ---- .../c_runtime/ftell/test1/testfile.txt | 1 - .../palsuite/c_runtime/fwrite/test1/test1.cpp | 103 --- .../pal/tests/palsuite/common/palsuite.h | 1 + .../pal/tests/palsuite/compilableTests.txt | 22 - .../PAL_errno/test1/PAL_errno.cpp | 52 -- .../PAL_get_stderr/test1/PAL_get_stderr.cpp | 51 -- .../PAL_get_stdin/test1/PAL_get_stdin.cpp | 67 -- .../PAL_get_stdout/test1/PAL_get_stdout.cpp | 51 -- .../pal/tests/palsuite/paltestlist.txt | 15 - .../tools/StressLogAnalyzer/StressLogDump.cpp | 4 +- src/coreclr/utilcode/debug.cpp | 9 +- src/coreclr/vm/pgo.cpp | 6 +- 67 files changed, 255 insertions(+), 4201 deletions(-) delete mode 100644 src/coreclr/pal/src/cruntime/file.cpp delete mode 100644 src/coreclr/pal/src/cruntime/filecrt.cpp delete mode 100644 src/coreclr/pal/src/cruntime/printfcpp.cpp delete mode 100644 src/coreclr/pal/src/include/pal/printfcpp.hpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/errno/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/errno/test2/test2.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/ferror/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/ferror/test1/testfile delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/ferror/test2/test2.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/ferror/test2/testfile delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fflush/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fgets/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fgets/test2/test2.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fgets/test3/test3.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fopen/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fopen/test2/test2.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fopen/test3/test3.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fopen/test4/test4.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fopen/test5/test5.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fopen/test6/test6.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fopen/test7/test7.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fputs/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fputs/test2/test2.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fread/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fread/test1/testfile delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fread/test2/test2.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fread/test2/testfile delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fread/test3/test3.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fread/test3/testfile delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fseek/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/ftell/test1/ftell.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/ftell/test1/testfile.txt delete mode 100644 src/coreclr/pal/tests/palsuite/c_runtime/fwrite/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/pal_specific/PAL_errno/test1/PAL_errno.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stderr/test1/PAL_get_stderr.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stdin/test1/PAL_get_stdin.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stdout/test1/PAL_get_stdout.cpp diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index 9f72707263b07a..263818e7ad79f5 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -221,7 +221,7 @@ CrashInfo::EnumerateMemoryRegions() printf_error("snprintf failed building /proc//maps\n"); return false; } - FILE* mapsFile = fopen(mapPath, "r"); + FILE* mapsFile = fopen(mapPath, "rb"); if (mapsFile == nullptr) { printf_error("Problem reading maps file: fopen(%s) FAILED %s (%d)\n", mapPath, strerror(errno), errno); @@ -554,7 +554,7 @@ GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name) return false; } - FILE *statusFile = fopen(statusPath, "r"); + FILE *statusFile = fopen(statusPath, "rb"); if (statusFile == nullptr) { printf_error("GetStatus fopen(%s) FAILED %s (%d)\n", statusPath, strerror(errno), errno); diff --git a/src/coreclr/debug/createdump/createdumppal.cpp b/src/coreclr/debug/createdump/createdumppal.cpp index 4dd7204ce91fd5..03b06a84a46139 100644 --- a/src/coreclr/debug/createdump/createdumppal.cpp +++ b/src/coreclr/debug/createdump/createdumppal.cpp @@ -230,26 +230,6 @@ size_t u16_strlen(const WCHAR* str) // #ifdef _DEBUG - -PAL_FILE * -__cdecl -PAL_get_stderr(int caller) -{ - return (PAL_FILE*)stderr; -} - -int -__cdecl -PAL_fprintf(PAL_FILE* stream, const char* format, ...) -{ - va_list args; - va_start(args, format); - int result = vfprintf((FILE*)stream, format, args); - fflush((FILE*)stream); - va_end(args); - return result; -} - DWORD PALAPI GetCurrentProcessId() diff --git a/src/coreclr/dlls/mscordac/mscordac_unixexports.src b/src/coreclr/dlls/mscordac/mscordac_unixexports.src index 4e65be98fee183..8d94292d5c572c 100644 --- a/src/coreclr/dlls/mscordac/mscordac_unixexports.src +++ b/src/coreclr/dlls/mscordac/mscordac_unixexports.src @@ -25,14 +25,12 @@ nativeStringResourceTable_mscorrc #PAL_bsearch #PAL_CopyModuleData #PAL_errno -#PAL_fflush -#PAL__flushall #PAL_free #PAL_GetLogicalCpuCountFromOS #PAL_GetTotalCpuCount #PAL_GetUnwindInfoSize -#PAL_get_stdout -#PAL_get_stderr +#PAL_stdout +#PAL_stderr #PAL_GetApplicationGroupId #PAL_GetTransportName #PAL_GetCurrentThread @@ -52,7 +50,6 @@ nativeStringResourceTable_mscorrc #PAL_malloc #PAL_realloc #PAL_qsort -#PAL_fprintf #PAL__wcstoui64 #PAL_wcstoul #PAL_wcstod @@ -65,9 +62,6 @@ nativeStringResourceTable_mscorrc #PAL_wcschr #PAL_wcscat #PAL_wcsstr -#PAL__open -#PAL__pread -#PAL__close #_wcsicmp #sprintf_s diff --git a/src/coreclr/ilasm/main.cpp b/src/coreclr/ilasm/main.cpp index 838f05aa996cf8..0fe683838d8997 100644 --- a/src/coreclr/ilasm/main.cpp +++ b/src/coreclr/ilasm/main.cpp @@ -531,7 +531,8 @@ extern "C" int _cdecl wmain(int argc, _In_ WCHAR **argv) else { InvalidOption: - fprintf(stderr, "Error : Invalid Option: %LS\n", argv[i]); + MAKE_UTF8PTR_FROMWIDE_NOTHROW(invalidOpt, argv[i]); + fprintf(stderr, "Error : Invalid Option: %s\n", invalidOpt); goto ErrorExit; } } diff --git a/src/coreclr/ildasm/dasm.cpp b/src/coreclr/ildasm/dasm.cpp index 860b36f2e93666..be95e36fa5d53f 100644 --- a/src/coreclr/ildasm/dasm.cpp +++ b/src/coreclr/ildasm/dasm.cpp @@ -7352,9 +7352,14 @@ void CloseNamespace(__inout __nullterminated char* szString) FILE* OpenOutput(_In_ __nullterminated const WCHAR* wzFileName) { +#ifdef HOST_WINDOWS FILE* pfile = NULL; if(g_uCodePage == 0xFFFFFFFF) _wfopen_s(&pfile,wzFileName,W("wb")); else _wfopen_s(&pfile,wzFileName,W("wt")); +#else + FILE* pfile = NULL; + _wfopen_s(&pfile,wzFileName,W("w")); +#endif if(pfile) { diff --git a/src/coreclr/ildasm/dis.cpp b/src/coreclr/ildasm/dis.cpp index 58c86e0e9ae746..21fc8c86790285 100644 --- a/src/coreclr/ildasm/dis.cpp +++ b/src/coreclr/ildasm/dis.cpp @@ -1113,14 +1113,19 @@ BOOL Disassemble(IMDInternalImport *pImport, BYTE *ILHeader, void *GUICookie, md { if(pFile) fclose(pFile); pFile = NULL; - if(fopen_s(&pFile,szFileName,"rt") != 0) +#ifdef HOST_WINDOWS + const char* const mode = "rt"; +#else + const char* const mode = "r"; +#endif + if(fopen_s(&pFile,szFileName, mode) != 0) { char* pch = strrchr(szFileName, DIRECTORY_SEPARATOR_CHAR_A); #ifdef HOST_WINDOWS if(pch == NULL) pch = strrchr(szFileName,':'); #endif pFile = NULL; - if(pch) fopen_s(&pFile,pch+1,"rt"); + if(pch) fopen_s(&pFile,pch+1, mode); } if(bIsNewFile) { diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index e182debb628fb4..c351419687fbf3 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -9089,7 +9089,7 @@ void JitTimer::PrintCsvMethodStats(Compiler* comp) { totCycles += m_info.m_cyclesByPhase[i]; } - fprintf(s_csvFile, "%llu,", m_info.m_cyclesByPhase[i]); + fprintf(s_csvFile, "%llu,", (unsigned long long)m_info.m_cyclesByPhase[i]); if ((JitConfig.JitMeasureIR() != 0) && PhaseReportsIRSize[i]) { @@ -9102,7 +9102,7 @@ void JitTimer::PrintCsvMethodStats(Compiler* comp) fprintf(s_csvFile, "%u,", comp->info.compNativeCodeSize); fprintf(s_csvFile, "%zu,", comp->compInfoBlkSize); fprintf(s_csvFile, "%zu,", comp->compGetArenaAllocator()->getTotalBytesAllocated()); - fprintf(s_csvFile, "%llu,", m_info.m_totalCycles); + fprintf(s_csvFile, "%llu,", (unsigned long long)m_info.m_totalCycles); fprintf(s_csvFile, "%f\n", CachedCyclesPerSecond()); fflush(s_csvFile); diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 8b9f362267cee0..4a0a341b2272ef 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -3982,32 +3982,13 @@ PAL_GetCurrentThreadAffinitySet(SIZE_T size, UINT_PTR* data); #ifndef PAL_STDCPP_COMPAT #define exit PAL_exit #define realloc PAL_realloc -#define fopen PAL_fopen -#define fprintf PAL_fprintf -#define vfprintf PAL_vfprintf #define rand PAL_rand #define time PAL_time #define getenv PAL_getenv -#define fgets PAL_fgets #define qsort PAL_qsort #define bsearch PAL_bsearch -#define ferror PAL_ferror -#define fread PAL_fread -#define fwrite PAL_fwrite -#define ftell PAL_ftell -#define fclose PAL_fclose -#define fflush PAL_fflush -#define fputs PAL_fputs -#define fseek PAL_fseek -#define fgetpos PAL_fgetpos -#define fsetpos PAL_fsetpos -#define setvbuf PAL_setvbuf #define malloc PAL_malloc #define free PAL_free -#define _open PAL__open -#define _pread PAL__pread -#define _close PAL__close -#define _flushall PAL__flushall #ifdef HOST_AMD64 #define _mm_getcsr PAL__mm_getcsr @@ -4086,6 +4067,68 @@ PALIMPORT int __cdecl iswxdigit(wint_t); PALIMPORT wint_t __cdecl towupper(wint_t); PALIMPORT wint_t __cdecl towlower(wint_t); PALIMPORT int remove(const char*); + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +/* Locale categories */ +#define LC_ALL 0 +#define LC_COLLATE 1 +#define LC_CTYPE 2 +#define LC_MONETARY 3 +#define LC_NUMERIC 4 +#define LC_TIME 5 + +#define _IOFBF 0 /* setvbuf should set fully buffered */ +#define _IOLBF 1 /* setvbuf should set line buffered */ +#define _IONBF 2 /* setvbuf should set unbuffered */ + +struct _FILE; + +#ifdef DEFINE_DUMMY_FILE_TYPE +#define FILE _PAL_FILE +struct _PAL_FILE; +#else +typedef _FILE FILE; +#endif // DEFINE_DUMMY_FILE_TYPE + +PALIMPORT int __cdecl fclose(FILE *); +PALIMPORT int __cdecl fflush(FILE *); +PALIMPORT size_t __cdecl fwrite(const void *, size_t, size_t, FILE *); +PALIMPORT size_t __cdecl fread(void *, size_t, size_t, FILE *); +PALIMPORT char * __cdecl fgets(char *, int, FILE *); +PALIMPORT int __cdecl fputs(const char *, FILE *); +PALIMPORT int __cdecl fprintf(FILE *, const char *, ...); +PALIMPORT int __cdecl vfprintf(FILE *, const char *, va_list); +PALIMPORT int __cdecl fseek(FILE *, LONG, int); +PALIMPORT LONG __cdecl ftell(FILE *); +PALIMPORT int __cdecl ferror(FILE *); +PALIMPORT FILE * __cdecl fopen(const char *, const char *); +PALIMPORT int __cdecl setvbuf(FILE *stream, char *, int, size_t); + +// We need a PAL shim for errno and the standard streams as it's not possible to replicate these definition from the standard library +// in all cases. Instead, we shim it and implement the PAL function where we can include the standard headers. +// When we allow people to include the standard headers, then we can remove this. + +PALIMPORT DLLEXPORT int * __cdecl PAL_errno(); +#define errno (*PAL_errno()) + +// Only provide a prototype for the PAL forwarders for the standard streams if we are not including the standard headers. +#ifndef DEFINE_DUMMY_FILE_TYPE + +extern "C" PALIMPORT DLLEXPORT FILE* __cdecl PAL_stdout(); +extern "C" PALIMPORT DLLEXPORT FILE* __cdecl PAL_stdin(); +extern "C" PALIMPORT DLLEXPORT FILE* __cdecl PAL_stderr(); +#define stdout PAL_stdout() +#define stdin PAL_stdin() +#define stderr PAL_stderr() + +#endif + +#ifdef DEFINE_DUMMY_FILE_TYPE +#undef FILE +#endif #endif // PAL_STDCPP_COMPAT /* _TRUNCATE */ @@ -4125,6 +4168,10 @@ PALIMPORT errno_t __cdecl _wcslwr_s(WCHAR *, size_t sz); PALIMPORT DLLEXPORT errno_t __cdecl _i64tow_s(long long, WCHAR *, size_t, int); PALIMPORT int __cdecl _wtoi(const WCHAR *); +#ifndef DEFINE_DUMMY_FILE_TYPE +PALIMPORT FILE * __cdecl _wfopen(const WCHAR *, const WCHAR *); +#endif + inline int _stricmp(const char* a, const char* b) { return strcasecmp(a, b); @@ -4321,56 +4368,6 @@ PALIMPORT time_t __cdecl time(time_t *); #endif // !PAL_STDCPP_COMPAT -PALIMPORT DLLEXPORT int __cdecl _open(const char *szPath, int nFlags, ...); -PALIMPORT DLLEXPORT size_t __cdecl _pread(int fd, void *buf, size_t nbytes, ULONG64 offset); -PALIMPORT DLLEXPORT int __cdecl _close(int); -PALIMPORT DLLEXPORT int __cdecl _flushall(); - -#ifdef PAL_STDCPP_COMPAT - -struct _PAL_FILE; -typedef struct _PAL_FILE PAL_FILE; - -#else // PAL_STDCPP_COMPAT - -struct _FILE; -typedef struct _FILE FILE; -typedef struct _FILE PAL_FILE; - -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 - -/* Locale categories */ -#define LC_ALL 0 -#define LC_COLLATE 1 -#define LC_CTYPE 2 -#define LC_MONETARY 3 -#define LC_NUMERIC 4 -#define LC_TIME 5 - -#define _IOFBF 0 /* setvbuf should set fully buffered */ -#define _IOLBF 1 /* setvbuf should set line buffered */ -#define _IONBF 2 /* setvbuf should set unbuffered */ - -#endif // PAL_STDCPP_COMPAT - -PALIMPORT int __cdecl PAL_fclose(PAL_FILE *); -PALIMPORT DLLEXPORT int __cdecl PAL_fflush(PAL_FILE *); -PALIMPORT size_t __cdecl PAL_fwrite(const void *, size_t, size_t, PAL_FILE *); -PALIMPORT size_t __cdecl PAL_fread(void *, size_t, size_t, PAL_FILE *); -PALIMPORT char * __cdecl PAL_fgets(char *, int, PAL_FILE *); -PALIMPORT int __cdecl PAL_fputs(const char *, PAL_FILE *); -PALIMPORT DLLEXPORT int __cdecl PAL_fprintf(PAL_FILE *, const char *, ...); -PALIMPORT int __cdecl PAL_vfprintf(PAL_FILE *, const char *, va_list); -PALIMPORT int __cdecl PAL_fseek(PAL_FILE *, LONG, int); -PALIMPORT LONG __cdecl PAL_ftell(PAL_FILE *); -PALIMPORT int __cdecl PAL_ferror(PAL_FILE *); -PALIMPORT PAL_FILE * __cdecl PAL_fopen(const char *, const char *); -PALIMPORT int __cdecl PAL_setvbuf(PAL_FILE *stream, char *, int, size_t); - -PALIMPORT PAL_FILE * __cdecl _wfopen(const WCHAR *, const WCHAR *); - /* Maximum value that can be returned by the rand function. */ #ifndef PAL_STDCPP_COMPAT @@ -4380,29 +4377,6 @@ PALIMPORT PAL_FILE * __cdecl _wfopen(const WCHAR *, const WCHAR *); PALIMPORT int __cdecl rand(void); PALIMPORT void __cdecl srand(unsigned int); -#ifdef _MSC_VER -#define PAL_get_caller _MSC_VER -#else -#define PAL_get_caller 0 -#endif - -PALIMPORT DLLEXPORT PAL_FILE * __cdecl PAL_get_stdout(int caller); -PALIMPORT PAL_FILE * __cdecl PAL_get_stdin(int caller); -PALIMPORT DLLEXPORT PAL_FILE * __cdecl PAL_get_stderr(int caller); -PALIMPORT DLLEXPORT int * __cdecl PAL_errno(int caller); - -#ifdef PAL_STDCPP_COMPAT -#define PAL_stdout (PAL_get_stdout(PAL_get_caller)) -#define PAL_stdin (PAL_get_stdin(PAL_get_caller)) -#define PAL_stderr (PAL_get_stderr(PAL_get_caller)) -#define PAL_errno (*PAL_errno(PAL_get_caller)) -#else // PAL_STDCPP_COMPAT -#define stdout (PAL_get_stdout(PAL_get_caller)) -#define stdin (PAL_get_stdin(PAL_get_caller)) -#define stderr (PAL_get_stderr(PAL_get_caller)) -#define errno (*PAL_errno(PAL_get_caller)) -#endif // PAL_STDCPP_COMPAT - PALIMPORT DLLEXPORT char * __cdecl getenv(const char *); PALIMPORT DLLEXPORT int __cdecl _putenv(const char *); diff --git a/src/coreclr/pal/inc/pal_assert.h b/src/coreclr/pal/inc/pal_assert.h index 87af991d9abbc3..a4b49c1ddafc5f 100644 --- a/src/coreclr/pal/inc/pal_assert.h +++ b/src/coreclr/pal/inc/pal_assert.h @@ -35,7 +35,7 @@ extern "C" { #if defined(_DEBUG) #define _ASSERTE(e) do { \ if (!(e)) { \ - PAL_fprintf (PAL_get_stderr(PAL_get_caller), \ + fprintf (stderr, \ "ASSERT FAILED\n" \ "\tExpression: %s\n" \ "\tLocation: line %d in %s\n" \ diff --git a/src/coreclr/pal/inc/rt/palrt.h b/src/coreclr/pal/inc/rt/palrt.h index 1e52727ccdba95..1f7f413456965c 100644 --- a/src/coreclr/pal/inc/rt/palrt.h +++ b/src/coreclr/pal/inc/rt/palrt.h @@ -681,9 +681,9 @@ inline int __cdecl _vscprintf_unsafe(const char *_Format, va_list _ArgList) } } -inline errno_t __cdecl _wfopen_unsafe(PAL_FILE * *ff, const WCHAR *fileName, const WCHAR *mode) +inline errno_t __cdecl _wfopen_unsafe(FILE * *ff, const WCHAR *fileName, const WCHAR *mode) { - PAL_FILE *result = _wfopen(fileName, mode); + FILE *result = _wfopen(fileName, mode); if(result == 0) { return 1; } else { @@ -692,9 +692,9 @@ inline errno_t __cdecl _wfopen_unsafe(PAL_FILE * *ff, const WCHAR *fileName, con } } -inline errno_t __cdecl _fopen_unsafe(PAL_FILE * *ff, const char *fileName, const char *mode) +inline errno_t __cdecl _fopen_unsafe(FILE * *ff, const char *fileName, const char *mode) { - PAL_FILE *result = PAL_fopen(fileName, mode); + FILE *result = fopen(fileName, mode); if(result == 0) { return 1; } else { diff --git a/src/coreclr/pal/src/CMakeLists.txt b/src/coreclr/pal/src/CMakeLists.txt index 13aa8b9c7f756f..745162987a42e4 100644 --- a/src/coreclr/pal/src/CMakeLists.txt +++ b/src/coreclr/pal/src/CMakeLists.txt @@ -129,11 +129,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND (CLR_CMAKE_HOST_ARCH_AMD64 OR CLR_CM endif() set(SOURCES - cruntime/file.cpp - cruntime/filecrt.cpp cruntime/malloc.cpp cruntime/misc.cpp - cruntime/printfcpp.cpp cruntime/thread.cpp cruntime/wchar.cpp debug/debug.cpp diff --git a/src/coreclr/pal/src/cruntime/file.cpp b/src/coreclr/pal/src/cruntime/file.cpp deleted file mode 100644 index 57cf7faa4caf3b..00000000000000 --- a/src/coreclr/pal/src/cruntime/file.cpp +++ /dev/null @@ -1,665 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*++ - - - -Module Name: - - file.c - -Abstract: - - Implementation of the file functions in the C runtime library that - are Windows specific. - - - ---*/ - -#include "pal/palinternal.h" -#include "pal/dbgmsg.h" -#include "pal/file.h" -#include "pal/cruntime.h" - -#include "pal/thread.hpp" -#include "pal/threadsusp.hpp" - -#include -#include -#include -#include - -#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL - #define CLEARERR(f) clearerr((f)->bsdFilePtr) -#else - #define CLEARERR(f) -#endif - -SET_DEFAULT_DEBUG_CHANNEL(CRT); - -/* Global variables storing the std streams.*/ -PAL_FILE PAL_Stdout; -PAL_FILE PAL_Stdin; -PAL_FILE PAL_Stderr; - -/*++ - -Function: - - CRTInitStdStreams. - - Initializes the standard streams. - Returns TRUE on success, FALSE otherwise. ---*/ -BOOL CRTInitStdStreams() -{ - /* stdout */ - PAL_Stdout.bsdFilePtr = stdout; - PAL_Stdout.PALferrorCode = PAL_FILE_NOERROR; - PAL_Stdout.bTextMode = TRUE; - - /* stdin */ - PAL_Stdin.bsdFilePtr = stdin; - PAL_Stdin.PALferrorCode = PAL_FILE_NOERROR; - PAL_Stdin.bTextMode = TRUE; - - /* stderr */ - PAL_Stderr.bsdFilePtr = stderr; - PAL_Stderr.PALferrorCode = PAL_FILE_NOERROR; - PAL_Stderr.bTextMode = TRUE; - return TRUE; -} - -/*++ -Function : - - MapFileOpenModes - - Maps Windows file open modes to Unix fopen modes and validates. - ---*/ -static LPSTR MapFileOpenModes(LPSTR str , BOOL * bTextMode) -{ - LPSTR retval = NULL; - LPSTR temp = NULL; - - if (NULL == bTextMode) - { - ASSERT("MapFileOpenModes called with a NULL parameter for bTextMode.\n"); - return NULL; - } - - *bTextMode = TRUE; - - if (NULL == str) - { - ASSERT("MapFileOpenModes called with a NULL parameter for str.\n"); - return NULL; - } - - /* The PAL behaves differently for some Windows file open modes: - - c, n, S, R, and T: these are all hints to the system that aren't supported - by the PAL. Since the user cannot depend on this behavior, it's safe to - simply ignore these modes. - - D: specifies a file as temporary. This file is expected to be deleted when - the last file descriptor is closed. The PAL does not support this behavior - and asserts when this mode is used. - - t: represents opening in text mode. Calls to fdopen on Unix don't accept - 't' so it is silently stripped out. However, the PAL supports the mode by - having the PAL wrappers do the translation of CR-LF to LF and vice versa. - - t vs. b: To get binary mode, you must explicitly use 'b'. If neither mode - is specified on Windows, the default mode is defined by the global - variable _fmode. The PAL simply defaults to text mode. After examining - CLR usage patterns, the PAL behavior seems acceptable. */ - - /* Check if the mode specifies deleting the temporary file - automatically when the last file descriptor is closed. - The PAL does not support this behavior. */ - if (NULL != strchr(str,'D')) - { - ASSERT("The PAL doesn't support the 'D' flag for fopen.\n"); - return NULL; - } - - /* Check if the mode specifies opening in binary. - If so, set the bTextMode to false. */ - if(NULL != strchr(str,'b')) - { - *bTextMode = FALSE; - } - - retval = (LPSTR)PAL_malloc( ( strlen( str ) + 1 ) * sizeof( CHAR ) ); - if (NULL == retval) - { - ERROR("Unable to allocate memory.\n"); - return NULL; - } - - temp = retval; - while ( *str ) - { - if ( *str == 'r' || *str == 'w' || *str == 'a' ) - { - *temp = *str; - temp++; - if ( ( ++str != NULL ) && *str == '+' ) - { - *temp = *str; - temp++; - str++; - } - } - else - { - str++; - } - } - *temp = '\0'; - return retval; -} - -#if UNGETC_NOT_RETURN_EOF -/*++ -Function : - - WriteOnlyMode - - Returns TRUE to if a file is opened in write-only mode, - Otherwise FALSE. - ---*/ -static BOOL WriteOnlyMode(FILE* pFile) -{ - INT fd, flags; - - if (pFile != NULL) - { - fd = fileno(pFile); - if ((flags = fcntl(fd, F_GETFL)) >= 0) - { - if ((flags & O_ACCMODE) == O_WRONLY) - { - return TRUE; - } - } - } - return FALSE; -} -#endif //UNGETC_NOT_RETURN_EOF - -/*++ - -Function : - fopen - -see MSDN doc. - ---*/ -PAL_FILE * -__cdecl -PAL_fopen(const char * fileName, const char * mode) -{ - PAL_FILE *f = NULL; - LPSTR supported = NULL; - LPSTR UnixFileName = NULL; - struct stat stat_data; - BOOL bTextMode = TRUE; - - PERF_ENTRY(fopen); - ENTRY("fopen ( fileName=%p (%s) mode=%p (%s))\n", fileName, fileName, mode , mode ); - - _ASSERTE(fileName != NULL); - _ASSERTE(mode != NULL); - - if ( *mode == 'r' || *mode == 'w' || *mode == 'a' ) - { - supported = MapFileOpenModes( (char*)mode,&bTextMode); - - if ( !supported ) - { - goto done; - } - - UnixFileName = strdup(fileName); - if (UnixFileName == NULL ) - { - ERROR("strdup() failed\n"); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto done; - } - - /*I am not checking for the case where stat fails - *as fopen will handle the error more gracefully in case - *UnixFileName is invalid*/ - if ((stat(UnixFileName, &stat_data) == 0 ) && - ((stat_data.st_mode & S_IFMT) == S_IFDIR)) - { - goto done; - } - - f = (PAL_FILE*)PAL_malloc( sizeof( PAL_FILE ) ); - if ( f ) - { - f->bsdFilePtr = (FILE*)fopen( UnixFileName, supported ); - f->PALferrorCode = PAL_FILE_NOERROR; - f->bTextMode = bTextMode; - if ( !f->bsdFilePtr ) - { - /* Failed */ - PAL_free( f ); - f = NULL; - } -#if UNGETC_NOT_RETURN_EOF - else - { - f->bWriteOnlyMode = WriteOnlyMode(f->bsdFilePtr); - } -#endif //UNGETC_NOT_RETURN_EOF - } - else - { - ERROR( "Unable to allocate memory to the PAL_FILE wrapper\n" ); - } - } - else - { - ERROR( "The mode flags must start with either an a, w, or r.\n" ); - } - -done: - PAL_free( supported ); - supported = NULL; - PAL_free( UnixFileName ); - - LOGEXIT( "fopen returns FILE* %p\n", f ); - PERF_EXIT(fopen); - return f; -} - -/*++ -Function: - _wfopen - -see MSDN doc. - ---*/ -PAL_FILE * -__cdecl -_wfopen( - const wchar_16 *fileName, - const wchar_16 *mode) -{ - CHAR mbFileName[ _MAX_PATH ]; - CHAR mbMode[ 10 ]; - PAL_FILE * filePtr = NULL; - - PERF_ENTRY(_wfopen); - ENTRY("_wfopen(fileName:%p (%S), mode:%p (%S))\n", fileName, fileName, mode, mode); - - _ASSERTE(fileName != NULL); - _ASSERTE(mode != NULL); - - /* Convert the parameters to ASCII and defer to PAL_fopen */ - if ( WideCharToMultiByte( CP_ACP, 0, fileName, -1, mbFileName, - sizeof mbFileName, NULL, NULL ) != 0 ) - { - if ( WideCharToMultiByte( CP_ACP, 0, mode, -1, mbMode, - sizeof mbMode, NULL, NULL ) != 0 ) - { - filePtr = PAL_fopen(mbFileName, mbMode); - } - else - { - ERROR( "An error occurred while converting mode to ANSI.\n" ); - } - } - else - { - ERROR( "An error occurred while converting" - " fileName to ANSI string.\n" ); - } - LOGEXIT("_wfopen returning FILE* %p\n", filePtr); - PERF_EXIT(_wfopen); - return filePtr; -} - -/*++ -Function - PAL_get_stdout. - - Returns the stdout stream. ---*/ -PAL_FILE * __cdecl PAL_get_stdout(int caller) -{ - PERF_ENTRY(get_stdout); - ENTRY("PAL_get_stdout\n"); - LOGEXIT("PAL_get_stdout returns PAL_FILE * %p\n", &PAL_Stdout ); - PERF_EXIT(get_stdout); - return &PAL_Stdout; -} - -/*++ -Function - PAL_get_stdin. - - Returns the stdin stream. ---*/ -PAL_FILE * __cdecl PAL_get_stdin(int caller) -{ - PERF_ENTRY(get_stdin); - ENTRY("PAL_get_stdin\n"); - LOGEXIT("PAL_get_stdin returns PAL_FILE * %p\n", &PAL_Stdin ); - PERF_EXIT(get_stdin); - return &PAL_Stdin; -} - -/*++ -Function - PAL_get_stderr. - - Returns the stderr stream. ---*/ -PAL_FILE * __cdecl PAL_get_stderr(int caller) -{ - PERF_ENTRY(get_stderr); - ENTRY("PAL_get_stderr\n"); - LOGEXIT("PAL_get_stderr returns PAL_FILE * %p\n", &PAL_Stderr ); - PERF_EXIT(get_stderr); - return &PAL_Stderr; -} - -/*++ - -Function: - - PAL_pread - -See msdn for more details. ---*/ -size_t __cdecl PAL__pread(int fd, void *buf, size_t nbytes, ULONG64 offset) -{ - return pread(fd, buf, nbytes, offset); -} - -/*++ - -Function: - - _close - -See msdn for more details. ---*/ -int __cdecl PAL__close(int handle) -{ - INT nRetVal = 0; - - PERF_ENTRY(_close); - ENTRY( "_close( handle=%d )\n", handle ); - - nRetVal = close( handle ); - - LOGEXIT( "_close returning %d.\n", nRetVal ); - PERF_EXIT(_close); - return nRetVal; -} - -int __cdecl PAL__flushall() -{ - return fflush(NULL); -} - -int __cdecl PAL_getc(PAL_FILE *stream); - -/*++ -Function : - - fread - - See MSDN for more details. ---*/ - -size_t -__cdecl -PAL_fread(void * buffer, size_t size, size_t count, PAL_FILE * f) -{ - size_t nReadBytes = 0; - - PERF_ENTRY(fread); - ENTRY( "fread( buffer=%p, size=%d, count=%d, f=%p )\n", - buffer, size, count, f ); - - _ASSERTE(f != NULL); - - CLEARERR(f); - - if(f->bTextMode != TRUE) - { - nReadBytes = fread( buffer, size, count, f->bsdFilePtr ); - } - else - { - size_t i=0; - if(size > 0) - { - size_t j=0; - LPSTR temp = (LPSTR)buffer; - int nChar = 0; - int nCount =0; - - for(i=0; i< count; i++) - { - for(j=0; j< size; j++) - { - if((nChar = PAL_getc(f)) == EOF) - { - nReadBytes = i; - goto done; - } - else - { - temp[nCount++]= (char)nChar; - } - } - } - } - nReadBytes = i; - } - -done: - LOGEXIT( "fread returning size_t %d\n", nReadBytes ); - PERF_EXIT(fread); - return nReadBytes; -} - - -/*++ -Function : - - ferror - - See MSDN for more details. ---*/ -int -_cdecl -PAL_ferror(PAL_FILE * f) -{ - INT nErrorCode = PAL_FILE_NOERROR; - - PERF_ENTRY(ferror); - ENTRY( "ferror( f=%p )\n", f ); - - _ASSERTE(f != NULL); - - nErrorCode = ferror( f->bsdFilePtr ); - if ( 0 == nErrorCode ) - { - /* See if the PAL file error code is set. */ - nErrorCode = f->PALferrorCode; - } - - LOGEXIT( "ferror returns %d\n", nErrorCode ); - PERF_EXIT(ferror); - return nErrorCode; -} - - -/*++ -Function : - - fclose - - See MSDN for more details. ---*/ -int -_cdecl -PAL_fclose(PAL_FILE * f) -{ - INT nRetVal = 0; - - PERF_ENTRY(fclose); - ENTRY( "fclose( f=%p )\n", f ); - - _ASSERTE(f != NULL); - - CLEARERR(f); - - nRetVal = fclose( f->bsdFilePtr ); - PAL_free( f ); - - LOGEXIT( "fclose returning %d\n", nRetVal ); - PERF_EXIT(fclose); - return nRetVal; -} - -/*++ -Function : - - fputs - - See MSDN for more details. ---*/ -int -_cdecl -PAL_fputs(const char * str, PAL_FILE * f) -{ - INT nRetVal = 0; - - PERF_ENTRY(fputs); - ENTRY( "fputs( %p (%s), %p )\n", str, str, f); - - _ASSERTE(str != NULL); - _ASSERTE(f != NULL); - - CLEARERR(f); - - nRetVal = fputs( str, f->bsdFilePtr ); - - LOGEXIT( "fputs returning %d\n", nRetVal ); - PERF_EXIT(fputs); - return nRetVal; -} - -/*++ -Function : - - ftell - - See MSDN for more details. ---*/ -LONG -_cdecl -PAL_ftell(PAL_FILE * f) -{ - long lRetVal = 0; - - PERF_ENTRY(ftell); - ENTRY( "ftell( %p )\n", f ); - - _ASSERTE(f != NULL); - lRetVal = ftell( f->bsdFilePtr ); - -#ifdef HOST_64BIT - /* Windows does not set an error if the file pointer's position - is greater than _I32_MAX. It just returns -1. */ - if (lRetVal > _I32_MAX) - { - lRetVal = -1; - } -#endif - - LOGEXIT( "ftell returning %ld\n", lRetVal ); - PERF_EXIT(ftell); - /* This explicit cast to LONG is used to silence any potential warnings - due to implicitly casting the native long lRetVal to LONG when returning. */ - return (LONG)lRetVal; -} - -/*++ -Function : - getc - - See MSDN for more details. ---*/ -int -_cdecl -PAL_getc(PAL_FILE * f) -{ - INT nRetVal = 0; - INT temp =0; - - PERF_ENTRY(getc); - ENTRY( "getc( %p )\n", f ); - - _ASSERTE(f != NULL); - - CLEARERR(f); - - nRetVal = getc( f->bsdFilePtr ); - - if ( (f->bTextMode) && (nRetVal == '\r') ) - { - if ((temp = getc( f->bsdFilePtr ))== '\n') - { - nRetVal ='\n'; - } - else if (EOF == ungetc( temp, f->bsdFilePtr )) - { - ERROR("ungetc operation failed\n"); - } - } - - LOGEXIT( "getc returning %d\n", nRetVal ); - PERF_EXIT(getc); - return nRetVal; -} - -/*++ -Function : - - setvbuf - - See MSDN for more details. ---*/ -int -_cdecl -PAL_setvbuf(PAL_FILE *f, char *buf, int type, size_t size) -{ - INT nRetVal = 0; - - PERF_ENTRY(setvbuf); - ENTRY( "setvbuf( %p, %p, %d, %ul )\n", f, buf, type, size); - - _ASSERTE(f != NULL); - - nRetVal = setvbuf(f->bsdFilePtr, buf, type, size); - - LOGEXIT( "setvbuf returning %d\n", nRetVal ); - PERF_EXIT(setvbuf); - return nRetVal; -} diff --git a/src/coreclr/pal/src/cruntime/filecrt.cpp b/src/coreclr/pal/src/cruntime/filecrt.cpp deleted file mode 100644 index 5d2fe0e5d999f4..00000000000000 --- a/src/coreclr/pal/src/cruntime/filecrt.cpp +++ /dev/null @@ -1,378 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*++ - -Module Name: - - filecrt.cpp - -Abstract: - - Implementation of the file functions in the C runtime library that - are Windows specific. - ---*/ - -#include "pal/thread.hpp" -#include "pal/file.hpp" - -#include "pal/palinternal.h" -#include "pal/dbgmsg.h" -#include "pal/file.h" -#include "pal/cruntime.h" - -#include -#include -#include - -#ifdef __APPLE__ -#include -#endif // __APPLE__ - -using namespace CorUnix; - -SET_DEFAULT_DEBUG_CHANNEL(CRT); - -/*++ -Function: - PAL_fflush - -See MSDN for more details. ---*/ -int -_cdecl -PAL_fflush( PAL_FILE *stream ) -{ - int nRetVal = 0; - - PERF_ENTRY(fflush); - ENTRY( "fflush( %p )\n", stream ); - - nRetVal = fflush(stream ? stream->bsdFilePtr : NULL); - - LOGEXIT( "fflush returning %d\n", nRetVal ); - PERF_EXIT(fflush); - return nRetVal; -} - - -/*++ -PAL__open - -Wrapper function for InternalOpen. - -Input parameters: - -szPath = pointer to a pathname of a file to be opened -nFlags = arguments that control how the file should be accessed -mode = file permission settings that are used only when a file is created - -Return value: - File descriptor on success, -1 on failure ---*/ -int -__cdecl -PAL__open( - const char *szPath, - int nFlags, - ... - ) -{ - int nRet = -1; - int mode = 0; - va_list ap; - - // If nFlags does not contain O_CREAT, the mode parameter will be ignored. - if (nFlags & O_CREAT) - { - va_start(ap, nFlags); - mode = va_arg(ap, int); - va_end(ap); - } - - nRet = InternalOpen(szPath, nFlags, mode); - return nRet; -} - -/*++ -InternalOpen - -Wrapper for open. - -Input parameters: - -szPath = pointer to a pathname of a file to be opened -nFlags = arguments that control how the file should be accessed -mode = file permission settings that are used only when a file is created - -Return value: - File descriptor on success, -1 on failure ---*/ -int -CorUnix::InternalOpen( - const char *szPath, - int nFlags, - ... - ) -{ - int nRet = -1; - int mode = 0; - va_list ap; - - // If nFlags does not contain O_CREAT, the mode parameter will be ignored. - if (nFlags & O_CREAT) - { - va_start(ap, nFlags); - mode = va_arg(ap, int); - va_end(ap); - } - - do - { -#if OPEN64_IS_USED_INSTEAD_OF_OPEN - nRet = open64(szPath, nFlags, mode); -#else - nRet = open(szPath, nFlags, mode); -#endif - } - while ((nRet == -1) && (errno == EINTR)); - - return nRet; -} - - -/*++ -PAL_fgets - -Wrapper function for InternalFgets. - -Input parameters: - -sz = stores characters read from the given file stream -nSize = number of characters to be read -pf = stream to read characters from - -Return value: - Returns a pointer to the string storing the characters on success - and NULL on failure. ---*/ -char * -__cdecl -PAL_fgets( - char *sz, - int nSize, - PAL_FILE *pf - ) -{ - char * szBuf; - - PERF_ENTRY(fgets); - ENTRY( "fgets(sz=%p (%s) nSize=%d pf=%p)\n", sz, sz, nSize, pf); - - if (pf != NULL) - { - szBuf = InternalFgets(sz, nSize, pf->bsdFilePtr, pf->bTextMode); - } - else - { - szBuf = NULL; - } - - LOGEXIT("fgets() returns %p\n", szBuf); - PERF_EXIT(fgets); - - return szBuf; -} - -/*++ -InternalFgets - -Wrapper for fgets. - -Input parameters: - -sz = stores characters read from the given file stream -nSize = number of characters to be read -f = stream to read characters from -fTextMode = flag that indicates if file contents are text or binary - -Return value: - Returns a pointer to the string storing the characters on success - and NULL on failure. - -Notes: -In Unix systems, fgets() can return an error if it gets interrupted by a -signal before reading anything, and errno is set to EINTR. When this -happens, it is SOP to call fgets again. ---*/ -char * -CorUnix::InternalFgets( - char *sz, - int nSize, - FILE *f, - bool fTextMode - ) -{ - char *retval = NULL; - - _ASSERTE(sz != NULL); - _ASSERTE(f != NULL); - -#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL - clearerr(f); -#endif - - do - { - retval = fgets(sz, nSize, f); - if (NULL==retval) - { - if (feof(f)) - { - TRACE("Reached EOF\n"); - break; - } - /* The man page suggests using ferror and feof to distinguish - between error and EOF, but feof and errno is sufficient. - Not all cases that set errno also flag ferror, so just - checking errno is the best solution. */ - if (EINTR != errno) - { - WARN("got error; errno is %d (%s)\n",errno, strerror(errno)); - break; - } - /* we ignored a EINTR error, reset the stream's error state */ - clearerr(f); - TRACE("call got interrupted (EINTR), trying again\n"); - } - if (fTextMode) - { - int len = strlen(sz); - if ((len>=2) && (sz[len-1]=='\n') && (sz[len-2]=='\r')) - { - sz[len-2]='\n'; - sz[len-1]='\0'; - } - } - } while(NULL == retval); - - return retval; -} - -/*++ -PAL_fwrite - -Wrapper function for InternalFwrite. - -Input parameters: - -pvBuffer = array of objects to write to the given file stream -nSize = size of a object in bytes -nCount = number of objects to write -pf = stream to write characters to - -Return value: - Returns the number of objects written. ---*/ -size_t -__cdecl -PAL_fwrite( - const void *pvBuffer, - size_t nSize, - size_t nCount, - PAL_FILE *pf - ) -{ - size_t nWrittenBytes = 0; - - PERF_ENTRY(fwrite); - ENTRY( "fwrite( pvBuffer=%p, nSize=%d, nCount=%d, pf=%p )\n", - pvBuffer, nSize, nCount, pf); - _ASSERTE(pf != NULL); - - nWrittenBytes = InternalFwrite(pvBuffer, nSize, nCount, pf->bsdFilePtr, &pf->PALferrorCode); - - LOGEXIT( "fwrite returning size_t %d\n", nWrittenBytes ); - PERF_EXIT(fwrite); - return nWrittenBytes; -} - -/*++ -InternalFwrite - -Wrapper for fwrite. - -Input parameters: - -pvBuffer = array of objects to write to the given file stream -nSize = size of a object in bytes -nCount = number of objects to write -f = stream to write characters to -pnErrorCode = reference to a PAL_FILE's fwrite error code field - -Return value: - Returns the number of objects written. ---*/ -size_t -CorUnix::InternalFwrite( - const void *pvBuffer, - size_t nSize, - size_t nCount, - FILE *f, - INT *pnErrorCode - ) -{ - size_t nWrittenBytes = 0; - _ASSERTE(f != NULL); - -#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL - clearerr(f); -#endif - - nWrittenBytes = fwrite(pvBuffer, nSize, nCount, f); - - // Make sure no error occurred. - if ( nWrittenBytes < nCount ) - { - // Set the FILE* error code - *pnErrorCode = PAL_FILE_ERROR; - } - - return nWrittenBytes; -} - - -/*++ -PAL_fseek - -Wrapper function for fseek. - -Input parameters: - -pf = a given file stream -lOffset = distance from position to set file-position indicator -nWhence = method used to determine the file_position indicator location relative to lOffset - -Return value: - 0 on success, -1 on failure. ---*/ -int -_cdecl -PAL_fseek( - PAL_FILE * pf, - LONG lOffset, - int nWhence - ) -{ - int nRet = 0; - - PERF_ENTRY(fseek); - ENTRY( "fseek( %p, %ld, %d )\n", pf, lOffset, nWhence ); - - nRet = fseek(pf ? pf->bsdFilePtr : NULL, lOffset, nWhence); - - LOGEXIT("fseek returning %d\n", nRet); - PERF_EXIT(fseek); - return nRet; -} diff --git a/src/coreclr/pal/src/cruntime/misc.cpp b/src/coreclr/pal/src/cruntime/misc.cpp index 0820be8c2579e6..d079cd0abc4d6d 100644 --- a/src/coreclr/pal/src/cruntime/misc.cpp +++ b/src/coreclr/pal/src/cruntime/misc.cpp @@ -108,28 +108,30 @@ __iscsym( int c ) PERF_EXIT(__iscsym); return 0; } - - /*++ -Function : +PAL forwarders for standard macro headers. - PAL_errno +--*/ +PALIMPORT DLLEXPORT int * __cdecl PAL_errno() +{ + return &errno; +} - Returns the address of the errno. +extern "C" PALIMPORT DLLEXPORT FILE* __cdecl PAL_stdout() +{ + return stdout; +} ---*/ -int * __cdecl PAL_errno( int caller ) +extern "C" PALIMPORT DLLEXPORT FILE* __cdecl PAL_stdin() { - int *retval; - PERF_ENTRY(errno); - ENTRY( "PAL_errno( void )\n" ); - retval = (INT*)(&errno); - LOGEXIT("PAL_errno returns %p\n",retval); - PERF_EXIT(errno); - return retval; + return stdin; } +extern "C" PALIMPORT DLLEXPORT FILE* __cdecl PAL_stderr() +{ + return stderr; +} /*++ Function: diff --git a/src/coreclr/pal/src/cruntime/printfcpp.cpp b/src/coreclr/pal/src/cruntime/printfcpp.cpp deleted file mode 100644 index b9c1c92b9bc8d0..00000000000000 --- a/src/coreclr/pal/src/cruntime/printfcpp.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*++ - - - -Module Name: - - printfcpp.cpp - -Abstract: - - Implementation of suspension safe printf functions. - -Revision History: - - - ---*/ - -#include "pal/corunix.hpp" -#include "pal/thread.hpp" -#include "pal/malloc.hpp" -#include "pal/file.hpp" -#include "pal/printfcpp.hpp" -#include "pal/palinternal.h" -#include "pal/dbgmsg.h" -#include "pal/cruntime.h" - -#include - -SET_DEFAULT_DEBUG_CHANNEL(CRT); - -extern "C" -{ - -// Forward declare functions that are in header files we can't include yet -int vfprintf(FILE* stream, const char* format, va_list ap); - -/*++ -Function: - PAL_fprintf - -See MSDN doc. ---*/ -int -__cdecl -PAL_fprintf(PAL_FILE *stream,const char *format,...) -{ - LONG Length = 0; - va_list ap; - - PERF_ENTRY(fprintf); - ENTRY("PAL_fprintf(stream=%p,format=%p (%s))\n",stream, format, format); - - va_start(ap, format); - Length = vfprintf(stream->bsdFilePtr, format, ap); - va_end(ap); - - LOGEXIT("PAL_fprintf returns int %d\n", Length); - PERF_EXIT(fprintf); - return Length; -} - -/******************************************************************************* -Function: - PAL_vfprintf - -Parameters: - stream - - out stream - Format - - format string - ap - - stdarg parameter list -*******************************************************************************/ - -int __cdecl PAL_vfprintf(PAL_FILE *stream, const char *format, va_list ap) -{ - return vfprintf(stream->bsdFilePtr, format, ap); -} - -} // end extern "C" diff --git a/src/coreclr/pal/src/cruntime/wchar.cpp b/src/coreclr/pal/src/cruntime/wchar.cpp index 8635b303d5783e..d5704ef0ca9d82 100644 --- a/src/coreclr/pal/src/cruntime/wchar.cpp +++ b/src/coreclr/pal/src/cruntime/wchar.cpp @@ -944,3 +944,51 @@ PAL_wcstod( const wchar_16 * nptr, wchar_16 **endptr ) PERF_EXIT(wcstod); return RetVal; } + +/*++ +Function: + _wfopen + +see MSDN doc. + +--*/ +extern "C" +FILE * +__cdecl +_wfopen( + const wchar_16 *fileName, + const wchar_16 *mode) +{ + CHAR mbFileName[ _MAX_PATH ]; + CHAR mbMode[ 10 ]; + FILE * filePtr = NULL; + + PERF_ENTRY(_wfopen); + ENTRY("_wfopen(fileName:%p (%S), mode:%p (%S))\n", fileName, fileName, mode, mode); + + _ASSERTE(fileName != NULL); + _ASSERTE(mode != NULL); + + /* Convert the parameters to ASCII and defer to PAL_fopen */ + if ( WideCharToMultiByte( CP_ACP, 0, fileName, -1, mbFileName, + sizeof mbFileName, NULL, NULL ) != 0 ) + { + if ( WideCharToMultiByte( CP_ACP, 0, mode, -1, mbMode, + sizeof mbMode, NULL, NULL ) != 0 ) + { + filePtr = fopen(mbFileName, mbMode); + } + else + { + ERROR( "An error occurred while converting mode to ANSI.\n" ); + } + } + else + { + ERROR( "An error occurred while converting" + " fileName to ANSI string.\n" ); + } + LOGEXIT("_wfopen returning FILE* %p\n", filePtr); + PERF_EXIT(_wfopen); + return filePtr; +} diff --git a/src/coreclr/pal/src/file/file.cpp b/src/coreclr/pal/src/file/file.cpp index a15be4dba7418f..8139f87c5d8613 100644 --- a/src/coreclr/pal/src/file/file.cpp +++ b/src/coreclr/pal/src/file/file.cpp @@ -1529,6 +1529,52 @@ SetFileAttributesW( return bRet; } +/*++ +InternalOpen + +Wrapper for open. + +Input parameters: + +szPath = pointer to a pathname of a file to be opened +nFlags = arguments that control how the file should be accessed +mode = file permission settings that are used only when a file is created + +Return value: + File descriptor on success, -1 on failure +--*/ +int +CorUnix::InternalOpen( + const char *szPath, + int nFlags, + ... + ) +{ + int nRet = -1; + int mode = 0; + va_list ap; + + // If nFlags does not contain O_CREAT, the mode parameter will be ignored. + if (nFlags & O_CREAT) + { + va_start(ap, nFlags); + mode = va_arg(ap, int); + va_end(ap); + } + + do + { +#if OPEN64_IS_USED_INSTEAD_OF_OPEN + nRet = open64(szPath, nFlags, mode); +#else + nRet = open(szPath, nFlags, mode); +#endif + } + while ((nRet == -1) && (errno == EINTR)); + + return nRet; +} + PAL_ERROR CorUnix::InternalWriteFile( CPalThread *pThread, @@ -3547,42 +3593,3 @@ BOOL FILEInitStdHandles(void) pStdErr = INVALID_HANDLE_VALUE; return FALSE; } - -/*++ -FILECleanupStdHandles - -Remove all regions, locked by a file pointer, from shared memory - -(no parameters) - ---*/ -void FILECleanupStdHandles(void) -{ - HANDLE stdin_handle; - HANDLE stdout_handle; - HANDLE stderr_handle; - - TRACE("closing standard handles\n"); - stdin_handle = pStdIn; - stdout_handle = pStdOut; - stderr_handle = pStdErr; - - pStdIn = INVALID_HANDLE_VALUE; - pStdOut = INVALID_HANDLE_VALUE; - pStdErr = INVALID_HANDLE_VALUE; - - if (stdin_handle != INVALID_HANDLE_VALUE) - { - CloseHandle(stdin_handle); - } - - if (stdout_handle != INVALID_HANDLE_VALUE) - { - CloseHandle(stdout_handle); - } - - if (stderr_handle != INVALID_HANDLE_VALUE) - { - CloseHandle(stderr_handle); - } -} diff --git a/src/coreclr/pal/src/include/pal/cruntime.h b/src/coreclr/pal/src/include/pal/cruntime.h index a2ce2789c30631..b09559565fd153 100644 --- a/src/coreclr/pal/src/include/pal/cruntime.h +++ b/src/coreclr/pal/src/include/pal/cruntime.h @@ -27,121 +27,6 @@ Module Name: #ifdef __cplusplus typedef char16_t wchar_16; // __wchar_16 (which is defined in palinternal.h) is defined as wchar_16_cpp. -extern "C" -{ -#endif // __cplusplus - -typedef enum -{ - PFF_NONE = 0, - PFF_MINUS = 1, - PFF_POUND = 2, - PFF_ZERO = 4, - PFF_SPACE = 8, - PFF_PLUS = 16 -}PRINTF_FORMAT_FLAGS; - -typedef enum -{ - WIDTH_DEFAULT = -1, - WIDTH_STAR = -2, /* e.g. "%*.10s" */ - WIDTH_INVALID = -3 /* e.g. "%*3.10s" */ -}WIDTH_FLAGS; - -typedef enum -{ - PRECISION_DEFAULT = -1, - PRECISION_STAR = -2, /* e.g. "%10.*s" */ - PRECISION_DOT = -3, /* e.g. "%10.s" */ - PRECISION_INVALID = -4 /* e.g. "%10.*3s" */ -}PRECISION_FLAGS; - -typedef enum -{ - PFF_PREFIX_DEFAULT = -1, - PFF_PREFIX_SHORT = 1, - PFF_PREFIX_LONG = 2, - PFF_PREFIX_LONGLONG = 3, - PFF_PREFIX_LONG_W = 4 -}PRINTF_PREFIXES; - -typedef enum -{ - PFF_TYPE_DEFAULT = -1, - PFF_TYPE_CHAR = 1, - PFF_TYPE_STRING = 2, - PFF_TYPE_WSTRING = 3, - PFF_TYPE_INT = 4, - PFF_TYPE_P = 5, - PFF_TYPE_N = 6, - PFF_TYPE_FLOAT = 7 -}PRINTF_TYPES; - -typedef enum -{ - SCANF_PREFIX_SHORT = 1, - SCANF_PREFIX_LONG = 2, - SCANF_PREFIX_LONGLONG = 3 -}SCANF_PREFIXES; - -typedef enum -{ - SCANF_TYPE_CHAR = 1, - SCANF_TYPE_STRING = 2, - SCANF_TYPE_INT = 3, - SCANF_TYPE_N = 4, - SCANF_TYPE_FLOAT = 5, - SCANF_TYPE_BRACKETS = 6, - SCANF_TYPE_SPACE = 7 -}SCANF_TYPES; - -/*++ - -struct PAL_FILE. -Used to mimic the behavior of windows. -fwrite under windows can set the ferror flag, -under BSD fwrite doesn't. ---*/ -struct _FILE -{ - FILE * bsdFilePtr; /* The BSD file to be passed to the - functions needing it. */ - - INT PALferrorCode; /* The ferror code that fwrite sets, - incase of error */ - - BOOL bTextMode; /* Boolean variable to denote that the - fle is opened in text/binary mode*/ -#if UNGETC_NOT_RETURN_EOF - BOOL bWriteOnlyMode;/* Boolean variable to denote that the - fle is opened in write-only mode*/ -#endif //UNGETC_NOT_RETURN_EOF -}; - -enum CRT_ERROR_CODES -{ - PAL_FILE_NOERROR = 0, - PAL_FILE_ERROR -}; - -/* Global variables storing the std streams. Defined in cruntime/file.c. */ -extern PAL_FILE PAL_Stdout; -extern PAL_FILE PAL_Stdin; -extern PAL_FILE PAL_Stderr; - -/*++ - -Functio: - - CRTInitStdStreams. - - Initializes the standard streams. - Returns TRUE on success, FALSE otherwise. ---*/ -BOOL CRTInitStdStreams( void ); - -#ifdef __cplusplus -} #endif // __cplusplus #endif /* _PAL_CRUNTIME_H_ */ diff --git a/src/coreclr/pal/src/include/pal/file.h b/src/coreclr/pal/src/include/pal/file.h index 52054306cb3a64..9b6e3195364043 100644 --- a/src/coreclr/pal/src/include/pal/file.h +++ b/src/coreclr/pal/src/include/pal/file.h @@ -101,15 +101,6 @@ Return value: --*/ BOOL FILEInitStdHandles(void); -/*++ -FILECleanupStdHandles - -Close promary handles for stdin, stdout and stderr - -(no parameters, no return value) ---*/ -void FILECleanupStdHandles(void); - /*++ Function : @@ -123,86 +114,6 @@ Windows behavoir. */ void FILEGetProperNotFoundError( LPCSTR lpPath, LPDWORD lpErrorCode ); -/*++ -PAL_fflush - -Calls fflush - -Input parameters: - -PAL_FILE *stream = stream to be flushed. - -Return value: - 0 is returned on success, otherwise EOF is returned. ---*/ -int _cdecl PAL_fflush( PAL_FILE *stream ); - -/*++ -PAL_fgets - -Wrapper function for InternalFgets. - -Input parameters: - -sz = stores characters read from the given file stream -nSize = number of characters to be read -pf = stream to read characters from - -Return value: - Returns a pointer to the string storing the characters on success - and NULL on failure. ---*/ -char * __cdecl PAL_fgets(char *sz, int nSize, PAL_FILE *pf); - -/*++ -PAL_fwrite - -Wrapper function for InternalFwrite - -Input parameters: - -pvBuffer = array of objects to write to the given file stream -nSize = size of a object in bytes -nCount = number of objects to write -pf = stream to write characters to - -Return value: - Returns the number of objects written. ---*/ -size_t __cdecl PAL_fwrite(const void *pvBuffer, size_t nSize, size_t nCount, PAL_FILE *pf); - -/*++ -PAL__open - -Wrapper function for InternalOpen. - -Input parameters: - -szPath = pointer to a pathname of a file to be opened -nFlags = arguments that control how the file should be accessed -mode = file permission settings that are used only when a file is created - -Return value: - File descriptor on success, -1 on failure ---*/ -int __cdecl PAL__open(const char *szPath, int nFlags, ...); - -/*++ -PAL_fseek - -Wrapper function for fseek - -Input parameters: - -pf = a given file stream -lOffset = distance from position to set file-position indicator -nWhence = method used to determine the file_position indicator location relative to lOffset - -Return value: - 0 on success, -1 on failure. ---*/ -int _cdecl PAL_fseek(PAL_FILE *pf, LONG lOffset, int nWhence); - #ifdef __cplusplus } #endif // __cplusplus diff --git a/src/coreclr/pal/src/include/pal/file.hpp b/src/coreclr/pal/src/include/pal/file.hpp index c35aa5cbeb86d6..9f162b70ff4934 100644 --- a/src/coreclr/pal/src/include/pal/file.hpp +++ b/src/coreclr/pal/src/include/pal/file.hpp @@ -126,31 +126,6 @@ namespace CorUnix PathCharString& lpBuffer ); - /*++ - InternalFgets - Wraps fgets - --*/ - char * - InternalFgets( - char *sz, - int nSize, - FILE *f, - bool fTextMode - ); - - /*++ - InternalFwrite - Wraps fwrite - --*/ - size_t - InternalFwrite( - const void *pvBuffer, - size_t nSize, - size_t nCount, - FILE *f, - INT *pnErrorCode - ); - /*++ InternalOpen Wraps open @@ -224,15 +199,6 @@ Return value: --*/ BOOL FILEInitStdHandles(void); -/*++ -FILECleanupStdHandles - -Close primary handles for stdin, stdout and stderr - -(no parameters, no return value) ---*/ -void FILECleanupStdHandles(void); - /*++ Function : diff --git a/src/coreclr/pal/src/include/pal/palinternal.h b/src/coreclr/pal/src/include/pal/palinternal.h index 0b2bb01d7b3ec8..041118d3916514 100644 --- a/src/coreclr/pal/src/include/pal/palinternal.h +++ b/src/coreclr/pal/src/include/pal/palinternal.h @@ -259,6 +259,21 @@ function_name() to call the system's implementation #define remove DUMMY_remove #define printf DUMMY_printf #define vprintf DUMMY_vprintf +#define fopen DUMMY_fopen +#define setvbuf DUMMY_setvbuf +#define fprintf DUMMY_fprintf +#define vfprintf DUMMY_vfprintf +#define fgets DUMMY_fgets +#define ferror DUMMY_ferror +#define fread DUMMY_fread +#define fwrite DUMMY_fwrite +#define ftell DUMMY_ftell +#define fclose DUMMY_fclose +#define fflush DUMMY_fflush +#define fputs DUMMY_fputs +#define fseek DUMMY_fseek +#define fgetpos DUMMY_fgetpos +#define fsetpos DUMMY_fsetpos /* RAND_MAX needed to be renamed to avoid duplicate definition when including stdlib.h header files. PAL_RAND_MAX should have the same value as RAND_MAX @@ -353,7 +368,8 @@ function_name() to call the system's implementation #define intptr_t PAL_intptr_t #define uintptr_t PAL_uintptr_t #define timeval PAL_timeval -#define FILE PAL_FILE + +#define DEFINE_DUMMY_FILE_TYPE #include "pal.h" #include "palprivate.h" @@ -433,7 +449,6 @@ function_name() to call the system's implementation #undef qsort #undef bsearch #undef time -#undef FILE #undef fclose #undef fopen #undef fread @@ -447,7 +462,6 @@ function_name() to call the system's implementation #undef fgetpos #undef fsetpos #undef getcwd -#undef _flushall #undef setvbuf #undef unlink #undef size_t @@ -619,6 +633,7 @@ function_name() to call the system's implementation #include #include #include +#include #ifdef __APPLE__ diff --git a/src/coreclr/pal/src/include/pal/printfcpp.hpp b/src/coreclr/pal/src/include/pal/printfcpp.hpp deleted file mode 100644 index 44526cb8a4eaae..00000000000000 --- a/src/coreclr/pal/src/include/pal/printfcpp.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*++ - - - -Module Name: - - pal/printfcpp.hpp - -Abstract: - Declarations for suspension safe memory allocation functions - - - ---*/ - -#ifndef _PRINTFCPP_HPP -#define _PRINTFCPP_HPP - -#ifdef __cplusplus -#include "pal/threadinfo.hpp" -#endif - -#include - -#ifdef __cplusplus -typedef char16_t wchar_16; // __wchar_16_cpp (which is defined in palinternal.h) needs to be redefined to wchar_16. -extern "C" -{ -#endif // __cplusplus - - int - __cdecl - PAL_vfprintf( - PAL_FILE *stream, - const char *format, - va_list ap); - -#ifdef __cplusplus -} -#endif // __cplusplus - -#endif // _PRINTFCPP_HPP - diff --git a/src/coreclr/pal/src/include/pal/threadsusp.hpp b/src/coreclr/pal/src/include/pal/threadsusp.hpp index c3e59df89d7995..d9441372a5a274 100644 --- a/src/coreclr/pal/src/include/pal/threadsusp.hpp +++ b/src/coreclr/pal/src/include/pal/threadsusp.hpp @@ -32,7 +32,6 @@ Module Name: // instantiation time. #include "pal/threadinfo.hpp" #include "pal/thread.hpp" -#include "pal/printfcpp.hpp" #include "pal/mutex.hpp" #include "pal/init.h" #if !HAVE_MACH_EXCEPTIONS diff --git a/src/coreclr/pal/src/init/pal.cpp b/src/coreclr/pal/src/init/pal.cpp index 606bd5311522cd..9d0c82ac4ae53f 100644 --- a/src/coreclr/pal/src/init/pal.cpp +++ b/src/coreclr/pal/src/init/pal.cpp @@ -96,13 +96,6 @@ int CacheLineSize; using namespace CorUnix; -// -// $$TODO The C++ compiler doesn't like pal/cruntime.h so duplicate the -// necessary prototype here -// - -extern "C" BOOL CRTInitStdStreams(void); - extern bool g_running_in_exe; #if defined(HOST_ARM64) @@ -673,13 +666,6 @@ Initialize( } } - if (FALSE == CRTInitStdStreams()) - { - ERROR("Unable to initialize CRT standard streams\n"); - palError = ERROR_PALINIT_STD_STREAMS; - goto CLEANUP15; - } - TRACE("First-time PAL initialization complete.\n"); init_count++; @@ -699,9 +685,6 @@ Initialize( } goto done; - /* No cleanup required for CRTInitStdStreams */ -CLEANUP15: - FILECleanupStdHandles(); CLEANUP14: SEHCleanup(); CLEANUP13: diff --git a/src/coreclr/pal/src/locale/unicode.cpp b/src/coreclr/pal/src/locale/unicode.cpp index 8bfa58608e5942..862f98a6b21279 100644 --- a/src/coreclr/pal/src/locale/unicode.cpp +++ b/src/coreclr/pal/src/locale/unicode.cpp @@ -109,9 +109,9 @@ BOOL GetUnicodeData(INT nUnicodeValue, UnicodeDataRec *pDataRec) return bRet; } -wchar_16 +char16_t __cdecl -PAL_ToUpperInvariant( wchar_16 c ) +PAL_ToUpperInvariant( char16_t c ) { UnicodeDataRec dataRec; @@ -140,9 +140,9 @@ PAL_ToUpperInvariant( wchar_16 c ) } } -wchar_16 +char16_t __cdecl -PAL_ToLowerInvariant( wchar_16 c ) +PAL_ToLowerInvariant( char16_t c ) { UnicodeDataRec dataRec; diff --git a/src/coreclr/pal/src/misc/fmtmessage.cpp b/src/coreclr/pal/src/misc/fmtmessage.cpp index bd9a649a35835b..c7de98718c1d35 100644 --- a/src/coreclr/pal/src/misc/fmtmessage.cpp +++ b/src/coreclr/pal/src/misc/fmtmessage.cpp @@ -25,8 +25,6 @@ Revision History: #include "pal/module.h" #include "pal/misc.h" -#include "pal/printfcpp.hpp" - #include "errorstrings.h" #include diff --git a/src/coreclr/pal/src/misc/perftrace.cpp b/src/coreclr/pal/src/misc/perftrace.cpp index 5dd0d38209d164..a0d52a415017bf 100644 --- a/src/coreclr/pal/src/misc/perftrace.cpp +++ b/src/coreclr/pal/src/misc/perftrace.cpp @@ -92,12 +92,9 @@ typedef struct _pal_perf_program_info char start_time[32]; /* must be at least 26 characters */ } pal_perf_program_info; -typedef PAL_FILE PERF_FILE; -#define PERF_FILEFN(x) PAL_ ## x - static ULONGLONG PERFGetTicks(); static double PERFComputeStandardDeviation(pal_perf_api_info *api); -static void PERFPrintProgramHeaderInfo(PERF_FILE * hFile, BOOL completedExecution); +static void PERFPrintProgramHeaderInfo(FILE * hFile, BOOL completedExecution); static BOOL PERFInitProgramInfo(LPWSTR command_line, LPWSTR exe_path); static BOOL PERFReadSetting( ); static void PERFLogFileName(PathCharString * destFileString, const char *fileName, const char *suffix, int max_length); @@ -212,7 +209,7 @@ PERFComputeStandardDeviation(pal_perf_api_info *api) static void -PERFPrintProgramHeaderInfo(PERF_FILE * hFile, BOOL completedExecution) +PERFPrintProgramHeaderInfo(FILE * hFile, BOOL completedExecution) { ULONGLONG etime = 0; ULONGLONG ttime = 0; @@ -222,11 +219,11 @@ PERFPrintProgramHeaderInfo(PERF_FILE * hFile, BOOL completedExecution) ttime = program_info.total_duration; ptime = program_info.pal_duration; } - PERF_FILEFN(fprintf)(hFile,"#LOG\tversion=1.00\n"); + fprintf(hFile,"#LOG\tversion=1.00\n"); - PERF_FILEFN(fprintf)(hFile, "#MACHINE\thostname=%s\tcpu_clock_frequency=%g\n", program_info.hostname, + fprintf(hFile, "#MACHINE\thostname=%s\tcpu_clock_frequency=%g\n", program_info.hostname, program_info.cpu_clock_frequency); - PERF_FILEFN(fprintf)(hFile, "#PROCESS\tprocess_id=%d\ttotal_latency=" LLFORMAT "\tthread_times=" LLFORMAT "\tpal_time=" LLFORMAT "\texe_path=%s\tcommand_line=%s\tstart_time=%s", + fprintf(hFile, "#PROCESS\tprocess_id=%d\ttotal_latency=" LLFORMAT "\tthread_times=" LLFORMAT "\tpal_time=" LLFORMAT "\texe_path=%s\tcommand_line=%s\tstart_time=%s", program_info.process_id, etime, ttime, ptime, program_info.exe_path,program_info.command_line,program_info.start_time); } @@ -619,11 +616,11 @@ PERFWriteCounters( pal_perf_api_info * table ) off = table; PERFLogFileName(fileName, profile_summary_log_name, "_perf_summary.log"); - hFile = PERF_FILEFN(fopen)(fileName, "a+"); + hFile = fopen(fileName, "a+"); if(hFile != NULL) { PERFPrintProgramHeaderInfo(hFile, TRUE); - PERF_FILEFN(fprintf)(hFile,"#api_name\tapi_id\tperf_entries\tperf_exits\tsum_of_latency\tmin_latency\tmax_latency\tstd_dev_latency\tsum_of_square_latency\n"); + fprintf(hFile,"#api_name\tapi_id\tperf_entries\tperf_exits\tsum_of_latency\tmin_latency\tmax_latency\tstd_dev_latency\tsum_of_square_latency\n"); for(i=0;icounter > 0 || !report_only_called_apis) { - PERF_FILEFN(fprintf)(hFile,"%s\t%d\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t%g\t%g\n", + fprintf(hFile,"%s\t%d\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t" LLFORMAT "\t%g\t%g\n", API_list[i], i, off->entries, off->counter,off->sum_duration, min_duration, off->max_duration, dev, off->sum_of_square_duration); } @@ -653,36 +650,36 @@ PERFWriteCounters( pal_perf_api_info * table ) { return -1; } - PERF_FILEFN(fclose)(hFile); + fclose(hFile); if (pal_perf_histogram_size > 0) { off = table; PERFLogFileName(fileName, profile_summary_log_name, "_perf_summary.hist"); - hFile = PERF_FILEFN(fopen)(fileName, "a+"); + hFile = fopen(fileName, "a+"); if (hFile != NULL) { DWORD j; - PERF_FILEFN(fprintf)(hFile,"#api_name\tapi_id"); + fprintf(hFile,"#api_name\tapi_id"); for (j = 0; j < pal_perf_histogram_size; j++) { - PERF_FILEFN(fprintf)(hFile, "\t%d", j*pal_perf_histogram_step); + fprintf(hFile, "\t%d", j*pal_perf_histogram_step); } - PERF_FILEFN(fprintf)(hFile, "\n"); + fprintf(hFile, "\n"); for(i = 0; i < PAL_API_NUMBER; i++) { if (off->counter > 0) { - PERF_FILEFN(fprintf)(hFile,"%s\t%d", API_list[i], i); + fprintf(hFile,"%s\t%d", API_list[i], i); for (j = 0; j < pal_perf_histogram_size; j++) { - PERF_FILEFN(fprintf)(hFile, "\t%d", off->histograms[j]); + fprintf(hFile, "\t%d", off->histograms[j]); } - PERF_FILEFN(fprintf)(hFile, "\n"); + fprintf(hFile, "\n"); } off++; @@ -692,7 +689,7 @@ PERFWriteCounters( pal_perf_api_info * table ) { return -1; } - PERF_FILEFN(fclose)(hFile); + fclose(hFile); } return 0; @@ -993,7 +990,7 @@ PERFFlushLog(pal_perf_thread_info * local_info, BOOL output_header) PERFLogFileName(fileName, profile_time_log_name, "_perf_time.log"); - hFile = PERF_FILEFN(fopen)(fileName, "a+"); + hFile = fopen(fileName, "a+"); if(hFile) { @@ -1003,7 +1000,7 @@ PERFFlushLog(pal_perf_thread_info * local_info, BOOL output_header) } if (local_info->buf_offset > 0) { - nWrittenBytes = PERF_FILEFN(fwrite)(local_info->pal_write_buf, local_info->buf_offset, 1, hFile); + nWrittenBytes = fwrite(local_info->pal_write_buf, local_info->buf_offset, 1, hFile); if (nWrittenBytes < 1) { ERROR("fwrite() failed with errno == %d\n", errno); @@ -1011,7 +1008,7 @@ PERFFlushLog(pal_perf_thread_info * local_info, BOOL output_header) } local_info->buf_offset = 0; } - PERF_FILEFN(fclose)(hFile); + fclose(hFile); ret = TRUE; } diff --git a/src/coreclr/pal/tests/palsuite/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/CMakeLists.txt index 09bfdb0e173dbb..4b9960f910c5c1 100644 --- a/src/coreclr/pal/tests/palsuite/CMakeLists.txt +++ b/src/coreclr/pal/tests/palsuite/CMakeLists.txt @@ -67,32 +67,7 @@ add_executable_clr(paltests c_runtime/atoi/test1/test1.cpp c_runtime/bsearch/test1/test1.cpp c_runtime/bsearch/test2/test2.cpp - c_runtime/errno/test1/test1.cpp - c_runtime/errno/test2/test2.cpp - c_runtime/exit/test1/test1.cpp - c_runtime/exit/test2/test2.cpp - c_runtime/ferror/test1/test1.cpp - c_runtime/ferror/test2/test2.cpp - c_runtime/fflush/test1/test1.cpp - c_runtime/fgets/test1/test1.cpp - c_runtime/fgets/test2/test2.cpp - c_runtime/fgets/test3/test3.cpp - c_runtime/fopen/test1/test1.cpp - c_runtime/fopen/test2/test2.cpp - c_runtime/fopen/test3/test3.cpp - c_runtime/fopen/test4/test4.cpp - c_runtime/fopen/test5/test5.cpp - c_runtime/fopen/test6/test6.cpp - c_runtime/fopen/test7/test7.cpp - c_runtime/fputs/test1/test1.cpp - c_runtime/fputs/test2/test2.cpp - c_runtime/fread/test1/test1.cpp - c_runtime/fread/test2/test2.cpp - c_runtime/fread/test3/test3.cpp c_runtime/free/test1/test1.cpp - c_runtime/fseek/test1/test1.cpp - c_runtime/ftell/test1/ftell.cpp - c_runtime/fwrite/test1/test1.cpp c_runtime/isalnum/test1/test1.cpp c_runtime/isalpha/test1/test1.cpp c_runtime/isdigit/test1/test1.cpp @@ -453,11 +428,7 @@ add_executable_clr(paltests miscellaneous/SetEnvironmentVariableW/test4/test4.cpp miscellaneous/SetLastError/test1/test.cpp miscellaneous/_i64tow/test1/test1.cpp - pal_specific/PAL_errno/test1/PAL_errno.cpp # pal_specific/PAL_GetUserTempDirectoryW/test1/PAL_GetUserTempDirectoryW.cpp - #pal_specific/PAL_get_stderr/test1/PAL_get_stderr.cpp - #pal_specific/PAL_get_stdin/test1/PAL_get_stdin.cpp - #pal_specific/PAL_get_stdout/test1/PAL_get_stdout.cpp pal_specific/PAL_Initialize_Terminate/test1/PAL_Initialize_Terminate.cpp pal_specific/PAL_Initialize_Terminate/test2/pal_initialize_twice.cpp # pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test1/PAL_RegisterLibraryW_UnregisterLibraryW.cpp diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/errno/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/errno/test1/test1.cpp deleted file mode 100644 index 7daab481ef10cf..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/errno/test1/test1.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: test1.c -** -** Purpose: Test that errno begins as 0, and sets to ERANGE when that -** error is forced with wcstoul. -** -** -**==========================================================================*/ - -#include - -PALTEST(c_runtime_errno_test1_paltest_errno_test1, "c_runtime/errno/test1/paltest_errno_test1") -{ - WCHAR overstr[] = {'4','2','9','4','9','6','7','2','9','6',0}; - WCHAR *end; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - /* - The only value that must be supported is - ERANGE, in the event that wcstoul() fails due to overflow. - */ - - wcstoul(overstr, &end, 10); - - if (errno != ERANGE) - { - Fail("ERROR: wcstoul did not set errno to ERANGE. Instead " - "the value of errno is %d\n", errno); - } - - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/errno/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/errno/test2/test2.cpp deleted file mode 100644 index 7a0e27007f7186..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/errno/test2/test2.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: test2.c -** -** Purpose: Test that errno is 'per-thread' as noted in the documentation. -** -** -**==========================================================================*/ - -#include - -/* - This thread function just checks that errno is initially 0 and then sets - it to a new value before returning. -*/ -DWORD PALAPI ThreadFunc_errno_test2( LPVOID lpParam ) -{ - - if(errno != 0) - { - *((DWORD*)lpParam) = 1; - } - - errno = 20; - - return 0; -} - - -PALTEST(c_runtime_errno_test2_paltest_errno_test2, "c_runtime/errno/test2/paltest_errno_test2") -{ - DWORD dwThreadId, dwThrdParam = 0; - HANDLE hThread; - - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - /* Set errno to a value within this thread */ - - errno = 50; - - hThread = CreateThread(NULL, 0, ThreadFunc_errno_test2, &dwThrdParam, 0, &dwThreadId); - - if (hThread == NULL) - { - Fail("ERROR: CreateThread failed to create a thread. " - "GetLastError() returned %d.\n",GetLastError()); - } - - WaitForSingleObject(hThread, INFINITE); - - /* This checks the result of calling the thread */ - if(dwThrdParam) - { - Fail("ERROR: errno was not set to 0 in the new thread. Each " - "thread should have its own value for errno.\n"); - } - - /* Check to make sure errno is still set to 50 */ - if(errno != 50) - { - Fail("ERROR: errno should be 50 in the main thread, even though " - "it was set to 20 in another thread. Currently it is %d.\n", - errno); - } - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test1/test1.cpp deleted file mode 100644 index 5863d51d0c091a..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test1/test1.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test1.c -** -** Purpose: Tests the PAL implementation of the ferror function. -** -** Depends: -** fopen -** fread -** fclose -** -** -** -**===================================================================*/ - -#include - -PALTEST(c_runtime_ferror_test1_paltest_ferror_test1, "c_runtime/ferror/test1/paltest_ferror_test1") -{ - const char filename[] = "testfile"; - char buffer[128]; - FILE * fp = NULL; - int result; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - /* Open a file in READ mode */ - - if((fp = fopen(filename, "r")) == NULL) - { - Fail("Unable to open a file for reading. Is the file " - "in the directory? It should be."); - } - - /* Read 10 characters from the file. The file has 15 - characters in it. - */ - - if((result = fread(buffer,1,10,fp)) == 0) - { - Fail("ERROR: Zero characters read from the file. It should have " - "read 10 character in from a 15 character file."); - } - - if(ferror(fp) != 0) - { - Fail("ERROR: ferror returned a value not equal to 0. The read " - "operation shouldn't have caused an error, and ferror should " - "return 0 still."); - } - - /* - Close the open file and end the test. - */ - - if(fclose(fp) != 0) - { - Fail("ERROR: fclose failed when trying to close a file pointer. " - "This test depends on fclose working properly."); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test1/testfile b/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test1/testfile deleted file mode 100644 index 273c1a9ffdc201..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test1/testfile +++ /dev/null @@ -1 +0,0 @@ -This is a test. \ No newline at end of file diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test2/test2.cpp deleted file mode 100644 index 3ac444b0ec35c6..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test2/test2.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test2.c -** -** Purpose: Open a read-only file and attempt to write some data to it. -** Check to ensure that an ferror occurs. -** -** Depends: -** fopen -** fwrite -** fclose -** -** -** -**===================================================================*/ - -#include - -PALTEST(c_runtime_ferror_test2_paltest_ferror_test2, "c_runtime/ferror/test2/paltest_ferror_test2") -{ - const char filename[] = "testfile"; - FILE * fp = NULL; - int result; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - /* Open a file in READONLY mode */ - - if((fp = fopen(filename, "r")) == NULL) - { - Fail("Unable to open a file for reading."); - } - - /* Attempt to write 14 characters to the file. */ - - if((result = fwrite("This is a test",1,14,fp)) != 0) - { - Fail("ERROR: %d characters written. 0 characters should " - "have been written, since this file is read-only.", result); - } - - if(ferror(fp) == 0) - { - Fail("ERROR: ferror should have generated an error when " - "write was called on a read-only file. But, it " - "retured 0, indicating no error.\n"); - } - - /* Close the file. */ - - if(fclose(fp) != 0) - { - Fail("ERROR: fclose failed when trying to close a file pointer. " - "This test depends on fclose working properly."); - } - - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test2/testfile b/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test2/testfile deleted file mode 100644 index 0135842a03e1ad..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/ferror/test2/testfile +++ /dev/null @@ -1 +0,0 @@ -This is a test file. This needs to be kept in CVS. \ No newline at end of file diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fflush/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fflush/test1/test1.cpp deleted file mode 100644 index 716a151b5f8f77..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fflush/test1/test1.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: test1.c -** -** Purpose: Tests to see that fflush is working properly. Flushes a couple -** buffers and checks the return value. Can't figure out a way to test -** and ensure it is really dropping the buffers, since the system -** does this automatically most of the time ... -** -** -**==========================================================================*/ - -/* This function is really tough to test. Right now it just tests - a bunch of return values. No solid way to ensure that it is really - flushing a buffer or not -- might have to be a manual test someday. -*/ - -#include - - -PALTEST(c_runtime_fflush_test1_paltest_fflush_test1, "c_runtime/fflush/test1/paltest_fflush_test1") -{ - - int TheReturn; - FILE* TheFile; - FILE* AnotherFile = NULL; - - PAL_Initialize(argc,argv); - - TheFile = fopen("theFile","w+"); - - if(TheFile == NULL) - { - Fail("ERROR: fopen failed. Test depends on this function."); - } - - TheReturn = fwrite("foo",3,3,TheFile); - - if(TheReturn != 3) - { - Fail("ERROR: fwrite failed. Test depends on this function."); - } - - /* Test to see that FlushFileBuffers returns a success value */ - TheReturn = fflush(TheFile); - - if(TheReturn != 0) - { - Fail("ERROR: The fflush function returned non-zero, which " - "indicates failure, when trying to flush a buffer."); - } - - /* Test to see that FlushFileBuffers returns a success value */ - TheReturn = fflush(NULL); - - if(TheReturn != 0) - { - Fail("ERROR: The fflush function returned non-zero, which " - "indicates failure, when trying to flush all buffers."); - } - - /* Test to see that FlushFileBuffers returns a success value */ - TheReturn = fflush(AnotherFile); - - if(TheReturn != 0) - { - Fail("ERROR: The fflush function returned non-zero, which " - "indicates failure, when trying to flush a stream not " - "associated with a file."); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fgets/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fgets/test1/test1.cpp deleted file mode 100644 index e16c9e2ad9d079..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fgets/test1/test1.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: test1.c -** -** Purpose: Writes a simple file and calls fgets() to get a string shorter -** than the first line of the file. Verifies that the correct -** string is returned. -** -** -**==========================================================================*/ - -#include - -PALTEST(c_runtime_fgets_test1_paltest_fgets_test1, "c_runtime/fgets/test1/paltest_fgets_test1") -{ - const char outBuf1[] = "This is a test.\n"; - const char outBuf2[] = "This is too."; - char inBuf[sizeof(outBuf1) + sizeof(outBuf2)]; - const char filename[] = "testfile.tmp"; - const int offset = 5; /* value chosen arbitrarily */ - int actualLen; - int expectedLen; - FILE * fp; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - /*write the file that we will use to test */ - fp = fopen(filename, "w"); - if (fp == NULL) - { - Fail("Unable to open file for write.\n"); - } - - fwrite(outBuf1, sizeof(outBuf1[0]), sizeof(outBuf1), fp); - fwrite(outBuf2, sizeof(outBuf2[0]), sizeof(outBuf2), fp); - - if (fclose(fp) != 0) - { - Fail("Error closing a file opened for write.\n"); - } - - - /*now read back the entire first string*/ - fp = fopen(filename, "r"); - if (fp == NULL) - { - Fail("Unable to open file for read.\n"); - } - - /*note: +1 because strlen() returns the length of a string _not_ - including the NULL, while fgets() returns a string of specified - maximum length _including_ the NULL.*/ - if (fgets(inBuf, strlen(outBuf1) - offset + 1, fp) != inBuf) - { - Fail("Error reading from file using fgets.\n"); - } - - - expectedLen = strlen(outBuf1) - offset; - actualLen = strlen(inBuf); - - if (actualLen < expectedLen) - { - Fail("fgets() was asked to read a one-line string and given the " - "length of the string as a parameter. The string it has " - "read is too short.\n"); - } - if (actualLen > expectedLen) - { - Fail("fgets() was asked to read a one-line string and given the " - "length of the string as a parameter. The string it has " - "read is too long.\n"); - } - if (memcmp(inBuf, outBuf1, actualLen) != 0) - { - /*We didn't read back exactly outBuf1*/ - Fail("fgets() was asked to read a one-line string, and given the " - "length of the string as an parameter. It has returned a " - "string of the correct length, but the contents are not " - "correct.\n"); - } - - if (fclose(fp) != 0) - { - Fail("Error closing file after using fgets().\n"); - } - - - PAL_Terminate(); - return PASS; - -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fgets/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fgets/test2/test2.cpp deleted file mode 100644 index 7f21d075817529..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fgets/test2/test2.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: test2.c -** -** Purpose: Calls fgets to read a full line from a file. A maximum length -** parameter greater than the length of the line is passed. -** -** -**==========================================================================*/ - -#include - -PALTEST(c_runtime_fgets_test2_paltest_fgets_test2, "c_runtime/fgets/test2/paltest_fgets_test2") -{ - const char outBuf1[] = "This is a test.\n"; - const char outBuf2[] = "This is too."; - - char inBuf[sizeof(outBuf1) + sizeof(outBuf2)]; - const char filename[] = "testfile.tmp"; - const int offset = 5; /*value chosen arbitrarily*/ - int expectedLen; - int actualLen; - - FILE * fp; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - /*write the file that we will use to test */ - fp = fopen(filename, "w"); - if (fp == NULL) - { - Fail("Unable to open file for write.\n"); - } - - fwrite(outBuf1, sizeof(outBuf1[0]), sizeof(outBuf1), fp); - fwrite(outBuf2, sizeof(outBuf2[0]), sizeof(outBuf2), fp); - - if (fclose(fp) != 0) - { - Fail("error closing stream opened for write.\n"); - } - - /*Read until the first linebreak*/ - fp = fopen(filename, "r"); - if (fp == NULL) - { - Fail("Unable to open file for read.\n"); - } - - - if (fgets(inBuf, sizeof(outBuf1) + offset , fp) != inBuf) - { - Fail("Error reading from file using fgets.\n"); - } - - /*note: -1 because strlen returns the length of a string _not_ - including the NULL, while fgets returns a string of specified - maximum length _including_ the NULL.*/ - expectedLen = strlen(outBuf1); - actualLen = strlen(inBuf); - if (actualLen > expectedLen) - { - Fail("fgets() was asked to read the first line of a file, but did " - "not stop at the end of the line.\n"); - } - else if (actualLen < expectedLen) - { - Fail("fgets() was asked to read the first line of a file, but did " - "not read the entire line.\n"); - } - else if (memcmp(inBuf, outBuf1, actualLen) != 0) - { - /*We didn't read back exactly outBuf1*/ - Fail("fgets() was asked to read the first line of a file. It " - "has read back a string of the correct length, but the" - " contents are not correct.\n"); - } - - if (fclose(fp) != 0) - { - Fail("Error closing file after using fgets().\n"); - } - - PAL_Terminate(); - return PASS; - -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fgets/test3/test3.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fgets/test3/test3.cpp deleted file mode 100644 index f46b179de1ddc8..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fgets/test3/test3.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: test3.c -** -** Purpose: Tries to read from an empty file using fgets(), to verify -** handling of EOF condition. -** -** -**==========================================================================*/ - -#include - -PALTEST(c_runtime_fgets_test3_paltest_fgets_test3, "c_runtime/fgets/test3/paltest_fgets_test3") -{ - char inBuf[10]; - const char filename[] = "testfile.tmp"; - - FILE * fp; - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - /*write the empty file that we will use to test */ - fp = fopen(filename, "w"); - if (fp == NULL) - { - Fail("Unable to open file for write.\n"); - } - - /*Don't write anything*/ - - if (fclose(fp) != 0) - { - Fail("Error closing stream opened for write.\n"); - } - - - /*Open the file and try to read.*/ - fp = fopen(filename, "r"); - if (fp == NULL) - { - Fail("Unable to open file for read.\n"); - } - - - if (fgets(inBuf, sizeof(inBuf) , fp) != NULL) - { - /*NULL could also mean an error condition, but since the PAL - doesn't supply feof or ferror, we can't distinguish between - the two.*/ - Fail("fgets doesn't handle EOF properly. When asked to read from " - "an empty file, it didn't return NULL as it should have.\n"); - } - - if (fclose(fp) != 0) - { - Fail("Error closing an empty file after trying to use fgets().\n"); - } - PAL_Terminate(); - return PASS; - -} - - - - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test1/test1.cpp deleted file mode 100644 index abbc328d594607..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test1/test1.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test1.c -** -** Purpose: Tests the PAL implementation of the fopen function. -** This test simply attempts to open a number of files -** with different modes. It checks to ensure a valid -** file pointer is returned. It doesn't do any checking -** to ensure the mode is really what it claims. And checks -** for a NULL pointer when attempts to open a directory. -** - -** -**===================================================================*/ - -#include - -struct testCase -{ - int CorrectResult; - char mode[20]; -}; - -PALTEST(c_runtime_fopen_test1_paltest_fopen_test1, "c_runtime/fopen/test1/paltest_fopen_test1") -{ - - FILE *fp; - char name[128]; - int i; - - struct testCase testCases[] = - { - {0, "r"}, {1, "w"}, {1, "a"}, - {0, "r+"}, {1, "w+"}, {1, "a+"}, - {1, "wt"}, {1, "wb"}, {1, "wS"}, - {1, "w+c"}, {1, "w+n"}, {1, "wR"}, - {1, "wT"}, {0, "tw"} - }; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - - for(i = 0; i < sizeof(testCases) / sizeof(struct testCase); i++) - { - strcpy(name,"testfiles"); - strcat(name,testCases[i].mode); - - fp = fopen(name,testCases[i].mode); - - if ((fp == 0 && testCases[i].CorrectResult != 0) || - (testCases[i].CorrectResult == 0 && fp != 0) ) - { - Fail("ERROR: fopen returned incorrectly " - "opening a file in %s mode. Perhaps it opened a " - "read only file which didn't exist and returned a correct " - "pointer?",testCases[i].mode); - } - - memset(name, '\0', 128); - - } - - /* When attempt to open a directory fopen should returned NULL */ - if ( fopen(".", "r") != NULL) - { - Fail("ERROR: fopen returned non-NULL when trying to open a directory" - " the returned value was %d\n", fp); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test2/test2.cpp deleted file mode 100644 index b588c41fd23e38..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test2/test2.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test2.c -** -** Purpose: Tests the PAL implementation of the fopen function. -** Test to ensure that you can write to a 'w' mode file. -** And that you can't read from a 'w' mode file. -** -** Depends: -** fprintf -** fseek -** fgets -** - -** -**===================================================================*/ - -#include - -PALTEST(c_runtime_fopen_test2_paltest_fopen_test2, "c_runtime/fopen/test2/paltest_fopen_test2") -{ - - FILE *fp; - char buffer[128]; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - if( (fp = fopen( "testfile", "w" )) == NULL ) - { - Fail( "ERROR: The file failed to open with 'w' mode.\n" ); - } - - /* Test that you can write */ - if(fprintf(fp,"%s","some text") <= 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'w' mode " - "but fprintf failed. Either fopen or fprintf have problems."); - } - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it."); - } - - /* Test that you can't read */ - if(fgets(buffer,10,fp) != NULL) - { - Fail("ERROR: Tried to READ from a file with only 'w' mode set. " - "This should fail, but fgets didn't return NULL. Either " - "fgets or fopen is broken."); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test3/test3.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test3/test3.cpp deleted file mode 100644 index 3be80f16109912..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test3/test3.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test3.c -** -** Purpose: Tests the PAL implementation of the fopen function. -** Test to ensure that you can write to a 'w+' mode file. -** And that you can read from a 'w+' mode file. -** -** Depends: -** fprintf -** fseek -** fgets -** - -** -**===================================================================*/ - -#include - -PALTEST(c_runtime_fopen_test3_paltest_fopen_test3, "c_runtime/fopen/test3/paltest_fopen_test3") -{ - - FILE *fp; - char buffer[128]; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - /* Open a file with 'w+' mode */ - if( (fp = fopen( "testfile", "w+" )) == NULL ) - { - Fail( "ERROR: The file failed to open with 'w+' mode.\n" ); - } - - /* Write some text to the file */ - if(fprintf(fp,"%s","some text") <= 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'w+' mode " - "but fprintf failed. Either fopen or fprintf have problems."); - } - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it."); - } - - /* Attempt to read from the 'w+' only file, should pass */ - if(fgets(buffer,10,fp) == NULL) - { - Fail("ERROR: Tried to READ from a file with 'w+' mode set. " - "This should succeed, but fgets returned NULL. Either fgets " - "or fopen is broken."); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test4/test4.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test4/test4.cpp deleted file mode 100644 index 24f8bfa11e79b7..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test4/test4.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test4.c -** -** Purpose: Tests the PAL implementation of the fopen function. -** Test to ensure that you can't write to a 'r' mode file. -** And that you can read from a 'r' mode file. -** -** Depends: -** fprintf -** fclose -** fgets -** - -** -**===================================================================*/ - -#include - -PALTEST(c_runtime_fopen_test4_paltest_fopen_test4, "c_runtime/fopen/test4/paltest_fopen_test4") -{ - - FILE *fp; - char buffer[128]; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - /* Open a file with 'w' mode */ - if( (fp = fopen( "testfile", "w" )) == NULL ) - { - Fail( "ERROR: The file failed to open with 'w' mode.\n" ); - } - - /* Write some text to the file */ - if(fprintf(fp,"%s","some text") <= 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'w' mode " - "but fprintf failed. Either fopen or fprintf have problems."); - } - - if(fclose(fp)) - { - Fail("ERROR: Attempted to close a file, but fclose failed. " - "This test depends upon it."); - } - - /* Open a file with 'r' mode */ - if( (fp = fopen( "testfile", "r" )) == NULL ) - { - Fail( "ERROR: The file failed to open with 'r' mode.\n" ); - } - - /* Attempt to read from the 'r' only file, should pass */ - if(fgets(buffer,10,fp) == NULL) - { - Fail("ERROR: Tried to READ from a file with 'r' mode set. " - "This should succeed, but fgets returned NULL. Either fgets " - "or fopen is broken."); - } - - /* Write some text to the file */ - if(fprintf(fp,"%s","some text") > 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'r' mode " - "but fprintf succeeded It should have failed. " - "Either fopen or fprintf have problems."); - } - - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test5/test5.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test5/test5.cpp deleted file mode 100644 index 04205aca833529..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test5/test5.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test5.c -** -** Purpose: Tests the PAL implementation of the fopen function. -** Test to ensure that you can write to a 'r+' mode file. -** And that you can read from a 'r+' mode file. -** -** Depends: -** fprintf -** fclose -** fgets -** fseek -** - -** -**===================================================================*/ - -#include - -PALTEST(c_runtime_fopen_test5_paltest_fopen_test5, "c_runtime/fopen/test5/paltest_fopen_test5") -{ - - FILE *fp; - char buffer[128]; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - /* Open a file with 'w' mode */ - if( (fp = fopen( "testfile", "w" )) == NULL ) - { - Fail( "ERROR: The file failed to open with 'w' mode.\n" ); - } - - if(fclose(fp)) - { - Fail("ERROR: Attempted to close a file, but fclose failed. " - "This test depends upon it."); - } - - if( (fp = fopen( "testfile", "r+" )) == NULL ) - { - Fail( "ERROR: The file failed to open with 'r+' mode.\n" ); - } - - /* Write some text to the file */ - if(fprintf(fp,"%s","some text") <= 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'r+' mode " - "but fprintf failed. Either fopen or fprintf have problems."); - } - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it."); - } - - /* Attempt to read from the 'r+' only file, should pass */ - if(fgets(buffer,10,fp) == NULL) - { - Fail("ERROR: Tried to READ from a file with 'r+' mode set. " - "This should succeed, but fgets returned NULL. Either fgets " - "or fopen is broken."); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test6/test6.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test6/test6.cpp deleted file mode 100644 index 13f00cc9300b66..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test6/test6.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test6.c -** -** Purpose: Tests the PAL implementation of the fopen function. -** Test to ensure that you can write to an 'a' mode file. -** And that you can't read from a 'a' mode file. Also ensure -** that you can use fseek and still write to the end of a file. -** -** Depends: -** fprintf -** fgets -** fseek -** fclose -** - -** -**===================================================================*/ - -#include - -PALTEST(c_runtime_fopen_test6_paltest_fopen_test6, "c_runtime/fopen/test6/paltest_fopen_test6") -{ - - FILE *fp; - char buffer[128]; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - /* Open a file with 'a+' mode */ - if( (fp = fopen( "testfile", "a" )) == NULL ) - { - Fail( "ERROR: The file failed to open with 'a' mode.\n" ); - } - - /* Write some text to the file */ - if(fprintf(fp,"%s","some text") <= 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'a' mode " - "but fprintf failed. Either fopen or fprintf have problems."); - } - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it."); - } - - /* Attempt to read from the 'a' only file, should fail */ - if(fgets(buffer,10,fp) != NULL) - { - Fail("ERROR: Tried to READ from a file with 'a' mode set. " - "This should fail, but fgets returned success. Either fgets " - "or fopen is broken."); - } - - - /* Attempt to write to a file after using 'a' and fseek */ - fp = fopen("testfile2", "a"); - if(fp == NULL) - { - Fail("ERROR: The file failed to be created with 'a' mode.\n"); - } - - /* write text to the file initially */ - if(fprintf(fp,"%s","abcd") <= 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'a' mode " - "but fprintf failed. Either fopen or fprintf have problems.\n"); - } - - /* set the pointer to the front of the file */ - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it.\n"); - } - - /* using 'a' should still write to the end of the file, not the front */ - if(fputs("efgh",fp) < 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'a' mode " - "but fputs failed.\n"); - } - - /* set the pointer to the front of the file */ - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it.\n"); - } - - /* a file with 'a' mode can only write, so close the file before reading */ - if(fclose(fp)) - { - Fail("ERROR: fclose failed when it should have succeeded.\n"); - } - - /* open the file again to read */ - fp = fopen("testfile2","r"); - if(fp == NULL) - { - Fail("ERROR: fopen failed to open the file using 'r' mode"); - } - - /* Attempt to read from the 'a' only file, should succeed */ - if(fgets(buffer,10,fp) == NULL) - { - Fail("ERROR: Tried to READ from a file with 'a' mode set. " - "This should pass, but fgets returned failure. Either fgets " - "or fopen is broken.\n"); - } - - /* Compare what was read and what should have been in the file */ - if(memcmp(buffer,"abcdefgh",8)) - { - Fail("ERROR: The string read should have equaled 'abcdefgh' " - "but instead it is %s\n", buffer); - } - - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test7/test7.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test7/test7.cpp deleted file mode 100644 index 0cf274d8c6b78d..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fopen/test7/test7.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test7.c -** -** Purpose: Tests the PAL implementation of the fopen function. -** Test to ensure that you can write to an 'a+' mode file. -** And that you can read from a 'a+' mode file. Also ensure -** that you can use fseek and still write to the end of a file. -** -** Depends: -** fprintf -** fgets -** fseek -** fclose -** - -** -**===================================================================*/ - -#include - -PALTEST(c_runtime_fopen_test7_paltest_fopen_test7, "c_runtime/fopen/test7/paltest_fopen_test7") -{ - - FILE *fp; - char buffer[128]; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - /* Open a file with 'a+' mode */ - if( (fp = fopen( "testfile", "a+" )) == NULL ) - { - Fail( "ERROR: The file failed to open with 'a+' mode.\n" ); - } - - /* Write some text to the file */ - if(fprintf(fp,"%s","some text") <= 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'a+' mode " - "but fprintf failed. Either fopen or fprintf have problems.\n"); - } - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it.\n"); - } - - /* Attempt to read from the 'a+' only file, should succeed */ - if(fgets(buffer,10,fp) == NULL) - { - Fail("ERROR: Tried to READ from a file with 'a+' mode set. " - "This should pass, but fgets returned failure. Either fgets " - "or fopen is broken.\n"); - } - - - /* Attempt to write to a file after using 'a+' and fseek */ - fp = fopen("testfile2", "a+"); - if(fp == NULL) - { - Fail("ERROR: The file failed to be created with 'a+' mode.\n"); - } - - /* write text to the file initially */ - if(fprintf(fp,"%s","abcd") <= 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'a+' mode " - "but fprintf failed. Either fopen or fprintf have problems.\n"); - } - - /* set the pointer to the front of the file */ - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it.\n"); - } - - /* using 'a+' should still write to the end of the file, not the front */ - if(fputs("efgh",fp) < 0) - { - Fail("ERROR: Attempted to WRITE to a file opened with 'a+' mode " - "but fputs failed.\n"); - } - - /* set the pointer to the front of the file */ - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it.\n"); - } - - /* Attempt to read from the 'a+' only file, should succeed */ - if(fgets(buffer,10,fp) == NULL) - { - Fail("ERROR: Tried to READ from a file with 'a+' mode set. " - "This should pass, but fgets returned failure. Either fgets " - "or fopen is broken.\n"); - } - - /* Compare what was read and what should have been in the file */ - if(memcmp(buffer,"abcdefgh",8)) - { - Fail("ERROR: The string read should have equaled 'abcdefgh' " - "but instead it is %s\n", buffer); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fputs/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fputs/test1/test1.cpp deleted file mode 100644 index c746daff9e6d24..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fputs/test1/test1.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test1.c -** -** Purpose: Call fputs twice and write two strings to a file. Then -** call fread on the file and check that the data which was written is what -** we expect it to be. -** - -** -**===================================================================*/ - - -#include - - -PALTEST(c_runtime_fputs_test1_paltest_fputs_test1, "c_runtime/fputs/test1/paltest_fputs_test1") -{ - - FILE* TheFile; - char* StringOne = "FooBar"; - char* StringTwo = "BarFoo"; - char* CompleteString = "FooBarBarFoo"; - char ReadBuffer[64]; - int ret; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - /* Open the file that we'll be working with */ - - TheFile = fopen("TestFile", "w+"); - - if(TheFile == NULL) - { - Fail("ERROR: fopen failed to open the file 'TestFile' in read/write " - "mode.\n"); - } - - /* Call fputs twice to write two strings to the file stream */ - - if(fputs(StringOne, TheFile) < 0) - { - Fail("ERROR: fputs returned a negative value when attempting to " - "put the string '%s' to the file.\n",StringOne); - } - - if(fputs(StringTwo, TheFile) < 0) - { - Fail("ERROR: fputs returned a negative value when attempting to " - "put the string '%s' to the file.\n",StringTwo); - } - - /* Flush the buffers */ - if(fflush(TheFile) != 0) - { - Fail("ERROR: fflush failed to properly flush the buffers.\n"); - } - - /* Now read from the file to ensure the data was written correctly. - Note: We read more than what was written to make sure nothing extra - was written. - */ - - if(fseek(TheFile, 0, SEEK_SET) != 0) - { - Fail("ERROR: fseek failed to set the file pointer back to the start " - "of the file.\n"); - } - - - if((ret = fread(ReadBuffer, 1, 20, TheFile)) != 12) - { - Fail("ERROR: fread should have returned that it read in 12 characters " - "from the file, but instead it returned %d.\n", ret); - } - - ReadBuffer[ret] = '\0'; - - if(strcmp(ReadBuffer, CompleteString) != 0) - { - Fail("ERROR: The data read back from the file is not exactly the same " - "as the data that was written by fputs. The file contains '%s' " - "instead of '%s'.\n",ReadBuffer, CompleteString); - } - - if(fclose(TheFile) != 0) - { - Fail("ERROR: fclose failed to close the file stream.\n"); - } - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fputs/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fputs/test2/test2.cpp deleted file mode 100644 index aa8d36a5115b09..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fputs/test2/test2.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test2.c -** -** Purpose: Check to see that fputs fails and returns EOF when called on -** a closed file stream and a read-only file stream. -** - -** -**===================================================================*/ - -#include - -PALTEST(c_runtime_fputs_test2_paltest_fputs_test2, "c_runtime/fputs/test2/paltest_fputs_test2") -{ - - FILE* TheFile; - char* StringOne = "FooBar"; - int ret; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - /* Create a file with read/write access */ - - TheFile = fopen("TestFile", "w+"); - - if(TheFile == NULL) - { - Fail("ERROR: fopen failed to open the file 'TestFile' in read/write " - "mode.\n"); - } - - /* Then close that file we just opened */ - - if(fclose(TheFile) != 0) - { - Fail("ERROR: fclose failed to close the file.\n"); - } - - /* Check that calling fputs on this closed file stream fails. */ - - if((ret = fputs(StringOne, TheFile)) >= 0) - { - Fail("ERROR: fputs should have failed to write to a closed " - "file stream, but it didn't return a negative value.\n"); - } - - if(ret != EOF) - { - Fail("ERROR: fputs should have returned EOF on an error, but instead " - "returned %d.\n",ret); - } - - /* Open a file as Readonly */ - - TheFile = fopen("TestFile", "r"); - - if(TheFile == NULL) - { - Fail("ERROR: fopen failed to open the file 'TestFile' in read/write " - "mode.\n"); - } - - /* Check that fputs fails when trying to write to a read-only stream */ - - if((ret = fputs(StringOne, TheFile)) >= 0) - { - Fail("ERROR: fputs should have failed to write to a read-only " - "file stream, but it didn't return a negative value.\n"); - } - - if(ret != EOF) - { - Fail("ERROR: fputs should have returned EOF when writing to a " - "read-only filestream, but instead " - "returned %d.\n",ret); - } - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fread/test1/test1.cpp deleted file mode 100644 index 3cfe9fa444a566..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test1/test1.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test1.c -** -** Purpose: Tests the PAL implementation of the fread function. -** Open a file in READ mode, and then try to read all -** the characters, more than all the characters, -** 0 characters and 0 sized characters and check that -** the return values are correct. -** -** Depends: -** fopen -** fseek -** fclose -** -** -**===================================================================*/ - -/* Note: testfile should exist in the directory with 15 characters - in it ... something got lost if it isn't here. -*/ - -/* Note: Under win32, fread() crashes when passed NULL. The test to ensure that - it returns 0 has been removed to reflect this. -*/ - -#include - -PALTEST(c_runtime_fread_test1_paltest_fread_test1, "c_runtime/fread/test1/paltest_fread_test1") -{ - const char filename[] = "testfile"; - char buffer[128]; - FILE * fp = NULL; - int result; - - if (0 != PAL_Initialize(argc, argv)) - { - return FAIL; - } - - /* Open a file in READ mode */ - - if((fp = fopen(filename, "r")) == NULL) - { - Fail("Unable to open a file for reading. Is the file " - "in the directory? It should be."); - } - - /* Read 15 characters from the file. The file has exactly this many - in it. - */ - if((result = fread(buffer,1,15,fp)) == 0) - { - Fail("ERROR: Zero characters read from the file. It should have " - "15 characters in it."); - } - - if(result != 15) - { - Fail("ERROR: The fread function should have returned that it read " - "in 15 characters from the file. But it indicates having " - "read %i characters.",result); - } - - /* Go back to the start of the file */ - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it."); - } - - /* Attempt to read 17 characters, the return should still be 15 */ - - if((result = fread(buffer,1,17,fp)) == 0) - { - Fail("ERROR: Zero characters read from the file. It should have " - "15 characters in it. Though, it attempted to read 17."); - } - - if(result != 15) - { - Fail("ERROR: The fread function should have returned that it read " - "in 15 characters from the file. " - "But it indicates having read %i characters.",result); - } - - /* Back to the start of the file */ - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it."); - } - - /* Read 0 characters and ensure the function returns 0 */ - - if((result = fread(buffer,1,0,fp)) != 0) - { - Fail("ERROR: The return value should be 0, as we attempted to " - "read 0 characters."); - } - - /* Read characters of 0 size and ensure the return value is 0 */ - - if((result = fread(buffer,0,5,fp)) != 0) - { - Fail("ERROR: The return value should be 0, as we attempted to " - "read 0 sized data."); - } - - /* Close the file */ - - if(fclose(fp)) - { - Fail("ERROR: fclose failed. Test depends on it."); - } - - /* Read 5 characters of 1 size from a closed file pointer - and ensure the return value is 0 - */ - - if((result = fread(buffer,1,5,fp)) != 0) - { - Fail("ERROR: The return value should be 0, as we attempted to " - "read data from a closed file pointer."); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test1/testfile b/src/coreclr/pal/tests/palsuite/c_runtime/fread/test1/testfile deleted file mode 100644 index 273c1a9ffdc201..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test1/testfile +++ /dev/null @@ -1 +0,0 @@ -This is a test. \ No newline at end of file diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fread/test2/test2.cpp deleted file mode 100644 index de372204eb66e7..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test2/test2.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test2.c -** -** Purpose: Tests the PAL implementation of the fread function. -** Open a file in READ mode, and then try to read all -** the characters, more than all the characters, -** 0 characters and 0 sized characters and check that -** the strings read in are correct. -** -** Depends: -** fopen -** fseek -** fclose -** strcmp -** memset -** -** -**===================================================================*/ - -/* Note: testfile should exist in the directory with 15 characters - in it ... something got lost if it isn't here. -*/ - -/* Note: The behaviour in win32 is to crash if a NULL pointer is passed to - fread, so the test to check that it returns 0 has been removed. -*/ - -#include - -PALTEST(c_runtime_fread_test2_paltest_fread_test2, "c_runtime/fread/test2/paltest_fread_test2") -{ - const char filename[] = "testfile"; - char buffer[128]; - FILE * fp = NULL; - - if (0 != PAL_Initialize(argc, argv)) - { - return FAIL; - } - - /* Open a file in READ mode */ - - if((fp = fopen(filename, "r")) == NULL) - { - Fail("Unable to open a file for reading. Is the file " - "in the directory? It should be."); - } - - /* Read 15 characters from the file. The file has exactly this many - in it. Then check to see that the data read in is correct. - Note: The 'testfile' should have "This is a test." written in it. - */ - memset(buffer,'\0',128); - fread(buffer,1,15,fp); - - if(strcmp(buffer,"This is a test.") != 0) - { - Fail("ERROR: The data read in should have been " - "'This is a test.' but, the buffer contains '%s'.", - buffer); - } - - /* Go back to the start of the file */ - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it."); - } - - /* Attempt to read 17 characters. The same 15 characters should - be in the buffer. - */ - - memset(buffer,'\0',128); - fread(buffer,1,17,fp); - - if(strcmp(buffer,"This is a test.") != 0) - { - Fail("ERROR: The data read in should have been " - "'This is a test.' but, the buffer contains '%s'.", - buffer); - } - - /* Back to the start of the file */ - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it."); - } - - /* Read 0 characters and ensure the buffer is empty */ - - memset(buffer,'\0',128); - fread(buffer,1,0,fp); - - if(strcmp(buffer,"\0") != 0) - { - Fail("ERROR: The data read in should have been " - "NULL but, the buffer contains '%s'.", - buffer); - } - - /* Read characters of 0 size and ensure the buffer is empty */ - - memset(buffer,'\0',128); - fread(buffer,0,5,fp); - - if(strcmp(buffer,"\0") != 0) - { - Fail("ERROR: The data read in should have been " - "NULL but, the buffer contains '%s'.", - buffer); - } - - /* Close the file */ - - if(fclose(fp)) - { - Fail("ERROR: fclose failed. Test depends on it."); - } - - /* Read 5 characters of 1 size from a closed file pointer - and ensure the buffer is empty - */ - memset(buffer,'\0',128); - fread(buffer,1,5,fp); - if(strcmp(buffer,"\0") != 0) - { - Fail("ERROR: The data read in should have been " - "NULL but, the buffer contains '%s'.", - buffer); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test2/testfile b/src/coreclr/pal/tests/palsuite/c_runtime/fread/test2/testfile deleted file mode 100644 index 273c1a9ffdc201..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test2/testfile +++ /dev/null @@ -1 +0,0 @@ -This is a test. \ No newline at end of file diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test3/test3.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fread/test3/test3.cpp deleted file mode 100644 index 75ae9c6ad1b3d0..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test3/test3.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test3.c -** -** Purpose: Tests the PAL implementation of the fread function. -** Open a file in READ mode, then try to read from the file with -** different 'size' params. Check to ensure the return values and -** the text in the buffer is correct. -** -** Depends: -** fopen -** fseek -** strcmp -** memset -** -** -**===================================================================*/ - -/* Note: testfile should exist in the directory with 15 characters - in it ... something got lost if it isn't here. -*/ - -#include - -PALTEST(c_runtime_fread_test3_paltest_fread_test3, "c_runtime/fread/test3/paltest_fread_test3") -{ - const char filename[] = "testfile"; - char buffer[128]; - FILE * fp = NULL; - int result; - - if (0 != PAL_Initialize(argc, argv)) - { - return FAIL; - } - - /* Open a file in READ mode */ - - if((fp = fopen(filename, "r")) == NULL) - { - Fail("Unable to open a file for reading. Is the file " - "in the directory? It should be."); - } - - memset(buffer,'x',128); - - /* Put the null one character past the end of the text that was read - in, to ensure that it wasn't reading in 0 - */ - - buffer[16] = '\0'; - - /* Attempt to read in 5 bytes at a time. This should return 3 and - contain the full string in the buffer. - */ - - if((result = fread(buffer,5,3,fp)) != 3) - { - Fail("ERROR: Attempted to read in data of size 5. The file has " - "15 bytes in it so 3 items should have been read. But the value " - "returned was %d.",result); - } - - if(strcmp(buffer, "This is a test.x") != 0) - { - Fail("ERROR: The buffer should have contained the text " - "'This is a test.x' but instead contained '%s'.",buffer); - } - - memset(buffer,'x',128); - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it."); - } - - buffer[16] = '\0'; - - /* Attempt to read in 6 bytes at a time. The return should be 2. The - full string should still be in the buffer. - */ - - if((result = fread(buffer,6,3,fp)) != 2) - { - Fail("ERROR: Attempted to read in data of size 6. The file has " - "15 bytes in it, so 2 items should have been read. But the " - "value returned was %d.",result); - } - - if(strcmp(buffer, "This is a test.x") != 0) - { - Fail("ERROR: The buffer should have contained the text " - "'This is a test.x' but instead contained '%s'.",buffer); - } - - memset(buffer,'x',128); - - buffer[7] = '\0'; - - if(fseek(fp, 0, SEEK_SET)) - { - Fail("ERROR: fseek failed, and this test depends on it."); - } - - /* Attempt to read in 6 bytes at a time but only one item max. - The return should be 1. The first 6 characters should be in the - buffer. - */ - - if((result = fread(buffer,6,1,fp)) != 1) - { - Fail("ERROR: Attempted to read in data of size 6 with a max count " - "of 1. Thus, one item should have been read, but the " - "value returned was %d.",result); - } - - if(strcmp(buffer, "This ix") != 0) - { - Fail("ERROR: The buffer should have contained the text " - "'This ix.' but instead contained '%s'.",buffer); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test3/testfile b/src/coreclr/pal/tests/palsuite/c_runtime/fread/test3/testfile deleted file mode 100644 index 273c1a9ffdc201..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fread/test3/testfile +++ /dev/null @@ -1 +0,0 @@ -This is a test. \ No newline at end of file diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fseek/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fseek/test1/test1.cpp deleted file mode 100644 index 7fdfc2fef2fe4d..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fseek/test1/test1.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: test1.c -** -** Purpose: Call fseek to move a file pointer to the start of a file, -** a position offset from the start, a position offset from the -** current position, and a position offset from the end of the -** file. Check that the file pointer is at the correct position -** after each seek. -** -** -**==========================================================================*/ - -#include - -const char filename[] = "testfile.txt"; - -static BOOL Cleanup(HANDLE hFile) -{ - BOOL result= TRUE; - - if (fclose((PAL_FILE*)hFile)) - { - Trace("fseek: ERROR -> Unable to close file \"%s\".\n", - filename); - result= FALSE; - } - if (!DeleteFileA(filename)) - { - result= FALSE; - Trace("fseek: ERROR -> Unable to delete file \"%s\". ", - "GetLastError returned %u.\n", - filename, - GetLastError()); - } - return result; -} - -PALTEST(c_runtime_fseek_test1_paltest_fseek_test1, "c_runtime/fseek/test1/paltest_fseek_test1") -{ - char outBuf[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - char inBuf[20]; - FILE * fp; - int size = ( sizeof(outBuf)/sizeof(char) ) - 1; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - - /*create the file*/ - fp = fopen(filename, "w"); - if (fp == NULL) - { - Fail("Unable to open a file for write.\n"); - } - if(fprintf(fp, outBuf) != size) - { - Trace("Unable to write to %s.\n", filename); - Cleanup(fp); - Fail(""); - } - - if (fclose(fp) != 0) - { - Trace("Unable to close newly written file.\n"); - if (!DeleteFileA(filename)) - { - Trace("fseek: ERROR -> Unable to delete file \"%s\". ", - "GetLastError returned %u.\n", - filename, - GetLastError()); - } - Fail(""); - } - - fp = fopen(filename, "r"); - if (fp == NULL) - { - if (!DeleteFileA(filename)) - { - Trace("_putw: ERROR -> Unable to delete file \"%s\". ", - "GetLastError returned %u.\n", - filename, - GetLastError()); - } - Fail("Unable to open a file for read.\n"); - } - - /*seek to the start*/ - if (fseek(fp, 0, SEEK_SET) != 0) - { - Cleanup(fp); - Fail("fseek failed when seeking the start of a file.\n"); - } - if (fgets(inBuf, 11, fp) != inBuf) - { - Cleanup(fp); - Fail("Unable to read from file after using fseek to move to the start.\n"); - } - if (strncmp(inBuf, outBuf, 10) != 0) - { - Cleanup(fp); - Fail("fseek was asked to seek the start of a file," - "but didn't get there.\n"); - } - - /*Seek with an offset from the start*/ - - if (fseek(fp, 10, SEEK_SET) != 0) - { - Cleanup(fp); - Fail("fseek failed when called with SEEK_SET and a positive offset.\n"); - } - - if (fgets(inBuf, 6, fp) != inBuf) - { - Cleanup(fp); - Fail("fgets failed after feek was called with SEEK_SET" - "and a positive offset.\n"); - } - - - if (strncmp(inBuf, "ABCDE", 5) != 0) - { - Cleanup(fp); - Fail("fseek did not move to the correct position when passed SEEK_SET" - " and a positive offset.\n"); - } - - /*now move backwards and read the same string*/ - if (fseek(fp, -5, SEEK_CUR) != 0) - { - Cleanup(fp); - Fail("fseek failed when passed SEEK_CUR and a negative offset.\n"); - } - - if (fgets(inBuf, 6, fp) != inBuf) - { - Cleanup(fp); - Fail("fgets failed after fseek was called with SEEK_CUR and a " - "negative offset.\n"); - } - - if (strncmp(inBuf, "ABCDE", 5) != 0) - { - Cleanup(fp); - Fail("fseek did not move to the correct position when called with" - " SEEK_CUR and a negative offset.\n"); - } - - /*Try seeking relative to the end of the file.*/ - if (fseek(fp, -10, SEEK_END) != 0) - { - Cleanup(fp); - Fail("fseek failed when called with SEEK_END and a negative" - " offset.\n"); - } - if (fgets(inBuf, 2, fp) != inBuf) - { - Cleanup(fp); - Fail("fgets failed after fseek was called with SEEK_END and a " - "negative offset\n"); - } - - if (strncmp(inBuf, "Q", 1) != 0) - { - Cleanup(fp); - Fail("fseek did not move to the correct position when called with " - "SEEK_END and a negative offset.\n"); - } - - - /*close the file*/ - if(!Cleanup(fp)) - { - Fail(""); - } - - PAL_Terminate(); - return PASS; -} - - - - - - diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/ftell/test1/ftell.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/ftell/test1/ftell.cpp deleted file mode 100644 index 1c60fab20791e9..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/ftell/test1/ftell.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: ftell.c (test 1) -** -** Purpose: Tests the PAL implementation of the ftell function. -** -** -**===================================================================*/ - -#include - -FILE* pFile; -struct TESTS -{ - long lDist; - int nFrom; - long lPosition; -}; - - -/************************************************* -** -** Validate -** -** Purpose: -** Tests whether the move was successful. If -** it passes, it returns TRUE. If it fails -** it outputs some error messages and returns -** FALSE. -** -*************************************************/ -BOOL Validate(long lExpected) -{ - long lPos = -2; - - if (((lPos = ftell(pFile)) == -1) || (lPos != lExpected)) - { - Trace("ftell: ERROR -> ftell returned %ld when expecting %ld.\n", - lPos, - lExpected); - if (fclose(pFile) != 0) - { - Trace("ftell: ERROR -> fclose failed to close the file.\n"); - } - return FALSE; - } - return TRUE; -} - - -/************************************************* -** -** MovePointer -** -** Purpose: -** Accepts the distance to move and the -** distance and calls fseek to move the file -** pointer. If the fseek fails, error messages -** are displayed and FALSE is returned. TRUE -** is returned on a successful fseek. -** -*************************************************/ -BOOL MovePointer(long lDist, int nFrom) -{ - /* move the file pointer*/ - if (fseek(pFile, lDist, nFrom) != 0) - { - Trace("ftell: ERROR -> fseek failed to move the file pointer " - "%l characters.\n", - lDist); - if (fclose(pFile) != 0) - { - Trace("ftell: ERROR -> fclose failed to close the file.\n"); - } - return FALSE; - } - return TRUE; -} - - - -PALTEST(c_runtime_ftell_test1_paltest_ftell_test1, "c_runtime/ftell/test1/paltest_ftell_test1") -{ - const char szFileName[] = {"testfile.txt"}; - long lPos = -1; - int i; - char szTempBuffer[256]; - struct TESTS testCase[] = - { - {0, SEEK_SET, 0}, - {10, SEEK_CUR, 10}, - {-5, SEEK_CUR, 5}, - {-2, SEEK_END, 50} - }; - - - - if (0 != PAL_Initialize(argc,argv)) - { - return FAIL; - } - - memset(szTempBuffer, 0, 256); - - - /* open the test file */ - pFile = fopen(szFileName, "r"); - if (pFile == NULL) - { - Fail("ftell: ERROR -> fopen failed to open the file \"%s\".\n"); - } - - /* loop through the test cases */ - for (i = 0; i < (sizeof(testCase)/sizeof(struct TESTS)); i++) - { - if (MovePointer(testCase[i].lDist, testCase[i].nFrom) != TRUE) - { - Fail(""); - } - else if (Validate(testCase[i].lPosition) != TRUE) - { - Fail(""); - } - } - - if (fclose(pFile) != 0) - { - Fail("ftell: ERROR -> fclose failed to close the file.\n"); - } - - /* lets just see if we can find out where we are in a closed stream... */ - if ((lPos = ftell(pFile)) != -1) - { - Fail("ftell: ERROR -> ftell returned a valid position (%ld) on a " - "closed file handle\n", - lPos); - } - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/ftell/test1/testfile.txt b/src/coreclr/pal/tests/palsuite/c_runtime/ftell/test1/testfile.txt deleted file mode 100644 index dd0fe15fe101b7..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/ftell/test1/testfile.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown fox jumped over the lazy dog's back. \ No newline at end of file diff --git a/src/coreclr/pal/tests/palsuite/c_runtime/fwrite/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/c_runtime/fwrite/test1/test1.cpp deleted file mode 100644 index 5b74faa8eac925..00000000000000 --- a/src/coreclr/pal/tests/palsuite/c_runtime/fwrite/test1/test1.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================ -** -** Source: test1.c -** -** Purpose: Write a short string to a file and check that it was written -** properly. -** -** -**==========================================================================*/ - -#include - -PALTEST(c_runtime_fwrite_test1_paltest_fwrite_test1, "c_runtime/fwrite/test1/paltest_fwrite_test1") -{ - const char filename[] = "testfile.tmp"; - const char outBuffer[] = "This is a test."; - char inBuffer[sizeof(outBuffer) + 10]; - int itemsExpected; - int itemsWritten; - FILE * fp = NULL; - - if (PAL_Initialize(argc, argv)) - { - return FAIL; - } - - if((fp = fopen(filename, "w")) == NULL) - { - Fail("Unable to open a file for write.\n"); - } - - itemsExpected = sizeof(outBuffer); - itemsWritten = fwrite(outBuffer, - sizeof(outBuffer[0]), - sizeof(outBuffer), - fp); - - if (itemsWritten == 0) - { - if(fclose(fp) != 0) - { - Fail("fwrite: Error occurred during the closing of a file.\n"); - } - - Fail("fwrite() couldn't write to a stream at all\n"); - } - else if (itemsWritten != itemsExpected) - { - if(fclose(fp) != 0) - { - Fail("fwrite: Error occurred during the closing of a file.\n"); - } - - Fail("fwrite() produced errors writing to a stream.\n"); - } - - if(fclose(fp) != 0) - { - Fail("fwrite: Error occurred during the closing of a file.\n"); - } - - /* open the file to verify what was written to the file */ - if ((fp = fopen(filename, "r")) == NULL) - { - Fail("Couldn't open newly written file for read.\n"); - } - - if (fgets(inBuffer, sizeof(inBuffer), fp) == NULL) - { - if(fclose(fp) != 0) - { - Fail("fwrite: Error occurred during the closing of a file.\n"); - } - - Fail("We wrote something to a file using fwrite() and got errors" - " when we tried to read it back using fgets(). Either " - "fwrite() or fgets() is broken.\n"); - } - - if (strcmp(inBuffer, outBuffer) != 0) - { - if(fclose(fp) != 0) - { - Fail("fwrite: Error occurred during the closing of a file.\n"); - } - - Fail("fwrite() (or fgets()) is broken. The string read back from" - " the file does not match the string written.\n"); - } - - if(fclose(fp) != 0) - { - Fail("fwrite: Error occurred during the closing of a file.\n"); - } - - PAL_Terminate(); - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/common/palsuite.h b/src/coreclr/pal/tests/palsuite/common/palsuite.h index c275b3812a256c..9494daed71bebd 100644 --- a/src/coreclr/pal/tests/palsuite/common/palsuite.h +++ b/src/coreclr/pal/tests/palsuite/common/palsuite.h @@ -25,6 +25,7 @@ typedef unsigned short char16_t; #include #include #include +#include #define PALTEST(testfunc, testname) \ int __cdecl testfunc(int argc, char* argv[]); \ diff --git a/src/coreclr/pal/tests/palsuite/compilableTests.txt b/src/coreclr/pal/tests/palsuite/compilableTests.txt index 779f7b55f52d83..9a26d821065301 100644 --- a/src/coreclr/pal/tests/palsuite/compilableTests.txt +++ b/src/coreclr/pal/tests/palsuite/compilableTests.txt @@ -14,28 +14,7 @@ c_runtime/errno/test1/paltest_errno_test1 c_runtime/errno/test2/paltest_errno_test2 c_runtime/exit/test1/paltest_exit_test1 c_runtime/exit/test2/paltest_exit_test2 -c_runtime/ferror/test1/paltest_ferror_test1 -c_runtime/ferror/test2/paltest_ferror_test2 -c_runtime/fflush/test1/paltest_fflush_test1 -c_runtime/fgets/test1/paltest_fgets_test1 -c_runtime/fgets/test2/paltest_fgets_test2 -c_runtime/fgets/test3/paltest_fgets_test3 -c_runtime/fopen/test1/paltest_fopen_test1 -c_runtime/fopen/test2/paltest_fopen_test2 -c_runtime/fopen/test3/paltest_fopen_test3 -c_runtime/fopen/test4/paltest_fopen_test4 -c_runtime/fopen/test5/paltest_fopen_test5 -c_runtime/fopen/test6/paltest_fopen_test6 -c_runtime/fopen/test7/paltest_fopen_test7 -c_runtime/fputs/test1/paltest_fputs_test1 -c_runtime/fputs/test2/paltest_fputs_test2 -c_runtime/fread/test1/paltest_fread_test1 -c_runtime/fread/test2/paltest_fread_test2 -c_runtime/fread/test3/paltest_fread_test3 c_runtime/free/test1/paltest_free_test1 -c_runtime/fseek/test1/paltest_fseek_test1 -c_runtime/ftell/test1/paltest_ftell_test1 -c_runtime/fwrite/test1/paltest_fwrite_test1 c_runtime/isalnum/test1/paltest_isalnum_test1 c_runtime/isalpha/test1/paltest_isalpha_test1 c_runtime/isdigit/test1/paltest_isdigit_test1 @@ -356,7 +335,6 @@ miscellaneous/SetEnvironmentVariableW/test3/paltest_setenvironmentvariablew_test miscellaneous/SetEnvironmentVariableW/test4/paltest_setenvironmentvariablew_test4 miscellaneous/SetLastError/test1/paltest_setlasterror_test1 miscellaneous/_i64tow/test1/paltest_i64tow_test1 -pal_specific/PAL_errno/test1/paltest_pal_errno_test1 pal_specific/PAL_GetUserTempDirectoryW/test1/paltest_pal_getusertempdirectoryw_test1 pal_specific/PAL_Initialize_Terminate/test1/paltest_pal_initialize_terminate_test1 pal_specific/PAL_Initialize_Terminate/test2/paltest_pal_initialize_terminate_test2 diff --git a/src/coreclr/pal/tests/palsuite/pal_specific/PAL_errno/test1/PAL_errno.cpp b/src/coreclr/pal/tests/palsuite/pal_specific/PAL_errno/test1/PAL_errno.cpp deleted file mode 100644 index 29f2da53b055b8..00000000000000 --- a/src/coreclr/pal/tests/palsuite/pal_specific/PAL_errno/test1/PAL_errno.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================= -** -** Source: pal_errno.c -** -** Purpose: Positive test the PAL_errno API. -** call PAL_errno to retrieve the pointer to -** the per-thread errno value. -** -** -**============================================================*/ -#include - -PALTEST(pal_specific_PAL_errno_test1_paltest_pal_errno_test1, "pal_specific/PAL_errno/test1/paltest_pal_errno_test1") -{ - int err; - FILE *pFile = NULL; - - /*Initialize the PAL environment*/ - err = PAL_Initialize(argc, argv); - if( 0 != err) - { - return FAIL; - } - - /*Try to open a not-exist file to read to generate an error*/ - pFile = fopen( "no_exist_file_name", "r" ); - - if( NULL != pFile ) - { - Trace("\nFailed to call fopen to open a not exist for reading, " - "an error is expected, but no error occurred\n"); - - if( EOF == fclose( pFile ) ) - { - Trace("\nFailed to call fclose to close a file stream\n"); - } - Fail( "Test failed! fopen() Should not have worked!" ); - } - - /*retrieve the per-thread error value pointer*/ - if( 2 != errno ) - { - Fail("\nFailed to call PAL_errno API, this value is not correct." - " The correct value is ENOENT[2] ( No such file or directory.).\n"); - } - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stderr/test1/PAL_get_stderr.cpp b/src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stderr/test1/PAL_get_stderr.cpp deleted file mode 100644 index 2377505158c657..00000000000000 --- a/src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stderr/test1/PAL_get_stderr.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================= -** -** Source: pal_get_stderr.c -** -** Purpose: Positive test the PAL_get_stderr API. -** Call PAL_get_stderr to retrieve the PAL standard error -** output stream pointer. -** This test case should be run manually and automatically. -** - -** -**============================================================*/ -#include - -PALTEST(pal_specific_PAL_get_stderr_test1_paltest_pal_get_stderr_test1, "pal_specific/PAL_get_stderr/test1/paltest_pal_get_stderr_test1") -{ - int err; - FILE *pPAL_stderr = NULL; - const char *pMsg = "\nThis is a PAL_get_stderr test message, " - "not an error message!\n"; - - /*Initialize the PAL environment*/ - err = PAL_Initialize(argc, argv); - if(0 != err) - { - return FAIL; - } - - /*retrieve the PAL standard error output stream pointer*/ - pPAL_stderr = PAL_get_stderr(); - - if(NULL == pPAL_stderr) - { - Fail("\nFailed to call PAL_get_stderr API, error code = %u\n", - GetLastError()); - } - - /*output a test message through PAL standard error stream*/ - err = fputs(pMsg, pPAL_stderr); - if(EOF == err) - { - Fail("\nFailed to call fputs to output message to PAL stdandard " - "error stream, error code=%u\n", GetLastError()); - } - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stdin/test1/PAL_get_stdin.cpp b/src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stdin/test1/PAL_get_stdin.cpp deleted file mode 100644 index 91d8a3a9a650e3..00000000000000 --- a/src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stdin/test1/PAL_get_stdin.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================= -** -** Source: pal_get_stdin.c -** -** Purpose: Positive test the PAL_get_stdout API. -** Call PAL_get_stdin to retrieve the PAL standard input -** stream pointer. -** This test case should be run manually. -** - -** -**============================================================*/ -#include - -PALTEST(pal_specific_PAL_get_stdin_test1_paltest_pal_get_stdin_test1, "pal_specific/PAL_get_stdin/test1/paltest_pal_get_stdin_test1") -{ - int err; - FILE *pPAL_stdin = NULL; - char Buffer[256]; - - /*Initialize the PAL environment*/ - err = PAL_Initialize(argc, argv); - if(0 != err) - { - return FAIL; - } - - /*retrieve the PAL standard input stream pointer*/ - pPAL_stdin = PAL_get_stdin(); - if(NULL == pPAL_stdin) - { - Fail("\nFailed to call PAL_get_stdin API to retrieve the " - "PAL standard input stream pointer, " - "error code = %u\n", GetLastError()); - } - - /*zero the buffer*/ - memset(Buffer, 0, 256); - - printf("\nPlease input some words: (less than 255 characters)\n"); - - /*further test the input stream*/ - /*read message from the PAL standard input stream*/ - if(NULL == fgets(Buffer, 255, pPAL_stdin)) - { - Fail( "Failed to call fgets to get a string from PAL standard " - "input stream, error code=%u\n", GetLastError()); - } - else - { - if(1 == strlen(Buffer) && Buffer[0] == '\n') - { - printf("\nEmpty input!\n"); - } - else - { - printf("\nYour input words are:\n%s\n", Buffer); - } - } - - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stdout/test1/PAL_get_stdout.cpp b/src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stdout/test1/PAL_get_stdout.cpp deleted file mode 100644 index d891e9ac40abc5..00000000000000 --- a/src/coreclr/pal/tests/palsuite/pal_specific/PAL_get_stdout/test1/PAL_get_stdout.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================= -** -** Source: pal_get_stdout.c -** -** Purpose: Positive test the PAL_get_stdout API. -** Call PAL_get_stdout to retrieve the PAL standard output -** stream pointer. -** This test case should be run manually and automatically. -** - -** -**============================================================*/ -#include - -PALTEST(pal_specific_PAL_get_stdout_test1_paltest_pal_get_stdout_test1, "pal_specific/PAL_get_stdout/test1/paltest_pal_get_stdout_test1") -{ - int err; - FILE *pPAL_stdout = NULL; - const char *pMsg = "\nThis is a PAL_get_stdout test output message, " - "not an error message!\n"; - - /*Initialize the PAL environment*/ - err = PAL_Initialize(argc, argv); - if(0 != err) - { - return FAIL; - } - - /*retrieve the PAL output stream pointer*/ - pPAL_stdout = PAL_get_stdout(); - if(NULL == pPAL_stdout) - { - Fail("\nFailed to call PAL_get_stdout API to retrieve the " - "standard PAL output stream pointer, error code=%u\n", - GetLastError()); - } - - /*output a test message through PAL standard output stream*/ - err = fputs(pMsg, pPAL_stdout); - if(EOF == err) - { - Fail("\nFailed to call fputs to output message to PAL stdandard " - "output stream, error code=%u\n", GetLastError()); - } - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/paltestlist.txt b/src/coreclr/pal/tests/palsuite/paltestlist.txt index 505538abf4b77f..878f00a2adf85c 100644 --- a/src/coreclr/pal/tests/palsuite/paltestlist.txt +++ b/src/coreclr/pal/tests/palsuite/paltestlist.txt @@ -5,21 +5,7 @@ c_runtime/bsearch/test2/paltest_bsearch_test2 c_runtime/errno/test1/paltest_errno_test1 c_runtime/errno/test2/paltest_errno_test2 c_runtime/exit/test1/paltest_exit_test1 -c_runtime/fflush/test1/paltest_fflush_test1 -c_runtime/fgets/test1/paltest_fgets_test1 -c_runtime/fgets/test2/paltest_fgets_test2 -c_runtime/fgets/test3/paltest_fgets_test3 -c_runtime/fopen/test1/paltest_fopen_test1 -c_runtime/fopen/test2/paltest_fopen_test2 -c_runtime/fopen/test3/paltest_fopen_test3 -c_runtime/fopen/test4/paltest_fopen_test4 -c_runtime/fopen/test5/paltest_fopen_test5 -c_runtime/fopen/test6/paltest_fopen_test6 -c_runtime/fopen/test7/paltest_fopen_test7 -c_runtime/fputs/test1/paltest_fputs_test1 c_runtime/free/test1/paltest_free_test1 -c_runtime/fseek/test1/paltest_fseek_test1 -c_runtime/fwrite/test1/paltest_fwrite_test1 c_runtime/isalnum/test1/paltest_isalnum_test1 c_runtime/isalpha/test1/paltest_isalpha_test1 c_runtime/isdigit/test1/paltest_isdigit_test1 @@ -302,7 +288,6 @@ miscellaneous/SetEnvironmentVariableW/test3/paltest_setenvironmentvariablew_test miscellaneous/SetEnvironmentVariableW/test4/paltest_setenvironmentvariablew_test4 miscellaneous/SetLastError/test1/paltest_setlasterror_test1 miscellaneous/_i64tow/test1/paltest_i64tow_test1 -pal_specific/PAL_errno/test1/paltest_pal_errno_test1 pal_specific/PAL_Initialize_Terminate/test1/paltest_pal_initialize_terminate_test1 pal_specific/PAL_Initialize_Terminate/test2/paltest_pal_initialize_terminate_test2 samples/test1/paltest_samples_test1 diff --git a/src/coreclr/tools/StressLogAnalyzer/StressLogDump.cpp b/src/coreclr/tools/StressLogAnalyzer/StressLogDump.cpp index f3be8b8812218e..49bf662c21dde5 100644 --- a/src/coreclr/tools/StressLogAnalyzer/StressLogDump.cpp +++ b/src/coreclr/tools/StressLogAnalyzer/StressLogDump.cpp @@ -243,7 +243,7 @@ void formatOutput(struct IDebugDataSpaces* memCallBack, ___in FILE* file, __inou fprintf (file, " (%s", Symbol); if (Displacement) { - fprintf (file, "+%#llx", Displacement); + fprintf (file, "+%#llx", (unsigned long long)Displacement); } fprintf (file, ")"); } @@ -528,7 +528,7 @@ HRESULT StressLog::Dump(ULONG64 outProcLog, const char* fileName, struct IDebugD latestLog->readPtr = NULL; if (!bDoGcHist) { - fprintf(file, "------------ Last message from thread %llx -----------\n", latestLog->threadId); + fprintf(file, "------------ Last message from thread %llx -----------\n", (unsigned long long)latestLog->threadId); } } diff --git a/src/coreclr/utilcode/debug.cpp b/src/coreclr/utilcode/debug.cpp index 363ff6599bab3f..9784bc71dc98ef 100644 --- a/src/coreclr/utilcode/debug.cpp +++ b/src/coreclr/utilcode/debug.cpp @@ -20,9 +20,8 @@ #include "log.h" -extern "C" _CRTIMP int __cdecl _flushall(void); - #ifdef HOST_WINDOWS +extern "C" _CRTIMP int __cdecl _flushall(void); void CreateCrashDumpIfEnabled(bool stackoverflow = false); #endif @@ -51,7 +50,11 @@ static void DECLSPEC_NORETURN FailFastOnAssert() WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency! FlushLogging(); // make certain we get the last part of the log +#ifdef HOST_WINDOWS _flushall(); +#else + fflush(NULL); +#endif ShutdownLogging(); #ifdef HOST_WINDOWS @@ -254,7 +257,7 @@ bool _DbgBreakCheck( if (formattedMessages) { OutputDebugStringUtf8(formatBuffer); - fprintf(stderr, formatBuffer); + fprintf(stderr, "%s", formatBuffer); } else { diff --git a/src/coreclr/vm/pgo.cpp b/src/coreclr/vm/pgo.cpp index 20b060d2cb4a8e..c42a2236b3b981 100644 --- a/src/coreclr/vm/pgo.cpp +++ b/src/coreclr/vm/pgo.cpp @@ -205,7 +205,7 @@ void PgoManager::WritePgoData() return; } - FILE* const pgoDataFile = _wfopen(fileName, W("w")); + FILE* const pgoDataFile = _wfopen(fileName, W("wb")); if (pgoDataFile == NULL) { @@ -238,7 +238,7 @@ void PgoManager::WritePgoData() unsigned lastOffset = 0; auto lambda = [data, pgoDataFile] (const ICorJitInfo::PgoInstrumentationSchema &schema) { - fprintf(pgoDataFile, s_RecordString, schema.InstrumentationKind, schema.ILOffset, schema.Count, schema.Other); + fprintf(pgoDataFile, s_RecordString, (unsigned int)schema.InstrumentationKind, schema.ILOffset, schema.Count, schema.Other); for (int32_t iEntry = 0; iEntry < schema.Count; iEntry++) { size_t entryOffset = schema.Offset + iEntry * InstrumentationKindToSize(schema.InstrumentationKind); @@ -367,7 +367,7 @@ void PgoManager::ReadPgoData() return; } - FILE* const pgoDataFile = _wfopen(fileName, W("r")); + FILE* const pgoDataFile = _wfopen(fileName, W("rb")); if (pgoDataFile == NULL) { From bfc1a7428dff1230fe9a293d9ded663ffa24f17a Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 15 Feb 2024 19:22:44 -0500 Subject: [PATCH 075/158] Vectorize TensorPrimitives.PopCount (#98281) * Vectorize TensorPrimitives.PopCount * Update comment per PR feedback --------- Co-authored-by: Eirik Tsarpalis --- .../netcore/TensorPrimitives.netcore.cs | 267 +++++++++++++----- 1 file changed, 194 insertions(+), 73 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs index 065c0f4fd08aaf..dfd0897e701816 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.netcore.cs @@ -12959,14 +12959,10 @@ private static T HorizontalAggregate(Vector128 x) where TAggre x = TAggregate.Invoke(x, Vector128.Shuffle(x.AsInt32(), Vector128.Create(2, 3, 0, 1)).As()); x = TAggregate.Invoke(x, Vector128.Shuffle(x.AsInt32(), Vector128.Create(1, 0, 3, 2)).As()); } - else if (Unsafe.SizeOf() == 8) - { - x = TAggregate.Invoke(x, Vector128.Shuffle(x.AsInt64(), Vector128.Create(1, 0)).As()); - } else { - Debug.Fail("Should not be reachable"); - throw new NotSupportedException(); + Debug.Assert(Unsafe.SizeOf() == 8); + x = TAggregate.Invoke(x, Vector128.Shuffle(x.AsInt64(), Vector128.Create(1, 0)).As()); } return x.ToScalar(); @@ -13068,15 +13064,12 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(AlignmentUInt32Mask_16x17) (uint)(count * 16)); } - if (Unsafe.SizeOf() == 8) + Debug.Assert(Unsafe.SizeOf() == 8); { return Vector128.LoadUnsafe( ref Unsafe.As(ref MemoryMarshal.GetReference(AlignmentUInt64Mask_8x9)), (uint)(count * 8)); } - - Debug.Fail("Shouldn't get here"); - throw new NotSupportedException(); } /// @@ -13107,15 +13100,12 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(AlignmentUInt32Mask_16x17) (uint)(count * 16)); } - if (Unsafe.SizeOf() == 8) + Debug.Assert(Unsafe.SizeOf() == 8); { return Vector256.LoadUnsafe( ref Unsafe.As(ref MemoryMarshal.GetReference(AlignmentUInt64Mask_8x9)), (uint)(count * 8)); } - - Debug.Fail("Shouldn't get here"); - throw new NotSupportedException(); } /// @@ -13146,15 +13136,12 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(AlignmentUInt32Mask_16x17) (uint)(count * 16)); } - if (Unsafe.SizeOf() == 8) + Debug.Assert(Unsafe.SizeOf() == 8); { return Vector512.LoadUnsafe( ref Unsafe.As(ref MemoryMarshal.GetReference(AlignmentUInt64Mask_8x9)), (uint)(count * 8)); } - - Debug.Fail("Shouldn't get here - CreateAlignmentMaskVector512"); - throw new NotSupportedException(); } /// @@ -13185,15 +13172,12 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(RemainderUInt32Mask_16x17) (uint)(count * 16) + 12); // last 4 ints in the row } - if (Unsafe.SizeOf() == 8) + Debug.Assert(Unsafe.SizeOf() == 8); { return Vector128.LoadUnsafe( ref Unsafe.As(ref MemoryMarshal.GetReference(RemainderUInt64Mask_8x9)), (uint)(count * 8) + 6); // last 2 longs in the row } - - Debug.Fail("Shouldn't get here - CreateRemainderMaskVector128"); - throw new NotSupportedException(); } /// @@ -13224,15 +13208,12 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(RemainderUInt32Mask_16x17) (uint)(count * 16) + 8); // last 8 ints in the row } - if (Unsafe.SizeOf() == 8) + Debug.Assert(Unsafe.SizeOf() == 8); { return Vector256.LoadUnsafe( ref Unsafe.As(ref MemoryMarshal.GetReference(RemainderUInt64Mask_8x9)), (uint)(count * 8) + 4); // last 4 longs in the row } - - Debug.Fail("Shouldn't get here - CreateRemainderMaskVector256"); - throw new NotSupportedException(); } /// @@ -13263,15 +13244,12 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(RemainderUInt32Mask_16x17) (uint)(count * 16)); } - if (Unsafe.SizeOf() == 8) + Debug.Assert(Unsafe.SizeOf() == 8); { return Vector512.LoadUnsafe( ref Unsafe.As(ref MemoryMarshal.GetReference(RemainderUInt64Mask_8x9)), (uint)(count * 8)); } - - Debug.Fail("Shouldn't get here - CreateRemainderMaskVector512"); - throw new NotSupportedException(); } // TODO: The uses of these ApplyScalar methods are all as part of operators when handling edge cases (NaN, Infinity, really large inputs, etc.) @@ -13750,7 +13728,7 @@ private static int IndexOfFinalAggregate(Vector128 resul return resultIndex.As().ToScalar(); } - if (sizeof(T) == 1) + Debug.Assert(sizeof(T) == 1); { // Compare 0,1,2,3,4,5,6,7 with 8,9,10,11,12,13,14,15 tmpResult = Vector128.Shuffle(result.AsByte(), Vector128.Create((byte)8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7)).As(); @@ -13775,8 +13753,6 @@ private static int IndexOfFinalAggregate(Vector128 resul // Return 0 return resultIndex.As().ToScalar(); } - - throw new NotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -18847,13 +18823,11 @@ public static Vector128 Invoke(Vector128 x) { return Vector128.Ceiling(x.AsSingle()).As(); } - - if (typeof(T) == typeof(double)) + else { + Debug.Assert(typeof(T) == typeof(double)); return Vector128.Ceiling(x.AsDouble()).As(); } - - throw new NotSupportedException(); } public static Vector256 Invoke(Vector256 x) @@ -18862,13 +18836,11 @@ public static Vector256 Invoke(Vector256 x) { return Vector256.Ceiling(x.AsSingle()).As(); } - - if (typeof(T) == typeof(double)) + else { + Debug.Assert(typeof(T) == typeof(double)); return Vector256.Ceiling(x.AsDouble()).As(); } - - throw new NotSupportedException(); } public static Vector512 Invoke(Vector512 x) @@ -18877,13 +18849,11 @@ public static Vector512 Invoke(Vector512 x) { return Vector512.Ceiling(x.AsSingle()).As(); } - - if (typeof(T) == typeof(double)) + else { + Debug.Assert(typeof(T) == typeof(double)); return Vector512.Ceiling(x.AsDouble()).As(); } - - throw new NotSupportedException(); } } @@ -18899,13 +18869,11 @@ public static Vector128 Invoke(Vector128 x) { return Vector128.Floor(x.AsSingle()).As(); } - - if (typeof(T) == typeof(double)) + else { + Debug.Assert(typeof(T) == typeof(double)); return Vector128.Floor(x.AsDouble()).As(); } - - throw new NotSupportedException(); } public static Vector256 Invoke(Vector256 x) @@ -18914,13 +18882,11 @@ public static Vector256 Invoke(Vector256 x) { return Vector256.Floor(x.AsSingle()).As(); } - - if (typeof(T) == typeof(double)) + else { + Debug.Assert(typeof(T) == typeof(double)); return Vector256.Floor(x.AsDouble()).As(); } - - throw new NotSupportedException(); } public static Vector512 Invoke(Vector512 x) @@ -18929,13 +18895,11 @@ public static Vector512 Invoke(Vector512 x) { return Vector512.Floor(x.AsSingle()).As(); } - - if (typeof(T) == typeof(double)) + else { + Debug.Assert(typeof(T) == typeof(double)); return Vector512.Floor(x.AsDouble()).As(); } - - throw new NotSupportedException(); } } @@ -18956,9 +18920,10 @@ public static Vector128 Invoke(Vector128 x) Vector128.Floor(x.AsSingle()).As(), Vector128.Ceiling(x.AsSingle()).As()); } - - if (typeof(T) == typeof(double)) + else { + Debug.Assert(typeof(T) == typeof(double)); + if (Sse41.IsSupported) return Sse41.RoundToZero(x.AsDouble()).As(); if (AdvSimd.Arm64.IsSupported) return AdvSimd.Arm64.RoundToZero(x.AsDouble()).As(); @@ -18966,8 +18931,6 @@ public static Vector128 Invoke(Vector128 x) Vector128.Floor(x.AsDouble()).As(), Vector128.Ceiling(x.AsDouble()).As()); } - - throw new NotSupportedException(); } public static Vector256 Invoke(Vector256 x) @@ -18980,17 +18943,16 @@ public static Vector256 Invoke(Vector256 x) Vector256.Floor(x.AsSingle()).As(), Vector256.Ceiling(x.AsSingle()).As()); } - - if (typeof(T) == typeof(double)) + else { + Debug.Assert(typeof(T) == typeof(double)); + if (Avx.IsSupported) return Avx.RoundToZero(x.AsDouble()).As(); return Vector256.ConditionalSelect(Vector256.GreaterThanOrEqual(x, Vector256.Zero), Vector256.Floor(x.AsDouble()).As(), Vector256.Ceiling(x.AsDouble()).As()); } - - throw new NotSupportedException(); } public static Vector512 Invoke(Vector512 x) @@ -19003,28 +18965,187 @@ public static Vector512 Invoke(Vector512 x) Vector512.Floor(x.AsSingle()).As(), Vector512.Ceiling(x.AsSingle()).As()); } - - if (typeof(T) == typeof(double)) + else { + Debug.Assert(typeof(T) == typeof(double)); + if (Avx512F.IsSupported) return Avx512F.RoundScale(x.AsDouble(), 0b11).As(); return Vector512.ConditionalSelect(Vector512.GreaterThanOrEqual(x, Vector512.Zero), Vector512.Floor(x.AsDouble()).As(), Vector512.Ceiling(x.AsDouble()).As()); } - - throw new NotSupportedException(); } } /// T.PopCount(x) internal readonly struct PopCountOperator : IUnaryOperator where T : IBinaryInteger { - public static bool Vectorizable => false; // TODO: Vectorize + // TODO https://github.com/dotnet/runtime/issues/96162: Use AVX512 popcount operations when available + + public static bool Vectorizable => + // The implementation uses a vectorized version of the BitOperations.PopCount software fallback: + // https://github.com/dotnet/runtime/blob/aff061bab1b6d9ccd5731bd16fa8e89ad82ab75a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs#L496-L508 + // This relies on 64-bit shifts for sizeof(T) == 8, and such shifts aren't accelerated on today's hardware. + // Alternative approaches, such as doing two 32-bit operations and combining them were observed to not + // provide any meaningfuls speedup over scalar. So for now, we don't vectorize when sizeof(T) == 8. + sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4; + public static T Invoke(T x) => T.PopCount(x); - public static Vector128 Invoke(Vector128 x) => throw new NotSupportedException(); - public static Vector256 Invoke(Vector256 x) => throw new NotSupportedException(); - public static Vector512 Invoke(Vector512 x) => throw new NotSupportedException(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Invoke(Vector128 x) + { + if (sizeof(T) == 1) + { + if (AdvSimd.IsSupported) + { + return AdvSimd.PopCount(x.AsByte()).As(); + } + + if (PackedSimd.IsSupported) + { + return PackedSimd.PopCount(x.AsByte()).As(); + } + + Vector128 c1 = Vector128.Create((byte)0x55); + Vector128 c2 = Vector128.Create((byte)0x33); + Vector128 c3 = Vector128.Create((byte)0x0F); + + // We don't have a per element shuffle for byte on some platforms. + // However, we do currently always have a 16-bit shift available and + // due to how the algorithm works, we don't need to worry about + // any bits that shift into the lower 8-bits from the upper 8-bits. + Vector128 tmp = x.AsByte(); + tmp -= (x.AsUInt16() >> 1).AsByte() & c1; + tmp = (tmp & c2) + ((tmp.AsUInt16() >> 2).AsByte() & c2); + return ((tmp + (tmp.AsUInt16() >> 4).AsByte()) & c3).As(); + } + + if (sizeof(T) == 2) + { + Vector128 c1 = Vector128.Create((ushort)0x5555); + Vector128 c2 = Vector128.Create((ushort)0x3333); + Vector128 c3 = Vector128.Create((ushort)0x0F0F); + Vector128 c4 = Vector128.Create((ushort)0x0101); + + Vector128 tmp = x.AsUInt16(); + tmp -= (tmp >> 1) & c1; + tmp = (tmp & c2) + ((tmp >> 2) & c2); + tmp = (((tmp + (tmp >> 4)) & c3) * c4) >> 8; + return tmp.As(); + } + + Debug.Assert(sizeof(T) == 4); + { + Vector128 c1 = Vector128.Create(0x55555555u); + Vector128 c2 = Vector128.Create(0x33333333u); + Vector128 c3 = Vector128.Create(0x0F0F0F0Fu); + Vector128 c4 = Vector128.Create(0x01010101u); + + Vector128 tmp = x.AsUInt32(); + tmp -= (tmp >> 1) & c1; + tmp = (tmp & c2) + ((tmp >> 2) & c2); + tmp = (((tmp + (tmp >> 4)) & c3) * c4) >> 24; + return tmp.As(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Invoke(Vector256 x) + { + if (sizeof(T) == 1) + { + Vector256 c1 = Vector256.Create((byte)0x55); + Vector256 c2 = Vector256.Create((byte)0x33); + Vector256 c3 = Vector256.Create((byte)0x0F); + + // We don't have a per element shuffle for byte on some platforms. + // However, we do currently always have a 16-bit shift available and + // due to how the algorithm works, we don't need to worry about + // any bits that shift into the lower 8-bits from the upper 8-bits. + Vector256 tmp = x.AsByte(); + tmp -= (x.AsUInt16() >> 1).AsByte() & c1; + tmp = (tmp & c2) + ((tmp.AsUInt16() >> 2).AsByte() & c2); + return ((tmp + (tmp.AsUInt16() >> 4).AsByte()) & c3).As(); + } + + if (sizeof(T) == 2) + { + Vector256 c1 = Vector256.Create((ushort)0x5555); + Vector256 c2 = Vector256.Create((ushort)0x3333); + Vector256 c3 = Vector256.Create((ushort)0x0F0F); + Vector256 c4 = Vector256.Create((ushort)0x0101); + + Vector256 tmp = x.AsUInt16(); + tmp -= (tmp >> 1) & c1; + tmp = (tmp & c2) + ((tmp >> 2) & c2); + tmp = (((tmp + (tmp >> 4)) & c3) * c4) >> 8; + return tmp.As(); + } + + Debug.Assert(sizeof(T) == 4); + { + Vector256 c1 = Vector256.Create(0x55555555u); + Vector256 c2 = Vector256.Create(0x33333333u); + Vector256 c3 = Vector256.Create(0x0F0F0F0Fu); + Vector256 c4 = Vector256.Create(0x01010101u); + + Vector256 tmp = x.AsUInt32(); + tmp -= (tmp >> 1) & c1; + tmp = (tmp & c2) + ((tmp >> 2) & c2); + tmp = (((tmp + (tmp >> 4)) & c3) * c4) >> 24; + return tmp.As(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Invoke(Vector512 x) + { + if (sizeof(T) == 1) + { + Vector512 c1 = Vector512.Create((byte)0x55); + Vector512 c2 = Vector512.Create((byte)0x33); + Vector512 c3 = Vector512.Create((byte)0x0F); + + // We don't have a per element shuffle for byte on some platforms. + // However, we do currently always have a 16-bit shift available and + // due to how the algorithm works, we don't need to worry about + // any bits that shift into the lower 8-bits from the upper 8-bits. + Vector512 tmp = x.AsByte(); + tmp -= (x.AsUInt16() >> 1).AsByte() & c1; + tmp = (tmp & c2) + ((tmp.AsUInt16() >> 2).AsByte() & c2); + return ((tmp + (tmp.AsUInt16() >> 4).AsByte()) & c3).As(); + } + + if (sizeof(T) == 2) + { + Vector512 c1 = Vector512.Create((ushort)0x5555); + Vector512 c2 = Vector512.Create((ushort)0x3333); + Vector512 c3 = Vector512.Create((ushort)0x0F0F); + Vector512 c4 = Vector512.Create((ushort)0x0101); + + Vector512 tmp = x.AsUInt16(); + tmp -= (tmp >> 1) & c1; + tmp = (tmp & c2) + ((tmp >> 2) & c2); + tmp = (((tmp + (tmp >> 4)) & c3) * c4) >> 8; + return tmp.As(); + } + + Debug.Assert(sizeof(T) == 4); + { + Vector512 c1 = Vector512.Create(0x55555555u); + Vector512 c2 = Vector512.Create(0x33333333u); + Vector512 c3 = Vector512.Create(0x0F0F0F0Fu); + Vector512 c4 = Vector512.Create(0x01010101u); + + Vector512 tmp = x.AsUInt32(); + tmp -= (tmp >> 1) & c1; + tmp = (tmp & c2) + ((tmp >> 2) & c2); + tmp = (((tmp + (tmp >> 4)) & c3) * c4) >> 24; + return tmp.As(); + } + } } /// T.LeadingZeroCount(x) From 79837bc2669e6bb06d9b5de2fd33f872618d531b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 15 Feb 2024 17:14:41 -0800 Subject: [PATCH 076/158] Don't strip pointed-to type information in IL stubs (#98392) --- .../TypeSystem/Interop/IL/Marshaller.cs | 4 +- src/coreclr/vm/dllimport.cpp | 7 +++ src/coreclr/vm/fieldmarshaler.cpp | 3 ++ src/coreclr/vm/ilmarshalers.cpp | 2 +- src/coreclr/vm/ilmarshalers.h | 38 +++++++++++++- src/coreclr/vm/mlinfo.cpp | 28 ++++++++--- src/coreclr/vm/mtypes.h | 2 + src/coreclr/vm/stubgen.cpp | 28 ++++------- src/coreclr/vm/stubgen.h | 12 ----- .../PInvoke/Primitives/Pointer/CMakeLists.txt | 6 +-- .../Pointer/NonBlittablePointerNative.cpp | 9 ---- .../Primitives/Pointer/PointerNative.cpp | 20 ++++++++ .../PInvoke/Primitives/Pointer/Program.cs | 50 ++++++++++++++++--- 13 files changed, 150 insertions(+), 59 deletions(-) delete mode 100644 src/tests/Interop/PInvoke/Primitives/Pointer/NonBlittablePointerNative.cpp create mode 100644 src/tests/Interop/PInvoke/Primitives/Pointer/PointerNative.cpp diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs index c5b17ef7ede85b..4160711b6a3601 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/IL/Marshaller.cs @@ -908,13 +908,13 @@ protected override void EmitMarshalArgumentManagedToNative() { ILCodeStream marshallingCodeStream = _ilCodeStreams.MarshallingCodeStream; ILEmitter emitter = _ilCodeStreams.Emitter; - ILLocalVariable native = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.IntPtr)); + ILLocalVariable native = emitter.NewLocal(Context.GetPointerType(ManagedParameterType)); ILLocalVariable vPinnedByRef = emitter.NewLocal(ManagedParameterType, true); marshallingCodeStream.EmitLdArg(Index - 1); marshallingCodeStream.EmitStLoc(vPinnedByRef); marshallingCodeStream.EmitLdLoc(vPinnedByRef); - marshallingCodeStream.Emit(ILOpcode.conv_i); + marshallingCodeStream.Emit(ILOpcode.conv_u); marshallingCodeStream.EmitStLoc(native); _ilCodeStreams.CallsiteSetupCodeStream.EmitLdLoc(native); } diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index f3f1c29d532657..d395b8e32cf5c4 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -545,6 +545,13 @@ class ILStubState : public StubState pcsExceptionHandler->EmitINITOBJ(m_slIL.GetDispatchCodeStream()->GetToken(returnTypeHnd)); } break; + case ELEMENT_TYPE_PTR: + pcsExceptionHandler->EmitPOP(); + pcsExceptionHandler->EmitLDC(0); + pcsExceptionHandler->EmitCONV_U(); + _ASSERTE(retvalLocalNum != (DWORD)-1); + pcsExceptionHandler->EmitSTLOC(retvalLocalNum); + break; case ELEMENT_TYPE_BOOLEAN: case ELEMENT_TYPE_CHAR: case ELEMENT_TYPE_I1: diff --git a/src/coreclr/vm/fieldmarshaler.cpp b/src/coreclr/vm/fieldmarshaler.cpp index 57fc5b82bad14a..0d29374cecded6 100644 --- a/src/coreclr/vm/fieldmarshaler.cpp +++ b/src/coreclr/vm/fieldmarshaler.cpp @@ -189,6 +189,9 @@ VOID ParseNativeType(Module* pModule, case MarshalInfo::MARSHAL_TYPE_FIXED_WSTR: *pNFD = NativeFieldDescriptor(pFD, CoreLibBinder::GetClass(CLASS__UINT16), pargs->fs.fixedStringLength); break; + case MarshalInfo::MARSHAL_TYPE_POINTER: + *pNFD = NativeFieldDescriptor(pFD, NativeFieldCategory::INTEGER, sizeof(void*), sizeof(void*)); + break; case MarshalInfo::MARSHAL_TYPE_UNKNOWN: default: *pNFD = NativeFieldDescriptor(pFD); diff --git a/src/coreclr/vm/ilmarshalers.cpp b/src/coreclr/vm/ilmarshalers.cpp index 59bb7c9ebed2de..a06cb57e1df0b3 100644 --- a/src/coreclr/vm/ilmarshalers.cpp +++ b/src/coreclr/vm/ilmarshalers.cpp @@ -3482,7 +3482,7 @@ MarshalerOverrideStatus ILBlittableValueClassWithCopyCtorMarshaler::ArgumentOver pslILDispatch->EmitLDLOCA(dwNewValueTypeLocal); #else LocalDesc locDesc(pargs->mm.m_pMT); - locDesc.MakeCopyConstructedPointer(); + locDesc.MakePointer(); pslIL->SetStubTargetArgType(&locDesc); pslILDispatch->EmitLDARG(argidx); diff --git a/src/coreclr/vm/ilmarshalers.h b/src/coreclr/vm/ilmarshalers.h index 02589d44994fb5..757307b5006340 100644 --- a/src/coreclr/vm/ilmarshalers.h +++ b/src/coreclr/vm/ilmarshalers.h @@ -365,7 +365,7 @@ class ILMarshaler // Convert the loaded local containing a native address // into a non-GC type for the byref case. - pslILEmit->EmitCONV_I(); + pslILEmit->EmitCONV_U(); } void EmitLoadManagedValue(ILCodeStream* pslILEmit) @@ -399,7 +399,7 @@ class ILMarshaler // Convert the loaded value containing a native address // into a non-GC type for the byref case. - pslILEmit->EmitCONV_I(); + pslILEmit->EmitCONV_U(); } void EmitStoreManagedValue(ILCodeStream* pslILEmit) @@ -1814,6 +1814,40 @@ class ILBlittableValueClassMarshaler : public ILCopyMarshalerBase } }; +class ILPointerMarshaler final : public ILCopyMarshalerBase +{ +public: + enum + { + c_fInOnly = TRUE, + c_nativeSize = TARGET_POINTER_SIZE, + }; +protected: + LocalDesc GetManagedType() override + { + LIMITED_METHOD_CONTRACT; + LocalDesc native(m_pargs->m_pMT); + native.MakePointer(); + return native; + } + + LocalDesc GetNativeType() override + { + LIMITED_METHOD_CONTRACT; + LocalDesc native(m_pargs->m_pMT); + native.MakePointer(); + return native; + } + + virtual void EmitReInitNative(ILCodeStream* pslILEmit) override + { + STANDARD_VM_CONTRACT; + + pslILEmit->EmitLDC(0); + pslILEmit->EmitCONV_U(); + EmitStoreNativeValue(pslILEmit); + } +}; class ILDelegateMarshaler : public ILMarshaler { diff --git a/src/coreclr/vm/mlinfo.cpp b/src/coreclr/vm/mlinfo.cpp index 4c63edb027f303..39eff93cc6c24d 100644 --- a/src/coreclr/vm/mlinfo.cpp +++ b/src/coreclr/vm/mlinfo.cpp @@ -1144,18 +1144,26 @@ namespace return MarshalInfo::MARSHAL_TYPE_GENERIC_8; #ifdef TARGET_64BIT case ELEMENT_TYPE_U: - case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_FNPTR: case ELEMENT_TYPE_I: return MarshalInfo::MARSHAL_TYPE_GENERIC_8; #else case ELEMENT_TYPE_U: return MarshalInfo::MARSHAL_TYPE_GENERIC_U4; - case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_FNPTR: case ELEMENT_TYPE_I: return MarshalInfo::MARSHAL_TYPE_GENERIC_4; #endif + case ELEMENT_TYPE_PTR: + { + BYTE ptrByte; + sig.SkipCustomModifiers(); + sig.GetByte(&ptrByte); + _ASSERTE(ptrByte == ELEMENT_TYPE_PTR); + TypeHandle sigTH = sig.GetTypeHandleThrowing(pModule, pTypeContext); + *pMTOut = sigTH.GetMethodTable(); + return MarshalInfo::MARSHAL_TYPE_POINTER; + } case ELEMENT_TYPE_R4: return MarshalInfo::MARSHAL_TYPE_FLOAT; case ELEMENT_TYPE_R8: @@ -1693,17 +1701,23 @@ MarshalInfo::MarshalInfo(Module* pModule, break; case ELEMENT_TYPE_PTR: + { if (nativeType != NATIVE_TYPE_DEFAULT) { m_resID = IDS_EE_BADMARSHAL_PTR; IfFailGoto(E_FAIL, lFail); } -#ifdef TARGET_64BIT - m_type = MARSHAL_TYPE_GENERIC_8; -#else - m_type = MARSHAL_TYPE_GENERIC_4; -#endif + + SigPointer sigtmp = sig; + BYTE ptrByte; + sigtmp.SkipCustomModifiers(); + sigtmp.GetByte(&ptrByte); + _ASSERTE(ptrByte == ELEMENT_TYPE_PTR); + TypeHandle sigTH = sigtmp.GetTypeHandleThrowing(pModule, pTypeContext); + m_args.m_pMT = sigTH.GetMethodTable(); + m_type = MARSHAL_TYPE_POINTER; break; + } case ELEMENT_TYPE_FNPTR: if (!(nativeType == NATIVE_TYPE_FUNC || nativeType == NATIVE_TYPE_DEFAULT)) diff --git a/src/coreclr/vm/mtypes.h b/src/coreclr/vm/mtypes.h index 0cbc86c1a85fcc..e25b9ca83ab35c 100644 --- a/src/coreclr/vm/mtypes.h +++ b/src/coreclr/vm/mtypes.h @@ -103,4 +103,6 @@ DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_FIXED_CSTR, FixedCSTRMar DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_BLITTABLE_LAYOUTCLASS, BlittableLayoutClassMarshaler) DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_LAYOUTCLASS, LayoutClassMarshaler) +DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_POINTER, PointerMarshaler) + #undef DEFINE_MARSHALER_TYPE diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp index 5f18e8f5d9123f..fcb0bf05bdc6e3 100644 --- a/src/coreclr/vm/stubgen.cpp +++ b/src/coreclr/vm/stubgen.cpp @@ -2639,22 +2639,16 @@ void ILStubLinker::TransformArgForJIT(LocalDesc *pLoc) case ELEMENT_TYPE_PTR: { -#ifdef TARGET_X86 - if (pLoc->bIsCopyConstructed) - { - // The only pointers that we don't transform to ELEMENT_TYPE_I are those that are - // ET_TYPE_CMOD_REQD/ET_TYPE_CMOD_REQD - // in the original signature. This convention is understood by the UM thunk compiler - // (code:UMThunkMarshInfo.CompileNExportThunk) which will generate different thunk code. - // Such parameters come from unmanaged by value but must enter the IL stub by reference - // because we are not supposed to make a copy. - } - else -#endif // TARGET_X86 - { - pLoc->ElementType[0] = ELEMENT_TYPE_I; - pLoc->cbType = 1; - } + // Don't transform pointer types to ELEMENT_TYPE_I. The JIT can handle the correct type information, + // and it's required for some cases (such as SwiftError*). + break; + } + + case ELEMENT_TYPE_BYREF: + { + // Transform ELEMENT_TYPE_BYREF to ELEMENT_TYPE_PTR to retain the pointed-to type information + // while making the type blittable. + pLoc->ElementType[0] = ELEMENT_TYPE_PTR; break; } @@ -2669,7 +2663,7 @@ void ILStubLinker::TransformArgForJIT(LocalDesc *pLoc) FALLTHROUGH; } - // pointers, byrefs, strings, arrays, other ref types -> ELEMENT_TYPE_I + // ref types -> ELEMENT_TYPE_I default: { pLoc->ElementType[0] = ELEMENT_TYPE_I; diff --git a/src/coreclr/vm/stubgen.h b/src/coreclr/vm/stubgen.h index 595de649220cc5..56b9b6f6cdcc1a 100644 --- a/src/coreclr/vm/stubgen.h +++ b/src/coreclr/vm/stubgen.h @@ -45,7 +45,6 @@ struct LocalDesc { Module* pSigModule; size_t cbArrayBoundsInfo; - BOOL bIsCopyConstructed; // used for E_T_PTR }; LocalDesc() @@ -56,7 +55,6 @@ struct LocalDesc { ElementType[0] = static_cast(elemType); cbType = 1; - bIsCopyConstructed = FALSE; } inline LocalDesc(TypeHandle thType) @@ -64,7 +62,6 @@ struct LocalDesc ElementType[0] = ELEMENT_TYPE_INTERNAL; cbType = 1; InternalToken = thType; - bIsCopyConstructed = FALSE; } inline LocalDesc(MethodTable *pMT) @@ -73,7 +70,6 @@ struct LocalDesc ElementType[0] = ELEMENT_TYPE_INTERNAL; cbType = 1; InternalToken = TypeHandle(pMT); - bIsCopyConstructed = FALSE; } void MakeByRef() @@ -94,14 +90,6 @@ struct LocalDesc ChangeType(ELEMENT_TYPE_SZARRAY); } - // makes the LocalDesc semantically equivalent to ET_TYPE_CMOD_REQD/ET_TYPE_CMOD_REQD - void MakeCopyConstructedPointer() - { - LIMITED_METHOD_CONTRACT; - MakePointer(); - bIsCopyConstructed = TRUE; - } - void MakePointer() { LIMITED_METHOD_CONTRACT; diff --git a/src/tests/Interop/PInvoke/Primitives/Pointer/CMakeLists.txt b/src/tests/Interop/PInvoke/Primitives/Pointer/CMakeLists.txt index ca190ec6735d3b..0d8bf6e334e20d 100644 --- a/src/tests/Interop/PInvoke/Primitives/Pointer/CMakeLists.txt +++ b/src/tests/Interop/PInvoke/Primitives/Pointer/CMakeLists.txt @@ -1,6 +1,6 @@ include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") set(SOURCES - NonBlittablePointerNative.cpp + PointerNative.cpp ) -add_library (NonBlittablePointerNative SHARED ${SOURCES}) -install (TARGETS NonBlittablePointerNative DESTINATION bin) +add_library (PointerNative SHARED ${SOURCES}) +install (TARGETS PointerNative DESTINATION bin) diff --git a/src/tests/Interop/PInvoke/Primitives/Pointer/NonBlittablePointerNative.cpp b/src/tests/Interop/PInvoke/Primitives/Pointer/NonBlittablePointerNative.cpp deleted file mode 100644 index a2b90f5bfcf910..00000000000000 --- a/src/tests/Interop/PInvoke/Primitives/Pointer/NonBlittablePointerNative.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include - -extern "C" DLL_EXPORT void STDMETHODCALLTYPE Negate(bool* ptr) -{ - *ptr = !*ptr; -} diff --git a/src/tests/Interop/PInvoke/Primitives/Pointer/PointerNative.cpp b/src/tests/Interop/PInvoke/Primitives/Pointer/PointerNative.cpp new file mode 100644 index 00000000000000..80171831069777 --- /dev/null +++ b/src/tests/Interop/PInvoke/Primitives/Pointer/PointerNative.cpp @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE Negate(bool* ptr) +{ + *ptr = !*ptr; +} + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE GetNaN(float* ptr) +{ + *ptr = std::numeric_limits::quiet_NaN(); +} + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE NegateDecimal(DECIMAL* ptr) +{ + ptr->sign = ptr->sign == 0 ? 0x80 : 0; +} diff --git a/src/tests/Interop/PInvoke/Primitives/Pointer/Program.cs b/src/tests/Interop/PInvoke/Primitives/Pointer/Program.cs index 57663d03a8c0d6..623494315728d7 100644 --- a/src/tests/Interop/PInvoke/Primitives/Pointer/Program.cs +++ b/src/tests/Interop/PInvoke/Primitives/Pointer/Program.cs @@ -5,23 +5,61 @@ using System.Runtime.InteropServices; using Xunit; -namespace NonBlittablePointer +namespace Pointer { - static class NonBlittablePointerNative + static class PointerNative { - [DllImport(nameof(NonBlittablePointerNative))] + [DllImport(nameof(PointerNative))] public static unsafe extern void Negate(bool* ptr); + + [DllImport(nameof(PointerNative))] + public static unsafe extern void GetNaN(float* ptr); + + [DllImport(nameof(PointerNative))] + public static unsafe extern void NegateDecimal(decimal* ptr); + + [DllImport(nameof(PointerNative))] + public static unsafe extern void GetNaN(BlittableWrapper* ptr); + + public struct BlittableWrapper + { + public T Value; + } } [ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))] public class Program { [Fact] - public static unsafe int TestEntryPoint() + public static unsafe void PointerToBool() { bool value = true; - NonBlittablePointerNative.Negate(&value); - return value == false ? 100 : 101; + PointerNative.Negate(&value); + Assert.False(value); + } + + [Fact] + public static unsafe void PointerToFloat() + { + float value = 1.0f; + PointerNative.GetNaN(&value); + Assert.True(float.IsNaN(value)); + } + + [Fact] + public static unsafe void PointerToDecimal() + { + decimal value = 1.0m; + PointerNative.NegateDecimal(&value); + Assert.Equal(-1.0m, value); + } + + [Fact] + public static unsafe void PointerToStructOfGeneric() + { + PointerNative.BlittableWrapper wrapper = new(){ Value = 1.0f }; + PointerNative.GetNaN(&wrapper); + Assert.True(float.IsNaN(wrapper.Value)); } } } From a3f74163d65fbbd81b9ed946e9d74f080be1dc70 Mon Sep 17 00:00:00 2001 From: Matous Kozak <55735845+matouskozak@users.noreply.github.com> Date: Fri, 16 Feb 2024 09:31:26 +0100 Subject: [PATCH 077/158] [mono] Block SIMD types in Swift interop (#98429) Temporarily block SIMD types in Swift Interop to simplify current runtime implementation and not cause unintended behavior. --------- Co-authored-by: Milos Kotlar --- src/mono/mono/metadata/marshal.c | 4 ++-- .../SwiftInvalidCallConv/SwiftInvalidCallConv.cs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 1e10d82ee40885..c57fa32127a9f7 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3711,9 +3711,9 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, swift_error_args++; } else if (param_klass == swift_self) { swift_self_args++; - } else if (!m_class_is_blittable (param_klass) && m_class_get_this_arg (param_klass)->type != MONO_TYPE_FNPTR) { + } else if (!m_class_is_blittable (param_klass) || m_class_is_simd_type (param_klass)) { swift_error_args = swift_self_args = 0; - mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Passing non-primitive value types to a P/Invoke with the Swift calling convention is unsupported."); + mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Passing non-blittable types to a P/Invoke with the Swift calling convention is unsupported."); break; } } diff --git a/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.cs b/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.cs index 41c98e3791f91c..9e4bc140829fd9 100644 --- a/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.cs +++ b/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Swift; +using System.Numerics; using Xunit; public class InvalidCallingConvTests @@ -36,6 +37,10 @@ public class StringClass [DllImport(SwiftLib, EntryPoint = "$s20SwiftInvalidCallConv10simpleFuncyyF")] public static extern void FuncWithNonPrimitiveArg(StringClass arg1); + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s20SwiftInvalidCallConv10simpleFuncyyF")] + public static extern void FuncWithSIMDArg(Vector4 vec); + [Fact] public static void TestFuncWithTwoSelfParameters() { @@ -77,4 +82,12 @@ public static void TestFuncWithNonPrimitiveArg() arg1.value = "fail"; Assert.Throws(() => FuncWithNonPrimitiveArg(arg1)); } + + [Fact] + public static void TestFuncWithSIMDArg() + { + // Invalid due to a SIMD argument. + Vector4 vec = new Vector4(); // Using Vector4 as it is a SIMD type across all architectures for Mono + Assert.Throws(() => FuncWithSIMDArg(vec)); + } } From d972a19c077e899d0b3fff97d955968e50906396 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 16 Feb 2024 02:15:36 -0800 Subject: [PATCH 078/158] Delete suppressions against resolved issues (#98512) --- src/tests/issues.targets | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 4f4e73cf099887..2737556054ab7d 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1185,13 +1185,10 @@ https://github.com/dotnet/runtime/issues/88689 - https://github.com/dotnet/runtime/issues/71095 - - - https://github.com/dotnet/runtime/issues/79022 + https://github.com/dotnet/runtime/issues/90308 - https://github.com/dotnet/runtime/issues/74223 + https://github.com/dotnet/runtime/issues/69399 https://github.com/dotnet/runtime/issues/54185 From 7185df8eda6c25d101170a2809094adc9f493b51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 16 Feb 2024 20:31:44 +0900 Subject: [PATCH 079/158] Add some eventlog APIs to WindowsAPIs.txt (#98545) Hello world size regressed by 50 kB because we now need the lazy p/invoke code paths. This API is present in OneCore.lib and has existed since at least Windows 2000. It's missing in mincore.lib, but AFAIK mincore is pretty much deprecated. --- src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txt b/src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txt index bc68e90ec76730..d5a50a702a6d16 100644 --- a/src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txt +++ b/src/coreclr/nativeaot/BuildIntegration/WindowsAPIs.txt @@ -86,6 +86,7 @@ advapi32!CredWriteDomainCredentialsW advapi32!CredWriteW advapi32!DeleteAce advapi32!DeleteService +advapi32!DeregisterEventSource advapi32!DestroyPrivateObjectSecurity advapi32!DuplicateToken advapi32!DuplicateTokenEx @@ -206,6 +207,8 @@ advapi32!RegFlushKey advapi32!RegGetKeySecurity advapi32!RegGetValueA advapi32!RegGetValueW +advapi32!RegisterEventSourceA +advapi32!RegisterEventSourceW advapi32!RegisterServiceCtrlHandlerA advapi32!RegisterServiceCtrlHandlerExA advapi32!RegisterServiceCtrlHandlerExW @@ -239,6 +242,8 @@ advapi32!RegSetValueExA advapi32!RegSetValueExW advapi32!RegUnLoadKeyA advapi32!RegUnLoadKeyW +advapi32!ReportEventA +advapi32!ReportEventW advapi32!RevertToSelf advapi32!SetAclInformation advapi32!SetFileSecurityW From de12fe75a599220f7390ba5af8f9a2d4e6de5ec0 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Fri, 16 Feb 2024 15:05:13 +0100 Subject: [PATCH 080/158] [browser] Move reflection from C# to C (#98534) --- .../src/Interop/Browser/Interop.Runtime.cs | 8 +- .../src/Resources/Strings.resx | 4 +- .../JavaScript/Interop/JavaScriptExports.cs | 6 +- .../JavaScript/JSHostImplementation.cs | 128 ++++----------- .../tests/debugger-test/debugger-main.js | 2 +- src/mono/browser/runtime/corebindings.c | 155 +++++++++++++++++- src/mono/browser/runtime/driver.c | 2 +- src/mono/browser/runtime/invoke-cs.ts | 16 +- src/mono/browser/runtime/managed-exports.ts | 8 +- src/mono/browser/runtime/marshal-to-js.ts | 2 +- src/mono/browser/runtime/strings.ts | 11 +- src/mono/wasi/mono-include/driver.h | 1 - 12 files changed, 207 insertions(+), 136 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 000dd103d21890..23067f81ea601c 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -64,7 +64,11 @@ internal static unsafe partial class Runtime [MethodImpl(MethodImplOptions.InternalCall)] public static extern void CancelPromise(nint gcHandle); #endif - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern void SetEntryAssembly(Assembly assembly, int entryPointMetadataToken); + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void AssemblyGetEntryPoint(IntPtr assemblyNamePtr, int auto_insert_breakpoint, void** monoMethodPtrPtr); + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void BindAssemblyExports(IntPtr assemblyNamePtr); + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void GetAssemblyExport(IntPtr assemblyNamePtr, IntPtr namespacePtr, IntPtr classnamePtr, IntPtr methodNamePtr, IntPtr* monoMethodPtrPtr); } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx index e77e419997996d..9cb71cc66223ad 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/Resources/Strings.resx @@ -138,8 +138,8 @@ Managed entrypoint handle is not set. - - Cannot resolve managed entrypoint {0} in assembly {1}. + + Cannot resolve managed entrypoint handle. Return type '{0}' from main method in not supported. diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs index 9ec4a2400cd211..b244d69abdfb55 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs @@ -15,7 +15,7 @@ namespace System.Runtime.InteropServices.JavaScript internal static unsafe partial class JavaScriptExports { // the marshaled signature is: - // Task? CallEntrypoint(string mainAssemblyName, string[] args, bool waitForDebugger) + // Task? CallEntrypoint(char* assemblyNamePtr, string[] args) public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() @@ -30,11 +30,11 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer) arg_exc.AssertCurrentThreadContext(); #endif - arg_1.ToManaged(out string? mainAssemblyName); + arg_1.ToManaged(out IntPtr assemblyNamePtr); arg_2.ToManaged(out string?[]? args); arg_3.ToManaged(out bool waitForDebugger); - Task? result = JSHostImplementation.CallEntrypoint(mainAssemblyName, args, waitForDebugger); + Task? result = JSHostImplementation.CallEntrypoint(assemblyNamePtr, args, waitForDebugger); arg_result.ToJS(result, (ref JSMarshalerArgument arg, int value) => { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs index 7268951b68828b..27a6d548868929 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs @@ -200,47 +200,19 @@ public static void LoadSatelliteAssembly(byte[] dllBytes) AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(dllBytes)); } - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Dynamic access from JavaScript")] - [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Dynamic access from JavaScript")] - public static Task? CallEntrypoint(string? assemblyName, string?[]? args, bool waitForDebugger) + public static unsafe Task? CallEntrypoint(IntPtr assemblyNamePtr, string?[]? args, bool waitForDebugger) { try { - if (string.IsNullOrEmpty(assemblyName)) - { - throw new MissingMethodException(SR.MissingManagedEntrypointHandle); - } - if (!assemblyName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)) - { - assemblyName += ".dll"; - } - Assembly mainAssembly = Assembly.LoadFrom(assemblyName); - - MethodInfo? method = mainAssembly.EntryPoint; + void* ptr; + Interop.Runtime.AssemblyGetEntryPoint(assemblyNamePtr, waitForDebugger ? 1 : 0, &ptr); + RuntimeMethodHandle methodHandle = GetMethodHandleFromIntPtr((IntPtr)ptr); + // this would not work for generic types. But Main() could not be generic, so we are fine. + MethodInfo? method = MethodBase.GetMethodFromHandle(methodHandle) as MethodInfo; if (method == null) { - throw new InvalidOperationException(string.Format(SR.CannotResolveManagedEntrypoint, "Main", assemblyName)); + throw new InvalidOperationException(SR.CannotResolveManagedEntrypointHandle); } - if (method.IsSpecialName) - { - // we are looking for the original async method, rather than for the compiler generated wrapper like
- // because we need to yield to browser event loop - var type = method.DeclaringType!; - var name = method.Name; - var asyncName = name + "$"; - method = type.GetMethod(asyncName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); - if (method == null) - { - asyncName = name.Substring(1, name.Length - 2); - method = type.GetMethod(asyncName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); - } - if (method == null) - { - throw new InvalidOperationException(string.Format(SR.CannotResolveManagedEntrypoint, asyncName, assemblyName)); - } - } - - Interop.Runtime.SetEntryAssembly(mainAssembly, waitForDebugger ? method.MetadataToken : 0); object[] argsToPass = System.Array.Empty(); Task? result = null; @@ -293,75 +265,32 @@ public static void LoadSatelliteAssembly(byte[] dllBytes) } } - private static string GeneratedInitializerClassName = "System.Runtime.InteropServices.JavaScript.__GeneratedInitializer"; - private static string GeneratedInitializerMethodName = "__Register_"; - - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Dynamic access from JavaScript")] - [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Dynamic access from JavaScript")] - public static Task BindAssemblyExports(string? assemblyName) + public static unsafe Task BindAssemblyExports(string? assemblyName) { - try - { - if (string.IsNullOrEmpty(assemblyName)) - { - throw new MissingMethodException("Missing assembly name"); - } - if (!assemblyName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase)) - { - assemblyName += ".dll"; - } - - Assembly assembly = Assembly.LoadFrom(assemblyName); - Type? type = assembly.GetType(GeneratedInitializerClassName); - if (type == null) - { - foreach (var module in assembly.Modules) - { - RuntimeHelpers.RunModuleConstructor(module.ModuleHandle); - } - } - else - { - MethodInfo? methodInfo = type.GetMethod(GeneratedInitializerMethodName, BindingFlags.NonPublic | BindingFlags.Static); - methodInfo?.Invoke(null, []); - } - - return Task.CompletedTask; - } - catch (Exception ex) - { - if (ex is TargetInvocationException refEx && refEx.InnerException != null) - ex = refEx.InnerException; - return Task.FromException(ex); - } + Interop.Runtime.BindAssemblyExports(Marshal.StringToCoTaskMemUTF8(assemblyName)); + return Task.CompletedTask; } - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "TODO https://github.com/dotnet/runtime/issues/98366")] - [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "TODO https://github.com/dotnet/runtime/issues/98366")] public static unsafe JSFunctionBinding BindManagedFunction(string fullyQualifiedName, int signatureHash, ReadOnlySpan signatures) { - if (string.IsNullOrEmpty(fullyQualifiedName)) - { - throw new ArgumentNullException(nameof(fullyQualifiedName)); - } + var (assemblyName, nameSpace, shortClassName, methodName) = ParseFQN(fullyQualifiedName); + var wrapper_name = $"__Wrapper_{methodName}_{signatureHash}"; + var dllName = assemblyName + ".dll"; - var signature = GetMethodSignature(signatures, null, null); - var (assemblyName, className, nameSpace, shortClassName, methodName) = ParseFQN(fullyQualifiedName); + IntPtr monoMethod; + Interop.Runtime.GetAssemblyExport( + Marshal.StringToCoTaskMemUTF8(dllName), + Marshal.StringToCoTaskMemUTF8(nameSpace), + Marshal.StringToCoTaskMemUTF8(shortClassName), + Marshal.StringToCoTaskMemUTF8(wrapper_name), + &monoMethod); - Assembly assembly = Assembly.LoadFrom(assemblyName + ".dll"); - Type? type = assembly.GetType(className); - if (type == null) + if (monoMethod == IntPtr.Zero) { - throw new InvalidOperationException("Class not found " + className); - } - var wrapper_name = $"__Wrapper_{methodName}_{signatureHash}"; - var methodInfo = type.GetMethod(wrapper_name, BindingFlags.NonPublic | BindingFlags.Static); - if (methodInfo == null) - { - throw new InvalidOperationException("Method not found " + wrapper_name); + Environment.FailFast($"Can't find {nameSpace}{shortClassName}{methodName} in {assemblyName}.dll"); } - var monoMethod = GetIntPtrFromMethodHandle(methodInfo.MethodHandle); + var signature = GetMethodSignature(signatures, null, null); JavaScriptImports.BindCSFunction(monoMethod, assemblyName, nameSpace, shortClassName, methodName, signatureHash, (IntPtr)signature.Header); @@ -381,14 +310,13 @@ public static void SetHasExternalEventLoop(Thread thread) #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetIntPtrFromMethodHandle(RuntimeMethodHandle methodHandle) + public static RuntimeMethodHandle GetMethodHandleFromIntPtr(IntPtr ptr) { - var temp = new IntPtrAndHandle { methodHandle = methodHandle }; - return temp.ptr; + var temp = new IntPtrAndHandle { ptr = ptr }; + return temp.methodHandle; } - - public static (string assemblyName, string className, string nameSpace, string shortClassName, string methodName) ParseFQN(string fqn) + public static (string assemblyName, string nameSpace, string shortClassName, string methodName) ParseFQN(string fqn) { var assembly = fqn.Substring(fqn.IndexOf('[') + 1, fqn.IndexOf(']') - 1).Trim(); fqn = fqn.Substring(fqn.IndexOf(']') + 1).Trim(); @@ -410,7 +338,7 @@ public static (string assemblyName, string className, string nameSpace, string s throw new InvalidOperationException("No class name specified " + fqn); if (string.IsNullOrEmpty(methodName)) throw new InvalidOperationException("No method name specified " + fqn); - return (assembly, className, nameSpace, shortClassName, methodName); + return (assembly, nameSpace, shortClassName, methodName); } } } diff --git a/src/mono/browser/debugger/tests/debugger-test/debugger-main.js b/src/mono/browser/debugger/tests/debugger-test/debugger-main.js index 58688dfc8a470e..f245ff64a33a2f 100644 --- a/src/mono/browser/debugger/tests/debugger-test/debugger-main.js +++ b/src/mono/browser/debugger/tests/debugger-test/debugger-main.js @@ -44,7 +44,7 @@ try { try { const monoMethodPtr = App.exports.DebuggerTests.BindStaticMethod.GetMonoMethodPtr(method_name); // this is only implemented for void methods with no arguments - const invoker = runtime.Module.cwrap("mono_wasm_invoke_method", "number", ["number", "number", "number"]); + const invoker = runtime.Module.cwrap("mono_wasm_invoke_method", "void", ["number", "number"]); return function () { try { return invoker(monoMethodPtr, 0, 0); diff --git a/src/mono/browser/runtime/corebindings.c b/src/mono/browser/runtime/corebindings.c index 68e8c6c7097660..978e7fd403830d 100644 --- a/src/mono/browser/runtime/corebindings.c +++ b/src/mono/browser/runtime/corebindings.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "wasm-config.h" @@ -24,10 +25,15 @@ extern void mono_wasm_resolve_or_reject_promise (void *data); extern void mono_wasm_cancel_promise (int task_holder_gc_handle); extern void mono_wasm_console_clear (); extern void mono_wasm_set_entrypoint_breakpoint (int entry_point_metadata_token); +extern void mono_wasm_trace_logger (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data); + +extern int mono_runtime_run_module_cctor (MonoImage *image, MonoError *error); typedef void (*background_job_cb)(void); -void mono_wasm_set_entry_assembly (MonoReflectionAssembly* ref_assembly, int entry_point_metadata_token); +void mono_wasm_bind_assembly_exports (char *assembly_name); +void mono_wasm_assembly_get_entry_point (char *assembly_name, int auto_insert_breakpoint, MonoMethod **method_out); +void mono_wasm_get_assembly_export (char *assembly_name, char *namespace, char *classname, char *methodname, MonoMethod **method_out); #ifndef DISABLE_THREADS void mono_wasm_release_cs_owned_object_post (pthread_t target_tid, int js_handle); @@ -84,7 +90,9 @@ void bindings_initialize_internals (void) mono_add_internal_call ("Interop/Runtime::InvokeJSFunctionSend", mono_wasm_invoke_js_function_send); mono_add_internal_call ("Interop/Runtime::CancelPromise", mono_wasm_cancel_promise); mono_add_internal_call ("Interop/Runtime::CancelPromisePost", mono_wasm_cancel_promise_post); - mono_add_internal_call ("Interop/Runtime::SetEntryAssembly", mono_wasm_set_entry_assembly); + mono_add_internal_call ("Interop/Runtime::AssemblyGetEntryPoint", mono_wasm_assembly_get_entry_point); + mono_add_internal_call ("Interop/Runtime::BindAssemblyExports", mono_wasm_bind_assembly_exports); + mono_add_internal_call ("Interop/Runtime::GetAssemblyExport", mono_wasm_get_assembly_export); #else mono_add_internal_call ("Interop/Runtime::ReleaseCSOwnedObject", mono_wasm_release_cs_owned_object); mono_add_internal_call ("Interop/Runtime::ResolveOrRejectPromise", mono_wasm_resolve_or_reject_promise); @@ -92,7 +100,9 @@ void bindings_initialize_internals (void) mono_add_internal_call ("Interop/Runtime::InvokeJSImport", mono_wasm_invoke_js_import); mono_add_internal_call ("Interop/Runtime::InvokeJSFunction", mono_wasm_invoke_js_function); mono_add_internal_call ("Interop/Runtime::CancelPromise", mono_wasm_cancel_promise); - mono_add_internal_call ("Interop/Runtime::SetEntryAssembly", mono_wasm_set_entry_assembly); + mono_add_internal_call ("Interop/Runtime::AssemblyGetEntryPoint", mono_wasm_assembly_get_entry_point); + mono_add_internal_call ("Interop/Runtime::BindAssemblyExports", mono_wasm_bind_assembly_exports); + mono_add_internal_call ("Interop/Runtime::GetAssemblyExport", mono_wasm_get_assembly_export); #endif /* DISABLE_THREADS */ mono_add_internal_call ("Interop/JsGlobalization::ChangeCaseInvariant", mono_wasm_change_case_invariant); @@ -108,14 +118,145 @@ void bindings_initialize_internals (void) mono_add_internal_call ("System.ConsolePal::Clear", mono_wasm_console_clear); } -void mono_wasm_set_entry_assembly (MonoReflectionAssembly* ref_assembly, int entry_point_metadata_token) +static MonoAssembly* _mono_wasm_assembly_load (char *assembly_name) +{ + assert (assembly_name); + MonoImageOpenStatus status; + MonoAssemblyName* aname = mono_assembly_name_new (assembly_name); + assert (aname); + + MonoAssembly *res = mono_assembly_load (aname, NULL, &status); + mono_assembly_name_free (aname); + free (assembly_name); + + return res; +} + +void mono_wasm_assembly_get_entry_point (char *assembly_name, int auto_insert_breakpoint, MonoMethod **method_out) { - MonoAssembly *assembly = mono_reflection_assembly_get_assembly (ref_assembly); + assert (assembly_name); + *method_out = NULL; + MonoAssembly* assembly = _mono_wasm_assembly_load (assembly_name); + if(!assembly) + goto end; + + MonoImage *image; + MonoMethod *method = NULL; + + image = mono_assembly_get_image (assembly); + uint32_t entry = mono_image_get_entry_point (image); + if (!entry) + goto end; + mono_domain_ensure_entry_assembly (mono_get_root_domain (), assembly); - if (entry_point_metadata_token != 0) + method = mono_get_method (image, entry, NULL); + + /* + * If the entry point looks like a compiler generated wrapper around + * an async method in the form "" then try to look up the async methods + * "$" and "Name" it could be wrapping. We do this because the generated + * sync wrapper will call task.GetAwaiter().GetResult() when we actually want + * to yield to the host runtime. + */ + if (mono_method_get_flags (method, NULL) & 0x0800 /* METHOD_ATTRIBUTE_SPECIAL_NAME */) { + const char *name = mono_method_get_name (method); + int name_length = strlen (name); + + if ((*name != '<') || (name [name_length - 1] != '>')) + goto end; + + MonoClass *klass = mono_method_get_class (method); + assert(klass); + char *async_name = malloc (name_length + 2); + snprintf (async_name, name_length + 2, "%s$", name); + + // look for "$" + MonoMethodSignature *sig = mono_method_get_signature (method, image, mono_method_get_token (method)); + MonoMethod *async_method = mono_class_get_method_from_name (klass, async_name, mono_signature_get_param_count (sig)); + if (async_method != NULL) { + free (async_name); + method = async_method; + goto end; + } + + // look for "Name" by trimming the first and last character of "" + async_name [name_length - 1] = '\0'; + async_method = mono_class_get_method_from_name (klass, async_name + 1, mono_signature_get_param_count (sig)); + + free (async_name); + if (async_method != NULL) + method = async_method; + } + +end: + if (auto_insert_breakpoint && method) { - mono_wasm_set_entrypoint_breakpoint (entry_point_metadata_token); + mono_wasm_set_entrypoint_breakpoint(mono_method_get_token (method)); + } + *method_out = method; +} + +void mono_wasm_bind_assembly_exports (char *assembly_name) +{ + MonoError error; + MonoAssembly* assembly; + MonoImage *image; + MonoClass *klass; + MonoMethod *method; + PVOLATILE(MonoObject) temp_exc = NULL; + + assert (assembly_name); + assembly = _mono_wasm_assembly_load (assembly_name); + assert (assembly); + image = mono_assembly_get_image (assembly); + assert (image); + + klass = mono_class_from_name (image, "System.Runtime.InteropServices.JavaScript", "__GeneratedInitializer"); + if (klass) { + method = mono_class_get_method_from_name (klass, "__Register_", -1); + if (method) { + mono_runtime_invoke (method, NULL, NULL, (MonoObject **)&temp_exc); + if (temp_exc) { + PVOLATILE(MonoObject) exc2 = NULL; + store_volatile((MonoObject**)&temp_exc, (MonoObject*)mono_object_to_string ((MonoObject*)temp_exc, (MonoObject **)&exc2)); + if (exc2) { + mono_wasm_trace_logger ("jsinterop", "critical", "mono_wasm_bind_assembly_exports unexpected double fault", 1, NULL); + } else { + mono_wasm_trace_logger ("jsinterop", "critical", mono_string_to_utf8((MonoString*)temp_exc), 1, NULL); + } + abort (); + } + } } + else if (!mono_runtime_run_module_cctor(image, &error)) { + //g_print ("Failed to run module constructor due to %s\n", mono_error_get_message (error)); + } +} + +void mono_wasm_get_assembly_export (char *assembly_name, char *namespace, char *classname, char *methodname, MonoMethod **method_out) +{ + MonoError error; + MonoAssembly* assembly; + MonoImage *image; + MonoClass *klass; + MonoMethod *method=NULL; + *method_out = NULL; + + assert (assembly_name); + assembly = _mono_wasm_assembly_load (assembly_name); + assert (assembly); + image = mono_assembly_get_image (assembly); + assert (image); + + klass = mono_class_from_name (image, namespace, classname); + assert (klass); + method = mono_class_get_method_from_name (klass, methodname, -1); + assert (method); + + *method_out = method; + free (namespace); + free (classname); + free (methodname); } #ifndef DISABLE_THREADS diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c index 3f47245a71efa6..a9db7956feda4e 100644 --- a/src/mono/browser/runtime/driver.c +++ b/src/mono/browser/runtime/driver.c @@ -242,7 +242,7 @@ mono_wasm_invoke_method (MonoMethod *method, void* args) // so, if that happens, we should abort the runtime if (temp_exc) { PVOLATILE(MonoObject) exc2 = NULL; - store_volatile((MonoObject**)temp_exc, (MonoObject*)mono_object_to_string ((MonoObject*)temp_exc, (MonoObject **)&exc2)); + store_volatile((MonoObject**)&temp_exc, (MonoObject*)mono_object_to_string ((MonoObject*)temp_exc, (MonoObject **)&exc2)); if (exc2) { mono_wasm_trace_logger ("jsinterop", "critical", "mono_wasm_invoke_method unexpected double fault", 1, NULL); } else { diff --git a/src/mono/browser/runtime/invoke-cs.ts b/src/mono/browser/runtime/invoke-cs.ts index 1b0338180ebffe..5127ee0a4f3a49 100644 --- a/src/mono/browser/runtime/invoke-cs.ts +++ b/src/mono/browser/runtime/invoke-cs.ts @@ -11,19 +11,16 @@ import { get_sig, get_signature_argument_count, bound_cs_function_symbol, get_signature_version, alloc_stack_frame, get_signature_type, } from "./marshal"; -import { MonoMethod, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, MarshalerType, MonoAssembly } from "./types/internal"; -import cwraps from "./cwraps"; +import { MonoMethod, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, MarshalerType } from "./types/internal"; import { assert_js_interop } from "./invoke-js"; import { startMeasure, MeasuredBlock, endMeasure } from "./profiler"; import { bind_assembly_exports, invoke_sync_method } from "./managed-exports"; import { mono_log_debug } from "./logging"; -const _assembly_cache_by_name = new Map(); - export function mono_wasm_bind_cs_function(method: MonoMethod, assemblyName: string, namespaceName: string, shortClassName: string, methodName: string, signatureHash: number, signature: JSFunctionSignature): void { const fullyQualifiedName = `[${assemblyName}] ${namespaceName}.${shortClassName}:${methodName}`; const mark = startMeasure(); - mono_log_debug(`Binding [JSExport] ${assemblyName}.${shortClassName} from ${assemblyName} assembly`); + mono_log_debug(`Binding [JSExport] ${namespaceName}.${shortClassName}:${methodName} from ${assemblyName} assembly`); const version = get_signature_version(signature); mono_assert(version === 2, () => `Signature version ${version} mismatch.`); @@ -357,12 +354,3 @@ export async function mono_wasm_get_assembly_exports(assembly: string): Promise< return exportsByAssembly.get(assembly) || {}; } - -export function assembly_load(name: string): MonoAssembly { - if (_assembly_cache_by_name.has(name)) - return _assembly_cache_by_name.get(name); - - const result = cwraps.mono_wasm_assembly_load(name); - _assembly_cache_by_name.set(name, result); - return result; -} \ No newline at end of file diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts index 9a140ac0f6c70a..b41febf9c649fe 100644 --- a/src/mono/browser/runtime/managed-exports.ts +++ b/src/mono/browser/runtime/managed-exports.ts @@ -7,12 +7,13 @@ import { GCHandle, GCHandleNull, JSMarshalerArguments, MarshalerToCs, MarshalerT import cwraps from "./cwraps"; import { runtimeHelpers, Module, loaderHelpers, mono_assert } from "./globals"; import { JavaScriptMarshalerArgSize, alloc_stack_frame, get_arg, get_arg_gc_handle, is_args_exception, set_arg_intptr, set_arg_type, set_gc_handle } from "./marshal"; -import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_bool_to_cs, marshal_exception_to_cs, marshal_string_to_cs } from "./marshal-to-cs"; +import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_bool_to_cs, marshal_exception_to_cs, marshal_intptr_to_cs, marshal_string_to_cs } from "./marshal-to-cs"; import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begin_marshal_task_to_js, marshal_exception_to_js } from "./marshal-to-js"; import { do_not_force_dispose } from "./gc-handles"; import { assert_c_interop, assert_js_interop } from "./invoke-js"; import { mono_wasm_main_thread_ptr } from "./pthreads/shared"; import { _zero_region } from "./memory"; +import { stringToUTF8Ptr } from "./strings"; const managedExports: ManagedExports = {} as any; @@ -41,7 +42,7 @@ export function init_managed_exports(): void { managedExports.LoadLazyAssembly = get_method("LoadLazyAssembly"); } -// the marshaled signature is: Task? CallEntrypoint(string mainAssemblyName, string[] args) +// the marshaled signature is: Task? CallEntrypoint(char* mainAssemblyName, string[] args) export function call_entry_point(main_assembly_name: string, program_args: string[] | undefined, waitForDebugger: boolean): Promise { loaderHelpers.assert_runtime_running(); const sp = Module.stackSave(); @@ -51,7 +52,8 @@ export function call_entry_point(main_assembly_name: string, program_args: strin const arg1 = get_arg(args, 2); const arg2 = get_arg(args, 3); const arg3 = get_arg(args, 4); - marshal_string_to_cs(arg1, main_assembly_name); + const main_assembly_name_ptr = stringToUTF8Ptr(main_assembly_name); + marshal_intptr_to_cs(arg1, main_assembly_name_ptr); marshal_array_to_cs_impl(arg2, program_args && !program_args.length ? undefined : program_args, MarshalerType.String); marshal_bool_to_cs(arg3, waitForDebugger); diff --git a/src/mono/browser/runtime/marshal-to-js.ts b/src/mono/browser/runtime/marshal-to-js.ts index d3987586b01d4d..b58201080b49e9 100644 --- a/src/mono/browser/runtime/marshal-to-js.ts +++ b/src/mono/browser/runtime/marshal-to-js.ts @@ -59,7 +59,7 @@ export function initialize_marshalers_to_js(): void { } export function bind_arg_marshal_to_js(sig: JSMarshalerType, marshaler_type: MarshalerType, index: number): BoundMarshalerToJs | undefined { - if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void) { + if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void || marshaler_type === MarshalerType.Discard) { return undefined; } diff --git a/src/mono/browser/runtime/strings.ts b/src/mono/browser/runtime/strings.ts index ae73fe57ff50d0..d6fbe3a12898a1 100644 --- a/src/mono/browser/runtime/strings.ts +++ b/src/mono/browser/runtime/strings.ts @@ -5,7 +5,7 @@ import { mono_wasm_new_root, mono_wasm_new_root_buffer } from "./roots"; import { MonoString, MonoStringNull, WasmRoot, WasmRootBuffer } from "./types/internal"; import { Module } from "./globals"; import cwraps from "./cwraps"; -import { isSharedArrayBuffer, localHeapViewU8, getU32_local, setU16_local, localHeapViewU32, getU16_local, localHeapViewU16 } from "./memory"; +import { isSharedArrayBuffer, localHeapViewU8, getU32_local, setU16_local, localHeapViewU32, getU16_local, localHeapViewU16, _zero_region } from "./memory"; import { NativePointer, CharPtr } from "./types/emscripten"; export const interned_js_string_table = new Map(); @@ -44,6 +44,15 @@ export function stringToUTF8(str: string): Uint8Array { return _text_encoder_utf8.encode(str); } +export function stringToUTF8Ptr(str: string): CharPtr { + const bytes = str.length * 2; + const ptr = Module._malloc(bytes) as any; + _zero_region(ptr, str.length * 2); + const buffer = localHeapViewU8().subarray(ptr, ptr + bytes); + buffer.set(stringToUTF8(str)); + return ptr; +} + export function utf8ToStringRelaxed(buffer: Uint8Array): string { if (_text_decoder_utf8_relaxed === undefined) { return Module.UTF8ArrayToString(buffer, 0, buffer.byteLength); diff --git a/src/mono/wasi/mono-include/driver.h b/src/mono/wasi/mono-include/driver.h index 47c72f146f7c19..8931972755a6aa 100644 --- a/src/mono/wasi/mono-include/driver.h +++ b/src/mono/wasi/mono-include/driver.h @@ -11,7 +11,6 @@ MonoAssembly* mono_wasm_assembly_load(const char *name); MonoMethod* mono_wasi_assembly_get_entry_point (MonoAssembly *assembly); MonoClass* mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, const char *name); MonoMethod* mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int arguments); -MonoObject* mono_wasm_invoke_method (MonoMethod *method, MonoObject *this_arg, void *params[], MonoObject **out_exc); int mono_unbox_int (MonoObject *obj); void add_assembly(const char* base_dir, const char *name); From 8229e0054e047087a7c4065a707f42574f7a7732 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:01:14 -0500 Subject: [PATCH 081/158] Intrinsify LoadAndInsertScalar (#98532) --- .../Arm/AdvSimd.PlatformNotSupported.cs | 8 -- .../System/Runtime/Intrinsics/Arm/AdvSimd.cs | 9 +- .../ref/System.Runtime.Intrinsics.cs | 6 - src/mono/mono/mini/llvm-intrinsics.h | 7 + src/mono/mono/mini/mini-llvm.c | 123 +++++++++++++++--- src/mono/mono/mini/mini-ops.h | 4 +- src/mono/mono/mini/simd-intrinsics.c | 12 +- .../GenerateHWIntrinsicTests_Arm.cs | 104 ++++++++------- 8 files changed, 177 insertions(+), 96 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs index 73efb3377b9014..250e847b1b5c1e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs @@ -1550,9 +1550,6 @@ internal Arm64() { } /// public static Vector128 InsertSelectedScalar(Vector128 result, [ConstantExpected(Max = (byte)(1))] byte resultIndex, Vector128 value, [ConstantExpected(Max = (byte)(1))] byte valueIndex) { throw new PlatformNotSupportedException(); } -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - /// /// A64: LD2 { Vn.16B, Vn+1.16B }[Vm], [Xn] /// @@ -1702,7 +1699,6 @@ internal Arm64() { } /// A64: LD4 { Vn.2D, Vn+1.2D, Vn+2.2D, Vn+3.2D }[Vm], [Xn] /// public static unsafe (Vector128 Value1, Vector128 Value2, Vector128 Value3, Vector128 Value4) LoadAndInsertScalar((Vector128, Vector128, Vector128, Vector128) values, [ConstantExpected(Max = (byte)(1))] byte index, double* address) { throw new PlatformNotSupportedException(); } -#endif /// /// float64x2_t vld1q_dup_f64 (float64_t const * ptr) @@ -9168,9 +9164,6 @@ internal Arm64() { } /// public static unsafe Vector128 LoadAndInsertScalar(Vector128 value, [ConstantExpected(Max = (byte)(1))] byte index, ulong* address) { throw new PlatformNotSupportedException(); } -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - /// /// A64: LD2 { Vn.8B, Vn+1.8B }[Vm], [Xn] /// @@ -9275,7 +9268,6 @@ internal Arm64() { } /// A64: LD4 { Vn.2S, Vn+1.2S, Vn+2.2S, Vn+3.2S }[Vm], [Xn] /// public static unsafe (Vector64 Value1, Vector64 Value2, Vector64 Value3, Vector64 Value4) LoadAndInsertScalar((Vector64, Vector64, Vector64, Vector64) values, [ConstantExpected(Max = (byte)(1))] byte index, float* address) { throw new PlatformNotSupportedException(); } -#endif /// /// uint8x8_t vld1_dup_u8 (uint8_t const * ptr) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs index d3f60f70b3f64c..1bd012596acfed 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs @@ -1548,9 +1548,6 @@ internal Arm64() { } /// public static Vector128 InsertSelectedScalar(Vector128 result, [ConstantExpected(Max = (byte)(1))] byte resultIndex, Vector128 value, [ConstantExpected(Max = (byte)(1))] byte valueIndex) => Insert(result, resultIndex, Extract(value, valueIndex)); -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - /// /// A64: LD2 { Vn.16B, Vn+1.16B }[Vm], [Xn] /// @@ -1700,7 +1697,7 @@ internal Arm64() { } /// A64: LD4 { Vn.2D, Vn+1.2D, Vn+2.2D, Vn+3.2D }[Vm], [Xn] /// public static unsafe (Vector128 Value1, Vector128 Value2, Vector128 Value3, Vector128 Value4) LoadAndInsertScalar((Vector128, Vector128, Vector128, Vector128) values, [ConstantExpected(Max = (byte)(1))] byte index, double* address) => LoadAndInsertScalar(values, index, address); -#endif + /// /// float64x2_t vld1q_dup_f64 (float64_t const * ptr) /// A64: LD1R { Vt.2D }, [Xn] @@ -9164,9 +9161,6 @@ internal Arm64() { } /// public static unsafe Vector128 LoadAndInsertScalar(Vector128 value, [ConstantExpected(Max = (byte)(1))] byte index, ulong* address) => LoadAndInsertScalar(value, index, address); -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - /// /// A64: LD2 { Vn.8B, Vn+1.8B }[Vm], [Xn] /// @@ -9271,7 +9265,6 @@ internal Arm64() { } /// A64: LD4 { Vn.2S, Vn+1.2S, Vn+2.2S, Vn+3.2S }[Vm], [Xn] /// public static unsafe (Vector64 Value1, Vector64 Value2, Vector64 Value3, Vector64 Value4) LoadAndInsertScalar((Vector64, Vector64, Vector64, Vector64) values, [ConstantExpected(Max = (byte)(1))] byte index, float* address) => LoadAndInsertScalar(values, index, address); -#endif /// /// uint8x8_t vld1_dup_u8 (uint8_t const * ptr) diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index 48beef8f254073..3dcd043d38ce02 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -1916,8 +1916,6 @@ internal AdvSimd() { } public static unsafe System.Runtime.Intrinsics.Vector64 LoadAndInsertScalar(System.Runtime.Intrinsics.Vector64 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index, float* address) { throw null; } public static unsafe System.Runtime.Intrinsics.Vector64 LoadAndInsertScalar(System.Runtime.Intrinsics.Vector64 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(3))] byte index, ushort* address) { throw null; } public static unsafe System.Runtime.Intrinsics.Vector64 LoadAndInsertScalar(System.Runtime.Intrinsics.Vector64 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index, uint* address) { throw null; } -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 public static unsafe (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(7))] byte index, byte* address) { throw null; } public static unsafe (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(7))] byte index, sbyte* address) { throw null; } public static unsafe (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(3))] byte index, short* address) { throw null; } @@ -1939,7 +1937,6 @@ internal AdvSimd() { } public static unsafe (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2, System.Runtime.Intrinsics.Vector64 Value3, System.Runtime.Intrinsics.Vector64 Value4) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index, int* address) { throw null; } public static unsafe (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2, System.Runtime.Intrinsics.Vector64 Value3, System.Runtime.Intrinsics.Vector64 Value4) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index, uint* address) { throw null; } public static unsafe (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2, System.Runtime.Intrinsics.Vector64 Value3, System.Runtime.Intrinsics.Vector64 Value4) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64, System.Runtime.Intrinsics.Vector64) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index, float* address) { throw null; } -#endif public static unsafe System.Runtime.Intrinsics.Vector128 LoadAndReplicateToVector128(byte* address) { throw null; } public static unsafe System.Runtime.Intrinsics.Vector128 LoadAndReplicateToVector128(short* address) { throw null; } public static unsafe System.Runtime.Intrinsics.Vector128 LoadAndReplicateToVector128(int* address) { throw null; } @@ -3341,8 +3338,6 @@ internal Arm64() { } public static System.Runtime.Intrinsics.Vector64 InsertSelectedScalar(System.Runtime.Intrinsics.Vector64 result, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(3))] byte resultIndex, System.Runtime.Intrinsics.Vector64 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(3))] byte valueIndex) { throw null; } public static System.Runtime.Intrinsics.Vector64 InsertSelectedScalar(System.Runtime.Intrinsics.Vector64 result, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte resultIndex, System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(3))] byte valueIndex) { throw null; } public static System.Runtime.Intrinsics.Vector64 InsertSelectedScalar(System.Runtime.Intrinsics.Vector64 result, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte resultIndex, System.Runtime.Intrinsics.Vector64 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte valueIndex) { throw null; } -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 public static unsafe (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(15))] byte index, byte* address) { throw null; } public static unsafe (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(15))] byte index, sbyte* address) { throw null; } public static unsafe (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(7))] byte index, short* address) { throw null; } @@ -3373,7 +3368,6 @@ internal Arm64() { } public static unsafe (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2, System.Runtime.Intrinsics.Vector128 Value3, System.Runtime.Intrinsics.Vector128 Value4) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index, ulong* address) { throw null; } public static unsafe (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2, System.Runtime.Intrinsics.Vector128 Value3, System.Runtime.Intrinsics.Vector128 Value4) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(3))] byte index, float* address) { throw null; } public static unsafe (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2, System.Runtime.Intrinsics.Vector128 Value3, System.Runtime.Intrinsics.Vector128 Value4) LoadAndInsertScalar((System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128, System.Runtime.Intrinsics.Vector128) values, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index, double* address) { throw null; } -#endif public static unsafe System.Runtime.Intrinsics.Vector128 LoadAndReplicateToVector128(double* address) { throw null; } public static unsafe System.Runtime.Intrinsics.Vector128 LoadAndReplicateToVector128(long* address) { throw null; } public static unsafe System.Runtime.Intrinsics.Vector128 LoadAndReplicateToVector128(ulong* address) { throw null; } diff --git a/src/mono/mono/mini/llvm-intrinsics.h b/src/mono/mono/mini/llvm-intrinsics.h index 559c4ec66e1d9d..60aba3795723b5 100644 --- a/src/mono/mono/mini/llvm-intrinsics.h +++ b/src/mono/mono/mini/llvm-intrinsics.h @@ -380,6 +380,13 @@ INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD4R_V64, aarch64_neon_ld4r, Arm64, AddPoi INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD2R_V128, aarch64_neon_ld2r, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD3R_V128, aarch64_neon_ld3r, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD4R_V128, aarch64_neon_ld4r, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD2LANE_V64, aarch64_neon_ld2lane, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD3LANE_V64, aarch64_neon_ld3lane, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD4LANE_V64, aarch64_neon_ld4lane, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD2LANE_V128, aarch64_neon_ld2lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD3LANE_V128, aarch64_neon_ld3lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD4LANE_V128, aarch64_neon_ld4lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) + INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_SMAXV, aarch64_neon_smaxv, Arm64, Across, V64 | V128 | I1 | I2 | I4) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_UMAXV, aarch64_neon_umaxv, Arm64, Across, V64 | V128 | I1 | I2 | I4) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_SMINV, aarch64_neon_sminv, Arm64, Across, V64 | V128 | I1 | I2 | I4) diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 5998f24b32ea9e..dff8828b6cd0a2 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -659,6 +659,8 @@ get_vtype_size_align (MonoType *t) return ret; } +static LLVMTypeRef simd_valuetuple_to_llvm_type (EmitContext *ctx, MonoClass *klass); + /* * simd_class_to_llvm_type: * @@ -667,26 +669,28 @@ get_vtype_size_align (MonoType *t) static LLVMTypeRef simd_class_to_llvm_type (EmitContext *ctx, MonoClass *klass) { - guint32 nelems; - MonoTypeEnum type = mini_get_simd_type_info (klass, &nelems); - - return LLVMVectorType (primitive_type_to_llvm_type (type), nelems); + const char *klass_name = m_class_get_name (klass); + if (strstr (klass_name, "ValueTuple") != NULL) { + return simd_valuetuple_to_llvm_type (ctx, klass); + } else { + guint32 nelems; + MonoTypeEnum type = mini_get_simd_type_info (klass, &nelems); + return LLVMVectorType (primitive_type_to_llvm_type (type), nelems); + } + g_assert_not_reached (); } static LLVMTypeRef simd_valuetuple_to_llvm_type (EmitContext *ctx, MonoClass *klass) { const char *klass_name = m_class_get_name (klass); - if (strstr (klass_name, "ValueTuple") != NULL) { - MonoGenericInst *classInst = mono_class_get_generic_class (klass)->context.class_inst; - MonoType *etype = classInst->type_argv [0]; - if (etype->type != MONO_TYPE_GENERICINST) - g_assert_not_reached (); - MonoClass *eklass = etype->data.generic_class->cached_class; - LLVMTypeRef ltype = simd_class_to_llvm_type (ctx, eklass); - return LLVMArrayType (ltype, classInst->type_argc); - } - g_assert_not_reached (); + g_assert (strstr (klass_name, "ValueTuple") != NULL); + MonoGenericInst *classInst = mono_class_get_generic_class (klass)->context.class_inst; + MonoType *etype = classInst->type_argv [0]; + g_assert (etype->type == MONO_TYPE_GENERICINST); + MonoClass *eklass = etype->data.generic_class->cached_class; + LLVMTypeRef ltype = simd_class_to_llvm_type (ctx, eklass); + return LLVMArrayType (ltype, classInst->type_argc); } /* Return the 128 bit SIMD type corresponding to the mono type TYPE */ @@ -11575,6 +11579,93 @@ MONO_RESTORE_WARNING values [ins->dreg] = result; break; } + case OP_ARM64_LDM_INSERT: { + LLVMTypeRef ret_t = simd_class_to_llvm_type (ctx, ins->klass); + LLVMTypeRef vec_t = LLVMGetElementType (ret_t); + if (!addresses [ins->dreg]) + addresses [ins->dreg] = create_address (ctx, build_named_alloca (ctx, m_class_get_byval_arg (ins->klass), "arm64_ld_insert"), ret_t); + unsigned int n_elem_tuple = LLVMGetArrayLength (ret_t); + unsigned int n_elem_vector = LLVMGetVectorSize (vec_t); + LLVMTypeRef elem_t = LLVMGetElementType (vec_t); + unsigned int elem_bits = mono_llvm_get_prim_size_bits (elem_t); + unsigned int vector_size = n_elem_vector * elem_bits; + IntrinsicId iid; + switch (vector_size) { + case 64: { + switch (n_elem_tuple) { + case 2: + iid = INTRINS_AARCH64_ADV_SIMD_LD2LANE_V64; + break; + case 3: + iid = INTRINS_AARCH64_ADV_SIMD_LD3LANE_V64; + break; + case 4: + iid = INTRINS_AARCH64_ADV_SIMD_LD4LANE_V64; + break; + default: + g_assert_not_reached (); + break; + } + break; + } + case 128: { + switch (n_elem_tuple) { + case 2: + iid = INTRINS_AARCH64_ADV_SIMD_LD2LANE_V128; + break; + case 3: + iid = INTRINS_AARCH64_ADV_SIMD_LD3LANE_V128; + break; + case 4: + iid = INTRINS_AARCH64_ADV_SIMD_LD4LANE_V128; + break; + default: + g_assert_not_reached (); + break; + } + break; + } + default: + g_assert_not_reached (); + break; + + } + + lhs = LLVMBuildLoad2 (builder, ret_t, addresses [ins->sreg1]->value, ""); + + LLVMValueRef *args = g_new0(LLVMValueRef, n_elem_tuple + 2); + int idx = 0; + for ( ; idx < n_elem_tuple; idx++) { + args [idx] = LLVMBuildExtractValue (builder, lhs, idx, "extract_elem"); + } + args [idx++] = rhs; + args [idx] = arg3; + + llvm_ovr_tag_t ovr_tag = ovr_tag_from_llvm_type (vec_t); + + // convert rhs to a constant + ImmediateUnrollCtx ictx = immediate_unroll_begin (ctx, bb, 16, rhs, ret_t, ""); + int i = 0; + while (immediate_unroll_next (&ictx, &i)) { + LLVMValueRef retval = LLVMGetUndef (ret_t); + args [idx - 1] = const_int64 (i); + LLVMValueRef result_loaded = call_overloaded_intrins (ctx, iid, ovr_tag, args, ""); + for (int j = 0; j < n_elem_tuple; j++) { + LLVMValueRef elem = LLVMBuildExtractValue (builder, result_loaded, j, "extract_elem"); + retval = LLVMBuildInsertValue (builder, retval, elem, j, "insert_val"); + } + immediate_unroll_commit (&ictx, i, retval); + } + immediate_unroll_default (&ictx); + immediate_unroll_commit_default (&ictx, LLVMConstNull (ret_t)); + LLVMValueRef result = immediate_unroll_end (&ictx, &cbb); + + LLVMTypeRef retptr_t = pointer_type (ret_t); + LLVMValueRef dst = convert (ctx, addresses [ins->dreg]->value, retptr_t); + LLVMBuildStore (builder, result, dst); + values [ins->dreg] = result; + break; + } case OP_ARM64_LD1R: case OP_ARM64_LD1: { gboolean replicate = ins->opcode == OP_ARM64_LD1R; @@ -11614,7 +11705,7 @@ MONO_RESTORE_WARNING LLVMTypeRef etype = type_to_llvm_type (ctx, m_class_get_byval_arg (ins->klass)); addresses [ins->dreg] = create_address (ctx, build_named_alloca (ctx, m_class_get_byval_arg (ins->klass), oname), etype); } - LLVMTypeRef ret_t = simd_valuetuple_to_llvm_type (ctx, ins->klass); + LLVMTypeRef ret_t = simd_class_to_llvm_type (ctx, ins->klass); LLVMTypeRef vec_t = LLVMGetElementType (ret_t); LLVMValueRef ix = const_int32 (1); LLVMTypeRef e_t = scalar ? LLVMGetElementType (vec_t) : vec_t; @@ -11645,7 +11736,7 @@ MONO_RESTORE_WARNING } case OP_ARM64_LDM: { const char *oname = "arm64_ldm"; - LLVMTypeRef ret_t = simd_valuetuple_to_llvm_type (ctx, ins->klass); + LLVMTypeRef ret_t = simd_class_to_llvm_type (ctx, ins->klass); if (!addresses [ins->dreg]) addresses [ins->dreg] = create_address (ctx, build_named_alloca (ctx, m_class_get_byval_arg (ins->klass), oname), ret_t); LLVMTypeRef vec_t = LLVMGetElementType (ret_t); diff --git a/src/mono/mono/mini/mini-ops.h b/src/mono/mono/mini/mini-ops.h index 18ede2c0a01e48..1ad42c16d676ef 100644 --- a/src/mono/mono/mini/mini-ops.h +++ b/src/mono/mono/mini/mini-ops.h @@ -1629,7 +1629,9 @@ MINI_OP(OP_LSCNT64, "lscnt64", LREG, LREG, NONE) MINI_OP(OP_ARM64_CLZ, "arm64_clz", XREG, XREG, NONE) -MINI_OP3(OP_ARM64_LD1_INSERT, "arm64_ld1_insert", XREG, IREG, XREG, IREG) +MINI_OP3(OP_ARM64_LD1_INSERT, "arm64_ld1_insert", XREG, XREG, IREG, IREG) +MINI_OP3(OP_ARM64_LDM_INSERT, "arm64_ldm_insert", VREG, VREG, IREG, IREG) + MINI_OP(OP_ARM64_LD1, "arm64_ld1", XREG, IREG, NONE) MINI_OP(OP_ARM64_LD1R, "arm64_ld1r", XREG, IREG, NONE) diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index ec7e6794a73d4a..2c99dccd9f3380 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -4146,10 +4146,14 @@ emit_arm64_intrinsics ( ins->inst_c1 = arg0_type; return ins; } - case SN_LoadAndInsertScalar: - if (!is_intrinsics_vector_type (fsig->params [0])) - return NULL; - return emit_simd_ins_for_sig (cfg, klass, OP_ARM64_LD1_INSERT, 0, arg0_type, fsig, args); + case SN_LoadAndInsertScalar: { + int load_op; + if (is_intrinsics_vector_type (fsig->params [0])) + load_op = OP_ARM64_LD1_INSERT; + else + load_op = OP_ARM64_LDM_INSERT; + return emit_simd_ins_for_sig (cfg, klass, load_op, 0, arg0_type, fsig, args); + } case SN_InsertSelectedScalar: case SN_InsertScalar: case SN_Insert: { diff --git a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs index 92b39ef43c7816..8f47895a33408c 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs @@ -714,28 +714,27 @@ ("LoadAndInsertScalarTest.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128_UInt16_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.Insert(firstOp, ElementIndex, thirdOp, i) != result[i]"}), ("LoadAndInsertScalarTest.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128_UInt32_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.Insert(firstOp, ElementIndex, thirdOp, i) != result[i]"}), ("LoadAndInsertScalarTest.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128_UInt64_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.Insert(firstOp, ElementIndex, thirdOp, i) != result[i]"}), - // Tests disabled until mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_Byte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_SByte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_Int16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_UInt16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_Int32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_UInt32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_Single_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i]))"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_Byte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_SByte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_Int16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_UInt16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_Int32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_UInt32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_Single_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.SingleToInt32Bits(result3[i]))"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_Byte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_SByte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_Int16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_UInt16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_Int32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_UInt32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_Single_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.SingleToInt32Bits(result3[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input4, ElementIndex, newData[3], i)) != BitConverter.SingleToInt32Bits(result4[i]))"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_Byte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_SByte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_Int16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_UInt16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_Int32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_UInt32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x2_Single_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i]))"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_Byte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_SByte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_Int16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_UInt16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_Int32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_UInt32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x3_Single_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.SingleToInt32Bits(result3[i]))"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_Byte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_SByte_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_Int16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_UInt16_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_Int32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_UInt32_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector64x4_Single_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.SingleToInt32Bits(result3[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input4, ElementIndex, newData[3], i)) != BitConverter.SingleToInt32Bits(result4[i]))"}), ("LoadUnOpTest.template", new Dictionary { ["TestName"] = "LoadAndReplicateToVector64_Byte", ["Isa"] = "AdvSimd", ["Method"] = "LoadAndReplicateToVector64", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "firstOp[0] != result[i]"}), ("LoadUnOpTest.template", new Dictionary { ["TestName"] = "LoadAndReplicateToVector64_Int16", ["Isa"] = "AdvSimd", ["Method"] = "LoadAndReplicateToVector64", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "firstOp[0] != result[i]"}), ("LoadUnOpTest.template", new Dictionary { ["TestName"] = "LoadAndReplicateToVector64_Int32", ["Isa"] = "AdvSimd", ["Method"] = "LoadAndReplicateToVector64", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "firstOp[0] != result[i]"}), @@ -2139,37 +2138,36 @@ ("InsertSelectedScalarTest.template", new Dictionary { ["TestName"] = "InsertSelectedScalar_Vector128_UInt32_3_Vector128_UInt32_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "InsertSelectedScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt32", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex1"] = "3", ["ElementIndex2"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.Insert(firstOp, ElementIndex1, thirdOp[ElementIndex2], i) != result[i]"}), ("InsertSelectedScalarTest.template", new Dictionary { ["TestName"] = "InsertSelectedScalar_Vector128_UInt64_1_Vector128_UInt64_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "InsertSelectedScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt64", ["Op3VectorType"] = "Vector128", ["Op3BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ElementIndex1"] = "1", ["ElementIndex2"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.Insert(firstOp, ElementIndex1, thirdOp[ElementIndex2], i) != result[i]"}), ("LoadUnOpTest.template", new Dictionary { ["TestName"] = "LoadAndReplicateToVector128_Double", ["Isa"] = "AdvSimd.Arm64", ["Method"] = "LoadAndReplicateToVector128", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "BitConverter.DoubleToInt64Bits(firstOp[0]) != BitConverter.DoubleToInt64Bits(result[i])"}), - // Tests disabled until mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Byte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_SByte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Int16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_UInt16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Int32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_UInt32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Int64_0", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ElementIndex"] = "0", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_UInt64_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Single_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i]))"}), - // ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Double_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "(BitConverter.DoubleToInt64Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.DoubleToInt64Bits(result1[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.DoubleToInt64Bits(result2[i]))"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Byte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_SByte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Int16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_UInt16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Int32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_UInt32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Int64_0", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ElementIndex"] = "0", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_UInt64_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Single_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.SingleToInt32Bits(result3[i]))"}), - // ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Double_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "(BitConverter.DoubleToInt64Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.DoubleToInt64Bits(result1[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.DoubleToInt64Bits(result2[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.DoubleToInt64Bits(result3[i]))"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Byte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_SByte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Int16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_UInt16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Int32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_UInt32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Int64_0", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ElementIndex"] = "0", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_UInt64_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Single_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.SingleToInt32Bits(result3[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input4, ElementIndex, newData[3], i)) != BitConverter.SingleToInt32Bits(result4[i]))"}), - // ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Double_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "(BitConverter.DoubleToInt64Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.DoubleToInt64Bits(result1[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.DoubleToInt64Bits(result2[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.DoubleToInt64Bits(result3[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input4, ElementIndex, newData[3], i)) != BitConverter.DoubleToInt64Bits(result4[i]))"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Byte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_SByte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Int16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_UInt16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Int32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_UInt32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Int64_0", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ElementIndex"] = "0", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_UInt64_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i])"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Single_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i]))"}), + ("LoadAndInsertScalarx2Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x2_Double_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "(BitConverter.DoubleToInt64Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.DoubleToInt64Bits(result1[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.DoubleToInt64Bits(result2[i]))"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Byte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_SByte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Int16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_UInt16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Int32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_UInt32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Int64_0", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ElementIndex"] = "0", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_UInt64_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i])"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Single_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.SingleToInt32Bits(result3[i]))"}), + ("LoadAndInsertScalarx3Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x3_Double_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "(BitConverter.DoubleToInt64Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.DoubleToInt64Bits(result1[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.DoubleToInt64Bits(result2[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.DoubleToInt64Bits(result3[i]))"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Byte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_SByte_7", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "SByte", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSByte()", ["ElementIndex"] = "7", ["NextValueOp3"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Int16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_UInt16_3", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "3", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Int32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_UInt32_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Int64_0", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ElementIndex"] = "0", ["NextValueOp3"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_UInt64_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "(Helpers.Insert(input1, ElementIndex, newData[0], i) != result1[i]) || (Helpers.Insert(input2, ElementIndex, newData[1], i) != result2[i]) || (Helpers.Insert(input3, ElementIndex, newData[2], i) != result3[i]) || (Helpers.Insert(input4, ElementIndex, newData[3], i) != result4[i])"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Single_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Single", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Single", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetSingle()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "(BitConverter.SingleToInt32Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.SingleToInt32Bits(result1[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.SingleToInt32Bits(result2[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.SingleToInt32Bits(result3[i])) || (BitConverter.SingleToInt32Bits(Helpers.Insert(input4, ElementIndex, newData[3], i)) != BitConverter.SingleToInt32Bits(result4[i]))"}), + ("LoadAndInsertScalarx4Test.template", new Dictionary { ["TestName"] = "LoadAndInsertScalar_Vector128x4_Double_1", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndInsertScalar", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["ElementIndex"] = "1", ["NextValueOp3"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "(BitConverter.DoubleToInt64Bits(Helpers.Insert(input1, ElementIndex, newData[0], i)) != BitConverter.DoubleToInt64Bits(result1[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input2, ElementIndex, newData[1], i)) != BitConverter.DoubleToInt64Bits(result2[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input3, ElementIndex, newData[2], i)) != BitConverter.DoubleToInt64Bits(result3[i])) || (BitConverter.DoubleToInt64Bits(Helpers.Insert(input4, ElementIndex, newData[3], i)) != BitConverter.DoubleToInt64Bits(result4[i]))"}), ("LoadUnOpTest.template", new Dictionary { ["TestName"] = "LoadAndReplicateToVector128_Int64", ["Isa"] = "AdvSimd.Arm64", ["Method"] = "LoadAndReplicateToVector128", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Int64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "firstOp[0] != result[i]"}), ("LoadUnOpTest.template", new Dictionary { ["TestName"] = "LoadAndReplicateToVector128_UInt64", ["Isa"] = "AdvSimd.Arm64", ["Method"] = "LoadAndReplicateToVector128", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "firstOp[0] != result[i]"}), ("LoadVectorx2Test.template", new Dictionary { ["TestName"] = "LoadAndReplicateToVector128x2SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "LoadAndReplicateToVector128x2", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "result1[i] != input[0] || result2[i] != input[1]"}), From f4b0b33c7364c3261be045d0680329bcf363cd44 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Fri, 16 Feb 2024 17:01:54 +0100 Subject: [PATCH 082/158] [browser] Split JSImport/JSExport tests (#98557) --- ...me.InteropServices.JavaScript.Tests.csproj | 4 +- .../JavaScript/JSExportTest.cs | 417 ++++++++++ ...{JSImportExportTest.cs => JSImportTest.cs} | 757 ++---------------- .../JavaScript/JSInteropTestBase.cs | 340 ++++++++ .../JavaScript/JavaScriptTestHelper.cs | 7 + .../JavaScript/JavaScriptTestHelper.mjs | 31 + 6 files changed, 850 insertions(+), 706 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs rename src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/{JSImportExportTest.cs => JSImportTest.cs} (64%) create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSInteropTestBase.cs diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj index 14a51e460d913d..c3881268f2a6cd 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj @@ -21,7 +21,9 @@ - + + + diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs new file mode 100644 index 00000000000000..990e755c33e1d6 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs @@ -0,0 +1,417 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using System.Threading; +using Xunit; +using System.Diagnostics.CodeAnalysis; +#pragma warning disable xUnit1026 // Theory methods should use all of their parameters + +namespace System.Runtime.InteropServices.JavaScript.Tests +{ + public class JSExportAsyncTest : JSInteropTestBase, IAsyncLifetime + { + [Theory] + [MemberData(nameof(MarshalBooleanCases))] + public async Task JsExportBooleanAsync(bool value) + { + await JsExportTestAsync(value, + JavaScriptTestHelper.invoke1_BooleanAsync, + nameof(JavaScriptTestHelper.EchoBoolean), + "boolean"); + } + + private async Task JsExportTestAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(T value + , Func> invoke, string echoName, string jsType, string? jsClass = null) + { + T res; + res = await invoke(value, echoName); + Assert.Equal(value, res); + } + } + + //TODO [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy + public class JSExportTest : JSInteropTestBase, IAsyncLifetime + { + [Theory] + [MemberData(nameof(MarshalBooleanCases))] + public void JsExportBoolean(bool value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_Boolean, + nameof(JavaScriptTestHelper.EchoBoolean), + "boolean"); + } + + [Theory] + [MemberData(nameof(MarshalCharCases))] + public void JsExportChar(char value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_Char, + nameof(JavaScriptTestHelper.EchoChar), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalByteCases))] + public void JsExportByte(byte value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_Byte, + nameof(JavaScriptTestHelper.EchoByte), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalInt16Cases))] + public void JsExportInt16(short value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_Int16, + nameof(JavaScriptTestHelper.EchoInt16), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalInt32Cases))] + public void JsExportInt32(int value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_Int32, + nameof(JavaScriptTestHelper.EchoInt32), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalInt52Cases))] + public void JsExportInt52(long value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_Int52, + nameof(JavaScriptTestHelper.EchoInt52), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalBigInt64Cases))] + public void JsExportBigInt64(long value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_BigInt64, + nameof(JavaScriptTestHelper.EchoBigInt64), + "bigint"); + } + + [Theory] + [MemberData(nameof(MarshalDoubleCases))] + public void JsExportDouble(double value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_Double, + nameof(JavaScriptTestHelper.EchoDouble), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalSingleCases))] + public void JsExportSingle(float value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_Single, + nameof(JavaScriptTestHelper.EchoSingle), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalIntPtrCases))] + public void JsExportIntPtr(IntPtr value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_IntPtr, + nameof(JavaScriptTestHelper.EchoIntPtr), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalIntPtrCases))] + public unsafe void JsExportVoidPtr(IntPtr xvalue) + { + void* value = (void*)xvalue; + void* res = JavaScriptTestHelper.invoke1_VoidPtr(value, nameof(JavaScriptTestHelper.EchoVoidPtr)); + Assert.True(value == res); + } + + [Theory] + [MemberData(nameof(MarshalDateTimeCases))] + public void JsExportDateTime(DateTime value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_DateTime, + nameof(JavaScriptTestHelper.EchoDateTime), + "object", "Date"); + } + + + [Theory] + [MemberData(nameof(MarshalDateTimeOffsetCases))] + public void JsExportDateTimeOffset(DateTimeOffset value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_DateTimeOffset, + nameof(JavaScriptTestHelper.EchoDateTimeOffset), + "object", "Date"); + } + + [Theory] + [MemberData(nameof(MarshalNullableBooleanCases))] + public void JsExportNullableBoolean(bool? value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_NullableBoolean, + nameof(JavaScriptTestHelper.EchoNullableBoolean), + "boolean"); + } + + [Theory] + [MemberData(nameof(MarshalNullableInt32Cases))] + public void JsExportNullableInt32(int? value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_NullableInt32, + nameof(JavaScriptTestHelper.EchoNullableInt32), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalNullableBigInt64Cases))] + public void JsExportNullableBigInt64(long? value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_NullableBigInt64, + nameof(JavaScriptTestHelper.EchoNullableBigInt64), + "bigint"); + } + + [Theory] + [MemberData(nameof(MarshalNullableIntPtrCases))] + public void JsExportNullableIntPtr(IntPtr? value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_NullableIntPtr, + nameof(JavaScriptTestHelper.EchoNullableIntPtr), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalNullableDoubleCases))] + public void JsExportNullableDouble(double? value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_NullableDouble, + nameof(JavaScriptTestHelper.EchoNullableDouble), + "number"); + } + + [Theory] + [MemberData(nameof(MarshalNullableDateTimeCases))] + public void JsExportNullableDateTime(DateTime? value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_NullableDateTime, + nameof(JavaScriptTestHelper.EchoNullableDateTime), + "object"); + } + + [Theory] + [MemberData(nameof(MarshalStringCases))] + public void JsExportString(string value) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_String, + nameof(JavaScriptTestHelper.EchoString), + "string"); + } + + [Fact] + public void JsExportStringNoNs() + { + var actual = JavaScriptTestHelper.invoke2_String("test", nameof(JavaScriptTestHelperNoNamespace.EchoString)); + Assert.Equal("test51", actual); + } + + [Fact] + public void JsExportStructClassRecords() + { + var actual = JavaScriptTestHelper.invokeStructClassRecords("test"); + Assert.Equal(48, actual.Length); + Assert.Equal("test11", actual[0]); + Assert.Equal("test12", actual[1]); + Assert.Equal("test13", actual[2]); + Assert.Equal("test14", actual[3]); + Assert.Equal("test15", actual[4]); + Assert.Equal("test16", actual[5]); + Assert.Equal("test17", actual[6]); + Assert.Equal("test18", actual[7]); + Assert.Equal("test19", actual[8]); + Assert.Equal("test21", actual[9]); + Assert.Equal("test22", actual[10]); + Assert.Equal("test23", actual[11]); + Assert.Equal("test24", actual[12]); + Assert.Equal("test25", actual[13]); + Assert.Equal("test31", actual[14]); + Assert.Equal("test32", actual[15]); + Assert.Equal("test33", actual[16]); + Assert.Equal("test34", actual[17]); + Assert.Equal("test35", actual[18]); + Assert.Equal("test41", actual[19]); + Assert.Equal("test42", actual[20]); + Assert.Equal("test43", actual[21]); + Assert.Equal("test44", actual[22]); + Assert.Equal("test45", actual[23]); + Assert.Equal("test51", actual[24]); + Assert.Equal("test52", actual[25]); + Assert.Equal("test53", actual[26]); + Assert.Equal("test54", actual[27]); + Assert.Equal("test55", actual[28]); + Assert.Equal("test56", actual[29]); + Assert.Equal("test57", actual[30]); + Assert.Equal("test58", actual[31]); + Assert.Equal("test59", actual[32]); + Assert.Equal("test61", actual[33]); + Assert.Equal("test62", actual[34]); + Assert.Equal("test63", actual[35]); + Assert.Equal("test64", actual[36]); + Assert.Equal("test65", actual[37]); + Assert.Equal("test71", actual[38]); + Assert.Equal("test72", actual[39]); + Assert.Equal("test73", actual[40]); + Assert.Equal("test74", actual[41]); + Assert.Equal("test75", actual[42]); + Assert.Equal("test81", actual[43]); + Assert.Equal("test82", actual[44]); + Assert.Equal("test83", actual[45]); + Assert.Equal("test84", actual[46]); + Assert.Equal("test85", actual[47]); + } + + [Theory] + [MemberData(nameof(MarshalObjectCases))] + public void JsExportObject(object value, string clazz) + { + JsExportTest(value, + JavaScriptTestHelper.invoke1_Object, + nameof(JavaScriptTestHelper.EchoObject), + "object", clazz); + } + + [Theory] + [MemberData(nameof(MarshalExceptionCases))] + public void JsExportException(Exception value, string clazz) + { + if (clazz == "JSTestError") + { + value = JavaScriptTestHelper.createException("!CreateEx!"); + } + + JsExportTest(value, + JavaScriptTestHelper.invoke1_Exception, + nameof(JavaScriptTestHelper.EchoException), + "object", clazz); + } + + [Fact] + public void JsExportCatchToString() + { + var toString = JavaScriptTestHelper.catch1toString("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)); + Assert.DoesNotContain("Unexpected error", toString); + Assert.Contains("-t-e-s-t-", toString); + Assert.DoesNotContain(nameof(JavaScriptTestHelper.ThrowFromJSExport), toString); + } + + [Fact] + public void JsExportCatchStack() + { + var stack = JavaScriptTestHelper.catch1stack("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)); + Assert.Contains(nameof(JavaScriptTestHelper.ThrowFromJSExport), stack); + if (PlatformDetection.IsBrowserDomSupportedOrNodeJS) + { + Assert.Contains("catch1stack", stack); + } + } + + [Theory] + [MemberData(nameof(MarshalIJSObjectCases))] + public void JsExportIJSObject(JSObject value, string clazz) + { + if (clazz == "JSData") + { + value = JavaScriptTestHelper.createData("!CreateJS!"); + } + + JsExportTest(value, + JavaScriptTestHelper.invoke1_JSObject, + nameof(JavaScriptTestHelper.EchoIJSObject), + "object", clazz); + } + + [Theory] + [MemberData(nameof(MarshalInt32Cases))] + public async Task JsExportTaskOfInt(int value) + { + TaskCompletionSource tcs = new TaskCompletionSource(); + + var res = JavaScriptTestHelper.invoke1_TaskOfInt(tcs.Task, nameof(JavaScriptTestHelper.AwaitTaskOfObject)); + tcs.SetResult(value); + await Task.Yield(); + var rr = await res; + await Task.Yield(); + Assert.Equal(value, rr); + //GC.Collect(); + } + + [Fact] + public void JsExportCallback_FunctionIntInt() + { + int called = -1; + var chain = JavaScriptTestHelper.invoke1_FuncOfIntInt((int a) => + { + called = a; + return a; + }, nameof(JavaScriptTestHelper.BackFuncOfIntInt)); + + Assert.Equal(-1, called); + var actual = chain(42); + Assert.Equal(42, actual); + Assert.Equal(42, called); + } + + [Fact] + public void JsExportCallback_FunctionIntIntThrow() + { + int called = -1; + var expected = new Exception("test!!"); + var chain = JavaScriptTestHelper.invoke1_FuncOfIntInt((int a) => + { + called = a; + throw expected; + }, nameof(JavaScriptTestHelper.BackFuncOfIntInt)); + + Assert.Equal(-1, called); + var actual = Assert.Throws(() => chain(42)); + Assert.Equal(42, called); + Assert.Same(expected, actual); + } + + private void JsExportTest<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(T value + , Func invoke, string echoName, string jsType, string? jsClass = null) + { + T res; + res = invoke(value, echoName); + Assert.Equal(value, res); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs similarity index 64% rename from src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs rename to src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs index bd76d9e80f8fd2..b3c57387e46379 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportExportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs @@ -12,7 +12,7 @@ namespace System.Runtime.InteropServices.JavaScript.Tests { - public class JSImportExportTest : IAsyncLifetime + public class JSImportTest : JSInteropTestBase, IAsyncLifetime { [Fact] public unsafe void StructSize() @@ -113,7 +113,7 @@ public unsafe void OutOfRange() Assert.Contains("Overflow: value 9007199254740991 is out of -2147483648 2147483647 range", ex.Message); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public unsafe void OptimizedPaths() { JavaScriptTestHelper.optimizedReached = 0; @@ -211,13 +211,6 @@ public unsafe void CreateFunctionInternal() #region Arrays - public static IEnumerable MarshalByteArrayCases() - { - yield return new object[] { new byte[] { 1, 2, 3, byte.MaxValue, byte.MinValue } }; - yield return new object[] { new byte[] { } }; - yield return new object[] { null }; - } - [Theory] [MemberData(nameof(MarshalByteArrayCases))] public unsafe void JsImportByteArray(byte[]? expected) @@ -231,13 +224,6 @@ public unsafe void JsImportByteArray(byte[]? expected) } } - public static IEnumerable MarshalIntArrayCases() - { - yield return new object[] { new int[] { 1, 2, 3, int.MaxValue, int.MinValue } }; - yield return new object[] { new int[] { } }; - yield return new object[] { null }; - } - [Theory] [MemberData(nameof(MarshalIntArrayCases))] public unsafe void JsImportIntArray(int[]? expected) @@ -251,13 +237,6 @@ public unsafe void JsImportIntArray(int[]? expected) } } - public static IEnumerable MarshalDoubleArrayCases() - { - yield return new object[] { new double[] { 1, 2, 3, double.MaxValue, double.MinValue, double.Pi, double.NegativeInfinity, double.PositiveInfinity, double.NaN } }; - yield return new object[] { new double[] { } }; - yield return new object[] { null }; - } - [Theory] [MemberData(nameof(MarshalDoubleArrayCases))] public unsafe void JsImportDoubleArray(double[]? expected) @@ -271,14 +250,6 @@ public unsafe void JsImportDoubleArray(double[]? expected) } } - public static IEnumerable MarshalStringArrayCases() - { - yield return new object[] { new string[] { "\u0050\u0159\u00ed\u006c\u0069\u0161", "\u017e\u006c\u0075\u0165\u006f\u0075\u010d\u006b\u00fd" } }; - yield return new object[] { new string[] { string.Intern("hello"), string.Empty, null } }; - yield return new object[] { new string[] { } }; - yield return new object[] { null }; - } - [Theory] [MemberData(nameof(MarshalStringArrayCases))] public unsafe void JsImportStringArray(string[]? expected) @@ -293,28 +264,6 @@ public unsafe void JsImportStringArray(string[]? expected) } } - public class SomethingRef - { - } - - public class SomethingStruct - { - } - - public static IEnumerable MarshalObjectArrayCases() - { - yield return new object[] { new object[] { string.Intern("hello"), string.Empty } }; - yield return new object[] { new object[] { 1.1d, new DateTime(2022, 5, 8, 14, 55, 01, DateTimeKind.Utc), false, true } }; - yield return new object[] { new object[] { new double?(1.1d), new DateTime?(new DateTime(2022, 5, 8, 14, 55, 01, DateTimeKind.Utc)), new bool?(false), new bool?(true) } }; - yield return new object[] { new object[] { null, new object(), new SomethingRef(), new SomethingStruct(), new Exception("test") } }; - yield return new object[] { new object[] { "JSData" } }; // special cased, so we call createData in the test itself - yield return new object[] { new object[] { new byte[] { }, new int[] { }, new double[] { }, new string[] { }, new object[] { } } }; - yield return new object[] { new object[] { new byte[] { 1, 2, 3 }, new int[] { 1, 2, 3 }, new double[] { 1, 2, 3 }, new string[] { "a", "b", "c" }, new object[] { } } }; - yield return new object[] { new object[] { new object[] { new byte[] { 1, 2, 3 }, new int[] { 1, 2, 3 }, new double[] { 1, 2, 3 }, new string[] { "a", "b", "c" }, new object(), new SomethingRef(), new SomethingStruct(), new Exception("test") } } }; - yield return new object[] { new object[] { } }; - yield return new object[] { null }; - } - [Theory] [MemberData(nameof(MarshalObjectArrayCases))] public unsafe void JsImportObjectArray(object[]? expected) @@ -327,19 +276,10 @@ public unsafe void JsImportObjectArray(object[]? expected) Assert.Equal(expected, actual); if (expected != null) for (int i = 0; i < expected.Length; i++) - { - var actualI = JavaScriptTestHelper.store_ObjectArray(expected, i); - Assert.Equal(expected[i], actualI); - } - } - - public static IEnumerable MarshalObjectArrayCasesToDouble() - { - yield return new object[] { new object[] { (byte)42 } }; - yield return new object[] { new object[] { (short)42 } }; - yield return new object[] { new object[] { 42 } }; - yield return new object[] { new object[] { 3.14f } }; - yield return new object[] { new object[] { 'A' } }; + { + var actualI = JavaScriptTestHelper.store_ObjectArray(expected, i); + Assert.Equal(expected[i], actualI); + } } [Theory] @@ -360,25 +300,6 @@ public unsafe void JsImportObjectArrayToDouble(object[]? expected) } } - public static IEnumerable MarshalObjectArrayCasesThrow() - { - yield return new object[] { new object[] { () => { } } }; - yield return new object[] { new object[] { (int a) => { } } }; - yield return new object[] { new object[] { (int a) => { return a; } } }; - yield return new object[] { new object[] { (dummyDelegate)dummyDelegateA } }; - yield return new object[] { new object[] { 0L } }; - yield return new object[] { new object[] { 0UL } }; - yield return new object[] { new object[] { (sbyte)0 } }; - yield return new object[] { new object[] { (ushort)0 } }; - yield return new object[] { new object[] { new SomethingStruct[] { } } }; - yield return new object[] { new object[] { new SomethingRef[] { }, } }; - yield return new object[] { new object[] { new ArraySegment(new byte[] { 11 }), } }; - } - delegate void dummyDelegate(); - static void dummyDelegateA() - { - } - [Theory] [MemberData(nameof(MarshalObjectArrayCasesThrow))] public void JsImportObjectArrayThrows(object[]? expected) @@ -532,11 +453,6 @@ public unsafe void JsImportArraySegmentOfDouble() #endregion #region Boolean - public static IEnumerable MarshalBooleanCases() - { - yield return new object[] { true }; - yield return new object[] { false }; - } [Theory] [MemberData(nameof(MarshalBooleanCases))] @@ -551,28 +467,9 @@ public void JsImportBoolean(bool value) "boolean"); } - [Theory] - [MemberData(nameof(MarshalBooleanCases))] - public void JsExportBoolean(bool value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_Boolean, - nameof(JavaScriptTestHelper.EchoBoolean), - "boolean"); - } #endregion Boolean #region Char - public static IEnumerable MarshalCharCases() - { - yield return new object[] { (char)42 }; - yield return new object[] { (char)1 }; - yield return new object[] { '\u017D' }; - yield return new object[] { '\u2661' }; - yield return new object[] { char.MaxValue }; - yield return new object[] { char.MinValue }; - } - [Theory] [MemberData(nameof(MarshalCharCases))] public void JsImportChar(char value) @@ -586,26 +483,9 @@ public void JsImportChar(char value) "number"); } - [Theory] - [MemberData(nameof(MarshalCharCases))] - public void JsExportChar(char value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_Char, - nameof(JavaScriptTestHelper.EchoChar), - "number"); - } #endregion Char #region Byte - public static IEnumerable MarshalByteCases() - { - yield return new object[] { (byte)42 }; - yield return new object[] { (byte)1 }; - yield return new object[] { byte.MaxValue }; - yield return new object[] { byte.MinValue }; - } - [Theory] [MemberData(nameof(MarshalByteCases))] public void JsImportByte(byte value) @@ -619,16 +499,6 @@ public void JsImportByte(byte value) "number"); } - [Theory] - [MemberData(nameof(MarshalByteCases))] - public void JsExportByte(byte value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_Byte, - nameof(JavaScriptTestHelper.EchoByte), - "number"); - } - [Theory] [MemberData(nameof(OutOfRangeCases))] public void ByteOutOfRange(double value, string message) @@ -641,16 +511,6 @@ public void ByteOutOfRange(double value, string message) #endregion Byte #region Int16 - public static IEnumerable MarshalInt16Cases() - { - yield return new object[] { 42 }; - yield return new object[] { 0 }; - yield return new object[] { 1 }; - yield return new object[] { -1 }; - yield return new object[] { short.MaxValue }; - yield return new object[] { short.MinValue }; - } - [Theory] [MemberData(nameof(MarshalInt16Cases))] public void JsImportInt16(short value) @@ -664,38 +524,9 @@ public void JsImportInt16(short value) "number"); } - [Theory] - [MemberData(nameof(MarshalInt16Cases))] - public void JsExportInt16(short value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_Int16, - nameof(JavaScriptTestHelper.EchoInt16), - "number"); - } #endregion Int16 #region Int32 - public static IEnumerable MarshalInt32Cases() - { - yield return new object[] { 42 }; - yield return new object[] { 0 }; - yield return new object[] { 1 }; - yield return new object[] { -1 }; - yield return new object[] { int.MaxValue }; - yield return new object[] { int.MinValue }; - } - - public static IEnumerable OutOfRangeCases() - { - yield return new object[] { double.MaxValue, "Value is not an integer" }; - yield return new object[] { double.MinValue, "Value is not an integer" }; - yield return new object[] { double.NaN, "Value is not an integer" }; - yield return new object[] { double.NegativeInfinity, "Value is not an integer" }; - yield return new object[] { double.PositiveInfinity, "Value is not an integer" }; - yield return new object[] { (double)MAX_SAFE_INTEGER, "Overflow" }; - } - [Theory] [MemberData(nameof(MarshalInt32Cases))] public void JsImportInt32(int value) @@ -709,16 +540,6 @@ public void JsImportInt32(int value) "number"); } - [Theory] - [MemberData(nameof(MarshalInt32Cases))] - public void JsExportInt32(int value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_Int32, - nameof(JavaScriptTestHelper.EchoInt32), - "number"); - } - [Theory] [MemberData(nameof(OutOfRangeCases))] public void Int32OutOfRange(double value, string message) @@ -731,17 +552,6 @@ public void Int32OutOfRange(double value, string message) #endregion Int32 #region Int52 - const long MAX_SAFE_INTEGER = 9007199254740991L;// Number.MAX_SAFE_INTEGER - const long MIN_SAFE_INTEGER = -9007199254740991L;// Number.MIN_SAFE_INTEGER - public static IEnumerable MarshalInt52Cases() - { - yield return new object[] { -1 }; - yield return new object[] { 42 }; - yield return new object[] { 0 }; - yield return new object[] { 1 }; - yield return new object[] { MAX_SAFE_INTEGER }; - yield return new object[] { MIN_SAFE_INTEGER }; - } [Theory] [MemberData(nameof(MarshalInt52Cases))] @@ -756,30 +566,9 @@ public void JsImportInt52(long value) "number"); } - [Theory] - [MemberData(nameof(MarshalInt52Cases))] - public void JsExportInt52(long value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_Int52, - nameof(JavaScriptTestHelper.EchoInt52), - "number"); - } #endregion Int52 #region BigInt64 - public static IEnumerable MarshalBigInt64Cases() - { - yield return new object[] { -1 }; - yield return new object[] { 42 }; - yield return new object[] { 0 }; - yield return new object[] { 1 }; - yield return new object[] { MAX_SAFE_INTEGER }; - yield return new object[] { MIN_SAFE_INTEGER }; - yield return new object[] { long.MinValue }; - yield return new object[] { long.MaxValue }; - } - [Theory] [MemberData(nameof(MarshalBigInt64Cases))] public void JsImportBigInt64(long value) @@ -793,29 +582,9 @@ public void JsImportBigInt64(long value) "bigint"); } - [Theory] - [MemberData(nameof(MarshalBigInt64Cases))] - public void JsExportBigInt64(long value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_BigInt64, - nameof(JavaScriptTestHelper.EchoBigInt64), - "bigint"); - } #endregion BigInt64 #region Double - public static IEnumerable MarshalDoubleCases() - { - yield return new object[] { Math.PI }; - yield return new object[] { 0.0 }; - yield return new object[] { double.MaxValue }; - yield return new object[] { double.MinValue }; - yield return new object[] { double.NegativeInfinity }; - yield return new object[] { double.PositiveInfinity }; - yield return new object[] { double.NaN }; - } - [Theory] [MemberData(nameof(MarshalDoubleCases))] public void JsImportDouble(double value) @@ -829,29 +598,9 @@ public void JsImportDouble(double value) "number"); } - [Theory] - [MemberData(nameof(MarshalDoubleCases))] - public void JsExportDouble(double value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_Double, - nameof(JavaScriptTestHelper.EchoDouble), - "number"); - } #endregion Double #region Single - public static IEnumerable MarshalSingleCases() - { - yield return new object[] { (float)Math.PI }; - yield return new object[] { 0.0f }; - yield return new object[] { float.MaxValue }; - yield return new object[] { float.MinValue }; - yield return new object[] { float.NegativeInfinity }; - yield return new object[] { float.PositiveInfinity }; - yield return new object[] { float.NaN }; - } - [Theory] [MemberData(nameof(MarshalSingleCases))] public void JsImportSingle(float value) @@ -865,28 +614,9 @@ public void JsImportSingle(float value) "number"); } - [Theory] - [MemberData(nameof(MarshalSingleCases))] - public void JsExportSingle(float value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_Single, - nameof(JavaScriptTestHelper.EchoSingle), - "number"); - } #endregion Single #region IntPtr - public static IEnumerable MarshalIntPtrCases() - { - yield return new object[] { (IntPtr)42 }; - yield return new object[] { IntPtr.Zero }; - yield return new object[] { (IntPtr)1 }; - yield return new object[] { (IntPtr)(-1) }; - yield return new object[] { IntPtr.MaxValue }; - yield return new object[] { IntPtr.MinValue }; - } - [Theory] [MemberData(nameof(MarshalIntPtrCases))] public void JsImportIntPtr(IntPtr value) @@ -900,15 +630,6 @@ public void JsImportIntPtr(IntPtr value) "number"); } - [Theory] - [MemberData(nameof(MarshalIntPtrCases))] - public void JsExportIntPtr(IntPtr value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_IntPtr, - nameof(JavaScriptTestHelper.EchoIntPtr), - "number"); - } #endregion IntPtr #region VoidPtr @@ -929,24 +650,9 @@ public unsafe void JsImportVoidPtr(IntPtr xvalue) Assert.Equal("number", actualJsType); } - [Theory] - [MemberData(nameof(MarshalIntPtrCases))] - public unsafe void JsExportVoidPtr(IntPtr xvalue) - { - void* value = (void*)xvalue; - void* res = JavaScriptTestHelper.invoke1_VoidPtr(value, nameof(JavaScriptTestHelper.EchoVoidPtr)); - Assert.True(value == res); - } #endregion VoidPtr #region Datetime - public static IEnumerable MarshalDateTimeCases() - { - yield return new object[] { new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) }; - yield return new object[] { TrimNano(DateTime.UtcNow) }; - yield return new object[] { TrimNano(DateTime.MaxValue) }; - } - [Theory] [MemberData(nameof(MarshalDateTimeCases))] public void JSImportDateTime(DateTime value) @@ -960,25 +666,9 @@ public void JSImportDateTime(DateTime value) "object", "Date"); } - [Theory] - [MemberData(nameof(MarshalDateTimeCases))] - public void JsExportDateTime(DateTime value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_DateTime, - nameof(JavaScriptTestHelper.EchoDateTime), - "object", "Date"); - } #endregion Datetime #region DateTimeOffset - public static IEnumerable MarshalDateTimeOffsetCases() - { - yield return new object[] { DateTimeOffset.FromUnixTimeSeconds(0) }; - yield return new object[] { TrimNano(DateTimeOffset.UtcNow) }; - yield return new object[] { TrimNano(DateTimeOffset.MaxValue) }; - } - [Theory] [MemberData(nameof(MarshalDateTimeOffsetCases))] public void JSImportDateTimeOffset(DateTimeOffset value) @@ -992,25 +682,9 @@ public void JSImportDateTimeOffset(DateTimeOffset value) "object", "Date"); } - [Theory] - [MemberData(nameof(MarshalDateTimeOffsetCases))] - public void JsExportDateTimeOffset(DateTimeOffset value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_DateTimeOffset, - nameof(JavaScriptTestHelper.EchoDateTimeOffset), - "object", "Date"); - } #endregion DateTimeOffset #region NullableBoolean - public static IEnumerable MarshalNullableBooleanCases() - { - yield return new object[] { null }; - yield return new object[] { true }; - yield return new object[] { false }; - } - [Theory] [MemberData(nameof(MarshalNullableBooleanCases))] public void JsImportNullableBoolean(bool? value) @@ -1024,29 +698,9 @@ public void JsImportNullableBoolean(bool? value) "boolean"); } - [Theory] - [MemberData(nameof(MarshalNullableBooleanCases))] - public void JsExportNullableBoolean(bool? value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_NullableBoolean, - nameof(JavaScriptTestHelper.EchoNullableBoolean), - "boolean"); - } #endregion NullableBoolean #region NullableInt32 - public static IEnumerable MarshalNullableInt32Cases() - { - yield return new object[] { null }; - yield return new object[] { 42 }; - yield return new object[] { 0 }; - yield return new object[] { 1 }; - yield return new object[] { -1 }; - yield return new object[] { int.MaxValue }; - yield return new object[] { int.MinValue }; - } - [Theory] [MemberData(nameof(MarshalNullableInt32Cases))] public void JsImportNullableInt32(int? value) @@ -1060,31 +714,9 @@ public void JsImportNullableInt32(int? value) "number"); } - [Theory] - [MemberData(nameof(MarshalNullableInt32Cases))] - public void JsExportNullableInt32(int? value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_NullableInt32, - nameof(JavaScriptTestHelper.EchoNullableInt32), - "number"); - } #endregion NullableInt32 #region NullableBigInt64 - public static IEnumerable MarshalNullableBigInt64Cases() - { - yield return new object[] { null }; - yield return new object[] { 42L }; - yield return new object[] { 0L }; - yield return new object[] { 1L }; - yield return new object[] { -1L }; - yield return new object[] { MAX_SAFE_INTEGER }; - yield return new object[] { MIN_SAFE_INTEGER }; - yield return new object[] { long.MaxValue }; - yield return new object[] { long.MinValue }; - } - [Theory] [MemberData(nameof(MarshalNullableBigInt64Cases))] public void JsImportNullableBigInt64(long? value) @@ -1098,29 +730,9 @@ public void JsImportNullableBigInt64(long? value) "bigint"); } - [Theory] - [MemberData(nameof(MarshalNullableBigInt64Cases))] - public void JsExportNullableBigInt64(long? value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_NullableBigInt64, - nameof(JavaScriptTestHelper.EchoNullableBigInt64), - "bigint"); - } #endregion NullableBigInt64 #region NullableIntPtr - public static IEnumerable MarshalNullableIntPtrCases() - { - yield return new object[] { null }; - yield return new object[] { (IntPtr)42 }; - yield return new object[] { IntPtr.Zero }; - yield return new object[] { (IntPtr)1 }; - yield return new object[] { (IntPtr)(-1) }; - yield return new object[] { IntPtr.MaxValue }; - yield return new object[] { IntPtr.MinValue }; - } - [Theory] [MemberData(nameof(MarshalNullableIntPtrCases))] public void JsImportNullableIntPtr(IntPtr? value) @@ -1134,30 +746,9 @@ public void JsImportNullableIntPtr(IntPtr? value) "number"); } - [Theory] - [MemberData(nameof(MarshalNullableIntPtrCases))] - public void JsExportNullableIntPtr(IntPtr? value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_NullableIntPtr, - nameof(JavaScriptTestHelper.EchoNullableIntPtr), - "number"); - } #endregion NullableIntPtr #region NullableDouble - public static IEnumerable MarshalNullableDoubleCases() - { - yield return new object[] { null }; - yield return new object[] { Math.PI }; - yield return new object[] { 0.0 }; - yield return new object[] { double.MaxValue }; - yield return new object[] { double.MinValue }; - yield return new object[] { double.NegativeInfinity }; - yield return new object[] { double.PositiveInfinity }; - yield return new object[] { double.NaN }; - } - [Theory] [MemberData(nameof(MarshalNullableDoubleCases))] public void JsImportNullableDouble(double? value) @@ -1171,26 +762,9 @@ public void JsImportNullableDouble(double? value) "number"); } - [Theory] - [MemberData(nameof(MarshalNullableDoubleCases))] - public void JsExportNullableDouble(double? value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_NullableDouble, - nameof(JavaScriptTestHelper.EchoNullableDouble), - "number"); - } #endregion NullableDouble #region NullableDateTime - public static IEnumerable MarshalNullableDateTimeCases() - { - yield return new object[] { null }; - yield return new object[] { new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) }; - yield return new object[] { TrimNano(DateTime.UtcNow) }; - yield return new object[] { TrimNano(DateTime.MaxValue) }; - } - [Theory] [MemberData(nameof(MarshalNullableDateTimeCases))] public void JsImportNullableDateTime(DateTime? value) @@ -1204,27 +778,9 @@ public void JsImportNullableDateTime(DateTime? value) "object"); } - [Theory] - [MemberData(nameof(MarshalNullableDateTimeCases))] - public void JsExportNullableDateTime(DateTime? value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_NullableDateTime, - nameof(JavaScriptTestHelper.EchoNullableDateTime), - "object"); - } #endregion NullableDateTime #region String - public static IEnumerable MarshalStringCases() - { - yield return new object[] { null }; - yield return new object[] { string.Empty }; - yield return new object[] { "Ahoj" + Random.Shared.Next() };// shorted than 256 -> check in JS interned - yield return new object[] { "Ahoj" + new string('!', 300) };// longer than 256 -> no check in JS interned - yield return new object[] { string.Intern("dotnet") }; - } - [Theory] [MemberData(nameof(MarshalStringCases))] public void JsImportString(string value) @@ -1238,78 +794,6 @@ public void JsImportString(string value) , "string"); } - [Theory] - [MemberData(nameof(MarshalStringCases))] - public void JsExportString(string value) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_String, - nameof(JavaScriptTestHelper.EchoString), - "string"); - } - - [Fact] - public void JsExportStringNoNs() - { - var actual = JavaScriptTestHelper.invoke2_String("test", nameof(JavaScriptTestHelperNoNamespace.EchoString)); - Assert.Equal("test51", actual); - } - - [Fact] - public void JsExportStructClassRecords() - { - var actual = JavaScriptTestHelper.invokeStructClassRecords("test"); - Assert.Equal(48, actual.Length); - Assert.Equal("test11", actual[0]); - Assert.Equal("test12", actual[1]); - Assert.Equal("test13", actual[2]); - Assert.Equal("test14", actual[3]); - Assert.Equal("test15", actual[4]); - Assert.Equal("test16", actual[5]); - Assert.Equal("test17", actual[6]); - Assert.Equal("test18", actual[7]); - Assert.Equal("test19", actual[8]); - Assert.Equal("test21", actual[9]); - Assert.Equal("test22", actual[10]); - Assert.Equal("test23", actual[11]); - Assert.Equal("test24", actual[12]); - Assert.Equal("test25", actual[13]); - Assert.Equal("test31", actual[14]); - Assert.Equal("test32", actual[15]); - Assert.Equal("test33", actual[16]); - Assert.Equal("test34", actual[17]); - Assert.Equal("test35", actual[18]); - Assert.Equal("test41", actual[19]); - Assert.Equal("test42", actual[20]); - Assert.Equal("test43", actual[21]); - Assert.Equal("test44", actual[22]); - Assert.Equal("test45", actual[23]); - Assert.Equal("test51", actual[24]); - Assert.Equal("test52", actual[25]); - Assert.Equal("test53", actual[26]); - Assert.Equal("test54", actual[27]); - Assert.Equal("test55", actual[28]); - Assert.Equal("test56", actual[29]); - Assert.Equal("test57", actual[30]); - Assert.Equal("test58", actual[31]); - Assert.Equal("test59", actual[32]); - Assert.Equal("test61", actual[33]); - Assert.Equal("test62", actual[34]); - Assert.Equal("test63", actual[35]); - Assert.Equal("test64", actual[36]); - Assert.Equal("test65", actual[37]); - Assert.Equal("test71", actual[38]); - Assert.Equal("test72", actual[39]); - Assert.Equal("test73", actual[40]); - Assert.Equal("test74", actual[41]); - Assert.Equal("test75", actual[42]); - Assert.Equal("test81", actual[43]); - Assert.Equal("test82", actual[44]); - Assert.Equal("test83", actual[45]); - Assert.Equal("test84", actual[46]); - Assert.Equal("test85", actual[47]); - } - [Fact] public void JsImportNative() { @@ -1337,12 +821,6 @@ public void JsImportReboundInstanceMember() #endregion String #region Object - public static IEnumerable MarshalObjectCases() - { - yield return new object[] { new object(), "ManagedObject" }; - yield return new object[] { null, null }; - } - [Theory] [MemberData(nameof(MarshalObjectCases))] public void JSImportObject(object value, string clazz) @@ -1356,25 +834,9 @@ public void JSImportObject(object value, string clazz) "object", clazz); } - [Theory] - [MemberData(nameof(MarshalObjectCases))] - public void JsExportObject(object value, string clazz) - { - JsExportTest(value, - JavaScriptTestHelper.invoke1_Object, - nameof(JavaScriptTestHelper.EchoObject), - "object", clazz); - } #endregion Object #region Exception - public static IEnumerable MarshalExceptionCases() - { - yield return new object[] { new Exception("Test"), "ManagedError" }; - yield return new object[] { null, "JSTestError" }; - yield return new object[] { null, null }; - } - [Theory] [MemberData(nameof(MarshalExceptionCases))] public void JSImportException(Exception value, string clazz) @@ -1393,29 +855,6 @@ public void JSImportException(Exception value, string clazz) "object", clazz); } - [Theory] - [MemberData(nameof(MarshalExceptionCases))] - public void JsExportException(Exception value, string clazz) - { - if (clazz == "JSTestError") - { - value = JavaScriptTestHelper.createException("!CreateEx!"); - } - - JsExportTest(value, - JavaScriptTestHelper.invoke1_Exception, - nameof(JavaScriptTestHelper.EchoException), - "object", clazz); - } - - [Fact] - public void JsExportThrows() - { - var ex = Assert.Throws(() => JavaScriptTestHelper.invoke1_String("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport))); - Assert.DoesNotContain("Unexpected error", ex.Message); - Assert.Contains("-t-e-s-t-", ex.Message); - } - [Fact] public void JSImportReturnError() { @@ -1424,35 +863,9 @@ public void JSImportReturnError() Assert.Contains("this-is-error", err.Message); } - [Fact] - public void JsExportCatchToString() - { - var toString = JavaScriptTestHelper.catch1toString("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)); - Assert.DoesNotContain("Unexpected error", toString); - Assert.Contains("-t-e-s-t-", toString); - Assert.DoesNotContain(nameof(JavaScriptTestHelper.ThrowFromJSExport), toString); - } - - [Fact] - public void JsExportCatchStack() - { - var stack = JavaScriptTestHelper.catch1stack("-t-e-s-t-", nameof(JavaScriptTestHelper.ThrowFromJSExport)); - Assert.Contains(nameof(JavaScriptTestHelper.ThrowFromJSExport), stack); - if (PlatformDetection.IsBrowserDomSupportedOrNodeJS) - { - Assert.Contains("catch1stack", stack); - } - } - #endregion Exception #region JSObject - public static IEnumerable MarshalIJSObjectCases() - { - yield return new object[] { null, "JSData" }; - yield return new object[] { null, null }; - } - [Theory] [MemberData(nameof(MarshalIJSObjectCases))] public void JSImportIJSObject(JSObject value, string clazz) @@ -1471,20 +884,6 @@ public void JSImportIJSObject(JSObject value, string clazz) "object", clazz); } - [Theory] - [MemberData(nameof(MarshalIJSObjectCases))] - public void JsExportIJSObject(JSObject value, string clazz) - { - if (clazz == "JSData") - { - value = JavaScriptTestHelper.createData("!CreateJS!"); - } - - JsExportTest(value, - JavaScriptTestHelper.invoke1_JSObject, - nameof(JavaScriptTestHelper.EchoIJSObject), - "object", clazz); - } #endregion JSObject #region ProxyOfProxy @@ -1517,7 +916,7 @@ public async Task JsImportSleep() [Fact] public async Task JsImportTaskTypes() { - for(int i=0;i<100;i++) + for (int i = 0; i < 100; i++) { object a = new object(); Exception e = new Exception(); @@ -1641,14 +1040,6 @@ public async Task JsImportTaskEchoPendingException() await Assert.ThrowsAsync(async () => await task); } - public static IEnumerable TaskCases() - { - yield return new object[] { Math.PI }; - yield return new object[] { 0 }; - yield return new object[] { "test" }; - yield return new object[] { null }; - } - [Theory] [MemberData(nameof(TaskCases))] public async Task JsImportTaskAwaitPendingResult(object result) @@ -1713,26 +1104,11 @@ public async Task JsImportTaskAwait() await task; } - [Theory] - [MemberData(nameof(MarshalInt32Cases))] - public async Task JsExportTaskOfInt(int value) - { - TaskCompletionSource tcs = new TaskCompletionSource(); - - var res = JavaScriptTestHelper.invoke1_TaskOfInt(tcs.Task, nameof(JavaScriptTestHelper.AwaitTaskOfObject)); - tcs.SetResult(value); - await Task.Yield(); - var rr = await res; - await Task.Yield(); - Assert.Equal(value, rr); - //GC.Collect(); - } - #endregion #region Action - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportCallback_EchoAction() { bool called = false; @@ -1747,8 +1123,41 @@ public void JsImportCallback_EchoAction() Assert.True(called); } + /* TODO deputy + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWasmThreadingSupported))] + public void JsImportCallback_EchoActionThrows_MT() + { + bool called = false; + Action expected = () => + { + called = true; + }; + var actual = JavaScriptTestHelper.echo1_ActionAction(expected); + Assert.NotEqual(expected, actual); + Assert.False(called); + // with deputy thread, call back to C# from synchronous JS function is not allowed + Assert.Throws(()=>actual()); + Assert.False(called); + } + */ [Fact] + public async Task JsImportCallback_Async() + { + bool called = false; + var promise = JavaScriptTestHelper.backback_FuncIntIntFuncIntIntAsync((a,b) => + { + called = true; + return a + b; + }, 123, 321); + Assert.False(called); + var actual = await promise; + Assert.True(called); + Assert.Equal(444, actual); + } + + + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy [OuterLoop] public async Task JsImportCallback_EchoActionMany() { @@ -1769,7 +1178,7 @@ public async Task JsImportCallback_EchoActionMany() } } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportCallback_Action() { bool called = false; @@ -1780,7 +1189,7 @@ public void JsImportCallback_Action() Assert.True(called); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportEcho_ActionAction() { bool called = false; @@ -1793,7 +1202,7 @@ public void JsImportEcho_ActionAction() Assert.True(called); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportEcho_ActionIntActionInt() { int calledA = -1; @@ -1806,7 +1215,7 @@ public void JsImportEcho_ActionIntActionInt() Assert.Equal(42, calledA); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportCallback_ActionInt() { int called = -1; @@ -1817,7 +1226,7 @@ public void JsImportCallback_ActionInt() Assert.Equal(42, called); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportCallback_FunctionIntInt() { int called = -1; @@ -1830,7 +1239,7 @@ public void JsImportCallback_FunctionIntInt() Assert.Equal(42, res); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportBackCallback_FunctionIntInt() { int called = -1; @@ -1845,7 +1254,7 @@ public void JsImportBackCallback_FunctionIntInt() Assert.Equal(84, called); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportBackCallback_FunctionIntIntIntInt() { int calledA = -1; @@ -1864,7 +1273,7 @@ public void JsImportBackCallback_FunctionIntIntIntInt() Assert.Equal(84, calledB); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportCallback_ActionIntInt() { int calledA = -1; @@ -1878,7 +1287,7 @@ public void JsImportCallback_ActionIntInt() Assert.Equal(43, calledB); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportCallback_ActionLongLong() { long calledA = -1; @@ -1892,7 +1301,7 @@ public void JsImportCallback_ActionLongLong() Assert.Equal(43, calledB); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportCallback_ActionIntLong() { int calledA = -1; @@ -1906,7 +1315,7 @@ public void JsImportCallback_ActionIntLong() Assert.Equal(43, calledB); } - [Fact] + [Fact] //TODO [Fact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWasmThreadingSupported))] // this test doesn't make sense with deputy public void JsImportCallback_ActionIntThrow() { int called = -1; @@ -1920,39 +1329,6 @@ public void JsImportCallback_ActionIntThrow() Assert.Same(expected, actual); } - [Fact] - public void JsExportCallback_FunctionIntInt() - { - int called = -1; - var chain = JavaScriptTestHelper.invoke1_FuncOfIntInt((int a) => - { - called = a; - return a; - }, nameof(JavaScriptTestHelper.BackFuncOfIntInt)); - - Assert.Equal(-1, called); - var actual = chain(42); - Assert.Equal(42, actual); - Assert.Equal(42, called); - } - - [Fact] - public void JsExportCallback_FunctionIntIntThrow() - { - int called = -1; - var expected = new Exception("test!!"); - var chain = JavaScriptTestHelper.invoke1_FuncOfIntInt((int a) => - { - called = a; - throw expected; - }, nameof(JavaScriptTestHelper.BackFuncOfIntInt)); - - Assert.Equal(-1, called); - var actual = Assert.Throws(() => chain(42)); - Assert.Equal(42, called); - Assert.Same(expected, actual); - } - [Fact] public void JsImportMath() { @@ -1962,14 +1338,6 @@ public void JsImportMath() #endregion - private void JsExportTest<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(T value - , Func invoke, string echoName, string jsType, string? jsClass = null) - { - T res; - res = invoke(value, echoName); - Assert.Equal(value, res); - } - private void JsImportTest<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(T value , Action store1 , Func retrieve1 @@ -2112,26 +1480,5 @@ public void JsImportMath() Assert.Equal((Exception)(object)value, resEx); } } - - public async Task InitializeAsync() - { - await JavaScriptTestHelper.InitializeAsync(); - } - - public async Task DisposeAsync() - { - await JavaScriptTestHelper.DisposeAsync(); - } - - // js Date doesn't have nanosecond precision - public static DateTime TrimNano(DateTime date) - { - return new DateTime(date.Ticks - (date.Ticks % TimeSpan.TicksPerMillisecond), DateTimeKind.Utc); - } - - public static DateTimeOffset TrimNano(DateTimeOffset date) - { - return new DateTime(date.Ticks - (date.Ticks % TimeSpan.TicksPerMillisecond), DateTimeKind.Utc); - } } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSInteropTestBase.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSInteropTestBase.cs new file mode 100644 index 00000000000000..288ad2b227d75e --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSInteropTestBase.cs @@ -0,0 +1,340 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using System.Threading; +using Xunit; +using System.Diagnostics.CodeAnalysis; +#pragma warning disable xUnit1026 // Theory methods should use all of their parameters + +namespace System.Runtime.InteropServices.JavaScript.Tests +{ + public class JSInteropTestBase + { + public static IEnumerable MarshalCharCases() + { + yield return new object[] { (char)42 }; + yield return new object[] { (char)1 }; + yield return new object[] { '\u017D' }; + yield return new object[] { '\u2661' }; + yield return new object[] { char.MaxValue }; + yield return new object[] { char.MinValue }; + } + + public static IEnumerable MarshalByteCases() + { + yield return new object[] { (byte)42 }; + yield return new object[] { (byte)1 }; + yield return new object[] { byte.MaxValue }; + yield return new object[] { byte.MinValue }; + } + + public static IEnumerable MarshalInt16Cases() + { + yield return new object[] { 42 }; + yield return new object[] { 0 }; + yield return new object[] { 1 }; + yield return new object[] { -1 }; + yield return new object[] { short.MaxValue }; + yield return new object[] { short.MinValue }; + } + + public static IEnumerable MarshalInt32Cases() + { + yield return new object[] { 42 }; + yield return new object[] { 0 }; + yield return new object[] { 1 }; + yield return new object[] { -1 }; + yield return new object[] { int.MaxValue }; + yield return new object[] { int.MinValue }; + } + + public static IEnumerable OutOfRangeCases() + { + yield return new object[] { double.MaxValue, "Value is not an integer" }; + yield return new object[] { double.MinValue, "Value is not an integer" }; + yield return new object[] { double.NaN, "Value is not an integer" }; + yield return new object[] { double.NegativeInfinity, "Value is not an integer" }; + yield return new object[] { double.PositiveInfinity, "Value is not an integer" }; + yield return new object[] { (double)MAX_SAFE_INTEGER, "Overflow" }; + } + + const long MAX_SAFE_INTEGER = 9007199254740991L;// Number.MAX_SAFE_INTEGER + const long MIN_SAFE_INTEGER = -9007199254740991L;// Number.MIN_SAFE_INTEGER + public static IEnumerable MarshalInt52Cases() + { + yield return new object[] { -1 }; + yield return new object[] { 42 }; + yield return new object[] { 0 }; + yield return new object[] { 1 }; + yield return new object[] { MAX_SAFE_INTEGER }; + yield return new object[] { MIN_SAFE_INTEGER }; + } + + public static IEnumerable MarshalBigInt64Cases() + { + yield return new object[] { -1 }; + yield return new object[] { 42 }; + yield return new object[] { 0 }; + yield return new object[] { 1 }; + yield return new object[] { MAX_SAFE_INTEGER }; + yield return new object[] { MIN_SAFE_INTEGER }; + yield return new object[] { long.MinValue }; + yield return new object[] { long.MaxValue }; + } + + public static IEnumerable MarshalDoubleCases() + { + yield return new object[] { Math.PI }; + yield return new object[] { 0.0 }; + yield return new object[] { double.MaxValue }; + yield return new object[] { double.MinValue }; + yield return new object[] { double.NegativeInfinity }; + yield return new object[] { double.PositiveInfinity }; + yield return new object[] { double.NaN }; + } + + public static IEnumerable MarshalSingleCases() + { + yield return new object[] { (float)Math.PI }; + yield return new object[] { 0.0f }; + yield return new object[] { float.MaxValue }; + yield return new object[] { float.MinValue }; + yield return new object[] { float.NegativeInfinity }; + yield return new object[] { float.PositiveInfinity }; + yield return new object[] { float.NaN }; + } + + public static IEnumerable MarshalIntPtrCases() + { + yield return new object[] { (IntPtr)42 }; + yield return new object[] { IntPtr.Zero }; + yield return new object[] { (IntPtr)1 }; + yield return new object[] { (IntPtr)(-1) }; + yield return new object[] { IntPtr.MaxValue }; + yield return new object[] { IntPtr.MinValue }; + } + + public static IEnumerable MarshalDateTimeCases() + { + yield return new object[] { new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) }; + yield return new object[] { TrimNano(DateTime.UtcNow) }; + yield return new object[] { TrimNano(DateTime.MaxValue) }; + } + + public static IEnumerable MarshalDateTimeOffsetCases() + { + yield return new object[] { DateTimeOffset.FromUnixTimeSeconds(0) }; + yield return new object[] { TrimNano(DateTimeOffset.UtcNow) }; + yield return new object[] { TrimNano(DateTimeOffset.MaxValue) }; + } + + public static IEnumerable MarshalByteArrayCases() + { + yield return new object[] { new byte[] { 1, 2, 3, byte.MaxValue, byte.MinValue } }; + yield return new object[] { new byte[] { } }; + yield return new object[] { null }; + } + + public static IEnumerable MarshalIntArrayCases() + { + yield return new object[] { new int[] { 1, 2, 3, int.MaxValue, int.MinValue } }; + yield return new object[] { new int[] { } }; + yield return new object[] { null }; + } + + public static IEnumerable MarshalDoubleArrayCases() + { + yield return new object[] { new double[] { 1, 2, 3, double.MaxValue, double.MinValue, double.Pi, double.NegativeInfinity, double.PositiveInfinity, double.NaN } }; + yield return new object[] { new double[] { } }; + yield return new object[] { null }; + } + + public static IEnumerable MarshalStringArrayCases() + { + yield return new object[] { new string[] { "\u0050\u0159\u00ed\u006c\u0069\u0161", "\u017e\u006c\u0075\u0165\u006f\u0075\u010d\u006b\u00fd" } }; + yield return new object[] { new string[] { string.Intern("hello"), string.Empty, null } }; + yield return new object[] { new string[] { } }; + yield return new object[] { null }; + } + + public static IEnumerable MarshalBooleanCases() + { + yield return new object[] { true }; + yield return new object[] { false }; + } + + public static IEnumerable MarshalObjectArrayCasesToDouble() + { + yield return new object[] { new object[] { (byte)42 } }; + yield return new object[] { new object[] { (short)42 } }; + yield return new object[] { new object[] { 42 } }; + yield return new object[] { new object[] { 3.14f } }; + yield return new object[] { new object[] { 'A' } }; + } + + protected delegate void dummyDelegate(); + protected static void dummyDelegateA() + { + } + + public class SomethingRef + { + } + + public class SomethingStruct + { + } + + public static IEnumerable MarshalObjectArrayCasesThrow() + { + yield return new object[] { new object[] { () => { } } }; + yield return new object[] { new object[] { (int a) => { } } }; + yield return new object[] { new object[] { (int a) => { return a; } } }; + yield return new object[] { new object[] { (dummyDelegate)dummyDelegateA } }; + yield return new object[] { new object[] { 0L } }; + yield return new object[] { new object[] { 0UL } }; + yield return new object[] { new object[] { (sbyte)0 } }; + yield return new object[] { new object[] { (ushort)0 } }; + yield return new object[] { new object[] { new SomethingStruct[] { } } }; + yield return new object[] { new object[] { new SomethingRef[] { }, } }; + yield return new object[] { new object[] { new ArraySegment(new byte[] { 11 }), } }; + } + + public static IEnumerable MarshalObjectArrayCases() + { + yield return new object[] { new object[] { string.Intern("hello"), string.Empty } }; + yield return new object[] { new object[] { 1.1d, new DateTime(2022, 5, 8, 14, 55, 01, DateTimeKind.Utc), false, true } }; + yield return new object[] { new object[] { new double?(1.1d), new DateTime?(new DateTime(2022, 5, 8, 14, 55, 01, DateTimeKind.Utc)), new bool?(false), new bool?(true) } }; + yield return new object[] { new object[] { null, new object(), new SomethingRef(), new SomethingStruct(), new Exception("test") } }; + yield return new object[] { new object[] { "JSData" } }; // special cased, so we call createData in the test itself + yield return new object[] { new object[] { new byte[] { }, new int[] { }, new double[] { }, new string[] { }, new object[] { } } }; + yield return new object[] { new object[] { new byte[] { 1, 2, 3 }, new int[] { 1, 2, 3 }, new double[] { 1, 2, 3 }, new string[] { "a", "b", "c" }, new object[] { } } }; + yield return new object[] { new object[] { new object[] { new byte[] { 1, 2, 3 }, new int[] { 1, 2, 3 }, new double[] { 1, 2, 3 }, new string[] { "a", "b", "c" }, new object(), new SomethingRef(), new SomethingStruct(), new Exception("test") } } }; + yield return new object[] { new object[] { } }; + yield return new object[] { null }; + } + + public static IEnumerable MarshalNullableBooleanCases() + { + yield return new object[] { null }; + yield return new object[] { true }; + yield return new object[] { false }; + } + + public static IEnumerable MarshalNullableInt32Cases() + { + yield return new object[] { null }; + yield return new object[] { 42 }; + yield return new object[] { 0 }; + yield return new object[] { 1 }; + yield return new object[] { -1 }; + yield return new object[] { int.MaxValue }; + yield return new object[] { int.MinValue }; + } + + public static IEnumerable MarshalNullableBigInt64Cases() + { + yield return new object[] { null }; + yield return new object[] { 42L }; + yield return new object[] { 0L }; + yield return new object[] { 1L }; + yield return new object[] { -1L }; + yield return new object[] { MAX_SAFE_INTEGER }; + yield return new object[] { MIN_SAFE_INTEGER }; + yield return new object[] { long.MaxValue }; + yield return new object[] { long.MinValue }; + } + + public static IEnumerable MarshalNullableIntPtrCases() + { + yield return new object[] { null }; + yield return new object[] { (IntPtr)42 }; + yield return new object[] { IntPtr.Zero }; + yield return new object[] { (IntPtr)1 }; + yield return new object[] { (IntPtr)(-1) }; + yield return new object[] { IntPtr.MaxValue }; + yield return new object[] { IntPtr.MinValue }; + } + + public static IEnumerable MarshalNullableDoubleCases() + { + yield return new object[] { null }; + yield return new object[] { Math.PI }; + yield return new object[] { 0.0 }; + yield return new object[] { double.MaxValue }; + yield return new object[] { double.MinValue }; + yield return new object[] { double.NegativeInfinity }; + yield return new object[] { double.PositiveInfinity }; + yield return new object[] { double.NaN }; + } + + public static IEnumerable MarshalNullableDateTimeCases() + { + yield return new object[] { null }; + yield return new object[] { new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) }; + yield return new object[] { TrimNano(DateTime.UtcNow) }; + yield return new object[] { TrimNano(DateTime.MaxValue) }; + } + + public static IEnumerable MarshalStringCases() + { + yield return new object[] { null }; + yield return new object[] { string.Empty }; + yield return new object[] { "Ahoj" + Random.Shared.Next() };// shorted than 256 -> check in JS interned + yield return new object[] { "Ahoj" + new string('!', 300) };// longer than 256 -> no check in JS interned + yield return new object[] { string.Intern("dotnet") }; + } + + public static IEnumerable MarshalObjectCases() + { + yield return new object[] { new object(), "ManagedObject" }; + yield return new object[] { null, null }; + } + + public static IEnumerable MarshalExceptionCases() + { + yield return new object[] { new Exception("Test"), "ManagedError" }; + yield return new object[] { null, "JSTestError" }; + yield return new object[] { null, null }; + } + + public static IEnumerable MarshalIJSObjectCases() + { + yield return new object[] { null, "JSData" }; + yield return new object[] { null, null }; + } + + public static IEnumerable TaskCases() + { + yield return new object[] { Math.PI }; + yield return new object[] { 0 }; + yield return new object[] { "test" }; + yield return new object[] { null }; + } + + public async Task InitializeAsync() + { + await JavaScriptTestHelper.InitializeAsync(); + } + + public async Task DisposeAsync() + { + await JavaScriptTestHelper.DisposeAsync(); + } + + // js Date doesn't have nanosecond precision + public static DateTime TrimNano(DateTime date) + { + return new DateTime(date.Ticks - (date.Ticks % TimeSpan.TicksPerMillisecond), DateTimeKind.Utc); + } + + public static DateTimeOffset TrimNano(DateTimeOffset date) + { + return new DateTime(date.Ticks - (date.Ticks % TimeSpan.TicksPerMillisecond), DateTimeKind.Utc); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs index bfd645039b56cb..3b3dff2974e782 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs @@ -449,6 +449,9 @@ public static async Task AwaitTaskOfObject([JSMarshalAs>] internal static partial Func backback_FuncIntIntFuncIntInt([JSMarshalAs>] Func fun, [JSMarshalAs] int a, [JSMarshalAs] int b); + [JSImport("backbackAsync", "JavaScriptTestHelper")] + internal static partial Task backback_FuncIntIntFuncIntIntAsync([JSMarshalAs>] Func fun, [JSMarshalAs] int a, [JSMarshalAs] int b); + [JSImport("back3", "JavaScriptTestHelper")] internal static partial void back3_ActionInt([JSMarshalAs>] Action? action, [JSMarshalAs] int a); @@ -500,6 +503,10 @@ public static Func BackFuncOfIntInt([JSMarshalAs] internal static partial bool invoke1_Boolean([JSMarshalAs] bool value, [JSMarshalAs] string name); + + [JSImport("invoke1Async", "JavaScriptTestHelper")] + internal static partial Task invoke1_BooleanAsync(bool value, string name); + [JSExport] [return: JSMarshalAs] public static bool EchoBoolean([JSMarshalAs] bool arg1) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs index baec1cb231c2b8..6b917e3f3fee0b 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs @@ -214,6 +214,24 @@ export function invoke1(arg1, name) { return res; } +export async function invoke1Async(arg1, name) { + if (globalThis.gc) { + // console.log('globalThis.gc'); + globalThis.gc(); + } + // console.log(`invoke1: ${name}(arg1:${arg1 !== null ? typeof arg1 : ''})`) + const JavaScriptTestHelper = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper; + const fn = JavaScriptTestHelper[name]; + + await delay(10); + + // console.log("invoke1:" + typeof fn); + // console.log("invoke1:" + fn.toString()); + const res = fn(arg1); + // console.log(`invoke1: res ${res !== null ? typeof res : ''}`) + return res; +} + export function invoke2(arg1, name) { const fn = dllExports.JavaScriptTestHelperNoNamespace[name]; //console.log("invoke1:" + fn.toString()); @@ -379,6 +397,15 @@ export function backback(arg1, arg2, arg3) { } } +export async function backbackAsync(arg1, arg2, arg3) { + if (globalThis.gc) { + // console.log('globalThis.gc'); + globalThis.gc(); + } + await delay(10); + return arg1(arg2, arg3); +} + export const instance = {} globalThis.javaScriptTestHelper = instance; @@ -396,3 +423,7 @@ export async function setup() { } // console.log('JavaScriptTestHelper:' Object.keys(globalThis.JavaScriptTestHelper)); + +export function delay(ms) { + return new Promise(resolve => globalThis.setTimeout(resolve, ms)); +} \ No newline at end of file From 9394d2e1755c727fe217ba6e696b99ffdeb8be74 Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Fri, 16 Feb 2024 11:39:15 -0500 Subject: [PATCH 083/158] JIT: Refactor Compiler::optRedirectBlock; remove BasicBlock::CopyTarget (#98526) Part of #93020. When cloning a set of basic blocks (such as during loop cloning, finally cloning, etc.), the successors of the copied blocks may be different from the successors of their original counterparts, due to the successors being copied as well. We currently handle this as so: * Create the block copies without initializing their successors. * Copy the target(s) of the original block over with BasicBlock::CopyTarget; don't create any edges yet, as the new block's target(s) may change. * In Compiler::optRedirectBlock, determine if the new block's target needs to be redirected to the copy of the original target, using a mapping of original blocks to their copies. We may or may not also initialize/update the successor edges for the new block. This PR simplifies this process in preparation for updating BasicBlock to track its successors via FlowEdge pointers, instead of BasicBlock pointers. This is the new process: * Create the block copies without initializing their successors. * In Compiler::optRedirectBlock, for each successor of the original block, determine if the new block should use the successor, or if the new block should be redirected to a copy of that successor, using the block map. Either way, all of the new block's successor edges must be initialized here. --- src/coreclr/jit/block.cpp | 56 +++----- src/coreclr/jit/block.h | 5 +- src/coreclr/jit/compiler.h | 11 +- src/coreclr/jit/fgbasic.cpp | 22 ++-- src/coreclr/jit/fgehopt.cpp | 3 +- src/coreclr/jit/flowgraph.cpp | 33 +---- src/coreclr/jit/optimizer.cpp | 242 +++++++++++++++++----------------- 7 files changed, 158 insertions(+), 214 deletions(-) diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index e71566da1f76a5..01ead6622e6e14 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -837,46 +837,6 @@ void BasicBlock::CloneBlockState(Compiler* compiler, BasicBlock* to, const Basic } } -//------------------------------------------------------------------------ -// CopyTarget: Copy the block kind and targets. The targets in the `from` block remain valid. -// Use `TransferTarget` to copy the pointer to the target descriptor (e.g., for BBJ_SWITCH/BBJ_EHFINALLYRET) -// after which the `from` block target is invalid. -// -// Arguments: -// compiler - Jit compiler instance -// from - Block to copy from -// -void BasicBlock::CopyTarget(Compiler* compiler, const BasicBlock* from) -{ - switch (from->GetKind()) - { - case BBJ_SWITCH: - SetSwitch(new (compiler, CMK_BasicBlock) BBswtDesc(compiler, from->GetSwitchTargets())); - break; - case BBJ_EHFINALLYRET: - SetEhf(new (compiler, CMK_BasicBlock) BBehfDesc(compiler, from->GetEhfTargets())); - break; - case BBJ_COND: - SetCond(from->GetTrueTarget(), from->GetFalseTarget()); - break; - case BBJ_ALWAYS: - SetKindAndTarget(from->GetKind(), from->GetTarget()); - CopyFlags(from, BBF_NONE_QUIRK); - break; - case BBJ_CALLFINALLY: - case BBJ_CALLFINALLYRET: - case BBJ_EHCATCHRET: - case BBJ_EHFILTERRET: - case BBJ_LEAVE: - SetKindAndTarget(from->GetKind(), from->GetTarget()); - break; - default: - SetKindAndTarget(from->GetKind()); // Clear the target - break; - } - assert(KindIs(from->GetKind())); -} - //------------------------------------------------------------------------ // TransferTarget: Like CopyTarget, but copies the target descriptors for block types which have // them (BBJ_SWITCH/BBJ_EHFINALLYRET), that is, take their memory, after which the `from` block @@ -1781,6 +1741,22 @@ bool BasicBlock::hasEHBoundaryOut() const return returnVal; } +//------------------------------------------------------------------------ +// BBswtDesc copy ctor: copy a switch descriptor, but don't set up the jump table +// +// Arguments: +// other - existing switch descriptor to copy (except for its jump table) +// +BBswtDesc::BBswtDesc(const BBswtDesc* other) + : bbsDstTab(nullptr) + , bbsCount(other->bbsCount) + , bbsDominantCase(other->bbsDominantCase) + , bbsDominantFraction(other->bbsDominantFraction) + , bbsHasDefault(other->bbsHasDefault) + , bbsHasDominantCase(other->bbsHasDominantCase) +{ +} + //------------------------------------------------------------------------ // BBswtDesc copy ctor: copy a switch descriptor // diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index d434ae8a92b01f..041af02b1c15ad 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -1660,9 +1660,6 @@ struct BasicBlock : private LIR::Range // Clone block state and statements from `from` block to `to` block (which must be new/empty) static void CloneBlockState(Compiler* compiler, BasicBlock* to, const BasicBlock* from); - // Copy the block kind and targets. The `from` block is untouched. - void CopyTarget(Compiler* compiler, const BasicBlock* from); - // Copy the block kind and take memory ownership of the targets. void TransferTarget(BasicBlock* from); @@ -1839,6 +1836,8 @@ struct BBswtDesc { } + BBswtDesc(const BBswtDesc* other); + BBswtDesc(Compiler* comp, const BBswtDesc* other); void removeDefault() diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index ab93a8044bb54c..a18fee959436b4 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6856,16 +6856,9 @@ class Compiler bool optExtractInitTestIncr( BasicBlock** pInitBlock, BasicBlock* bottom, BasicBlock* top, GenTree** ppInit, GenTree** ppTest, GenTree** ppIncr); - enum class RedirectBlockOption - { - DoNotChangePredLists, // do not modify pred lists - UpdatePredLists, // add/remove to pred lists - AddToPredLists, // only add to pred lists - }; - void optRedirectBlock(BasicBlock* blk, - BlockToBlockMap* redirectMap, - const RedirectBlockOption = RedirectBlockOption::DoNotChangePredLists); + BasicBlock* newBlk, + BlockToBlockMap* redirectMap); // Marks the containsCall information to "loop" and any parent loops. void AddContainsCallAllContainingLoops(FlowGraphNaturalLoop* loop); diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 6f9eba855f8399..9ba1d20cd4bd6b 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -620,14 +620,13 @@ void Compiler::fgReplaceJumpTarget(BasicBlock* block, BasicBlock* oldTarget, Bas case BBJ_EHCATCHRET: case BBJ_EHFILTERRET: case BBJ_LEAVE: // This function can be called before import, so we still have BBJ_LEAVE - - if (block->TargetIs(oldTarget)) - { - block->SetTarget(newTarget); - FlowEdge* const oldEdge = fgRemoveRefPred(oldTarget, block); - fgAddRefPred(newTarget, block, oldEdge); - } + { + assert(block->TargetIs(oldTarget)); + block->SetTarget(newTarget); + FlowEdge* const oldEdge = fgRemoveRefPred(oldTarget, block); + fgAddRefPred(newTarget, block, oldEdge); break; + } case BBJ_COND: @@ -709,6 +708,10 @@ void Compiler::fgReplaceJumpTarget(BasicBlock* block, BasicBlock* oldTarget, Bas break; } + case BBJ_EHFINALLYRET: + fgReplaceEhfSuccessor(block, oldTarget, newTarget); + break; + default: assert(!"Block doesn't have a jump target!"); unreached(); @@ -5297,11 +5300,8 @@ BasicBlock* Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable) case BBJ_ALWAYS: case BBJ_EHCATCHRET: case BBJ_SWITCH: - fgReplaceJumpTarget(predBlock, block, succBlock); - break; - case BBJ_EHFINALLYRET: - fgReplaceEhfSuccessor(predBlock, block, succBlock); + fgReplaceJumpTarget(predBlock, block, succBlock); break; } } diff --git a/src/coreclr/jit/fgehopt.cpp b/src/coreclr/jit/fgehopt.cpp index c72927f589fb4a..4c9a6e3b1dab10 100644 --- a/src/coreclr/jit/fgehopt.cpp +++ b/src/coreclr/jit/fgehopt.cpp @@ -1099,8 +1099,7 @@ PhaseStatus Compiler::fgCloneFinally() } else { - newBlock->CopyTarget(this, block); - optRedirectBlock(newBlock, &blockMap, RedirectBlockOption::AddToPredLists); + optRedirectBlock(block, newBlock, &blockMap); } } diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 85860f494f8139..1c40239b35beeb 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -5602,36 +5602,9 @@ void FlowGraphNaturalLoop::Duplicate(BasicBlock** insertAfter, BlockToBlockMap* // Jump target should not be set yet assert(!newBlk->HasInitializedTarget()); - // First copy the jump destination(s) from "blk". - newBlk->CopyTarget(comp, blk); - - // Now redirect the new block according to "blockMap". - comp->optRedirectBlock(newBlk, map); - - // Add predecessor edges for the new successors, as well as the fall-through paths. - switch (newBlk->GetKind()) - { - case BBJ_ALWAYS: - case BBJ_CALLFINALLY: - case BBJ_CALLFINALLYRET: - comp->fgAddRefPred(newBlk->GetTarget(), newBlk); - break; - - case BBJ_COND: - comp->fgAddRefPred(newBlk->GetFalseTarget(), newBlk); - comp->fgAddRefPred(newBlk->GetTrueTarget(), newBlk); - break; - - case BBJ_SWITCH: - for (BasicBlock* const switchDest : newBlk->SwitchTargets()) - { - comp->fgAddRefPred(switchDest, newBlk); - } - break; - - default: - break; - } + // Redirect the new block according to "blockMap". + // opRedirectBlock will set newBlk's successors, and add pred edges for the successors. + comp->optRedirectBlock(blk, newBlk, map); return BasicBlockVisit::Continue; }); diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 9d5c50f25d429f..c1362b987aab73 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -553,170 +553,162 @@ void Compiler::optCheckPreds() #endif // DEBUG //------------------------------------------------------------------------ -// optRedirectBlock: Replace the branch successors of a block based on a block map. +// optRedirectBlock: Initialize the branch successors of a block based on a block map. // -// Updates the successors of `blk`: if `blk2` is a branch successor of `blk`, and there is a mapping -// for `blk2->blk3` in `redirectMap`, change `blk` so that `blk3` is this branch successor. +// Updates the successors of `newBlk`, a copy of `blk`: +// If `blk2` is a branch successor of `blk`, and there is a mapping +// for `blk2->blk3` in `redirectMap`, make `blk3` a successor of `newBlk`. +// Else, make `blk2` a successor of `newBlk`. // // Arguments: -// blk - block to redirect -// redirectMap - block->block map specifying how the `blk` target will be redirected. -// predOption - specifies how to update the pred lists +// blk - the original block, which doesn't need redirecting +// newBlk - copy of blk, with uninitialized successors +// redirectMap - block->block map specifying how to redirect the target of `blk`. // // Notes: -// Pred lists for successors of `blk` may be changed, depending on `predOption`. +// Initially, `newBlk` should not have any successors set. +// Upon returning, `newBlk` should have all of its successors initialized. +// `blk` must have its successors set upon entry; these won't be changed. // -void Compiler::optRedirectBlock(BasicBlock* blk, BlockToBlockMap* redirectMap, RedirectBlockOption predOption) +void Compiler::optRedirectBlock(BasicBlock* blk, BasicBlock* newBlk, BlockToBlockMap* redirectMap) { - const bool updatePreds = (predOption == RedirectBlockOption::UpdatePredLists); - const bool addPreds = (predOption == RedirectBlockOption::AddToPredLists); + // Caller should not have initialized newBlk's target yet + assert(newBlk->KindIs(BBJ_ALWAYS)); + assert(!newBlk->HasInitializedTarget()); - BasicBlock* newJumpDest = nullptr; + BasicBlock* newTarget; + // Initialize the successors of "newBlk". + // For each successor, use "blockMap" to determine if the successor needs to be redirected. switch (blk->GetKind()) { - case BBJ_THROW: - case BBJ_RETURN: - case BBJ_EHFILTERRET: - case BBJ_EHFAULTRET: - case BBJ_EHCATCHRET: - // These have no jump destination to update. - break; - - case BBJ_CALLFINALLY: - if (addPreds && blk->bbFallsThrough()) - { - fgAddRefPred(blk->Next(), blk); - } + case BBJ_ALWAYS: + // Copy BBF_NONE_QUIRK flag for BBJ_ALWAYS blocks only + newBlk->CopyFlags(blk, BBF_NONE_QUIRK); FALLTHROUGH; - case BBJ_ALWAYS: - case BBJ_LEAVE: + case BBJ_CALLFINALLY: case BBJ_CALLFINALLYRET: - // All of these have a single jump destination to update. - if (redirectMap->Lookup(blk->GetTarget(), &newJumpDest)) + case BBJ_LEAVE: + // Determine if newBlk should be redirected to a different target from blk's target + if (redirectMap->Lookup(blk->GetTarget(), &newTarget)) { - if (updatePreds) - { - fgRemoveRefPred(blk->GetTarget(), blk); - } - blk->SetTarget(newJumpDest); - if (updatePreds || addPreds) - { - fgAddRefPred(newJumpDest, blk); - } + // newBlk needs to be redirected to a new target + newBlk->SetKindAndTarget(blk->GetKind(), newTarget); } - else if (addPreds) + else { - fgAddRefPred(blk->GetTarget(), blk); + // newBlk uses the same target as blk + newBlk->SetKindAndTarget(blk->GetKind(), blk->GetTarget()); } + + fgAddRefPred(newBlk->GetTarget(), newBlk); break; case BBJ_COND: - // Update jump taken when condition is true - if (redirectMap->Lookup(blk->GetTrueTarget(), &newJumpDest)) + { + BasicBlock* trueTarget; + BasicBlock* falseTarget; + + // Determine if newBLk should be redirected to a different true target from blk's true target + if (redirectMap->Lookup(blk->GetTrueTarget(), &newTarget)) { - if (updatePreds) - { - fgRemoveRefPred(blk->GetTrueTarget(), blk); - } - blk->SetTrueTarget(newJumpDest); - if (updatePreds || addPreds) - { - fgAddRefPred(newJumpDest, blk); - } + // newBlk needs to be redirected to a new true target + trueTarget = newTarget; } - else if (addPreds) + else { - fgAddRefPred(blk->GetTrueTarget(), blk); + // newBlk uses the same true target as blk + trueTarget = blk->GetTrueTarget(); } - // Update jump taken when condition is false - if (redirectMap->Lookup(blk->GetFalseTarget(), &newJumpDest)) + // Do the same lookup for the false target + if (redirectMap->Lookup(blk->GetFalseTarget(), &newTarget)) { - if (updatePreds) - { - fgRemoveRefPred(blk->GetFalseTarget(), blk); - } - blk->SetFalseTarget(newJumpDest); - if (updatePreds || addPreds) - { - fgAddRefPred(newJumpDest, blk); - } + falseTarget = newTarget; } - else if (addPreds) + else { - fgAddRefPred(blk->GetFalseTarget(), blk); + falseTarget = blk->GetFalseTarget(); } + + fgAddRefPred(trueTarget, newBlk); + fgAddRefPred(falseTarget, newBlk); + newBlk->SetCond(trueTarget, falseTarget); break; + } case BBJ_EHFINALLYRET: { - BBehfDesc* ehfDesc = blk->GetEhfTargets(); - BasicBlock* newSucc = nullptr; - for (unsigned i = 0; i < ehfDesc->bbeCount; i++) + BBehfDesc* currEhfDesc = blk->GetEhfTargets(); + BBehfDesc* newEhfDesc = new (this, CMK_BasicBlock) BBehfDesc; + newEhfDesc->bbeCount = currEhfDesc->bbeCount; + newEhfDesc->bbeSuccs = new (this, CMK_BasicBlock) BasicBlock*[newEhfDesc->bbeCount]; + BasicBlock** jumpPtr = newEhfDesc->bbeSuccs; + + for (BasicBlock* const ehfTarget : blk->EHFinallyRetSuccs()) { - BasicBlock* const succ = ehfDesc->bbeSuccs[i]; - if (redirectMap->Lookup(succ, &newSucc)) + // Determine if newBlk should target ehfTarget, or be redirected + if (redirectMap->Lookup(ehfTarget, &newTarget)) { - if (updatePreds) - { - fgRemoveRefPred(succ, blk); - } - if (updatePreds || addPreds) - { - fgAddRefPred(newSucc, blk); - } - ehfDesc->bbeSuccs[i] = newSucc; + *jumpPtr = newTarget; } - else if (addPreds) + else { - fgAddRefPred(succ, blk); + *jumpPtr = ehfTarget; } + + fgAddRefPred(*jumpPtr, newBlk); + jumpPtr++; } + + newBlk->SetEhf(newEhfDesc); + break; } - break; case BBJ_SWITCH: { - bool redirected = false; - for (unsigned i = 0; i < blk->GetSwitchTargets()->bbsCount; i++) + BBswtDesc* currSwtDesc = blk->GetSwitchTargets(); + BBswtDesc* newSwtDesc = new (this, CMK_BasicBlock) BBswtDesc(currSwtDesc); + newSwtDesc->bbsDstTab = new (this, CMK_BasicBlock) BasicBlock*[newSwtDesc->bbsCount]; + BasicBlock** jumpPtr = newSwtDesc->bbsDstTab; + + for (BasicBlock* const switchTarget : blk->SwitchTargets()) { - BasicBlock* const switchDest = blk->GetSwitchTargets()->bbsDstTab[i]; - if (redirectMap->Lookup(switchDest, &newJumpDest)) - { - if (updatePreds) - { - fgRemoveRefPred(switchDest, blk); - } - if (updatePreds || addPreds) - { - fgAddRefPred(newJumpDest, blk); - } - blk->GetSwitchTargets()->bbsDstTab[i] = newJumpDest; - redirected = true; - } - else if (addPreds) + // Determine if newBlk should target switchTarget, or be redirected + if (redirectMap->Lookup(switchTarget, &newTarget)) { - fgAddRefPred(switchDest, blk); + *jumpPtr = newTarget; } - } - // If any redirections happened, invalidate the switch table map for the switch. - if (redirected) - { - // Don't create a new map just to try to remove an entry. - BlockToSwitchDescMap* switchMap = GetSwitchDescMap(/* createIfNull */ false); - if (switchMap != nullptr) + else { - switchMap->Remove(blk); + *jumpPtr = switchTarget; } + + fgAddRefPred(*jumpPtr, newBlk); + jumpPtr++; } + + newBlk->SetSwitch(newSwtDesc); + break; } - break; + + case BBJ_EHCATCHRET: + case BBJ_EHFILTERRET: + // newBlk's jump target should not need to be redirected + assert(!redirectMap->Lookup(blk->GetTarget(), &newTarget)); + newBlk->SetKindAndTarget(blk->GetKind(), blk->GetTarget()); + fgAddRefPred(newBlk->GetTarget(), newBlk); + break; default: - unreached(); + // blk doesn't have a jump destination + assert(blk->NumSucc() == 0); + newBlk->SetKindAndTarget(blk->GetKind()); + break; } + + assert(newBlk->KindIs(blk->GetKind())); } //----------------------------------------------------------------------------- @@ -2219,8 +2211,6 @@ bool Compiler::optInvertWhileLoop(BasicBlock* block) // is maintained no matter which condition block we point to, but we'll lose optimization potential // (and create spaghetti code) if we get it wrong. // - BlockToBlockMap blockMap(getAllocator(CMK_LoopOpt)); - bool blockMapInitialized = false; unsigned const loopFirstNum = bTop->bbNum; unsigned const loopBottomNum = bTest->bbNum; @@ -2233,16 +2223,30 @@ bool Compiler::optInvertWhileLoop(BasicBlock* block) continue; } - if (!blockMapInitialized) - { - blockMapInitialized = true; - blockMap.Set(bTest, bNewCond); - } - // Redirect the predecessor to the new block. JITDUMP("Redirecting non-loop " FMT_BB " -> " FMT_BB " to " FMT_BB " -> " FMT_BB "\n", predBlock->bbNum, bTest->bbNum, predBlock->bbNum, bNewCond->bbNum); - optRedirectBlock(predBlock, &blockMap, RedirectBlockOption::UpdatePredLists); + + switch (predBlock->GetKind()) + { + case BBJ_ALWAYS: + case BBJ_CALLFINALLY: + case BBJ_CALLFINALLYRET: + case BBJ_COND: + case BBJ_SWITCH: + case BBJ_EHFINALLYRET: + fgReplaceJumpTarget(predBlock, bTest, bNewCond); + break; + + case BBJ_EHCATCHRET: + case BBJ_EHFILTERRET: + // These block types should not need redirecting + break; + + default: + assert(!"Unexpected bbKind for predecessor block"); + break; + } } // If we have profile data for all blocks and we know that we are cloning the From 355b19d82592f711a6caf610f3f7ab97f773db4a Mon Sep 17 00:00:00 2001 From: Jeremi Kurdek <59935235+jkurdek@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:54:05 +0100 Subject: [PATCH 084/158] [mono] Intrinsify multiple StoreVector API's (#98514) * enabled storevector 128/64x* intrinsics and tests * implemented storevector 128/64x* intrinsics * applied review suggestions * implemented storevector128/64x*andzip intrinsics * applied review suggestions --- .../Arm/AdvSimd.PlatformNotSupported.cs | 4 +- .../System/Runtime/Intrinsics/Arm/AdvSimd.cs | 4 +- .../ref/System.Runtime.Intrinsics.cs | 4 +- src/mono/mono/mini/llvm-intrinsics.h | 13 +- src/mono/mono/mini/mini-llvm.c | 22 ++ src/mono/mono/mini/mini-ops.h | 2 + src/mono/mono/mini/simd-intrinsics.c | 44 ++++ src/mono/mono/mini/simd-methods.h | 12 ++ .../GenerateHWIntrinsicTests_Arm.cs | 204 +++++++++--------- 9 files changed, 200 insertions(+), 109 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs index 250e847b1b5c1e..7ccb2b70e6eb15 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs @@ -3949,6 +3949,7 @@ internal Arm64() { } /// A64: ST3 { Vt.2D, Vt+1.2D, Vt+2.2D, Vt+3.2D }[index], [Xn] /// public static unsafe void StoreSelectedScalar(double* address, (Vector128 value1, Vector128 value2, Vector128 value3, Vector128 value4) value, [ConstantExpected(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } +#endif /// /// A64: ST2 { Vn.16B, Vn+1.16B }, [Xn] @@ -4249,7 +4250,6 @@ internal Arm64() { } /// A64: ST1 { Vn.2D, Vn+1.2D, Vn+2.2D, Vn+3.2D }, [Xn] /// public static unsafe void StoreVector128x4(double* address, (Vector128 Value1, Vector128 Value2, Vector128 Value3, Vector128 Value4) value) { throw new PlatformNotSupportedException(); } -#endif /// /// float64x2_t vsubq_f64 (float64x2_t a, float64x2_t b) @@ -16039,6 +16039,7 @@ internal Arm64() { } /// A64: ST4 { Vt.2S, Vt+1.2S, Vt+2.2S, Vt+3.2S }[index], [Xn] /// public static unsafe void StoreSelectedScalar(float* address, (Vector64 value1, Vector64 value2, Vector64 value3, Vector64 value4) value, [ConstantExpected(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } +#endif /// /// A64: ST2 { Vn.8B, Vn+1.8B }, [Xn] @@ -16249,7 +16250,6 @@ internal Arm64() { } /// A64: ST1 { Vn.2S, Vn+1.2S, Vn+2.2S, Vn+3.2S }, [Xn] /// public static unsafe void StoreVector64x4(float* address, (Vector64 Value1, Vector64 Value2, Vector64 Value3, Vector64 Value4) value) { throw new PlatformNotSupportedException(); } -#endif /// /// uint8x8_t vsub_u8 (uint8x8_t a, uint8x8_t b) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs index 1bd012596acfed..8ead33cfde5afc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs @@ -3947,6 +3947,7 @@ internal Arm64() { } /// A64: ST4 { Vt.2D, Vt+1.2D, Vt+2.2D, Vt+3.2D }[index], [Xn] /// public static unsafe void StoreSelectedScalar(double* address, (Vector128 value1, Vector128 value2, Vector128 value3, Vector128 value4) value, [ConstantExpected(Max = (byte)(1))] byte index) => StoreSelectedScalar(address, value, index); +#endif /// /// A64: ST2 { Vn.16B, Vn+1.16B }, [Xn] @@ -4247,7 +4248,6 @@ internal Arm64() { } /// A64: ST1 { Vn.2D, Vn+1.2D, Vn+2.2D, Vn+3.2D }, [Xn] /// public static unsafe void StoreVector128x4(double* address, (Vector128 Value1, Vector128 Value2, Vector128 Value3, Vector128 Value4) value) => StoreVector128x4(address, value); -#endif /// /// float64x2_t vsubq_f64 (float64x2_t a, float64x2_t b) @@ -16036,6 +16036,7 @@ internal Arm64() { } /// A64: ST4 { Vt.2S, Vt+1.2S, Vt+2.2S, Vt+3.2S }[index], [Xn] /// public static unsafe void StoreSelectedScalar(float* address, (Vector64 value1, Vector64 value2, Vector64 value3, Vector64 value4) value, [ConstantExpected(Max = (byte)(1))] byte index) => StoreSelectedScalar(address, value, index); +#endif /// /// A64: ST2 { Vn.8B, Vn+1.8B }, [Xn] @@ -16246,7 +16247,6 @@ internal Arm64() { } /// A64: ST1 { Vn.2S, Vn+1.2S, Vn+2.2S, Vn+3.2S }, [Xn] /// public static unsafe void StoreVector64x4(float* address, (Vector64 Value1, Vector64 Value2, Vector64 Value3, Vector64 Value4) value) => StoreVector64x4(address, value); -#endif /// /// uint8x8_t vsub_u8 (uint8x8_t a, uint8x8_t b) diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index 3dcd043d38ce02..82e179628b11f4 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -2927,6 +2927,7 @@ public static unsafe void StoreSelectedScalar(ulong* address, System.Runtime.Int public static unsafe void StoreSelectedScalar(int* address, (System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2, System.Runtime.Intrinsics.Vector64 value3, System.Runtime.Intrinsics.Vector64 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } public static unsafe void StoreSelectedScalar(uint* address, (System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2, System.Runtime.Intrinsics.Vector64 value3, System.Runtime.Intrinsics.Vector64 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } public static unsafe void StoreSelectedScalar(float* address, (System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2, System.Runtime.Intrinsics.Vector64 value3, System.Runtime.Intrinsics.Vector64 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } +#endif public unsafe static void StoreVector64x2AndZip(byte* address, (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2) value) { throw null; } public unsafe static void StoreVector64x2AndZip(sbyte* address, (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2) value) { throw null; } public unsafe static void StoreVector64x2AndZip(short* address, (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2) value) { throw null; } @@ -2969,7 +2970,6 @@ public static unsafe void StoreSelectedScalar(ulong* address, System.Runtime.Int public static unsafe void StoreVector64x4(int* address, (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2, System.Runtime.Intrinsics.Vector64 Value3, System.Runtime.Intrinsics.Vector64 Value4) value) { throw null; } public static unsafe void StoreVector64x4(uint* address, (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2, System.Runtime.Intrinsics.Vector64 Value3, System.Runtime.Intrinsics.Vector64 Value4) value) { throw null; } public static unsafe void StoreVector64x4(float* address, (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2, System.Runtime.Intrinsics.Vector64 Value3, System.Runtime.Intrinsics.Vector64 Value4) value) { throw null; } -#endif public static System.Runtime.Intrinsics.Vector128 Subtract(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 Subtract(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector128 Subtract(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } @@ -3780,6 +3780,7 @@ public static unsafe void StorePairScalarNonTemporal(uint* address, System.Runti public static unsafe void StoreSelectedScalar(ulong* address, (System.Runtime.Intrinsics.Vector128 value1, System.Runtime.Intrinsics.Vector128 value2, System.Runtime.Intrinsics.Vector128 value3, System.Runtime.Intrinsics.Vector128 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { throw null; } public static unsafe void StoreSelectedScalar(float* address, (System.Runtime.Intrinsics.Vector128 value1, System.Runtime.Intrinsics.Vector128 value2, System.Runtime.Intrinsics.Vector128 value3, System.Runtime.Intrinsics.Vector128 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(3))] byte index) { throw null; } public static unsafe void StoreSelectedScalar(double* address, (System.Runtime.Intrinsics.Vector128 value1, System.Runtime.Intrinsics.Vector128 value2, System.Runtime.Intrinsics.Vector128 value3, System.Runtime.Intrinsics.Vector128 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { throw null; } +#endif public unsafe static void StoreVector128x2AndZip(byte* address, (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2) value) { throw null; } public unsafe static void StoreVector128x2AndZip(sbyte* address, (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2) value) { throw null; } public unsafe static void StoreVector128x2AndZip(short* address, (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2) value) { throw null; } @@ -3840,7 +3841,6 @@ public static unsafe void StorePairScalarNonTemporal(uint* address, System.Runti public static unsafe void StoreVector128x4(ulong* address, (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2, System.Runtime.Intrinsics.Vector128 Value3, System.Runtime.Intrinsics.Vector128 Value4) value) { throw null; } public static unsafe void StoreVector128x4(float* address, (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2, System.Runtime.Intrinsics.Vector128 Value3, System.Runtime.Intrinsics.Vector128 Value4) value) { throw null; } public static unsafe void StoreVector128x4(double* address, (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2, System.Runtime.Intrinsics.Vector128 Value3, System.Runtime.Intrinsics.Vector128 Value4) value) { throw null; } -#endif public static System.Runtime.Intrinsics.Vector128 Subtract(System.Runtime.Intrinsics.Vector128 left, System.Runtime.Intrinsics.Vector128 right) { throw null; } public static System.Runtime.Intrinsics.Vector64 SubtractSaturateScalar(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } public static System.Runtime.Intrinsics.Vector64 SubtractSaturateScalar(System.Runtime.Intrinsics.Vector64 left, System.Runtime.Intrinsics.Vector64 right) { throw null; } diff --git a/src/mono/mono/mini/llvm-intrinsics.h b/src/mono/mono/mini/llvm-intrinsics.h index 60aba3795723b5..ff8dfa2998a335 100644 --- a/src/mono/mono/mini/llvm-intrinsics.h +++ b/src/mono/mono/mini/llvm-intrinsics.h @@ -386,7 +386,18 @@ INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD4LANE_V64, aarch64_neon_ld4lane, Arm64, INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD2LANE_V128, aarch64_neon_ld2lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD3LANE_V128, aarch64_neon_ld3lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD4LANE_V128, aarch64_neon_ld4lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) - +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST1X2_V64, aarch64_neon_st1x2, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST1X3_V64, aarch64_neon_st1x3, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST1X4_V64, aarch64_neon_st1x4, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST1X2_V128, aarch64_neon_st1x2, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST1X3_V128, aarch64_neon_st1x3, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST1X4_V128, aarch64_neon_st1x4, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST2_V64, aarch64_neon_st2, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST3_V64, aarch64_neon_st3, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST4_V64, aarch64_neon_st4, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST2_V128, aarch64_neon_st2, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST3_V128, aarch64_neon_st3, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST4_V128, aarch64_neon_st4, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_SMAXV, aarch64_neon_smaxv, Arm64, Across, V64 | V128 | I1 | I2 | I4) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_UMAXV, aarch64_neon_umaxv, Arm64, Across, V64 | V128 | I1 | I2 | I4) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_SMINV, aarch64_neon_sminv, Arm64, Across, V64 | V128 | I1 | I2 | I4) diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index dff8828b6cd0a2..03a5a2485eb688 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -11749,6 +11749,28 @@ MONO_RESTORE_WARNING values [ins->dreg] = result; break; } + case OP_ARM64_STM: { + LLVMTypeRef tuple_t = simd_valuetuple_to_llvm_type (ctx, ins->klass); + LLVMTypeRef vec_t = LLVMGetElementType (tuple_t); + + IntrinsicId iid = (IntrinsicId) ins->inst_c0; + llvm_ovr_tag_t ovr_tag = ovr_tag_from_llvm_type (vec_t); + + LLVMValueRef value_tuple = LLVMBuildLoad2 (builder, tuple_t, addresses [ins->sreg2]->value, "load_param"); + + int len = LLVMGetArrayLength (tuple_t); + + LLVMValueRef *args = g_alloca ((len + 1) * sizeof (LLVMValueRef)); + + for (int i = 0; i < len; i++) { + LLVMValueRef elem = LLVMBuildExtractValue (builder, value_tuple, i, "extract_elem"); + args [i] = elem; + } + args [len] = lhs; + + call_overloaded_intrins (ctx, iid, ovr_tag, args, ""); + break; + } case OP_ARM64_ST1: { LLVMTypeRef t = LLVMTypeOf (rhs); LLVMValueRef address = convert (ctx, lhs, pointer_type (t)); diff --git a/src/mono/mono/mini/mini-ops.h b/src/mono/mono/mini/mini-ops.h index 1ad42c16d676ef..81d9dd29332adf 100644 --- a/src/mono/mono/mini/mini-ops.h +++ b/src/mono/mono/mini/mini-ops.h @@ -1650,6 +1650,8 @@ MINI_OP(OP_ARM64_ST1, "arm64_st1", NONE, IREG, XREG) MINI_OP(OP_ARM64_SXTL, "arm64_sxtl", XREG, XREG, NONE) MINI_OP(OP_ARM64_SXTL2, "arm64_sxtl2", XREG, XREG, NONE) +MINI_OP(OP_ARM64_STM, "arm64_stm", NONE, IREG, VREG) + MINI_OP(OP_ARM64_SMULH, "arm64_smulh", LREG, LREG, LREG) MINI_OP(OP_ARM64_SQRT_SCALAR, "arm64_sqrt_scalar", XREG, XREG, NONE) MINI_OP(OP_ARM64_TRN1, "arm64_trn1", XREG, XREG, XREG) diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index 2c99dccd9f3380..fe8cdaa1dd69aa 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -3957,6 +3957,18 @@ static SimdIntrinsic advsimd_methods [] = { {SN_StorePairScalar, OP_ARM64_STP_SCALAR}, {SN_StorePairScalarNonTemporal, OP_ARM64_STNP_SCALAR}, {SN_StoreSelectedScalar}, + {SN_StoreVector128x2}, + {SN_StoreVector128x2AndZip}, + {SN_StoreVector128x3}, + {SN_StoreVector128x3AndZip}, + {SN_StoreVector128x4}, + {SN_StoreVector128x4AndZip}, + {SN_StoreVector64x2}, + {SN_StoreVector64x2AndZip}, + {SN_StoreVector64x3}, + {SN_StoreVector64x3AndZip}, + {SN_StoreVector64x4}, + {SN_StoreVector64x4AndZip}, {SN_Subtract, OP_XBINOP, OP_ISUB, None, None, OP_XBINOP, OP_FSUB}, {SN_SubtractHighNarrowingLower, OP_ARM64_SUBHN}, {SN_SubtractHighNarrowingUpper, OP_ARM64_SUBHN2}, @@ -4355,6 +4367,38 @@ emit_arm64_intrinsics ( MONO_ADD_INS (cfg->cbb, ins); return ins; } + case SN_StoreVector128x2: + case SN_StoreVector128x3: + case SN_StoreVector128x4: + case SN_StoreVector64x2: + case SN_StoreVector64x3: + case SN_StoreVector64x4: + case SN_StoreVector128x2AndZip: + case SN_StoreVector128x3AndZip: + case SN_StoreVector128x4AndZip: + case SN_StoreVector64x2AndZip: + case SN_StoreVector64x3AndZip: + case SN_StoreVector64x4AndZip: { + int iid = 0; + switch (id) { + case SN_StoreVector128x2: iid = INTRINS_AARCH64_ADV_SIMD_ST1X2_V128; break; + case SN_StoreVector128x3: iid = INTRINS_AARCH64_ADV_SIMD_ST1X3_V128; break; + case SN_StoreVector128x4: iid = INTRINS_AARCH64_ADV_SIMD_ST1X4_V128; break; + case SN_StoreVector64x2: iid = INTRINS_AARCH64_ADV_SIMD_ST1X2_V64; break; + case SN_StoreVector64x3: iid = INTRINS_AARCH64_ADV_SIMD_ST1X3_V64; break; + case SN_StoreVector64x4: iid = INTRINS_AARCH64_ADV_SIMD_ST1X4_V64; break; + case SN_StoreVector128x2AndZip: iid = INTRINS_AARCH64_ADV_SIMD_ST2_V128; break; + case SN_StoreVector128x3AndZip: iid = INTRINS_AARCH64_ADV_SIMD_ST3_V128; break; + case SN_StoreVector128x4AndZip: iid = INTRINS_AARCH64_ADV_SIMD_ST4_V128; break; + case SN_StoreVector64x2AndZip: iid = INTRINS_AARCH64_ADV_SIMD_ST2_V64; break; + case SN_StoreVector64x3AndZip: iid = INTRINS_AARCH64_ADV_SIMD_ST3_V64; break; + case SN_StoreVector64x4AndZip: iid = INTRINS_AARCH64_ADV_SIMD_ST4_V64; break; + default: g_assert_not_reached (); + } + + MonoClass* klass_tuple_var = mono_class_from_mono_type_internal (fsig->params [1]); + return emit_simd_ins_for_sig (cfg, klass_tuple_var, OP_ARM64_STM, iid, arg0_type, fsig, args); + } default: g_assert_not_reached (); } diff --git a/src/mono/mono/mini/simd-methods.h b/src/mono/mono/mini/simd-methods.h index 3bc775e9aee5a6..70138b552685b2 100644 --- a/src/mono/mono/mini/simd-methods.h +++ b/src/mono/mono/mini/simd-methods.h @@ -624,6 +624,18 @@ METHOD(StorePairNonTemporal) METHOD(StorePairScalar) METHOD(StorePairScalarNonTemporal) METHOD(StoreSelectedScalar) +METHOD(StoreVector128x2) +METHOD(StoreVector128x3) +METHOD(StoreVector128x4) +METHOD(StoreVector128x2AndZip) +METHOD(StoreVector128x3AndZip) +METHOD(StoreVector128x4AndZip) +METHOD(StoreVector64x2) +METHOD(StoreVector64x3) +METHOD(StoreVector64x4) +METHOD(StoreVector64x2AndZip) +METHOD(StoreVector64x3AndZip) +METHOD(StoreVector64x4AndZip) METHOD(SubtractHighNarrowingLower) METHOD(SubtractHighNarrowingUpper) METHOD(SubtractRoundedHighNarrowingLower) diff --git a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs index 8f47895a33408c..510948877cd8b9 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs @@ -1724,48 +1724,48 @@ // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2SByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2ByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2UShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2ShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2UInt32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Int32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2FloatAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3SByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3ByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3UShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3ShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3UInt32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3Int32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3FloatAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4SByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4ByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4UShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4ShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4UInt32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4Int32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4FloatAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2SByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2ByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2UShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2ShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2UInt32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Int32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2FloatAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3SByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3ByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3UShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3ShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3UInt32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3Int32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector64x3FloatAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x3AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4SByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4ByteAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4UShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4ShortAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4UInt32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4Int32AndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector64x4FloatAndZip", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x4AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), ("VecBinOpTest.template", new Dictionary { ["TestName"] = "Subtract_Vector64_Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "Subtract", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "Helpers.Subtract(left[i], right[i]) != result[i]"}), ("VecBinOpTest.template", new Dictionary { ["TestName"] = "Subtract_Vector64_Int16", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "Subtract", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "Helpers.Subtract(left[i], right[i]) != result[i]"}), ("VecBinOpTest.template", new Dictionary { ["TestName"] = "Subtract_Vector64_Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "Subtract", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int32", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "Helpers.Subtract(left[i], right[i]) != result[i]"}), @@ -2574,66 +2574,66 @@ // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2SByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2ByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2ShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UInt32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Int32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UInt64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Int64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2FloatAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2DoubleAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3SByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3ByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3ShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UInt32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Int32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UInt64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Int64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3FloatAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3DoubleAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4SByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4ByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4ShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UInt32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Int32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UInt64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Int64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4FloatAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), - // ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4DoubleAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i] || input3[i] != result[(OpElementCount * 2) + i] || input4[i] != result[(OpElementCount * 3) + i]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2SByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2ByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2ShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UInt32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Int32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UInt64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Int64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2FloatAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2DoubleAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2AndZip", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i * 2] || input2[i] != result[(i * 2) + 1]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3SByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3ByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3ShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UInt32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Int32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3UInt64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3Int64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3FloatAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx3Test.template", new Dictionary { ["TestName"] = "StoreVector128x3DoubleAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x3AndZip", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i * 3] || input2[i] != result[(i * 3) + 1] || input3[i] != result[(i * 3) + 2]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4SByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4ByteAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4ShortAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UInt32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Int32AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4UInt64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4Int64AndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4FloatAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), + ("StoreVectorx4Test.template", new Dictionary { ["TestName"] = "StoreVector128x4DoubleAndZip", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x4AndZip", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "input1[i] != result[i * 4] || input2[i] != result[(i * 4) + 1] || input3[i] != result[(i * 4) + 2] || input4[i] != result[(i * 4) + 3]"}), ("VecBinOpTest.template", new Dictionary { ["TestName"] = "Subtract_Vector128_Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "Subtract", ["RetVectorType"] = "Vector128", ["RetBaseType"] = "Double", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "Double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Double", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetDouble()", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateIterResult"] = "BitConverter.DoubleToInt64Bits(Helpers.Subtract(left[i], right[i])) != BitConverter.DoubleToInt64Bits(result[i])"}), ("SimpleBinOpTest.template", new Dictionary { ["TestName"] = "SubtractSaturateScalar_Vector64_Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "SubtractSaturateScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Byte", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetByte()", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateFirstResult"] = "Helpers.SubtractSaturate(left[0], right[0]) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0"}), ("SimpleBinOpTest.template", new Dictionary { ["TestName"] = "SubtractSaturateScalar_Vector64_Int16", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "SubtractSaturateScalar", ["RetVectorType"] = "Vector64", ["RetBaseType"] = "Int16", ["Op1VectorType"] = "Vector64", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp1"] = "TestLibrary.Generator.GetInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateFirstResult"] = "Helpers.SubtractSaturate(left[0], right[0]) != result[0]", ["ValidateRemainingResults"] = "result[i] != 0"}), From 24330e90c8d98d46fd5f70bd6c743e4749c7dff5 Mon Sep 17 00:00:00 2001 From: ashwinik30 <109573163+ashwinik30@users.noreply.github.com> Date: Fri, 16 Feb 2024 23:59:14 +0530 Subject: [PATCH 085/158] Use double braces to initialize OSSL_PARAM so both clang and gcc are happy Co-authored-by: Ashwini Kadam --- .../libs/System.Security.Cryptography.Native/pal_evp_mac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_mac.c b/src/native/libs/System.Security.Cryptography.Native/pal_evp_mac.c index f3bce0a7890960..38bac3258913c9 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_evp_mac.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_mac.c @@ -121,7 +121,7 @@ int32_t CryptoNative_EvpMacInit(EVP_MAC_CTX* ctx, size_t keyLengthT = Int32ToSizeT(keyLength); - OSSL_PARAM params[4] = { 0 }; + OSSL_PARAM params[4] = {{0}}; int i = 0; params[i++] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, (void*) key, keyLengthT); params[i++] = OSSL_PARAM_construct_int32(OSSL_MAC_PARAM_XOF, &xof); @@ -340,7 +340,7 @@ int32_t CryptoNative_EvpMacOneShot(EVP_MAC* mac, size_t dataLengthT = Int32ToSizeT(dataLength); size_t macLengthT = Int32ToSizeT(destinationLength); - OSSL_PARAM params[5] = { 0 }; + OSSL_PARAM params[5] = {{0}}; int i = 0; params[i++] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY, (void*)key, keyLengthT); From cd850233414468a8ffe84fc8fa05a36d0b3b1316 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 16 Feb 2024 10:35:47 -0800 Subject: [PATCH 086/158] JIT: ARM64 SVE format encodings, `SVE_HX_3A_B` to `SVE_JL_3A` and `SVE_IC_3A` to `SVE_IC_3A_C` (#98332) * Initial format boilerplate * SVE_HX_3A_B format working * SVE_HX_3A_E working * SVE_IV_3A working * SVE_JI_3A_A and SVE_JL_3A working * SVE_IC_3A working * cleanup * SVE_IC_3A_A working * Finishing up * remove case * Fix build. Added emitInsSve_R_R_R_I * Formatting --- src/coreclr/jit/codegenarm64test.cpp | 126 ++ src/coreclr/jit/emitarm64.cpp | 1643 +++++++++++++++++++------- src/coreclr/jit/emitarm64.h | 88 +- src/coreclr/jit/instrsarm64sve.h | 8 +- 4 files changed, 1442 insertions(+), 423 deletions(-) diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index ac75834f317e05..47cc0fb6bfd194 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -7598,6 +7598,132 @@ void CodeGen::genArm64EmitterUnitTestsSve() INS_SCALABLE_OPTS_UNPREDICATED); theEmitter->emitIns_R_R_I(INS_sve_str, EA_SCALABLE, REG_V2, REG_R3, 255, INS_OPTS_NONE, INS_SCALABLE_OPTS_UNPREDICATED); + + // IF_SVE_HX_3A_B + theEmitter->emitIns_R_R_R_I(INS_sve_ld1b, EA_SCALABLE, REG_V0, REG_P0, REG_V1, 0, + INS_OPTS_SCALABLE_S); // LD1B {.S }, /Z, [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1sb, EA_SCALABLE, REG_V2, REG_P7, REG_V3, 5, + INS_OPTS_SCALABLE_S); // LD1SB {.S }, /Z, [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1b, EA_SCALABLE, REG_V4, REG_P3, REG_V1, 5, + INS_OPTS_SCALABLE_S); // LDFF1B {.S }, /Z, [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1sb, EA_SCALABLE, REG_V2, REG_P6, REG_V0, 31, + INS_OPTS_SCALABLE_S); // LDFF1SB {.S }, /Z, [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1b, EA_SCALABLE, REG_V0, REG_P0, REG_V1, 0, + INS_OPTS_SCALABLE_D); // LD1B {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1sb, EA_SCALABLE, REG_V2, REG_P7, REG_V3, 5, + INS_OPTS_SCALABLE_D); // LD1SB {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1b, EA_SCALABLE, REG_V4, REG_P3, REG_V1, 5, + INS_OPTS_SCALABLE_D); // LDFF1B {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1sb, EA_SCALABLE, REG_V2, REG_P6, REG_V0, 31, + INS_OPTS_SCALABLE_D); // LDFF1SB {.D }, /Z, [.D{, #}] + + // IF_SVE_HX_3A_E + theEmitter->emitIns_R_R_R_I(INS_sve_ld1h, EA_SCALABLE, REG_V1, REG_P0, REG_V2, 0, + INS_OPTS_SCALABLE_S); // LD1H {.S }, /Z, [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1sh, EA_SCALABLE, REG_V2, REG_P4, REG_V3, 2, + INS_OPTS_SCALABLE_S); // LD1SH {.S }, /Z, [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1w, EA_SCALABLE, REG_V1, REG_P2, REG_V9, 124, + INS_OPTS_SCALABLE_S); // LD1W {.S }, /Z, [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1h, EA_SCALABLE, REG_V4, REG_P7, REG_V3, 6, + INS_OPTS_SCALABLE_S); // LDFF1H {.S }, /Z, [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1sh, EA_SCALABLE, REG_V3, REG_P5, REG_V4, 62, + INS_OPTS_SCALABLE_S); // LDFF1SH {.S }, /Z, [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1w, EA_SCALABLE, REG_V2, REG_P1, REG_V3, 124, + INS_OPTS_SCALABLE_S); // LDFF1W {.S }, /Z, [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1h, EA_SCALABLE, REG_V1, REG_P0, REG_V2, 0, + INS_OPTS_SCALABLE_D); // LD1H {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1sh, EA_SCALABLE, REG_V2, REG_P4, REG_V3, 2, + INS_OPTS_SCALABLE_D); // LD1SH {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1w, EA_SCALABLE, REG_V1, REG_P2, REG_V9, 124, + INS_OPTS_SCALABLE_D); // LD1W {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1h, EA_SCALABLE, REG_V4, REG_P7, REG_V3, 6, + INS_OPTS_SCALABLE_D); // LDFF1H {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1sh, EA_SCALABLE, REG_V3, REG_P5, REG_V4, 62, + INS_OPTS_SCALABLE_D); // LDFF1SH {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1w, EA_SCALABLE, REG_V2, REG_P1, REG_V3, 124, + INS_OPTS_SCALABLE_D); // LDFF1W {.D }, /Z, [.D{, #}] + + // IF_SVE_IV_3A + theEmitter->emitIns_R_R_R_I(INS_sve_ld1d, EA_SCALABLE, REG_V1, REG_P2, REG_V3, 0, + INS_OPTS_SCALABLE_D); // LD1D {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1sw, EA_SCALABLE, REG_V6, REG_P5, REG_V4, 0, + INS_OPTS_SCALABLE_D); // LD1SW {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1d, EA_SCALABLE, REG_V7, REG_P3, REG_V1, 248, + INS_OPTS_SCALABLE_D); // LDFF1D {.D }, /Z, [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ldff1sw, EA_SCALABLE, REG_V2, REG_P0, REG_V4, 124, + INS_OPTS_SCALABLE_D); // LDFF1SW {.D }, /Z, [.D{, #}] + + // IF_SVE_JI_3A_A + theEmitter->emitIns_R_R_R_I(INS_sve_st1b, EA_SCALABLE, REG_V1, REG_P2, REG_V3, 0, + INS_OPTS_SCALABLE_S); // ST1B {.S }, , [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1b, EA_SCALABLE, REG_V1, REG_P2, REG_V3, 31, + INS_OPTS_SCALABLE_S); // ST1B {.S }, , [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1h, EA_SCALABLE, REG_V5, REG_P3, REG_V2, 0, + INS_OPTS_SCALABLE_S); // ST1H {.S }, , [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1h, EA_SCALABLE, REG_V5, REG_P3, REG_V2, 62, + INS_OPTS_SCALABLE_S); // ST1H {.S }, , [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1w, EA_SCALABLE, REG_V5, REG_P4, REG_V1, 0, + INS_OPTS_SCALABLE_S); // ST1W {.S }, , [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1w, EA_SCALABLE, REG_V5, REG_P4, REG_V1, 124, + INS_OPTS_SCALABLE_S); // ST1W {.S }, , [.S{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1b, EA_SCALABLE, REG_V1, REG_P2, REG_V3, 0, + INS_OPTS_SCALABLE_D); // ST1B {.D }, , [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1b, EA_SCALABLE, REG_V1, REG_P2, REG_V3, 31, + INS_OPTS_SCALABLE_D); // ST1B {.D }, , [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1h, EA_SCALABLE, REG_V5, REG_P3, REG_V2, 0, + INS_OPTS_SCALABLE_D); // ST1H {.D }, , [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1h, EA_SCALABLE, REG_V5, REG_P3, REG_V2, 62, + INS_OPTS_SCALABLE_D); // ST1H {.D }, , [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1w, EA_SCALABLE, REG_V5, REG_P4, REG_V1, 0, + INS_OPTS_SCALABLE_D); // ST1W {.D }, , [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1w, EA_SCALABLE, REG_V5, REG_P4, REG_V1, 124, + INS_OPTS_SCALABLE_D); // ST1W {.D }, , [.D{, #}] + + // IF_SVE_JL_3A + theEmitter->emitIns_R_R_R_I(INS_sve_st1d, EA_SCALABLE, REG_V3, REG_P7, REG_V4, 0, + INS_OPTS_SCALABLE_D); // ST1D {.D }, , [.D{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_st1d, EA_SCALABLE, REG_V3, REG_P7, REG_V4, 248, + INS_OPTS_SCALABLE_D); // ST1D {.D }, , [.D{, #}] + + // IF_SVE_IC_3A + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rd, EA_SCALABLE, REG_V1, REG_P2, REG_R3, 504, + INS_OPTS_SCALABLE_D); // LD1RD {.D }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rsw, EA_SCALABLE, REG_V4, REG_P5, REG_R6, 252, + INS_OPTS_SCALABLE_D); // LD1RSW {.D }, /Z, [{, #}] + + // IF_SVE_IC_3A_A + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rsh, EA_SCALABLE, REG_V0, REG_P1, REG_R2, 0, + INS_OPTS_SCALABLE_S); // LD1RSH {.S }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rw, EA_SCALABLE, REG_V5, REG_P4, REG_R3, 0, + INS_OPTS_SCALABLE_S); // LD1RW {.S }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rsh, EA_SCALABLE, REG_V0, REG_P1, REG_R2, 126, + INS_OPTS_SCALABLE_D); // LD1RSH {.D }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rw, EA_SCALABLE, REG_V5, REG_P4, REG_R3, 252, + INS_OPTS_SCALABLE_D); // LD1RW {.D }, /Z, [{, #}] + + // IF_SVE_IC_3A_B + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rh, EA_SCALABLE, REG_V0, REG_P2, REG_R3, 0, + INS_OPTS_SCALABLE_H); // LD1RH {.H }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rsb, EA_SCALABLE, REG_V6, REG_P5, REG_R4, 0, + INS_OPTS_SCALABLE_H); // LD1RSB {.H }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rh, EA_SCALABLE, REG_V5, REG_P4, REG_R3, 126, + INS_OPTS_SCALABLE_S); // LD1RH {.S }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rsb, EA_SCALABLE, REG_V2, REG_P1, REG_R0, 63, + INS_OPTS_SCALABLE_S); // LD1RSB {.S }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rh, EA_SCALABLE, REG_V3, REG_P2, REG_R1, 126, + INS_OPTS_SCALABLE_D); // LD1RH {.D }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rsb, EA_SCALABLE, REG_V4, REG_P5, REG_R6, 63, + INS_OPTS_SCALABLE_D); // LD1RSB {.D }, /Z, [{, #}] + + // IF_SVE_IC_3A_C + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rb, EA_SCALABLE, REG_V1, REG_P2, REG_R3, 0, + INS_OPTS_SCALABLE_B); // LD1RB {.B }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rb, EA_SCALABLE, REG_V5, REG_P4, REG_R3, 63, + INS_OPTS_SCALABLE_H); // LD1RB {.H }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rb, EA_SCALABLE, REG_V6, REG_P7, REG_R8, 0, + INS_OPTS_SCALABLE_S); // LD1RB {.S }, /Z, [{, #}] + theEmitter->emitIns_R_R_R_I(INS_sve_ld1rb, EA_SCALABLE, REG_V1, REG_P0, REG_R9, 63, + INS_OPTS_SCALABLE_B); // LD1RB {.D }, /Z, [{, #}] } #endif // defined(TARGET_ARM64) && defined(DEBUG) diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 60282237609798..dea80c05e6b8be 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -2355,6 +2355,89 @@ void emitter::emitInsSanityCheck(instrDesc* id) // iiiiii break; + case IF_SVE_HX_3A_B: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) + elemsize = id->idOpSize(); + assert(insOptsScalableWords(id->idInsOpt())); + assert(isScalableVectorSize(elemsize)); + assert(isVectorRegister(id->idReg1())); + assert(isLowPredicateRegister(id->idReg2())); + assert(isVectorRegister(id->idReg3())); + assert(isValidUimm5(emitGetInsSC(id))); + break; + + case IF_SVE_HX_3A_E: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) + elemsize = id->idOpSize(); + assert(insOptsScalableWords(id->idInsOpt())); + assert(isScalableVectorSize(elemsize)); + assert(isVectorRegister(id->idReg1())); + assert(isLowPredicateRegister(id->idReg2())); + assert(isVectorRegister(id->idReg3())); + break; + + case IF_SVE_IV_3A: // ...........iiiii ...gggnnnnnttttt -- SVE 64-bit gather load (vector plus immediate) + elemsize = id->idOpSize(); + assert(id->idInsOpt() == INS_OPTS_SCALABLE_D); + assert(isScalableVectorSize(elemsize)); + assert(isVectorRegister(id->idReg1())); + assert(isLowPredicateRegister(id->idReg2())); + assert(isVectorRegister(id->idReg3())); + break; + + case IF_SVE_JI_3A_A: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit scatter store (vector plus immediate) + elemsize = id->idOpSize(); + assert(insOptsScalableWords(id->idInsOpt())); + assert(isScalableVectorSize(elemsize)); + assert(isVectorRegister(id->idReg1())); + assert(isLowPredicateRegister(id->idReg2())); + assert(isVectorRegister(id->idReg3())); + break; + + case IF_SVE_JL_3A: // ...........iiiii ...gggnnnnnttttt -- SVE 64-bit scatter store (vector plus immediate) + elemsize = id->idOpSize(); + assert(id->idInsOpt() == INS_OPTS_SCALABLE_D); + assert(isScalableVectorSize(elemsize)); + assert(isVectorRegister(id->idReg1())); + assert(isLowPredicateRegister(id->idReg2())); + assert(isVectorRegister(id->idReg3())); + assert(isValidUimm5_MultipleOf8(emitGetInsSC(id))); + break; + + case IF_SVE_IC_3A: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + elemsize = id->idOpSize(); + assert(id->idInsOpt() == INS_OPTS_SCALABLE_D); + assert(isScalableVectorSize(elemsize)); + assert(isVectorRegister(id->idReg1())); + assert(isLowPredicateRegister(id->idReg2())); + assert(isGeneralRegister(id->idReg3())); + break; + + case IF_SVE_IC_3A_A: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + elemsize = id->idOpSize(); + assert(insOptsScalableWords(id->idInsOpt())); + assert(isScalableVectorSize(elemsize)); + assert(isVectorRegister(id->idReg1())); + assert(isLowPredicateRegister(id->idReg2())); + assert(isGeneralRegister(id->idReg3())); + break; + + case IF_SVE_IC_3A_B: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + elemsize = id->idOpSize(); + assert(insOptsScalableAtLeastHalf(id->idInsOpt())); + assert(isScalableVectorSize(elemsize)); + assert(isVectorRegister(id->idReg1())); + assert(isLowPredicateRegister(id->idReg2())); + assert(isGeneralRegister(id->idReg3())); + break; + + case IF_SVE_IC_3A_C: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + elemsize = id->idOpSize(); + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isScalableVectorSize(elemsize)); + assert(isVectorRegister(id->idReg1())); + assert(isLowPredicateRegister(id->idReg2())); + assert(isGeneralRegister(id->idReg3())); + break; + default: printf("unexpected format %s\n", emitIfName(id->idInsFmt())); assert(!"Unexpected format"); @@ -11498,101 +11581,6 @@ void emitter::emitIns_R_R_R_I(instruction ins, } break; - case INS_sve_cmpeq: - case INS_sve_cmpgt: - case INS_sve_cmpge: - case INS_sve_cmpne: - case INS_sve_cmple: - case INS_sve_cmplt: - assert(insOptsScalableStandard(opt)); - assert(isPredicateRegister(reg1)); // DDDD - assert(isLowPredicateRegister(reg2)); // ggg - assert(isVectorRegister(reg3)); // nnnnn - assert(isValidSimm5(imm)); // iiiii - fmt = IF_SVE_CY_3A; - break; - - case INS_sve_cmphi: - case INS_sve_cmphs: - case INS_sve_cmplo: - case INS_sve_cmpls: - assert(insOptsScalableStandard(opt)); - assert(isPredicateRegister(reg1)); // DDDD - assert(isLowPredicateRegister(reg2)); // ggg - assert(isVectorRegister(reg3)); // nnnnn - assert(isValidUimm7(imm)); // iiiii - fmt = IF_SVE_CY_3B; - break; - - case INS_sve_sdot: - case INS_sve_udot: - assert(isVectorRegister(reg1)); // ddddd - assert(isVectorRegister(reg2)); // nnnnn - assert(isLowVectorRegister(reg3)); // mmmm - - if (opt == INS_OPTS_SCALABLE_B) - { - assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm - assert(isValidUimm2(imm)); // ii - fmt = IF_SVE_EY_3A; - } - else if (opt == INS_OPTS_SCALABLE_H) - { - assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm - assert(isValidUimm2(imm)); // ii - fmt = IF_SVE_EG_3A; - } - else - { - assert(insOptsNone(opt)); - assert(isValidImm1(imm)); // i - opt = INS_OPTS_SCALABLE_H; - fmt = IF_SVE_EY_3B; - } - break; - - case INS_sve_usdot: - case INS_sve_sudot: - assert(opt == INS_OPTS_SCALABLE_B); - assert(isVectorRegister(reg1)); // ddddd - assert(isVectorRegister(reg2)); // nnnnn - assert(isVectorRegister(reg3)); // mmm - assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); - assert(isValidUimm2(imm)); // ii - fmt = IF_SVE_EZ_3A; - break; - - case INS_sve_mul: - assert(insOptsScalableAtLeastHalf(opt)); - assert(isVectorRegister(reg1)); // ddddd - assert(isVectorRegister(reg2)); // nnnnn - assert(isLowVectorRegister(reg3)); // mmmm - - switch (opt) - { - case INS_OPTS_SCALABLE_H: - assert(isValidUimm3(imm)); // iii - assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm - fmt = IF_SVE_FD_3A; - break; - - case INS_OPTS_SCALABLE_S: - assert(isValidUimm2(imm)); // ii - assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm - fmt = IF_SVE_FD_3B; - break; - - case INS_OPTS_SCALABLE_D: - assert(isValidImm1(imm)); // i - fmt = IF_SVE_FD_3C; - break; - - default: - unreached(); - break; - } - break; - case INS_fmul: // by element, imm[0..3] selects the element of reg3 case INS_fmla: case INS_fmls: @@ -11832,109 +11820,499 @@ void emitter::emitIns_R_R_R_I(instruction ins, fmt = IF_DV_3AI; break; - case INS_sve_cdot: - assert(insOptsScalableWords(opt)); - assert(isVectorRegister(reg1)); // ddddd - assert(isVectorRegister(reg2)); // nnnnn - assert(isVectorRegister(reg3)); // mmmmm - assert(isValidRot(imm)); // rr - assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx - - // Convert rot to bitwise representation - imm = emitEncodeRotationImm0_to_270(imm); - fmt = IF_SVE_EJ_3A; - break; + default: + // fallback to emit SVE instructions. + return emitInsSve_R_R_R_I(ins, attr, reg1, reg2, reg3, imm, opt, attrReg2); - case INS_sve_cmla: - case INS_sve_sqrdcmlah: - assert(insOptsScalableStandard(opt)); - assert(isVectorRegister(reg1)); // ddddd - assert(isVectorRegister(reg2)); // nnnnn - assert(isVectorRegister(reg3)); // mmmmm - assert(isValidRot(imm)); // rr - assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + } // end switch (ins) - // Convert rot to bitwise representation - imm = emitEncodeRotationImm0_to_270(imm); - fmt = IF_SVE_EK_3A; - break; + if (isLdSt) + { + assert(!isAddSub); + assert(isGeneralRegisterOrSP(reg3)); + assert(insOptsNone(opt) || insOptsIndexed(opt)); - case INS_sve_ld1d: - assert(insOptsScalable(opt)); + if (isSIMD) + { + assert(isValidVectorLSPDatasize(size)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); - assert(isGeneralRegister(reg3)); - assert(isValidSimm4(imm)); - if (opt == INS_OPTS_SCALABLE_Q) + assert(isVectorRegister(reg2)); + assert((scale >= 2) && (scale <= 4)); + } + else + { + assert(isValidGeneralDatasize(size)); + assert(isGeneralRegisterOrZR(reg1)); + assert(isGeneralRegisterOrZR(reg2)); + assert((scale == 2) || (scale == 3)); + } + + // Load/Store Pair reserved encodings: + if (emitInsIsLoad(ins)) + { + assert(reg1 != reg2); + } + if (insOptsIndexed(opt)) + { + assert(reg1 != reg3); + assert(reg2 != reg3); + } + + reg3 = encodingSPtoZR(reg3); + + ssize_t mask = (1 << scale) - 1; // the mask of low bits that must be zero to encode the immediate + if (imm == 0) + { + assert(insOptsNone(opt)); // PRE/POST Index doesn't make sense with an immediate of zero + + fmt = IF_LS_3B; + } + else + { + if ((imm & mask) == 0) { - fmt = IF_SVE_IH_3A_A; + imm >>= scale; // The immediate is scaled by the size of the ld/st + + if ((imm >= -64) && (imm <= 63)) + { + fmt = IF_LS_3C; + } } - else +#ifdef DEBUG + if (fmt != IF_LS_3C) { - assert(opt == INS_OPTS_SCALABLE_D); - fmt = IF_SVE_IH_3A; + assert(!"Instruction cannot be encoded: IF_LS_3C"); + } +#endif + } + } + else if (isAddSub) + { + bool reg2IsSP = (reg2 == REG_SP); + assert(!isLdSt); + assert(isValidGeneralDatasize(size)); + assert(isGeneralRegister(reg3)); + + if (setFlags || insOptsAluShift(opt)) // Can't encode SP in reg1 with setFlags or AluShift option + { + assert(isGeneralRegisterOrZR(reg1)); + } + else + { + assert(isGeneralRegisterOrSP(reg1)); + reg1 = encodingSPtoZR(reg1); + } + + if (insOptsAluShift(opt)) // Can't encode SP in reg2 with AluShift option + { + assert(isGeneralRegister(reg2)); + } + else + { + assert(isGeneralRegisterOrSP(reg2)); + reg2 = encodingSPtoZR(reg2); + } + + if (insOptsAnyExtend(opt)) + { + assert((imm >= 0) && (imm <= 4)); + + fmt = IF_DR_3C; + } + else if (insOptsAluShift(opt)) + { + // imm should be non-zero and in [1..63] + assert(isValidImmShift(imm, size) && (imm != 0)); + fmt = IF_DR_3B; + } + else if (imm == 0) + { + assert(insOptsNone(opt)); + + if (reg2IsSP) + { + // To encode the SP register as reg2 we must use the IF_DR_3C encoding + // and also specify a LSL of zero (imm == 0) + opt = INS_OPTS_LSL; + fmt = IF_DR_3C; + } + else + { + fmt = IF_DR_3A; + } + } + else + { + assert(!"Instruction cannot be encoded: Add/Sub IF_DR_3A"); + } + } + + assert(fmt != IF_NONE); + + instrDesc* id = emitNewInstrCns(attr, imm); + + id->idIns(ins); + id->idInsFmt(fmt); + id->idInsOpt(opt); + + id->idReg1(reg1); + id->idReg2(reg2); + id->idReg3(reg3); + + // Record the attribute for the second register in the pair + id->idGCrefReg2(GCT_NONE); + if (attrReg2 != EA_UNKNOWN) + { + // Record the attribute for the second register in the pair + assert((fmt == IF_LS_3B) || (fmt == IF_LS_3C)); + if (EA_IS_GCREF(attrReg2)) + { + id->idGCrefReg2(GCT_GCREF); + } + else if (EA_IS_BYREF(attrReg2)) + { + id->idGCrefReg2(GCT_BYREF); + } + } + + dispIns(id); + appendToCurIG(id); +} + +/***************************************************************************** + * + * Add a SVE instruction referencing three registers and a constant. + */ + +void emitter::emitInsSve_R_R_R_I(instruction ins, + emitAttr attr, + regNumber reg1, + regNumber reg2, + regNumber reg3, + ssize_t imm, + insOpts opt /* = INS_OPTS_NONE */, + emitAttr attrReg2 /* = EA_UNKNOWN */) +{ + emitAttr size = EA_SIZE(attr); + emitAttr elemsize = EA_UNKNOWN; + insFormat fmt = IF_NONE; + + /* Figure out the encoding format of the instruction */ + switch (ins) + { + case INS_sve_cmpeq: + case INS_sve_cmpgt: + case INS_sve_cmpge: + case INS_sve_cmpne: + case INS_sve_cmple: + case INS_sve_cmplt: + assert(insOptsScalableStandard(opt)); + assert(isPredicateRegister(reg1)); // DDDD + assert(isLowPredicateRegister(reg2)); // ggg + assert(isVectorRegister(reg3)); // nnnnn + assert(isValidSimm5(imm)); // iiiii + fmt = IF_SVE_CY_3A; + break; + + case INS_sve_cmphi: + case INS_sve_cmphs: + case INS_sve_cmplo: + case INS_sve_cmpls: + assert(insOptsScalableStandard(opt)); + assert(isPredicateRegister(reg1)); // DDDD + assert(isLowPredicateRegister(reg2)); // ggg + assert(isVectorRegister(reg3)); // nnnnn + assert(isValidUimm7(imm)); // iiiii + fmt = IF_SVE_CY_3B; + break; + + case INS_sve_sdot: + case INS_sve_udot: + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isLowVectorRegister(reg3)); // mmmm + + if (opt == INS_OPTS_SCALABLE_B) + { + assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm + assert(isValidUimm2(imm)); // ii + fmt = IF_SVE_EY_3A; + } + else if (opt == INS_OPTS_SCALABLE_H) + { + assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm + assert(isValidUimm2(imm)); // ii + fmt = IF_SVE_EG_3A; + } + else + { + assert(insOptsNone(opt)); + assert(isValidImm1(imm)); // i + opt = INS_OPTS_SCALABLE_H; + fmt = IF_SVE_EY_3B; + } + break; + + case INS_sve_usdot: + case INS_sve_sudot: + assert(opt == INS_OPTS_SCALABLE_B); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isVectorRegister(reg3)); // mmm + assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); + assert(isValidUimm2(imm)); // ii + fmt = IF_SVE_EZ_3A; + break; + + case INS_sve_mul: + assert(insOptsScalableAtLeastHalf(opt)); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isLowVectorRegister(reg3)); // mmmm + + switch (opt) + { + case INS_OPTS_SCALABLE_H: + assert(isValidUimm3(imm)); // iii + assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm + fmt = IF_SVE_FD_3A; + break; + + case INS_OPTS_SCALABLE_S: + assert(isValidUimm2(imm)); // ii + assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm + fmt = IF_SVE_FD_3B; + break; + + case INS_OPTS_SCALABLE_D: + assert(isValidImm1(imm)); // i + fmt = IF_SVE_FD_3C; + break; + + default: + unreached(); + break; + } + break; + + case INS_sve_cdot: + assert(insOptsScalableWords(opt)); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isVectorRegister(reg3)); // mmmmm + assert(isValidRot(imm)); // rr + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + + // Convert rot to bitwise representation + imm = emitEncodeRotationImm0_to_270(imm); + fmt = IF_SVE_EJ_3A; + break; + + case INS_sve_cmla: + case INS_sve_sqrdcmlah: + assert(insOptsScalableStandard(opt)); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isVectorRegister(reg3)); // mmmmm + assert(isValidRot(imm)); // rr + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + + // Convert rot to bitwise representation + imm = emitEncodeRotationImm0_to_270(imm); + fmt = IF_SVE_EK_3A; + break; + + case INS_sve_ld1d: + assert(insOptsScalable(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + + if (isGeneralRegister(reg3)) + { + assert(isValidSimm4(imm)); + if (opt == INS_OPTS_SCALABLE_Q) + { + fmt = IF_SVE_IH_3A_A; + } + else + { + assert(opt == INS_OPTS_SCALABLE_D); + fmt = IF_SVE_IH_3A; + } + } + else + { + assert(opt == INS_OPTS_SCALABLE_D); + assert(isVectorRegister(reg3)); + assert(isValidUimm5_MultipleOf8(imm)); + fmt = IF_SVE_IV_3A; + } + break; + + case INS_sve_ldff1d: + assert(opt == INS_OPTS_SCALABLE_D); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isVectorRegister(reg3)); + assert(isValidUimm5_MultipleOf8(imm)); + fmt = IF_SVE_IV_3A; + break; + + case INS_sve_ld1w: + assert(insOptsScalableWordsOrQuadwords(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + + if (isGeneralRegister(reg3)) + { + assert(isValidSimm4(imm)); + fmt = IF_SVE_IH_3A_F; + } + else + { + assert(insOptsScalableWords(opt)); + assert(isVectorRegister(reg3)); + assert(isValidUimm5_MultipleOf4(imm)); + fmt = IF_SVE_HX_3A_E; } break; - case INS_sve_ld1w: - assert(insOptsScalableWordsOrQuadwords(opt)); - assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); - assert(isGeneralRegister(reg3)); - assert(isValidSimm4(imm)); - fmt = IF_SVE_IH_3A_F; - break; - case INS_sve_ld1sw: assert(opt == INS_OPTS_SCALABLE_D); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); - assert(isGeneralRegister(reg3)); - assert(isValidSimm4(imm)); - fmt = IF_SVE_IJ_3A; + assert(isLowPredicateRegister(reg2)); + + if (isGeneralRegister(reg3)) + { + assert(isValidSimm4(imm)); + fmt = IF_SVE_IJ_3A; + } + else + { + assert(isVectorRegister(reg3)); + assert(isValidUimm5_MultipleOf4(imm)); + fmt = IF_SVE_IV_3A; + } + break; + + case INS_sve_ldff1sw: + assert(opt == INS_OPTS_SCALABLE_D); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isVectorRegister(reg3)); + assert(isValidUimm5_MultipleOf4(imm)); + fmt = IF_SVE_IV_3A; break; case INS_sve_ld1sb: assert(insOptsScalableAtLeastHalf(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); - assert(isGeneralRegister(reg3)); - assert(isValidSimm4(imm)); - fmt = IF_SVE_IJ_3A_D; + assert(isLowPredicateRegister(reg2)); + + if (isGeneralRegister(reg3)) + { + assert(isGeneralRegister(reg3)); + assert(isValidSimm4(imm)); + fmt = IF_SVE_IJ_3A_D; + } + else + { + assert(insOptsScalableWords(opt)); + assert(isVectorRegister(reg3)); + assert(isValidUimm5(imm)); + fmt = IF_SVE_HX_3A_B; + } break; case INS_sve_ld1b: assert(insOptsScalableStandard(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); - assert(isGeneralRegister(reg3)); - assert(isValidSimm4(imm)); - fmt = IF_SVE_IJ_3A_E; + assert(isLowPredicateRegister(reg2)); + + if (isGeneralRegister(reg3)) + { + assert(isValidSimm4(imm)); + fmt = IF_SVE_IJ_3A_E; + } + else + { + assert(insOptsScalableWords(opt)); + assert(isVectorRegister(reg3)); + assert(isValidUimm5(imm)); + fmt = IF_SVE_HX_3A_B; + } + break; + + case INS_sve_ldff1b: + case INS_sve_ldff1sb: + assert(insOptsScalableWords(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isVectorRegister(reg3)); + assert(isValidUimm5(imm)); + fmt = IF_SVE_HX_3A_B; break; case INS_sve_ld1sh: assert(insOptsScalableWords(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); - assert(isGeneralRegister(reg3)); - assert(isValidSimm4(imm)); - fmt = IF_SVE_IJ_3A_F; + assert(isLowPredicateRegister(reg2)); + + if (isGeneralRegister(reg3)) + { + assert(isValidSimm4(imm)); + fmt = IF_SVE_IJ_3A_F; + } + else + { + assert(isVectorRegister(reg3)); + assert(isValidUimm5_MultipleOf2(imm)); + fmt = IF_SVE_HX_3A_E; + } + break; + + case INS_sve_ld1h: + assert(insOptsScalableAtLeastHalf(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + + if (isGeneralRegister(reg3)) + { + assert(isValidSimm4(imm)); + fmt = IF_SVE_IJ_3A_G; + } + else + { + assert(isVectorRegister(reg3)); + assert(isValidUimm5_MultipleOf2(imm)); + fmt = IF_SVE_HX_3A_E; + } + break; + + case INS_sve_ldff1h: + case INS_sve_ldff1sh: + assert(insOptsScalableWords(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isVectorRegister(reg3)); + assert(isValidUimm5_MultipleOf2(imm)); + fmt = IF_SVE_HX_3A_E; break; - case INS_sve_ld1h: - assert(insOptsScalableAtLeastHalf(opt)); + case INS_sve_ldff1w: + assert(insOptsScalableWords(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); - assert(isGeneralRegister(reg3)); - assert(isValidSimm4(imm)); - fmt = IF_SVE_IJ_3A_G; + assert(isLowPredicateRegister(reg2)); + assert(isVectorRegister(reg3)); + assert(isValidUimm5_MultipleOf4(imm)); + fmt = IF_SVE_HX_3A_E; break; case INS_sve_ldnf1sw: case INS_sve_ldnf1d: assert(opt == INS_OPTS_SCALABLE_D); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); + assert(isLowPredicateRegister(reg2)); assert(isGeneralRegister(reg3)); assert(isValidSimm4(imm)); fmt = IF_SVE_IL_3A; @@ -11944,7 +12322,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_ldnf1w: assert(insOptsScalableWords(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); + assert(isLowPredicateRegister(reg2)); assert(isGeneralRegister(reg3)); assert(isValidSimm4(imm)); fmt = IF_SVE_IL_3A_A; @@ -11954,7 +12332,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_ldnf1sb: assert(insOptsScalableAtLeastHalf(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); + assert(isLowPredicateRegister(reg2)); assert(isGeneralRegister(reg3)); assert(isValidSimm4(imm)); fmt = IF_SVE_IL_3A_B; @@ -11975,7 +12353,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_ldnt1d: assert(insOptsScalableStandard(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); + assert(isLowPredicateRegister(reg2)); assert(isGeneralRegister(reg3)); assert(isValidSimm4(imm)); @@ -12017,7 +12395,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_ld1rod: assert(insOptsScalableStandard(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); + assert(isLowPredicateRegister(reg2)); assert(isGeneralRegister(reg3)); #ifdef DEBUG @@ -12078,7 +12456,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_ld4q: assert(opt == INS_OPTS_SCALABLE_Q); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); + assert(isLowPredicateRegister(reg2)); assert(isGeneralRegister(reg3)); #ifdef DEBUG @@ -12119,7 +12497,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_ld4d: assert(insOptsScalableStandard(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); + assert(isLowPredicateRegister(reg2)); assert(isGeneralRegister(reg3)); #ifdef DEBUG @@ -12191,7 +12569,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_st4q: assert(opt == INS_OPTS_SCALABLE_Q); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); + assert(isLowPredicateRegister(reg2)); assert(isGeneralRegister(reg3)); #ifdef DEBUG @@ -12224,7 +12602,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_stnt1d: assert(insOptsScalableStandard(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); + assert(isLowPredicateRegister(reg2)); assert(isGeneralRegister(reg3)); assert(isValidSimm4(imm)); @@ -12259,33 +12637,51 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_st1w: case INS_sve_st1d: assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); - assert(isGeneralRegister(reg3)); - assert(isValidSimm4(imm)); + assert(isLowPredicateRegister(reg2)); - if (opt == INS_OPTS_SCALABLE_Q && (ins == INS_sve_st1d)) - { - fmt = IF_SVE_JN_3C_D; - } - else + if (isGeneralRegister(reg3)) { - if ((ins == INS_sve_st1w) && insOptsScalableWords(opt)) + assert(isValidSimm4(imm)); + + if (opt == INS_OPTS_SCALABLE_Q && (ins == INS_sve_st1d)) { - fmt = IF_SVE_JN_3B; + fmt = IF_SVE_JN_3C_D; } else { -#if DEBUG - if (ins == INS_sve_st1w) + if ((ins == INS_sve_st1w) && insOptsScalableWords(opt)) { - assert(opt == INS_OPTS_SCALABLE_Q); + fmt = IF_SVE_JN_3B; } else { - assert(opt == INS_OPTS_SCALABLE_D); - } +#if DEBUG + if (ins == INS_sve_st1w) + { + assert(opt == INS_OPTS_SCALABLE_Q); + } + else + { + assert(opt == INS_OPTS_SCALABLE_D); + } #endif // DEBUG - fmt = IF_SVE_JN_3C; + fmt = IF_SVE_JN_3C; + } + } + } + else + { + assert(isVectorRegister(reg3)); + if ((ins == INS_sve_st1w) && insOptsScalableWords(opt)) + { + assert(isValidUimm5_MultipleOf4(imm)); + fmt = IF_SVE_JI_3A_A; + } + else + { + assert(ins == INS_sve_st1d); + assert(isValidUimm5_MultipleOf8(imm)); + fmt = IF_SVE_JL_3A; } } break; @@ -12304,7 +12700,7 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_st4d: assert(insOptsScalableStandard(opt)); assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); + assert(isLowPredicateRegister(reg2)); assert(isGeneralRegister(reg3)); #ifdef DEBUG @@ -12374,12 +12770,39 @@ void emitter::emitIns_R_R_R_I(instruction ins, case INS_sve_st1b: case INS_sve_st1h: assert(isVectorRegister(reg1)); - assert(isPredicateRegister(reg2)); - assert(isGeneralRegister(reg3)); - assert(isValidSimm4(imm)); - // st1h is reserved for scalable B - assert((ins == INS_sve_st1h) ? insOptsScalableAtLeastHalf(opt) : insOptsScalableStandard(opt)); - fmt = IF_SVE_JN_3A; + assert(isLowPredicateRegister(reg2)); + + if (isGeneralRegister(reg3)) + { + assert(isValidSimm4(imm)); + // st1h is reserved for scalable B + assert((ins == INS_sve_st1h) ? insOptsScalableAtLeastHalf(opt) : insOptsScalableStandard(opt)); + fmt = IF_SVE_JN_3A; + } + else + { + assert(insOptsScalableWords(opt)); + assert(isVectorRegister(reg3)); + +#ifdef DEBUG + switch (ins) + { + case INS_sve_st1b: + assert(isValidUimm5(imm)); + break; + + case INS_sve_st1h: + assert(isValidUimm5_MultipleOf2(imm)); + break; + + default: + assert(!"Invalid instruction"); + break; + } +#endif // DEBUG + + fmt = IF_SVE_JI_3A_A; + } break; case INS_sve_fmla: @@ -12620,190 +13043,131 @@ void emitter::emitIns_R_R_R_I(instruction ins, { assert(opt == INS_OPTS_SCALABLE_S); assert(isValidUimm2(imm)); // ii - fmt = IF_SVE_FJ_3B; - } - break; - - case INS_sve_sqrdmlah: - case INS_sve_sqrdmlsh: - assert(isVectorRegister(reg1)); // ddddd - assert(isVectorRegister(reg2)); // nnnnn - assert(isLowVectorRegister(reg3)); // mmmm - - if (opt == INS_OPTS_SCALABLE_H) - { - assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm - assert(isValidUimm3(imm)); // i ii - fmt = IF_SVE_FK_3A; - } - else if (opt == INS_OPTS_SCALABLE_S) - { - assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm - assert(isValidUimm2(imm)); // ii - fmt = IF_SVE_FK_3B; - } - else - { - assert(opt == INS_OPTS_SCALABLE_D); - assert(isValidImm1(imm)); // i - fmt = IF_SVE_FK_3C; - } - break; - - case INS_sve_fcadd: - assert(insOptsScalableAtLeastHalf(opt)); - assert(isVectorRegister(reg1)); - assert(isLowPredicateRegister(reg2)); - assert(isVectorRegister(reg3)); - assert(isScalableVectorSize(size)); - imm = emitEncodeRotationImm90_or_270(imm); - fmt = IF_SVE_GP_3A; - break; - - case INS_sve_fmlalb: - case INS_sve_fmlalt: - case INS_sve_fmlslb: - case INS_sve_fmlslt: - case INS_sve_bfmlalb: - case INS_sve_bfmlalt: - case INS_sve_bfmlslb: - case INS_sve_bfmlslt: - assert(opt == INS_OPTS_SCALABLE_H); - assert(isVectorRegister(reg1)); // ddddd - assert(isVectorRegister(reg2)); // nnnnn - assert(isVectorRegister(reg3)); // mmm - assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); - assert(isValidUimm3(imm)); // ii i - fmt = IF_SVE_GZ_3A; - break; - - default: - unreached(); - break; - - } // end switch (ins) - - if (isLdSt) - { - assert(!isAddSub); - assert(isGeneralRegisterOrSP(reg3)); - assert(insOptsNone(opt) || insOptsIndexed(opt)); - - if (isSIMD) - { - assert(isValidVectorLSPDatasize(size)); - assert(isVectorRegister(reg1)); - assert(isVectorRegister(reg2)); - assert((scale >= 2) && (scale <= 4)); - } - else - { - assert(isValidGeneralDatasize(size)); - assert(isGeneralRegisterOrZR(reg1)); - assert(isGeneralRegisterOrZR(reg2)); - assert((scale == 2) || (scale == 3)); - } - - // Load/Store Pair reserved encodings: - if (emitInsIsLoad(ins)) - { - assert(reg1 != reg2); - } - if (insOptsIndexed(opt)) - { - assert(reg1 != reg3); - assert(reg2 != reg3); - } - - reg3 = encodingSPtoZR(reg3); - - ssize_t mask = (1 << scale) - 1; // the mask of low bits that must be zero to encode the immediate - if (imm == 0) - { - assert(insOptsNone(opt)); // PRE/POST Index doesn't make sense with an immediate of zero - - fmt = IF_LS_3B; - } - else - { - if ((imm & mask) == 0) - { - imm >>= scale; // The immediate is scaled by the size of the ld/st - - if ((imm >= -64) && (imm <= 63)) - { - fmt = IF_LS_3C; - } - } -#ifdef DEBUG - if (fmt != IF_LS_3C) - { - assert(!"Instruction cannot be encoded: IF_LS_3C"); - } -#endif - } - } - else if (isAddSub) - { - bool reg2IsSP = (reg2 == REG_SP); - assert(!isLdSt); - assert(isValidGeneralDatasize(size)); - assert(isGeneralRegister(reg3)); - - if (setFlags || insOptsAluShift(opt)) // Can't encode SP in reg1 with setFlags or AluShift option - { - assert(isGeneralRegisterOrZR(reg1)); - } - else - { - assert(isGeneralRegisterOrSP(reg1)); - reg1 = encodingSPtoZR(reg1); - } - - if (insOptsAluShift(opt)) // Can't encode SP in reg2 with AluShift option - { - assert(isGeneralRegister(reg2)); - } - else - { - assert(isGeneralRegisterOrSP(reg2)); - reg2 = encodingSPtoZR(reg2); - } - - if (insOptsAnyExtend(opt)) - { - assert((imm >= 0) && (imm <= 4)); - - fmt = IF_DR_3C; - } - else if (insOptsAluShift(opt)) - { - // imm should be non-zero and in [1..63] - assert(isValidImmShift(imm, size) && (imm != 0)); - fmt = IF_DR_3B; - } - else if (imm == 0) - { - assert(insOptsNone(opt)); + fmt = IF_SVE_FJ_3B; + } + break; - if (reg2IsSP) + case INS_sve_sqrdmlah: + case INS_sve_sqrdmlsh: + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isLowVectorRegister(reg3)); // mmmm + + if (opt == INS_OPTS_SCALABLE_H) { - // To encode the SP register as reg2 we must use the IF_DR_3C encoding - // and also specify a LSL of zero (imm == 0) - opt = INS_OPTS_LSL; - fmt = IF_DR_3C; + assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm + assert(isValidUimm3(imm)); // i ii + fmt = IF_SVE_FK_3A; + } + else if (opt == INS_OPTS_SCALABLE_S) + { + assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); // mmm + assert(isValidUimm2(imm)); // ii + fmt = IF_SVE_FK_3B; } else { - fmt = IF_DR_3A; + assert(opt == INS_OPTS_SCALABLE_D); + assert(isValidImm1(imm)); // i + fmt = IF_SVE_FK_3C; } - } - else - { - assert(!"Instruction cannot be encoded: Add/Sub IF_DR_3A"); - } - } + break; + + case INS_sve_fcadd: + assert(insOptsScalableAtLeastHalf(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isVectorRegister(reg3)); + assert(isScalableVectorSize(size)); + imm = emitEncodeRotationImm90_or_270(imm); + fmt = IF_SVE_GP_3A; + break; + + case INS_sve_ld1rd: + assert(opt == INS_OPTS_SCALABLE_D); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isValidUimm6_MultipleOf8(imm)); + fmt = IF_SVE_IC_3A; + break; + + case INS_sve_ld1rsw: + assert(opt == INS_OPTS_SCALABLE_D); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isValidUimm6_MultipleOf4(imm)); + fmt = IF_SVE_IC_3A; + break; + + case INS_sve_ld1rsh: + assert(insOptsScalableWords(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isValidUimm6_MultipleOf2(imm)); + fmt = IF_SVE_IC_3A_A; + break; + + case INS_sve_ld1rw: + assert(insOptsScalableWords(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isValidUimm6_MultipleOf4(imm)); + fmt = IF_SVE_IC_3A_A; + break; + + case INS_sve_ld1rh: + assert(insOptsScalableAtLeastHalf(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isValidUimm6_MultipleOf2(imm)); + fmt = IF_SVE_IC_3A_B; + break; + + case INS_sve_ld1rsb: + assert(insOptsScalableAtLeastHalf(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isValidUimm6(imm)); + fmt = IF_SVE_IC_3A_B; + break; + + case INS_sve_ld1rb: + assert(insOptsScalableStandard(opt)); + assert(isVectorRegister(reg1)); + assert(isLowPredicateRegister(reg2)); + assert(isGeneralRegister(reg3)); + assert(isValidUimm6(imm)); + fmt = IF_SVE_IC_3A_C; + break; + + case INS_sve_fmlalb: + case INS_sve_fmlalt: + case INS_sve_fmlslb: + case INS_sve_fmlslt: + case INS_sve_bfmlalb: + case INS_sve_bfmlalt: + case INS_sve_bfmlslb: + case INS_sve_bfmlslt: + assert(opt == INS_OPTS_SCALABLE_H); + assert(isVectorRegister(reg1)); // ddddd + assert(isVectorRegister(reg2)); // nnnnn + assert(isVectorRegister(reg3)); // mmm + assert((REG_V0 <= reg3) && (reg3 <= REG_V7)); + assert(isValidUimm3(imm)); // ii i + fmt = IF_SVE_GZ_3A; + break; + + default: + unreached(); + break; + } // end switch (ins) assert(fmt != IF_NONE); instrDesc* id = emitNewInstrCns(attr, imm); @@ -12816,22 +13180,6 @@ void emitter::emitIns_R_R_R_I(instruction ins, id->idReg2(reg2); id->idReg3(reg3); - // Record the attribute for the second register in the pair - id->idGCrefReg2(GCT_NONE); - if (attrReg2 != EA_UNKNOWN) - { - // Record the attribute for the second register in the pair - assert((fmt == IF_LS_3B) || (fmt == IF_LS_3C)); - if (EA_IS_GCREF(attrReg2)) - { - id->idGCrefReg2(GCT_GCREF); - } - else if (EA_IS_BYREF(attrReg2)) - { - id->idGCrefReg2(GCT_BYREF); - } - } - dispIns(id); appendToCurIG(id); } @@ -17445,6 +17793,60 @@ void emitter::emitIns_Call(EmitCallType callType, return 0; } +/***************************************************************************** + * + * Returns the encoding to select the 4/8 byte elemsize for an Arm64 Sve vector instruction at bit location '30'. + * This only works on select formats. + */ + +/*static*/ emitter::code_t emitter::insEncodeSveElemsize_30_or_21(insFormat fmt, emitAttr size) +{ + switch (fmt) + { + case IF_SVE_HX_3A_B: + case IF_SVE_HX_3A_E: + switch (size) + { + case EA_4BYTE: + return 0; + + case EA_8BYTE: + return (1 << 30); + + default: + break; + } + + assert(!"Invalid size for vector register"); + return 0; + + case IF_SVE_IV_3A: + assert(size == EA_8BYTE); + return 0; + + case IF_SVE_JI_3A_A: + switch (size) + { + case EA_4BYTE: + return (1 << 21); + + case EA_8BYTE: + return 0; + + default: + break; + } + + assert(!"Invalid size for vector register"); + return 0; + + default: + break; + } + + assert(!"Unexpected instruction format"); + return 0; +} /***************************************************************************** * * Returns the encoding for the field 'i1:tszh:tszl' at bit locations '23-22:20-18'. @@ -17627,6 +18029,7 @@ void emitter::emitIns_Call(EmitCallType callType, case INS_sve_ld1q: case INS_sve_ldnt1sw: case INS_sve_st1q: + case INS_sve_ld1rb: return 1; case INS_sve_ld2b: @@ -19032,34 +19435,163 @@ void emitter::emitIns_Call(EmitCallType callType, } break; - case EA_8BYTE: - switch (fmt) + case EA_8BYTE: + switch (fmt) + { + case IF_SVE_IH_3A_F: + // Note: Bit '15' is not actually part of 'dtype', but it is necessary to set to '1' to get the + // proper encoding for D. + return ((code | (1 << 15)) | (1 << 22)) | (1 << 21); // Set bit '22', '21' and '15' to 1. + + case IF_SVE_II_4A_H: + // Note: Bit '14' is not actually part of 'dtype', but it is necessary to set to '1' to get the + // proper encoding for D. + return ((code | (1 << 14)) | (1 << 22)) | (1 << 21); // Set bit '22', '21' and '14' to 1. + + default: + break; + } + break; + + case EA_16BYTE: + switch (fmt) + { + case IF_SVE_IH_3A_F: + return code | (1 << 20); // Set bit '20' to 1. + + case IF_SVE_II_4A_H: + // Note: Bit '15' is not actually part of 'dtype', but it is necessary to set to '1' to get the + // proper encoding for Q. + return code | (1 << 15); // Set bit '15' to 1. + + default: + break; + } + break; + + default: + assert(!"Invalid size for encoding dtype."); + break; + } + + assert(!"Invalid instruction format"); + return code; +} + +/***************************************************************************** + * + * Returns the encoding to select the 1/2/4/8 byte elemsize for an Arm64 Sve vector instruction + * for the 'dtypeh' and 'dtypel' fields. + */ + +/*static*/ emitter::code_t emitter::insEncodeSveElemsize_dtypeh_dtypel(instruction ins, + insFormat fmt, + emitAttr size, + code_t code) +{ + switch (fmt) + { + case IF_SVE_IC_3A_A: + switch (size) + { + case EA_4BYTE: + switch (ins) + { + case INS_sve_ld1rsh: + return code | (1 << 13); // set bit '13' + + case INS_sve_ld1rw: + return code | (1 << 14); // set bit '14' + + default: + break; + } + break; + + case EA_8BYTE: + switch (ins) + { + case INS_sve_ld1rsh: + return code; + + case INS_sve_ld1rw: + return code | (1 << 14) | (1 << 13); // set bits '14' and '13' + + default: + break; + } + break; + + default: + break; + } + break; + + case IF_SVE_IC_3A_B: + switch (size) { - case IF_SVE_IH_3A_F: - // Note: Bit '15' is not actually part of 'dtype', but it is necessary to set to '1' to get the - // proper encoding for D. - return ((code | (1 << 15)) | (1 << 22)) | (1 << 21); // Set bit '22', '21' and '15' to 1. + case EA_2BYTE: + switch (ins) + { + case INS_sve_ld1rh: + return code | (1 << 13); // set bit '13' - case IF_SVE_II_4A_H: - // Note: Bit '14' is not actually part of 'dtype', but it is necessary to set to '1' to get the - // proper encoding for D. - return ((code | (1 << 14)) | (1 << 22)) | (1 << 21); // Set bit '22', '21' and '14' to 1. + case INS_sve_ld1rsb: + return code | (1 << 24) | (1 << 14); // set bit '24' and '14' + + default: + break; + } + break; + + case EA_4BYTE: + switch (ins) + { + case INS_sve_ld1rh: + return code | (1 << 14); // set bit '14' + + case INS_sve_ld1rsb: + return code | (1 << 24) | (1 << 13); // set bit '24' and '13' + + default: + break; + } + break; + + case EA_8BYTE: + switch (ins) + { + case INS_sve_ld1rh: + return code | (1 << 14) | (1 << 13); // set bits '14' and '13' + + case INS_sve_ld1rsb: + return code | (1 << 24); // set bit '24' + + default: + break; + } + break; default: break; } break; - case EA_16BYTE: - switch (fmt) + case IF_SVE_IC_3A_C: + assert(ins == INS_sve_ld1rb); + switch (size) { - case IF_SVE_IH_3A_F: - return code | (1 << 20); // Set bit '20' to 1. + case EA_1BYTE: + return code; - case IF_SVE_II_4A_H: - // Note: Bit '15' is not actually part of 'dtype', but it is necessary to set to '1' to get the - // proper encoding for Q. - return code | (1 << 15); // Set bit '15' to 1. + case EA_2BYTE: + return code | (1 << 13); // set bit '13' + + case EA_4BYTE: + return code | (1 << 14); // set bit '14' + + case EA_8BYTE: + return code | (1 << 14) | (1 << 13); // set bits '14' and '13' default: break; @@ -19067,11 +19599,10 @@ void emitter::emitIns_Call(EmitCallType callType, break; default: - assert(!"Invalid size for encoding dtype."); break; } - assert(!"Invalid instruction format"); + assert(!"Unexpected instruction format"); return code; } @@ -19167,27 +19698,82 @@ void emitter::emitIns_Call(EmitCallType callType, /***************************************************************************** * - * Returns the encoding for the immediate value as 5-bits at bit locations '20-16'. + * // Returns the encoding for the immediate value that is a multiple of 2 as 5-bits at bit locations '20-16'. */ -/*static*/ emitter::code_t emitter::insEncodeSimm5_20_to_16(ssize_t imm) +/*static*/ emitter::code_t emitter::insEncodeUimm5_MultipleOf2_20_to_16(ssize_t imm) { - assert(isValidSimm5(imm)); - if (imm < 0) - { - imm = (imm & 0x1F); - } - return (code_t)imm << 16; + assert(isValidUimm5_MultipleOf2(imm)); + return insEncodeUimm5_20_to_16(imm / 2); } /***************************************************************************** * - * Returns the encoding for the unsigned immediate value as 5-bits at bit locations '20-16'. + * // Returns the encoding for the immediate value that is a multiple of 4 as 5-bits at bit locations '20-16'. */ -/*static*/ emitter::code_t emitter::insEncodeUimm5_20_to_16(ssize_t imm) +/*static*/ emitter::code_t emitter::insEncodeUimm5_MultipleOf4_20_to_16(ssize_t imm) { - assert(isValidUimm5(imm)); + assert(isValidUimm5_MultipleOf4(imm)); + return insEncodeUimm5_20_to_16(imm / 4); +} + +/***************************************************************************** + * + * // Returns the encoding for the immediate value that is a multiple of 8 as 5-bits at bit locations '20-16'. + */ + +/*static*/ emitter::code_t emitter::insEncodeUimm5_MultipleOf8_20_to_16(ssize_t imm) +{ + assert(isValidUimm5_MultipleOf8(imm)); + return insEncodeUimm5_20_to_16(imm / 8); +} + +/***************************************************************************** + * + * // Returns the encoding for the immediate value that is a multiple of 2 as 6-bits at bit locations '21-16'. + */ + +/*static*/ emitter::code_t emitter::insEncodeUimm6_MultipleOf2_21_to_16(ssize_t imm) +{ + assert(isValidUimm6_MultipleOf2(imm)); + return insEncodeUimm6_21_to_16(imm / 2); +} + +/***************************************************************************** + * + * // Returns the encoding for the immediate value that is a multiple of 4 as 6-bits at bit locations '21-16'. + */ + +/*static*/ emitter::code_t emitter::insEncodeUimm6_MultipleOf4_21_to_16(ssize_t imm) +{ + assert(isValidUimm6_MultipleOf4(imm)); + return insEncodeUimm6_21_to_16(imm / 4); +} + +/***************************************************************************** + * + * // Returns the encoding for the immediate value that is a multiple of 8 as 6-bits at bit locations '21-16'. + */ + +/*static*/ emitter::code_t emitter::insEncodeUimm6_MultipleOf8_21_to_16(ssize_t imm) +{ + assert(isValidUimm6_MultipleOf8(imm)); + return insEncodeUimm6_21_to_16(imm / 8); +} + +/***************************************************************************** + * + * Returns the encoding for the immediate value as 5-bits at bit locations '20-16'. + */ + +/*static*/ emitter::code_t emitter::insEncodeSimm5_20_to_16(ssize_t imm) +{ + assert(isValidSimm5(imm)); + if (imm < 0) + { + imm = (imm & 0x1F); + } return (code_t)imm << 16; } @@ -19279,6 +19865,28 @@ void emitter::emitIns_Call(EmitCallType callType, return (code_t)(imm - 1) << 16; } +/***************************************************************************** + * + * Returns the encoding for the immediate value as 5-bits at bit locations '20-16'. + */ + +/*static*/ emitter::code_t emitter::insEncodeUimm5_20_to_16(ssize_t imm) +{ + assert(isValidUimm5(imm)); + return (code_t)imm << 16; +} + +/***************************************************************************** + * + * Returns the encoding for the immediate value as 6-bits at bit locations '21-16'. + */ + +/*static*/ emitter::code_t emitter::insEncodeUimm6_21_to_16(ssize_t imm) +{ + assert(isValidUimm6(imm)); + return (code_t)imm << 16; +} + /***************************************************************************** * * Returns the encoding for the immediate value as 8-bits at bit locations '12-5'. @@ -22708,6 +23316,136 @@ BYTE* emitter::emitOutput_InstrSve(BYTE* dst, instrDesc* id) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_HX_3A_B: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_V_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeUimm5_20_to_16(imm); // iiiii + code |= insEncodeSveElemsize_30_or_21(fmt, optGetSveElemsize(id->idInsOpt())); + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_HX_3A_E: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) + case IF_SVE_IV_3A: // ...........iiiii ...gggnnnnnttttt -- SVE 64-bit gather load (vector plus immediate) + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_V_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeSveElemsize_30_or_21(fmt, optGetSveElemsize(id->idInsOpt())); + + switch (ins) + { + case INS_sve_ld1d: + case INS_sve_ldff1d: + code |= insEncodeUimm5_MultipleOf8_20_to_16(imm); // iiiii + break; + + case INS_sve_ld1w: + case INS_sve_ld1sw: + case INS_sve_ldff1w: + case INS_sve_ldff1sw: + code |= insEncodeUimm5_MultipleOf4_20_to_16(imm); // iiiii + break; + + default: + code |= insEncodeUimm5_MultipleOf2_20_to_16(imm); // iiiii + break; + } + + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_JL_3A: // ...........iiiii ...gggnnnnnttttt -- SVE 64-bit scatter store (vector plus immediate) + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_V_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeUimm5_MultipleOf8_20_to_16(imm); // iiiii + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_JI_3A_A: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit scatter store (vector plus immediate) + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_V_9_to_5(id->idReg3()); // nnnnn + code |= insEncodeSveElemsize_30_or_21(fmt, optGetSveElemsize(id->idInsOpt())); + + switch (ins) + { + case INS_sve_st1h: + code |= insEncodeUimm5_MultipleOf2_20_to_16(imm); // iiiii + break; + + case INS_sve_st1w: + code |= insEncodeUimm5_MultipleOf4_20_to_16(imm); // iiiii + break; + + default: + assert(ins == INS_sve_st1b); + code |= insEncodeUimm5_20_to_16(imm); // iiiii + break; + } + + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_IC_3A: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg3()); // nnnnn + + switch (ins) + { + case INS_sve_ld1rd: + code |= insEncodeUimm6_MultipleOf8_21_to_16(imm); // iiiiii + break; + + default: + assert(ins == INS_sve_ld1rsw); + code |= insEncodeUimm6_MultipleOf4_21_to_16(imm); // iiiiii + break; + } + + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_IC_3A_A: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + case IF_SVE_IC_3A_B: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + case IF_SVE_IC_3A_C: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ttttt + code |= insEncodeReg_P_12_to_10(id->idReg2()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg3()); // nnnnn + code = insEncodeSveElemsize_dtypeh_dtypel(ins, fmt, optGetSveElemsize(id->idInsOpt()), code); + + switch (ins) + { + case INS_sve_ld1rw: + code |= insEncodeUimm6_MultipleOf4_21_to_16(imm); // iiiiii + break; + + case INS_sve_ld1rh: + case INS_sve_ld1rsh: + code |= insEncodeUimm6_MultipleOf2_21_to_16(imm); // iiiiii + break; + + default: + code |= insEncodeUimm6_21_to_16(imm); // iiiiii + break; + } + + dst += emitOutput_Instr(dst, code); + break; + default: assert(!"Unexpected format"); break; @@ -23122,6 +23860,31 @@ void emitter::emitDispSveImmMulVl(regNumber reg1, ssize_t imm) printf("]"); } +/***************************************************************************** + * + * Prints the encoding for format [.D{, #}] + */ +void emitter::emitDispSveImmIndex(regNumber reg1, insOpts opt, ssize_t imm) +{ + printf("["); + if (isVectorRegister(reg1)) + { + emitDispSveReg(reg1, opt, imm != 0); + } + else + { + emitDispReg(reg1, EA_8BYTE, imm != 0); + } + if (imm != 0) + { + // This does not have to be printed as hex. + // We only do it because the capstone disassembly displays this immediate as hex. + // We could not modify capstone without affecting other cases. + emitDispImm(imm, false, /* alwaysHex */ (imm > 31)); + } + printf("]"); +} + /***************************************************************************** * * Prints the encoding for the Extend Type encoding in loads/stores @@ -26218,6 +26981,33 @@ void emitter::emitDispInsHelp( emitDispSveReg(id->idReg4(), id->idInsOpt(), false); break; + // {.S }, /Z, [.S{, #}] + // {.D }, /Z, [.D{, #}] + case IF_SVE_HX_3A_B: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) + // {.S }, /Z, [.S{, #}] + // {.D }, /Z, [.D{, #}] + case IF_SVE_HX_3A_E: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) + // {.D }, /Z, [.D{, #}] + case IF_SVE_IV_3A: // ...........iiiii ...gggnnnnnttttt -- SVE 64-bit gather load (vector plus immediate) + // {.S }, , [.S{, #}] + // {.D }, , [.D{, #}] + case IF_SVE_JI_3A_A: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit scatter store (vector plus immediate) + // {.D }, , [.D{, #}] + case IF_SVE_JL_3A: // ...........iiiii ...gggnnnnnttttt -- SVE 64-bit scatter store (vector plus immediate) + // {.D }, /Z, [{, #}] + case IF_SVE_IC_3A: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + // {.D }, /Z, [{, #}] + case IF_SVE_IC_3A_A: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + // {.D }, /Z, [{, #}] + case IF_SVE_IC_3A_B: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + // {.D }, /Z, [{, #}] + case IF_SVE_IC_3A_C: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + imm = emitGetInsSC(id); + emitDispSveConsecutiveRegList(id->idReg1(), insGetSveReg1ListSize(id->idIns()), id->idInsOpt(), true); + emitDispPredicateReg(id->idReg2(), insGetPredicateType(fmt), id->idInsOpt(), true); + emitDispSveImmIndex(id->idReg3(), id->idInsOpt(), imm); + break; + default: printf("unexpected format %s", emitIfName(id->idInsFmt())); assert(!"unexpectedFormat"); @@ -29900,6 +30690,27 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insLatency = PERFSCORE_LATENCY_2C; break; + case IF_SVE_HX_3A_B: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) + case IF_SVE_HX_3A_E: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) + case IF_SVE_IV_3A: // ...........iiiii ...gggnnnnnttttt -- SVE 64-bit gather load (vector plus immediate) + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_9C; + break; + + case IF_SVE_JI_3A_A: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit scatter store (vector plus immediate) + case IF_SVE_JL_3A: // ...........iiiii ...gggnnnnnttttt -- SVE 64-bit scatter store (vector plus immediate) + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + + case IF_SVE_IC_3A: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + case IF_SVE_IC_3A_A: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + case IF_SVE_IC_3A_B: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + case IF_SVE_IC_3A_C: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element + result.insThroughput = PERFSCORE_THROUGHPUT_3C; + result.insLatency = PERFSCORE_LATENCY_6C; + break; + default: // all other instructions perfScoreUnhandledInstruction(id, &result); diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index 99b36bd4ec5341..cd4b3bc9738198 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -54,6 +54,7 @@ void emitDispSveExtendOpts(insOpts opt); void emitDispSveExtendOptsModN(insOpts opt, int n); void emitDispSveModAddr(instruction ins, regNumber reg1, regNumber reg2, insOpts opt, insFormat fmt); void emitDispSveImmMulVl(regNumber reg1, ssize_t imm); +void emitDispSveImmIndex(regNumber reg1, insOpts opt, ssize_t imm); void emitDispLSExtendOpts(insOpts opt); void emitDispReg(regNumber reg, emitAttr attr, bool addComma); void emitDispSveReg(regNumber reg, insOpts opt, bool addComma); @@ -533,6 +534,11 @@ static code_t insEncodeSveElemsize_sz_21(emitAttr size); // This specifically encodes the field 'tszh:tszl' at bit locations '22:20-19'. static code_t insEncodeSveElemsize_tszh_22_tszl_20_to_19(emitAttr size); +// Returns the encoding to select the 4/8 byte elemsize for an Arm64 Sve vector instruction at bit location '30' or +// '21'. +// This only works on select formats. +static code_t insEncodeSveElemsize_30_or_21(insFormat fmt, emitAttr size); + // Returns the encoding for the field 'i1:tszh:tszl' at bit locations '23-22:20-18'. static code_t insEncodeSveElemsize_tszh_tszl_and_imm(const insOpts opt, const ssize_t imm); @@ -579,6 +585,10 @@ static code_t insEncodeSveElemsize_dtype(instruction ins, emitAttr size, code_t // for the 'dtype' field. static code_t insEncodeSveElemsize_dtype_ld1w(instruction ins, insFormat fmt, emitAttr size, code_t code); +// Returns the encoding to select the 1/2/4/8 byte elemsize for an Arm64 Sve vector instruction +// for the 'dtypeh' and 'dtypel' fields. +static code_t insEncodeSveElemsize_dtypeh_dtypel(instruction ins, insFormat fmt, emitAttr size, code_t code); + // Returns the encoding for the immediate value as 4-bits at bit locations '19-16'. static code_t insEncodeSimm4_19_to_16(ssize_t imm); @@ -600,12 +610,27 @@ static code_t insEncodeSimm4_MultipleOf16_19_to_16(ssize_t imm); // Returns the encoding for the immediate value that is a multiple of 32 as 4-bits at bit locations '19-16'. static code_t insEncodeSimm4_MultipleOf32_19_to_16(ssize_t imm); +// Returns the encoding for the immediate value that is a multiple of 2 as 5-bits at bit locations '20-16'. +static code_t insEncodeUimm5_MultipleOf2_20_to_16(ssize_t imm); + +// Returns the encoding for the immediate value that is a multiple of 4 as 5-bits at bit locations '20-16'. +static code_t insEncodeUimm5_MultipleOf4_20_to_16(ssize_t imm); + +// Returns the encoding for the immediate value that is a multiple of 8 as 5-bits at bit locations '20-16'. +static code_t insEncodeUimm5_MultipleOf8_20_to_16(ssize_t imm); + +// Returns the encoding for the immediate value that is a multiple of 2 as 6-bits at bit locations '21-16'. +static code_t insEncodeUimm6_MultipleOf2_21_to_16(ssize_t imm); + +// Returns the encoding for the immediate value that is a multiple of 4 as 6-bits at bit locations '21-16'. +static code_t insEncodeUimm6_MultipleOf4_21_to_16(ssize_t imm); + +// Returns the encoding for the immediate value that is a multiple of 8 as 6-bits at bit locations '21-16'. +static code_t insEncodeUimm6_MultipleOf8_21_to_16(ssize_t imm); + // Returns the encoding for the immediate value as 5-bits at bit locations '20-16'. static code_t insEncodeSimm5_20_to_16(ssize_t imm); -// Returns the encoding for the unsigned immediate value as 5-bits at bit locations '20-16'. -static code_t insEncodeUimm5_20_to_16(ssize_t imm); - // Returns the encoding for the immediate value as 2-bits at bit locations '9-8'. static code_t insEncodeUimm2_9_to_8(ssize_t imm); @@ -630,6 +655,12 @@ 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 5-bits at bit locations '20-16'. +static code_t insEncodeUimm5_20_to_16(ssize_t imm); + +// Returns the encoding for the immediate value as 6-bits at bit locations '21-16'. +static code_t insEncodeUimm6_21_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); @@ -704,6 +735,42 @@ static bool isValidSimm4_MultipleOf32(ssize_t value) return (-256 <= value) && (value <= 224) && (value % 32 == 0); }; +// Returns true if 'value' is a legal signed multiple of 2 immediate 5 bit encoding (such as for LD1H). +static bool isValidUimm5_MultipleOf2(ssize_t value) +{ + return (0 <= value) && (value <= 62) && (value % 2 == 0); +}; + +// Returns true if 'value' is a legal signed multiple of 4 immediate 5 bit encoding (such as for LD1W). +static bool isValidUimm5_MultipleOf4(ssize_t value) +{ + return (0 <= value) && (value <= 124) && (value % 4 == 0); +}; + +// Returns true if 'value' is a legal signed multiple of 8 immediate 5 bit encoding (such as for LD1D). +static bool isValidUimm5_MultipleOf8(ssize_t value) +{ + return (0 <= value) && (value <= 248) && (value % 8 == 0); +}; + +// Returns true if 'value' is a legal signed multiple of 2 immediate 6 bit encoding (such as for LD1RH). +static bool isValidUimm6_MultipleOf2(ssize_t value) +{ + return (0 <= value) && (value <= 126) && (value % 2 == 0); +}; + +// Returns true if 'value' is a legal signed multiple of 4 immediate 6 bit encoding (such as for LD1RSW). +static bool isValidUimm6_MultipleOf4(ssize_t value) +{ + return (0 <= value) && (value <= 252) && (value % 4 == 0); +}; + +// Returns true if 'value' is a legal signed multiple of 8 immediate 6 bit encoding (such as for LD1RD). +static bool isValidUimm6_MultipleOf8(ssize_t value) +{ + return (0 <= value) && (value <= 504) && (value % 8 == 0); +}; + // Returns true if 'value' is a legal immediate 1 bit encoding (such as for PEXT). static bool isValidImm1(ssize_t value) { @@ -746,6 +813,12 @@ static bool isValidUimm5(ssize_t value) return (0 <= value) && (value <= 0x1FLL); }; +// Returns true if 'value' is a legal unsigned immediate 6 bit encoding (such as for LD1RD). +static bool isValidUimm6(ssize_t value) +{ + return (0 <= value) && (value <= 63); +} + // Returns true if 'value' is a legal unsigned immediate 5 bit encoding, starting from 1 (such as for SHRNB). static bool isValidUimm5From1(ssize_t value) { @@ -1363,6 +1436,15 @@ void emitIns_R_R_R_I(instruction ins, insOpts opt = INS_OPTS_NONE, emitAttr attrReg2 = EA_UNKNOWN); +void emitInsSve_R_R_R_I(instruction ins, + emitAttr attr, + regNumber reg1, + regNumber reg2, + regNumber reg3, + ssize_t imm, + insOpts opt = INS_OPTS_NONE, + emitAttr attrReg2 = EA_UNKNOWN); + void emitIns_R_R_R_I_I(instruction ins, emitAttr attr, regNumber reg1, diff --git a/src/coreclr/jit/instrsarm64sve.h b/src/coreclr/jit/instrsarm64sve.h index 7350a288c92f5f..0c0a4019237149 100644 --- a/src/coreclr/jit/instrsarm64sve.h +++ b/src/coreclr/jit/instrsarm64sve.h @@ -265,7 +265,7 @@ INST6(ld1sb, "ld1sb", 0, IF_SV // LD1SB {.D }, /Z, [, .D, ] SVE_HW_4A 110001000h0mmmmm 000gggnnnnnttttt C400 0000 // LD1SB {.S }, /Z, [, .S, ] SVE_HW_4A_A 100001000h0mmmmm 000gggnnnnnttttt 8400 0000 // LD1SB {.D }, /Z, [, .D] SVE_HW_4B 11000100010mmmmm 100gggnnnnnttttt C440 8000 - // LD1SB {.D }, /Z, [.D{, #}] SVE_HX_3A_B 10000100001iiiii 100gggnnnnnttttt 8420 8000 + // LD1SB {.S }, /Z, [.S{, #}] SVE_HX_3A_B 10000100001iiiii 100gggnnnnnttttt 8420 8000 // LD1SB {.D }, /Z, [{, #, MUL VL}] SVE_IJ_3A_D 101001011000iiii 101gggnnnnnttttt A580 A000 // LD1SB {.D }, /Z, [, ] SVE_IK_4A_F 10100101100mmmmm 010gggnnnnnttttt A580 4000 @@ -275,7 +275,7 @@ INST6(ld1b, "ld1b", 0, IF_SV // LD1B {.D }, /Z, [, .D, ] SVE_HW_4A 110001000h0mmmmm 010gggnnnnnttttt C400 4000 // LD1B {.S }, /Z, [, .S, ] SVE_HW_4A_A 100001000h0mmmmm 010gggnnnnnttttt 8400 4000 // LD1B {.D }, /Z, [, .D] SVE_HW_4B 11000100010mmmmm 110gggnnnnnttttt C440 C000 - // LD1B {.D }, /Z, [.D{, #}] SVE_HX_3A_B 10000100001iiiii 110gggnnnnnttttt 8420 C000 + // LD1B {.S }, /Z, [.S{, #}] SVE_HX_3A_B 10000100001iiiii 110gggnnnnnttttt 8420 C000 // LD1B {.B }, /Z, [{, #, MUL VL}] SVE_IJ_3A_E 101001000000iiii 101gggnnnnnttttt A400 A000 // LD1B {.B }, /Z, [, ] SVE_IK_4A_H 10100100000mmmmm 010gggnnnnnttttt A400 4000 @@ -395,7 +395,7 @@ INST5(ldff1sb, "ldff1sb", 0, IF_SV // LDFF1SB {.D }, /Z, [, .D, ] SVE_HW_4A 110001000h0mmmmm 001gggnnnnnttttt C400 2000 // LDFF1SB {.S }, /Z, [, .S, ] SVE_HW_4A_A 100001000h0mmmmm 001gggnnnnnttttt 8400 2000 // LDFF1SB {.D }, /Z, [, .D] SVE_HW_4B 11000100010mmmmm 101gggnnnnnttttt C440 A000 - // LDFF1SB {.D }, /Z, [.D{, #}] SVE_HX_3A_B 10000100001iiiii 101gggnnnnnttttt 8420 A000 + // LDFF1SB {.S }, /Z, [.S{, #}] SVE_HX_3A_B 10000100001iiiii 101gggnnnnnttttt 8420 A000 // LDFF1SB {.D }, /Z, [{, }] SVE_IG_4A_D 10100101100mmmmm 011gggnnnnnttttt A580 6000 @@ -404,7 +404,7 @@ INST5(ldff1b, "ldff1b", 0, IF_SV // LDFF1B {.D }, /Z, [, .D, ] SVE_HW_4A 110001000h0mmmmm 011gggnnnnnttttt C400 6000 // LDFF1B {.S }, /Z, [, .S, ] SVE_HW_4A_A 100001000h0mmmmm 011gggnnnnnttttt 8400 6000 // LDFF1B {.D }, /Z, [, .D] SVE_HW_4B 11000100010mmmmm 111gggnnnnnttttt C440 E000 - // LDFF1B {.D }, /Z, [.D{, #}] SVE_HX_3A_B 10000100001iiiii 111gggnnnnnttttt 8420 E000 + // LDFF1B {.S }, /Z, [.S{, #}] SVE_HX_3A_B 10000100001iiiii 111gggnnnnnttttt 8420 E000 // LDFF1B {.B }, /Z, [{, }] SVE_IG_4A_E 10100100000mmmmm 011gggnnnnnttttt A400 6000 From 2714e155f3f513211075a5b8fcdf9526a5fcc13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Sat, 17 Feb 2024 06:11:07 +0900 Subject: [PATCH 087/158] Remove exclusion for a fixed test (#98542) --- src/tests/issues.targets | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 2737556054ab7d..60f4dd83b52c09 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1012,9 +1012,6 @@ https://github.com/dotnet/runtimelab/issues/165 - - https://github.com/dotnet/runtimelab/issues/201 - https://github.com/dotnet/runtimelab/issues/200 From efe8b8789b0ef472c21e6ea934d2dc4481719283 Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Fri, 16 Feb 2024 16:42:55 -0500 Subject: [PATCH 088/158] [mono] Fix msvc warning (#98568) * Fix msvc warning * One more signing mismatch --------- Co-authored-by: Steve Pfister --- src/mono/mono/mini/mini-llvm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 03a5a2485eb688..5db5828eaaa91d 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -11634,7 +11634,7 @@ MONO_RESTORE_WARNING lhs = LLVMBuildLoad2 (builder, ret_t, addresses [ins->sreg1]->value, ""); LLVMValueRef *args = g_new0(LLVMValueRef, n_elem_tuple + 2); - int idx = 0; + unsigned int idx = 0; for ( ; idx < n_elem_tuple; idx++) { args [idx] = LLVMBuildExtractValue (builder, lhs, idx, "extract_elem"); } @@ -11650,7 +11650,7 @@ MONO_RESTORE_WARNING LLVMValueRef retval = LLVMGetUndef (ret_t); args [idx - 1] = const_int64 (i); LLVMValueRef result_loaded = call_overloaded_intrins (ctx, iid, ovr_tag, args, ""); - for (int j = 0; j < n_elem_tuple; j++) { + for (unsigned int j = 0; j < n_elem_tuple; j++) { LLVMValueRef elem = LLVMBuildExtractValue (builder, result_loaded, j, "extract_elem"); retval = LLVMBuildInsertValue (builder, retval, elem, j, "insert_val"); } From 50c01531acc9a95202b7ec457ff8de592c2555b7 Mon Sep 17 00:00:00 2001 From: Andy Bevan <7716304+AndyBevan@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:43:49 -0500 Subject: [PATCH 089/158] Added more verbose error message when calling method using reflection and target type does not match passed type (#98129) * Added more verbose error message when calling method using reflection and target type does not match passed type (fixes #97022) * Fixed resource string as per PR comment * Fixed the test related to a resource string change * Modified test to have a contains check rather than validating the English string. * Modifications to AOT implementation for validating target type when a method is invoked using reflection, inline with other PR changes --- .../Reflection/Core/Execution/MethodBaseInvoker.cs | 2 +- .../src/Resources/Strings.resx | 5 ++++- .../src/System/Reflection/MethodInvokerCommon.cs | 2 +- .../System.Reflection.Tests/DefaultBinderTests.cs | 12 ++++++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodBaseInvoker.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodBaseInvoker.cs index 78fc4d99d2792a..a14c0007dc8f10 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodBaseInvoker.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodBaseInvoker.cs @@ -57,7 +57,7 @@ protected static void ValidateThis(object thisObject, RuntimeTypeHandle declarin throw new TargetException(SR.RFLCT_Targ_StatMethReqTarg); if (!RuntimeAugments.IsAssignable(thisObject, declaringTypeHandle)) - throw new TargetException(SR.RFLCT_Targ_ITargMismatch); + throw new TargetException(SR.Format(SR.RFLCT_Targ_ITargMismatch_WithType, declaringTypeHandle.GetRuntimeTypeInfoForRuntimeTypeHandle().Name, thisObject.GetType().Name)); } } } diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 09348ec9d3ce43..b6d8fd00aa4049 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3344,6 +3344,9 @@ Object does not match target type. + + Object type {0} does not match target type {1}. + Non-static field requires a target. @@ -4307,4 +4310,4 @@ Blocking wait is not supported on the JS interop threads. - \ No newline at end of file + diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs index 0096a9f397c397..4eda76d9364b75 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs @@ -96,7 +96,7 @@ internal static void ValidateInvokeTarget(object? target, MethodBase method) if (!method.DeclaringType!.IsInstanceOfType(target)) { - throw new TargetException(SR.RFLCT_Targ_ITargMismatch); + throw new TargetException(SR.Format(SR.RFLCT_Targ_ITargMismatch_WithType, method.DeclaringType.Name, target.GetType().Name)); } } diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/DefaultBinderTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/DefaultBinderTests.cs index 874e1f3d5864c6..d1c1f63d830e0a 100644 --- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/DefaultBinderTests.cs +++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/DefaultBinderTests.cs @@ -107,6 +107,18 @@ public static void DefaultBinderNamedParametersSkippedAndOutOfOrderTest() Assert.Equal("MethodMoreParameters", method.Name); } + [Fact] + public void InvokeWithIncorrectTargetTypeThrowsCorrectException() + { + Type t = typeof(Sample); + object incorrectInstance = Activator.CreateInstance(t); + MethodInvoker invoker = MethodInvoker.Create(typeof(Test).GetMethod(nameof(Test.TestMethod))); + + TargetException ex = Assert.Throws(() => invoker.Invoke(obj: incorrectInstance, "NotAnInt")); + Assert.Contains(nameof(Test), ex.Message); + Assert.Contains(nameof(Sample), ex.Message); + } + [Fact] public static void InvokeWithNamedParameters1st2ndTest() { From a9403137532c52d2a16a5ad70e2bdd00aaf67e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Sat, 17 Feb 2024 10:10:51 +0900 Subject: [PATCH 090/158] Delete type_traits.h (#98553) Bunch of template metaprogramming that is not actually used. --- src/coreclr/nativeaot/Runtime/inc/daccess.h | 1 - .../nativeaot/Runtime/inc/type_traits.hpp | 311 ------------------ src/coreclr/nativeaot/Runtime/slist.inl | 21 ++ 3 files changed, 21 insertions(+), 312 deletions(-) delete mode 100644 src/coreclr/nativeaot/Runtime/inc/type_traits.hpp diff --git a/src/coreclr/nativeaot/Runtime/inc/daccess.h b/src/coreclr/nativeaot/Runtime/inc/daccess.h index 7c237cc5ed30cd..2dd7c772f4e0d8 100644 --- a/src/coreclr/nativeaot/Runtime/inc/daccess.h +++ b/src/coreclr/nativeaot/Runtime/inc/daccess.h @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#include "type_traits.hpp" #include "CommonTypes.h" #include "../../inc/daccess.h" diff --git a/src/coreclr/nativeaot/Runtime/inc/type_traits.hpp b/src/coreclr/nativeaot/Runtime/inc/type_traits.hpp deleted file mode 100644 index 0bd237aa1bc2e0..00000000000000 --- a/src/coreclr/nativeaot/Runtime/inc/type_traits.hpp +++ /dev/null @@ -1,311 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// -// type_traits.hpp -// -// Type trait metaprogramming utilities. -// - -#ifndef __TYPE_TRAITS_HPP__ -#define __TYPE_TRAITS_HPP__ - -#include "CommonTypes.h" - -namespace type_traits -{ - -namespace imp -{ - -struct true_type { static const bool value = true; }; -struct false_type { static const bool value = false; }; - -//////////////////////////////////////////////////////////////////////////////// -// Helper types Small and Big - guarantee that sizeof(Small) < sizeof(Big) -// - -template -struct conversion_helper -{ - typedef char Small; - struct Big { char dummy[2]; }; - static Big Test(...); - static Small Test(U); - static T MakeT(); -}; - -//////////////////////////////////////////////////////////////////////////////// -// class template conversion -// Figures out the conversion relationships between two types -// Invocations (T and U are types): -// a) conversion::exists -// returns (at compile time) true if there is an implicit conversion from T -// to U (example: Derived to Base) -// b) conversion::exists2Way -// returns (at compile time) true if there are both conversions from T -// to U and from U to T (example: int to char and back) -// c) conversion::sameType -// returns (at compile time) true if T and U represent the same type -// -// NOTE: might not work if T and U are in a private inheritance hierarchy. -// - -template -struct conversion -{ - typedef imp::conversion_helper H; - static const bool exists = sizeof(typename H::Small) == sizeof((H::Test(H::MakeT()))); - static const bool exists2Way = exists && conversion::exists; - static const bool sameType = false; -}; - -template -struct conversion -{ - static const bool exists = true; - static const bool exists2Way = true; - static const bool sameType = true; -}; - -template -struct conversion -{ - static const bool exists = false; - static const bool exists2Way = false; - static const bool sameType = false; -}; - -template -struct conversion -{ - static const bool exists = false; - static const bool exists2Way = false; - static const bool sameType = false; -}; - -template <> -struct conversion -{ - static const bool exists = true; - static const bool exists2Way = true; - static const bool sameType = true; -}; - -template -struct is_base_of_helper; - -template <> -struct is_base_of_helper : public true_type {} ; - -template <> -struct is_base_of_helper : public false_type {} ; - -}// imp - -//////////////////////////////////////////////////////////////////////////////// -// is_base_of::value is typedefed to be true if TDerived derives from TBase -// and false otherwise. -// -// -// NOTE: use TR1 type_traits::is_base_of when available. -// -#ifdef _MSC_VER - -template -struct is_base_of : public imp::is_base_of_helper<__is_base_of( TBase, TDerived)> {}; - -#else - -// Note that we need to compare pointer types here, since conversion of types by-value -// just tells us whether or not an implicit conversion constructor exists. We handle -// type parameters that are already pointers specially; see below. -template -struct is_base_of : public imp::is_base_of_helper::exists> {}; - -// Specialization to handle type parameters that are already pointers. -template -struct is_base_of : public imp::is_base_of_helper::exists> {}; - -// Specialization to handle invalid mixing of pointer types. -template -struct is_base_of : public imp::false_type {}; - -// Specialization to handle invalid mixing of pointer types. -template -struct is_base_of : public imp::false_type {}; - -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Remove const qualifications, if any. Access using remove_const::type -// -template struct remove_const { typedef T type; }; -template struct remove_const { typedef T type; }; - -//////////////////////////////////////////////////////////////////////////////// -// is_signed::value is true if T is a signed integral type, false otherwise. -// -template -struct is_signed { static const bool value = (static_cast(-1) < 0); }; - -} - -//////////////////////////////////////////////////////////////////////////////// -// These are related to type traits, but they are more like asserts of type -// traits in that the result is that either the compiler does or does not -// produce an error. -// -namespace type_constraints -{ - -//////////////////////////////////////////////////////////////////////////////// -// derived_from will produce a compiler error if TDerived does not -// derive from TBase. -// -// NOTE: use TR1 type_traits::is_base_of when available. -// - -template struct is_base_of -{ - is_base_of() - { - static_assert((type_traits::is_base_of::value), - "is_base_of() constraint violation: TDerived does not derive from TBase"); - } -}; - -}; // namespace type_constraints - -namespace rh { namespace std -{ - // Import some select components of the STL - - // TEMPLATE FUNCTION for_each - template - inline - _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func) - { // perform function for each element - for (; _First != _Last; ++_First) - _Func(*_First); - return (_Func); - } - - template - inline - _InIt find(_InIt _First, _InIt _Last, const _Ty& _Val) - { // find first matching _Val - for (; _First != _Last; ++_First) - if (*_First == _Val) - break; - return (_First); - } - - template - inline - _InIt find_if(_InIt _First, _InIt _Last, _Pr _Pred) - { // find first satisfying _Pred - for (; _First != _Last; ++_First) - if (_Pred(*_First)) - break; - return (_First); - } - - template - inline - bool exists(_InIt _First, _InIt _Last, const _Ty& _Val) - { - return find(_First, _Last, _Val) != _Last; - } - - template - inline - bool exists_if(_InIt _First, _InIt _Last, _Pr _Pred) - { - return find_if(_First, _Last, _Pred) != _Last; - } - - template - inline - uintptr_t count(_InIt _First, _InIt _Last, const _Ty& _Val) - { - uintptr_t _Ret = 0; - for (; _First != _Last; _First++) - if (*_First == _Val) - ++_Ret; - return _Ret; - } - - template - inline - uintptr_t count_if(_InIt _First, _InIt _Last, _Pr _Pred) - { - uintptr_t _Ret = 0; - for (; _First != _Last; _First++) - if (_Pred(*_First)) - ++_Ret; - return _Ret; - } - - // Forward declaration, each collection requires specialization - template - inline - _FwdIt remove(_FwdIt _First, _FwdIt _Last, const _Ty& _Val); -} // namespace std -} // namespace rh - -#if 0 - -// ----------------------------------------------------------------- -// Holding place for unused-but-possibly-useful-in-the-future code. - -// ------------------------------------------------- -// This belongs in type_traits.hpp - -// -// is_pointer::value is true if the type is a pointer, false otherwise -// -template struct is_pointer : public false_type {}; -template struct is_pointer : public true_type {}; - -// -// Remove pointer from type, if it has one. Use remove_pointer::type -// Further specialized in daccess.h -// -template struct remove_pointer { typedef T type; }; -template struct remove_pointer { typedef T type; }; - -// ------------------------------------------------- -// This belongs in daccess.h - -namespace type_traits -{ - -// -// is_pointer::value is true if the type is a pointer, false otherwise -// specialized from type_traits.hpp -// -template struct is_pointer > : public type_traits::true_type {}; - -// -// remove_pointer::type is T with one less pointer qualification, if it had one. -// specialized from type_traits.hpp -// -template struct remove_pointer > { typedef T type; }; - -} // type_traits - -namespace dac -{ - -// -// is_dptr::value is true if T is a __DPtr, false otherwise. -// This is a partial specialization case for the positive case. -// -//template struct is_dptr > : public type_traits::true_type {}; - -} - -#endif - -#endif - diff --git a/src/coreclr/nativeaot/Runtime/slist.inl b/src/coreclr/nativeaot/Runtime/slist.inl index bdfbb131f27bcd..a9fb5989875473 100644 --- a/src/coreclr/nativeaot/Runtime/slist.inl +++ b/src/coreclr/nativeaot/Runtime/slist.inl @@ -8,6 +8,27 @@ MSVC_DISABLE_WARNING(4127) // conditional expression is constant -- //------------------------------------------------------------------------------------------------- namespace rh { namespace std { + template + inline + uintptr_t count(_InIt _First, _InIt _Last, const _Ty& _Val) + { + uintptr_t _Ret = 0; + for (; _First != _Last; _First++) + if (*_First == _Val) + ++_Ret; + return _Ret; + } + + template + inline + _InIt find(_InIt _First, _InIt _Last, const _Ty& _Val) + { // find first matching _Val + for (; _First != _Last; ++_First) + if (*_First == _Val) + break; + return (_First); + } + // Specialize rh::std::find for SList iterators so that it will use _Traits::Equals. template inline From 21062aa4977f5546d41a086d047ff1be4a6d1c41 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 16 Feb 2024 19:06:19 -0800 Subject: [PATCH 091/158] Use Ubuntu-22.04-based Tizen image (#98589) --- eng/pipelines/common/templates/pipeline-with-resources.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/pipelines/common/templates/pipeline-with-resources.yml b/eng/pipelines/common/templates/pipeline-with-resources.yml index fddbe8c9bd8661..5c01cc93fba523 100644 --- a/eng/pipelines/common/templates/pipeline-with-resources.yml +++ b/eng/pipelines/common/templates/pipeline-with-resources.yml @@ -101,7 +101,7 @@ resources: ROOTFS_DIR: /crossrootfs/x64 - container: tizen_armel - image: mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-cross-armel-tizen + image: mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-cross-armel-tizen env: ROOTFS_DIR: /crossrootfs/armel From 55b2009680c5e126f7d9acfee9cc7133d7fd4960 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 16 Feb 2024 19:07:41 -0800 Subject: [PATCH 092/158] Forward the pgoinstrument argument to the inner build for VMR/Source-build builds (#98446) --- eng/DotNetBuild.props | 1 + eng/Subsets.props | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/eng/DotNetBuild.props b/eng/DotNetBuild.props index 8527ec175ad88b..3c68bf354bae17 100644 --- a/eng/DotNetBuild.props +++ b/eng/DotNetBuild.props @@ -61,6 +61,7 @@ $(InnerBuildArgs) /p:WasmEnableThreads=true $(InnerBuildArgs) $(FlagParameterPrefix)s clr.nativeaotlibs+clr.nativeaotruntime+libs+packs /p:BuildNativeAOTRuntimePack=true /p:SkipLibrariesNativeRuntimePackages=true + $(InnerBuildArgs) $(FlagParameterPrefix)pgoinstrument $(InnerBuildArgs) /p:DotNetBuildRepo=true diff --git a/eng/Subsets.props b/eng/Subsets.props index cd4532699b10e3..dd284ea6d99776 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -43,6 +43,11 @@ clr+libs+tools+host+packs + + + true + + <_subset Condition="'$(Subset)' != ''">+$(Subset.ToLowerInvariant())+ @@ -79,7 +84,7 @@ '$(BuildTargetFramework)' == '' or '$(BuildAllConfigurations)' == 'true'">libs.native+ $(DefaultLibrariesSubsets)libs.sfx+libs.oob+libs.pretest - + $(DefaultLibrariesSubsets)+libs.tests tools.illink @@ -391,7 +396,7 @@ - + @@ -448,7 +453,7 @@ - + @@ -508,10 +513,10 @@ - + - + @@ -521,7 +526,7 @@ - + @@ -531,12 +536,12 @@ - + - + From 05c51fac03cd355e7088f8b946986f7d3382a367 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Fri, 16 Feb 2024 23:32:17 -0500 Subject: [PATCH 093/158] Add additional hash algorithms for X.509 SKI calculation --- .../TestUtilities/System/AssertExtensions.cs | 13 ++ .../ref/System.Security.Cryptography.cs | 6 + .../X509SubjectKeyIdentifierExtension.cs | 21 ++ .../X509SubjectKeyIdentifierHashAlgorithm.cs | 30 +++ .../SubjectKeyIdentifierTests.cs | 208 +++++++++++++++++- 5 files changed, 273 insertions(+), 5 deletions(-) diff --git a/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs b/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs index d74b25c120aec5..232c05afb8d271 100644 --- a/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs +++ b/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs @@ -504,6 +504,19 @@ public static void CollectionEqual(IEnumerable expected, IEnumerable ac } } + /// + /// Validates that the actual span is not equal to the expected span. + /// + /// The sequence that should be not be equal to. + /// The actual sequence. + public static void SequenceNotEqual(ReadOnlySpan expected, ReadOnlySpan actual) where T : IEquatable + { + if (expected.SequenceEqual(actual)) + { + throw new XunitException($"Expected: Contents of expected to differ from actual but were the same."); + } + } + /// /// Validates that the actual span is equal to the expected span. /// If this fails, determine where the differences are and create an exception with that information. diff --git a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs index 6220729b74a45c..a6ee388c8ceca2 100644 --- a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs +++ b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs @@ -3612,6 +3612,12 @@ public enum X509SubjectKeyIdentifierHashAlgorithm Sha1 = 0, ShortSha1 = 1, CapiSha1 = 2, + Sha256 = 3, + Sha384 = 4, + Sha512 = 5, + ShortSha256 = 6, + ShortSha384 = 7, + ShortSha512 = 8, } [System.FlagsAttribute] public enum X509VerificationFlags diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SubjectKeyIdentifierExtension.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SubjectKeyIdentifierExtension.cs index 52c4ad6f8b9513..a97061a885f774 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SubjectKeyIdentifierExtension.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SubjectKeyIdentifierExtension.cs @@ -186,11 +186,32 @@ private static byte[] GenerateSubjectKeyIdentifierFromPublicKey(PublicKey key, X case X509SubjectKeyIdentifierHashAlgorithm.CapiSha1: // CAPI SHA1 is the SHA-1 hash over the whole SubjectPublicKeyInfo return HashSubjectPublicKeyInfo(key, HashAlgorithmName.SHA1); + case X509SubjectKeyIdentifierHashAlgorithm.Sha256: + return HashSubjectPublicKeyInfo(key, HashAlgorithmName.SHA256); + case X509SubjectKeyIdentifierHashAlgorithm.Sha384: + return HashSubjectPublicKeyInfo(key, HashAlgorithmName.SHA384); + case X509SubjectKeyIdentifierHashAlgorithm.Sha512: + return HashSubjectPublicKeyInfo(key, HashAlgorithmName.SHA512); + case X509SubjectKeyIdentifierHashAlgorithm.ShortSha256: + return HashSubjectPublicKeyLeft160Bits(key, HashAlgorithmName.SHA256); + case X509SubjectKeyIdentifierHashAlgorithm.ShortSha384: + return HashSubjectPublicKeyLeft160Bits(key, HashAlgorithmName.SHA384); + case X509SubjectKeyIdentifierHashAlgorithm.ShortSha512: + return HashSubjectPublicKeyLeft160Bits(key, HashAlgorithmName.SHA512); default: throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, algorithm), nameof(algorithm)); } } + private static byte[] HashSubjectPublicKeyLeft160Bits(PublicKey key, HashAlgorithmName hashAlgorithmName) + { + const int TruncateSize = 160 / 8; + Span hash = stackalloc byte[512 / 8]; // Largest known hash is 512-bits. + int written = CryptographicOperations.HashData(hashAlgorithmName, key.EncodedKeyValue.RawData, hash); + Debug.Assert(written >= TruncateSize); + return hash.Slice(0, TruncateSize).ToArray(); + } + private static byte[] HashSubjectPublicKeyInfo(PublicKey key, HashAlgorithmName hashAlgorithmName) { Span hash = stackalloc byte[512 / 8]; // Largest known hash is 512-bits. diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SubjectKeyIdentifierHashAlgorithm.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SubjectKeyIdentifierHashAlgorithm.cs index 5210a244455ebe..efe5777cc5b8c2 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SubjectKeyIdentifierHashAlgorithm.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SubjectKeyIdentifierHashAlgorithm.cs @@ -11,5 +11,35 @@ public enum X509SubjectKeyIdentifierHashAlgorithm Sha1 = 0, ShortSha1 = 1, CapiSha1 = 2, + + /// + /// The SHA-256 hash over the SubjectPublicKeyInfo as described in RFC 7093. + /// + Sha256 = 3, + + /// + /// The SHA-384 hash over the SubjectPublicKeyInfo as described in RFC 7093. + /// + Sha384 = 4, + + /// + /// The SHA-512 hash over the SubjectPublicKeyInfo as described in RFC 7093. + /// + Sha512 = 5, + + /// + /// The SHA-256 hash over the subjectPublicKey truncated to the leftmost 160-bits as described in RFC 7093. + /// + ShortSha256 = 6, + + /// + /// The SHA-384 hash over the subjectPublicKey truncated to the leftmost 160-bits as described in RFC 7093. + /// + ShortSha384 = 7, + + /// + /// The SHA-512 hash over the subjectPublicKey truncated to the leftmost 160-bits as described in RFC 7093. + /// + ShortSha512 = 8, } } diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/ExtensionsTests/SubjectKeyIdentifierTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/ExtensionsTests/SubjectKeyIdentifierTests.cs index e4d743a00e015b..583cac7b75c4a5 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/ExtensionsTests/SubjectKeyIdentifierTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/ExtensionsTests/SubjectKeyIdentifierTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using Test.Cryptography; using Xunit; @@ -9,6 +10,10 @@ namespace System.Security.Cryptography.X509Certificates.Tests.ExtensionsTests [SkipOnPlatform(TestPlatforms.Browser, "Browser doesn't support X.509 certificates")] public static class SubjectKeyIdentifierTests { + private const string EcPublicKey = "1.2.840.10045.2.1"; + private static ReadOnlySpan NistP256r1 => [0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]; + private static ReadOnlySpan BrainpoolP256r1 => [0x06, 0x09, 0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07]; + [Fact] public static void DefaultConstructor() { @@ -148,6 +153,184 @@ public static void DecodeFromBER() Assert.Equal(skid, Convert.ToHexString(ext.SubjectKeyIdentifierBytes.Span)); } + [Theory] + [MemberData(nameof(Rfc7093Examples))] + public static void EncodeDecode_Rfc7093_Examples( + byte[] subjectPublicKeyInfo, + X509SubjectKeyIdentifierHashAlgorithm algorithm, + string expectedDer, + string expectedIdentifier) + { + EncodeDecodeSubjectPublicKeyInfo( + subjectPublicKeyInfo, + algorithm, + false, + Convert.FromHexString(expectedDer), + expectedIdentifier); + } + + [Theory] + [MemberData(nameof(Rfc7093Vectors))] + public static void EncodeDecode_Rfc7093_TestVectors( + byte[] key, + byte[] parameters, + X509SubjectKeyIdentifierHashAlgorithm algorithm, + string expectedDer, + string expectedIdentifier) + { + EncodeDecodePublicKey( + new PublicKey(new Oid("1.2.3.4"), new AsnEncodedData(parameters), new AsnEncodedData(key)), + algorithm, + false, + Convert.FromHexString(expectedDer), + expectedIdentifier); + } + + [Theory] + [InlineData(X509SubjectKeyIdentifierHashAlgorithm.ShortSha256)] + [InlineData(X509SubjectKeyIdentifierHashAlgorithm.ShortSha384)] + [InlineData(X509SubjectKeyIdentifierHashAlgorithm.ShortSha512)] + public static void Rfc7093_Truncated_SubjectPublicKeyOnly(X509SubjectKeyIdentifierHashAlgorithm algorithm) + { + ReadOnlySpan ecKey = + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + ]; + + PublicKey nistP256Key = new PublicKey(new Oid(EcPublicKey), new AsnEncodedData(NistP256r1), new AsnEncodedData(ecKey)); + PublicKey brainboolP256Key = new PublicKey(new Oid(EcPublicKey), new AsnEncodedData(BrainpoolP256r1), new AsnEncodedData(ecKey)); + + X509SubjectKeyIdentifierExtension nistP256Extension = new(nistP256Key, algorithm, critical: false); + X509SubjectKeyIdentifierExtension brainpoolP256Extension = new(brainboolP256Key, algorithm, critical: false); + + // Although the PublicKeys have different parameters by their curve, the key material is the same, so the + // hash should not differ. + AssertExtensions.SequenceEqual( + nistP256Extension.SubjectKeyIdentifierBytes.Span, + brainpoolP256Extension.SubjectKeyIdentifierBytes.Span); + } + + [Theory] + [InlineData(X509SubjectKeyIdentifierHashAlgorithm.Sha256)] + [InlineData(X509SubjectKeyIdentifierHashAlgorithm.Sha384)] + [InlineData(X509SubjectKeyIdentifierHashAlgorithm.Sha512)] + public static void Rfc7093_SubjectPublicKeyInfo(X509SubjectKeyIdentifierHashAlgorithm algorithm) + { + ReadOnlySpan ecKey = + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + ]; + + PublicKey nistP256Key = new PublicKey(new Oid(EcPublicKey), new AsnEncodedData(NistP256r1), new AsnEncodedData(ecKey)); + PublicKey brainboolP256Key = new PublicKey(new Oid(EcPublicKey), new AsnEncodedData(BrainpoolP256r1), new AsnEncodedData(ecKey)); + + X509SubjectKeyIdentifierExtension nistP256Extension = new(nistP256Key, algorithm, critical: false); + X509SubjectKeyIdentifierExtension brainpoolP256Extension = new(brainboolP256Key, algorithm, critical: false); + + // Although the PublicKeys have the same key, their parameters are different, thus should produce different + // hashes. + AssertExtensions.SequenceNotEqual( + nistP256Extension.SubjectKeyIdentifierBytes.Span, + brainpoolP256Extension.SubjectKeyIdentifierBytes.Span); + } + + public static IEnumerable Rfc7093Examples() + { + byte[] example = + [ + 0x30, 0x59, + 0x30, 0x13, + 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, + 0x03, 0x42, 0x00, + 0x04, + 0x7F, 0x7F, 0x35, 0xA7, 0x97, 0x94, 0xC9, 0x50, 0x06, 0x0B, 0x80, 0x29, 0xFC, 0x8F, 0x36, 0x3A, + 0x28, 0xF1, 0x11, 0x59, 0x69, 0x2D, 0x9D, 0x34, 0xE6, 0xAC, 0x94, 0x81, 0x90, 0x43, 0x47, 0x35, + 0xF8, 0x33, 0xB1, 0xA6, 0x66, 0x52, 0xDC, 0x51, 0x43, 0x37, 0xAF, 0xF7, 0xF5, 0xC9, 0xC7, 0x5D, + 0x67, 0x0C, 0x01, 0x9D, 0x95, 0xA5, 0xD6, 0x39, 0xB7, 0x27, 0x44, 0xC6, 0x4A, 0x91, 0x28, 0xBB, + ]; + + // Method 1 example from RFC 7093 + yield return new object[] + { + example, + X509SubjectKeyIdentifierHashAlgorithm.ShortSha256, + "0414BF37B3E5808FD46D54B28E846311BCCE1CAD2E1A", + "BF37B3E5808FD46D54B28E846311BCCE1CAD2E1A", + }; + + // Method 4 example from RFC 7093 + yield return new object[] + { + example, + X509SubjectKeyIdentifierHashAlgorithm.Sha256, + "04206D20896AB8BD833B6B66554BD59B20225D8A75A296088148399D7BF763D57405", + "6D20896AB8BD833B6B66554BD59B20225D8A75A296088148399D7BF763D57405", + }; + } + + public static IEnumerable Rfc7093Vectors() + { + byte[] key = [1, 2, 3, 4]; + byte[] parameters = [4, 4, 5, 6, 7, 8]; + + yield return new object[] + { + key, + parameters, + X509SubjectKeyIdentifierHashAlgorithm.Sha256, + "04200B710654AEB48CE6A1FF80C0F3E83FAD8DB63B7E1004BE8F3EEC10E95CF3C620", + "0B710654AEB48CE6A1FF80C0F3E83FAD8DB63B7E1004BE8F3EEC10E95CF3C620", + }; + + yield return new object[] + { + key, + parameters, + X509SubjectKeyIdentifierHashAlgorithm.Sha384, + "0430150E43FE7ACE471CDB3910809145AD44B5B7E641A0364D608A1C106C9AD47963BAFE05E431B7782D791DE1B7E25F69DA", + "150E43FE7ACE471CDB3910809145AD44B5B7E641A0364D608A1C106C9AD47963BAFE05E431B7782D791DE1B7E25F69DA", + }; + + yield return new object[] + { + key, + parameters, + X509SubjectKeyIdentifierHashAlgorithm.Sha512, + "0440493CF4FC4B5CF15C17D5FCF4F85CFD1CFBCF29BEA538B8063733922A43693FEECAE70A11BEE932E23C32350C1F624DB16962A6AE6EF4B29BB3BFAD838048006F", + "493CF4FC4B5CF15C17D5FCF4F85CFD1CFBCF29BEA538B8063733922A43693FEECAE70A11BEE932E23C32350C1F624DB16962A6AE6EF4B29BB3BFAD838048006F", + }; + + yield return new object[] + { + key, + parameters, + X509SubjectKeyIdentifierHashAlgorithm.ShortSha256, + "04149F64A747E1B97F131FABB6B447296C9B6F0201E7", + "9F64A747E1B97F131FABB6B447296C9B6F0201E7", + }; + + yield return new object[] + { + key, + parameters, + X509SubjectKeyIdentifierHashAlgorithm.ShortSha384, + "04145A667D62430A8C253EBAE433333904DC6E1D41DC", + "5A667D62430A8C253EBAE433333904DC6E1D41DC", + }; + + yield return new object[] + { + key, + parameters, + X509SubjectKeyIdentifierHashAlgorithm.ShortSha512, + "0414A7C976DB1723ADB41274178DC82E9B777941AB20", + "A7C976DB1723ADB41274178DC82E9B777941AB20", + }; + } + private static void EncodeDecode( byte[] certBytes, X509SubjectKeyIdentifierHashAlgorithm algorithm, @@ -155,15 +338,30 @@ private static void EncodeDecode( byte[] expectedDer, string expectedIdentifier) { - PublicKey pk; - using (var cert = new X509Certificate2(certBytes)) { - pk = cert.PublicKey; + EncodeDecodePublicKey(cert.PublicKey, algorithm, critical, expectedDer, expectedIdentifier); } + } - X509SubjectKeyIdentifierExtension ext = - new X509SubjectKeyIdentifierExtension(pk, algorithm, critical); + private static void EncodeDecodeSubjectPublicKeyInfo( + byte[] spkiBytes, + X509SubjectKeyIdentifierHashAlgorithm algorithm, + bool critical, + byte[] expectedDer, + string expectedIdentifier) + { + PublicKey publicKey = PublicKey.CreateFromSubjectPublicKeyInfo(spkiBytes, out _); + EncodeDecodePublicKey(publicKey, algorithm, critical, expectedDer, expectedIdentifier); + } + private static void EncodeDecodePublicKey( + PublicKey publicKey, + X509SubjectKeyIdentifierHashAlgorithm algorithm, + bool critical, + byte[] expectedDer, + string expectedIdentifier) + { + X509SubjectKeyIdentifierExtension ext = new X509SubjectKeyIdentifierExtension(publicKey, algorithm, critical); byte[] rawData = ext.RawData; Assert.Equal(expectedDer, rawData); From 722774a3c4cd04fa64bc697054bf1bea6ac9dea6 Mon Sep 17 00:00:00 2001 From: David Mason Date: Sat, 17 Feb 2024 00:08:31 -0800 Subject: [PATCH 094/158] Check for EnC in profiler rejit (#98546) --- src/coreclr/pal/prebuilt/inc/corerror.h | 1 + src/coreclr/vm/rejit.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/coreclr/pal/prebuilt/inc/corerror.h b/src/coreclr/pal/prebuilt/inc/corerror.h index 994648cb25ea7b..12d3490aaf09d8 100644 --- a/src/coreclr/pal/prebuilt/inc/corerror.h +++ b/src/coreclr/pal/prebuilt/inc/corerror.h @@ -214,6 +214,7 @@ #define CORDIAGIPC_E_UNKNOWN_ERROR EMAKEHR(0x1387) #define CORPROF_E_SUSPENSION_IN_PROGRESS EMAKEHR(0x1388) #define CORPROF_E_NOT_GC_OBJECT EMAKEHR(0x1389) +#define CORPROF_E_MODULE_IS_ENC EMAKEHR(0x138A) #define CORSEC_E_POLICY_EXCEPTION EMAKEHR(0x1416) #define CORSEC_E_MIN_GRANT_FAIL EMAKEHR(0x1417) #define CORSEC_E_NO_EXEC_PERM EMAKEHR(0x1418) diff --git a/src/coreclr/vm/rejit.cpp b/src/coreclr/vm/rejit.cpp index c4f7394a93870a..071fddacb3f411 100644 --- a/src/coreclr/vm/rejit.cpp +++ b/src/coreclr/vm/rejit.cpp @@ -508,6 +508,12 @@ HRESULT ReJitManager::UpdateActiveILVersions( continue; } + if (pModule->IsEditAndContinueEnabled()) + { + ReportReJITError(pModule, rgMethodDefs[i], NULL, CORPROF_E_MODULE_IS_ENC); + continue; + } + if (!pModule->GetMDImport()->IsValidToken(rgMethodDefs[i])) { ReportReJITError(pModule, rgMethodDefs[i], NULL, E_INVALIDARG); From cc89d7db095028948afde863114b3ca04147477d Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 17 Feb 2024 00:35:22 -0800 Subject: [PATCH 095/158] Fix error message for byref of byref TypeLoadException (#98548) Related to #98426 --- src/coreclr/dlls/mscorrc/mscorrc.rc | 9 ++++++--- src/coreclr/dlls/mscorrc/resource.h | 3 +++ src/coreclr/vm/clsload.cpp | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc index eb48bb390d0934..fbd4695227d93c 100644 --- a/src/coreclr/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/dlls/mscorrc/mscorrc.rc @@ -309,9 +309,12 @@ BEGIN IDS_CLASSLOAD_GENERICTYPE_RECURSIVE "Could not load type '%1' from assembly '%2' because it has recursive generic definition." IDS_CLASSLOAD_TOOMANYGENERICARGS "Could not load type '%1' from assembly '%2'. Internal limitation: Too many generic arguments." - IDS_CLASSLOAD_INLINE_ARRAY_FIELD_COUNT "InlineArrayAttribute requires that the target type has a single instance field. Type: '%1'. Assembly: '%2'." - IDS_CLASSLOAD_INLINE_ARRAY_LENGTH "InlineArrayAttribute requires that the length argument is greater than 0. Type: '%1'. Assembly: '%2'." - IDS_CLASSLOAD_INLINE_ARRAY_EXPLICIT "InlineArrayAttribute cannot be applied to a type with explicit layout. Type: '%1'. Assembly: '%2'." + IDS_CLASSLOAD_INLINE_ARRAY_FIELD_COUNT "InlineArrayAttribute requires that the target type has a single instance field. Type: '%1'. Assembly: '%2'." + IDS_CLASSLOAD_INLINE_ARRAY_LENGTH "InlineArrayAttribute requires that the length argument is greater than 0. Type: '%1'. Assembly: '%2'." + IDS_CLASSLOAD_INLINE_ARRAY_EXPLICIT "InlineArrayAttribute cannot be applied to a type with explicit layout. Type: '%1'. Assembly: '%2'." + + IDS_CLASSLOAD_BYREF_OF_BYREF "Could not create a ByRef of a ByRef. Type: '%1'. Assembly: '%2'." + IDS_CLASSLOAD_POINTER_OF_BYREF "Could not create a pointer to a ByRef. Type: '%1'. Assembly: '%2'." IDS_INVALID_RECURSIVE_GENERIC_FIELD_LOAD "Could not load type '%1' from assembly '%2' because of an invalid self-referential generic field." diff --git a/src/coreclr/dlls/mscorrc/resource.h b/src/coreclr/dlls/mscorrc/resource.h index 88473e27d10257..6cc4fc42beed46 100644 --- a/src/coreclr/dlls/mscorrc/resource.h +++ b/src/coreclr/dlls/mscorrc/resource.h @@ -174,6 +174,9 @@ #define IDS_CLASSLOAD_INLINE_ARRAY_LENGTH 0x17ad #define IDS_CLASSLOAD_INLINE_ARRAY_EXPLICIT 0x17ae +#define IDS_CLASSLOAD_BYREF_OF_BYREF 0x17af +#define IDS_CLASSLOAD_POINTER_OF_BYREF 0x17b0 + #define IDS_DEBUG_USERBREAKPOINT 0x17b6 #define IDS_PERFORMANCEMON_FUNCNOTFOUND 0x17bb diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 51dbb6c139aac1..9f83cf1a19c12e 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -2751,7 +2751,7 @@ TypeHandle ClassLoader::CreateTypeHandleForTypeKey(const TypeKey* pKey, AllocMem // no parameterized type allowed on a reference if (paramType.GetInternalCorElementType() == ELEMENT_TYPE_BYREF) { - ThrowTypeLoadException(pKey, IDS_CLASSLOAD_GENERAL); + ThrowTypeLoadException(pKey, (kind == ELEMENT_TYPE_BYREF) ? IDS_CLASSLOAD_BYREF_OF_BYREF : IDS_CLASSLOAD_POINTER_OF_BYREF); } // We do allow parameterized types of ByRefLike types. Languages may restrict them to produce safe or verifiable code, From c768315c5698391f45a6a47d56fec4ba3df59fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Sat, 17 Feb 2024 11:04:07 +0100 Subject: [PATCH 096/158] Fix NativeAOT publish failure on fi_FI culture (#98552) This culture uses `U+2212 : MINUS SIGN` instead of `-` for negative numbers which trips up msbuild when comparing the property. Instead of using an intermediate property just inline the usage and use `Contains()` for better readability. Fixes https://github.com/dotnet/runtime/issues/98550 --- .../Microsoft.DotNet.ILCompiler.SingleEntry.targets | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets index b96cf01e077975..eae701ec006915 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets @@ -5,8 +5,7 @@ <_hostOS>$(NETCoreSdkPortableRuntimeIdentifier.SubString(0, $(NETCoreSdkPortableRuntimeIdentifier.LastIndexOf('-')))) <_originalTargetOS>$(RuntimeIdentifier.SubString(0, $(RuntimeIdentifier.LastIndexOf('-')))) - <_indexOfPeriod>$(_originalTargetOS.IndexOf('.')) - <_originalTargetOS Condition="'$(_indexOfPeriod)' > -1">$(_originalTargetOS.SubString(0, $(_indexOfPeriod))) + <_originalTargetOS Condition="$(_originalTargetOS.Contains('.'))">$(_originalTargetOS.SubString(0, $(_originalTargetOS.IndexOf('.'))) <_originalTargetOS Condition="$(_originalTargetOS.StartsWith('win'))">win From 4b9a9d04139dd6c8c0344ef32e8df33f71740c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 17 Feb 2024 14:33:55 +0100 Subject: [PATCH 097/158] Fix Random.Shuffle covariance (#98471) * Fix Random.Shuffle covariance Made both overloads use an internal version that works on refs without checking covariance. Fixes #98470. * Update Random.cs * Use a simpler fix, add tests * Update Random.cs * Update Random.cs --- .../System.Private.CoreLib/src/System/Random.cs | 4 +++- .../System.Runtime.Extensions.Tests/System/Random.cs | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.cs b/src/libraries/System.Private.CoreLib/src/System/Random.cs index 65043bf4c00f61..190fa3583caac7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Random.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Random.cs @@ -294,7 +294,9 @@ public T[] GetItems(ReadOnlySpan choices, int length) public void Shuffle(T[] values) { ArgumentNullException.ThrowIfNull(values); - Shuffle(values.AsSpan()); + // this can't use AsSpan due to array covariance + // forcing it like this is safe due to everything being in the array already + Shuffle(new Span(ref MemoryMarshal.GetArrayDataReference(values), values.Length)); } /// diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Random.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Random.cs index 16c3a9a2353b7e..27cfa26920e064 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Random.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/Random.cs @@ -727,6 +727,16 @@ public static void Shuffle_Array_Seeded(bool emptyShuffle) AssertExtensions.SequenceEqual(stackalloc int[] { 1, 4, 3, 2 }, items); } + [Fact] + public static void Shuffle_Array_Covariance() + { + Random random = new Random(0x70636A61); + string[] items = ["", ""]; + object[] array = items; + random.Shuffle(array); + AssertExtensions.SequenceEqual((ReadOnlySpan)["", ""], items); + } + [Fact] public static void Shuffle_Array_ArgValidation() { From a6396867c276e61b48df4db498750f8f58f7de33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Sat, 17 Feb 2024 15:52:13 +0100 Subject: [PATCH 098/158] Fix missing closing parens in Microsoft.DotNet.ILCompiler.SingleEntry.targets (#98600) Oversight from https://github.com/dotnet/runtime/pull/98552 --- .../Microsoft.DotNet.ILCompiler.SingleEntry.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets index eae701ec006915..3a9c4d0153a7b1 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.DotNet.ILCompiler.SingleEntry.targets @@ -5,7 +5,7 @@ <_hostOS>$(NETCoreSdkPortableRuntimeIdentifier.SubString(0, $(NETCoreSdkPortableRuntimeIdentifier.LastIndexOf('-')))) <_originalTargetOS>$(RuntimeIdentifier.SubString(0, $(RuntimeIdentifier.LastIndexOf('-')))) - <_originalTargetOS Condition="$(_originalTargetOS.Contains('.'))">$(_originalTargetOS.SubString(0, $(_originalTargetOS.IndexOf('.'))) + <_originalTargetOS Condition="$(_originalTargetOS.Contains('.'))">$(_originalTargetOS.SubString(0, $(_originalTargetOS.IndexOf('.')))) <_originalTargetOS Condition="$(_originalTargetOS.StartsWith('win'))">win From a98d9f09bf9fca3177a8fe94d371d579c4de12b0 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Sat, 17 Feb 2024 16:13:15 +0100 Subject: [PATCH 099/158] Fix initial context for HW exceptions in the new EH (#98605) I have made a last minute change before creating the PR for the final fixes for the new EH that turned out to fire an assert in REGDISPLAY initialization. I was originally zeroing the whole context, but then figured out that just zeroing IP is sufficient to make the stack walker start at an explicit frame. However, there is one assert that also checks SP for validity (or 0) that started to fire with that change. This PR fixes it. --- src/coreclr/vm/exinfo.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/exinfo.cpp b/src/coreclr/vm/exinfo.cpp index 6bade35ec8963a..093f3436fb1b9d 100644 --- a/src/coreclr/vm/exinfo.cpp +++ b/src/coreclr/vm/exinfo.cpp @@ -333,8 +333,9 @@ ExInfo::ExInfo(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pEx if (exceptionKind == ExKind::HardwareFault) { // Hardware exception handling needs to start on the FaultingExceptionFrame, so we are - // passing in a context with zeroed out IP. + // passing in a context with zeroed out IP and SP. SetIP(&m_exContext, 0); + SetSP(&m_exContext, 0); m_exContext.ContextFlags = CONTEXT_FULL; } else From 9a391bcce1d53b90fca601cf417f49bfa68e6553 Mon Sep 17 00:00:00 2001 From: Theodore Tsirpanis Date: Sat, 17 Feb 2024 17:29:52 +0200 Subject: [PATCH 100/158] [mono] Do not throw in AssemblyExtensions.TryGetRawMetadata. (#96370) * [mono] Do not throw in AssemblyExtensions.TryGetRawMetadata. * Add a test for `AssemblyExtensions.TryGetRawMetadata`. * Try to read the assembly metadata blob in the test. --- .../tests/AssemblyExtensionsTest.cs | 32 +++++++++++++++++++ .../tests/System.Runtime.Loader.Tests.csproj | 2 ++ .../Reflection/Metadata/AssemblyExtensions.cs | 9 +++++- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs diff --git a/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs b/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs new file mode 100644 index 00000000000000..71a4cb434583d1 --- /dev/null +++ b/src/libraries/System.Runtime.Loader/tests/AssemblyExtensionsTest.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; +using System.Reflection.Metadata; +using System.Reflection; + +namespace System.Runtime.Loader.Tests +{ + public unsafe class AssemblyExtensionsTest + { + [Fact] + public void TryGetRawMetadata() + { + bool supportsRawMetadata = PlatformDetection.IsNotMonoRuntime && PlatformDetection.IsNotNativeAot; + + Assembly assembly = typeof(AssemblyExtensionsTest).Assembly; + bool hasMetadata = assembly.TryGetRawMetadata(out byte* blob, out int length); + + Assert.Equal(supportsRawMetadata, hasMetadata); + Assert.Equal(supportsRawMetadata, blob != null); + Assert.Equal(supportsRawMetadata, length > 0); + + if (supportsRawMetadata) + { + var metadataReader = new MetadataReader(blob, length); + string assemblyName = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name); + Assert.Equal(assembly.GetName().Name, assemblyName); + } + } + } +} diff --git a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj index 0ad5f15337a6e0..1874724006ee68 100644 --- a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj +++ b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj @@ -2,6 +2,7 @@ System.Runtime.Loader.Tests $(NetCoreAppCurrent) + true true true @@ -16,6 +17,7 @@ + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs index 9c677baa4bda0a..7eafc1a560964e 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.cs @@ -8,6 +8,13 @@ namespace System.Reflection.Metadata public static class AssemblyExtensions { [CLSCompliant(false)] - public static unsafe bool TryGetRawMetadata(this Assembly assembly, out byte* blob, out int length) => throw new NotImplementedException(); + public static unsafe bool TryGetRawMetadata(this Assembly assembly, out byte* blob, out int length) + { + ArgumentNullException.ThrowIfNull(assembly); + + blob = null; + length = 0; + return false; + } } } From 6d4fc1ad4725ca4cc77214b93bc1d37a6fa3553d Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 17 Feb 2024 17:02:29 +0100 Subject: [PATCH 101/158] [NativeAOT] Add null checks into memcpy/memset helpers (#98547) --- .../Runtime/CompilerHelpers/MemoryHelpers.cs | 33 +++++++++++++++++++ .../src/System.Private.CoreLib.csproj | 1 + .../ILCompiler.Compiler/Compiler/JitHelper.cs | 4 +-- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MemoryHelpers.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MemoryHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MemoryHelpers.cs new file mode 100644 index 00000000000000..644fcf1a59940e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MemoryHelpers.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; + +namespace Internal.Runtime.CompilerHelpers +{ + /// + /// These methods are used to implement memcpy and memset intrinsics with null checks. + /// + internal static class MemoryHelpers + { + private static unsafe void MemSet(ref byte dest, byte value, nuint size) + { + if (size > 0) + { + _ = dest; + SpanHelpers.Fill(ref dest, size, value); + } + } + + private static unsafe void MemCopy(ref byte dest, ref byte src, nuint size) + { + if (size > 0) + { + _ = dest; + _ = src; + Buffer.Memmove(ref dest, ref src, size); + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 4ca91458c70e71..d6e8c3a7e90274 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -100,6 +100,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs index 9e7285b751a3d3..c55dc58175b05f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -134,10 +134,10 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, break; case ReadyToRunHelper.MemCpy: - mangledName = "memcpy"; // TODO: Null reference handling + methodDesc = context.GetHelperEntryPoint("MemoryHelpers", "MemCopy"); break; case ReadyToRunHelper.MemSet: - mangledName = "memset"; // TODO: Null reference handling + methodDesc = context.GetHelperEntryPoint("MemoryHelpers", "MemSet"); break; case ReadyToRunHelper.GetRuntimeTypeHandle: From 1632e47b31be3c24229f00ca1ff0c84c50728df5 Mon Sep 17 00:00:00 2001 From: VincentWu <43398706+Xinlong-Wu@users.noreply.github.com> Date: Sun, 18 Feb 2024 03:53:25 +1100 Subject: [PATCH 102/158] [Mono][RISCV] Fix bugs so that pass more regression tests (#98602) * Revert "clean the code" This reverts commit 82793d7fe4fb6aae32cfafb4e4bce9f769653181. * skip test aliasing_retbuf * skip loopinfinally * skip loopinfinally * skip CdeclMemberFunctionTest * skip PlatformDefaultMemberFunctionTest * skip tests * fix atomic * fix atomic * fix OP_COMPARE * fix op_COMPARE * fix OP_ATOMIC_ADD * lowering OP_DIV_UN_IMM * lowering OP_IDIV_UN_IMM * fix OP_ATOMIC_ADD * fix ROUND Mode of R and F OP * process ArgOnStackR4 * support arglist * Fix handling of OP_IL_SEQ_POINT in mini-riscv.c * decompose OP_ICONV_TO_U4 as OP_ZEXT_I4 * Refactor division and remainder operations in mini-riscv.c * Update length of int_div and long_div instructions * update CI * process special case of OP_LCGT_UN rs1 -1 * decompose OP_ICONV_TO_OVF_U4 by sext * Refactor atomic unsigned load instructions in mini-riscv.c * Update RISC-V instructions and fix method-t-ir for multiplication * Add support for OP_CALL in mono_arch_lowering_pass * Fix unhandled op following OP_RCOMPARE and OP_FCOMPARE * Fix MAX_VIRTUAL_DELEGATE_OFFSET check in get_delegate_virtual_invoke_impl * Add workflow for syncing branch to daily build * Update start_handler length in cpu-riscv64.mdesc * Update scanCommit.yml * Update scanCommit.yml * Remove scanCommit.yml workflow * Update start_handler length in cpu-riscv64.mdesc * Add support for storing float arguments in integer registers for RISC-V architecture * Fix widening operation for RISC-V target * Add support for ArgHFA in mini-riscv.h * Update RISC-V instructions in mini-riscv.c and cpu-riscv64.mdesc * Fix RISC-V calling convention in mini-riscv.c * fix setting lmf in prolague * update test file * tmp * Revert "tmp" This reverts commit f0db3810624572f015008cd846bbaf2e4a1993b3. * clean pr * clang format file * format * Revert "clang format file" This reverts commit b8bc194cc2a3607f037a261fb61d0b1db565cfc4. * set cfa_reg * fix unwind * tmp * fix length of vcall2 * fix length of call * address comment * revert helpers.c * widen float type when r4 multiply by r8 --- src/mono/mono/mini/cpu-riscv64.mdesc | 47 +-- src/mono/mono/mini/method-to-ir.c | 5 + src/mono/mono/mini/mini-riscv.c | 567 +++++++++++++++++++++------ src/mono/mono/mini/mini-riscv.h | 12 +- src/tests/issues.targets | 76 +++- 5 files changed, 555 insertions(+), 152 deletions(-) diff --git a/src/mono/mono/mini/cpu-riscv64.mdesc b/src/mono/mono/mini/cpu-riscv64.mdesc index eba087a9b5901c..6adf5a6755d9d4 100644 --- a/src/mono/mono/mini/cpu-riscv64.mdesc +++ b/src/mono/mono/mini/cpu-riscv64.mdesc @@ -35,17 +35,18 @@ dummy_use: src1:i len:0 il_seq_point: len:0 seq_point: len:0 +arglist: src1:i len:12 check_this: src1:b len:4 get_ex_obj: dest:i len:4 gc_safe_point: src1:i len:12 clob:c -start_handler: len:16 clob:c +start_handler: len:36 clob:c call_handler: len:4 clob:c endfinally: len:32 endfilter: src1:i len:32 localloc: dest:i src1:i len:52 localloc_imm: dest:i len:28 generic_class_init: src1:a len:12 clob:c -ckfinite: dest:f src1:f len:28 +ckfinite: dest:f src1:f len:32 break: len:4 throw: src1:i len:4 @@ -54,21 +55,21 @@ rethrow: src1:i len:4 br: len:4 br_reg: src1:i len:4 jump_table: dest:i len:16 -call: dest:a len:4 clob:c -call_reg: dest:a src1:i len:4 clob:c -call_membase: dest:a src1:b len:20 clob:c -voidcall: len:4 clob:c -voidcall_reg: src1:i len:4 clob:c -voidcall_membase: src1:b len:20 clob:c -vcall2: len:16 clob:c +call: dest:a len:28 clob:c +call_reg: dest:a src1:i len:16 clob:c +call_membase: dest:a src1:b len:32 clob:c +voidcall: len:28 clob:c +voidcall_reg: src1:i len:16 clob:c +voidcall_membase: src1:b len:32 clob:c +vcall2: len:28 clob:c vcall2_reg: src1:i len:16 clob:c -vcall2_membase: src1:b len:28 clob:c -fcall: dest:f len:8 clob:c -fcall_reg: dest:f src1:i len:8 clob:c -fcall_membase: dest:f src1:b len:12 clob:c -rcall: dest:f len:8 clob:c -rcall_reg: dest:f src1:i len:8 clob:c -rcall_membase: dest:f src1:b len:12 clob:c +vcall2_membase: src1:b len:32 clob:c +fcall: dest:f len:28 clob:c +fcall_reg: dest:f src1:i len:16 clob:c +fcall_membase: dest:f src1:b len:32 clob:c +rcall: dest:f len:28 clob:c +rcall_reg: dest:f src1:i len:16 clob:c +rcall_membase: dest:f src1:b len:32 clob:c # Note: in RV32, it shoule be # lcall: dest:l ... @@ -101,8 +102,8 @@ loadr4_membase: dest:f src1:b len:24 loadr8_membase: dest:f src1:b len:24 memory_barrier: len:4 -atomic_add_i4: dest:i src1:i src2:i len:4 -atomic_add_i8: dest:i src1:i src2:i len:4 +atomic_add_i4: dest:i src1:i src2:i len:8 +atomic_add_i8: dest:i src1:i src2:i len:8 atomic_store_i1: dest:b src1:i len:8 atomic_store_u1: dest:b src1:i len:8 atomic_store_i2: dest:b src1:i len:8 @@ -113,7 +114,7 @@ atomic_store_i8: dest:b src1:i len:8 atomic_store_u8: dest:b src1:i len:8 atomic_load_i1: dest:b src1:i len:12 atomic_load_u1: dest:b src1:i len:12 -atomic_load_i2: dest:b src1:i len:12 +atomic_load_i2: dest:b src1:i len:24 atomic_load_u2: dest:b src1:i len:12 atomic_load_i4: dest:b src1:i len:12 atomic_load_u4: dest:b src1:i len:12 @@ -137,18 +138,18 @@ iconst: dest:i len:16 int_add: dest:i src1:i src2:i len:4 int_sub: dest:i src1:i src2:i len:4 int_mul: dest:i src1:i src2:i len:4 -int_div: dest:i src1:i src2:i len:32 +int_div: dest:i src1:i src2:i len:80 int_div_un: dest:i src1:i src2:i len:32 -int_rem: dest:i src1:i src2:i len:32 +int_rem: dest:i src1:i src2:i len:80 int_rem_un: dest:i src1:i src2:i len:32 i8const: dest:i len:16 long_add: dest:i src1:i src2:i len:4 long_sub: dest:i src1:i src2:i len:4 long_mul: dest:i src1:i src2:i len:4 -long_div: dest:i src1:i src2:i len:32 +long_div: dest:i src1:i src2:i len:80 long_div_un: dest:i src1:i src2:i len:32 -long_rem: dest:i src1:i src2:i len:32 +long_rem: dest:i src1:i src2:i len:80 long_rem_un: dest:i src1:i src2:i len:32 r8const: dest:f len:16 diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 913262e4e912e6..c696cb634566a8 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -8930,6 +8930,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b ins->sreg2 = sp [1]->dreg; type_from_op (cfg, ins, sp [0], sp [1]); CHECK_TYPE (ins); + + if (((sp [0]->type == STACK_R4 && sp [1]->type == STACK_R8) || + (sp [0]->type == STACK_R8 && sp [1]->type == STACK_R4))) + add_widen_op (cfg, ins, &sp [0], &sp [1]); + ins->dreg = alloc_dreg ((cfg), (MonoStackType)(ins)->type); /* Use the immediate opcodes if possible */ diff --git a/src/mono/mono/mini/mini-riscv.c b/src/mono/mono/mini/mini-riscv.c index afaa6838dd27f0..4f25347b37e673 100644 --- a/src/mono/mono/mini/mini-riscv.c +++ b/src/mono/mono/mini/mini-riscv.c @@ -9,6 +9,7 @@ #include "ir-emit.h" #include +#include #ifdef TARGET_RISCV64 #include "cpu-riscv64.h" @@ -157,9 +158,9 @@ get_delegate_invoke_impl (gboolean has_target, gboolean param_count, guint32 *co if (has_target) { start = code = mono_global_codeman_reserve (4 * 3); - code = mono_riscv_emit_load (code, RISCV_T0, RISCV_A0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), 0); + code = mono_riscv_emit_load (code, RISCV_T1, RISCV_A0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), 0); code = mono_riscv_emit_load (code, RISCV_A0, RISCV_A0, MONO_STRUCT_OFFSET (MonoDelegate, target), 0); - riscv_jalr (code, RISCV_ZERO, RISCV_T0, 0); + riscv_jalr (code, RISCV_ZERO, RISCV_T1, 0); g_assert ((code - start) <= 4 * 3); } else { @@ -168,12 +169,12 @@ get_delegate_invoke_impl (gboolean has_target, gboolean param_count, guint32 *co size = 8 + param_count * 4; start = code = mono_global_codeman_reserve (size); - code = mono_riscv_emit_load (code, RISCV_T0, RISCV_A0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), 0); + code = mono_riscv_emit_load (code, RISCV_T1, RISCV_A0, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), 0); /* slide down the arguments */ for (i = 0; i < param_count; ++i) riscv_addi (code, RISCV_A0 + i, RISCV_A0 + i + 1, 0); - riscv_jalr (code, RISCV_ZERO, RISCV_T0, 0); + riscv_jalr (code, RISCV_ZERO, RISCV_T1, 0); g_assert ((code - start) <= size); } @@ -196,8 +197,8 @@ get_delegate_virtual_invoke_impl (MonoTrampInfo **info, gboolean load_imt_reg, i GSList *unwind_ops; if (offset / (int)sizeof (target_mgreg_t) > MAX_VIRTUAL_DELEGATE_OFFSET) - NOT_IMPLEMENTED; - + return NULL; + MINI_BEGIN_CODEGEN (); start = code = mono_global_codeman_reserve (size); @@ -213,10 +214,10 @@ get_delegate_virtual_invoke_impl (MonoTrampInfo **info, gboolean load_imt_reg, i g_assert_not_reached (); /* Load this->vtable [offset] */ - code = mono_riscv_emit_load (code, RISCV_T0, RISCV_A0, MONO_STRUCT_OFFSET (MonoObject, vtable), 0); - code = mono_riscv_emit_load (code, RISCV_T0, RISCV_T0, offset, 0); + code = mono_riscv_emit_load (code, RISCV_T1, RISCV_A0, MONO_STRUCT_OFFSET (MonoObject, vtable), 0); + code = mono_riscv_emit_load (code, RISCV_T1, RISCV_T1, offset, 0); - riscv_jalr (code, RISCV_ZERO, RISCV_T0, 0); + riscv_jalr (code, RISCV_ZERO, RISCV_T1, 0); g_assert ((code - start) <= size); @@ -796,22 +797,129 @@ add_farg (CallInfo *cinfo, ArgInfo *ainfo, gboolean single) NOT_IMPLEMENTED; #endif } else { - ainfo->storage = single ? ArgOnStackR4 : ArgOnStackR8; - ainfo->slot_size = size; - ainfo->offset = cinfo->stack_usage; - cinfo->stack_usage += size; + // As ABI specifed, if there is ireg avaliable, store it into ireg + if (cinfo->next_arg <= RISCV_A7) { + ainfo->storage = single ? ArgR4InIReg : ArgR8InIReg; + ainfo->reg = cinfo->next_arg; + cinfo->next_arg++; + } else { + ainfo->storage = single ? ArgOnStackR4 : ArgOnStackR8; + ainfo->slot_size = size; + ainfo->offset = cinfo->stack_usage; + cinfo->stack_usage += size; + } + } +} + +static gboolean +is_hfa (MonoType *t, int *out_nfields, int *out_esize, int *field_offsets) +{ + MonoClass *klass; + gpointer iter; + MonoClassField *field; + MonoType *ftype, *prev_ftype = NULL; + int nfields = 0; + + klass = mono_class_from_mono_type_internal (t); + iter = NULL; + while ((field = mono_class_get_fields_internal (klass, &iter))) { + if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) + continue; + ftype = mono_field_get_type_internal (field); + ftype = mini_get_underlying_type (ftype); + + if (MONO_TYPE_ISSTRUCT (ftype)) { + int nested_nfields, nested_esize; + int nested_field_offsets [16]; + + MonoType *fixed_etype; + int fixed_len; + if (mono_marshal_shared_get_fixed_buffer_attr (field, &fixed_etype, &fixed_len)) { + if (fixed_etype->type != MONO_TYPE_R4 && fixed_etype->type != MONO_TYPE_R8) + return FALSE; + if (fixed_len > 16) + return FALSE; + nested_nfields = fixed_len; + nested_esize = fixed_etype->type == MONO_TYPE_R4 ? 4 : 8; + for (int i = 0; i < nested_nfields; ++i) + nested_field_offsets [i] = i * nested_esize; + } else { + if (!is_hfa (ftype, &nested_nfields, &nested_esize, nested_field_offsets)) + return FALSE; + } + + if (nested_esize == 4) + ftype = m_class_get_byval_arg (mono_defaults.single_class); + else + ftype = m_class_get_byval_arg (mono_defaults.double_class); + + if (prev_ftype && prev_ftype->type != ftype->type) + return FALSE; + prev_ftype = ftype; + for (int i = 0; i < nested_nfields; ++i) { + if (nfields + i < 4) + field_offsets [nfields + i] = + field->offset - MONO_ABI_SIZEOF (MonoObject) + nested_field_offsets [i]; + } + nfields += nested_nfields; + } else { + if (!(!m_type_is_byref (ftype) && (ftype->type == MONO_TYPE_R4 || ftype->type == MONO_TYPE_R8))) + return FALSE; + if (prev_ftype && prev_ftype->type != ftype->type) + return FALSE; + prev_ftype = ftype; + if (nfields < 4) + field_offsets [nfields] = field->offset - MONO_ABI_SIZEOF (MonoObject); + nfields++; + } } + if (nfields == 0 || nfields > 2) + return FALSE; + *out_nfields = nfields; + *out_esize = prev_ftype->type == MONO_TYPE_R4 ? 4 : 8; + return TRUE; } static void add_valuetype (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t) { - int size, aligned_size; + int size, aligned_size, nfields, esize; guint32 align; + int field_offsets [16]; size = mini_type_stack_size_full (t, &align, cinfo->pinvoke); aligned_size = ALIGN_TO (size, align); + if (is_hfa (t, &nfields, &esize, field_offsets)) { + /* + * The struct might include nested float structs aligned at 8, + * so need to keep track of the offsets of the individual fields. + */ + if (cinfo->next_farg + nfields - 1 <= RISCV_FA7) { + ainfo->storage = ArgHFA; + ainfo->reg = cinfo->next_farg; + ainfo->nregs = nfields; + ainfo->size = size; + ainfo->esize = esize; + for (int i = 0; i < nfields; ++i) + ainfo->foffsets [i] = GINT_TO_UINT8 (field_offsets [i]); + cinfo->next_farg += ainfo->nregs; + } else { + ainfo->nfregs_to_skip = cinfo->next_farg <= RISCV_FA7 ? RISCV_FA7 - cinfo->next_farg + 1 : 0; + cinfo->next_farg = RISCV_FA7 + 1; + + ainfo->offset = cinfo->stack_usage; + ainfo->storage = ArgVtypeOnStack; + cinfo->stack_usage += aligned_size; + ainfo->slot_size = aligned_size; + + ainfo->hfa = TRUE; + ainfo->nregs = nfields; + ainfo->esize = esize; + } + return; + } + // Scalars wider than 2×XLEN bits are passed by reference if (aligned_size > sizeof (host_mgreg_t) * 2) { if (cinfo->next_arg > RISCV_A7) { @@ -823,8 +931,8 @@ add_valuetype (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t) ainfo->storage = ArgVtypeByRef; ainfo->reg = cinfo->next_arg; ainfo->size = sizeof (host_mgreg_t); - ainfo->is_regpair = FALSE; - cinfo->next_arg += 1; + ainfo->nregs = 1; + cinfo->next_arg += ainfo->nregs; } } // Scalars that are 2×XLEN bits wide are passed in a pair of argument registers @@ -846,9 +954,8 @@ add_valuetype (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t) ainfo->reg = cinfo->next_arg; ainfo->size = sizeof (host_mgreg_t); - ainfo->is_regpair = FALSE; - - cinfo->next_arg += 1; + ainfo->nregs = 1; + cinfo->next_arg += ainfo->nregs; } // Scalars that are 2×XLEN bits wide are passed in a pair of argument // registers, with the low-order XLEN bits in the lower-numbered register @@ -857,9 +964,8 @@ add_valuetype (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t) ainfo->storage = ArgVtypeInIReg; ainfo->reg = cinfo->next_arg; ainfo->size = sizeof (host_mgreg_t) * 2; - ainfo->is_regpair = TRUE; - - cinfo->next_arg += 2; + ainfo->nregs = 2; + cinfo->next_arg += ainfo->nregs; } } // Scalars that are at most XLEN bits wide are passed in a single argument register @@ -873,9 +979,8 @@ add_valuetype (CallInfo *cinfo, ArgInfo *ainfo, MonoType *t) ainfo->storage = ArgVtypeInIReg; ainfo->reg = cinfo->next_arg; ainfo->size = sizeof (host_mgreg_t); - ainfo->is_regpair = FALSE; - - cinfo->next_arg += 1; + ainfo->nregs = 1; + cinfo->next_arg += ainfo->nregs; } } } @@ -1028,15 +1133,23 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) ArgInfo *ainfo = cinfo->args + sig->hasthis + pindex; // process the variable parameter sig->sentinelpos mark the first VARARG - if ((sig->call_convention == MONO_CALL_VARARG) && (pindex == sig->sentinelpos)) - NOT_IMPLEMENTED; + if ((sig->call_convention == MONO_CALL_VARARG) && (pindex == sig->sentinelpos)) { + cinfo->next_arg = RISCV_A7 + 1; + cinfo->next_farg = RISCV_FA7 + 1; + /* Emit the signature cookie just before the implicit arguments */ + add_param (cinfo, &cinfo->sig_cookie, mono_get_int_type ()); + } add_param (cinfo, ainfo, sig->params [pindex]); } /* Handle the case where there are no implicit arguments */ - if ((sig->call_convention == MONO_CALL_VARARG) && (pindex == sig->sentinelpos)) - NOT_IMPLEMENTED; + if ((sig->call_convention == MONO_CALL_VARARG) && (pindex == sig->sentinelpos)) { + cinfo->next_arg = RISCV_A7 + 1; + cinfo->next_farg = RISCV_FA7 + 1; + /* Emit the signature cookie just before the implicit arguments */ + add_param (cinfo, &cinfo->sig_cookie, mono_get_int_type ()); + } cinfo->stack_usage = ALIGN_TO (cinfo->stack_usage, MONO_ARCH_FRAME_ALIGNMENT); @@ -1059,6 +1172,7 @@ arg_get_storage (CallContext *ccontext, ArgInfo *ainfo) case ArgVtypeInIReg: return &ccontext->gregs [ainfo->reg]; case ArgInFReg: + case ArgHFA: return &ccontext->fregs [ainfo->reg]; case ArgOnStack: case ArgVtypeOnStack: @@ -1331,21 +1445,20 @@ mono_arch_is_inst_imm (int opcode, int imm_opcode, gint64 imm) gint static mono_arch_get_memory_ordering (int memory_barrier_kind) { - gint ordering; switch (memory_barrier_kind) { case MONO_MEMORY_BARRIER_ACQ: - ordering = RISCV_ORDER_AQ; + return RISCV_ORDER_AQ; break; case MONO_MEMORY_BARRIER_REL: - ordering = RISCV_ORDER_RL; + return RISCV_ORDER_RL; break; case MONO_MEMORY_BARRIER_SEQ: - ordering = RISCV_ORDER_ALL; + return RISCV_ORDER_ALL; + break; default: - ordering = RISCV_ORDER_NONE; + return RISCV_ORDER_NONE; break; } - return ordering; } GList * @@ -1489,6 +1602,24 @@ add_outarg_reg (MonoCompile *cfg, MonoCallInst *call, ArgStorage storage, int re MONO_ADD_INS (cfg->cbb, ins); mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, TRUE); break; + case ArgR4InIReg: + MONO_INST_NEW (cfg, ins, OP_MOVE_F_TO_I4); + ins->dreg = mono_alloc_ireg (cfg); + ins->sreg1 = arg->dreg; + MONO_ADD_INS (cfg->cbb, ins); + mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, FALSE); + break; + case ArgR8InIReg: +#ifdef TARGET_RISCV64 + MONO_INST_NEW (cfg, ins, OP_MOVE_F_TO_I8); + ins->dreg = mono_alloc_ireg (cfg); + ins->sreg1 = arg->dreg; + MONO_ADD_INS (cfg->cbb, ins); + mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, reg, FALSE); +#else + NOT_IMPLEMENTED; +#endif + break; } } @@ -1501,7 +1632,6 @@ add_outarg_reg (MonoCompile *cfg, MonoCallInst *call, ArgStorage storage, int re static void emit_sig_cookie (MonoCompile *cfg, MonoCallInst *call, CallInfo *cinfo) { - NOT_IMPLEMENTED; MonoMethodSignature *tmp_sig; int sig_reg; @@ -1549,6 +1679,7 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) /* Emit the inst of return at mono_arch_emit_setret() */ switch (cinfo->ret.storage) { case ArgVtypeInIReg: + case ArgHFA: if (MONO_IS_TAILCALL_OPCODE (call)) break; /* @@ -1612,6 +1743,8 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) case ArgInIReg: case ArgInFReg: case ArgInFRegR4: + case ArgR4InIReg: + case ArgR8InIReg: add_outarg_reg (cfg, call, ainfo->storage, ainfo->reg, arg); break; case ArgOnStack: { @@ -1644,6 +1777,9 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) } break; } + case ArgOnStackR4: + MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, RISCV_SP, ainfo->offset, arg->dreg); + break; case ArgOnStackR8: MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, RISCV_SP, ainfo->offset, arg->dreg); break; @@ -1651,7 +1787,8 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) case ArgVtypeByRef: case ArgVtypeOnStack: case ArgVtypeInMixed: - case ArgVtypeByRefOnStack: { + case ArgVtypeByRefOnStack: + case ArgHFA: { MonoInst *ins; guint32 align; guint32 size; @@ -1692,6 +1829,19 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src) if (ins->backend.size == 0) return; switch (ainfo->storage) { + case ArgHFA: + for (int i = 0; i < ainfo->nregs; ++i) { + if (ainfo->esize == 4) + MONO_INST_NEW (cfg, load, OP_LOADR4_MEMBASE); + else + MONO_INST_NEW (cfg, load, OP_LOADR8_MEMBASE); + load->dreg = mono_alloc_freg (cfg); + load->inst_basereg = src->dreg; + load->inst_offset = ainfo->foffsets [i]; + MONO_ADD_INS (cfg->cbb, load); + add_outarg_reg (cfg, call, ainfo->esize == 4 ? ArgInFRegR4 : ArgInFReg, ainfo->reg + i, load); + } + break; case ArgVtypeInIReg: MONO_INST_NEW (cfg, load, OP_LOAD_MEMBASE); load->dreg = mono_alloc_ireg (cfg); @@ -1873,7 +2023,6 @@ mono_arch_decompose_opts (MonoCompile *cfg, MonoInst *ins) case OP_ICONV_TO_I4: case OP_ICONV_TO_OVF_I4: case OP_ICONV_TO_OVF_I4_UN: - case OP_ICONV_TO_U4: case OP_ICONV_TO_I8: case OP_ICONV_TO_U8: case OP_LCONV_TO_U: @@ -1987,7 +2136,6 @@ mono_arch_decompose_opts (MonoCompile *cfg, MonoInst *ins) case OP_ICONV_TO_OVF_U2_UN: case OP_ICONV_TO_OVF_I8: case OP_ICONV_TO_OVF_I8_UN: - case OP_ICONV_TO_OVF_U4: case OP_ICONV_TO_OVF_U4_UN: case OP_ICONV_TO_OVF_U8: case OP_ICONV_TO_OVF_U8_UN: @@ -1995,6 +2143,17 @@ mono_arch_decompose_opts (MonoCompile *cfg, MonoInst *ins) case OP_ICONV_TO_OVF_U_UN: break; + + case OP_ICONV_TO_U4: + ins->opcode = OP_ZEXT_I4; + break; + case OP_ICONV_TO_OVF_U4: + MONO_EMIT_NEW_UNALU (cfg, OP_SEXT_I4, ins->dreg, ins->sreg1); + MONO_EMIT_NEW_ICOMPARE_IMM (cfg, ins->dreg, 0); + MONO_EMIT_NEW_COND_EXC (cfg, ILT, "OverflowException"); + MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, ins->dreg, ins->sreg1); + NULLIFY_INS (ins); + break; default: g_print ("Can't decompose the OP %s\n", mono_inst_name (ins->opcode)); NOT_IMPLEMENTED; @@ -2064,13 +2223,13 @@ mono_arch_allocate_vars (MonoCompile *cfg) cfg->ret->inst_c0 = cinfo->ret.reg; cfg->ret->dreg = cinfo->ret.reg; break; + case ArgHFA: case ArgVtypeInIReg: /* Allocate a local to hold the result, the epilog will copy it to the correct place */ cfg->ret->opcode = OP_REGOFFSET; cfg->ret->inst_basereg = cfg->frame_reg; - if (cinfo->ret.is_regpair) - offset += sizeof (host_mgreg_t); - offset += sizeof (host_mgreg_t); + g_assert (cinfo->ret.nregs > 0); + offset += cinfo->ret.nregs * sizeof (host_mgreg_t); cfg->ret->inst_offset = -offset; break; case ArgVtypeByRef: @@ -2108,23 +2267,27 @@ mono_arch_allocate_vars (MonoCompile *cfg) case ArgInIReg: case ArgInFReg: case ArgInFRegR4: + case ArgR4InIReg: + case ArgR8InIReg: offset += sizeof (host_mgreg_t); ins->inst_offset = -offset; break; case ArgOnStack: + case ArgOnStackR4: + case ArgOnStackR8: case ArgVtypeOnStack: /* These are in the parent frame */ g_assert (ainfo->offset >= 0); ins->inst_basereg = RISCV_FP; ins->inst_offset = ainfo->offset; break; + case ArgHFA: case ArgVtypeInIReg: ins->opcode = OP_REGOFFSET; ins->inst_basereg = cfg->frame_reg; /* These arguments are saved to the stack in the prolog */ - if (ainfo->is_regpair) - offset += sizeof (host_mgreg_t); - offset += sizeof (host_mgreg_t); + g_assert (ainfo->nregs > 0); + offset += ainfo->nregs * sizeof (host_mgreg_t); ins->inst_offset = -offset; break; case ArgVtypeInMixed: @@ -2275,6 +2438,7 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb) { loop_start: switch (ins->opcode) { + case OP_ARGLIST: case OP_CKFINITE: case OP_BREAK: case OP_IL_SEQ_POINT: @@ -2636,11 +2800,14 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb) next_ins->sreg1 = ins->dreg; next_ins->sreg2 = RISCV_ZERO; } else { - g_print ("Unhandaled op %s following after OP_RCOMPARE\n", mono_inst_name (next_ins->opcode)); - NOT_IMPLEMENTED; + if (cfg->verbose_level > 1) { + g_print ("Unhandaled op %s following after OP_RCOMPARE\n", mono_inst_name (next_ins->opcode)); + } + NULLIFY_INS (ins); } } else { - g_assert_not_reached (); + NULLIFY_INS (ins); + // g_assert_not_reached (); } break; } @@ -2716,14 +2883,15 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb) next_ins->opcode = OP_RISCV_BNE; next_ins->sreg1 = ins->dreg; next_ins->sreg2 = RISCV_ZERO; - } else if (next_ins->opcode == OP_BR) { - NULLIFY_INS (ins); } else { - g_print ("Unhandaled op %s following after OP_FCOMPARE\n", mono_inst_name (next_ins->opcode)); - NOT_IMPLEMENTED; + if (cfg->verbose_level > 1) { + g_print ("Unhandaled op %s following after OP_FCOMPARE\n", mono_inst_name (next_ins->opcode)); + } + NULLIFY_INS (ins); } } else { - g_assert_not_reached (); + NULLIFY_INS (ins); + // g_assert_not_reached (); } break; } @@ -2924,7 +3092,7 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb) break; } } else if (next_ins->opcode == OP_LCGT_UN || next_ins->opcode == OP_ICGT_UN) { - if (RISCV_VALID_I_IMM (ins->inst_imm + 1)) { + if ((ins->inst_imm != -1) && RISCV_VALID_I_IMM (ins->inst_imm + 1)) { // compare rs1, imm; lcgt_un rd => sltiu rd, rs1, imm; xori rd, rd, 1 ins->opcode = OP_RISCV_SLTIU; ins->dreg = next_ins->dreg; @@ -2936,6 +3104,14 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb) next_ins->sreg1 = ins->dreg; next_ins->inst_imm = 1; break; + } else if ((ins->inst_imm == -1)) { + // rs1 will never greater than -1 + next_ins->opcode = OP_ADD_IMM; + next_ins->sreg1 = RISCV_ZERO; + next_ins->inst_imm = 0; + + NULLIFY_INS (ins); + break; } } else if (next_ins->opcode == OP_LCGT || next_ins->opcode == OP_ICGT) { if (RISCV_VALID_I_IMM (ins->inst_imm + 1)) { @@ -3116,28 +3292,24 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb) next_ins->sreg1 = ins->sreg2; next_ins->sreg2 = ins->sreg1; NULLIFY_INS (ins); - } else if (next_ins->opcode == OP_IL_SEQ_POINT || next_ins->opcode == OP_MOVE || - next_ins->opcode == OP_LOAD_MEMBASE || next_ins->opcode == OP_NOP || - next_ins->opcode == OP_LOADI4_MEMBASE || next_ins->opcode == OP_BR || - next_ins->opcode == OP_LOADI8_MEMBASE || next_ins->opcode == OP_ICONST || - next_ins->opcode == OP_I8CONST || next_ins->opcode == OP_ADD_IMM) { + } else { /** * there is compare without branch OP followed * * icompare_imm R226 - * il_seq_point il: 0xc6 + * call * * what should I do? */ + if (cfg->verbose_level > 1) { + g_print ("Unhandaled op %s following after OP_{I|L}COMPARE{|_IMM}\n", + mono_inst_name (next_ins->opcode)); + } NULLIFY_INS (ins); break; - } else { - g_print ("Unhandaled op %s following after OP_{I|L}COMPARE{|_IMM}\n", - mono_inst_name (next_ins->opcode)); - NOT_IMPLEMENTED; } } else - g_assert_not_reached (); + NULLIFY_INS (ins); break; } @@ -3291,6 +3463,19 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb) g_assert (mono_op_imm_to_op (ins->opcode) == OP_LDIV); #else g_assert (mono_op_imm_to_op (ins->opcode) == OP_IDIV); +#endif + ins->opcode = mono_op_imm_to_op (ins->opcode); + ins->sreg2 = temp->dreg; + break; + case OP_DIV_UN_IMM: + NEW_INS_BEFORE (cfg, ins, temp, OP_I8CONST); + temp->inst_l = ins->inst_imm; + temp->dreg = mono_alloc_ireg (cfg); + +#ifdef TARGET_RISCV64 + g_assert (mono_op_imm_to_op (ins->opcode) == OP_LDIV_UN); +#else + g_assert (mono_op_imm_to_op (ins->opcode) == OP_IDIV_UN); #endif ins->opcode = mono_op_imm_to_op (ins->opcode); ins->sreg2 = temp->dreg; @@ -3298,6 +3483,7 @@ mono_arch_lowering_pass (MonoCompile *cfg, MonoBasicBlock *bb) case OP_IMUL_IMM: case OP_LMUL_IMM: case OP_IDIV_IMM: + case OP_IDIV_UN_IMM: case OP_IREM_IMM: case OP_LREM_IMM: case OP_IREM_UN_IMM: @@ -3553,6 +3739,7 @@ guint8 * mono_riscv_emit_load (guint8 *code, int rd, int rs1, target_mgreg_t imm, int length) { if (!RISCV_VALID_I_IMM (imm)) { + g_assert (rs1 != RISCV_T0); code = mono_riscv_emit_imm (code, RISCV_T0, imm); riscv_add (code, RISCV_T0, rs1, RISCV_T0); rs1 = RISCV_T0; @@ -3593,6 +3780,7 @@ guint8 * mono_riscv_emit_loadu (guint8 *code, int rd, int rs1, target_mgreg_t imm, int length) { if (!RISCV_VALID_I_IMM (imm)) { + g_assert (rs1 != RISCV_T0); code = mono_riscv_emit_imm (code, RISCV_T0, imm); riscv_add (code, RISCV_T0, rs1, RISCV_T0); rs1 = RISCV_T0; @@ -3624,6 +3812,7 @@ mono_riscv_emit_fload (guint8 *code, int rd, int rs1, target_mgreg_t imm, gboole { g_assert (riscv_stdext_d || (isSingle && riscv_stdext_f)); if (!RISCV_VALID_I_IMM (imm)) { + g_assert (rs1 != RISCV_T0); code = mono_riscv_emit_imm (code, RISCV_T0, imm); riscv_add (code, RISCV_T0, rs1, RISCV_T0); rs1 = RISCV_T0; @@ -3644,6 +3833,7 @@ guint8 * mono_riscv_emit_store (guint8 *code, int rs2, int rs1, target_mgreg_t imm, int length) { if (!RISCV_VALID_S_IMM (imm)) { + g_assert (rs1 != RISCV_T0 && rs2 != RISCV_T0); code = mono_riscv_emit_imm (code, RISCV_T0, imm); riscv_add (code, RISCV_T0, rs1, RISCV_T0); rs1 = RISCV_T0; @@ -3685,6 +3875,7 @@ mono_riscv_emit_fstore (guint8 *code, int rs2, int rs1, target_mgreg_t imm, gboo { g_assert (riscv_stdext_d || (isSingle && riscv_stdext_f)); if (!RISCV_VALID_I_IMM (imm)) { + g_assert (rs1 != RISCV_T0); code = mono_riscv_emit_imm (code, RISCV_T0, imm); riscv_add (code, RISCV_T0, rs1, RISCV_T0); rs1 = RISCV_T0; @@ -3879,12 +4070,14 @@ emit_setup_lmf (MonoCompile *cfg, guint8 *code, gint32 lmf_offset) * need to be restored during EH. */ g_assert (lmf_offset <= 0); - /* pc */ - code = mono_riscv_emit_store (code, RISCV_RA, RISCV_FP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMF, pc), 0); /* callee saved gregs + sp */ code = emit_store_regarray_cfa (cfg, code, MONO_ARCH_LMF_REGS, RISCV_FP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMF, gregs), (1 << RISCV_SP | 1 << RISCV_FP)); + /* pc */ + riscv_auipc (code, RISCV_RA, 0); + code = mono_riscv_emit_store (code, RISCV_RA, RISCV_FP, lmf_offset + MONO_STRUCT_OFFSET (MonoLMF, pc), 0); + return code; } @@ -3926,7 +4119,8 @@ emit_move_args (MonoCompile *cfg, guint8 *code) switch (ainfo->storage) { case ArgInIReg: - g_assert (ainfo->is_regpair == FALSE); + case ArgR4InIReg: + case ArgR8InIReg: code = mono_riscv_emit_store (code, ainfo->reg, ins->inst_basereg, ins->inst_offset, 0); if (i == 0 && sig->hasthis) { mono_add_var_location (cfg, ins, TRUE, ainfo->reg, 0, 0, code - cfg->native_code); @@ -3940,14 +4134,15 @@ emit_move_args (MonoCompile *cfg, guint8 *code) ainfo->storage == ArgInFRegR4); break; case ArgVtypeInIReg: - if (ainfo->is_regpair) + g_assert (ainfo->nregs <= 2); + if (ainfo->nregs == 2) code = mono_riscv_emit_store (code, ainfo->reg + 1, ins->inst_basereg, ins->inst_offset + sizeof (host_mgreg_t), 0); code = mono_riscv_emit_store (code, ainfo->reg, ins->inst_basereg, ins->inst_offset, 0); break; case ArgVtypeInMixed: - code = mono_riscv_emit_load (code, RISCV_T0, RISCV_S0, 0, 0); - code = mono_riscv_emit_store (code, RISCV_T0, ins->inst_basereg, + code = mono_riscv_emit_load (code, RISCV_T1, RISCV_S0, 0, 0); + code = mono_riscv_emit_store (code, RISCV_T1, ins->inst_basereg, ins->inst_offset + sizeof (host_mgreg_t), 0); code = mono_riscv_emit_store (code, ainfo->reg, ins->inst_basereg, ins->inst_offset, 0); break; @@ -3962,7 +4157,20 @@ emit_move_args (MonoCompile *cfg, guint8 *code) ins->inst_left->inst_offset, 0); // } break; + case ArgHFA: + for (int part = 0; part < ainfo->nregs; part++) { + if (ainfo->esize == 4) + code = mono_riscv_emit_fstore (code, ainfo->reg + part, ins->inst_basereg, + GTMREG_TO_INT (ins->inst_offset + ainfo->foffsets [part]), TRUE); + else + code = + mono_riscv_emit_fstore (code, ainfo->reg + part, ins->inst_basereg, + GTMREG_TO_INT (ins->inst_offset + ainfo->foffsets [part]), FALSE); + } + break; case ArgOnStack: + case ArgOnStackR4: + case ArgOnStackR8: case ArgVtypeOnStack: case ArgVtypeByRefOnStack: break; @@ -4013,10 +4221,26 @@ emit_move_return_value (MonoCompile *cfg, guint8 *code, MonoInst *ins) /* Load the destination address */ g_assert (loc && loc->opcode == OP_REGOFFSET); - code = mono_riscv_emit_load (code, RISCV_T0, loc->inst_basereg, loc->inst_offset, 0); - code = mono_riscv_emit_store (code, cinfo->ret.reg, RISCV_T0, 0, 0); - if (cinfo->ret.is_regpair) { - code = mono_riscv_emit_store (code, cinfo->ret.reg + 1, RISCV_T0, sizeof (host_mgreg_t), 0); + code = mono_riscv_emit_load (code, RISCV_T1, loc->inst_basereg, loc->inst_offset, 0); + code = mono_riscv_emit_store (code, cinfo->ret.reg, RISCV_T1, 0, 0); + g_assert (cinfo->ret.nregs <= 2); + if (cinfo->ret.nregs == 2) { + code = mono_riscv_emit_store (code, cinfo->ret.reg + 1, RISCV_T1, sizeof (host_mgreg_t), 0); + } + break; + } + case ArgHFA: { + MonoInst *loc = cfg->arch.vret_addr_loc; + int i; + + /* Load the destination address */ + g_assert (loc && loc->opcode == OP_REGOFFSET); + code = mono_riscv_emit_load (code, RISCV_T1, loc->inst_basereg, GTMREG_TO_INT (loc->inst_offset), 0); + for (i = 0; i < cinfo->ret.nregs; ++i) { + if (cinfo->ret.esize == 4) + code = mono_riscv_emit_fstore (code, cinfo->ret.reg + i, RISCV_T1, cinfo->ret.foffsets [i], TRUE); + else + code = mono_riscv_emit_fstore (code, cinfo->ret.reg + i, RISCV_T1, cinfo->ret.foffsets [i], FALSE); } break; } @@ -4103,6 +4327,7 @@ mono_arch_emit_prolog (MonoCompile *cfg) * - Setup frame */ int stack_size = 0; + mono_emit_unwind_op_def_cfa (cfg, code, RISCV_SP, 0); /* Setup frame */ if (RISCV_VALID_I_IMM (-cfg->stack_offset)) { @@ -4125,7 +4350,7 @@ mono_arch_emit_prolog (MonoCompile *cfg) code = mono_riscv_emit_imm (code, RISCV_T0, cfg->stack_offset); // calculate SP riscv_sub (code, RISCV_SP, RISCV_SP, RISCV_T0); - mono_emit_unwind_op_def_cfa (cfg, code, RISCV_SP, cfg->stack_offset); + mono_emit_unwind_op_def_cfa_offset (cfg, code, cfg->stack_offset); // save return value stack_size += sizeof (target_mgreg_t); @@ -4192,15 +4417,15 @@ mono_arch_emit_prolog (MonoCompile *cfg) ins = cfg->arch.ss_tramp_var; g_assert (ins->opcode == OP_REGOFFSET); - code = mono_riscv_emit_imm (code, RISCV_T0, (guint64)&ss_trampoline); - code = mono_riscv_emit_store (code, RISCV_T0, ins->inst_basereg, ins->inst_offset, 0); + code = mono_riscv_emit_imm (code, RISCV_T1, (guint64)&ss_trampoline); + code = mono_riscv_emit_store (code, RISCV_T1, ins->inst_basereg, ins->inst_offset, 0); } if (cfg->arch.bp_tramp_var) { /* Initialize bp_tramp_var */ ins = cfg->arch.bp_tramp_var; g_assert (ins->opcode == OP_REGOFFSET); - code = mono_riscv_emit_imm (code, RISCV_T0, (guint64)bp_trampoline); - code = mono_riscv_emit_store (code, RISCV_T0, ins->inst_basereg, ins->inst_offset, 0); + code = mono_riscv_emit_imm (code, RISCV_T1, (guint64)bp_trampoline); + code = mono_riscv_emit_store (code, RISCV_T1, ins->inst_basereg, ins->inst_offset, 0); } } @@ -4239,12 +4464,26 @@ mono_arch_emit_epilog (MonoCompile *cfg) case ArgVtypeInIReg: { MonoInst *ins = cfg->ret; - if (cinfo->ret.is_regpair) + g_assert (cinfo->ret.nregs <= 2); + if (cinfo->ret.nregs == 2) code = mono_riscv_emit_load (code, cinfo->ret.reg + 1, ins->inst_basereg, ins->inst_offset + sizeof (host_mgreg_t), 0); code = mono_riscv_emit_load (code, cinfo->ret.reg, ins->inst_basereg, ins->inst_offset, 0); break; } + case ArgHFA: { + MonoInst *ins = cfg->ret; + + for (int i = 0; i < cinfo->ret.nregs; ++i) { + if (cinfo->ret.esize == 4) + code = mono_riscv_emit_fload (code, cinfo->ret.reg + i, ins->inst_basereg, + GTMREG_TO_INT (ins->inst_offset + cinfo->ret.foffsets [i]), TRUE); + else + code = mono_riscv_emit_fload (code, cinfo->ret.reg + i, ins->inst_basereg, + GTMREG_TO_INT (ins->inst_offset + cinfo->ret.foffsets [i]), FALSE); + } + break; + } default: g_print ("Unable process returned storage %d(0x%x)\n", cinfo->ret.storage, cinfo->ret.storage); NOT_IMPLEMENTED; @@ -4317,13 +4556,13 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) g_assert (var->opcode == OP_REGOFFSET); /* Load ss_tramp_var */ /* This is equal to &ss_trampoline */ - code = mono_riscv_emit_load (code, RISCV_T0, var->inst_basereg, var->inst_offset, 0); + code = mono_riscv_emit_load (code, RISCV_T1, var->inst_basereg, var->inst_offset, 0); /* Load the trampoline address */ - code = mono_riscv_emit_load (code, RISCV_T0, RISCV_T0, 0, 0); + code = mono_riscv_emit_load (code, RISCV_T1, RISCV_T1, 0, 0); /* Call it if it is non-null */ // In riscv, we use jalr to jump - riscv_beq (code, RISCV_ZERO, RISCV_T0, 8); - riscv_jalr (code, RISCV_ZERO, RISCV_T0, 0); + riscv_beq (code, RISCV_ZERO, RISCV_T1, 8); + riscv_jalr (code, RISCV_ZERO, RISCV_T1, 0); } mono_add_seq_point (cfg, bb, ins, code - cfg->native_code); @@ -4333,8 +4572,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) MonoInst *var = cfg->arch.bp_tramp_var; g_assert (var); g_assert (var->opcode == OP_REGOFFSET); - /* Load the address of the bp trampoline into IP0 */ - code = mono_riscv_emit_load (code, RISCV_T0, var->inst_basereg, var->inst_offset, 0); + /* Load the address of the bp trampoline into T1 */ + code = mono_riscv_emit_load (code, RISCV_T1, var->inst_basereg, var->inst_offset, 0); /* * A placeholder for a possible breakpoint inserted by * mono_arch_set_breakpoint (). @@ -4363,9 +4602,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) riscv_addi (code, RISCV_T0, RISCV_SP, 0); loop_start = code; riscv_beq (code, RISCV_T0, RISCV_T1, 0); - code = mono_riscv_emit_store (code, RISCV_ZERO, RISCV_T0, 0, 0); - code = mono_riscv_emit_store (code, RISCV_ZERO, RISCV_T0, sizeof (host_mgreg_t), 0); + riscv_sd (code, RISCV_ZERO, RISCV_T0, 0); + riscv_sd (code, RISCV_ZERO, RISCV_T0, sizeof (host_mgreg_t)); #ifdef TARGET_RISCV32 + NOT_IMPLEMENTED; code = mono_riscv_emit_store (code, RISCV_ZERO, RISCV_T0, sizeof (host_mgreg_t) * 2, 0); code = mono_riscv_emit_store (code, RISCV_ZERO, RISCV_T0, sizeof (host_mgreg_t) * 3, 0); #endif @@ -4417,9 +4657,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) byte_offset = MONO_STRUCT_OFFSET (MonoVTable, initialized); /* Load vtable->initialized */ - code = mono_riscv_emit_load (code, RISCV_T0, ins->sreg1, byte_offset, 1); + code = mono_riscv_emit_load (code, RISCV_T1, ins->sreg1, byte_offset, 1); branch_label = code; - riscv_bne (code, RISCV_ZERO, RISCV_T0, 0); + riscv_bne (code, RISCV_ZERO, RISCV_T1, 0); /* Slowpath */ g_assert (ins->sreg1 == RISCV_A0); @@ -4440,6 +4680,12 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) riscv_andi (code, RISCV_T0, RISCV_T0, ~(RISCV_FCLASS_INF | RISCV_FCLASS_NAN)); code = mono_riscv_emit_branch_exc (cfg, code, OP_RISCV_EXC_BEQ, RISCV_T0, RISCV_ZERO, "ArithmeticException"); + if (ins->dreg != ins->sreg1) { + if (riscv_stdext_d) + riscv_fsgnj_d (code, ins->dreg, ins->sreg1, ins->sreg1); + else + riscv_fsgnj_s (code, ins->dreg, ins->sreg1, ins->sreg1); + } } case OP_BREAK: /* @@ -4450,6 +4696,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) code = mono_riscv_emit_call (cfg, code, MONO_PATCH_INFO_JIT_ICALL_ID, GUINT_TO_POINTER (MONO_JIT_ICALL_mono_break)); break; + case OP_ARGLIST: + g_assert (cfg->arch.cinfo); + riscv_addi (code, RISCV_T1, RISCV_FP, cfg->arch.cinfo->sig_cookie.offset); + code = mono_riscv_emit_store (code, RISCV_T1, ins->sreg1, 0, 0); + break; case OP_NOP: case OP_RELAXED_NOP: @@ -4635,40 +4886,77 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) riscv_fdiv_s (code, RISCV_ROUND_DY, ins->dreg, ins->sreg1, ins->sreg2); } break; - case OP_IDIV: - case OP_LDIV: + case OP_IREM: + case OP_IDIV: { g_assert (riscv_stdext_m); + /* Check for zero */ code = mono_riscv_emit_branch_exc (cfg, code, OP_RISCV_EXC_BEQ, ins->sreg2, RISCV_ZERO, "DivideByZeroException"); - riscv_div (code, ins->dreg, ins->sreg1, ins->sreg2); - break; - case OP_IDIV_UN: + /* Check for INT64_MIN/-1 */ #ifdef TARGET_RISCV64 - g_assert (riscv_stdext_m); - code = mono_riscv_emit_branch_exc (cfg, code, OP_RISCV_EXC_BEQ, ins->sreg2, RISCV_ZERO, - "DivideByZeroException"); - riscv_divuw (code, ins->dreg, ins->sreg1, ins->sreg2); - break; + code = mono_riscv_emit_imm (code, RISCV_T0, 0xffffffff80000000); +#else + code = mono_riscv_emit_imm (code, RISCV_T0, 0x80000000); #endif - case OP_LDIV_UN: + // compare t0, rs1; ceq rd => xor t0, t0, rs1; sltiu t0, t0, 1 + riscv_xor (code, RISCV_T0, RISCV_T0, ins->sreg1); + riscv_sltiu (code, RISCV_T1, RISCV_T0, 1); +#ifdef TARGET_RISCV64 + code = mono_riscv_emit_imm (code, RISCV_T0, 0xffffffffffffffff); +#else + code = mono_riscv_emit_imm (code, RISCV_T0, 0xffffffff); +#endif + riscv_xor (code, RISCV_T0, RISCV_T0, ins->sreg2); + riscv_sltiu (code, RISCV_T0, RISCV_T0, 1); + riscv_and (code, RISCV_T0, RISCV_T0, RISCV_T1); + riscv_addi (code, RISCV_T0, RISCV_T0, -1); + code = mono_riscv_emit_branch_exc (cfg, code, OP_RISCV_EXC_BEQ, RISCV_T0, RISCV_ZERO, "OverflowException"); + if (ins->opcode == OP_IREM) +#ifdef TARGET_RISCV64 + riscv_remw (code, ins->dreg, ins->sreg1, ins->sreg2); +#else + riscv_rem (code, ins->dreg, ins->sreg1, ins->sreg2); +#endif + else + riscv_div (code, ins->dreg, ins->sreg1, ins->sreg2); + break; + } + case OP_LREM: + case OP_LDIV: { g_assert (riscv_stdext_m); + /* Check for zero */ code = mono_riscv_emit_branch_exc (cfg, code, OP_RISCV_EXC_BEQ, ins->sreg2, RISCV_ZERO, "DivideByZeroException"); - riscv_divu (code, ins->dreg, ins->sreg1, ins->sreg2); + /* Check for INT64_MIN/-1 */ + code = mono_riscv_emit_imm (code, RISCV_T0, 0x8000000000000000); + // compare t0, rs1; ceq rd => xor t0, t0, rs1; sltiu t0, t0, 1 + riscv_xor (code, RISCV_T0, RISCV_T0, ins->sreg1); + riscv_sltiu (code, RISCV_T1, RISCV_T0, 1); + code = mono_riscv_emit_imm (code, RISCV_T0, 0xffffffffffffffff); + riscv_xor (code, RISCV_T0, RISCV_T0, ins->sreg2); + riscv_sltiu (code, RISCV_T0, RISCV_T0, 1); + riscv_and (code, RISCV_T0, RISCV_T0, RISCV_T1); + riscv_addi (code, RISCV_T0, RISCV_T0, -1); + code = mono_riscv_emit_branch_exc (cfg, code, OP_RISCV_EXC_BEQ, RISCV_T0, RISCV_ZERO, "OverflowException"); + if (ins->opcode == OP_LREM) + riscv_rem (code, ins->dreg, ins->sreg1, ins->sreg2); + else + riscv_div (code, ins->dreg, ins->sreg1, ins->sreg2); break; - case OP_IREM: + } + case OP_IDIV_UN: #ifdef TARGET_RISCV64 g_assert (riscv_stdext_m); code = mono_riscv_emit_branch_exc (cfg, code, OP_RISCV_EXC_BEQ, ins->sreg2, RISCV_ZERO, "DivideByZeroException"); - riscv_remw (code, ins->dreg, ins->sreg1, ins->sreg2); + riscv_divuw (code, ins->dreg, ins->sreg1, ins->sreg2); break; #endif - case OP_LREM: + case OP_LDIV_UN: g_assert (riscv_stdext_m); code = mono_riscv_emit_branch_exc (cfg, code, OP_RISCV_EXC_BEQ, ins->sreg2, RISCV_ZERO, "DivideByZeroException"); - riscv_rem (code, ins->dreg, ins->sreg1, ins->sreg2); + riscv_divu (code, ins->dreg, ins->sreg1, ins->sreg2); break; case OP_IREM_UN: #ifdef TARGET_RISCV64 @@ -4787,40 +5075,67 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) /* Atomic */ case OP_MEMORY_BARRIER: - riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); - break; + riscv_fence (code, mono_arch_get_memory_ordering (ins->backend.memory_barrier_kind), + mono_arch_get_memory_ordering (ins->backend.memory_barrier_kind)); + break; + /** + * OP_ATOMIC_ADD_I4 rd, rs1, rs2 + * this instriuction increase the value of address rs1 by rs2 + * and store the **new** value to rd. + * But in RISC-V amoadd rd, rs2(rs1) increase the value of address rs1 by rs2 + * and store the **old** value to rd, store the result value to address rs1. + * So we need more add rd, rd, rs2 to fix the rd as the **new** value. + */ case OP_ATOMIC_ADD_I4: - riscv_amoadd_w (code, RISCV_ORDER_ALL, ins->dreg, ins->sreg2, ins->sreg1); + riscv_amoadd_w (code, RISCV_ORDER_ALL, RISCV_T0, ins->sreg2, ins->sreg1); + riscv_addw (code, ins->dreg, RISCV_T0, ins->sreg2); break; case OP_ATOMIC_ADD_I8: - riscv_amoadd_d (code, RISCV_ORDER_ALL, ins->dreg, ins->sreg2, ins->sreg1); + riscv_amoadd_d (code, RISCV_ORDER_ALL, RISCV_T0, ins->sreg2, ins->sreg1); + riscv_add (code, ins->dreg, RISCV_T0, ins->sreg2); break; - case OP_ATOMIC_LOAD_I1: - case OP_ATOMIC_LOAD_U1: { + case OP_ATOMIC_LOAD_I1: { riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); code = mono_riscv_emit_load (code, ins->dreg, ins->sreg1, ins->inst_offset, 1); riscv_fence (code, RISCV_FENCE_R, RISCV_FENCE_MEM); break; } - case OP_ATOMIC_LOAD_U2: + case OP_ATOMIC_LOAD_U1: { + riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); + code = mono_riscv_emit_loadu (code, ins->dreg, ins->sreg1, ins->inst_offset, 1); + riscv_fence (code, RISCV_FENCE_R, RISCV_FENCE_MEM); + break; + } + case OP_ATOMIC_LOAD_U2: { + riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); + code = mono_riscv_emit_loadu (code, ins->dreg, ins->sreg1, ins->inst_offset, 2); + riscv_fence (code, RISCV_FENCE_R, RISCV_FENCE_MEM); + break; + } case OP_ATOMIC_LOAD_I2: { riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); code = mono_riscv_emit_load (code, ins->dreg, ins->sreg1, ins->inst_offset, 2); riscv_fence (code, RISCV_FENCE_R, RISCV_FENCE_MEM); + break; } - case OP_ATOMIC_LOAD_I4: - case OP_ATOMIC_LOAD_U4: { + case OP_ATOMIC_LOAD_I4: { riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); code = mono_riscv_emit_load (code, ins->dreg, ins->sreg1, ins->inst_offset, 4); riscv_fence (code, RISCV_FENCE_R, RISCV_FENCE_MEM); break; } + case OP_ATOMIC_LOAD_U4: { + riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); + code = mono_riscv_emit_loadu (code, ins->dreg, ins->sreg1, ins->inst_offset, 4); + riscv_fence (code, RISCV_FENCE_R, RISCV_FENCE_MEM); + break; + } case OP_ATOMIC_STORE_I1: case OP_ATOMIC_STORE_U1: { riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_W); code = mono_riscv_emit_store (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset, 1); if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ) - riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); + riscv_fence (code, RISCV_FENCE_ALL, RISCV_FENCE_ALL); break; } case OP_ATOMIC_STORE_I2: @@ -4828,7 +5143,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_W); code = mono_riscv_emit_store (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset, 2); if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ) - riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); + riscv_fence (code, RISCV_FENCE_ALL, RISCV_FENCE_ALL); break; } case OP_ATOMIC_STORE_I4: @@ -4836,14 +5151,14 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_W); code = mono_riscv_emit_store (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset, 4); if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ) - riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); + riscv_fence (code, RISCV_FENCE_ALL, RISCV_FENCE_ALL); break; } case OP_ATOMIC_CAS_I4: { g_assert (riscv_stdext_a); /** * loop_start: - * lr.w t0, rs1 + * lr.w.aqrl t0, rs1 * bne t0, rs3, loop_end * sc.w.rl t1, rs2, rs1 * bnez t1, loop_start @@ -4855,7 +5170,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) guint8 *loop_start, *branch_label; /* sreg2 is the value, sreg3 is the comparand */ loop_start = code; - riscv_lr_w (code, RISCV_ORDER_NONE, RISCV_T0, ins->sreg1); + riscv_lr_w (code, RISCV_ORDER_ALL, RISCV_T0, ins->sreg1); branch_label = code; riscv_bne (code, RISCV_T0, ins->sreg3, 0); riscv_sc_w (code, RISCV_ORDER_RL, RISCV_T1, ins->sreg2, ins->sreg1); @@ -4878,7 +5193,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_W); code = mono_riscv_emit_store (code, ins->sreg1, ins->inst_destbasereg, ins->inst_offset, 8); if (ins->backend.memory_barrier_kind == MONO_MEMORY_BARRIER_SEQ) - riscv_fence (code, RISCV_FENCE_MEM, RISCV_FENCE_MEM); + riscv_fence (code, RISCV_FENCE_ALL, RISCV_FENCE_ALL); break; } case OP_ATOMIC_LOAD_I8: @@ -4894,7 +5209,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) guint8 *loop_start, *branch_label; /* sreg2 is the value, sreg3 is the comparand */ loop_start = code; - riscv_lr_d (code, RISCV_ORDER_NONE, RISCV_T0, ins->sreg1); + riscv_lr_d (code, RISCV_ORDER_ALL, RISCV_T0, ins->sreg1); branch_label = code; riscv_bne (code, RISCV_T0, ins->sreg3, 0); riscv_sc_d (code, RISCV_ORDER_RL, RISCV_T1, ins->sreg2, ins->sreg1); @@ -4988,7 +5303,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_FCONV_TO_R4: case OP_RISCV_SETFREG_R4: { g_assert (riscv_stdext_d); - riscv_fcvt_s_d (code, RISCV_ROUND_TZ, ins->dreg, ins->sreg1); + riscv_fcvt_s_d (code, RISCV_ROUND_DY, ins->dreg, ins->sreg1); break; } case OP_RDIV: { @@ -5168,8 +5483,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_FCALL_MEMBASE: case OP_VCALL2_MEMBASE: case OP_VOIDCALL_MEMBASE: - code = mono_riscv_emit_load (code, RISCV_T0, ins->inst_basereg, ins->inst_offset, 0); - riscv_jalr (code, RISCV_RA, RISCV_T0, 0); + code = mono_riscv_emit_load (code, RISCV_T1, ins->inst_basereg, ins->inst_offset, 0); + riscv_jalr (code, RISCV_RA, RISCV_T1, 0); code = emit_move_return_value (cfg, code, ins); break; diff --git a/src/mono/mono/mini/mini-riscv.h b/src/mono/mono/mini/mini-riscv.h index 36b092c122bb70..d19fc88da5be7f 100644 --- a/src/mono/mono/mini/mini-riscv.h +++ b/src/mono/mono/mini/mini-riscv.h @@ -236,12 +236,15 @@ typedef enum { ArgNone, // only in void return type ArgInIReg = 0x01, ArgInFReg, + ArgR4InIReg, + ArgR8InIReg, #ifdef TARGET_RISCV64 ArgInFRegR4, #endif ArgOnStack, ArgOnStackR4, ArgOnStackR8, + ArgHFA, /* * Vtype passed in consecutive int registers. @@ -258,11 +261,18 @@ typedef struct { /* ArgVtypeInIRegs */ guint8 reg; int size; - guint8 is_regpair; + /* ArgVtypeInIRegs/ArgHFA */ + guint8 nregs; /* ArgOnStack */ int slot_size; gint32 offset; guint8 is_signed : 1; + /* ArgHFA */ + int esize; + /* The offsets of the float values inside the arg */ + guint16 foffsets [4]; + int nfregs_to_skip; + gboolean hfa; } ArgInfo; struct CallInfo { diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 60f4dd83b52c09..44c5fd4c58d267 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -3647,8 +3647,80 @@ - - https://github.com/dotnet/runtime/issues/54906 + + https://github.com/dotnet/runtime/issues/65704 + + + https://github.com/dotnet/runtime/issues/65704 + + + https://github.com/dotnet/runtime/issues/54393 + + + https://github.com/dotnet/runtime/issues/65695 + + + https://github.com/dotnet/runtime/issues/65695 + + + The function size is Huge(about 6MB), out of the ability of inst jal, can't jump to thunk area + + + The function size is Huge(about 6MB), out of the ability of inst jal, can't jump to thunk area + + + timeout + + + Did not find ilasm or ildasm in CORE_ROOT directory + + + Did not find ilasm or ildasm in CORE_ROOT directory + + + Can't find file dotnet-diagnostic-{pid}-*-socket + + + Can't find file dotnet-diagnostic-{pid}-*-socket + + + Can't find file dotnet-diagnostic-{pid}-*-socket + + + Can't find file dotnet-diagnostic-{pid}-*-socket + + + Can't find file dotnet-diagnostic-{pid}-*-socket + + + Can't find file dotnet-diagnostic-{pid}-*-socket + + + Can't find file dotnet-diagnostic-{pid}-*-socket + + + Can't find file dotnet-diagnostic-{pid}-*-socket + + + Can't find file dotnet-diagnostic-{pid}-*-socket + + + Can't find file dotnet-diagnostic-{pid}-*-socket + + + Process has start correctly, but EventPipeClient.ListAvailablePorts() still contain this PID + + + https://github.com/dotnet/runtime/issues/74891 + + + https://github.com/dotnet/runtime/issues/74891 + + + https://github.com/dotnet/runtime/issues/74891 + + + https://github.com/dotnet/runtime/issues/74891 From 1ad22a5bb11140a0790c499be1e4ec4245effea8 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 17 Feb 2024 18:41:12 +0100 Subject: [PATCH 103/158] Remove test exclusion for dotnet/runtimelab#164 (#98614) --- src/tests/issues.targets | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 44c5fd4c58d267..473e82bc3e9697 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -772,9 +772,6 @@ https://github.com/dotnet/runtime/issues/81673 - - https://github.com/dotnet/runtimelab/issues/164 - https://github.com/dotnet/runtimelab/issues/165 From d06742c2dbf9ded9c1a896c2a6d0ba1e333a906e Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Sat, 17 Feb 2024 19:44:23 +0100 Subject: [PATCH 104/158] try to fix https://github.com/dotnet/runtime/issues/96545 (#98608) --- src/mono/browser/runtime/invoke-js.ts | 5 ++++- .../pthreads/shared/emscripten-replacements.ts | 13 ++++++++++--- src/mono/browser/runtime/pthreads/worker/index.ts | 7 +++++++ src/mono/browser/runtime/scheduling.ts | 7 +++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/mono/browser/runtime/invoke-js.ts b/src/mono/browser/runtime/invoke-js.ts index 5323407eefa0fe..9c118329b15aaf 100644 --- a/src/mono/browser/runtime/invoke-js.ts +++ b/src/mono/browser/runtime/invoke-js.ts @@ -6,7 +6,7 @@ import BuildConfiguration from "consts:configuration"; import { marshal_exception_to_cs, bind_arg_marshal_to_cs } from "./marshal-to-cs"; import { get_signature_argument_count, bound_js_function_symbol, get_sig, get_signature_version, get_signature_type, imported_js_function_symbol, get_signature_handle, get_signature_function_name, get_signature_module_name } from "./marshal"; -import { setI32_unchecked, receiveWorkerHeapViews } from "./memory"; +import { setI32_unchecked, receiveWorkerHeapViews, forceThreadMemoryViewRefresh } from "./memory"; import { stringToMonoStringRoot } from "./strings"; import { MonoObject, MonoObjectRef, JSFunctionSignature, JSMarshalerArguments, WasmRoot, BoundMarshalerToJs, JSFnHandle, BoundMarshalerToCs, JSHandle, MarshalerType } from "./types/internal"; import { Int32Ptr } from "./types/emscripten"; @@ -52,6 +52,9 @@ export function mono_wasm_invoke_import_async(args: JSMarshalerArguments, signat let max_postpone_count = 10; function postpone_invoke_import_async() { if (max_postpone_count < 0 || is_thread_available()) { + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } bound_fn(args); Module._free(args as any); } else { diff --git a/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts b/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts index 2f004c49369a62..2d2c22cb1ab0e1 100644 --- a/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts +++ b/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts @@ -5,11 +5,11 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import BuildConfiguration from "consts:configuration"; import { dumpThreads, onWorkerLoadInitiated, resolveThreadPromises } from "../browser"; -import { mono_wasm_pthread_on_pthread_created } from "../worker"; +import { mono_wasm_pthread_on_pthread_created, onRunMessage } from "../worker"; import { PThreadLibrary, PThreadWorker, getModulePThread, getUnusedWorkerPool } from "./emscripten-internals"; -import { loaderHelpers, mono_assert } from "../../globals"; +import { Module, loaderHelpers, mono_assert } from "../../globals"; import { mono_log_warn } from "../../logging"; -import { PThreadPtrNull } from "./types"; +import { PThreadPtr, PThreadPtrNull } from "./types"; /** @module emscripten-replacements Replacements for individual functions in the emscripten PThreads library. * These have a hard dependency on the version of Emscripten that we are using and may need to be kept in sync with @@ -22,6 +22,13 @@ export function replaceEmscriptenPThreadLibrary(modulePThread: PThreadLibrary): const originalLoadWasmModuleToWorker = modulePThread.loadWasmModuleToWorker; const originalThreadInitTLS = modulePThread.threadInitTLS; const originalReturnWorkerToPool = modulePThread.returnWorkerToPool; + const original_emscripten_thread_init = (Module as any)["__emscripten_thread_init"]; + + + (Module as any)["__emscripten_thread_init"] = (pthread_ptr: PThreadPtr, isMainBrowserThread: number, isMainRuntimeThread: number, canBlock: number) => { + onRunMessage(pthread_ptr); + original_emscripten_thread_init(pthread_ptr, isMainBrowserThread, isMainRuntimeThread, canBlock); + }; modulePThread.loadWasmModuleToWorker = (worker: PThreadWorker): Promise => { const afterLoaded = originalLoadWasmModuleToWorker(worker); diff --git a/src/mono/browser/runtime/pthreads/worker/index.ts b/src/mono/browser/runtime/pthreads/worker/index.ts index 568bf510082928..29229c23e5fd8a 100644 --- a/src/mono/browser/runtime/pthreads/worker/index.ts +++ b/src/mono/browser/runtime/pthreads/worker/index.ts @@ -20,6 +20,7 @@ import { postRunWorker, preRunWorker } from "../../startup"; import { mono_log_debug, mono_log_error } from "../../logging"; import { CharPtr } from "../../types/emscripten"; import { utf8ToString } from "../../strings"; +import { forceThreadMemoryViewRefresh } from "../../memory"; // re-export some of the events types export { @@ -85,6 +86,10 @@ function monoDedicatedChannelMessageFromMainToWorker(event: MessageEvent mono_log_debug("got message from main on the dedicated channel", event.data); } +export function onRunMessage(pthread_ptr: PThreadPtr) { + monoThreadInfo.pthreadId = pthread_ptr; + forceThreadMemoryViewRefresh(); +} /// Called by emscripten when a pthread is setup to run on a worker. Can be called multiple times /// for the same webworker, since emscripten can reuse workers. @@ -92,6 +97,8 @@ function monoDedicatedChannelMessageFromMainToWorker(event: MessageEvent export function mono_wasm_pthread_on_pthread_created(): void { if (!WasmEnableThreads) return; try { + forceThreadMemoryViewRefresh(); + const pthread_id = mono_wasm_pthread_ptr(); mono_assert(!is_nullish(pthread_id), "pthread_self() returned null"); monoThreadInfo.pthreadId = pthread_id; diff --git a/src/mono/browser/runtime/scheduling.ts b/src/mono/browser/runtime/scheduling.ts index 69412b79473291..4651516271fe6b 100644 --- a/src/mono/browser/runtime/scheduling.ts +++ b/src/mono/browser/runtime/scheduling.ts @@ -6,6 +6,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import cwraps from "./cwraps"; import { ENVIRONMENT_IS_WORKER, Module, loaderHelpers } from "./globals"; import { is_thread_available } from "./pthreads/shared/emscripten-replacements"; +import { forceThreadMemoryViewRefresh } from "./memory"; let spread_timers_maximum = 0; let pump_count = 0; @@ -43,6 +44,9 @@ function mono_background_exec_until_done() { if (!loaderHelpers.is_runtime_running()) { return; } + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } while (pump_count > 0) { --pump_count; cwraps.mono_background_exec(); @@ -85,6 +89,9 @@ export function mono_wasm_schedule_timer(shortestDueTimeMs: number): void { function mono_wasm_schedule_timer_tick() { Module.maybeExit(); + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } if (!loaderHelpers.is_runtime_running()) { return; } From 483fd0ce035d9f7ec0c208a90a4f4dc34fc64049 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 17 Feb 2024 15:29:22 -0500 Subject: [PATCH 105/158] Include key in FrozenDictionary.this[] KeyNotFoundException (#98613) --- .../src/System/Collections/Frozen/FrozenDictionary.cs | 2 +- .../Collections/Immutable/ImmutableDictionary_2.Builder.cs | 6 +++--- .../System/Collections/Immutable/ImmutableDictionary_2.cs | 6 +++--- .../Immutable/ImmutableSortedDictionary_2.Builder.cs | 6 +++--- .../Immutable/ImmutableSortedDictionary_2.Node.cs | 2 +- .../Collections/Immutable/ImmutableSortedDictionary_2.cs | 6 +++--- .../src/System/Collections/ThrowHelper.cs | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.cs index dc06ca0cd9287f..46e3ae62e8b569 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.cs @@ -461,7 +461,7 @@ public ref readonly TValue this[TKey key] if (Unsafe.IsNullRef(ref Unsafe.AsRef(in valueRef))) { - ThrowHelper.ThrowKeyNotFoundException(); + ThrowHelper.ThrowKeyNotFoundException(key); } return ref valueRef; diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs index 6515395b458861..fac2b6163c13ba 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs @@ -409,12 +409,12 @@ public TValue this[TKey key] get { TValue value; - if (this.TryGetValue(key, out value!)) + if (!this.TryGetValue(key, out value!)) { - return value; + ThrowHelper.ThrowKeyNotFoundException(key); } - throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); + return value; } set diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.cs index 3cc1c12d38fb4d..ee164957d36aa2 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.cs @@ -246,12 +246,12 @@ public TValue this[TKey key] Requires.NotNullAllowStructs(key, nameof(key)); TValue value; - if (this.TryGetValue(key, out value!)) + if (!this.TryGetValue(key, out value!)) { - return value; + ThrowHelper.ThrowKeyNotFoundException(key); } - throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); + return value; } } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs index 9a975cc55e2712..ce8e4a0f17996e 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs @@ -178,12 +178,12 @@ public TValue this[TKey key] get { TValue value; - if (this.TryGetValue(key, out value!)) + if (!this.TryGetValue(key, out value!)) { - return value; + ThrowHelper.ThrowKeyNotFoundException(key); } - throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); + return value; } set diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs index de3e6cf95217e1..76330f19c18c8b 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs @@ -338,7 +338,7 @@ internal ref readonly TValue ValueRef(TKey key, IComparer keyComparer) ImmutableSortedDictionary.Node match = this.Search(key, keyComparer); if (match.IsEmpty) { - throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); + ThrowHelper.ThrowKeyNotFoundException(key); } return ref match._value; diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs index b4d7d381e10e17..d44687253fa527 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs @@ -197,12 +197,12 @@ public TValue this[TKey key] Requires.NotNullAllowStructs(key, nameof(key)); TValue? value; - if (this.TryGetValue(key, out value)) + if (!this.TryGetValue(key, out value)) { - return value; + ThrowHelper.ThrowKeyNotFoundException(key); } - throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key.ToString())); + return value; } } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/ThrowHelper.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/ThrowHelper.cs index 99800e23819f0e..4085f318e34156 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/ThrowHelper.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/ThrowHelper.cs @@ -26,8 +26,8 @@ public static void ThrowArgumentNullException(string? paramName) => throw new ArgumentNullException(paramName); [DoesNotReturn] - public static void ThrowKeyNotFoundException() => - throw new KeyNotFoundException(); + public static void ThrowKeyNotFoundException(TKey key) => + throw new KeyNotFoundException(SR.Format(SR.Arg_KeyNotFoundWithKey, key)); [DoesNotReturn] public static void ThrowInvalidOperationException() => From 432397d5d191db85a51409929d94e55e4a0e78c7 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Sun, 18 Feb 2024 00:20:54 +0100 Subject: [PATCH 106/158] Faster IndexOfAny for IgnoreCase Ascii letters (#96588) * Add packed IgnoreCase IndexOfAny variants * Emit SearchValues for ASCII sets of 4/5 values in RegexCompliler * Remove Any3CharPackedIgnoreCase * Update asserts * Attribute order * Fix build * More comments * Tweak ContainsCore --- .../System.Memory/tests/Span/SearchValues.cs | 4 + .../System.Private.CoreLib.Shared.projitems | 14 +- .../src/System/Globalization/Ordinal.cs | 2 +- .../Any1CharPackedIgnoreCaseSearchValues.cs | 52 ++++++++ .../Any1CharPackedSearchValues.cs | 42 ++++++ .../System/SearchValues/Any1SearchValues.cs | 52 ++++++++ .../SearchValues/Any2ByteSearchValues.cs | 41 ------ .../Any2CharPackedIgnoreCaseSearchValues.cs | 67 ++++++++++ .../Any2CharPackedSearchValues.cs | 42 ++++++ .../SearchValues/Any2CharSearchValues.cs | 51 -------- .../System/SearchValues/Any2SearchValues.cs | 52 ++++++++ .../SearchValues/Any3ByteSearchValues.cs | 41 ------ .../Any3CharPackedSearchValues.cs | 42 ++++++ .../SearchValues/Any3CharSearchValues.cs | 53 -------- .../System/SearchValues/Any3SearchValues.cs | 52 ++++++++ .../src/System/SearchValues/SearchValues.cs | 58 ++++++--- .../SearchValues/SingleByteSearchValues.cs | 41 ------ .../SearchValues/SingleCharSearchValues.cs | 49 ------- .../src/System/SpanHelpers.Packed.cs | 122 +++++++++++++----- .../src/System/SpanHelpers.T.cs | 9 ++ .../src/System/String.Searching.cs | 6 +- .../Text/RegularExpressions/RegexCompiler.cs | 76 +++++------ 22 files changed, 600 insertions(+), 368 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1CharPackedIgnoreCaseSearchValues.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1CharPackedSearchValues.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1SearchValues.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2ByteSearchValues.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharPackedIgnoreCaseSearchValues.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharPackedSearchValues.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharSearchValues.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2SearchValues.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3ByteSearchValues.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3CharPackedSearchValues.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3CharSearchValues.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3SearchValues.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/SingleByteSearchValues.cs delete mode 100644 src/libraries/System.Private.CoreLib/src/System/SearchValues/SingleCharSearchValues.cs diff --git a/src/libraries/System.Memory/tests/Span/SearchValues.cs b/src/libraries/System.Memory/tests/Span/SearchValues.cs index 9ef91acec80dab..f2020279cdf4b9 100644 --- a/src/libraries/System.Memory/tests/Span/SearchValues.cs +++ b/src/libraries/System.Memory/tests/Span/SearchValues.cs @@ -44,6 +44,10 @@ public static IEnumerable Values_MemberData() "aaa", "aaaa", "aaaaa", + "Aa", + "AaBb", + "AaBbCc", + "[]{}", "\uFFF0", "\uFFF0\uFFF2", "\uFFF0\uFFF2\uFFF4", diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index c9e500d22af6b6..a73e8247a58e72 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -423,14 +423,16 @@ + + + + + + + + - - - - - - diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs index b7de19aab570d7..5b18e8d2b6fcb3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/Ordinal.cs @@ -397,7 +397,7 @@ internal static int IndexOfOrdinalIgnoreCase(ReadOnlySpan source, ReadOnly // Do a quick search for the first element of "value". int relativeIndex = isLetter ? PackedSpanHelpers.PackedIndexOfIsSupported - ? PackedSpanHelpers.IndexOfAny(ref Unsafe.Add(ref searchSpace, offset), valueCharU, valueCharL, searchSpaceMinusValueTailLength) + ? PackedSpanHelpers.IndexOfAnyIgnoreCase(ref Unsafe.Add(ref searchSpace, offset), valueCharL, searchSpaceMinusValueTailLength) : SpanHelpers.IndexOfAnyChar(ref Unsafe.Add(ref searchSpace, offset), valueCharU, valueCharL, searchSpaceMinusValueTailLength) : SpanHelpers.IndexOfChar(ref Unsafe.Add(ref searchSpace, offset), valueChar, searchSpaceMinusValueTailLength); if (relativeIndex < 0) diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1CharPackedIgnoreCaseSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1CharPackedIgnoreCaseSearchValues.cs new file mode 100644 index 00000000000000..dfe6b3631f709e --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1CharPackedIgnoreCaseSearchValues.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; + +namespace System.Buffers +{ + internal sealed class Any1CharPackedIgnoreCaseSearchValues : SearchValues + { + // While this most commonly applies to ASCII letters, it also works for other values that differ by 0x20 (e.g. "[{" => "{"). + // _lowerCase is therefore not necessarily a lower case ASCII letter, but just the higher value (the one with the 0x20 bit set). + private readonly char _lowerCase, _upperCase; + private readonly uint _lowerCaseUint; + + public Any1CharPackedIgnoreCaseSearchValues(char value) + { + Debug.Assert((value | 0x20) == value); + + _lowerCase = value; + _upperCase = (char)(value & ~0x20); + _lowerCaseUint = value; + } + + internal override char[] GetValues() => + [_upperCase, _lowerCase]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsCore(char value) => + (uint)(value | 0x20) == _lowerCaseUint; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + internal override int IndexOfAny(ReadOnlySpan span) => + PackedSpanHelpers.IndexOfAnyIgnoreCase(ref MemoryMarshal.GetReference(span), _lowerCase, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + internal override int IndexOfAnyExcept(ReadOnlySpan span) => + PackedSpanHelpers.IndexOfAnyExceptIgnoreCase(ref MemoryMarshal.GetReference(span), _lowerCase, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAny(ReadOnlySpan span) => + span.LastIndexOfAny(_lowerCase, _upperCase); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => + span.LastIndexOfAnyExcept(_lowerCase, _upperCase); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1CharPackedSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1CharPackedSearchValues.cs new file mode 100644 index 00000000000000..e6aa0c870486d8 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1CharPackedSearchValues.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; + +namespace System.Buffers +{ + internal sealed class Any1CharPackedSearchValues : SearchValues + { + private readonly char _e0; + + public Any1CharPackedSearchValues(char value) => + _e0 = value; + + internal override char[] GetValues() => + [_e0]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsCore(char value) => + value == _e0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + internal override int IndexOfAny(ReadOnlySpan span) => + PackedSpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), _e0, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + internal override int IndexOfAnyExcept(ReadOnlySpan span) => + PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAny(ReadOnlySpan span) => + span.LastIndexOf(_e0); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => + span.LastIndexOfAnyExcept(_e0); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1SearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1SearchValues.cs new file mode 100644 index 00000000000000..9a52511b987509 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any1SearchValues.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#pragma warning disable 8500 // address of managed types + +namespace System.Buffers +{ + internal sealed class Any1SearchValues : SearchValues + where T : struct, IEquatable + where TImpl : struct, INumber + { + private readonly TImpl _e0; + + public Any1SearchValues(ReadOnlySpan values) + { + Debug.Assert(Unsafe.SizeOf() == Unsafe.SizeOf()); + Debug.Assert(values.Length == 1); + _e0 = values[0]; + } + + internal override unsafe T[] GetValues() + { + TImpl e0 = _e0; + return new[] { *(T*)&e0 }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override unsafe bool ContainsCore(T value) => + *(TImpl*)&value == _e0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int IndexOfAny(ReadOnlySpan span) => + SpanHelpers.NonPackedIndexOfValueType>(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int IndexOfAnyExcept(ReadOnlySpan span) => + SpanHelpers.NonPackedIndexOfValueType>(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAny(ReadOnlySpan span) => + SpanHelpers.LastIndexOfValueType(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => + SpanHelpers.LastIndexOfAnyExceptValueType(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, span.Length); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2ByteSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2ByteSearchValues.cs deleted file mode 100644 index 42f4acbfebf8b0..00000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2ByteSearchValues.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace System.Buffers -{ - internal sealed class Any2ByteSearchValues : SearchValues - { - private readonly byte _e0, _e1; - - public Any2ByteSearchValues(ReadOnlySpan values) - { - Debug.Assert(values.Length == 2); - (_e0, _e1) = (values[0], values[1]); - } - - internal override byte[] GetValues() => new[] { _e0, _e1 }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override bool ContainsCore(byte value) => - value == _e0 || value == _e1; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAny(ReadOnlySpan span) => - span.IndexOfAny(_e0, _e1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAnyExcept(ReadOnlySpan span) => - span.IndexOfAnyExcept(_e0, _e1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAny(ReadOnlySpan span) => - span.LastIndexOfAny(_e0, _e1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - span.LastIndexOfAnyExcept(_e0, _e1); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharPackedIgnoreCaseSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharPackedIgnoreCaseSearchValues.cs new file mode 100644 index 00000000000000..1073fcf3c81858 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharPackedIgnoreCaseSearchValues.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.Wasm; +using System.Runtime.Intrinsics.X86; + +namespace System.Buffers +{ + internal sealed class Any2CharPackedIgnoreCaseSearchValues : SearchValues + { + // While this most commonly applies to ASCII letters, it also works for other values that differ by 0x20 (e.g. "[]{}" => "{}"). + // _e0 and _e1 are therefore not necessarily lower case ASCII letters, but just the higher values (the ones with the 0x20 bit set). + private readonly char _e0, _e1; + private readonly uint _uint0, _uint1; + private IndexOfAnyAsciiSearcher.AsciiState _state; + + public Any2CharPackedIgnoreCaseSearchValues(char value0, char value1) + { + Debug.Assert((value0 | 0x20) == value0 && char.IsAscii(value0)); + Debug.Assert((value1 | 0x20) == value1 && char.IsAscii(value1)); + + (_e0, _e1) = (value0, value1); + (_uint0, _uint1) = (value0, value1); + IndexOfAnyAsciiSearcher.ComputeAsciiState([(char)(_e0 & ~0x20), _e0, (char)(_e1 & ~0x20), _e1], out _state); + } + + internal override char[] GetValues() => + [(char)(_e0 & ~0x20), _e0, (char)(_e1 & ~0x20), _e1]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsCore(char value) + { + uint lowerCase = (uint)(value | 0x20); + return lowerCase == _uint0 || lowerCase == _uint1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + internal override int IndexOfAny(ReadOnlySpan span) => + PackedSpanHelpers.IndexOfAnyIgnoreCase(ref MemoryMarshal.GetReference(span), _e0, _e1, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + internal override int IndexOfAnyExcept(ReadOnlySpan span) => + PackedSpanHelpers.IndexOfAnyExceptIgnoreCase(ref MemoryMarshal.GetReference(span), _e0, _e1, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + internal override int LastIndexOfAny(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.LastIndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] + internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => + IndexOfAnyAsciiSearcher.LastIndexOfAny( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length, ref _state); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharPackedSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharPackedSearchValues.cs new file mode 100644 index 00000000000000..d951b8e2375e0f --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharPackedSearchValues.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; + +namespace System.Buffers +{ + internal sealed class Any2CharPackedSearchValues : SearchValues + { + private readonly char _e0, _e1; + + public Any2CharPackedSearchValues(char value0, char value1) => + (_e0, _e1) = (value0, value1); + + internal override char[] GetValues() => + [_e0, _e1]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsCore(char value) => + value == _e0 || value == _e1; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + internal override int IndexOfAny(ReadOnlySpan span) => + PackedSpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), _e0, _e1, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + internal override int IndexOfAnyExcept(ReadOnlySpan span) => + PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, _e1, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAny(ReadOnlySpan span) => + span.LastIndexOfAny(_e0, _e1); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => + span.LastIndexOfAnyExcept(_e0, _e1); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharSearchValues.cs deleted file mode 100644 index a2bfde7f6fab94..00000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2CharSearchValues.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace System.Buffers -{ - internal sealed class Any2CharSearchValues : SearchValues - where TShouldUsePacked : struct, SearchValues.IRuntimeConst - { - private char _e0, _e1; - - public Any2CharSearchValues(char value0, char value1) => - (_e0, _e1) = (value0, value1); - - internal override char[] GetValues() => new[] { _e0, _e1 }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override bool ContainsCore(char value) => - value == _e0 || value == _e1; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAny(ReadOnlySpan span) => - (PackedSpanHelpers.PackedIndexOfIsSupported && TShouldUsePacked.Value) - ? PackedSpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), _e0, _e1, span.Length) - : SpanHelpers.NonPackedIndexOfAnyValueType>( - ref Unsafe.As(ref MemoryMarshal.GetReference(span)), - Unsafe.As(ref _e0), - Unsafe.As(ref _e1), - span.Length); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAnyExcept(ReadOnlySpan span) => - (PackedSpanHelpers.PackedIndexOfIsSupported && TShouldUsePacked.Value) - ? PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, _e1, span.Length) - : SpanHelpers.NonPackedIndexOfAnyValueType>( - ref Unsafe.As(ref MemoryMarshal.GetReference(span)), - Unsafe.As(ref _e0), - Unsafe.As(ref _e1), - span.Length); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAny(ReadOnlySpan span) => - span.LastIndexOfAny(_e0, _e1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - span.LastIndexOfAnyExcept(_e0, _e1); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2SearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2SearchValues.cs new file mode 100644 index 00000000000000..640a15f05c8f2b --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any2SearchValues.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#pragma warning disable 8500 // address of managed types + +namespace System.Buffers +{ + internal sealed class Any2SearchValues : SearchValues + where T : struct, IEquatable + where TImpl : struct, INumber + { + private readonly TImpl _e0, _e1; + + public Any2SearchValues(ReadOnlySpan values) + { + Debug.Assert(Unsafe.SizeOf() == Unsafe.SizeOf()); + Debug.Assert(values.Length == 2); + (_e0, _e1) = (values[0], values[1]); + } + + internal override unsafe T[] GetValues() + { + TImpl e0 = _e0, e1 = _e1; + return new[] { *(T*)&e0, *(T*)&e1 }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override unsafe bool ContainsCore(T value) => + *(TImpl*)&value == _e0 || *(TImpl*)&value == _e1; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int IndexOfAny(ReadOnlySpan span) => + SpanHelpers.NonPackedIndexOfAnyValueType>(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, _e1, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int IndexOfAnyExcept(ReadOnlySpan span) => + SpanHelpers.NonPackedIndexOfAnyValueType>(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, _e1, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAny(ReadOnlySpan span) => + SpanHelpers.LastIndexOfAnyValueType(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, _e1, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => + SpanHelpers.LastIndexOfAnyExceptValueType(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, _e1, span.Length); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3ByteSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3ByteSearchValues.cs deleted file mode 100644 index d7208dea435067..00000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3ByteSearchValues.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace System.Buffers -{ - internal sealed class Any3ByteSearchValues : SearchValues - { - private readonly byte _e0, _e1, _e2; - - public Any3ByteSearchValues(ReadOnlySpan values) - { - Debug.Assert(values.Length == 3); - (_e0, _e1, _e2) = (values[0], values[1], values[2]); - } - - internal override byte[] GetValues() => new[] { _e0, _e1, _e2 }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override bool ContainsCore(byte value) => - value == _e0 || value == _e1 || value == _e2; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAny(ReadOnlySpan span) => - span.IndexOfAny(_e0, _e1, _e2); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAnyExcept(ReadOnlySpan span) => - span.IndexOfAnyExcept(_e0, _e1, _e2); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAny(ReadOnlySpan span) => - span.LastIndexOfAny(_e0, _e1, _e2); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - span.LastIndexOfAnyExcept(_e0, _e1, _e2); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3CharPackedSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3CharPackedSearchValues.cs new file mode 100644 index 00000000000000..cebb695aa3fb79 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3CharPackedSearchValues.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics.X86; + +namespace System.Buffers +{ + internal sealed class Any3CharPackedSearchValues : SearchValues + { + private readonly char _e0, _e1, _e2; + + public Any3CharPackedSearchValues(char value0, char value1, char value2) => + (_e0, _e1, _e2) = (value0, value1, value2); + + internal override char[] GetValues() => + [_e0, _e1, _e2]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override bool ContainsCore(char value) => + value == _e0 || value == _e1 || value == _e2; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + internal override int IndexOfAny(ReadOnlySpan span) => + PackedSpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), _e0, _e1, _e2, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + internal override int IndexOfAnyExcept(ReadOnlySpan span) => + PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, _e1, _e2, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAny(ReadOnlySpan span) => + span.LastIndexOfAny(_e0, _e1, _e2); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => + span.LastIndexOfAnyExcept(_e0, _e1, _e2); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3CharSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3CharSearchValues.cs deleted file mode 100644 index c69dbff8d0a4c7..00000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3CharSearchValues.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace System.Buffers -{ - internal sealed class Any3CharSearchValues : SearchValues - where TShouldUsePacked : struct, SearchValues.IRuntimeConst - { - private char _e0, _e1, _e2; - - public Any3CharSearchValues(char value0, char value1, char value2) => - (_e0, _e1, _e2) = (value0, value1, value2); - - internal override char[] GetValues() => new[] { _e0, _e1, _e2 }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override bool ContainsCore(char value) => - value == _e0 || value == _e1 || value == _e2; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAny(ReadOnlySpan span) => - (PackedSpanHelpers.PackedIndexOfIsSupported && TShouldUsePacked.Value) - ? PackedSpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), _e0, _e1, _e2, span.Length) - : SpanHelpers.NonPackedIndexOfAnyValueType>( - ref Unsafe.As(ref MemoryMarshal.GetReference(span)), - Unsafe.As(ref _e0), - Unsafe.As(ref _e1), - Unsafe.As(ref _e2), - span.Length); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAnyExcept(ReadOnlySpan span) => - (PackedSpanHelpers.PackedIndexOfIsSupported && TShouldUsePacked.Value) - ? PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, _e1, _e2, span.Length) - : SpanHelpers.NonPackedIndexOfAnyValueType>( - ref Unsafe.As(ref MemoryMarshal.GetReference(span)), - Unsafe.As(ref _e0), - Unsafe.As(ref _e1), - Unsafe.As(ref _e2), - span.Length); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAny(ReadOnlySpan span) => - span.LastIndexOfAny(_e0, _e1, _e2); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - span.LastIndexOfAnyExcept(_e0, _e1, _e2); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3SearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3SearchValues.cs new file mode 100644 index 00000000000000..f166aef42703cd --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Any3SearchValues.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#pragma warning disable 8500 // address of managed types + +namespace System.Buffers +{ + internal sealed class Any3SearchValues : SearchValues + where T : struct, IEquatable + where TImpl : struct, INumber + { + private readonly TImpl _e0, _e1, _e2; + + public Any3SearchValues(ReadOnlySpan values) + { + Debug.Assert(Unsafe.SizeOf() == Unsafe.SizeOf()); + Debug.Assert(values.Length == 3); + (_e0, _e1, _e2) = (values[0], values[1], values[2]); + } + + internal override unsafe T[] GetValues() + { + TImpl e0 = _e0, e1 = _e1, e2 = _e2; + return new[] { *(T*)&e0, *(T*)&e1, *(T*)&e2 }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override unsafe bool ContainsCore(T value) => + *(TImpl*)&value == _e0 || *(TImpl*)&value == _e1 || *(TImpl*)&value == _e2; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int IndexOfAny(ReadOnlySpan span) => + SpanHelpers.NonPackedIndexOfAnyValueType>(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, _e1, _e2, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int IndexOfAnyExcept(ReadOnlySpan span) => + SpanHelpers.NonPackedIndexOfAnyValueType>(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, _e1, _e2, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAny(ReadOnlySpan span) => + SpanHelpers.LastIndexOfAnyValueType(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, _e1, _e2, span.Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => + SpanHelpers.LastIndexOfAnyExceptValueType(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), _e0, _e1, _e2, span.Length); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs index 13c6779a7d9851..7428c912c6cfda 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SearchValues.cs @@ -34,7 +34,7 @@ public static SearchValues Create(ReadOnlySpan values) if (values.Length == 1) { - return new SingleByteSearchValues(values); + return new Any1SearchValues(values); } // RangeByteSearchValues is slower than SingleByteSearchValues, but faster than Any2ByteSearchValues @@ -48,8 +48,8 @@ public static SearchValues Create(ReadOnlySpan values) Debug.Assert(values.Length is 2 or 3 or 4 or 5); return values.Length switch { - 2 => new Any2ByteSearchValues(values), - 3 => new Any3ByteSearchValues(values), + 2 => new Any2SearchValues(values), + 3 => new Any3SearchValues(values), 4 => new Any4SearchValues(values), _ => new Any5SearchValues(values), }; @@ -75,12 +75,18 @@ public static SearchValues Create(ReadOnlySpan values) return new EmptySearchValues(); } + // Vector128 isn't valid. Treat the values as shorts instead. + ReadOnlySpan shortValues = MemoryMarshal.CreateReadOnlySpan( + ref Unsafe.As(ref MemoryMarshal.GetReference(values)), + values.Length); + if (values.Length == 1) { char value = values[0]; + return PackedSpanHelpers.PackedIndexOfIsSupported && PackedSpanHelpers.CanUsePackedIndexOf(value) - ? new SingleCharSearchValues(value) - : new SingleCharSearchValues(value); + ? new Any1CharPackedSearchValues(value) + : new Any1SearchValues(shortValues); } // RangeCharSearchValues is slower than SingleCharSearchValues, but faster than Any2CharSearchValues @@ -95,9 +101,18 @@ public static SearchValues Create(ReadOnlySpan values) { char value0 = values[0]; char value1 = values[1]; - return PackedSpanHelpers.PackedIndexOfIsSupported && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1) - ? new Any2CharSearchValues(value0, value1) - : new Any2CharSearchValues(value0, value1); + + if (PackedSpanHelpers.PackedIndexOfIsSupported && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1)) + { + // If the two values are the same ASCII letter with both cases, we can use an approach that + // reduces the number of comparisons by masking off the bit that differs between lower and upper case (0x20). + // While this most commonly applies to ASCII letters, it also works for other values that differ by 0x20 (e.g. "[{" => "{"). + return (value0 ^ value1) == 0x20 + ? new Any1CharPackedIgnoreCaseSearchValues((char)Math.Max(value0, value1)) + : new Any2CharPackedSearchValues(value0, value1); + } + + return new Any2SearchValues(shortValues); } if (values.Length == 3) @@ -105,24 +120,37 @@ public static SearchValues Create(ReadOnlySpan values) char value0 = values[0]; char value1 = values[1]; char value2 = values[2]; + return PackedSpanHelpers.PackedIndexOfIsSupported && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1) && PackedSpanHelpers.CanUsePackedIndexOf(value2) - ? new Any3CharSearchValues(value0, value1, value2) - : new Any3CharSearchValues(value0, value1, value2); + ? new Any3CharPackedSearchValues(value0, value1, value2) + : new Any3SearchValues(shortValues); } // IndexOfAnyAsciiSearcher for chars is slower than Any3CharSearchValues, but faster than Any4SearchValues if (IndexOfAnyAsciiSearcher.IsVectorizationSupported && maxInclusive < 128) { + // If the values are sets of 2 ASCII letters with both cases, we can use an approach that + // reduces the number of comparisons by masking off the bit that differs between lower and upper case (0x20). + // While this most commonly applies to ASCII letters, it also works for other values that differ by 0x20 (e.g. "[]{}" => "{}"). + if (PackedSpanHelpers.PackedIndexOfIsSupported && values.Length == 4 && minInclusive > 0) + { + Span copy = stackalloc char[4]; + values.CopyTo(copy); + copy.Sort(); + + if ((copy[0] ^ copy[2]) == 0x20 && + (copy[1] ^ copy[3]) == 0x20) + { + // We pick the higher two values (with the 0x20 bit set). "AaBb" => 'a', 'b' + return new Any2CharPackedIgnoreCaseSearchValues(copy[2], copy[3]); + } + } + return (Ssse3.IsSupported || PackedSimd.IsSupported) && minInclusive == 0 ? new AsciiCharSearchValues(values) : new AsciiCharSearchValues(values); } - // Vector128 isn't valid. Treat the values as shorts instead. - ReadOnlySpan shortValues = MemoryMarshal.CreateReadOnlySpan( - ref Unsafe.As(ref MemoryMarshal.GetReference(values)), - values.Length); - if (values.Length == 4) { return new Any4SearchValues(shortValues); diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SingleByteSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SingleByteSearchValues.cs deleted file mode 100644 index b768c2541562f7..00000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SingleByteSearchValues.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace System.Buffers -{ - internal sealed class SingleByteSearchValues : SearchValues - { - private readonly byte _e0; - - public SingleByteSearchValues(ReadOnlySpan values) - { - Debug.Assert(values.Length == 1); - _e0 = values[0]; - } - - internal override byte[] GetValues() => new[] { _e0 }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override bool ContainsCore(byte value) => - value == _e0; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAny(ReadOnlySpan span) => - span.IndexOf(_e0); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAnyExcept(ReadOnlySpan span) => - span.IndexOfAnyExcept(_e0); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAny(ReadOnlySpan span) => - span.LastIndexOf(_e0); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - span.LastIndexOfAnyExcept(_e0); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SingleCharSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/SingleCharSearchValues.cs deleted file mode 100644 index b1348e3859b073..00000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/SingleCharSearchValues.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace System.Buffers -{ - internal sealed class SingleCharSearchValues : SearchValues - where TShouldUsePacked : struct, SearchValues.IRuntimeConst - { - private char _e0; - - public SingleCharSearchValues(char value) => - _e0 = value; - - internal override char[] GetValues() => new[] { _e0 }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override bool ContainsCore(char value) => - value == _e0; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAny(ReadOnlySpan span) => - (PackedSpanHelpers.PackedIndexOfIsSupported && TShouldUsePacked.Value) - ? PackedSpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), _e0, span.Length) - : SpanHelpers.NonPackedIndexOfValueType>( - ref Unsafe.As(ref MemoryMarshal.GetReference(span)), - Unsafe.As(ref _e0), - span.Length); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int IndexOfAnyExcept(ReadOnlySpan span) => - (PackedSpanHelpers.PackedIndexOfIsSupported && TShouldUsePacked.Value) - ? PackedSpanHelpers.IndexOfAnyExcept(ref MemoryMarshal.GetReference(span), _e0, span.Length) - : SpanHelpers.NonPackedIndexOfValueType>( - ref Unsafe.As(ref MemoryMarshal.GetReference(span)), - Unsafe.As(ref _e0), - span.Length); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAny(ReadOnlySpan span) => - span.LastIndexOf(_e0); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override int LastIndexOfAnyExcept(ReadOnlySpan span) => - span.LastIndexOfAnyExcept(_e0); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs index fca176fe438126..69ce8c1f7fada9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs @@ -1,10 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers.Binary; using System.Diagnostics; using System.Numerics; -using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; @@ -37,22 +35,22 @@ public static unsafe bool CanUsePackedIndexOf(T value) [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOf(ref char searchSpace, char value, int length) => - IndexOf>(ref Unsafe.As(ref searchSpace), (short)value, length); + IndexOf, NopTransform>(ref Unsafe.As(ref searchSpace), (short)value, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAnyExcept(ref char searchSpace, char value, int length) => - IndexOf>(ref Unsafe.As(ref searchSpace), (short)value, length); + IndexOf, NopTransform>(ref Unsafe.As(ref searchSpace), (short)value, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAny(ref char searchSpace, char value0, char value1, int length) => - IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, length); + IndexOfAny, NopTransform>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAnyExcept(ref char searchSpace, char value0, char value1, int length) => - IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, length); + IndexOfAny, NopTransform>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Sse2))] @@ -64,6 +62,44 @@ public static int IndexOfAny(ref char searchSpace, char value0, char value1, cha public static int IndexOfAnyExcept(ref char searchSpace, char value0, char value1, char value2, int length) => IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, (short)value2, length); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + public static int IndexOfAnyIgnoreCase(ref char searchSpace, char value, int length) + { + Debug.Assert((value | 0x20) == value); + + return IndexOf, Or20Transform>(ref Unsafe.As(ref searchSpace), (short)value, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + public static int IndexOfAnyExceptIgnoreCase(ref char searchSpace, char value, int length) + { + Debug.Assert((value | 0x20) == value); + + return IndexOf, Or20Transform>(ref Unsafe.As(ref searchSpace), (short)value, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + public static int IndexOfAnyIgnoreCase(ref char searchSpace, char value0, char value1, int length) + { + Debug.Assert((value0 | 0x20) == value0); + Debug.Assert((value1 | 0x20) == value1); + + return IndexOfAny, Or20Transform>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + public static int IndexOfAnyExceptIgnoreCase(ref char searchSpace, char value0, char value1, int length) + { + Debug.Assert((value0 | 0x20) == value0); + Debug.Assert((value1 | 0x20) == value1); + + return IndexOfAny, Or20Transform>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, length); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAnyInRange(ref char searchSpace, char lowInclusive, char rangeInclusive, int length) => @@ -277,8 +313,9 @@ public static bool Contains(ref short searchSpace, short value, int length) } [CompExactlyDependsOn(typeof(Sse2))] - private static int IndexOf(ref short searchSpace, short value, int length) + private static int IndexOf(ref short searchSpace, short value, int length) where TNegator : struct, SpanHelpers.INegator + where TTransform : struct, ITransform { Debug.Assert(CanUsePackedIndexOf(value)); @@ -290,10 +327,10 @@ private static int IndexOf(ref short searchSpace, short value, int len { length -= 4; - if (TNegator.NegateIfNeeded(searchSpace == value)) return 0; - if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, 1) == value)) return 1; - if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, 2) == value)) return 2; - if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, 3) == value)) return 3; + if (TNegator.NegateIfNeeded(TTransform.TransformInput(searchSpace) == value)) return 0; + if (TNegator.NegateIfNeeded(TTransform.TransformInput(Unsafe.Add(ref searchSpace, 1)) == value)) return 1; + if (TNegator.NegateIfNeeded(TTransform.TransformInput(Unsafe.Add(ref searchSpace, 2)) == value)) return 2; + if (TNegator.NegateIfNeeded(TTransform.TransformInput(Unsafe.Add(ref searchSpace, 3)) == value)) return 3; offset = 4; } @@ -302,7 +339,7 @@ private static int IndexOf(ref short searchSpace, short value, int len { length -= 1; - if (TNegator.NegateIfNeeded(Unsafe.Add(ref searchSpace, offset) == value)) return (int)offset; + if (TNegator.NegateIfNeeded(TTransform.TransformInput(Unsafe.Add(ref searchSpace, offset)) == value)) return (int)offset; offset += 1; } @@ -329,7 +366,7 @@ private static int IndexOf(ref short searchSpace, short value, int len { Vector512 source0 = Vector512.LoadUnsafe(ref currentSearchSpace); Vector512 source1 = Vector512.LoadUnsafe(ref currentSearchSpace, (nuint)Vector512.Count); - Vector512 packedSource = PackSources(source0, source1); + Vector512 packedSource = TTransform.TransformInput(PackSources(source0, source1)); if (HasMatch(packedValue, packedSource)) { @@ -352,7 +389,7 @@ private static int IndexOf(ref short searchSpace, short value, int len Vector512 source0 = Vector512.LoadUnsafe(ref firstVector); Vector512 source1 = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd); - Vector512 packedSource = PackSources(source0, source1); + Vector512 packedSource = TTransform.TransformInput(PackSources(source0, source1)); if (HasMatch(packedValue, packedSource)) { @@ -378,7 +415,7 @@ private static int IndexOf(ref short searchSpace, short value, int len { Vector256 source0 = Vector256.LoadUnsafe(ref currentSearchSpace); Vector256 source1 = Vector256.LoadUnsafe(ref currentSearchSpace, (nuint)Vector256.Count); - Vector256 packedSource = PackSources(source0, source1); + Vector256 packedSource = TTransform.TransformInput(PackSources(source0, source1)); Vector256 result = Vector256.Equals(packedValue, packedSource); result = NegateIfNeeded(result); @@ -403,7 +440,7 @@ private static int IndexOf(ref short searchSpace, short value, int len Vector256 source0 = Vector256.LoadUnsafe(ref firstVector); Vector256 source1 = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd); - Vector256 packedSource = PackSources(source0, source1); + Vector256 packedSource = TTransform.TransformInput(PackSources(source0, source1)); Vector256 result = Vector256.Equals(packedValue, packedSource); result = NegateIfNeeded(result); @@ -438,7 +475,7 @@ private static int IndexOf(ref short searchSpace, short value, int len { Vector128 source0 = Vector128.LoadUnsafe(ref currentSearchSpace); Vector128 source1 = Vector128.LoadUnsafe(ref currentSearchSpace, (nuint)Vector128.Count); - Vector128 packedSource = PackSources(source0, source1); + Vector128 packedSource = TTransform.TransformInput(PackSources(source0, source1)); Vector128 result = Vector128.Equals(packedValue, packedSource); result = NegateIfNeeded(result); @@ -463,7 +500,7 @@ private static int IndexOf(ref short searchSpace, short value, int len Vector128 source0 = Vector128.LoadUnsafe(ref firstVector); Vector128 source1 = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd); - Vector128 packedSource = PackSources(source0, source1); + Vector128 packedSource = TTransform.TransformInput(PackSources(source0, source1)); Vector128 result = Vector128.Equals(packedValue, packedSource); result = NegateIfNeeded(result); @@ -479,8 +516,9 @@ private static int IndexOf(ref short searchSpace, short value, int len } [CompExactlyDependsOn(typeof(Sse2))] - private static int IndexOfAny(ref short searchSpace, short value0, short value1, int length) + private static int IndexOfAny(ref short searchSpace, short value0, short value1, int length) where TNegator : struct, SpanHelpers.INegator + where TTransform : struct, ITransform { Debug.Assert(CanUsePackedIndexOf(value0)); Debug.Assert(CanUsePackedIndexOf(value1)); @@ -494,13 +532,13 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho { length -= 4; - lookUp = searchSpace; + lookUp = TTransform.TransformInput(searchSpace); if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1)) return 0; - lookUp = Unsafe.Add(ref searchSpace, 1); + lookUp = TTransform.TransformInput(Unsafe.Add(ref searchSpace, 1)); if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1)) return 1; - lookUp = Unsafe.Add(ref searchSpace, 2); + lookUp = TTransform.TransformInput(Unsafe.Add(ref searchSpace, 2)); if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1)) return 2; - lookUp = Unsafe.Add(ref searchSpace, 3); + lookUp = TTransform.TransformInput(Unsafe.Add(ref searchSpace, 3)); if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1)) return 3; offset = 4; @@ -510,7 +548,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho { length -= 1; - lookUp = Unsafe.Add(ref searchSpace, offset); + lookUp = TTransform.TransformInput(Unsafe.Add(ref searchSpace, offset)); if (TNegator.NegateIfNeeded(lookUp == value0 || lookUp == value1)) return (int)offset; offset += 1; @@ -538,7 +576,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho { Vector512 source0 = Vector512.LoadUnsafe(ref currentSearchSpace); Vector512 source1 = Vector512.LoadUnsafe(ref currentSearchSpace, (nuint)Vector512.Count); - Vector512 packedSource = PackSources(source0, source1); + Vector512 packedSource = TTransform.TransformInput(PackSources(source0, source1)); Vector512 result = NegateIfNeeded(Vector512.Equals(packedValue0, packedSource) | Vector512.Equals(packedValue1, packedSource)); if (result != Vector512.Zero) @@ -562,7 +600,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho Vector512 source0 = Vector512.LoadUnsafe(ref firstVector); Vector512 source1 = Vector512.LoadUnsafe(ref oneVectorAwayFromEnd); - Vector512 packedSource = PackSources(source0, source1); + Vector512 packedSource = TTransform.TransformInput(PackSources(source0, source1)); Vector512 result = NegateIfNeeded(Vector512.Equals(packedValue0, packedSource) | Vector512.Equals(packedValue1, packedSource)); if (result != Vector512.Zero) @@ -590,7 +628,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho { Vector256 source0 = Vector256.LoadUnsafe(ref currentSearchSpace); Vector256 source1 = Vector256.LoadUnsafe(ref currentSearchSpace, (nuint)Vector256.Count); - Vector256 packedSource = PackSources(source0, source1); + Vector256 packedSource = TTransform.TransformInput(PackSources(source0, source1)); Vector256 result = Vector256.Equals(packedValue0, packedSource) | Vector256.Equals(packedValue1, packedSource); result = NegateIfNeeded(result); @@ -615,7 +653,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho Vector256 source0 = Vector256.LoadUnsafe(ref firstVector); Vector256 source1 = Vector256.LoadUnsafe(ref oneVectorAwayFromEnd); - Vector256 packedSource = PackSources(source0, source1); + Vector256 packedSource = TTransform.TransformInput(PackSources(source0, source1)); Vector256 result = Vector256.Equals(packedValue0, packedSource) | Vector256.Equals(packedValue1, packedSource); result = NegateIfNeeded(result); @@ -651,7 +689,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho { Vector128 source0 = Vector128.LoadUnsafe(ref currentSearchSpace); Vector128 source1 = Vector128.LoadUnsafe(ref currentSearchSpace, (nuint)Vector128.Count); - Vector128 packedSource = PackSources(source0, source1); + Vector128 packedSource = TTransform.TransformInput(PackSources(source0, source1)); Vector128 result = Vector128.Equals(packedValue0, packedSource) | Vector128.Equals(packedValue1, packedSource); result = NegateIfNeeded(result); @@ -676,7 +714,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho Vector128 source0 = Vector128.LoadUnsafe(ref firstVector); Vector128 source1 = Vector128.LoadUnsafe(ref oneVectorAwayFromEnd); - Vector128 packedSource = PackSources(source0, source1); + Vector128 packedSource = TTransform.TransformInput(PackSources(source0, source1)); Vector128 result = Vector128.Equals(packedValue0, packedSource) | Vector128.Equals(packedValue1, packedSource); result = NegateIfNeeded(result); @@ -1283,5 +1321,29 @@ internal static Vector512 FixUpPackedVector512Result(Vector512 resul // We want to preserve the order of the two input vectors, so we deinterleave the packed value. return Avx512F.PermuteVar8x64(result.AsInt64(), Vector512.Create(0, 2, 4, 6, 1, 3, 5, 7)).AsByte(); } + + private interface ITransform + { + static abstract short TransformInput(short input); + static abstract Vector128 TransformInput(Vector128 input); + static abstract Vector256 TransformInput(Vector256 input); + static abstract Vector512 TransformInput(Vector512 input); + } + + private readonly struct NopTransform : ITransform + { + public static short TransformInput(short input) => input; + public static Vector128 TransformInput(Vector128 input) => input; + public static Vector256 TransformInput(Vector256 input) => input; + public static Vector512 TransformInput(Vector512 input) => input; + } + + private readonly struct Or20Transform : ITransform + { + public static short TransformInput(short input) => (short)(input | 0x20); + public static Vector128 TransformInput(Vector128 input) => input | Vector128.Create((byte)0x20); + public static Vector256 TransformInput(Vector256 input) => input | Vector256.Create((byte)0x20); + public static Vector512 TransformInput(Vector512 input) => input | Vector512.Create((byte)0x20); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs index 2cc6dc4405645d..da77d42320a98a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs @@ -1672,6 +1672,15 @@ private static unsafe int IndexOfAnyValueType(ref TValue searc { if (PackedSpanHelpers.PackedIndexOfIsSupported && typeof(TValue) == typeof(short) && PackedSpanHelpers.CanUsePackedIndexOf(value0) && PackedSpanHelpers.CanUsePackedIndexOf(value1)) { + if ((*(char*)&value0 ^ *(char*)&value1) == 0x20) + { + char lowerCase = (char)Math.Max(*(char*)&value0, *(char*)&value1); + + return typeof(TNegator) == typeof(DontNegate) + ? PackedSpanHelpers.IndexOfAnyIgnoreCase(ref Unsafe.As(ref searchSpace), lowerCase, length) + : PackedSpanHelpers.IndexOfAnyExceptIgnoreCase(ref Unsafe.As(ref searchSpace), lowerCase, length); + } + return typeof(TNegator) == typeof(DontNegate) ? PackedSpanHelpers.IndexOfAny(ref Unsafe.As(ref searchSpace), *(char*)&value0, *(char*)&value1, length) : PackedSpanHelpers.IndexOfAnyExcept(ref Unsafe.As(ref searchSpace), *(char*)&value0, *(char*)&value1, length); diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Searching.cs b/src/libraries/System.Private.CoreLib/src/System/String.Searching.cs index 2affdb008575a3..56d471168632d9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.Searching.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.Searching.cs @@ -84,10 +84,10 @@ private int IndexOfCharOrdinalIgnoreCase(char value) if (char.IsAsciiLetter(value)) { - char valueUc = (char)(value | 0x20); - char valueLc = (char)(value & ~0x20); + char valueLc = (char)(value | 0x20); + char valueUc = (char)(value & ~0x20); return PackedSpanHelpers.PackedIndexOfIsSupported - ? PackedSpanHelpers.IndexOfAny(ref _firstChar, valueLc, valueUc, Length) + ? PackedSpanHelpers.IndexOfAnyIgnoreCase(ref _firstChar, valueLc, Length) : SpanHelpers.IndexOfAnyChar(ref _firstChar, valueLc, valueUc, Length); } diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs index 58abbb8b181eac..bac950c6db2f97 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs @@ -920,20 +920,10 @@ void EmitFixedSet_LeftToRight() Call(primarySet.Negated ? s_spanIndexOfAnyExceptCharCharChar : s_spanIndexOfAnyCharCharChar); break; - case 4 or 5: - // tmp = ...IndexOfAny("abcd"); - // Note that this case differs slightly from the source generator, where it might choose to use - // SearchValues instead of a literal, but there's extra cost to doing so for RegexCompiler so - // it just always uses IndexOfAny(span). - Ldstr(new string(primarySet.Chars)); - Call(s_stringAsSpanMethod); - Call(primarySet.Negated ? s_spanIndexOfAnyExceptSpan : s_spanIndexOfAnySpan); - break; - default: + // tmp = ...IndexOfAny(setChars); // tmp = ...IndexOfAny(s_searchValues); - LoadSearchValues(primarySet.Chars); - Call(primarySet.Negated ? s_spanIndexOfAnyExceptSearchValues : s_spanIndexOfAnySearchValues); + EmitIndexOfAnyWithSearchValuesOrLiteral(new string(primarySet.Chars), except: primarySet.Negated); break; } } @@ -3619,9 +3609,7 @@ literal.SetChars is not null || case (true, _): // startingPos = slice.IndexOfAny(literal.SetChars); - Ldstr(literal.SetChars); - Call(s_stringAsSpanMethod); - Call(s_spanIndexOfAnySpan); + EmitIndexOfAnyWithSearchValuesOrLiteral(literal.SetChars); break; case (false, 2): @@ -3634,9 +3622,7 @@ literal.SetChars is not null || case (false, _): // startingPos = slice.IndexOfAny($"{node.Ch}{literal.SetChars}"); - Ldstr($"{node.Ch}{literal.SetChars}"); - Call(s_stringAsSpanMethod); - Call(s_spanIndexOfAnySpan); + EmitIndexOfAnyWithSearchValuesOrLiteral($"{node.Ch}{literal.SetChars}"); break; } } @@ -3650,8 +3636,7 @@ literal.SetChars is not null || Array.Resize(ref asciiChars, asciiChars.Length + 1); asciiChars[^1] = node.Ch; } - LoadSearchValues(asciiChars); - Call(s_spanIndexOfAnySearchValues); + EmitIndexOfAnyWithSearchValuesOrLiteral(new string(asciiChars)); } else if (literal.Range.LowInclusive == literal.Range.HighInclusive) // single char from a RegexNode.One { @@ -5274,15 +5259,7 @@ void EmitIndexOf(RegexNode node, bool useLast, bool negate) return; default: - Ldstr(setChars.ToString()); - Call(s_stringAsSpanMethod); - Call((useLast, negated) switch - { - (false, false) => s_spanIndexOfAnySpan, - (false, true) => s_spanIndexOfAnyExceptSpan, - (true, false) => s_spanLastIndexOfAnySpan, - (true, true) => s_spanLastIndexOfAnyExceptSpan, - }); + EmitIndexOfAnyWithSearchValuesOrLiteral(setChars.ToString(), last: useLast, except: negated); return; } } @@ -5290,14 +5267,7 @@ void EmitIndexOf(RegexNode node, bool useLast, bool negate) // IndexOfAny{Except}(SearchValues) if (RegexCharClass.TryGetAsciiSetChars(node.Str, out char[]? asciiChars)) { - LoadSearchValues(asciiChars); - Call((useLast, negated) switch - { - (false, false) => s_spanIndexOfAnySearchValues, - (false, true) => s_spanIndexOfAnyExceptSearchValues, - (true, false) => s_spanLastIndexOfAnySearchValues, - (true, true) => s_spanLastIndexOfAnyExceptSearchValues, - }); + EmitIndexOfAnyWithSearchValuesOrLiteral(new string(asciiChars), last: useLast, except: negated); return; } } @@ -6192,6 +6162,38 @@ private void EmitTimeoutCheckIfNeeded() } } + /// Emits a call to either IndexOfAny("abcd") or IndexOfAny(SearchValues) depending on the . + private void EmitIndexOfAnyWithSearchValuesOrLiteral(string chars, bool last = false, bool except = false) + { + Debug.Assert(chars.Length > 3); + + // SearchValues is faster than a regular IndexOfAny("abcd") for sets of 4/5 values iff they are ASCII. + // Only emit SearchValues instances when we know they'll be faster to avoid increasing the startup cost too much. + if (chars.Length is 4 or 5 && !RegexCharClass.IsAscii(chars)) + { + Ldstr(chars); + Call(s_stringAsSpanMethod); + Call((last, except) switch + { + (false, false) => s_spanIndexOfAnySpan, + (false, true) => s_spanIndexOfAnyExceptSpan, + (true, false) => s_spanLastIndexOfAnySpan, + (true, true) => s_spanLastIndexOfAnyExceptSpan, + }); + } + else + { + LoadSearchValues(chars.ToCharArray()); + Call((last, except) switch + { + (false, false) => s_spanIndexOfAnySearchValues, + (false, true) => s_spanIndexOfAnyExceptSearchValues, + (true, false) => s_spanLastIndexOfAnySearchValues, + (true, true) => s_spanLastIndexOfAnyExceptSearchValues, + }); + } + } + /// /// Adds an entry in for the given and emits a load of that initialized value. /// From 2f43856e78faebe7347fdd76acc26de7ffaec539 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 17 Feb 2024 21:32:49 -0800 Subject: [PATCH 107/158] Include namespace in the reflection error message (#98617) --- .../Internal/Reflection/Core/Execution/MethodBaseInvoker.cs | 2 +- .../src/System/Reflection/FieldAccessor.cs | 6 +++--- .../src/System/Reflection/MethodInvokerCommon.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodBaseInvoker.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodBaseInvoker.cs index a14c0007dc8f10..9a33ed504cd45d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodBaseInvoker.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/MethodBaseInvoker.cs @@ -57,7 +57,7 @@ protected static void ValidateThis(object thisObject, RuntimeTypeHandle declarin throw new TargetException(SR.RFLCT_Targ_StatMethReqTarg); if (!RuntimeAugments.IsAssignable(thisObject, declaringTypeHandle)) - throw new TargetException(SR.Format(SR.RFLCT_Targ_ITargMismatch_WithType, declaringTypeHandle.GetRuntimeTypeInfoForRuntimeTypeHandle().Name, thisObject.GetType().Name)); + throw new TargetException(SR.Format(SR.RFLCT_Targ_ITargMismatch_WithType, declaringTypeHandle.GetRuntimeTypeInfoForRuntimeTypeHandle(), thisObject.GetType())); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldAccessor.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldAccessor.cs index 2370d8fff263a7..cbe7f96a9ef36d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldAccessor.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/FieldAccessor.cs @@ -381,7 +381,7 @@ internal void VerifyInitOnly() if ((_fieldInfo.Attributes & FieldAttributes.InitOnly) == FieldAttributes.InitOnly && _fieldAccessType != FieldAccessorType.SlowPathUntilClassInitialized) { - ThrowHelperFieldAccessException(_fieldInfo.Name, _fieldInfo.DeclaringType?.FullName); + ThrowHelperFieldAccessException(_fieldInfo); } } @@ -480,8 +480,8 @@ private static FieldAccessorType GetIntPtrAccessorTypeForStatic() private static void ThrowHelperArgumentException(object target, FieldInfo fieldInfo) => throw new ArgumentException(SR.Format(SR.Arg_FieldDeclTarget, fieldInfo.Name, fieldInfo.DeclaringType, target.GetType())); - private static void ThrowHelperFieldAccessException(string fieldName, string? declaringTypeName) => - throw new FieldAccessException(SR.Format(SR.RFLCT_CannotSetInitonlyStaticField, fieldName, declaringTypeName)); + private static void ThrowHelperFieldAccessException(FieldInfo fieldInfo) => + throw new FieldAccessException(SR.Format(SR.RFLCT_CannotSetInitonlyStaticField, fieldInfo.Name, fieldInfo.DeclaringType)); private enum FieldAccessorType { diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs index 4eda76d9364b75..9e0232c1f4fb95 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvokerCommon.cs @@ -96,7 +96,7 @@ internal static void ValidateInvokeTarget(object? target, MethodBase method) if (!method.DeclaringType!.IsInstanceOfType(target)) { - throw new TargetException(SR.Format(SR.RFLCT_Targ_ITargMismatch_WithType, method.DeclaringType.Name, target.GetType().Name)); + throw new TargetException(SR.Format(SR.RFLCT_Targ_ITargMismatch_WithType, method.DeclaringType, target.GetType())); } } From d7acf9cd0bb995b0ba074a0808d383069bcdba18 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Sun, 18 Feb 2024 12:54:32 +0100 Subject: [PATCH 108/158] [browser][MT] JSType.OneWay fire and forget (#98567) --- .../workflow/debugging/mono/wasm-debugging.md | 8 +- .../src/Interop/Browser/Interop.Runtime.cs | 8 +- .../JSImportGenerator/JSGeneratorFactory.cs | 6 + .../gen/JSImportGenerator/JSTypeFlags.cs | 1 + .../JSImportGenerator/Resources/Strings.resx | 3 + .../src/CompatibilitySuppressions.xml | 12 ++ .../JavaScript/Interop/JavaScriptExports.cs | 45 +++--- .../Interop/JavaScriptImports.Generated.cs | 1 + .../JavaScript/JSFunctionBinding.cs | 33 +++-- .../JavaScript/JSHostImplementation.cs | 1 + .../JavaScript/JSMarshalerArgument.cs | 7 + .../JavaScript/JSMarshalerType.cs | 9 ++ .../JavaScript/JSProxyContext.cs | 5 +- .../JavaScript/JSSynchronizationContext.cs | 2 +- .../InteropServices/JavaScript/JSType.cs | 9 ++ .../JavaScript/MarshalerType.cs | 1 + .../JavaScript/JSExportTest.cs | 11 ++ .../JavaScript/JSImportTest.cs | 9 ++ .../JavaScript/JavaScriptTestHelper.cs | 16 +++ .../JavaScript/JavaScriptTestHelper.mjs | 6 + .../JavaScript/WebWorkerTestHelper.cs | 4 + .../tests/debugger-test/debugger-main.js | 6 +- src/mono/browser/runtime/corebindings.c | 44 +++--- src/mono/browser/runtime/cwraps.ts | 9 +- src/mono/browser/runtime/debug.ts | 2 +- src/mono/browser/runtime/driver.c | 33 ++++- src/mono/browser/runtime/exports-binding.ts | 7 +- src/mono/browser/runtime/invoke-cs.ts | 91 +++++++----- src/mono/browser/runtime/invoke-js.ts | 131 +++++++++--------- src/mono/browser/runtime/managed-exports.ts | 67 ++++++--- src/mono/browser/runtime/marshal-to-cs.ts | 13 +- src/mono/browser/runtime/marshal-to-js.ts | 10 +- src/mono/browser/runtime/marshal.ts | 13 +- src/mono/browser/runtime/memory.ts | 7 + .../shared/emscripten-replacements.ts | 1 + .../browser/runtime/pthreads/shared/index.ts | 10 +- src/mono/browser/runtime/startup.ts | 4 +- src/mono/browser/runtime/strings.ts | 3 +- src/mono/browser/runtime/types/internal.ts | 5 + src/mono/mono/utils/mono-threads-wasm.c | 22 +-- src/mono/mono/utils/mono-threads-wasm.h | 15 -- 41 files changed, 446 insertions(+), 244 deletions(-) diff --git a/docs/workflow/debugging/mono/wasm-debugging.md b/docs/workflow/debugging/mono/wasm-debugging.md index 59014ef147e28d..80b06319eb36e1 100644 --- a/docs/workflow/debugging/mono/wasm-debugging.md +++ b/docs/workflow/debugging/mono/wasm-debugging.md @@ -180,8 +180,8 @@ $func166 @ dotnet.wasm:0xba0a $func2810 @ dotnet.wasm:0xabacf $func1615 @ dotnet.wasm:0x6f8eb $func1619 @ dotnet.wasm:0x6ff58 -$mono_wasm_invoke_method @ dotnet.wasm:0x96c9 -Module._mono_wasm_invoke_method @ dotnet.6.0.1.hopd7ipo8x.js:1 +$mono_wasm_invoke_jsexport @ dotnet.wasm:0x96c9 +Module.mono_wasm_invoke_jsexport @ dotnet.6.0.1.hopd7ipo8x.js:1 managed__Microsoft_AspNetCore_Components_WebAssembly__Microsoft_AspNetCore_Components_WebAssembly_Services_DefaultWebAssemblyJSRuntime_BeginInvokeDotNet @ managed__Microsoft_AspNetCore_Components_WebAssembly__Microsoft_AspNetCore_Components_WebAssembly_Services_DefaultWebAssemblyJSRuntime_BeginInvokeDotNet:19 beginInvokeDotNetFromJS @ blazor.webassembly.js:1 b @ blazor.webassembly.js:1 @@ -244,8 +244,8 @@ $mono_jit_runtime_invoke @ dotnet.wasm:0x1dec32 $do_runtime_invoke @ dotnet.wasm:0x95fca $mono_runtime_try_invoke @ dotnet.wasm:0x966fe $mono_runtime_invoke @ dotnet.wasm:0x98982 -$mono_wasm_invoke_method @ dotnet.wasm:0x227de2 -Module._mono_wasm_invoke_method @ dotnet..y6ggkhlo8e.js:9927 +$mono_wasm_invoke_jsexport @ dotnet.wasm:0x227de2 +Module.mono_wasm_invoke_jsexport @ dotnet..y6ggkhlo8e.js:9927 managed__Microsoft_AspNetCore_Components_WebAssembly__Microsoft_AspNetCore_Components_WebAssembly_Services_DefaultWebAssemblyJSRuntime_BeginInvokeDotNet @ managed__Microsoft_AspNetCore_Components_WebAssembly__Microsoft_AspNetCore_Components_WebAssembly_Services_DefaultWebAssemblyJSRuntime_BeginInvokeDotNet:19 beginInvokeDotNetFromJS @ blazor.webassembly.js:1 b @ blazor.webassembly.js:1 diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 23067f81ea601c..f66afe40b4661b 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -47,11 +47,11 @@ internal static unsafe partial class Runtime public static extern void UninstallWebWorkerInterop(); [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void InvokeJSImportSync(nint data, nint signature); + public static extern void InvokeJSImportSync(nint signature, nint args); [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void InvokeJSImportSyncSend(nint targetNativeTID, nint data, nint signature); + public static extern void InvokeJSImportSyncSend(nint targetNativeTID, nint signature, nint args); [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void InvokeJSImportAsyncPost(nint targetNativeTID, nint data, nint signature); + public static extern void InvokeJSImportAsyncPost(nint targetNativeTID, nint signature, nint args); [MethodImpl(MethodImplOptions.InternalCall)] public static extern void CancelPromise(nint taskHolderGCHandle); [MethodImpl(MethodImplOptions.InternalCall)] @@ -60,7 +60,7 @@ internal static unsafe partial class Runtime [MethodImpl(MethodImplOptions.InternalCall)] public static extern unsafe void BindJSImport(void* signature, out int is_exception, out object result); [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void InvokeJSImport(int importHandle, nint data); + public static extern void InvokeJSImportST(int importHandle, nint args); [MethodImpl(MethodImplOptions.InternalCall)] public static extern void CancelPromise(nint gcHandle); #endif diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs index a1cbebb3fda5a2..9d0577cb7d5417 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs @@ -42,6 +42,8 @@ ResolvedGenerator fail(string failReason) return ResolvedGenerator.NotSupported(new(info, context)); // void + case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.OneWay }: + return ResolvedGenerator.Resolved(new VoidGenerator(MarshalerType.OneWay)); case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Discard }: case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Void }: case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.None }: @@ -52,6 +54,10 @@ ResolvedGenerator fail(string failReason) case { JSType: JSTypeFlags.Discard }: return fail(SR.DiscardOnlyVoid); + // oneway no void + case { JSType: JSTypeFlags.OneWay }: + return fail(SR.OneWayOnlyVoid); + // primitive case { TypeInfo: JSSimpleTypeInfo simple }: return Create(info, isToJs, simple.KnownType, Array.Empty(), jsMarshalingInfo.JSType, Array.Empty(), fail); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSTypeFlags.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSTypeFlags.cs index ab498c62d55fdb..e39fbe19425ba5 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSTypeFlags.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSTypeFlags.cs @@ -22,6 +22,7 @@ internal enum JSTypeFlags : int MemoryView = 0x800, Any = 0x1000, Discard = 0x2000, + OneWay = 0x4000, Missing = 0x4000_0000, } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Resources/Strings.resx index 1c6e47ef214f15..1e27fdcd8bb969 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Resources/Strings.resx @@ -184,6 +184,9 @@ 'JSType.Discard' could be only used with void return argument. + + 'JSType.OneWay' could be only used with void returning method. + Type {0} is not supported as argument of marshaled function. {0} is a type of the argument diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml index 93694b543159a5..b5cc7926a51696 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml @@ -12,4 +12,16 @@ ref/net9.0/System.Runtime.InteropServices.JavaScript.dll runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll + + CP0001 + T:System.Runtime.InteropServices.JavaScript.JSType.OneWay + ref/net9.0/System.Runtime.InteropServices.JavaScript.dll + runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll + + + CP0002 + M:System.Runtime.InteropServices.JavaScript.JSMarshalerType.get_OneWay + ref/net9.0/System.Runtime.InteropServices.JavaScript.dll + runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll + diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs index b244d69abdfb55..1cd0730d41f535 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; @@ -14,12 +16,11 @@ namespace System.Runtime.InteropServices.JavaScript // TODO: all the calls here should be running on deputy or TP in MT, not in UI thread internal static unsafe partial class JavaScriptExports { - // the marshaled signature is: - // Task? CallEntrypoint(char* assemblyNamePtr, string[] args) + // the marshaled signature is: Task? CallEntrypoint(char* assemblyNamePtr, string[] args) public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() - ref JSMarshalerArgument arg_result = ref arguments_buffer[1]; // initialized by caller in alloc_stack_frame() + ref JSMarshalerArgument arg_res = ref arguments_buffer[1]; // initialized by caller in alloc_stack_frame() ref JSMarshalerArgument arg_1 = ref arguments_buffer[2]; // initialized and set by caller ref JSMarshalerArgument arg_2 = ref arguments_buffer[3]; // initialized and set by caller ref JSMarshalerArgument arg_3 = ref arguments_buffer[4]; // initialized and set by caller @@ -28,6 +29,7 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer) #if FEATURE_WASM_MANAGED_THREADS // when we arrive here, we are on the thread which owns the proxies arg_exc.AssertCurrentThreadContext(); + Debug.Assert(arg_res.slot.Type == MarshalerType.TaskPreCreated); #endif arg_1.ToManaged(out IntPtr assemblyNamePtr); @@ -36,7 +38,7 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer) Task? result = JSHostImplementation.CallEntrypoint(assemblyNamePtr, args, waitForDebugger); - arg_result.ToJS(result, (ref JSMarshalerArgument arg, int value) => + arg_res.ToJS(result, (ref JSMarshalerArgument arg, int value) => { arg.ToJS(value); }); @@ -47,6 +49,7 @@ public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer) } } + // the marshaled signature is: void LoadLazyAssembly(byte[] dll, byte[] pdb) public static void LoadLazyAssembly(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; @@ -70,6 +73,7 @@ public static void LoadLazyAssembly(JSMarshalerArgument* arguments_buffer) } } + // the marshaled signature is: void LoadSatelliteAssembly(byte[] dll) public static void LoadSatelliteAssembly(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; @@ -91,10 +95,8 @@ public static void LoadSatelliteAssembly(JSMarshalerArgument* arguments_buffer) } } - // The JS layer invokes this method when the JS wrapper for a JS owned object - // has been collected by the JS garbage collector - // the marshaled signature is: - // void ReleaseJSOwnedObjectByGCHandle(GCHandle gcHandle) + // The JS layer invokes this method when the JS wrapper for a JS owned object has been collected by the JS garbage collector + // the marshaled signature is: void ReleaseJSOwnedObjectByGCHandle(GCHandle gcHandle) public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() @@ -112,8 +114,7 @@ public static void ReleaseJSOwnedObjectByGCHandle(JSMarshalerArgument* arguments } } - // the marshaled signature is: - // TRes? CallDelegate(GCHandle callback, T1? arg1, T2? arg2, T3? arg3) + // the marshaled signature is: TRes? CallDelegate(GCHandle callback, T1? arg1, T2? arg2, T3? arg3) public static void CallDelegate(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by JS caller in alloc_stack_frame() @@ -149,8 +150,7 @@ public static void CallDelegate(JSMarshalerArgument* arguments_buffer) } } - // the marshaled signature is: - // void CompleteTask(GCHandle holder, Exception? exceptionResult, T? result) + // the marshaled signature is: void CompleteTask(GCHandle holder, Exception? exceptionResult, T? result) public static void CompleteTask(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() @@ -209,12 +209,11 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer) } } - // the marshaled signature is: - // string GetManagedStackTrace(GCHandle exception) + // the marshaled signature is: string GetManagedStackTrace(GCHandle exception) public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() - ref JSMarshalerArgument arg_return = ref arguments_buffer[1]; // used as return value + ref JSMarshalerArgument arg_res = ref arguments_buffer[1]; // used as return value ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller try { @@ -224,7 +223,7 @@ public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer) GCHandle exception_gc_handle = (GCHandle)arg_1.slot.GCHandle; if (exception_gc_handle.Target is Exception exception) { - arg_return.ToJS(exception.StackTrace); + arg_res.ToJS(exception.StackTrace); } else { @@ -241,19 +240,18 @@ public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer) // this is here temporarily, until JSWebWorker becomes public API [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicMethods, "System.Runtime.InteropServices.JavaScript.JSWebWorker", "System.Runtime.InteropServices.JavaScript")] - // the marshaled signature is: - // void InstallMainSynchronizationContext(nint jsNativeTID, out GCHandle contextHandle) + // the marshaled signature is: GCHandle InstallMainSynchronizationContext(nint jsNativeTID) public static void InstallMainSynchronizationContext(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() + ref JSMarshalerArgument arg_res = ref arguments_buffer[1];// initialized and set by caller ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller - ref JSMarshalerArgument arg_2 = ref arguments_buffer[3];// initialized and set by caller try { var jsSynchronizationContext = JSSynchronizationContext.InstallWebWorkerInterop(true, CancellationToken.None); jsSynchronizationContext.ProxyContext.JSNativeTID = arg_1.slot.IntPtrValue; - arg_2.slot.GCHandle = jsSynchronizationContext.ProxyContext.ContextHandle; + arg_res.slot.GCHandle = jsSynchronizationContext.ProxyContext.ContextHandle; } catch (Exception ex) { @@ -263,12 +261,11 @@ public static void InstallMainSynchronizationContext(JSMarshalerArgument* argume #endif - // the marshaled signature is: - // Task BindAssemblyExports(string assemblyName) + // the marshaled signature is: Task BindAssemblyExports(string assemblyName) public static void BindAssemblyExports(JSMarshalerArgument* arguments_buffer) { ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() - ref JSMarshalerArgument arg_result = ref arguments_buffer[1]; // used as return value + ref JSMarshalerArgument arg_res = ref arguments_buffer[1]; // used as return value ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller try { @@ -279,7 +276,7 @@ public static void BindAssemblyExports(JSMarshalerArgument* arguments_buffer) var result = JSHostImplementation.BindAssemblyExports(assemblyName); - arg_result.ToJS(result); + arg_res.ToJS(result); } catch (Exception ex) { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs index 2e41abd76c5c1e..db356f8afe4c20 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs @@ -57,6 +57,7 @@ internal static unsafe partial class JavaScriptImports #if DEBUG [JSImport("globalThis.console.log")] + [return: JSMarshalAs] public static partial void Log([JSMarshalAs] string message); #endif } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs index ae432cafc323cc..43afa95f76b1dd 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs @@ -30,6 +30,7 @@ internal JSFunctionBinding() { } internal static volatile uint nextImportHandle = 1; internal int ImportHandle; internal bool IsAsync; + internal bool IsOneWay; #if DEBUG internal string? FunctionName; #endif @@ -285,6 +286,11 @@ internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span arguments[1].slot.GCHandle = holder.GCHandle; } + if (signature.IsOneWay) + { + arguments[1].slot.Type = MarshalerType.OneWay; + } + #if FEATURE_WASM_MANAGED_THREADS // if we are on correct thread already or this is synchronous call, just call it if (targetContext.IsCurrentThread()) @@ -299,15 +305,15 @@ internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span #endif } - else if (!signature.IsAsync) + else if (signature.IsAsync || signature.IsOneWay) { - //sync - DispatchJSImportSyncSend(signature, targetContext, arguments); + //async + DispatchJSImportAsyncPost(signature, targetContext, arguments); } else { - //async - DispatchJSImportAsyncPost(signature, targetContext, arguments); + //sync + DispatchJSImportSyncSend(signature, targetContext, arguments); } #else InvokeJSImportCurrent(signature, arguments); @@ -332,9 +338,9 @@ internal static unsafe void InvokeJSImportCurrent(JSFunctionBinding signature, S fixed (JSMarshalerArgument* args = arguments) { #if FEATURE_WASM_MANAGED_THREADS - Interop.Runtime.InvokeJSImportSync((nint)args, (nint)signature.Header); + Interop.Runtime.InvokeJSImportSync((nint)signature.Header, (nint)args); #else - Interop.Runtime.InvokeJSImport(signature.ImportHandle, (nint)args); + Interop.Runtime.InvokeJSImportST(signature.ImportHandle, (nint)args); #endif } @@ -361,7 +367,7 @@ internal static unsafe void DispatchJSImportSyncSend(JSFunctionBinding signature // we also don't throw PNSE here, because we know that the target has JS interop installed and that it could not block // so it could take some time, while target is CPU busy, but not forever // see also https://github.com/dotnet/runtime/issues/76958#issuecomment-1921418290 - Interop.Runtime.InvokeJSImportSyncSend(targetContext.JSNativeTID, args, sig); + Interop.Runtime.InvokeJSImportSyncSend(targetContext.JSNativeTID, sig, args); ref JSMarshalerArgument exceptionArg = ref arguments[0]; if (exceptionArg.slot.Type != MarshalerType.None) @@ -375,7 +381,10 @@ internal static unsafe void DispatchJSImportSyncSend(JSFunctionBinding signature #endif internal static unsafe void DispatchJSImportAsyncPost(JSFunctionBinding signature, JSProxyContext targetContext, Span arguments) { - // this copy is freed in mono_wasm_invoke_import_async + // meaning JS side needs to dispose it + ref JSMarshalerArgument exc = ref arguments[0]; + exc.slot.ReceiverShouldFree = true; + var bytes = sizeof(JSMarshalerArgument) * arguments.Length; void* cpy = (void*)Marshal.AllocHGlobal(bytes); void* src = Unsafe.AsPointer(ref arguments[0]); @@ -385,7 +394,7 @@ internal static unsafe void DispatchJSImportAsyncPost(JSFunctionBinding signatur // we already know that we are not on the right thread // this will return quickly after sending the message // async - Interop.Runtime.InvokeJSImportAsyncPost(targetContext.JSNativeTID, (nint)cpy, sig); + Interop.Runtime.InvokeJSImportAsyncPost(targetContext.JSNativeTID, sig, (nint)cpy); } @@ -431,8 +440,8 @@ internal static unsafe void ResolveOrRejectPromise(JSProxyContext targetContext, else { // meaning JS side needs to dispose it - ref JSMarshalerArgument res = ref arguments[1]; - res.slot.BooleanValue = true; + ref JSMarshalerArgument exc = ref arguments[0]; + exc.slot.ReceiverShouldFree = true; // this copy is freed in mono_wasm_resolve_or_reject_promise var bytes = sizeof(JSMarshalerArgument) * arguments.Length; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs index 27a6d548868929..335aa72371eeeb 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs @@ -153,6 +153,7 @@ public static unsafe JSFunctionBinding GetMethodSignature(ReadOnlySpan @@ -74,6 +79,7 @@ public unsafe void Initialize() // also this is called multiple times JSProxyContext.JSImportWithUnknownContext(); slot.ContextHandle = IntPtr.Zero; + slot.ReceiverShouldFree = false; #endif } @@ -83,6 +89,7 @@ internal unsafe void InitializeWithContext(JSProxyContext knownProxyContext) { slot.Type = MarshalerType.None; slot.ContextHandle = knownProxyContext.ContextHandle; + slot.ReceiverShouldFree = false; } #endif // this is always called from ToManaged() marshaler diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerType.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerType.cs index b76ffdf9d61592..16e4cbf11c9a28 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerType.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSMarshalerType.cs @@ -44,6 +44,15 @@ private JSMarshalerType(JSFunctionBinding.JSBindingType signatureType) Type = MarshalerType.Discard }); + /// + /// Dispatches the call asynchronously and doesn't wait for result. + /// + /// The marshaler metadata. + public static JSMarshalerType OneWay { get; } = new JSMarshalerType(new JSFunctionBinding.JSBindingType + { + Type = MarshalerType.OneWay + }); + /// /// Marshal as JavaScript Boolean type. /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs index c33ee713acddba..e03f924de110ce 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs @@ -36,7 +36,8 @@ private JSProxyContext() #else public nint ContextHandle; public nint JSNativeTID; // target thread where JavaScript is running - public int ManagedTID; + public nint NativeTID; // current pthread id + public int ManagedTID; // current managed thread id public bool IsMainThread; public JSSynchronizationContext SynchronizationContext; @@ -58,7 +59,7 @@ public static IntPtr GetNativeThreadId() public JSProxyContext(bool isMainThread, JSSynchronizationContext synchronizationContext) { SynchronizationContext = synchronizationContext; - JSNativeTID = GetNativeThreadId(); + NativeTID = JSNativeTID = GetNativeThreadId(); ManagedTID = Environment.CurrentManagedThreadId; IsMainThread = isMainThread; ContextHandle = (nint)GCHandle.Alloc(this, GCHandleType.Normal); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs index 3953633580811f..b9c151ac79d08f 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs @@ -179,7 +179,7 @@ private unsafe void ScheduleJSPump() { // While we COULD pump here, we don't want to. We want the pump to happen on the next event loop turn. // Otherwise we could get a chain where a pump generates a new work item and that makes us pump again, forever. - TargetThreadScheduleBackgroundJob(ProxyContext.JSNativeTID, (delegate* unmanaged[Cdecl])&BackgroundJobHandler); + TargetThreadScheduleBackgroundJob(ProxyContext.NativeTID, (delegate* unmanaged[Cdecl])&BackgroundJobHandler); } public override void Post(SendOrPostCallback d, object? state) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSType.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSType.cs index 1e2df771371961..8f9a5ef68f1563 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSType.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSType.cs @@ -28,6 +28,15 @@ public sealed class Discard : JSType internal Discard() { } } + /// + /// Could return immediately without waiting for the execution to finish, when dispatching the call to another thread. + /// Suppresses marshaling of the JavaScript function's return value. + /// + public sealed class OneWay : JSType + { + internal OneWay() { } + } + /// /// Marshal as JavaScript Boolean type. /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs index 25a52d82b2742e..d9f11387dedaa9 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs @@ -34,6 +34,7 @@ internal enum MarshalerType : byte Span, Action, Function, + OneWay, #if !JSIMPORTGENERATOR // only on runtime diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs index 990e755c33e1d6..30eadd7a7f2c77 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs @@ -24,6 +24,17 @@ await JsExportTestAsync(value, "boolean"); } + [Theory] + [MemberData(nameof(MarshalInt32Cases))] + public async Task JsExportInt32OneWay(int value) + { + JavaScriptTestHelper.optimizedReached=0; + + JavaScriptTestHelper.invoke1O(value); + await Task.Yield(); + Assert.Equal(value, JavaScriptTestHelper.optimizedReached); + } + private async Task JsExportTestAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] T>(T value , Func> invoke, string echoName, string jsType, string? jsClass = null) { diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs index b3c57387e46379..68598527681bdd 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs @@ -527,6 +527,15 @@ public void JsImportInt16(short value) #endregion Int16 #region Int32 + [Theory] + [MemberData(nameof(MarshalInt32Cases))] + public async Task JsImportInt32OneWay(int value) + { + JavaScriptTestHelper.store1OneWay_Int32(value); + await Task.Yield(); + Assert.Equal(value, JavaScriptTestHelper.retrieve1_Int32()); + } + [Theory] [MemberData(nameof(MarshalInt32Cases))] public void JsImportInt32(int value) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs index 3b3dff2974e782..6d6a484890782e 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs @@ -15,6 +15,7 @@ namespace System.Runtime.InteropServices.JavaScript.Tests public partial class JavaScriptTestHelper { [JSImport("globalThis.console.log")] + [return: JSMarshalAs] public static partial void Log([JSMarshalAs] string message); [JSImport("globalThis.window.location.toString")] @@ -27,6 +28,7 @@ public partial class JavaScriptTestHelper public static partial string ReboundMemberEcho(string message); [JSExport] + [return: JSMarshalAs] public static void ConsoleWriteLine([JSMarshalAs] string message) { Console.WriteLine(message); @@ -73,6 +75,15 @@ public static void Optimized1V(int a1) [JSImport("invoke1V", "JavaScriptTestHelper")] public static partial void invoke1V(int a1); + [JSExport] + [return: JSMarshalAs] + public static void Optimized1O(int a1) + { + optimizedReached += a1; + } + [JSImport("invoke1O", "JavaScriptTestHelper")] + public static partial void invoke1O(int a1); + [JSExport] public static int Optimized1R(int a1) { @@ -261,6 +272,11 @@ internal static partial void Relaxed(string a1, Exception ex, [JSImport("store1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial void store1_Int32([JSMarshalAs] int value); + + [JSImport("store1", "JavaScriptTestHelper")] + [return: JSMarshalAs] + internal static partial void store1OneWay_Int32([JSMarshalAs] int value); + [JSImport("retrieve1", "JavaScriptTestHelper")] [return: JSMarshalAs] internal static partial int retrieve1_Int32(); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs index 6b917e3f3fee0b..e0cf11376d8d34 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.mjs @@ -186,6 +186,12 @@ export function invoke1V(arg1) { fn(arg1); } +export function invoke1O(arg1) { + const JavaScriptTestHelper = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper; + const fn = JavaScriptTestHelper['Optimized1O']; + fn(arg1); +} + export function invoke1R(arg1) { const JavaScriptTestHelper = dllExports.System.Runtime.InteropServices.JavaScript.Tests.JavaScriptTestHelper; const fn = JavaScriptTestHelper['Optimized1R']; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTestHelper.cs index 35cdd8ff1858bd..96312c7aba35c8 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTestHelper.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTestHelper.cs @@ -325,6 +325,10 @@ public static Task RunOnThreadPool(Func job, CancellationToken cancellatio public static Task RunOnNewThread(Func job, CancellationToken cancellationToken) { + if( Environment.CurrentManagedThreadId == 1) + { + throw new Exception("This unit test should be executed with -backgroundExec otherwise it's prone to consume all threads too quickly"); + } TaskCompletionSource tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var thread = new Thread(() => { diff --git a/src/mono/browser/debugger/tests/debugger-test/debugger-main.js b/src/mono/browser/debugger/tests/debugger-test/debugger-main.js index f245ff64a33a2f..fcac7508184364 100644 --- a/src/mono/browser/debugger/tests/debugger-test/debugger-main.js +++ b/src/mono/browser/debugger/tests/debugger-test/debugger-main.js @@ -36,15 +36,15 @@ try { } } - // this is fake implementation of legacy `bind_static_method` which uses `mono_wasm_invoke_method` + // this is fake implementation of legacy `bind_static_method` which uses `mono_wasm_invoke_jsexport` // We have unit tests that stop on unhandled managed exceptions. - // as opposed to [JSExport], the `mono_wasm_invoke_method` doesn't handle managed exceptions. + // as opposed to [JSExport], the `mono_wasm_invoke_jsexport` doesn't handle managed exceptions. // Same way as old `bind_static_method` didn't App.bind_static_method_native = (method_name) => { try { const monoMethodPtr = App.exports.DebuggerTests.BindStaticMethod.GetMonoMethodPtr(method_name); // this is only implemented for void methods with no arguments - const invoker = runtime.Module.cwrap("mono_wasm_invoke_method", "void", ["number", "number"]); + const invoker = runtime.Module.cwrap("mono_wasm_invoke_jsexport", "void", ["number", "number"]); return function () { try { return invoker(monoMethodPtr, 0, 0); diff --git a/src/mono/browser/runtime/corebindings.c b/src/mono/browser/runtime/corebindings.c index 978e7fd403830d..7329fb4bcd1924 100644 --- a/src/mono/browser/runtime/corebindings.c +++ b/src/mono/browser/runtime/corebindings.c @@ -21,11 +21,12 @@ //JS funcs extern void mono_wasm_release_cs_owned_object (int js_handle); -extern void mono_wasm_resolve_or_reject_promise (void *data); +extern void mono_wasm_resolve_or_reject_promise (void *args); extern void mono_wasm_cancel_promise (int task_holder_gc_handle); extern void mono_wasm_console_clear (); extern void mono_wasm_set_entrypoint_breakpoint (int entry_point_metadata_token); extern void mono_wasm_trace_logger (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data); +extern void mono_wasm_invoke_js_function (int function_js_handle, void *args); extern int mono_runtime_run_module_cctor (MonoImage *image, MonoError *error); @@ -37,24 +38,21 @@ void mono_wasm_get_assembly_export (char *assembly_name, char *namespace, char * #ifndef DISABLE_THREADS void mono_wasm_release_cs_owned_object_post (pthread_t target_tid, int js_handle); -void mono_wasm_resolve_or_reject_promise_post (pthread_t target_tid, void *data); +void mono_wasm_resolve_or_reject_promise_post (pthread_t target_tid, void *args); void mono_wasm_cancel_promise_post (pthread_t target_tid, int task_holder_gc_handle); extern void mono_wasm_install_js_worker_interop (int context_gc_handle); extern void mono_wasm_uninstall_js_worker_interop (); -extern void mono_wasm_invoke_import_async (void* args, void* signature); -void mono_wasm_invoke_import_async_post (pthread_t target_tid, void* args, void* signature); -extern void mono_wasm_invoke_import_sync (void* args, void* signature); -void mono_wasm_invoke_import_sync_send (pthread_t target_tid, void* args, void* signature); -extern void mono_wasm_invoke_js_function (int function_js_handle, void *args); +extern void mono_wasm_invoke_jsimport (void* signature, void* args); +void mono_wasm_invoke_jsimport_async_post (pthread_t target_tid, void* signature, void* args); +void mono_wasm_invoke_jsimport_sync_send (pthread_t target_tid, void* signature, void* args); void mono_wasm_invoke_js_function_send (pthread_t target_tid, int function_js_handle, void *args); extern void mono_threads_wasm_async_run_in_target_thread_vi (pthread_t target_thread, void (*func) (gpointer), gpointer user_data1); extern void mono_threads_wasm_async_run_in_target_thread_vii (pthread_t target_thread, void (*func) (gpointer, gpointer), gpointer user_data1, gpointer user_data2); extern void mono_threads_wasm_sync_run_in_target_thread_vii (pthread_t target_thread, void (*func) (gpointer, gpointer), gpointer user_data1, gpointer user_data2); #else extern void mono_wasm_bind_js_import (void *signature, int *is_exception, MonoObject **result); -extern void mono_wasm_invoke_js_import (int function_handle, void *args); -extern void mono_wasm_invoke_js_function (int function_js_handle, void *args); +extern void mono_wasm_invoke_jsimport_ST (int function_handle, void *args); #endif /* DISABLE_THREADS */ // HybridGlobalization @@ -77,33 +75,27 @@ void bindings_initialize_internals (void) #endif /* ENABLE_JS_INTEROP_BY_VALUE */ #ifndef DISABLE_THREADS - mono_add_internal_call ("Interop/Runtime::ReleaseCSOwnedObject", mono_wasm_release_cs_owned_object); mono_add_internal_call ("Interop/Runtime::ReleaseCSOwnedObjectPost", mono_wasm_release_cs_owned_object_post); - mono_add_internal_call ("Interop/Runtime::ResolveOrRejectPromise", mono_wasm_resolve_or_reject_promise); mono_add_internal_call ("Interop/Runtime::ResolveOrRejectPromisePost", mono_wasm_resolve_or_reject_promise_post); mono_add_internal_call ("Interop/Runtime::InstallWebWorkerInterop", mono_wasm_install_js_worker_interop); mono_add_internal_call ("Interop/Runtime::UninstallWebWorkerInterop", mono_wasm_uninstall_js_worker_interop); - mono_add_internal_call ("Interop/Runtime::InvokeJSImportSync", mono_wasm_invoke_import_sync); - mono_add_internal_call ("Interop/Runtime::InvokeJSImportSyncSend", mono_wasm_invoke_import_sync_send); - mono_add_internal_call ("Interop/Runtime::InvokeJSImportAsyncPost", mono_wasm_invoke_import_async_post); - mono_add_internal_call ("Interop/Runtime::InvokeJSFunction", mono_wasm_invoke_js_function); + mono_add_internal_call ("Interop/Runtime::InvokeJSImportSync", mono_wasm_invoke_jsimport); + mono_add_internal_call ("Interop/Runtime::InvokeJSImportSyncSend", mono_wasm_invoke_jsimport_sync_send); + mono_add_internal_call ("Interop/Runtime::InvokeJSImportAsyncPost", mono_wasm_invoke_jsimport_async_post); mono_add_internal_call ("Interop/Runtime::InvokeJSFunctionSend", mono_wasm_invoke_js_function_send); - mono_add_internal_call ("Interop/Runtime::CancelPromise", mono_wasm_cancel_promise); mono_add_internal_call ("Interop/Runtime::CancelPromisePost", mono_wasm_cancel_promise_post); - mono_add_internal_call ("Interop/Runtime::AssemblyGetEntryPoint", mono_wasm_assembly_get_entry_point); - mono_add_internal_call ("Interop/Runtime::BindAssemblyExports", mono_wasm_bind_assembly_exports); - mono_add_internal_call ("Interop/Runtime::GetAssemblyExport", mono_wasm_get_assembly_export); #else + mono_add_internal_call ("Interop/Runtime::BindJSImport", mono_wasm_bind_js_import); + mono_add_internal_call ("Interop/Runtime::InvokeJSImportST", mono_wasm_invoke_jsimport_ST); +#endif /* DISABLE_THREADS */ + mono_add_internal_call ("Interop/Runtime::ReleaseCSOwnedObject", mono_wasm_release_cs_owned_object); mono_add_internal_call ("Interop/Runtime::ResolveOrRejectPromise", mono_wasm_resolve_or_reject_promise); - mono_add_internal_call ("Interop/Runtime::BindJSImport", mono_wasm_bind_js_import); - mono_add_internal_call ("Interop/Runtime::InvokeJSImport", mono_wasm_invoke_js_import); mono_add_internal_call ("Interop/Runtime::InvokeJSFunction", mono_wasm_invoke_js_function); mono_add_internal_call ("Interop/Runtime::CancelPromise", mono_wasm_cancel_promise); mono_add_internal_call ("Interop/Runtime::AssemblyGetEntryPoint", mono_wasm_assembly_get_entry_point); mono_add_internal_call ("Interop/Runtime::BindAssemblyExports", mono_wasm_bind_assembly_exports); mono_add_internal_call ("Interop/Runtime::GetAssemblyExport", mono_wasm_get_assembly_export); -#endif /* DISABLE_THREADS */ mono_add_internal_call ("Interop/JsGlobalization::ChangeCaseInvariant", mono_wasm_change_case_invariant); mono_add_internal_call ("Interop/JsGlobalization::ChangeCase", mono_wasm_change_case); @@ -276,14 +268,14 @@ void mono_wasm_cancel_promise_post (pthread_t target_tid, int task_holder_gc_han mono_threads_wasm_async_run_in_target_thread_vi (target_tid, (void (*) (gpointer))mono_wasm_cancel_promise, (gpointer)task_holder_gc_handle); } -void mono_wasm_invoke_import_async_post (pthread_t target_tid, void* args, void* signature) +void mono_wasm_invoke_jsimport_async_post (pthread_t target_tid, void* signature, void* args) { - mono_threads_wasm_async_run_in_target_thread_vii (target_tid, (void (*) (gpointer, gpointer))mono_wasm_invoke_import_async, (gpointer)args, (gpointer)signature); + mono_threads_wasm_async_run_in_target_thread_vii (target_tid, (void (*) (gpointer, gpointer))mono_wasm_invoke_jsimport, (gpointer)signature, (gpointer)args); } -void mono_wasm_invoke_import_sync_send (pthread_t target_tid, void* args, void* signature) +void mono_wasm_invoke_jsimport_sync_send (pthread_t target_tid, void* signature, void* args) { - mono_threads_wasm_sync_run_in_target_thread_vii (target_tid, (void (*) (gpointer, gpointer))mono_wasm_invoke_import_sync, (gpointer)args, (gpointer)signature); + mono_threads_wasm_sync_run_in_target_thread_vii (target_tid, (void (*) (gpointer, gpointer))mono_wasm_invoke_jsimport, (gpointer)signature, (gpointer)args); } void mono_wasm_invoke_js_function_send (pthread_t target_tid, int function_js_handle, void *args) diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index 65e245585a671b..7dc7c207697ec9 100644 --- a/src/mono/browser/runtime/cwraps.ts +++ b/src/mono/browser/runtime/cwraps.ts @@ -12,6 +12,7 @@ import type { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr, ManagedPointer } from "./t import { Module, runtimeHelpers } from "./globals"; import { mono_log_error } from "./logging"; import { mono_assert } from "./globals"; +import { PThreadPtr } from "./pthreads/shared/types"; type SigLine = [lazyOrSkip: boolean | (() => boolean), name: string, returnType: string | null, argTypes?: string[], opts?: any]; @@ -25,6 +26,8 @@ const threading_cwraps: SigLine[] = WasmEnableThreads ? [ [true, "mono_wasm_diagnostic_server_post_resume_runtime", "void", []], [true, "mono_wasm_diagnostic_server_create_stream", "number", []], [false, "mono_wasm_init_finalizer_thread", null, []], + [false, "mono_wasm_invoke_jsexport_async_post", "void", ["number", "number", "number"]], + [false, "mono_wasm_invoke_jsexport_sync_send", "void", ["number", "number", "number"]], ] : []; // when the method is assigned/cached at usage, instead of being invoked directly from cwraps, it can't be marked lazy, because it would be re-bound on each call @@ -61,7 +64,7 @@ const fn_signatures: SigLine[] = [ [() => !runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler, "mono_wasm_profiler_init_aot", "void", ["string"]], [true, "mono_wasm_profiler_init_browser", "void", ["number"]], [false, "mono_wasm_exec_regression", "number", ["number", "string"]], - [false, "mono_wasm_invoke_method", "void", ["number", "number"]], + [false, "mono_wasm_invoke_jsexport", "void", ["number", "number"]], [true, "mono_wasm_write_managed_pointer_unsafe", "void", ["number", "number"]], [true, "mono_wasm_copy_managed_pointer", "void", ["number", "number"]], [true, "mono_wasm_i52_to_f64", "number", ["number", "number"]], @@ -140,6 +143,8 @@ export interface t_ThreadingCwraps { mono_wasm_diagnostic_server_post_resume_runtime(): void; mono_wasm_diagnostic_server_create_stream(): VoidPtr; mono_wasm_init_finalizer_thread(): void; + mono_wasm_invoke_jsexport_async_post(targetTID: PThreadPtr, method: MonoMethod, args: VoidPtr): void; + mono_wasm_invoke_jsexport_sync_send(targetTID: PThreadPtr, method: MonoMethod, args: VoidPtr): void; } export interface t_ProfilerCwraps { @@ -176,7 +181,7 @@ export interface t_Cwraps { mono_wasm_getenv(name: string): CharPtr; mono_wasm_set_main_args(argc: number, argv: VoidPtr): void; mono_wasm_exec_regression(verbose_level: number, image: string): number; - mono_wasm_invoke_method(method: MonoMethod, args: JSMarshalerArguments): void; + mono_wasm_invoke_jsexport(method: MonoMethod, args: JSMarshalerArguments): void; mono_wasm_write_managed_pointer_unsafe(destination: VoidPtr | MonoObjectRef, pointer: ManagedPointer): void; mono_wasm_copy_managed_pointer(destination: VoidPtr | MonoObjectRef, source: VoidPtr | MonoObjectRef): void; mono_wasm_i52_to_f64(source: VoidPtr, error: Int32Ptr): number; diff --git a/src/mono/browser/runtime/debug.ts b/src/mono/browser/runtime/debug.ts index acc467e4b6d3e1..1c919426bc3a84 100644 --- a/src/mono/browser/runtime/debug.ts +++ b/src/mono/browser/runtime/debug.ts @@ -82,7 +82,7 @@ export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: n const { res_ok, res } = commands_received.remove(id); if (!res_ok) - throw new Error("Failed on mono_wasm_invoke_method_debugger_agent_with_parms"); + throw new Error("Failed on mono_wasm_send_dbg_command_with_parms"); return res; } diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c index a9db7956feda4e..1d0133a57e6e88 100644 --- a/src/mono/browser/runtime/driver.c +++ b/src/mono/browser/runtime/driver.c @@ -228,7 +228,7 @@ mono_wasm_load_runtime (int debug_level) } EMSCRIPTEN_KEEPALIVE void -mono_wasm_invoke_method (MonoMethod *method, void* args) +mono_wasm_invoke_jsexport (MonoMethod *method, void* args) { PVOLATILE(MonoObject) temp_exc = NULL; @@ -244,7 +244,7 @@ mono_wasm_invoke_method (MonoMethod *method, void* args) PVOLATILE(MonoObject) exc2 = NULL; store_volatile((MonoObject**)&temp_exc, (MonoObject*)mono_object_to_string ((MonoObject*)temp_exc, (MonoObject **)&exc2)); if (exc2) { - mono_wasm_trace_logger ("jsinterop", "critical", "mono_wasm_invoke_method unexpected double fault", 1, NULL); + mono_wasm_trace_logger ("jsinterop", "critical", "mono_wasm_invoke_jsexport unexpected double fault", 1, NULL); } else { mono_wasm_trace_logger ("jsinterop", "critical", mono_string_to_utf8((MonoString*)temp_exc), 1, NULL); } @@ -253,6 +253,35 @@ mono_wasm_invoke_method (MonoMethod *method, void* args) MONO_EXIT_GC_UNSAFE; } +#ifndef DISABLE_THREADS + +extern void mono_threads_wasm_async_run_in_target_thread_vii (void* target_thread, void (*func) (gpointer, gpointer), gpointer user_data1, gpointer user_data2); +extern void mono_threads_wasm_sync_run_in_target_thread_vii (void* target_thread, void (*func) (gpointer, gpointer), gpointer user_data1, gpointer user_data2); + +static void +mono_wasm_invoke_jsexport_async_post_cb (MonoMethod *method, void* args) +{ + mono_wasm_invoke_jsexport (method, args); + // TODO assert receiver_should_free ? + free (args); +} + +// async +EMSCRIPTEN_KEEPALIVE void +mono_wasm_invoke_jsexport_async_post (void* target_thread, MonoMethod *method, void* args /*JSMarshalerArguments*/) +{ + mono_threads_wasm_async_run_in_target_thread_vii(target_thread, (void (*)(gpointer, gpointer))mono_wasm_invoke_jsexport_async_post_cb, method, args); +} + +// sync +EMSCRIPTEN_KEEPALIVE void +mono_wasm_invoke_jsexport_sync_send (void* target_thread, MonoMethod *method, void* args /*JSMarshalerArguments*/) +{ + mono_threads_wasm_sync_run_in_target_thread_vii(target_thread, (void (*)(gpointer, gpointer))mono_wasm_invoke_jsexport, method, args); +} + +#endif /* DISABLE_THREADS */ + EMSCRIPTEN_KEEPALIVE void mono_wasm_string_from_utf16_ref (const mono_unichar2 * chars, int length, MonoString **result) { diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts index 17aadf3780e431..c410dca9e75881 100644 --- a/src/mono/browser/runtime/exports-binding.ts +++ b/src/mono/browser/runtime/exports-binding.ts @@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { mono_wasm_debugger_log, mono_wasm_add_dbg_command_received, mono_wasm_set_entrypoint_breakpoint, mono_wasm_fire_debugger_agent_message_with_data, mono_wasm_fire_debugger_agent_message_with_data_to_pause } from "./debug"; import { mono_wasm_release_cs_owned_object } from "./gc-handles"; -import { mono_wasm_bind_js_import, mono_wasm_invoke_js_function, mono_wasm_invoke_import_async, mono_wasm_invoke_import_sync, mono_wasm_invoke_js_import } from "./invoke-js"; +import { mono_wasm_bind_js_import, mono_wasm_invoke_js_function, mono_wasm_invoke_jsimport, mono_wasm_invoke_jsimport_ST } from "./invoke-js"; import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } from "./jiterpreter"; import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry"; import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue } from "./jiterpreter-jit-call"; @@ -48,8 +48,7 @@ export const mono_wasm_threads_imports = !WasmEnableThreads ? [] : [ // corebindings.c mono_wasm_install_js_worker_interop, mono_wasm_uninstall_js_worker_interop, - mono_wasm_invoke_import_async, - mono_wasm_invoke_import_sync, + mono_wasm_invoke_jsimport, ]; export const mono_wasm_imports = [ @@ -90,7 +89,7 @@ export const mono_wasm_imports = [ mono_wasm_release_cs_owned_object, mono_wasm_bind_js_import, mono_wasm_invoke_js_function, - mono_wasm_invoke_js_import, + mono_wasm_invoke_jsimport_ST, mono_wasm_resolve_or_reject_promise, mono_wasm_cancel_promise, mono_wasm_change_case_invariant, diff --git a/src/mono/browser/runtime/invoke-cs.ts b/src/mono/browser/runtime/invoke-cs.ts index 5127ee0a4f3a49..2978e7ea8afce8 100644 --- a/src/mono/browser/runtime/invoke-cs.ts +++ b/src/mono/browser/runtime/invoke-cs.ts @@ -14,7 +14,7 @@ import { import { MonoMethod, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, MarshalerType } from "./types/internal"; import { assert_js_interop } from "./invoke-js"; import { startMeasure, MeasuredBlock, endMeasure } from "./profiler"; -import { bind_assembly_exports, invoke_sync_method } from "./managed-exports"; +import { bind_assembly_exports, invoke_async_jsexport, invoke_sync_jsexport } from "./managed-exports"; import { mono_log_debug } from "./logging"; export function mono_wasm_bind_cs_function(method: MonoMethod, assemblyName: string, namespaceName: string, shortClassName: string, methodName: string, signatureHash: number, signature: JSFunctionSignature): void { @@ -38,7 +38,16 @@ export function mono_wasm_bind_cs_function(method: MonoMethod, assemblyName: str const res_sig = get_sig(signature, 1); let res_marshaler_type = get_signature_type(res_sig); + + // hack until we have public API for JSType.OneWay + if (WasmEnableThreads && shortClassName === "DefaultWebAssemblyJSRuntime" + && namespaceName === "Microsoft.AspNetCore.Components.WebAssembly.Services" + && (methodName === "BeginInvokeDotNet" || methodName === "EndInvokeJS")) { + res_marshaler_type = MarshalerType.OneWay; + } + const is_async = res_marshaler_type == MarshalerType.Task; + const is_oneway = res_marshaler_type == MarshalerType.OneWay; if (is_async) { res_marshaler_type = MarshalerType.TaskPreCreated; } @@ -51,30 +60,39 @@ export function mono_wasm_bind_cs_function(method: MonoMethod, assemblyName: str arg_marshalers, res_converter, is_async, + is_oneway, isDisposed: false, }; let bound_fn: Function; - // void - if (args_count == 0 && !res_converter) { - bound_fn = bind_fn_0V(closure); - } - else if (args_count == 1 && !res_converter) { - bound_fn = bind_fn_1V(closure); - } - else if (is_async && args_count == 1 && res_converter) { - bound_fn = bind_fn_1RA(closure); - } - else if (is_async && args_count == 2 && res_converter) { - bound_fn = bind_fn_2RA(closure); - } - else if (args_count == 1 && res_converter) { - bound_fn = bind_fn_1R(closure); - } - else if (args_count == 2 && res_converter) { - bound_fn = bind_fn_2R(closure); - } - else { + + if (is_async) { + if (args_count == 1 && res_converter) { + bound_fn = bind_fn_1RA(closure); + } + else if (args_count == 2 && res_converter) { + bound_fn = bind_fn_2RA(closure); + } + else { + bound_fn = bind_fn(closure); + } + } else if (is_oneway) { bound_fn = bind_fn(closure); + } else { + if (args_count == 0 && !res_converter) { + bound_fn = bind_fn_0V(closure); + } + else if (args_count == 1 && !res_converter) { + bound_fn = bind_fn_1V(closure); + } + else if (args_count == 1 && res_converter) { + bound_fn = bind_fn_1R(closure); + } + else if (args_count == 2 && res_converter) { + bound_fn = bind_fn_2R(closure); + } + else { + bound_fn = bind_fn(closure); + } } // this is just to make debugging easier. @@ -107,7 +125,7 @@ function bind_fn_0V(closure: BindingClosure) { try { const args = alloc_stack_frame(2); // call C# side - invoke_sync_method(method, args); + invoke_sync_jsexport(method, args); } finally { Module.stackRestore(sp); endMeasure(mark, MeasuredBlock.callCsFunction, fqn); @@ -130,7 +148,7 @@ function bind_fn_1V(closure: BindingClosure) { marshaler1(args, arg1); // call C# side - invoke_sync_method(method, args); + invoke_sync_jsexport(method, args); } finally { Module.stackRestore(sp); endMeasure(mark, MeasuredBlock.callCsFunction, fqn); @@ -154,7 +172,7 @@ function bind_fn_1R(closure: BindingClosure) { marshaler1(args, arg1); // call C# side - invoke_sync_method(method, args); + invoke_sync_jsexport(method, args); const js_result = res_converter(args); return js_result; @@ -171,7 +189,7 @@ function bind_fn_1RA(closure: BindingClosure) { const res_converter = closure.res_converter!; const fqn = closure.fullyQualifiedName; if (!WasmEnableThreads) (closure) = null; - return function bound_fn_1R(arg1: any) { + return function bind_fn_1RA(arg1: any) { const mark = startMeasure(); loaderHelpers.assert_runtime_running(); mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); @@ -184,7 +202,7 @@ function bind_fn_1RA(closure: BindingClosure) { let promise = res_converter(args); // call C# side - invoke_sync_method(method, args); + invoke_async_jsexport(method, args, 3); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, undefined, promise); @@ -215,7 +233,7 @@ function bind_fn_2R(closure: BindingClosure) { marshaler2(args, arg2); // call C# side - invoke_sync_method(method, args); + invoke_sync_jsexport(method, args); const js_result = res_converter(args); return js_result; @@ -233,7 +251,7 @@ function bind_fn_2RA(closure: BindingClosure) { const res_converter = closure.res_converter!; const fqn = closure.fullyQualifiedName; if (!WasmEnableThreads) (closure) = null; - return function bound_fn_2R(arg1: any, arg2: any) { + return function bind_fn_2RA(arg1: any, arg2: any) { const mark = startMeasure(); loaderHelpers.assert_runtime_running(); mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); @@ -247,7 +265,7 @@ function bind_fn_2RA(closure: BindingClosure) { let promise = res_converter(args); // call C# side - invoke_sync_method(method, args); + invoke_async_jsexport(method, args, 4); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, undefined, promise); @@ -267,6 +285,7 @@ function bind_fn(closure: BindingClosure) { const method = closure.method; const fqn = closure.fullyQualifiedName; const is_async = closure.is_async; + const is_oneway = closure.is_oneway; if (!WasmEnableThreads) (closure) = null; return function bound_fn(...js_args: any[]) { const mark = startMeasure(); @@ -289,13 +308,20 @@ function bind_fn(closure: BindingClosure) { } // call C# side - invoke_sync_method(method, args); if (is_async) { + invoke_async_jsexport(method, args, 2 + args_count); // in case the C# side returned synchronously js_result = end_marshal_task_to_js(args, undefined, js_result); } - else if (res_converter) { - js_result = res_converter(args); + else if (is_oneway) { + // call C# side, fire and forget + invoke_async_jsexport(method, args, 2 + args_count); + } + else { + invoke_sync_jsexport(method, args); + if (res_converter) { + js_result = res_converter(args); + } } return js_result; } finally { @@ -312,6 +338,7 @@ type BindingClosure = { arg_marshalers: (BoundMarshalerToCs)[], res_converter: BoundMarshalerToJs | undefined, is_async: boolean, + is_oneway: boolean, isDisposed: boolean, } diff --git a/src/mono/browser/runtime/invoke-js.ts b/src/mono/browser/runtime/invoke-js.ts index 9c118329b15aaf..f429bf3e98b9fd 100644 --- a/src/mono/browser/runtime/invoke-js.ts +++ b/src/mono/browser/runtime/invoke-js.ts @@ -5,19 +5,18 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import BuildConfiguration from "consts:configuration"; import { marshal_exception_to_cs, bind_arg_marshal_to_cs } from "./marshal-to-cs"; -import { get_signature_argument_count, bound_js_function_symbol, get_sig, get_signature_version, get_signature_type, imported_js_function_symbol, get_signature_handle, get_signature_function_name, get_signature_module_name } from "./marshal"; +import { get_signature_argument_count, bound_js_function_symbol, get_sig, get_signature_version, get_signature_type, imported_js_function_symbol, get_signature_handle, get_signature_function_name, get_signature_module_name, is_receiver_should_free } from "./marshal"; import { setI32_unchecked, receiveWorkerHeapViews, forceThreadMemoryViewRefresh } from "./memory"; import { stringToMonoStringRoot } from "./strings"; import { MonoObject, MonoObjectRef, JSFunctionSignature, JSMarshalerArguments, WasmRoot, BoundMarshalerToJs, JSFnHandle, BoundMarshalerToCs, JSHandle, MarshalerType } from "./types/internal"; import { Int32Ptr } from "./types/emscripten"; -import { ENVIRONMENT_IS_WORKER, INTERNAL, Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; +import { INTERNAL, Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { bind_arg_marshal_to_js } from "./marshal-to-js"; import { mono_wasm_new_external_root } from "./roots"; import { mono_log_debug, mono_wasm_symbolicate_string } from "./logging"; import { mono_wasm_get_jsobj_from_js_handle } from "./gc-handles"; import { endMeasure, MeasuredBlock, startMeasure } from "./profiler"; import { wrap_as_cancelable_promise } from "./cancelable-promise"; -import { is_thread_available } from "./pthreads/shared/emscripten-replacements"; export const js_import_wrapper_by_fn_handle: Function[] = [null];// 0th slot is dummy, main thread we free them on shutdown. On web worker thread we free them when worker is detached. @@ -36,7 +35,7 @@ export function mono_wasm_bind_js_import(signature: JSFunctionSignature, is_exce } } -export function mono_wasm_invoke_import_async(args: JSMarshalerArguments, signature: JSFunctionSignature) { +export function mono_wasm_invoke_jsimport(signature: JSFunctionSignature, args: JSMarshalerArguments) { if (!WasmEnableThreads) return; assert_js_interop(); @@ -49,44 +48,13 @@ export function mono_wasm_invoke_import_async(args: JSMarshalerArguments, signat } mono_assert(bound_fn, () => `Imported function handle expected ${function_handle}`); - let max_postpone_count = 10; - function postpone_invoke_import_async() { - if (max_postpone_count < 0 || is_thread_available()) { - if (WasmEnableThreads) { - forceThreadMemoryViewRefresh(); - } - bound_fn(args); - Module._free(args as any); - } else { - max_postpone_count--; - Module.safeSetTimeout(postpone_invoke_import_async, 10); - } - } - - if (WasmEnableThreads && !ENVIRONMENT_IS_WORKER) { - // give thread chance to load before we run more synchronous code on UI thread - postpone_invoke_import_async(); - } - else { - bound_fn(args); - // this works together with AllocHGlobal in JSFunctionBinding.DispatchJSImportAsync - Module._free(args as any); - } + bound_fn(args); } -export function mono_wasm_invoke_import_sync(args: JSMarshalerArguments, signature: JSFunctionSignature) { - if (!WasmEnableThreads) return; - assert_js_interop(); - - const function_handle = get_signature_handle(signature); - - let bound_fn = js_import_wrapper_by_fn_handle[function_handle]; - if (bound_fn == undefined) { - // it was not bound yet, let's do it now - bound_fn = bind_js_import(signature); - } +export function mono_wasm_invoke_jsimport_ST(function_handle: JSFnHandle, args: JSMarshalerArguments): void { + if (WasmEnableThreads) return; + const bound_fn = js_import_wrapper_by_fn_handle[function_handle]; mono_assert(bound_fn, () => `Imported function handle expected ${function_handle}`); - bound_fn(args); } @@ -128,6 +96,9 @@ function bind_js_import(signature: JSFunctionSignature): Function { const res_marshaler_type = get_signature_type(res_sig); const res_converter = bind_arg_marshal_to_cs(res_sig, res_marshaler_type, 1); + const is_oneway = res_marshaler_type == MarshalerType.OneWay; + const is_async = res_marshaler_type == MarshalerType.Task || res_marshaler_type == MarshalerType.TaskPreCreated; + const closure: BindingClosure = { fn, fqn: js_module_name + ":" + js_function_name, @@ -136,26 +107,32 @@ function bind_js_import(signature: JSFunctionSignature): Function { res_converter, has_cleanup, arg_cleanup, + is_oneway, + is_async, isDisposed: false, }; - let bound_fn: Function; - if (args_count == 0 && !res_converter) { - bound_fn = bind_fn_0V(closure); - } - else if (args_count == 1 && !has_cleanup && !res_converter) { - bound_fn = bind_fn_1V(closure); - } - else if (args_count == 1 && !has_cleanup && res_converter) { - bound_fn = bind_fn_1R(closure); - } - else if (args_count == 2 && !has_cleanup && res_converter) { - bound_fn = bind_fn_2R(closure); + let bound_fn: WrappedJSFunction; + if (is_async || is_oneway || has_cleanup) { + bound_fn = bind_fn(closure); } else { - bound_fn = bind_fn(closure); + if (args_count == 0 && !res_converter) { + bound_fn = bind_fn_0V(closure); + } + else if (args_count == 1 && !res_converter) { + bound_fn = bind_fn_1V(closure); + } + else if (args_count == 1 && res_converter) { + bound_fn = bind_fn_1R(closure); + } + else if (args_count == 2 && res_converter) { + bound_fn = bind_fn_2R(closure); + } else { + bound_fn = bind_fn(closure); + } } - // this is just to make debugging easier. + // this is just to make debugging easier by naming the function in the stack trace. // It's not CSP compliant and possibly not performant, that's why it's only enabled in debug builds // in Release configuration, it would be a trimmed by rollup if (BuildConfiguration === "Debug" && !runtimeHelpers.cspPolicy) { @@ -167,13 +144,41 @@ function bind_js_import(signature: JSFunctionSignature): Function { } } - (bound_fn)[imported_js_function_symbol] = closure; + function async_bound_fn(args: JSMarshalerArguments): void { + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } + bound_fn(args); + } + function sync_bound_fn(args: JSMarshalerArguments): void { + const previous = runtimeHelpers.isPendingSynchronousCall; + try { + runtimeHelpers.isPendingSynchronousCall = true; + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } + bound_fn(args); + } + finally { + runtimeHelpers.isPendingSynchronousCall = previous; + } + } + + let wrapped_fn: WrappedJSFunction; + if (is_async || is_oneway) { + wrapped_fn = async_bound_fn; + } + else { + wrapped_fn = sync_bound_fn; + } + + (wrapped_fn)[imported_js_function_symbol] = closure; - js_import_wrapper_by_fn_handle[function_handle] = bound_fn; + js_import_wrapper_by_fn_handle[function_handle] = wrapped_fn; endMeasure(mark, MeasuredBlock.bindJsFunction, js_function_name); - return bound_fn; + return wrapped_fn; } function bind_fn_0V(closure: BindingClosure) { @@ -274,6 +279,7 @@ function bind_fn(closure: BindingClosure) { const fqn = closure.fqn; if (!WasmEnableThreads) (closure) = null; return function bound_fn(args: JSMarshalerArguments) { + const is_async = WasmEnableThreads && is_receiver_should_free(args); const mark = startMeasure(); try { mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); @@ -303,11 +309,16 @@ function bind_fn(closure: BindingClosure) { marshal_exception_to_cs(args, ex); } finally { + if (is_async) { + Module._free(args as any); + } endMeasure(mark, MeasuredBlock.callCsFunction, fqn); } }; } +type WrappedJSFunction = (args: JSMarshalerArguments) => void; + type BindingClosure = { fn: Function, fqn: string, @@ -316,6 +327,8 @@ type BindingClosure = { arg_marshalers: (BoundMarshalerToJs)[], res_converter: BoundMarshalerToCs | undefined, has_cleanup: boolean, + is_oneway: boolean, + is_async: boolean, arg_cleanup: (Function | undefined)[] } @@ -325,12 +338,6 @@ export function mono_wasm_invoke_js_function(bound_function_js_handle: JSHandle, bound_fn(args); } -export function mono_wasm_invoke_js_import(function_handle: JSFnHandle, args: JSMarshalerArguments): void { - const bound_fn = js_import_wrapper_by_fn_handle[function_handle]; - mono_assert(bound_fn, () => `Imported function handle expected ${function_handle}`); - bound_fn(args); -} - export function mono_wasm_set_module_imports(module_name: string, moduleImports: any) { importedModules.set(module_name, moduleImports); mono_log_debug(`added module imports '${module_name}'`); diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts index b41febf9c649fe..87da9ac8d259ef 100644 --- a/src/mono/browser/runtime/managed-exports.ts +++ b/src/mono/browser/runtime/managed-exports.ts @@ -60,7 +60,7 @@ export function call_entry_point(main_assembly_name: string, program_args: strin // because this is async, we could pre-allocate the promise let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated, marshal_int32_to_js); - invoke_sync_method(managedExports.CallEntrypoint, args); + invoke_async_jsexport(managedExports.CallEntrypoint, args, 5); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, marshal_int32_to_js, promise); @@ -84,7 +84,7 @@ export function load_satellite_assembly(dll: Uint8Array): void { const arg1 = get_arg(args, 2); set_arg_type(arg1, MarshalerType.Array); marshal_array_to_cs(arg1, dll, MarshalerType.Byte); - invoke_sync_method(managedExports.LoadSatelliteAssembly, args); + invoke_sync_jsexport(managedExports.LoadSatelliteAssembly, args); } finally { Module.stackRestore(sp); } @@ -101,7 +101,7 @@ export function load_lazy_assembly(dll: Uint8Array, pdb: Uint8Array | null): voi set_arg_type(arg2, MarshalerType.Array); marshal_array_to_cs(arg1, dll, MarshalerType.Byte); marshal_array_to_cs(arg2, pdb, MarshalerType.Byte); - invoke_sync_method(managedExports.LoadLazyAssembly, args); + invoke_sync_jsexport(managedExports.LoadLazyAssembly, args); } finally { Module.stackRestore(sp); } @@ -117,7 +117,8 @@ export function release_js_owned_object_by_gc_handle(gc_handle: GCHandle) { const arg1 = get_arg(args, 2); set_arg_type(arg1, MarshalerType.Object); set_gc_handle(arg1, gc_handle); - invoke_sync_method(managedExports.ReleaseJSOwnedObjectByGCHandle, args); + // this must stay synchronous for free_gcv_handle sake + invoke_sync_jsexport(managedExports.ReleaseJSOwnedObjectByGCHandle, args); } finally { Module.stackRestore(sp); } @@ -145,7 +146,7 @@ export function complete_task(holder_gc_handle: GCHandle, isCanceling: boolean, mono_assert(res_converter, "res_converter missing"); res_converter(arg3, data); } - invoke_sync_method(managedExports.CompleteTask, args); + invoke_async_jsexport(managedExports.CompleteTask, args, 4); } finally { Module.stackRestore(sp); } @@ -176,7 +177,7 @@ export function call_delegate(callback_gc_handle: GCHandle, arg1_js: any, arg2_j arg3_converter(arg4, arg3_js); } - invoke_sync_method(managedExports.CallDelegate, args); + invoke_sync_jsexport(managedExports.CallDelegate, args); if (res_converter) { const res = get_arg(args, 1); @@ -198,7 +199,7 @@ export function get_managed_stack_trace(exception_gc_handle: GCHandle) { set_arg_type(arg1, MarshalerType.Exception); set_gc_handle(arg1, exception_gc_handle); - invoke_sync_method(managedExports.GetManagedStackTrace, args); + invoke_sync_jsexport(managedExports.GetManagedStackTrace, args); const res = get_arg(args, 1); return marshal_string_to_js(res); } finally { @@ -206,34 +207,68 @@ export function get_managed_stack_trace(exception_gc_handle: GCHandle) { } } -// void InstallMainSynchronizationContext(nint jsNativeTID, out GCHandle contextHandle) +// GCHandle InstallMainSynchronizationContext(nint jsNativeTID) export function install_main_synchronization_context(): GCHandle { if (!WasmEnableThreads) return GCHandleNull; assert_c_interop(); const sp = Module.stackSave(); try { - // tid in, gc_handle out - const bytes = JavaScriptMarshalerArgSize * 4; + // this block is like alloc_stack_frame() but without set_args_context() + const bytes = JavaScriptMarshalerArgSize * 3; const args = Module.stackAlloc(bytes) as any; _zero_region(args, bytes); + + const res = get_arg(args, 1); const arg1 = get_arg(args, 2); - const arg2 = get_arg(args, 3); set_arg_intptr(arg1, mono_wasm_main_thread_ptr() as any); - cwraps.mono_wasm_invoke_method(managedExports.InstallMainSynchronizationContext!, args); + + // this block is like invoke_sync_jsexport() but without assert_js_interop() + cwraps.mono_wasm_invoke_jsexport(managedExports.InstallMainSynchronizationContext!, args); if (is_args_exception(args)) { const exc = get_arg(args, 0); throw marshal_exception_to_js(exc); } - return get_arg_gc_handle(arg2) as any; + return get_arg_gc_handle(res) as any; } finally { Module.stackRestore(sp); } } -export function invoke_sync_method(method: MonoMethod, args: JSMarshalerArguments): void { +export function invoke_async_jsexport(method: MonoMethod, args: JSMarshalerArguments, size: number): void { + assert_js_interop(); + if (!WasmEnableThreads || runtimeHelpers.isCurrentThread) { + cwraps.mono_wasm_invoke_jsexport(method, args as any); + if (is_args_exception(args)) { + const exc = get_arg(args, 0); + throw marshal_exception_to_js(exc); + } + } else { + throw new Error("Should be unreachable until we implement deputy." + size); + /* + set_receiver_should_free(args); + const bytes = JavaScriptMarshalerArgSize * size; + const cpy = Module._malloc(bytes) as any; + copyBytes(args as any, cpy, bytes); + twraps.mono_wasm_invoke_jsexport_async_post(runtimeHelpers.managedThreadTID, method, cpy); + */ + } +} + +export function invoke_sync_jsexport(method: MonoMethod, args: JSMarshalerArguments): void { assert_js_interop(); - cwraps.mono_wasm_invoke_method(method, args as any); + if (!WasmEnableThreads || runtimeHelpers.isCurrentThread) { + cwraps.mono_wasm_invoke_jsexport(method, args as any); + } else { + throw new Error("Should be unreachable until we implement deputy."); + /* + if (!runtimeHelpers.isCurrentThread && runtimeHelpers.isPendingSynchronousCall) { + throw new Error("Cannot call synchronous C# method from inside a synchronous call to a JS method."); + } + // this is blocking too + twraps.mono_wasm_invoke_jsexport_sync_send(runtimeHelpers.managedThreadTID, method, args as any); + */ + } if (is_args_exception(args)) { const exc = get_arg(args, 0); throw marshal_exception_to_js(exc); @@ -253,7 +288,7 @@ export function bind_assembly_exports(assemblyName: string): Promise { // because this is async, we could pre-allocate the promise let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated); - invoke_sync_method(managedExports.BindAssemblyExports, args); + invoke_async_jsexport(managedExports.BindAssemblyExports, args, 3); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, marshal_int32_to_js, promise); diff --git a/src/mono/browser/runtime/marshal-to-cs.ts b/src/mono/browser/runtime/marshal-to-cs.ts index c1b663044c8b84..54ab212258ce5c 100644 --- a/src/mono/browser/runtime/marshal-to-cs.ts +++ b/src/mono/browser/runtime/marshal-to-cs.ts @@ -8,7 +8,7 @@ import WasmEnableJsInteropByValue from "consts:wasmEnableJsInteropByValue"; import { isThenable } from "./cancelable-promise"; import cwraps from "./cwraps"; import { alloc_gcv_handle, assert_not_disposed, cs_owned_js_handle_symbol, js_owned_gc_handle_symbol, mono_wasm_get_js_handle, setup_managed_proxy, teardown_managed_proxy } from "./gc-handles"; -import { Module, loaderHelpers, mono_assert } from "./globals"; +import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { ManagedError, set_gc_handle, set_js_handle, set_arg_type, set_arg_i32, set_arg_f64, set_arg_i52, set_arg_f32, set_arg_i16, set_arg_u8, set_arg_b8, set_arg_date, @@ -59,11 +59,12 @@ export function initialize_marshalers_to_cs(): void { js_to_cs_marshalers.set(MarshalerType.None, _marshal_null_to_cs);// also void js_to_cs_marshalers.set(MarshalerType.Discard, _marshal_null_to_cs);// also void js_to_cs_marshalers.set(MarshalerType.Void, _marshal_null_to_cs);// also void + js_to_cs_marshalers.set(MarshalerType.OneWay, _marshal_null_to_cs);// also void } } export function bind_arg_marshal_to_cs(sig: JSMarshalerType, marshaler_type: MarshalerType, index: number): BoundMarshalerToCs | undefined { - if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void) { + if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void || marshaler_type === MarshalerType.Discard || marshaler_type === MarshalerType.OneWay) { return undefined; } let res_marshaler: MarshalerToCs | undefined = undefined; @@ -234,7 +235,7 @@ export function marshal_string_to_cs(arg: JSMarshalerArgument, value: string) { function _marshal_string_to_cs_impl(arg: JSMarshalerArgument, value: string) { if (WasmEnableJsInteropByValue) { const bufferLen = value.length * 2; - const buffer = Module._malloc(bufferLen); + const buffer = Module._malloc(bufferLen);// together with Marshal.FreeHGlobal stringToUTF16(buffer as any, buffer as any + bufferLen, value); set_arg_intptr(arg, buffer); set_arg_length(arg, value.length); @@ -261,13 +262,14 @@ function _marshal_function_to_cs(arg: JSMarshalerArgument, value: Function, _?: mono_check(value && value instanceof Function, "Value is not a Function"); // TODO: we could try to cache value -> existing JSHandle - const wrapper: any = (args: JSMarshalerArguments) => { + const wrapper: any = function delegate_wrapper(args: JSMarshalerArguments) { const exc = get_arg(args, 0); const res = get_arg(args, 1); const arg1 = get_arg(args, 2); const arg2 = get_arg(args, 3); const arg3 = get_arg(args, 4); + const previousPendingSynchronousCall = runtimeHelpers.isPendingSynchronousCall; try { mono_assert(!WasmEnableThreads || !wrapper.isDisposed, "Function is disposed and should not be invoked anymore."); @@ -283,6 +285,7 @@ function _marshal_function_to_cs(arg: JSMarshalerArgument, value: Function, _?: if (arg3_converter) { arg3_js = arg3_converter(arg3); } + runtimeHelpers.isPendingSynchronousCall = true; // this is alway synchronous call for now const res_js = value(arg1_js, arg2_js, arg3_js); if (res_converter) { res_converter(res, res_js); @@ -290,6 +293,8 @@ function _marshal_function_to_cs(arg: JSMarshalerArgument, value: Function, _?: } catch (ex) { marshal_exception_to_cs(exc, ex); + } finally { + runtimeHelpers.isPendingSynchronousCall = previousPendingSynchronousCall; } }; diff --git a/src/mono/browser/runtime/marshal-to-js.ts b/src/mono/browser/runtime/marshal-to-js.ts index b58201080b49e9..29f6bcdb54c450 100644 --- a/src/mono/browser/runtime/marshal-to-js.ts +++ b/src/mono/browser/runtime/marshal-to-js.ts @@ -14,7 +14,7 @@ import { get_arg_b8, get_arg_date, get_arg_length, get_arg, set_arg_type, get_signature_arg2_type, get_signature_arg1_type, cs_to_js_marshalers, get_signature_res_type, get_arg_u16, array_element_size, get_string_root, - ArraySegment, Span, MemoryViewType, get_signature_arg3_type, get_arg_i64_big, get_arg_intptr, get_arg_element_type, JavaScriptMarshalerArgSize, proxy_debug_symbol, set_js_handle + ArraySegment, Span, MemoryViewType, get_signature_arg3_type, get_arg_i64_big, get_arg_intptr, get_arg_element_type, JavaScriptMarshalerArgSize, proxy_debug_symbol, set_js_handle, is_receiver_should_free } from "./marshal"; import { monoStringToString, utf16ToString } from "./strings"; import { GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToJs, MarshalerType, JSHandle } from "./types/internal"; @@ -55,11 +55,12 @@ export function initialize_marshalers_to_js(): void { cs_to_js_marshalers.set(MarshalerType.None, _marshal_null_to_js); cs_to_js_marshalers.set(MarshalerType.Void, _marshal_null_to_js); cs_to_js_marshalers.set(MarshalerType.Discard, _marshal_null_to_js); + cs_to_js_marshalers.set(MarshalerType.OneWay, _marshal_null_to_js); } } export function bind_arg_marshal_to_js(sig: JSMarshalerType, marshaler_type: MarshalerType, index: number): BoundMarshalerToJs | undefined { - if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void || marshaler_type === MarshalerType.Discard) { + if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void || marshaler_type === MarshalerType.Discard || marshaler_type === MarshalerType.OneWay) { return undefined; } @@ -337,6 +338,7 @@ function create_task_holder(res_converter?: MarshalerToJs) { export function mono_wasm_resolve_or_reject_promise(args: JSMarshalerArguments): void { const exc = get_arg(args, 0); + const is_async = WasmEnableThreads && is_receiver_should_free(args); try { loaderHelpers.assert_runtime_running(); @@ -351,7 +353,7 @@ export function mono_wasm_resolve_or_reject_promise(args: JSMarshalerArguments): mono_assert(holder, () => `Cannot find Promise for JSHandle ${js_handle}`); holder.resolve_or_reject(type, js_handle, arg_value); - if (WasmEnableThreads && get_arg_b8(res)) { + if (is_async) { // this works together with AllocHGlobal in JSFunctionBinding.ResolveOrRejectPromise Module._free(args as any); } @@ -361,7 +363,7 @@ export function mono_wasm_resolve_or_reject_promise(args: JSMarshalerArguments): } } catch (ex: any) { - if (WasmEnableThreads) { + if (is_async) { mono_assert(false, () => `Failed to resolve or reject promise ${ex}`); } marshal_exception_to_cs(exc, ex); diff --git a/src/mono/browser/runtime/marshal.ts b/src/mono/browser/runtime/marshal.ts index 8aca7501b8653a..91d600fc27a736 100644 --- a/src/mono/browser/runtime/marshal.ts +++ b/src/mono/browser/runtime/marshal.ts @@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles"; import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; -import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8, localHeapViewF64, localHeapViewI32, localHeapViewU8, _zero_region } from "./memory"; +import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8, localHeapViewF64, localHeapViewI32, localHeapViewU8, _zero_region, getB32, setB32 } from "./memory"; import { mono_wasm_new_external_root } from "./roots"; import { GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot, MarshalerType } from "./types/internal"; import { TypedArray, VoidPtr } from "./types/emscripten"; @@ -42,6 +42,17 @@ export function is_args_exception(args: JSMarshalerArguments): boolean { return exceptionType !== MarshalerType.None; } +export function is_receiver_should_free(args: JSMarshalerArguments): boolean { + if (WasmEnableThreads) return false; + mono_assert(args, "Null args"); + return getB32(args + 20); +} + +export function set_receiver_should_free(args: JSMarshalerArguments): void { + mono_assert(args, "Null args"); + setB32(args + 20, true); +} + export function set_args_context(args: JSMarshalerArguments): void { if (!WasmEnableThreads) return; mono_assert(args, "Null args"); diff --git a/src/mono/browser/runtime/memory.ts b/src/mono/browser/runtime/memory.ts index 0ae04672797cf2..cef0f641e50bf5 100644 --- a/src/mono/browser/runtime/memory.ts +++ b/src/mono/browser/runtime/memory.ts @@ -390,6 +390,13 @@ export function localHeapViewF64(): Float64Array { return Module.HEAPF64; } +export function copyBytes(srcPtr: VoidPtr, dstPtr: VoidPtr, bytes: number): void { + const heap = localHeapViewU8(); + const src = heap.subarray(srcPtr as any, srcPtr as any + bytes); + const dst = heap.subarray(dstPtr as any, dstPtr as any + bytes); + dst.set(src); +} + // when we run with multithreading enabled, we need to make sure that the memory views are updated on each worker // on non-MT build, this will be a no-op trimmed by rollup export function receiveWorkerHeapViews() { diff --git a/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts b/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts index 2d2c22cb1ab0e1..0acb8b6615c69d 100644 --- a/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts +++ b/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts @@ -80,6 +80,7 @@ export function replaceEmscriptenPThreadLibrary(modulePThread: PThreadLibrary): let availableThreadCount = 0; export function is_thread_available() { + if (!WasmEnableThreads) return true; return availableThreadCount > 0; } diff --git a/src/mono/browser/runtime/pthreads/shared/index.ts b/src/mono/browser/runtime/pthreads/shared/index.ts index 2e2b6a1dc71602..aadab84fa12067 100644 --- a/src/mono/browser/runtime/pthreads/shared/index.ts +++ b/src/mono/browser/runtime/pthreads/shared/index.ts @@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import BuildConfiguration from "consts:configuration"; import { ENVIRONMENT_IS_PTHREAD, Module, loaderHelpers, mono_assert, runtimeHelpers } from "../../globals"; -import { mono_log_debug, set_thread_prefix } from "../../logging"; +import { set_thread_prefix } from "../../logging"; import { bindings_init } from "../../startup"; import { forceDisposeProxies } from "../../gc-handles"; import { GCHandle, GCHandleNull, WorkerToMainMessageType, monoMessageSymbol } from "../../types/internal"; @@ -33,9 +33,11 @@ export function isMonoThreadMessage(x: unknown): x is MonoThreadMessage { export function mono_wasm_install_js_worker_interop(context_gc_handle: GCHandle): void { if (!WasmEnableThreads) return; bindings_init(); - if (!runtimeHelpers.proxyGCHandle) { - runtimeHelpers.proxyGCHandle = context_gc_handle; - mono_log_debug("Installed JSSynchronizationContext"); + mono_assert(!runtimeHelpers.proxyGCHandle, "JS interop should not be already installed on this worker."); + runtimeHelpers.proxyGCHandle = context_gc_handle; + if (ENVIRONMENT_IS_PTHREAD) { + runtimeHelpers.managedThreadTID = mono_wasm_pthread_ptr(); + runtimeHelpers.isCurrentThread = true; } Module.runtimeKeepalivePush(); monoThreadInfo.isDirtyBecauseOfInterop = true; diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index e8416dc30a4b4a..127d65dea4e818 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -25,7 +25,7 @@ import { mono_log_debug, mono_log_error, mono_log_warn } from "./logging"; // threads import { preAllocatePThreadWorkerPool, mono_wasm_init_threads } from "./pthreads/browser"; import { currentWorkerThreadEvents, dotnetPthreadCreated, initWorkerThreadEvents, monoThreadInfo } from "./pthreads/worker"; -import { update_thread_info } from "./pthreads/shared"; +import { mono_wasm_pthread_ptr, update_thread_info } from "./pthreads/shared"; import { jiterpreter_allocate_tables } from "./jiterpreter-support"; import { localHeapViewU8 } from "./memory"; import { assertNoProxies } from "./gc-handles"; @@ -516,6 +516,8 @@ export function start_runtime() { monoThreadInfo.isRegistered = true; update_thread_info(); runtimeHelpers.proxyGCHandle = install_main_synchronization_context(); + runtimeHelpers.managedThreadTID = mono_wasm_pthread_ptr(); + runtimeHelpers.isCurrentThread = true; } // get GCHandle of the ctx diff --git a/src/mono/browser/runtime/strings.ts b/src/mono/browser/runtime/strings.ts index d6fbe3a12898a1..aae59c50b69a90 100644 --- a/src/mono/browser/runtime/strings.ts +++ b/src/mono/browser/runtime/strings.ts @@ -171,7 +171,7 @@ export function stringToMonoStringRoot(string: string, result: WasmRoot): void { +function stringToInternedMonoStringRoot(string: string | symbol, result: WasmRoot): void { let text: string | undefined; if (typeof (string) === "symbol") { text = string.description; @@ -244,6 +244,7 @@ function storeStringInInternTable(string: string, root: WasmRoot, in function stringToMonoStringNewRoot(string: string, result: WasmRoot): void { const bufferLen = (string.length + 1) * 2; + // TODO this could be stack allocated const buffer = Module._malloc(bufferLen); stringToUTF16(buffer as any, buffer as any + bufferLen, string); cwraps.mono_wasm_string_from_utf16_ref(buffer, string.length, result.address); diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index adcdf194a2f02b..a2eb9b623f90c4 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -3,6 +3,7 @@ import type { AssetEntry, DotnetModuleConfig, LoadBootResourceCallback, LoadingResource, MonoConfig, RuntimeAPI, SingleAssetBehaviors } from "."; import type { PThreadLibrary } from "../pthreads/shared/emscripten-internals"; +import { PThreadPtr } from "../pthreads/shared/types"; import type { CharPtr, EmscriptenModule, ManagedPointer, NativePointer, VoidPtr, Int32Ptr } from "./emscripten"; export type GCHandle = { @@ -200,6 +201,9 @@ export type RuntimeHelpers = { getWasmIndirectFunctionTable(): WebAssembly.Table, runtimeReady: boolean, proxyGCHandle: GCHandle | undefined, + managedThreadTID: PThreadPtr, + isCurrentThread: boolean, + isPendingSynchronousCall: boolean, // true when we are in the middle of a synchronous call from managed code with the same JSProxyContext cspPolicy: boolean, allAssetsInMemory: PromiseAndController, @@ -333,6 +337,7 @@ export enum MarshalerType { Span, Action, Function, + OneWay, // only on runtime JSException, diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c index ea8faf904c2569..67d3e7a1c6a790 100644 --- a/src/mono/mono/utils/mono-threads-wasm.c +++ b/src/mono/mono/utils/mono-threads-wasm.c @@ -45,7 +45,7 @@ wasm_get_stack_size (void) return (guint8*)emscripten_stack_get_base () - (guint8*)emscripten_stack_get_end (); } -#else /* WASI */ +#else /* HOST_BROWSER -> WASI */ // TODO after https://github.com/llvm/llvm-project/commit/1532be98f99384990544bd5289ba339bca61e15b // use __stack_low && __stack_high @@ -79,7 +79,7 @@ wasm_get_stack_base (void) // this will need further change for multithreading as the stack will allocated be per thread at different addresses } -#endif +#endif /* HOST_BROWSER */ int mono_thread_info_get_system_max_stack_size (void) @@ -176,6 +176,7 @@ mono_native_thread_set_name (MonoNativeThreadId tid, const char *name) { #ifndef DISABLE_THREADS // note there is also emscripten_set_thread_name, but it only changes the name for emscripten profiler + // this only sets the name for the current thread mono_wasm_pthread_set_name (name); #endif } @@ -540,23 +541,6 @@ mono_threads_wasm_on_thread_registered (void) } #ifndef DISABLE_THREADS -void -mono_threads_wasm_async_run_in_ui_thread (void (*func) (void)) -{ - emscripten_async_run_in_main_runtime_thread (EM_FUNC_SIG_V, func); -} - -void -mono_threads_wasm_async_run_in_ui_thread_vi (void (*func) (gpointer), gpointer user_data) -{ - emscripten_async_run_in_main_runtime_thread (EM_FUNC_SIG_VI, func, user_data); -} - -void -mono_threads_wasm_async_run_in_ui_thread_vii (void (*func) (gpointer, gpointer), gpointer user_data1, gpointer user_data2) -{ - emscripten_async_run_in_main_runtime_thread (EM_FUNC_SIG_VII, func, user_data1, user_data2); -} void mono_threads_wasm_async_run_in_target_thread (pthread_t target_thread, void (*func) (void)) diff --git a/src/mono/mono/utils/mono-threads-wasm.h b/src/mono/mono/utils/mono-threads-wasm.h index 1c4934c2f9b43f..13d0b6cb76a6c8 100644 --- a/src/mono/mono/utils/mono-threads-wasm.h +++ b/src/mono/mono/utils/mono-threads-wasm.h @@ -27,21 +27,6 @@ MonoNativeThreadId mono_threads_wasm_ui_thread_tid (void); #ifndef DISABLE_THREADS -/** - * Runs the given function asynchronously on the main thread. - * See emscripten/threading.h emscripten_async_run_in_main_runtime_thread - */ -void -mono_threads_wasm_async_run_in_ui_thread (void (*func) (void)); - -/* - * Variant that takes an argument. Add more variants as needed. - */ -void -mono_threads_wasm_async_run_in_ui_thread_vi (void (*func)(gpointer), gpointer user_data); - -void -mono_threads_wasm_async_run_in_ui_thread_vii (void (*func)(gpointer, gpointer), gpointer user_data1, gpointer user_data2); void mono_threads_wasm_async_run_in_target_thread (pthread_t target_thread, void (*func) (void)); From b61e8809a19dc0e38c3bf707de7ee7627dae843c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szczepan=20=C4=86wikli=C5=84ski?= Date: Sun, 18 Feb 2024 17:59:06 +0100 Subject: [PATCH 109/158] Disable optimize ThreadStaticAccess for FreeBSD/arm64 (#98629) --- src/coreclr/vm/jitinterface.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 20c8321a7c4960..62d69c0fe4ef98 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -1580,6 +1580,8 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken, // Optimization is disabled for linux/x86 #elif defined(TARGET_LINUX_MUSL) && defined(TARGET_ARM64) // Optimization is disabled for linux musl arm64 +#elif defined(TARGET_FREEBSD) && defined(TARGET_ARM64) + // Optimization is disabled for FreeBSD/arm64 #else bool optimizeThreadStaticAccess = true; #if !defined(TARGET_OSX) && defined(TARGET_UNIX) && defined(TARGET_AMD64) From 3296daea97f517709ec428320340e77283b5cb4d Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Sun, 18 Feb 2024 10:21:13 -0800 Subject: [PATCH 110/158] JIT: Streaming mode for SPMI (#98440) Create a new SPMI (replay) mode that binds an SPMI process to a collection and jit and initial options set, and then repeatedly reads method numbers and overriding jit options from a file until the file is closed, or contains a line beginning with "stop". Clients can poll the stdout from this mode to extract output from the jit for each invocation, end-delimited by `[streaming] Done.` This seems to be quite a bit faster than launching a new process for each invocation. For example, given a reply file like ``` 49974!JitRLCSE=!JitRLCSEAlpha=0.02!JitRandomCSE=161 49974!JitRLCSE=!JitRLCSEAlpha=0.02!JitRandomCSE=171 49974!JitRLCSE=!JitRLCSEAlpha=0.02!JitRandomCSE=161!JitReplayCSE=1,0!JitReplayCSEReward=0,0 49974!JitRLCSE=!JitRLCSEAlpha=0.02!JitRandomCSE=171!JitReplayCSE=0!JitReplayCSEReward=0 stop ``` one can now do something like: ``` %% superpmi.exe -v q -jitoption JitMetrics=1 clrjit.dll collection.mch -streaming 49974-replay.txt ; Total bytes of code 119, prolog size 6, PerfScore 30.75, instruction count 35, allocated bytes for code 119, num cse 1 num cand 1 RL Policy Gradient Stochastic seq 1,0 likelihoods 0.500,1.000 baseLikelihoods 0.000,0.500,1.000,0.500 spmi index 49974 (MethodHash=870c8ba4) for method System.Threading.Channels.AsyncOperation`1[System.__Canon]:GetResult(short):System.__Canon:this (Tier1) [streaming] Done. ; Total bytes of code 117, prolog size 6, PerfScore 32.50, instruction count 34, allocated bytes for code 117, num cse 0 num cand 1 RL Policy Gradient Stochastic seq 0 likelihoods 0.500 baseLikelihoods 0.000,0.500,1.000,0.500 spmi index 49974 (MethodHash=870c8ba4) for method System.Threading.Channels.AsyncOperation`1[System.__Canon]:GetResult(short):System.__Canon:this (Tier1) [streaming] Done. ; Total bytes of code 119, prolog size 6, PerfScore 30.75, instruction count 35, allocated bytes for code 119, num cse 1 num cand 1 RL Policy Gradient Update seq 1,0 updatedparams 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000 spmi index 49974 (MethodHash=870c8ba4) for method System.Threading.Channels.AsyncOperation`1[System.__Canon]:GetResult(short):System.__Canon:this (Tier1) [streaming] Done. ; Total bytes of code 117, prolog size 6, PerfScore 32.50, instruction count 34, allocated bytes for code 117, num cse 0 num cand 1 RL Policy Gradient Update seq 0 updatedparams 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000 spmi index 49974 (MethodHash=870c8ba4) for method System.Threading.Channels.AsyncOperation`1[System.__Canon]:GetResult(short):System.__Canon:this (Tier1) [streaming] Done. ``` The input format is like shown above, a method number and then option settings prefixed by `!`. Lines starting with `#` are ignored. `-streaming stdin` is also supported, for your interactive SPMI needs. --- src/coreclr/jit/codegencommon.cpp | 3 +- src/coreclr/jit/optcse.cpp | 12 +- src/coreclr/jit/utils.h | 8 + .../superpmi/superpmi-shared/methodcontext.h | 7 + .../superpmi-shared/methodcontextreader.cpp | 9 + .../superpmi-shared/methodcontextreader.h | 3 + .../tools/superpmi/superpmi/CMakeLists.txt | 1 + .../tools/superpmi/superpmi/commandline.cpp | 33 +- .../tools/superpmi/superpmi/commandline.h | 3 + .../tools/superpmi/superpmi/jitinstance.cpp | 5 + .../tools/superpmi/superpmi/jitinstance.h | 2 + .../superpmi/superpmi/streamingsuperpmi.cpp | 318 ++++++++++++++++++ .../tools/superpmi/superpmi/superpmi.cpp | 6 + 13 files changed, 402 insertions(+), 8 deletions(-) create mode 100644 src/coreclr/tools/superpmi/superpmi/streamingsuperpmi.cpp diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index c468473067b6e1..825837fe45ef50 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -2075,7 +2075,8 @@ void CodeGen::genEmitMachineCode() { printf("; ============================================================\n\n"); } - printf(""); // in our logic this causes a flush + + fflush(jitstdout()); } if (verbose) diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index 99d03f5958054c..e4b3c9faeff4fb 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -2254,7 +2254,7 @@ void CSE_HeuristicReplay::ConsiderCandidates() return; } - static ConfigIntArray JitReplayCSEArray; + ConfigIntArray JitReplayCSEArray; JitReplayCSEArray.EnsureInit(JitConfig.JitReplayCSE()); for (unsigned i = 0; i < JitReplayCSEArray.GetLength(); i++) @@ -2312,7 +2312,7 @@ CSE_HeuristicRL::CSE_HeuristicRL(Compiler* pCompiler) // Parameters // - static ConfigDoubleArray initialParameters; + ConfigDoubleArray initialParameters; initialParameters.EnsureInit(JitConfig.JitRLCSE()); const unsigned initialParamLength = initialParameters.GetLength(); @@ -2359,7 +2359,7 @@ CSE_HeuristicRL::CSE_HeuristicRL(Compiler* pCompiler) // Reward // - static ConfigDoubleArray rewards; + ConfigDoubleArray rewards; rewards.EnsureInit(JitConfig.JitReplayCSEReward()); const unsigned rewardsLength = rewards.GetLength(); @@ -2377,7 +2377,7 @@ CSE_HeuristicRL::CSE_HeuristicRL(Compiler* pCompiler) // if (JitConfig.JitRLCSEAlpha() != nullptr) { - static ConfigDoubleArray JitRLCSEAlphaArray; + ConfigDoubleArray JitRLCSEAlphaArray; JitRLCSEAlphaArray.EnsureInit(JitConfig.JitRLCSEAlpha()); m_alpha = JitRLCSEAlphaArray.GetData()[0]; } @@ -3276,8 +3276,8 @@ void CSE_HeuristicRL::UpdateParameters() return; } - ArrayStack choices(m_pCompiler->getAllocator(CMK_CSE)); - static ConfigIntArray JitReplayCSEArray; + ArrayStack choices(m_pCompiler->getAllocator(CMK_CSE)); + ConfigIntArray JitReplayCSEArray; JitReplayCSEArray.EnsureInit(JitConfig.JitReplayCSE()); // We have an undiscounted reward, so it applies equally diff --git a/src/coreclr/jit/utils.h b/src/coreclr/jit/utils.h index 5378a08422365e..0b1b6840be6ec3 100644 --- a/src/coreclr/jit/utils.h +++ b/src/coreclr/jit/utils.h @@ -246,6 +246,10 @@ class ConfigMethodRange class ConfigIntArray { public: + ConfigIntArray() : m_values(nullptr), m_length(0) + { + } + // Ensure the string has been parsed. void EnsureInit(const WCHAR* str) { @@ -276,6 +280,10 @@ class ConfigIntArray class ConfigDoubleArray { public: + ConfigDoubleArray() : m_values(nullptr), m_length(0) + { + } + // Ensure the string has been parsed. void EnsureInit(const WCHAR* str) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 3344552ca06058..74128026015006 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -886,6 +886,13 @@ class MethodContext bool WasEnvironmentChanged(const Environment& prevEnv); + void Reset() + { + delete cr; + FreeTempAllocations(); + cr = new CompileResult(); + } + CompileResult* cr; CompileResult* originalCR; int index; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontextreader.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontextreader.cpp index 2937661c044dd4..14f1d37dde61f7 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontextreader.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontextreader.cpp @@ -619,3 +619,12 @@ bool MethodContextReader::IsMethodExcluded(MethodContext* mc) } return false; } + +void MethodContextReader::Reset(const int* newIndexes, int newIndexCount) +{ + Indexes = newIndexes; + IndexCount = newIndexCount; + curIndexPos = 0; + curMCIndex = 0; + curTOCIndex = 0; +} diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontextreader.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontextreader.h index c46f40a3600365..b3c77e1637efeb 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontextreader.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontextreader.h @@ -149,6 +149,9 @@ class MethodContextReader // Return should this method context be excluded from the replay or not. bool IsMethodExcluded(MethodContext* mc); + + // Reset for reading a new sequence of method indices + void Reset(const int* newIndexes, int newIndexCount); }; #pragma pack(pop) diff --git a/src/coreclr/tools/superpmi/superpmi/CMakeLists.txt b/src/coreclr/tools/superpmi/superpmi/CMakeLists.txt index 5dd42eb5631b26..ccb766f0d0788b 100644 --- a/src/coreclr/tools/superpmi/superpmi/CMakeLists.txt +++ b/src/coreclr/tools/superpmi/superpmi/CMakeLists.txt @@ -33,6 +33,7 @@ set(SUPERPMI_SOURCES methodstatsemitter.cpp neardiffer.cpp parallelsuperpmi.cpp + streamingsuperpmi.cpp superpmi.cpp fileio.cpp jithost.cpp diff --git a/src/coreclr/tools/superpmi/superpmi/commandline.cpp b/src/coreclr/tools/superpmi/superpmi/commandline.cpp index 6635995f65bacf..ddd42e3a2e098b 100644 --- a/src/coreclr/tools/superpmi/superpmi/commandline.cpp +++ b/src/coreclr/tools/superpmi/superpmi/commandline.cpp @@ -110,6 +110,11 @@ void CommandLine::DumpHelp(const char* program) printf(" If 'workerCount' is not specified, the number of workers used is\n"); printf(" the number of processors on the machine.\n"); printf("\n"); + printf(" -streaming filename\n"); + printf(" Streaming mode. Read and execute work requests from indicated file (can be 'stdin').\n"); + printf(" Each line is a method context number and additional force jit options for that method.\n"); + printf(" Blank line or EOF terminates\n"); + printf("\n"); printf(" -failureLimit \n"); printf(" For a positive 'limit' number, replay and asm diffs will exit if it sees more than 'limit' failures.\n"); printf(" Otherwise, all methods will be compiled.\n"); @@ -170,7 +175,7 @@ void CommandLine::DumpHelp(const char* program) printf(" ; if there are any failures, record their MC numbers in the file fail.mcl\n"); } -static bool ParseJitOption(const char* optionString, WCHAR** key, WCHAR** value) +bool CommandLine::ParseJitOption(const char* optionString, WCHAR** key, WCHAR** value) { char tempKey[1024]; @@ -468,6 +473,17 @@ bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o) } o->hash = argv[i]; } + else if ((_strnicmp(&argv[i][1], "streaming", argLen) == 0)) + { + if (++i >= argc) + { + LogError("'-streaming' must be followed by a file name or 'stdin'."); + DumpHelp(argv[0]); + return false; + } + + o->streamFile = argv[i]; + } else if ((_strnicmp(&argv[i][1], "parallel", argLen) == 0)) { o->parallel = true; @@ -677,6 +693,21 @@ bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o) } } + if (o->streamFile != nullptr) + { + if (o->parallel) + { + LogError("streaming mode and parallel mode are incompatible."); + return false; + } + + if (o->nameOfJit2 != nullptr) + { + LogError("streaming mode and diff mode are incompatible."); + return false; + } + } + SPMI_TARGET_ARCHITECTURE defaultSpmiTargetArchitecture = GetSpmiTargetArchitecture(); SetSuperPmiTargetArchitecture(o->targetArchitecture); diff --git a/src/coreclr/tools/superpmi/superpmi/commandline.h b/src/coreclr/tools/superpmi/superpmi/commandline.h index c9a9bcb46a798e..6c31264eadcc01 100644 --- a/src/coreclr/tools/superpmi/superpmi/commandline.h +++ b/src/coreclr/tools/superpmi/superpmi/commandline.h @@ -25,6 +25,7 @@ class CommandLine bool ignoreStoredConfig = false; bool applyDiff = false; bool parallel = false; // User specified to use /parallel mode. + char* streamFile = nullptr; #if !defined(USE_MSVCDIS) && defined(USE_COREDISTOOLS) bool useCoreDisTools = true; // Use CoreDisTools library instead of Msvcdis #else @@ -58,6 +59,8 @@ class CommandLine LightWeightMap** pJitOptions, LightWeightMap** pForceJitOptions); + static bool ParseJitOption(const char* optionString, WCHAR** key, WCHAR** value); + private: static void DumpHelp(const char* program); }; diff --git a/src/coreclr/tools/superpmi/superpmi/jitinstance.cpp b/src/coreclr/tools/superpmi/superpmi/jitinstance.cpp index d13e0c75800c87..7c55db51af20cf 100644 --- a/src/coreclr/tools/superpmi/superpmi/jitinstance.cpp +++ b/src/coreclr/tools/superpmi/superpmi/jitinstance.cpp @@ -641,3 +641,8 @@ const MethodContext::Environment& JitInstance::getEnvironment() { return environment; } + +void JitInstance::updateForceOptions(LightWeightMap* newForceOptions) +{ + forceOptions = newForceOptions; +} diff --git a/src/coreclr/tools/superpmi/superpmi/jitinstance.h b/src/coreclr/tools/superpmi/superpmi/jitinstance.h index 42f1f4ade7c465..03e283c1d5b62d 100644 --- a/src/coreclr/tools/superpmi/superpmi/jitinstance.h +++ b/src/coreclr/tools/superpmi/superpmi/jitinstance.h @@ -81,6 +81,8 @@ class JitInstance void* allocateLongLivedArray(size_t size); void freeArray(void* array); void freeLongLivedArray(void* array); + + void updateForceOptions(LightWeightMap* newForceOptions); }; #endif diff --git a/src/coreclr/tools/superpmi/superpmi/streamingsuperpmi.cpp b/src/coreclr/tools/superpmi/superpmi/streamingsuperpmi.cpp new file mode 100644 index 00000000000000..4da3f0f0561fbd --- /dev/null +++ b/src/coreclr/tools/superpmi/superpmi/streamingsuperpmi.cpp @@ -0,0 +1,318 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "standardpch.h" +#include "superpmi.h" +#include "jitinstance.h" +#include "simpletimer.h" +#include "mclist.h" +#include "lightweightmap.h" +#include "commandline.h" +#include "errorhandling.h" +#include "methodcontext.h" +#include "methodcontextreader.h" +#include "spmiutil.h" +#include "fileio.h" +#include "commandline.h" + +#if defined(_WIN32) +#define strtok_r strtok_s +#endif + +static bool AddJitOption(LightWeightMap* map, char* newOption) +{ + WCHAR* key; + WCHAR* value; + + if (!CommandLine::ParseJitOption(newOption, &key, &value)) + { + return false; + } + + DWORD keyIndex = + (DWORD)map->AddBuffer((unsigned char*)key, sizeof(WCHAR) * ((unsigned int)u16_strlen(key) + 1)); + DWORD valueIndex = + (DWORD)map->AddBuffer((unsigned char*)value, sizeof(WCHAR) * ((unsigned int)u16_strlen(value) + 1)); + map->Add(keyIndex, valueIndex); + + delete[] key; + delete[] value; + + return true; +} + +struct CacheEntry +{ + MethodContext* mc; + int age; +}; + +static MethodContext* getMethodContext(int index, MethodContextReader* reader) +{ + enum { CACHE_SIZE = 100 }; + static CacheEntry cache[CACHE_SIZE] = {}; + static int count = 0; + static int age = 0; + int i = 0; + + // Search the cache + // + for (; i < count; i++) + { + if (cache[i].mc->index == index) + { + break; + } + } + + if (i == count) + { + // Method not found in cache + // + LogDebug("[streaming] loading MC %i from file", index); + if (i == CACHE_SIZE) + { + // Cache is full, evict oldest entry + // + int oldestAge = age; + int oldestEntry = -1; + for (int j = 0; j < CACHE_SIZE; j++) + { + if (cache[j].age < oldestAge) + { + oldestEntry = j; + oldestAge = cache[j].age; + } + } + + LogDebug("[streaming] evicting MC %i from cache", cache[oldestEntry].mc->index); + delete cache[oldestEntry].mc; + cache[oldestEntry].mc = nullptr; + i = oldestEntry; + } + else + { + count++; + } + + reader->Reset(&index, 1); + MethodContextBuffer mcb = reader->GetNextMethodContext(); + + if (mcb.Error()) + { + return nullptr; + } + + MethodContext* mc = nullptr; + if (!MethodContext::Initialize(index, mcb.buff, mcb.size, &mc)) + { + return nullptr; + } + + cache[i].mc = mc; + } + else + { + LogDebug("[streaming] found MC %i in cache", index); + } + + // Move to front... + // + if (i != 0) + { + CacheEntry temp = cache[0]; + cache[0] = cache[i]; + cache[i] = temp; + } + + cache[0].age = age++; + return cache[0].mc; +} + +int doStreamingSuperPMI(CommandLine::Options& o) +{ + HRESULT hr = E_FAIL; + SimpleTimer st; + st.Start(); + + FILE* streamFile = nullptr; + if (_stricmp(o.streamFile, "stdin") == 0) + { + streamFile = stdin; + } + else + { + streamFile = fopen(o.streamFile, "r"); + } + + if (streamFile == nullptr) + { + LogError("Failed to open file '%s'. GetLastError()=%u", o.streamFile, GetLastError()); + return 1; + } + + // Just one worker for now... all method selection done via stream file + // + o.workerCount = 1; + o.indexes = nullptr; + o.indexCount = -1; + o.hash = nullptr; + o.offset = -1; + o.increment = -1; + + // The method context reader handles skipping any unrequested method contexts + // Used in conjunction with an MCI file, it does a lot less work... + MethodContextReader* reader = + new MethodContextReader(o.nameOfInputMethodContextFile, o.indexes, o.indexCount, o.hash, o.offset, o.increment); + if (!reader->isValid()) + { + return (int)SpmiResult::GeneralFailure; + } + + JitInstance* jit = nullptr; + + enum { BUFFER_SIZE = 2048 }; + + char line[BUFFER_SIZE]; + const char* const seps = "!"; + char *next = nullptr; + + // Syntax is dddd { ! =value }* + // Likes starting with '#' are ignored + // + while (fgets(line, BUFFER_SIZE, streamFile) != nullptr) + { + for (int i = 0; i < BUFFER_SIZE; i++) + { + if (line[i] == '\n' || line[i] == '\r') + { + line[i]= 0; + break; + } + } + line[BUFFER_SIZE - 1] = '0'; + + LogDebug("[streaming] Request: '%s'", line); + + if (line[0] == '#') + { + continue; + } + + if (strncmp(line, "quit", 4) == 0) + { + LogDebug("[streaming] Quitting"); + break; + } + + char* tok = strtok_r(line, seps, &next); + const int index = atoi(tok); + + if (index == 0) + { + LogDebug("[streaming] Stopping"); + break; + } + + LogDebug("[streaming] Method %d", index); + + LightWeightMap* baseForceJitOptions = o.forceJitOptions; + LightWeightMap* forceJitOptions = nullptr; + bool skip = false; + + while ((tok = strtok_r(nullptr, seps, &next))) + { + if (forceJitOptions == nullptr) + { + if (baseForceJitOptions == nullptr) + { + forceJitOptions = new LightWeightMap(); + } + else + { + forceJitOptions = new LightWeightMap(*baseForceJitOptions); + } + } + + bool added = AddJitOption(forceJitOptions, tok); + + if (!added) + { + LogInfo("[streaming] unable to parse option '%s'", tok); + skip = true; + break; + } + } + + if (skip) + { + continue; + } + + LogDebug("[streaming] Launching..."); + MethodContext* const mc = getMethodContext(index, reader); + + if (mc == nullptr) + { + return (int)SpmiResult::GeneralFailure; + } + + if (mc->index != index) + { + LogDebug("MC cache lookup failure, wanted index %d, got index %d\n", index, mc->index); + return (int)SpmiResult::GeneralFailure; + } + + if (jit == nullptr) + { + LogDebug("[streaming] loading jit %s", o.nameOfJit); + SimpleTimer stInitJit; + jit = JitInstance::InitJit(o.nameOfJit, o.breakOnAssert, &stInitJit, mc, forceJitOptions, o.jitOptions); + + if (jit == nullptr) + { + // InitJit already printed a failure message + return (int)SpmiResult::JitFailedToInit; + } + } + else + { + jit->updateForceOptions(forceJitOptions); + jit->resetConfig(mc); + } + + LogDebug("[streaming] invoking jit"); + fflush(stdout); + + bool collectThroughput = false; + ReplayResults res = jit->CompileMethod(mc, reader->GetMethodContextIndex(), collectThroughput); + + if (res.Result == ReplayResult::Success) + { + if (Logger::IsLogLevelEnabled(LOGLEVEL_DEBUG)) + { + mc->cr->dumpToConsole(); // Dump the compile results if doing debug logging + } + } + else if (res.Result == ReplayResult::Error) + { + LogDebug("[streaming] jit compilation failed"); + + LogError("Method %d of size %d failed to load and compile correctly%s (%s).", + reader->GetMethodContextIndex(), mc->methodSize, + (o.nameOfJit2 == nullptr) ? "" : " by JIT1", o.nameOfJit); + } + + // Protocol with clients is for them to read stdout. Let them know we're done. + // + printf("[streaming] Done. Status=%d\n", (int) res.Result); + fflush(stdout); + + // Cleanup + // + delete forceJitOptions; + mc->Reset(); + } + + return (int)SpmiResult::Success; +} diff --git a/src/coreclr/tools/superpmi/superpmi/superpmi.cpp b/src/coreclr/tools/superpmi/superpmi/superpmi.cpp index 0410b14846db4a..4ac03d18ce9a6d 100644 --- a/src/coreclr/tools/superpmi/superpmi/superpmi.cpp +++ b/src/coreclr/tools/superpmi/superpmi/superpmi.cpp @@ -21,6 +21,7 @@ #include "fileio.h" extern int doParallelSuperPMI(CommandLine::Options& o); +extern int doStreamingSuperPMI(CommandLine::Options& o); // NOTE: these output status strings are parsed by parallelsuperpmi.cpp::ProcessChildStdOut(). // There must be a single, fixed prefix common to all strings, to ease the determination of when @@ -230,6 +231,11 @@ int __cdecl main(int argc, char* argv[]) return doParallelSuperPMI(o); } + if (o.streamFile != nullptr) + { + return doStreamingSuperPMI(o); + } + SetBreakOnException(o.breakOnException); if (o.methodStatsTypes != NULL && From 7bcb5a8e3700084cf8fc21c4b34ef0846c6a82c5 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 19 Feb 2024 08:34:38 +0100 Subject: [PATCH 111/158] [browser][MT] try to fix "Invalid atomic access index" - more (#98640) --- src/mono/browser/runtime/marshal-to-cs.ts | 4 +++- src/mono/browser/runtime/marshal.ts | 5 ++++- src/mono/browser/runtime/scheduling.ts | 3 +++ src/mono/browser/runtime/web-socket.ts | 14 +++++++++++++- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/mono/browser/runtime/marshal-to-cs.ts b/src/mono/browser/runtime/marshal-to-cs.ts index 54ab212258ce5c..132de0f75dcd42 100644 --- a/src/mono/browser/runtime/marshal-to-cs.ts +++ b/src/mono/browser/runtime/marshal-to-cs.ts @@ -18,7 +18,7 @@ import { set_arg_element_type, ManagedObject, JavaScriptMarshalerArgSize, proxy_debug_symbol, get_arg_gc_handle, get_arg_type } from "./marshal"; import { get_marshaler_to_js_by_type } from "./marshal-to-js"; -import { _zero_region, localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory"; +import { _zero_region, forceThreadMemoryViewRefresh, localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory"; import { stringToMonoStringRoot, stringToUTF16 } from "./strings"; import { JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToCs, MarshalerType } from "./types/internal"; import { TypedArray } from "./types/emscripten"; @@ -359,6 +359,7 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: mono_assert(!holder.isCanceled, "This promise already canceled."); holder.isResolved = true; if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); settleUnsettledPromise(); } // we can unregister the GC handle just on JS side @@ -387,6 +388,7 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: mono_assert(!holder.isResolved, "This promise already resolved."); holder.isResolved = true; if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); settleUnsettledPromise(); } // we can unregister the GC handle just on JS side diff --git a/src/mono/browser/runtime/marshal.ts b/src/mono/browser/runtime/marshal.ts index 91d600fc27a736..6d0f6ec0308fa7 100644 --- a/src/mono/browser/runtime/marshal.ts +++ b/src/mono/browser/runtime/marshal.ts @@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { js_owned_gc_handle_symbol, teardown_managed_proxy } from "./gc-handles"; import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; -import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8, localHeapViewF64, localHeapViewI32, localHeapViewU8, _zero_region, getB32, setB32 } from "./memory"; +import { getF32, getF64, getI16, getI32, getI64Big, getU16, getU32, getU8, setF32, setF64, setI16, setI32, setI64Big, setU16, setU32, setU8, localHeapViewF64, localHeapViewI32, localHeapViewU8, _zero_region, getB32, setB32, forceThreadMemoryViewRefresh } from "./memory"; import { mono_wasm_new_external_root } from "./roots"; import { GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot, MarshalerType } from "./types/internal"; import { TypedArray, VoidPtr } from "./types/emscripten"; @@ -24,6 +24,9 @@ export const JSMarshalerTypeSize = 32; export const JSMarshalerSignatureHeaderSize = 4 * 8; // without Exception and Result export function alloc_stack_frame(size: number): JSMarshalerArguments { + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } const bytes = JavaScriptMarshalerArgSize * size; const args = Module.stackAlloc(bytes) as any; _zero_region(args, bytes); diff --git a/src/mono/browser/runtime/scheduling.ts b/src/mono/browser/runtime/scheduling.ts index 4651516271fe6b..4b01536b735ee9 100644 --- a/src/mono/browser/runtime/scheduling.ts +++ b/src/mono/browser/runtime/scheduling.ts @@ -34,6 +34,9 @@ function prevent_timer_throttling_tick() { if (!loaderHelpers.is_runtime_running()) { return; } + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } cwraps.mono_wasm_execute_timer(); pump_count++; mono_background_exec_until_done(); diff --git a/src/mono/browser/runtime/web-socket.ts b/src/mono/browser/runtime/web-socket.ts index b40e9e5daf4862..6c558938cface6 100644 --- a/src/mono/browser/runtime/web-socket.ts +++ b/src/mono/browser/runtime/web-socket.ts @@ -6,7 +6,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { prevent_timer_throttling } from "./scheduling"; import { Queue } from "./queue"; import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, createPromiseController, loaderHelpers, mono_assert } from "./globals"; -import { setI32, localHeapViewU8 } from "./memory"; +import { setI32, localHeapViewU8, forceThreadMemoryViewRefresh } from "./memory"; import { VoidPtr } from "./types/emscripten"; import { PromiseController } from "./types/internal"; import { mono_log_warn } from "./logging"; @@ -69,6 +69,9 @@ export function ws_wasm_create(uri: string, sub_protocols: string[] | null, rece try { if (ws[wasm_ws_is_aborted]) return; if (!loaderHelpers.is_runtime_running()) return; + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } open_promise_control.resolve(ws); prevent_timer_throttling(); } catch (error: any) { @@ -79,6 +82,9 @@ export function ws_wasm_create(uri: string, sub_protocols: string[] | null, rece try { if (ws[wasm_ws_is_aborted]) return; if (!loaderHelpers.is_runtime_running()) return; + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } web_socket_on_message(ws, ev); prevent_timer_throttling(); } catch (error: any) { @@ -90,6 +96,9 @@ export function ws_wasm_create(uri: string, sub_protocols: string[] | null, rece ws.removeEventListener("message", local_on_message); if (ws[wasm_ws_is_aborted]) return; if (!loaderHelpers.is_runtime_running()) return; + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } ws[wasm_ws_close_received] = true; ws["close_status"] = ev.code; @@ -119,6 +128,9 @@ export function ws_wasm_create(uri: string, sub_protocols: string[] | null, rece try { if (ws[wasm_ws_is_aborted]) return; if (!loaderHelpers.is_runtime_running()) return; + if (WasmEnableThreads) { + forceThreadMemoryViewRefresh(); + } ws.removeEventListener("message", local_on_message); const message = ev.message ? "WebSocket error: " + ev.message From e42e18c7fa8d2eb8c628169aafa5b59a0219d3bf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 09:47:08 +0100 Subject: [PATCH 112/158] Automated bump of chrome version (#98624) Co-authored-by: github-actions[bot] --- eng/testing/ChromeVersions.props | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/eng/testing/ChromeVersions.props b/eng/testing/ChromeVersions.props index 9cea0303f97b79..8549c7646a516d 100644 --- a/eng/testing/ChromeVersions.props +++ b/eng/testing/ChromeVersions.props @@ -1,13 +1,12 @@ - 120.0.6099.129 - 1217362 - https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1217362 - 12.0.267 - - 120.0.6099.130 - 1217362 - https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/1217378 - 12.0.267 + 121.0.6167.184 + 1233107 + https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1233114 + 12.1.285 + 121.0.6167.185 + 1233107 + https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/1233136 + 12.1.285 - + \ No newline at end of file From a1bdd0be07da94faf1ddedadde469c6f247f1a2b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 19 Feb 2024 03:52:28 -0500 Subject: [PATCH 113/158] Ensure file-opening exceptions emerge asynchronously from File.Append/WriteAllLinesAsync (#98612) --- .../src/System/IO/File.cs | 41 +++++++++++-------- .../File/ReadWriteAllLinesAsync.cs | 7 +++- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index ec17866f079217..9326f6533306e4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -911,11 +911,6 @@ private static StreamReader AsyncStreamReader(string path, Encoding encoding) new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan), encoding, detectEncodingFromByteOrderMarks: true); - private static StreamWriter AsyncStreamWriter(string path, Encoding encoding, bool append) - => new StreamWriter( - new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous), - encoding); - public static Task ReadAllTextAsync(string path, CancellationToken cancellationToken = default(CancellationToken)) => ReadAllTextAsync(path, Encoding.UTF8, cancellationToken); @@ -1116,13 +1111,31 @@ private static async Task InternalReadAllLinesAsync(string path, Encod public static Task WriteAllLinesAsync(string path, IEnumerable contents, CancellationToken cancellationToken = default(CancellationToken)) => WriteAllLinesAsync(path, contents, UTF8NoBOM, cancellationToken); - public static Task WriteAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken)) + public static Task WriteAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken)) => + WriteAllLinesAsync(path, contents, encoding, append: false, cancellationToken); + + private static Task WriteAllLinesAsync(string path, IEnumerable contents, Encoding encoding, bool append, CancellationToken cancellationToken) { Validate(path, encoding); ArgumentNullException.ThrowIfNull(contents); - return cancellationToken.IsCancellationRequested - ? Task.FromCanceled(cancellationToken) - : InternalWriteAllLinesAsync(AsyncStreamWriter(path, encoding, append: false), contents, cancellationToken); + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + StreamWriter writer; + try + { + writer = new StreamWriter( + new FileStream(path, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read, DefaultBufferSize, FileOptions.Asynchronous), + encoding); + } + catch (Exception e) + { + return Task.FromException(e); + } + + return InternalWriteAllLinesAsync(writer, contents, cancellationToken); } private static async Task InternalWriteAllLinesAsync(StreamWriter writer, IEnumerable contents, CancellationToken cancellationToken) @@ -1159,14 +1172,8 @@ private static async Task InternalWriteAllLinesAsync(StreamWriter writer, IEnume public static Task AppendAllLinesAsync(string path, IEnumerable contents, CancellationToken cancellationToken = default(CancellationToken)) => AppendAllLinesAsync(path, contents, UTF8NoBOM, cancellationToken); - public static Task AppendAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken)) - { - Validate(path, encoding); - ArgumentNullException.ThrowIfNull(contents); - return cancellationToken.IsCancellationRequested - ? Task.FromCanceled(cancellationToken) - : InternalWriteAllLinesAsync(AsyncStreamWriter(path, encoding, append: true), contents, cancellationToken); - } + public static Task AppendAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken)) => + WriteAllLinesAsync(path, contents, encoding, append: true, cancellationToken); /// /// Creates a file symbolic link identified by that points to . diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/ReadWriteAllLinesAsync.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/ReadWriteAllLinesAsync.cs index 441771f05675e6..0dc72cab3f42f0 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/ReadWriteAllLinesAsync.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/ReadWriteAllLinesAsync.cs @@ -98,8 +98,11 @@ public async Task OpenFile_ThrowsIOExceptionAsync() using (File.Create(path)) { - await Assert.ThrowsAsync(async () => await WriteAsync(path, lines)); - await Assert.ThrowsAsync(async () => await ReadAsync(path)); + Task t = WriteAsync(path, lines); + await Assert.ThrowsAsync(async () => await t); + + t = ReadAsync(path); + await Assert.ThrowsAsync(async () => await t); } } From 0f6df43ff9bfb98f0fcac781125ce9b4e5b5ba66 Mon Sep 17 00:00:00 2001 From: JongHeonChoi Date: Mon, 19 Feb 2024 18:08:50 +0900 Subject: [PATCH 114/158] [RISC-V] Set setFrameRequired like the other archs (#98535) --- src/coreclr/inc/clrconfigvalues.h | 5 ----- src/coreclr/jit/regalloc.cpp | 10 ++++++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 12563f8f970502..30956bf4a67418 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -304,12 +304,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitDebuggable, W("JitDebuggable"), 0, "If set, #endif RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitEnableNoWayAssert, W("JitEnableNoWayAssert"), INTERNAL_JitEnableNoWayAssert_Default, "") -#if defined(TARGET_RISCV64) -// TODO-RISCV64-CQ: In RISCV64, currently jitc always generates JitFramed codes. -RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JitFramed, W("JitFramed"), 1, "Forces EBP frames") -#else RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JitFramed, W("JitFramed"), 0, "Forces EBP frames") -#endif // TARGET_RISCV64 CONFIG_DWORD_INFO(INTERNAL_JitThrowOnAssertionFailure, W("JitThrowOnAssertionFailure"), 0, "Throw managed exception on assertion failures during JIT instead of failfast") CONFIG_DWORD_INFO(INTERNAL_JitGCStress, W("JitGCStress"), 0, "GC stress mode for jit") CONFIG_DWORD_INFO(INTERNAL_JitHeartbeat, W("JitHeartbeat"), 0, "") diff --git a/src/coreclr/jit/regalloc.cpp b/src/coreclr/jit/regalloc.cpp index fa2ef10764a5a1..211452f0076b35 100644 --- a/src/coreclr/jit/regalloc.cpp +++ b/src/coreclr/jit/regalloc.cpp @@ -262,6 +262,16 @@ bool Compiler::rpMustCreateEBPFrame(INDEBUG(const char** wbReason)) } #endif // TARGET_LOONGARCH64 +#ifdef TARGET_RISCV64 + // TODO-RISCV64-NYI: This is temporary: force a frame pointer-based frame until genFnProlog + // can handle non-frame pointer frames. + if (!result) + { + INDEBUG(reason = "Temporary RISCV64 force frame pointer"); + result = true; + } +#endif // TARGET_RISCV64 + #ifdef DEBUG if ((result == true) && (wbReason != nullptr)) { From 85679d284c0965a99b4b48d7051510e34ce9f4b3 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 19 Feb 2024 02:02:20 -0800 Subject: [PATCH 115/158] [wasm] New jiterpreter backbranch scan pass (#98530) Remove the C code responsible for generating the backward_branch_offsets table stored in InterpMethod that was used by the jiterpreter. Add a new pre-pass to the jiterpreter that does a quick opcode scan to find back-branch targets on the fly when a trace is actually being compiled. Reimplement interpreter branch decoding in a central place in the jiterpreter, and generalize it based on opcode info tables instead of a bunch of hand-written decode logic. Remove some dead options and dead code from the jiterpreter typescript. Fix an enum that got out of sync. Minor debug logging improvements. --- .../browser/runtime/jiterpreter-opcodes.ts | 44 +++--- .../browser/runtime/jiterpreter-support.ts | 3 + .../runtime/jiterpreter-trace-generator.ts | 129 ++++++++++++++---- src/mono/browser/runtime/jiterpreter.ts | 24 +--- src/mono/mono/mini/interp/interp-internals.h | 2 - src/mono/mono/mini/interp/jiterpreter.c | 6 +- src/mono/mono/mini/interp/mintops.h | 1 + src/mono/mono/mini/interp/transform.c | 33 ----- 8 files changed, 140 insertions(+), 102 deletions(-) diff --git a/src/mono/browser/runtime/jiterpreter-opcodes.ts b/src/mono/browser/runtime/jiterpreter-opcodes.ts index 9c046ebf8319c0..d535070df2ae8b 100644 --- a/src/mono/browser/runtime/jiterpreter-opcodes.ts +++ b/src/mono/browser/runtime/jiterpreter-opcodes.ts @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// Keep this file in sync with mintops.def. The order and values need to match exactly. - import cwraps from "./cwraps"; import { utf8ToString } from "./strings"; import { OpcodeInfoType } from "./jiterpreter-enums"; @@ -28,27 +26,29 @@ export type SimdInfoTable = { [argument_count: number]: SimdInfoSubtable } +// Keep in sync with mintops.h export const enum MintOpArgType { - MintOpNoArgs = 0, - MintOpShortInt, - MintOpUShortInt, - MintOpInt, - MintOpLongInt, - MintOpFloat, - MintOpDouble, - MintOpBranch, - MintOpShortBranch, - MintOpSwitch, - MintOpMethodToken, - MintOpFieldToken, - MintOpClassToken, - MintOpTwoShorts, - MintOpTwoInts, - MintOpShortAndInt, - MintOpShortAndShortBranch, - MintOpPair2, - MintOpPair3, - MintOpPair4 + MintOpNoArgs = 0, + MintOpShortInt, + MintOpUShortInt, + MintOpInt, + MintOpLongInt, + MintOpFloat, + MintOpDouble, + MintOpBranch, + MintOpShortBranch, + MintOpSwitch, + MintOpMethodToken, + MintOpFieldToken, + MintOpClassToken, + MintOpVTableToken, + MintOpTwoShorts, + MintOpTwoInts, + MintOpShortAndInt, + MintOpShortAndShortBranch, + MintOpPair2, + MintOpPair3, + MintOpPair4 } // keep in sync with jiterpreter.c, see mono_jiterp_relop_fp diff --git a/src/mono/browser/runtime/jiterpreter-support.ts b/src/mono/browser/runtime/jiterpreter-support.ts index cfd034cef408a7..998056d0aa145c 100644 --- a/src/mono/browser/runtime/jiterpreter-support.ts +++ b/src/mono/browser/runtime/jiterpreter-support.ts @@ -1321,6 +1321,9 @@ class Cfg { this.builder.appendU8(WasmOpcode.br_if); this.builder.appendULeb(this.blockStack.indexOf(this.backDispatchOffsets[0])); } else { + if (this.trace > 0) + mono_log_info(`${this.backDispatchOffsets.length} back branch offsets after filtering.`); + // the loop needs to start with a br_table that performs dispatch based on the current value // of the dispatch index local // br_table has to be surrounded by a block in order for a depth of 0 to be fallthrough diff --git a/src/mono/browser/runtime/jiterpreter-trace-generator.ts b/src/mono/browser/runtime/jiterpreter-trace-generator.ts index f84bb6a662f4a0..a2edb883820281 100644 --- a/src/mono/browser/runtime/jiterpreter-trace-generator.ts +++ b/src/mono/browser/runtime/jiterpreter-trace-generator.ts @@ -9,7 +9,7 @@ import { } from "./memory"; import { WasmOpcode, WasmSimdOpcode, WasmValtype, - getOpcodeName, + getOpcodeName, MintOpArgType } from "./jiterpreter-opcodes"; import { MintOpcode, SimdInfo, @@ -33,7 +33,7 @@ import { disabledOpcodes, countCallTargets, callTargetCounts, - trace, traceOnError, traceOnRuntimeError, + trace, traceOnError, emitPadding, traceBranchDisplacements, traceEip, nullCheckValidation, traceNullCheckOptimizations, @@ -109,6 +109,7 @@ function is_backward_branch_target( if (!backwardBranchTable) return false; + // TODO: sort the table and exploit that for faster scan. Not important yet for (let i = 0; i < backwardBranchTable.length; i++) { const actualOffset = (backwardBranchTable[i] * 2) + startOfBody; if (actualOffset === ip) @@ -128,6 +129,74 @@ function get_known_constant_value(builder: WasmBuilder, localOffset: number): Kn return knownConstantValues.get(localOffset); } +// Perform a quick scan through the opcodes potentially in this trace to build a table of +// backwards branch targets, compatible with the layout of the old one that was generated in C. +// We do this here to match the exact way that the jiterp calculates branch targets, since +// there were previously corner cases where jiterp and interp disagreed. +export function generateBackwardBranchTable( + ip: MintOpcodePtr, startOfBody: MintOpcodePtr, sizeOfBody: MintOpcodePtr, +): Uint16Array | null { + const endOfBody = startOfBody + sizeOfBody; + // TODO: Cache this table object instance and reuse it to reduce gc pressure? + const table : number[] = []; + // IP of the start of the trace in U16s, relative to startOfBody. + const rbase16 = (ip - startOfBody) / 2; + + while (ip < endOfBody) { + // IP of the current opcode in U16s, relative to startOfBody. This is what the back branch table uses + const rip16 = (ip - startOfBody) / 2; + const opcode = getU16(ip); + // HACK + if (opcode === MintOpcode.MINT_SWITCH) + break; + + const opLengthU16 = cwraps.mono_jiterp_get_opcode_info(opcode, OpcodeInfoType.Length); + // Any opcode with a branch argtype will have a decoded displacement, even if we don't + // implement the opcode. Everything else will return undefined here and be skipped + const displacement = getBranchDisplacement(ip, opcode); + if (typeof (displacement) !== "number") { + ip += (opLengthU16 * 2); + continue; + } + + // These checks shouldn't fail unless memory is corrupted or something is wrong with the decoder. + // We don't want to cause decoder bugs to make the application exit, though - graceful degradation. + if (displacement === 0) { + mono_log_info(`opcode @${ip} branch target is self. aborting backbranch table generation`); + break; + } + + const rtarget16 = rip16 + (displacement); + if (rtarget16 < 0) { + mono_log_info(`opcode @${ip}'s displacement of ${displacement} goes before body: ${rtarget16}. aborting backbranch table generation`); + break; + } + + // If the relative target is before the start of the trace, don't record it. + // The trace will be unable to successfully branch to it so it would just make the table bigger. + if (rtarget16 >= rbase16) + table.push(rtarget16); + + switch (opcode) { + case MintOpcode.MINT_CALL_HANDLER: + case MintOpcode.MINT_CALL_HANDLER_S: + // While this formally isn't a backward branch target, we want to record + // the offset of its following instruction so that the jiterpreter knows + // to generate the necessary dispatch code to enable branching back to it. + table.push(rip16 + opLengthU16); + break; + } + + ip += (opLengthU16 * 2); + } + + if (table.length <= 0) + return null; + // Not important yet, so not doing it + // table.sort((a, b) => a - b); + return new Uint16Array(table); +} + export function generateWasmBody( frame: NativePointer, traceName: string, ip: MintOpcodePtr, startOfBody: MintOpcodePtr, endOfBody: MintOpcodePtr, @@ -1546,7 +1615,7 @@ export function generateWasmBody( } } - if ((trace > 1) || traceOnError || traceOnRuntimeError || mostRecentOptions!.dumpTraces || instrumentedTraceId) { + if ((trace > 1) || traceOnError || mostRecentOptions!.dumpTraces || instrumentedTraceId) { let stmtText = `${(ip).toString(16)} ${opname} `; const firstDreg = ip + 2; const firstSreg = firstDreg + (numDregs * 2); @@ -2572,13 +2641,45 @@ function append_call_handler_store_ret_ip( builder.callHandlerReturnAddresses.push(retIp); } +function getBranchDisplacement( + ip: MintOpcodePtr, opcode: MintOpcode +) : number | undefined { + const opArgType = cwraps.mono_jiterp_get_opcode_info(opcode, OpcodeInfoType.OpArgType), + payloadOffset = cwraps.mono_jiterp_get_opcode_info(opcode, OpcodeInfoType.Sregs), + payloadAddress = ip + 2 + (payloadOffset * 2); + + let result : number; + switch (opArgType) { + case MintOpArgType.MintOpBranch: + result = getI32_unaligned(payloadAddress); + break; + case MintOpArgType.MintOpShortBranch: + result = getI16(payloadAddress); + break; + case MintOpArgType.MintOpShortAndShortBranch: + result = getI16(payloadAddress + 2); + break; + default: + return undefined; + } + + if (traceBranchDisplacements) + mono_log_info(`${getOpcodeName(opcode)} @${ip} displacement=${result}`); + + return result; +} + function emit_branch( builder: WasmBuilder, ip: MintOpcodePtr, - frame: NativePointer, opcode: MintOpcode, displacement?: number + frame: NativePointer, opcode: MintOpcode ): boolean { const isSafepoint = (opcode >= MintOpcode.MINT_BRFALSE_I4_SP) && (opcode <= MintOpcode.MINT_BLT_UN_I8_IMM_SP); + const displacement = getBranchDisplacement(ip, opcode); + if (typeof (displacement) !== "number") + return false; + // If the branch is taken we bail out to allow the interpreter to do it. // So for brtrue, we want to do 'cond == 0' to produce a bailout only // when the branch will be taken (by skipping the bailout in this block) @@ -2592,15 +2693,7 @@ function emit_branch( case MintOpcode.MINT_BR_S: { const isCallHandler = (opcode === MintOpcode.MINT_CALL_HANDLER) || (opcode === MintOpcode.MINT_CALL_HANDLER_S); - displacement = ( - (opcode === MintOpcode.MINT_BR) || - (opcode === MintOpcode.MINT_CALL_HANDLER) - ) - ? getArgI32(ip, 1) - : getArgI16(ip, 1); - if (traceBranchDisplacements) - mono_log_info(`br.s @${ip} displacement=${displacement}`); const destination = ip + (displacement * 2); if (displacement <= 0) { @@ -2653,7 +2746,6 @@ function emit_branch( // Load the condition - displacement = getArgI16(ip, 2); append_ldloc(builder, getArgU16(ip, 1), is64 ? WasmOpcode.i64_load : WasmOpcode.i32_load); if ( (opcode === MintOpcode.MINT_BRFALSE_I4_S) || @@ -2684,11 +2776,6 @@ function emit_branch( } } - if (!displacement) - throw new Error("Branch had no displacement"); - else if (traceBranchDisplacements) - mono_log_info(`${getOpcodeName(opcode)} @${ip} displacement=${displacement}`); - const destination = ip + (displacement * 2); if (displacement < 0) { @@ -2741,10 +2828,6 @@ function emit_relop_branch( if (!relopInfo && !intrinsicFpBinop) return false; - const displacement = getArgI16(ip, 3); - if (traceBranchDisplacements) - mono_log_info(`relop @${ip} displacement=${displacement}`); - const operandLoadOp = relopInfo ? relopInfo[1] : ( @@ -2779,7 +2862,7 @@ function emit_relop_branch( builder.callImport("relop_fp"); } - return emit_branch(builder, ip, frame, opcode, displacement); + return emit_branch(builder, ip, frame, opcode); } function emit_math_intrinsic(builder: WasmBuilder, ip: MintOpcodePtr, opcode: MintOpcode): boolean { diff --git a/src/mono/browser/runtime/jiterpreter.ts b/src/mono/browser/runtime/jiterpreter.ts index 12167c9c03db3f..9d47c2e39b1eab 100644 --- a/src/mono/browser/runtime/jiterpreter.ts +++ b/src/mono/browser/runtime/jiterpreter.ts @@ -4,7 +4,7 @@ import { MonoMethod } from "./types/internal"; import { NativePointer } from "./types/emscripten"; import { Module, mono_assert, runtimeHelpers } from "./globals"; -import { getU16, getU32_unaligned, localHeapViewU8 } from "./memory"; +import { getU16 } from "./memory"; import { WasmValtype, WasmOpcode, getOpcodeName } from "./jiterpreter-opcodes"; import { MintOpcode } from "./mintops"; import cwraps from "./cwraps"; @@ -12,15 +12,15 @@ import { MintOpcodePtr, WasmBuilder, addWasmFunctionPointer, _now, isZeroPageReserved, getRawCwrap, importDef, JiterpreterOptions, getOptions, recordFailure, - getMemberOffset, getCounter, modifyCounter, + getCounter, modifyCounter, simdFallbackCounters, getWasmFunctionTable } from "./jiterpreter-support"; import { - JiterpMember, BailoutReasonNames, BailoutReason, + BailoutReasonNames, BailoutReason, JiterpreterTable, JiterpCounter, } from "./jiterpreter-enums"; import { - generateWasmBody + generateWasmBody, generateBackwardBranchTable } from "./jiterpreter-trace-generator"; import { mono_jiterp_free_method_data_interp_entry } from "./jiterpreter-interp-entry"; import { mono_jiterp_free_method_data_jit_call } from "./jiterpreter-jit-call"; @@ -34,10 +34,6 @@ export const // Record a trace of all managed interpreter opcodes then dump it to console // if an error occurs while compiling the output wasm traceOnError = false, - // Record trace but dump it when the trace has a runtime error instead - // requires trapTraceErrors to work and will slow trace compilation + - // increase memory usage - traceOnRuntimeError = false, // Trace the method name, location and reason for each abort traceAbortLocations = false, // Count the number of times a given method is seen as a call target, then @@ -61,11 +57,6 @@ export const // Print diagnostic information when generating backward branches // 1 = failures only, 2 = full detail traceBackBranches = 0, - // If we encounter an enter opcode that looks like a loop body and it was already - // jitted, we should abort the current trace since it's not worth continuing - // Unproductive if we have backward branches enabled because it can stop us from jitting - // nested loops - abortAtJittedLoopBodies = true, // Enable generating conditional backward branches for ENDFINALLY opcodes if we saw some CALL_HANDLER // opcodes previously, up to this many potential return addresses. If a trace contains more potential // return addresses than this we will not emit code for the ENDFINALLY opcode @@ -1030,11 +1021,8 @@ export function mono_interp_tier_prepare_jiterpreter( const methodName = utf8ToString(cwraps.mono_wasm_method_get_name(method)); info.name = methodFullName || methodName; - const imethod = getU32_unaligned(getMemberOffset(JiterpMember.Imethod) + frame); - const backBranchCount = getU32_unaligned(getMemberOffset(JiterpMember.BackwardBranchOffsetsCount) + imethod); - const pBackBranches = getU32_unaligned(getMemberOffset(JiterpMember.BackwardBranchOffsets) + imethod); - let backwardBranchTable = backBranchCount - ? new Uint16Array(localHeapViewU8().buffer, pBackBranches, backBranchCount) + let backwardBranchTable = mostRecentOptions.noExitBackwardBranches + ? generateBackwardBranchTable(ip, startOfBody, sizeOfBody) : null; // If we're compiling a trace that doesn't start at the beginning of a method, diff --git a/src/mono/mono/mini/interp/interp-internals.h b/src/mono/mono/mini/interp/interp-internals.h index 8c4fe67b002abf..c5f3707ab1ca5f 100644 --- a/src/mono/mono/mini/interp/interp-internals.h +++ b/src/mono/mono/mini/interp/interp-internals.h @@ -181,8 +181,6 @@ struct InterpMethod { unsigned int is_verbose : 1; #if HOST_BROWSER unsigned int contains_traces : 1; - guint16 *backward_branch_offsets; - unsigned int backward_branch_offsets_count; MonoBitSet *address_taken_bits; #endif #if PROFILE_INTERP diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index 0d4e17bf346f53..52a6f74f47d631 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -1165,7 +1165,9 @@ enum { JITERP_MEMBER_SPAN_LENGTH, JITERP_MEMBER_SPAN_DATA, JITERP_MEMBER_ARRAY_LENGTH, + // Kept as-is but no longer implemented JITERP_MEMBER_BACKWARD_BRANCH_OFFSETS, + // Ditto JITERP_MEMBER_BACKWARD_BRANCH_OFFSETS_COUNT, JITERP_MEMBER_CLAUSE_DATA_OFFSETS, JITERP_MEMBER_PARAMS_COUNT, @@ -1195,10 +1197,6 @@ mono_jiterp_get_member_offset (int member) { return offsetof (InterpFrame, imethod); case JITERP_MEMBER_DATA_ITEMS: return offsetof (InterpMethod, data_items); - case JITERP_MEMBER_BACKWARD_BRANCH_OFFSETS: - return offsetof (InterpMethod, backward_branch_offsets); - case JITERP_MEMBER_BACKWARD_BRANCH_OFFSETS_COUNT: - return offsetof (InterpMethod, backward_branch_offsets_count); case JITERP_MEMBER_CLAUSE_DATA_OFFSETS: return offsetof (InterpMethod, clause_data_offsets); case JITERP_MEMBER_RMETHOD: diff --git a/src/mono/mono/mini/interp/mintops.h b/src/mono/mono/mini/interp/mintops.h index 73b767daccb094..27e3821dbccf8c 100644 --- a/src/mono/mono/mini/interp/mintops.h +++ b/src/mono/mono/mini/interp/mintops.h @@ -8,6 +8,7 @@ #include #include +// If you change this, update jiterpreter-opcodes.ts. typedef enum { MintOpNoArgs, diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 98be733a89b121..ac654631313fe7 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -8746,11 +8746,6 @@ generate_compacted_code (InterpMethod *rtm, TransformData *td) int patchpoint_data_index = 0; td->relocs = g_ptr_array_new (); InterpBasicBlock *bb; -#if HOST_BROWSER - #define BACKWARD_BRANCH_OFFSETS_SIZE 64 - unsigned int backward_branch_offsets_count = 0; - guint16 backward_branch_offsets[BACKWARD_BRANCH_OFFSETS_SIZE] = { 0 }; -#endif // This iteration could be avoided at the cost of less precise size result, following // super instruction pass @@ -8771,13 +8766,6 @@ generate_compacted_code (InterpMethod *rtm, TransformData *td) g_assert (bb->native_offset <= bb->native_offset_estimate); td->cbb = bb; -#if HOST_BROWSER - if (bb->backwards_branch_target && rtm->contains_traces) { - if (backward_branch_offsets_count < BACKWARD_BRANCH_OFFSETS_SIZE) - backward_branch_offsets[backward_branch_offsets_count++] = ip - td->new_code; - } -#endif - if (bb->patchpoint_data) patchpoint_data_index = add_patchpoint_data (td, patchpoint_data_index, bb->native_offset, bb->index); if (!td->optimized && bb->patchpoint_bb) { @@ -8790,17 +8778,6 @@ generate_compacted_code (InterpMethod *rtm, TransformData *td) if (ins->opcode == MINT_TIER_PATCHPOINT_DATA) { int native_offset = (int)(ip - td->new_code); patchpoint_data_index = add_patchpoint_data (td, patchpoint_data_index, native_offset, -ins->data [0]); -#if HOST_BROWSER - } else if (rtm->contains_traces && ( - (ins->opcode == MINT_CALL_HANDLER_S) || (ins->opcode == MINT_CALL_HANDLER) - )) { - // While this formally isn't a backward branch target, we want to record - // the offset of its following instruction so that the jiterpreter knows - // to generate the necessary dispatch code to enable branching back to it. - ip = emit_compacted_instruction (td, ip, ins); - if (backward_branch_offsets_count < BACKWARD_BRANCH_OFFSETS_SIZE) - backward_branch_offsets[backward_branch_offsets_count++] = ip - td->new_code; -#endif } else { ip = emit_compacted_instruction (td, ip, ins); } @@ -8815,16 +8792,6 @@ generate_compacted_code (InterpMethod *rtm, TransformData *td) handle_relocations (td); g_ptr_array_free (td->relocs, TRUE); - -#if HOST_BROWSER - if (backward_branch_offsets_count > 0) { - rtm->backward_branch_offsets = imethod_alloc0 (td, backward_branch_offsets_count * sizeof(guint16)); - rtm->backward_branch_offsets_count = backward_branch_offsets_count; - memcpy(rtm->backward_branch_offsets, backward_branch_offsets, backward_branch_offsets_count * sizeof(guint16)); - } - - #undef BACKWARD_BRANCH_OFFSETS_SIZE -#endif } /* From 03c9d36276546f855b164eee802ae776d336cf2d Mon Sep 17 00:00:00 2001 From: Adeel Mujahid <3840695+am11@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:27:03 +0200 Subject: [PATCH 116/158] Add alpine support in install-native-deps script (#98603) Similar to debian-based distros, make it convenient for someone trying to use slim alpine image for non-x64 architectures (without cross-compilation). e.g. for arm64: ```sh $ docker run --rm -v$(pwd):/runtime -w /runtime \ --platform linux/arm64 -it alpine \ sh -c 'eng/install-native-dependencies.sh && ./build.sh' ``` --- eng/install-native-dependencies.sh | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/eng/install-native-dependencies.sh b/eng/install-native-dependencies.sh index 3fa4d87a9e4abb..42a3727b3188a0 100755 --- a/eng/install-native-dependencies.sh +++ b/eng/install-native-dependencies.sh @@ -20,17 +20,19 @@ case "$os" in . /etc/os-release fi - if [ "$ID" != "debian" ] && [ "$ID_LIKE" != "debian" ]; then + if [ "$ID" = "debian" ] || [ "$ID_LIKE" = "debian" ]; then + apt update + + apt install -y build-essential gettext locales cmake llvm clang lldb liblldb-dev libunwind8-dev libicu-dev liblttng-ust-dev \ + libssl-dev libkrb5-dev zlib1g-dev + + localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 + elif [ "$ID" = "alpine" ]; then + apk add build-base cmake bash curl clang llvm-dev krb5-dev lttng-ust-dev icu-dev zlib-dev openssl-dev + else echo "Unsupported distro. distro: $ID" exit 1 fi - - apt update - - apt install -y build-essential gettext locales cmake llvm clang lldb liblldb-dev libunwind8-dev libicu-dev liblttng-ust-dev \ - libssl-dev libkrb5-dev zlib1g-dev - - localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 ;; osx|maccatalyst|ios|iossimulator|tvos|tvossimulator) From 2df0ad1f49407a09e8007ff53c310714b70e0704 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 19 Feb 2024 11:52:29 +0100 Subject: [PATCH 117/158] [browser][MT] JSType.OneWay -> JSType.DiscardNoWait (#98647) --- .../JSImportGenerator/JSGeneratorFactory.cs | 8 +- .../gen/JSImportGenerator/JSTypeFlags.cs | 2 +- .../JSImportGenerator/Resources/Strings.resx | 4 +- .../src/CompatibilitySuppressions.xml | 4 +- .../Interop/JavaScriptImports.Generated.cs | 2 +- .../JavaScript/JSFunctionBinding.cs | 47 +++++----- .../JavaScript/JSHostImplementation.cs | 2 +- .../JavaScript/JSMarshalerArgument.cs | 1 + .../JavaScript/JSMarshalerType.cs | 4 +- .../InteropServices/JavaScript/JSType.cs | 4 +- .../JavaScript/MarshalerType.cs | 2 +- .../Marshaling/JSMarshalerArgument.Func.cs | 8 +- .../JavaScript/JSExportTest.cs | 5 +- .../JavaScript/JSImportTest.cs | 6 +- .../JavaScript/JavaScriptTestHelper.cs | 13 ++- src/mono/browser/runtime/driver.c | 3 +- src/mono/browser/runtime/invoke-cs.ts | 16 ++-- src/mono/browser/runtime/invoke-js.ts | 14 +-- src/mono/browser/runtime/marshal-to-cs.ts | 4 +- src/mono/browser/runtime/marshal-to-js.ts | 10 +- src/mono/browser/runtime/marshal.ts | 91 +++++++++++++------ src/mono/browser/runtime/memory.ts | 4 +- src/mono/browser/runtime/strings.ts | 4 +- src/mono/browser/runtime/types/internal.ts | 2 +- 24 files changed, 150 insertions(+), 110 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs index 9d0577cb7d5417..16f2dda426a1cf 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSGeneratorFactory.cs @@ -42,8 +42,8 @@ ResolvedGenerator fail(string failReason) return ResolvedGenerator.NotSupported(new(info, context)); // void - case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.OneWay }: - return ResolvedGenerator.Resolved(new VoidGenerator(MarshalerType.OneWay)); + case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.DiscardNoWait }: + return ResolvedGenerator.Resolved(new VoidGenerator(MarshalerType.DiscardNoWait)); case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Discard }: case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.Void }: case { TypeInfo: JSSimpleTypeInfo(KnownManagedType.Void), JSType: JSTypeFlags.None }: @@ -55,8 +55,8 @@ ResolvedGenerator fail(string failReason) return fail(SR.DiscardOnlyVoid); // oneway no void - case { JSType: JSTypeFlags.OneWay }: - return fail(SR.OneWayOnlyVoid); + case { JSType: JSTypeFlags.DiscardNoWait }: + return fail(SR.DiscardNoWaitOnlyVoid); // primitive case { TypeInfo: JSSimpleTypeInfo simple }: diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSTypeFlags.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSTypeFlags.cs index e39fbe19425ba5..5b34284f2225b6 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSTypeFlags.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/JSTypeFlags.cs @@ -22,7 +22,7 @@ internal enum JSTypeFlags : int MemoryView = 0x800, Any = 0x1000, Discard = 0x2000, - OneWay = 0x4000, + DiscardNoWait = 0x4000, Missing = 0x4000_0000, } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Resources/Strings.resx index 1e27fdcd8bb969..6aa6b8b9bb3379 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/gen/JSImportGenerator/Resources/Strings.resx @@ -184,8 +184,8 @@ 'JSType.Discard' could be only used with void return argument. - - 'JSType.OneWay' could be only used with void returning method. + + 'JSType.DiscardNoWait' could be only used with void returning method. Type {0} is not supported as argument of marshaled function. diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml index b5cc7926a51696..07a5ec1d2531e6 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/CompatibilitySuppressions.xml @@ -14,13 +14,13 @@ CP0001 - T:System.Runtime.InteropServices.JavaScript.JSType.OneWay + T:System.Runtime.InteropServices.JavaScript.JSType.DiscardNoWait ref/net9.0/System.Runtime.InteropServices.JavaScript.dll runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll CP0002 - M:System.Runtime.InteropServices.JavaScript.JSMarshalerType.get_OneWay + M:System.Runtime.InteropServices.JavaScript.JSMarshalerType.get_DiscardNoWait ref/net9.0/System.Runtime.InteropServices.JavaScript.dll runtimes/browser/lib/net9.0/System.Runtime.InteropServices.JavaScript.dll diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs index db356f8afe4c20..7d734d8babe780 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptImports.Generated.cs @@ -57,7 +57,7 @@ internal static unsafe partial class JavaScriptImports #if DEBUG [JSImport("globalThis.console.log")] - [return: JSMarshalAs] + [return: JSMarshalAs] // this means that the message will arrive out of order, especially across threads. public static partial void Log([JSMarshalAs] string message); #endif } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs index 43afa95f76b1dd..666f2caeb5a4a2 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs @@ -30,54 +30,51 @@ internal JSFunctionBinding() { } internal static volatile uint nextImportHandle = 1; internal int ImportHandle; internal bool IsAsync; - internal bool IsOneWay; + internal bool IsDiscardNoWait; #if DEBUG internal string? FunctionName; #endif - [StructLayout(LayoutKind.Sequential, Pack = 4)] + // keep in sync with JSBindingHeaderOffsets in marshal.ts + [StructLayout(LayoutKind.Explicit, Pack = 4)] internal struct JSBindingHeader { internal const int JSMarshalerSignatureHeaderSize = 4 * 8; // without Exception and Result + [FieldOffset(0)] public int Version; + [FieldOffset(4)] public int ArgumentCount; + [FieldOffset(8)] public int ImportHandle; - public int _Reserved; + [FieldOffset(16)] public int FunctionNameOffset; + [FieldOffset(20)] public int FunctionNameLength; + [FieldOffset(24)] public int ModuleNameOffset; + [FieldOffset(28)] public int ModuleNameLength; + [FieldOffset(32)] public JSBindingType Exception; + [FieldOffset(64)] public JSBindingType Result; } - [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 32)] + // keep in sync with JSBindingTypeOffsets in marshal.ts + [StructLayout(LayoutKind.Explicit, Pack = 4, Size = 32)] internal struct JSBindingType { + [FieldOffset(0)] internal MarshalerType Type; - internal MarshalerType __ReservedB1; - internal MarshalerType __ReservedB2; - internal MarshalerType __ReservedB3; - internal IntPtr __Reserved; - internal IntPtr JSCustomMarshallerCode; - internal int JSCustomMarshallerCodeLength; + [FieldOffset(16)] internal MarshalerType ResultMarshalerType; - internal MarshalerType __ReservedB4; - internal MarshalerType __ReservedB5; - internal MarshalerType __ReservedB6; + [FieldOffset(20)] internal MarshalerType Arg1MarshalerType; - internal MarshalerType __ReservedB7; - internal MarshalerType __ReservedB8; - internal MarshalerType __ReservedB9; + [FieldOffset(24)] internal MarshalerType Arg2MarshalerType; - internal MarshalerType __ReservedB10; - internal MarshalerType __ReservedB11; - internal MarshalerType __ReservedB12; + [FieldOffset(28)] internal MarshalerType Arg3MarshalerType; - internal MarshalerType __ReservedB13; - internal MarshalerType __ReservedB14; - internal MarshalerType __ReservedB15; } internal unsafe int ArgumentCount @@ -286,9 +283,9 @@ internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span arguments[1].slot.GCHandle = holder.GCHandle; } - if (signature.IsOneWay) + if (signature.IsDiscardNoWait) { - arguments[1].slot.Type = MarshalerType.OneWay; + arguments[1].slot.Type = MarshalerType.DiscardNoWait; } #if FEATURE_WASM_MANAGED_THREADS @@ -305,7 +302,7 @@ internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span #endif } - else if (signature.IsAsync || signature.IsOneWay) + else if (signature.IsAsync || signature.IsDiscardNoWait) { //async DispatchJSImportAsyncPost(signature, targetContext, arguments); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs index 335aa72371eeeb..736f2de5a134d1 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSHostImplementation.cs @@ -153,7 +153,7 @@ public static unsafe JSFunctionBinding GetMethodSignature(ReadOnlySpan /// The marshaler metadata. - public static JSMarshalerType OneWay { get; } = new JSMarshalerType(new JSFunctionBinding.JSBindingType + public static JSMarshalerType DiscardNoWait { get; } = new JSMarshalerType(new JSFunctionBinding.JSBindingType { - Type = MarshalerType.OneWay + Type = MarshalerType.DiscardNoWait }); /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSType.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSType.cs index 8f9a5ef68f1563..7d86122eae7f55 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSType.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSType.cs @@ -32,9 +32,9 @@ internal Discard() { } /// Could return immediately without waiting for the execution to finish, when dispatching the call to another thread. /// Suppresses marshaling of the JavaScript function's return value. /// - public sealed class OneWay : JSType + public sealed class DiscardNoWait : JSType { - internal OneWay() { } + internal DiscardNoWait() { } } /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs index d9f11387dedaa9..ee9e4e247b25e0 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/MarshalerType.cs @@ -34,7 +34,7 @@ internal enum MarshalerType : byte Span, Action, Function, - OneWay, + DiscardNoWait, #if !JSIMPORTGENERATOR // only on runtime diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Func.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Func.cs index 271acab66bae0f..d53c92400755c6 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Func.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Func.cs @@ -20,7 +20,7 @@ public void InvokeJS() // and would also allow the JS function to be collected - Span arguments = stackalloc JSMarshalerArgument[4]; + Span arguments = stackalloc JSMarshalerArgument[2]; ref JSMarshalerArgument args_exception = ref arguments[0]; ref JSMarshalerArgument args_return = ref arguments[1]; #if FEATURE_WASM_MANAGED_THREADS @@ -51,7 +51,7 @@ public ActionJS(JSObject holder, ArgumentToJSCallback arg1Marshaler) public void InvokeJS(T arg1) { - Span arguments = stackalloc JSMarshalerArgument[4]; + Span arguments = stackalloc JSMarshalerArgument[3]; ref JSMarshalerArgument args_exception = ref arguments[0]; ref JSMarshalerArgument args_return = ref arguments[1]; ref JSMarshalerArgument args_arg1 = ref arguments[2]; @@ -258,7 +258,7 @@ public TResult InvokeJS() // JSObject (held by this lambda) would be collected by GC after the lambda is collected // and would also allow the JS function to be collected - Span arguments = stackalloc JSMarshalerArgument[4]; + Span arguments = stackalloc JSMarshalerArgument[2]; ref JSMarshalerArgument args_exception = ref arguments[0]; ref JSMarshalerArgument args_return = ref arguments[1]; #if FEATURE_WASM_MANAGED_THREADS @@ -295,7 +295,7 @@ public FuncJS(JSObject holder, ArgumentToJSCallback arg1Marshaler, ArgumentTo public TResult InvokeJS(T arg1) { - Span arguments = stackalloc JSMarshalerArgument[4]; + Span arguments = stackalloc JSMarshalerArgument[3]; ref JSMarshalerArgument args_exception = ref arguments[0]; ref JSMarshalerArgument args_return = ref arguments[1]; ref JSMarshalerArgument args_arg1 = ref arguments[2]; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs index 30eadd7a7f2c77..bcc2c85e9e7857 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSExportTest.cs @@ -26,12 +26,11 @@ await JsExportTestAsync(value, [Theory] [MemberData(nameof(MarshalInt32Cases))] - public async Task JsExportInt32OneWay(int value) + public async Task JsExportInt32DiscardNoWait(int value) { JavaScriptTestHelper.optimizedReached=0; - JavaScriptTestHelper.invoke1O(value); - await Task.Yield(); + await JavaScriptTestHelper.Delay(0); Assert.Equal(value, JavaScriptTestHelper.optimizedReached); } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs index 68598527681bdd..96b89ffa9e4f4c 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JSImportTest.cs @@ -529,10 +529,10 @@ public void JsImportInt16(short value) #region Int32 [Theory] [MemberData(nameof(MarshalInt32Cases))] - public async Task JsImportInt32OneWay(int value) + public async Task JsImportInt32DiscardNoWait(int value) { - JavaScriptTestHelper.store1OneWay_Int32(value); - await Task.Yield(); + JavaScriptTestHelper.store1DiscardNoWait_Int32(value); + await JavaScriptTestHelper.Delay(0); Assert.Equal(value, JavaScriptTestHelper.retrieve1_Int32()); } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs index 6d6a484890782e..e5ea3a887e24b9 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/JavaScriptTestHelper.cs @@ -15,7 +15,7 @@ namespace System.Runtime.InteropServices.JavaScript.Tests public partial class JavaScriptTestHelper { [JSImport("globalThis.console.log")] - [return: JSMarshalAs] + [return: JSMarshalAs] public static partial void Log([JSMarshalAs] string message); [JSImport("globalThis.window.location.toString")] @@ -28,12 +28,15 @@ public partial class JavaScriptTestHelper public static partial string ReboundMemberEcho(string message); [JSExport] - [return: JSMarshalAs] + [return: JSMarshalAs] // this means that the message will arrive out of order, especially across threads. public static void ConsoleWriteLine([JSMarshalAs] string message) { Console.WriteLine(message); } + [JSImport("delay", "JavaScriptTestHelper")] + public static partial Task Delay(int ms); + [JSImport("catch1toString", "JavaScriptTestHelper")] public static partial string catch1toString(string message, string functionName); @@ -76,7 +79,7 @@ public static void Optimized1V(int a1) public static partial void invoke1V(int a1); [JSExport] - [return: JSMarshalAs] + [return: JSMarshalAs] // this means that the message will arrive out of order, especially across threads. public static void Optimized1O(int a1) { optimizedReached += a1; @@ -274,8 +277,8 @@ internal static partial void Relaxed(string a1, Exception ex, internal static partial void store1_Int32([JSMarshalAs] int value); [JSImport("store1", "JavaScriptTestHelper")] - [return: JSMarshalAs] - internal static partial void store1OneWay_Int32([JSMarshalAs] int value); + [return: JSMarshalAs] // this means that the message will arrive out of order, especially across threads. + internal static partial void store1DiscardNoWait_Int32([JSMarshalAs] int value); [JSImport("retrieve1", "JavaScriptTestHelper")] [return: JSMarshalAs] diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c index 1d0133a57e6e88..d76a8bacd92823 100644 --- a/src/mono/browser/runtime/driver.c +++ b/src/mono/browser/runtime/driver.c @@ -263,7 +263,8 @@ mono_wasm_invoke_jsexport_async_post_cb (MonoMethod *method, void* args) { mono_wasm_invoke_jsexport (method, args); // TODO assert receiver_should_free ? - free (args); + if (args) + free (args); } // async diff --git a/src/mono/browser/runtime/invoke-cs.ts b/src/mono/browser/runtime/invoke-cs.ts index 2978e7ea8afce8..287ee5ca6f6812 100644 --- a/src/mono/browser/runtime/invoke-cs.ts +++ b/src/mono/browser/runtime/invoke-cs.ts @@ -39,15 +39,15 @@ export function mono_wasm_bind_cs_function(method: MonoMethod, assemblyName: str const res_sig = get_sig(signature, 1); let res_marshaler_type = get_signature_type(res_sig); - // hack until we have public API for JSType.OneWay + // hack until we have public API for JSType.DiscardNoWait if (WasmEnableThreads && shortClassName === "DefaultWebAssemblyJSRuntime" && namespaceName === "Microsoft.AspNetCore.Components.WebAssembly.Services" && (methodName === "BeginInvokeDotNet" || methodName === "EndInvokeJS")) { - res_marshaler_type = MarshalerType.OneWay; + res_marshaler_type = MarshalerType.DiscardNoWait; } const is_async = res_marshaler_type == MarshalerType.Task; - const is_oneway = res_marshaler_type == MarshalerType.OneWay; + const is_discard_no_wait = res_marshaler_type == MarshalerType.DiscardNoWait; if (is_async) { res_marshaler_type = MarshalerType.TaskPreCreated; } @@ -60,7 +60,7 @@ export function mono_wasm_bind_cs_function(method: MonoMethod, assemblyName: str arg_marshalers, res_converter, is_async, - is_oneway, + is_discard_no_wait, isDisposed: false, }; let bound_fn: Function; @@ -75,7 +75,7 @@ export function mono_wasm_bind_cs_function(method: MonoMethod, assemblyName: str else { bound_fn = bind_fn(closure); } - } else if (is_oneway) { + } else if (is_discard_no_wait) { bound_fn = bind_fn(closure); } else { if (args_count == 0 && !res_converter) { @@ -285,7 +285,7 @@ function bind_fn(closure: BindingClosure) { const method = closure.method; const fqn = closure.fullyQualifiedName; const is_async = closure.is_async; - const is_oneway = closure.is_oneway; + const is_discard_no_wait = closure.is_discard_no_wait; if (!WasmEnableThreads) (closure) = null; return function bound_fn(...js_args: any[]) { const mark = startMeasure(); @@ -313,7 +313,7 @@ function bind_fn(closure: BindingClosure) { // in case the C# side returned synchronously js_result = end_marshal_task_to_js(args, undefined, js_result); } - else if (is_oneway) { + else if (is_discard_no_wait) { // call C# side, fire and forget invoke_async_jsexport(method, args, 2 + args_count); } @@ -338,7 +338,7 @@ type BindingClosure = { arg_marshalers: (BoundMarshalerToCs)[], res_converter: BoundMarshalerToJs | undefined, is_async: boolean, - is_oneway: boolean, + is_discard_no_wait: boolean, isDisposed: boolean, } diff --git a/src/mono/browser/runtime/invoke-js.ts b/src/mono/browser/runtime/invoke-js.ts index f429bf3e98b9fd..75d7c96b9c737e 100644 --- a/src/mono/browser/runtime/invoke-js.ts +++ b/src/mono/browser/runtime/invoke-js.ts @@ -96,7 +96,7 @@ function bind_js_import(signature: JSFunctionSignature): Function { const res_marshaler_type = get_signature_type(res_sig); const res_converter = bind_arg_marshal_to_cs(res_sig, res_marshaler_type, 1); - const is_oneway = res_marshaler_type == MarshalerType.OneWay; + const is_discard_no_wait = res_marshaler_type == MarshalerType.DiscardNoWait; const is_async = res_marshaler_type == MarshalerType.Task || res_marshaler_type == MarshalerType.TaskPreCreated; const closure: BindingClosure = { @@ -107,12 +107,12 @@ function bind_js_import(signature: JSFunctionSignature): Function { res_converter, has_cleanup, arg_cleanup, - is_oneway, + is_discard_no_wait, is_async, isDisposed: false, }; let bound_fn: WrappedJSFunction; - if (is_async || is_oneway || has_cleanup) { + if (is_async || is_discard_no_wait || has_cleanup) { bound_fn = bind_fn(closure); } else { @@ -165,7 +165,7 @@ function bind_js_import(signature: JSFunctionSignature): Function { } let wrapped_fn: WrappedJSFunction; - if (is_async || is_oneway) { + if (is_async || is_discard_no_wait) { wrapped_fn = async_bound_fn; } else { @@ -279,7 +279,7 @@ function bind_fn(closure: BindingClosure) { const fqn = closure.fqn; if (!WasmEnableThreads) (closure) = null; return function bound_fn(args: JSMarshalerArguments) { - const is_async = WasmEnableThreads && is_receiver_should_free(args); + const receiver_should_free = WasmEnableThreads && is_receiver_should_free(args); const mark = startMeasure(); try { mono_assert(!WasmEnableThreads || !closure.isDisposed, "The function was already disposed"); @@ -309,7 +309,7 @@ function bind_fn(closure: BindingClosure) { marshal_exception_to_cs(args, ex); } finally { - if (is_async) { + if (receiver_should_free) { Module._free(args as any); } endMeasure(mark, MeasuredBlock.callCsFunction, fqn); @@ -327,7 +327,7 @@ type BindingClosure = { arg_marshalers: (BoundMarshalerToJs)[], res_converter: BoundMarshalerToCs | undefined, has_cleanup: boolean, - is_oneway: boolean, + is_discard_no_wait: boolean, is_async: boolean, arg_cleanup: (Function | undefined)[] } diff --git a/src/mono/browser/runtime/marshal-to-cs.ts b/src/mono/browser/runtime/marshal-to-cs.ts index 132de0f75dcd42..fa0f299a5f2b80 100644 --- a/src/mono/browser/runtime/marshal-to-cs.ts +++ b/src/mono/browser/runtime/marshal-to-cs.ts @@ -59,12 +59,12 @@ export function initialize_marshalers_to_cs(): void { js_to_cs_marshalers.set(MarshalerType.None, _marshal_null_to_cs);// also void js_to_cs_marshalers.set(MarshalerType.Discard, _marshal_null_to_cs);// also void js_to_cs_marshalers.set(MarshalerType.Void, _marshal_null_to_cs);// also void - js_to_cs_marshalers.set(MarshalerType.OneWay, _marshal_null_to_cs);// also void + js_to_cs_marshalers.set(MarshalerType.DiscardNoWait, _marshal_null_to_cs);// also void } } export function bind_arg_marshal_to_cs(sig: JSMarshalerType, marshaler_type: MarshalerType, index: number): BoundMarshalerToCs | undefined { - if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void || marshaler_type === MarshalerType.Discard || marshaler_type === MarshalerType.OneWay) { + if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void || marshaler_type === MarshalerType.Discard || marshaler_type === MarshalerType.DiscardNoWait) { return undefined; } let res_marshaler: MarshalerToCs | undefined = undefined; diff --git a/src/mono/browser/runtime/marshal-to-js.ts b/src/mono/browser/runtime/marshal-to-js.ts index 29f6bcdb54c450..f02c81765e6a47 100644 --- a/src/mono/browser/runtime/marshal-to-js.ts +++ b/src/mono/browser/runtime/marshal-to-js.ts @@ -55,12 +55,12 @@ export function initialize_marshalers_to_js(): void { cs_to_js_marshalers.set(MarshalerType.None, _marshal_null_to_js); cs_to_js_marshalers.set(MarshalerType.Void, _marshal_null_to_js); cs_to_js_marshalers.set(MarshalerType.Discard, _marshal_null_to_js); - cs_to_js_marshalers.set(MarshalerType.OneWay, _marshal_null_to_js); + cs_to_js_marshalers.set(MarshalerType.DiscardNoWait, _marshal_null_to_js); } } export function bind_arg_marshal_to_js(sig: JSMarshalerType, marshaler_type: MarshalerType, index: number): BoundMarshalerToJs | undefined { - if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void || marshaler_type === MarshalerType.Discard || marshaler_type === MarshalerType.OneWay) { + if (marshaler_type === MarshalerType.None || marshaler_type === MarshalerType.Void || marshaler_type === MarshalerType.Discard || marshaler_type === MarshalerType.DiscardNoWait) { return undefined; } @@ -338,7 +338,7 @@ function create_task_holder(res_converter?: MarshalerToJs) { export function mono_wasm_resolve_or_reject_promise(args: JSMarshalerArguments): void { const exc = get_arg(args, 0); - const is_async = WasmEnableThreads && is_receiver_should_free(args); + const receiver_should_free = WasmEnableThreads && is_receiver_should_free(args); try { loaderHelpers.assert_runtime_running(); @@ -353,7 +353,7 @@ export function mono_wasm_resolve_or_reject_promise(args: JSMarshalerArguments): mono_assert(holder, () => `Cannot find Promise for JSHandle ${js_handle}`); holder.resolve_or_reject(type, js_handle, arg_value); - if (is_async) { + if (receiver_should_free) { // this works together with AllocHGlobal in JSFunctionBinding.ResolveOrRejectPromise Module._free(args as any); } @@ -363,7 +363,7 @@ export function mono_wasm_resolve_or_reject_promise(args: JSMarshalerArguments): } } catch (ex: any) { - if (is_async) { + if (receiver_should_free) { mono_assert(false, () => `Failed to resolve or reject promise ${ex}`); } marshal_exception_to_cs(exc, ex); diff --git a/src/mono/browser/runtime/marshal.ts b/src/mono/browser/runtime/marshal.ts index 6d0f6ec0308fa7..c3c07d1c188955 100644 --- a/src/mono/browser/runtime/marshal.ts +++ b/src/mono/browser/runtime/marshal.ts @@ -20,8 +20,47 @@ export const imported_js_function_symbol = Symbol.for("wasm imported_js_function export const proxy_debug_symbol = Symbol.for("wasm proxy_debug"); export const JavaScriptMarshalerArgSize = 32; +// keep in sync with JSMarshalerArgumentImpl offsets +const enum JSMarshalerArgumentOffsets { + BooleanValue = 0, + ByteValue = 0, + CharValue = 0, + Int16Value = 0, + Int32Value = 0, + Int64Value = 0, + SingleValue = 0, + DoubleValue = 0, + IntPtrValue = 0, + JSHandle = 4, + GCHandle = 4, + Length = 8, + Type = 12, + ElementType = 13, + ContextHandle = 16, + ReceiverShouldFree = 20, +} export const JSMarshalerTypeSize = 32; +// keep in sync with JSFunctionBinding.JSBindingType +const enum JSBindingTypeOffsets { + Type = 0, + ResultMarshalerType = 16, + Arg1MarshalerType = 20, + Arg2MarshalerType = 24, + Arg3MarshalerType = 28, +} export const JSMarshalerSignatureHeaderSize = 4 * 8; // without Exception and Result +// keep in sync with JSFunctionBinding.JSBindingHeader +const enum JSBindingHeaderOffsets { + Version = 0, + ArgumentCount = 4, + ImportHandle = 8, + FunctionNameOffset = 16, + FunctionNameLength = 20, + ModuleNameOffset = 24, + ModuleNameLength = 28, + Exception = 32, + Result = 64, +} export function alloc_stack_frame(size: number): JSMarshalerArguments { if (WasmEnableThreads) { @@ -48,12 +87,12 @@ export function is_args_exception(args: JSMarshalerArguments): boolean { export function is_receiver_should_free(args: JSMarshalerArguments): boolean { if (WasmEnableThreads) return false; mono_assert(args, "Null args"); - return getB32(args + 20); + return getB32(args + JSMarshalerArgumentOffsets.ReceiverShouldFree); } export function set_receiver_should_free(args: JSMarshalerArguments): void { mono_assert(args, "Null args"); - setB32(args + 20, true); + setB32(args + JSMarshalerArgumentOffsets.ReceiverShouldFree, true); } export function set_args_context(args: JSMarshalerArguments): void { @@ -72,58 +111,58 @@ export function get_sig(signature: JSFunctionSignature, index: number): JSMarsha export function get_signature_type(sig: JSMarshalerType): MarshalerType { mono_assert(sig, "Null sig"); - return getU8(sig); + return getU8(sig + JSBindingTypeOffsets.Type); } export function get_signature_res_type(sig: JSMarshalerType): MarshalerType { mono_assert(sig, "Null sig"); - return getU8(sig + 16); + return getU8(sig + JSBindingTypeOffsets.ResultMarshalerType); } export function get_signature_arg1_type(sig: JSMarshalerType): MarshalerType { mono_assert(sig, "Null sig"); - return getU8(sig + 20); + return getU8(sig + JSBindingTypeOffsets.Arg1MarshalerType); } export function get_signature_arg2_type(sig: JSMarshalerType): MarshalerType { mono_assert(sig, "Null sig"); - return getU8(sig + 24); + return getU8(sig + JSBindingTypeOffsets.Arg2MarshalerType); } export function get_signature_arg3_type(sig: JSMarshalerType): MarshalerType { mono_assert(sig, "Null sig"); - return getU8(sig + 28); + return getU8(sig + JSBindingTypeOffsets.Arg2MarshalerType); } export function get_signature_argument_count(signature: JSFunctionSignature): number { mono_assert(signature, "Null signatures"); - return getI32(signature + 4); + return getI32(signature + JSBindingHeaderOffsets.ArgumentCount); } export function get_signature_version(signature: JSFunctionSignature): number { mono_assert(signature, "Null signatures"); - return getI32(signature); + return getI32(signature + JSBindingHeaderOffsets.Version); } export function get_signature_handle(signature: JSFunctionSignature): number { mono_assert(signature, "Null signatures"); - return getI32(signature + 8); + return getI32(signature + JSBindingHeaderOffsets.ImportHandle); } export function get_signature_function_name(signature: JSFunctionSignature): string | null { mono_assert(signature, "Null signatures"); - const functionNameOffset = getI32(signature + 16); + const functionNameOffset = getI32(signature + JSBindingHeaderOffsets.FunctionNameOffset); if (functionNameOffset === 0) return null; - const functionNameLength = getI32(signature + 20); + const functionNameLength = getI32(signature + JSBindingHeaderOffsets.FunctionNameLength); mono_assert(functionNameOffset, "Null name"); return utf16ToString(signature + functionNameOffset, signature + functionNameOffset + functionNameLength); } export function get_signature_module_name(signature: JSFunctionSignature): string | null { mono_assert(signature, "Null signatures"); - const moduleNameOffset = getI32(signature + 24); + const moduleNameOffset = getI32(signature + JSBindingHeaderOffsets.ModuleNameOffset); if (moduleNameOffset === 0) return null; - const moduleNameLength = getI32(signature + 28); + const moduleNameLength = getI32(signature + JSBindingHeaderOffsets.ModuleNameLength); return utf16ToString(signature + moduleNameOffset, signature + moduleNameOffset + moduleNameLength); } @@ -134,24 +173,24 @@ export function get_sig_type(sig: JSMarshalerType): MarshalerType { export function get_arg_type(arg: JSMarshalerArgument): MarshalerType { mono_assert(arg, "Null arg"); - const type = getU8(arg + 12); + const type = getU8(arg + JSMarshalerArgumentOffsets.Type); return type; } export function get_arg_element_type(arg: JSMarshalerArgument): MarshalerType { mono_assert(arg, "Null arg"); - const type = getU8(arg + 13); + const type = getU8(arg + JSMarshalerArgumentOffsets.ElementType); return type; } export function set_arg_type(arg: JSMarshalerArgument, type: MarshalerType): void { mono_assert(arg, "Null arg"); - setU8(arg + 12, type); + setU8(arg + JSMarshalerArgumentOffsets.Type, type); } export function set_arg_element_type(arg: JSMarshalerArgument, type: MarshalerType): void { mono_assert(arg, "Null arg"); - setU8(arg + 13, type); + setU8(arg + JSMarshalerArgumentOffsets.ElementType, type); } export function get_arg_b8(arg: JSMarshalerArgument): boolean { @@ -274,29 +313,29 @@ export function set_arg_f32(arg: JSMarshalerArgument, value: number): void { export function get_arg_js_handle(arg: JSMarshalerArgument): JSHandle { mono_assert(arg, "Null arg"); - return getI32(arg + 4); + return getI32(arg + JSMarshalerArgumentOffsets.JSHandle); } export function set_arg_proxy_context(arg: JSMarshalerArgument): void { if (!WasmEnableThreads) return; mono_assert(arg, "Null arg"); - setI32(arg + 16, runtimeHelpers.proxyGCHandle); + setI32(arg + JSMarshalerArgumentOffsets.ContextHandle, runtimeHelpers.proxyGCHandle); } export function set_js_handle(arg: JSMarshalerArgument, jsHandle: JSHandle): void { mono_assert(arg, "Null arg"); - setI32(arg + 4, jsHandle); + setI32(arg + JSMarshalerArgumentOffsets.JSHandle, jsHandle); set_arg_proxy_context(arg); } export function get_arg_gc_handle(arg: JSMarshalerArgument): GCHandle { mono_assert(arg, "Null arg"); - return getI32(arg + 4); + return getI32(arg + JSMarshalerArgumentOffsets.GCHandle); } export function set_gc_handle(arg: JSMarshalerArgument, gcHandle: GCHandle): void { mono_assert(arg, "Null arg"); - setI32(arg + 4, gcHandle); + setI32(arg + JSMarshalerArgumentOffsets.GCHandle, gcHandle); set_arg_proxy_context(arg); } @@ -307,12 +346,12 @@ export function get_string_root(arg: JSMarshalerArgument): WasmRoot export function get_arg_length(arg: JSMarshalerArgument): number { mono_assert(arg, "Null arg"); - return getI32(arg + 8); + return getI32(arg + JSMarshalerArgumentOffsets.Length); } export function set_arg_length(arg: JSMarshalerArgument, size: number): void { mono_assert(arg, "Null arg"); - setI32(arg + 8, size); + setI32(arg + JSMarshalerArgumentOffsets.Length, size); } export function set_root(arg: JSMarshalerArgument, root: WasmRoot): void { @@ -393,7 +432,7 @@ export class ManagedError extends Error implements IDisposable { export function get_signature_marshaler(signature: JSFunctionSignature, index: number): JSHandle { mono_assert(signature, "Null signatures"); const sig = get_sig(signature, index); - return getU32(sig + 8); + return getU32(sig + JSBindingHeaderOffsets.ImportHandle); } diff --git a/src/mono/browser/runtime/memory.ts b/src/mono/browser/runtime/memory.ts index cef0f641e50bf5..ab4beebf1285aa 100644 --- a/src/mono/browser/runtime/memory.ts +++ b/src/mono/browser/runtime/memory.ts @@ -392,9 +392,7 @@ export function localHeapViewF64(): Float64Array { export function copyBytes(srcPtr: VoidPtr, dstPtr: VoidPtr, bytes: number): void { const heap = localHeapViewU8(); - const src = heap.subarray(srcPtr as any, srcPtr as any + bytes); - const dst = heap.subarray(dstPtr as any, dstPtr as any + bytes); - dst.set(src); + heap.copyWithin(dstPtr as any, srcPtr as any, srcPtr as any + bytes); } // when we run with multithreading enabled, we need to make sure that the memory views are updated on each worker diff --git a/src/mono/browser/runtime/strings.ts b/src/mono/browser/runtime/strings.ts index aae59c50b69a90..d0d798c161b28f 100644 --- a/src/mono/browser/runtime/strings.ts +++ b/src/mono/browser/runtime/strings.ts @@ -244,7 +244,9 @@ function storeStringInInternTable(string: string, root: WasmRoot, in function stringToMonoStringNewRoot(string: string, result: WasmRoot): void { const bufferLen = (string.length + 1) * 2; - // TODO this could be stack allocated + // TODO this could be stack allocated for small strings + // or temp_malloc/alloca for large strings + // or skip the scratch buffer entirely, and make a new MonoString of size string.length, pin it, and then call stringToUTF16 to write directly into the MonoString's chars const buffer = Module._malloc(bufferLen); stringToUTF16(buffer as any, buffer as any + bufferLen, string); cwraps.mono_wasm_string_from_utf16_ref(buffer, string.length, result.address); diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index a2eb9b623f90c4..3f021a50a01ce6 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -337,7 +337,7 @@ export enum MarshalerType { Span, Action, Function, - OneWay, + DiscardNoWait, // only on runtime JSException, From eeadd653e1982d7037a93a9ab38129c07336e7db Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:53:50 +0100 Subject: [PATCH 118/158] [main] Update dependencies from dotnet/runtime (#98511) * Update dependencies from https://github.com/dotnet/runtime build 20240215.1 Microsoft.DotNet.ILCompiler , Microsoft.NET.Sdk.IL , Microsoft.NETCore.App.Runtime.win-x64 , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , System.Reflection.Metadata , System.Reflection.MetadataLoadContext , System.Text.Json , Microsoft.SourceBuild.Intermediate.runtime.linux-x64 From Version 9.0.0-preview.2.24111.9 -> To Version 9.0.0-preview.2.24115.1 * Update dependencies from https://github.com/dotnet/runtime build 20240215.1 Microsoft.DotNet.ILCompiler , Microsoft.NET.Sdk.IL , Microsoft.NETCore.App.Runtime.win-x64 , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , System.Reflection.Metadata , System.Reflection.MetadataLoadContext , System.Text.Json , Microsoft.SourceBuild.Intermediate.runtime.linux-x64 From Version 9.0.0-preview.2.24111.9 -> To Version 9.0.0-preview.2.24115.1 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 36 ++++++++++++++++++------------------ eng/Versions.props | 14 +++++++------- global.json | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 509075938ac11c..61fe9ade1dbb81 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -314,43 +314,43 @@ https://github.com/dotnet/llvm-project 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/runtime - 9699f39112b2aea89a05a74199baf9049db85537 + 963626276e11bf5587aaed69826b62682b05d9c4 - + https://github.com/dotnet/runtime - 9699f39112b2aea89a05a74199baf9049db85537 + 963626276e11bf5587aaed69826b62682b05d9c4 - + https://github.com/dotnet/runtime - 9699f39112b2aea89a05a74199baf9049db85537 + 963626276e11bf5587aaed69826b62682b05d9c4 - + https://github.com/dotnet/runtime - 9699f39112b2aea89a05a74199baf9049db85537 + 963626276e11bf5587aaed69826b62682b05d9c4 - + https://github.com/dotnet/runtime - 9699f39112b2aea89a05a74199baf9049db85537 + 963626276e11bf5587aaed69826b62682b05d9c4 - + https://github.com/dotnet/runtime - 9699f39112b2aea89a05a74199baf9049db85537 + 963626276e11bf5587aaed69826b62682b05d9c4 - + https://github.com/dotnet/runtime - 9699f39112b2aea89a05a74199baf9049db85537 + 963626276e11bf5587aaed69826b62682b05d9c4 - + https://github.com/dotnet/runtime - 9699f39112b2aea89a05a74199baf9049db85537 + 963626276e11bf5587aaed69826b62682b05d9c4 - + https://github.com/dotnet/runtime - 9699f39112b2aea89a05a74199baf9049db85537 + 963626276e11bf5587aaed69826b62682b05d9c4 https://github.com/dotnet/xharness diff --git a/eng/Versions.props b/eng/Versions.props index 5c4fb67c97abfc..bb1645abaf2b4c 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -104,10 +104,10 @@ 6.0.0-preview.1.102 - 9.0.0-preview.2.24111.9 + 9.0.0-preview.2.24115.1 6.0.0 - 9.0.0-preview.2.24111.9 + 9.0.0-preview.2.24115.1 16.0.5-alpha.1.24112.1 16.0.5-alpha.1.24112.1 @@ -128,19 +128,19 @@ 8.0.0 5.0.0 4.5.5 - 9.0.0-preview.2.24111.9 - 9.0.0-preview.2.24111.9 + 9.0.0-preview.2.24115.1 + 9.0.0-preview.2.24115.1 6.0.0 5.0.0 5.0.0 5.0.0 7.0.0 - 9.0.0-preview.2.24111.9 + 9.0.0-preview.2.24115.1 6.0.0 7.0.0 4.5.4 4.5.0 - 9.0.0-preview.2.24111.9 + 9.0.0-preview.2.24115.1 8.0.0 8.0.0 @@ -218,7 +218,7 @@ 0.11.4-alpha.24112.1 - 9.0.0-preview.2.24111.9 + 9.0.0-preview.2.24115.1 9.0.0-preview.2.24112.1 diff --git a/global.json b/global.json index 75b4add3bebfc9..dc5aff95f8db27 100644 --- a/global.json +++ b/global.json @@ -13,6 +13,6 @@ "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.24112.1", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.NET.Sdk.IL": "9.0.0-preview.2.24111.9" + "Microsoft.NET.Sdk.IL": "9.0.0-preview.2.24115.1" } } From ae4c05aace3211bb5be5f17c6cfa74e322170db6 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Mon, 19 Feb 2024 15:42:39 +0100 Subject: [PATCH 119/158] [browser][MT] pre-load threads early in dotnet.js (#98637) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marek Fišera --- src/mono/browser/build/BrowserWasmApp.targets | 6 +- src/mono/browser/runtime/assets.ts | 3 +- src/mono/browser/runtime/cwraps.ts | 3 +- src/mono/browser/runtime/debug.ts | 1 + .../runtime/diagnostics/browser/controller.ts | 4 +- src/mono/browser/runtime/diagnostics/index.ts | 9 +- .../runtime/diagnostics/mock/environment.ts | 2 +- .../diagnostics/server_pthread/index.ts | 2 +- .../diagnostics/shared/controller-commands.ts | 4 +- src/mono/browser/runtime/dotnet.d.ts | 10 +- src/mono/browser/runtime/exports-binding.ts | 7 +- src/mono/browser/runtime/exports-internal.ts | 10 +- src/mono/browser/runtime/exports.ts | 2 +- src/mono/browser/runtime/interp-pgo.ts | 4 +- src/mono/browser/runtime/loader/assets.ts | 27 +- src/mono/browser/runtime/loader/config.ts | 12 +- src/mono/browser/runtime/loader/globals.ts | 2 + src/mono/browser/runtime/loader/run.ts | 3 +- src/mono/browser/runtime/loader/worker.ts | 11 +- src/mono/browser/runtime/managed-exports.ts | 2 +- src/mono/browser/runtime/marshal-to-cs.ts | 2 +- src/mono/browser/runtime/polyfills.ts | 9 +- src/mono/browser/runtime/pthreads/README.md | 4 +- .../browser/runtime/pthreads/browser/index.ts | 219 ----------- src/mono/browser/runtime/pthreads/index.ts | 18 + .../pthreads/{shared/index.ts => shared.ts} | 53 ++- .../pthreads/shared/emscripten-internals.ts | 65 ---- .../shared/emscripten-replacements.ts | 137 ------- .../runtime/pthreads/shared/tsconfig.json | 8 - .../browser/runtime/pthreads/shared/types.ts | 44 --- .../browser/runtime/pthreads/ui-thread.ts | 351 ++++++++++++++++++ .../eventloop.ts => worker-eventloop.ts} | 1 - .../{worker/events.ts => worker-events.ts} | 2 +- .../{worker/index.ts => worker-thread.ts} | 64 ++-- .../runtime/pthreads/worker/tsconfig.json | 7 - src/mono/browser/runtime/run.ts | 2 +- src/mono/browser/runtime/scheduling.ts | 2 +- src/mono/browser/runtime/startup.ts | 30 +- src/mono/browser/runtime/types/index.ts | 10 +- src/mono/browser/runtime/types/internal.ts | 71 +++- .../BootJsonData.cs | 9 +- src/tasks/WasmAppBuilder/WasmAppBuilder.cs | 20 +- 42 files changed, 653 insertions(+), 599 deletions(-) delete mode 100644 src/mono/browser/runtime/pthreads/browser/index.ts create mode 100644 src/mono/browser/runtime/pthreads/index.ts rename src/mono/browser/runtime/pthreads/{shared/index.ts => shared.ts} (75%) delete mode 100644 src/mono/browser/runtime/pthreads/shared/emscripten-internals.ts delete mode 100644 src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts delete mode 100644 src/mono/browser/runtime/pthreads/shared/tsconfig.json delete mode 100644 src/mono/browser/runtime/pthreads/shared/types.ts create mode 100644 src/mono/browser/runtime/pthreads/ui-thread.ts rename src/mono/browser/runtime/pthreads/{shared/eventloop.ts => worker-eventloop.ts} (99%) rename src/mono/browser/runtime/pthreads/{worker/events.ts => worker-events.ts} (98%) rename src/mono/browser/runtime/pthreads/{worker/index.ts => worker-thread.ts} (83%) delete mode 100644 src/mono/browser/runtime/pthreads/worker/tsconfig.json diff --git a/src/mono/browser/build/BrowserWasmApp.targets b/src/mono/browser/build/BrowserWasmApp.targets index ec2a81e1e066b9..6174769ef02ea4 100644 --- a/src/mono/browser/build/BrowserWasmApp.targets +++ b/src/mono/browser/build/BrowserWasmApp.targets @@ -121,7 +121,8 @@ - <_WasmPThreadPoolSize Condition="'$(_WasmPThreadPoolSize)' == ''">-1 + <_WasmPThreadPoolInitialSize Condition="'$(_WasmPThreadPoolInitialSize)' == ''">-1 + <_WasmPThreadPoolUnusedSize Condition="'$(_WasmPThreadPoolUnusedSize)' == ''">-1 @@ -148,7 +149,8 @@ NativeAssets="@(WasmNativeAsset)" DebugLevel="$(WasmDebugLevel)" IncludeThreadsWorker="$(WasmEnableThreads)" - PThreadPoolSize="$(_WasmPThreadPoolSize)" + PThreadPoolInitialSize="$(_WasmPThreadPoolInitialSize)" + PThreadPoolUnusedSize="$(_WasmPThreadPoolUnusedSize)" UseWebcil="$(WasmEnableWebcil)" WasmIncludeFullIcuData="$(WasmIncludeFullIcuData)" WasmIcuDataFileName="$(WasmIcuDataFileName)" diff --git a/src/mono/browser/runtime/assets.ts b/src/mono/browser/runtime/assets.ts index bd88949b6234c5..40dc39349ff565 100644 --- a/src/mono/browser/runtime/assets.ts +++ b/src/mono/browser/runtime/assets.ts @@ -1,13 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import type { AssetEntryInternal } from "./types/internal"; + import cwraps from "./cwraps"; import { mono_wasm_load_icu_data } from "./icu"; import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { mono_log_info, mono_log_debug, parseSymbolMapFile } from "./logging"; import { mono_wasm_load_bytes_into_heap } from "./memory"; import { endMeasure, MeasuredBlock, startMeasure } from "./profiler"; -import { AssetEntryInternal } from "./types/internal"; import { AssetEntry } from "./types"; import { VoidPtr } from "./types/emscripten"; import { setSegmentationRulesFromJson } from "./hybrid-globalization/grapheme-segmenter"; diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index 7dc7c207697ec9..dbb0babce82319 100644 --- a/src/mono/browser/runtime/cwraps.ts +++ b/src/mono/browser/runtime/cwraps.ts @@ -6,13 +6,12 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import type { MonoAssembly, MonoClass, MonoMethod, MonoObject, - MonoType, MonoObjectRef, MonoStringRef, JSMarshalerArguments + MonoType, MonoObjectRef, MonoStringRef, JSMarshalerArguments, PThreadPtr } from "./types/internal"; import type { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr, ManagedPointer } from "./types/emscripten"; import { Module, runtimeHelpers } from "./globals"; import { mono_log_error } from "./logging"; import { mono_assert } from "./globals"; -import { PThreadPtr } from "./pthreads/shared/types"; type SigLine = [lazyOrSkip: boolean | (() => boolean), name: string, returnType: string | null, argTypes?: string[], opts?: any]; diff --git a/src/mono/browser/runtime/debug.ts b/src/mono/browser/runtime/debug.ts index 1c919426bc3a84..74c0128f2e4e3b 100644 --- a/src/mono/browser/runtime/debug.ts +++ b/src/mono/browser/runtime/debug.ts @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import BuildConfiguration from "consts:configuration"; + import { INTERNAL, Module, loaderHelpers, runtimeHelpers } from "./globals"; import { toBase64StringImpl } from "./base64"; import cwraps from "./cwraps"; diff --git a/src/mono/browser/runtime/diagnostics/browser/controller.ts b/src/mono/browser/runtime/diagnostics/browser/controller.ts index 7bcdf62b868505..799972b1fd0e66 100644 --- a/src/mono/browser/runtime/diagnostics/browser/controller.ts +++ b/src/mono/browser/runtime/diagnostics/browser/controller.ts @@ -7,10 +7,10 @@ import { threads_c_functions as cwraps } from "../../cwraps"; import { INTERNAL, mono_assert } from "../../globals"; import { mono_log_info, mono_log_debug, mono_log_warn } from "../../logging"; import { withStackAlloc, getI32 } from "../../memory"; -import { Thread, waitForThread } from "../../pthreads/browser"; +import { waitForThread } from "../../pthreads"; import { isDiagnosticMessage, makeDiagnosticServerControlCommand } from "../shared/controller-commands"; import monoDiagnosticsMock from "consts:monoDiagnosticsMock"; -import { PThreadPtr } from "../../pthreads/shared/types"; +import { PThreadPtr, Thread } from "../../types/internal"; /// An object that can be used to control the diagnostic server. export interface ServerController { diff --git a/src/mono/browser/runtime/diagnostics/index.ts b/src/mono/browser/runtime/diagnostics/index.ts index 474fd8a824fd56..a9d6f9414267ae 100644 --- a/src/mono/browser/runtime/diagnostics/index.ts +++ b/src/mono/browser/runtime/diagnostics/index.ts @@ -37,12 +37,9 @@ let diagnosticsServerEnabled = false; let diagnosticsInitialized = false; export async function mono_wasm_init_diagnostics(): Promise { - if (diagnosticsInitialized) - return; - if (!WasmEnableThreads) { - mono_log_warn("ignoring diagnostics options because this runtime does not support diagnostics"); - return; - } + if (!WasmEnableThreads) return; + if (diagnosticsInitialized) return; + const options = diagnostic_options_from_environment(); if (!options) return; diff --git a/src/mono/browser/runtime/diagnostics/mock/environment.ts b/src/mono/browser/runtime/diagnostics/mock/environment.ts index bcbdf390a4dfe2..0de8c8b7acafc2 100644 --- a/src/mono/browser/runtime/diagnostics/mock/environment.ts +++ b/src/mono/browser/runtime/diagnostics/mock/environment.ts @@ -6,7 +6,7 @@ import type { FilterPredicate, MockEnvironment } from "./types"; import Serializer from "../server_pthread/ipc-protocol/base-serializer"; import { CommandSetId, EventPipeCommandId, ProcessCommandId } from "../server_pthread/ipc-protocol/types"; import { assertNever } from "../../types/internal"; -import { pthread_self } from "../../pthreads/worker"; +import { pthread_self } from "../../pthreads"; import { createPromiseController, mono_assert } from "../../globals"; diff --git a/src/mono/browser/runtime/diagnostics/server_pthread/index.ts b/src/mono/browser/runtime/diagnostics/server_pthread/index.ts index 1fbca276f3c897..cba9d5fba7d8dc 100644 --- a/src/mono/browser/runtime/diagnostics/server_pthread/index.ts +++ b/src/mono/browser/runtime/diagnostics/server_pthread/index.ts @@ -6,7 +6,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import monoDiagnosticsMock from "consts:monoDiagnosticsMock"; import { PromiseAndController, assertNever } from "../../types/internal"; -import { pthread_self } from "../../pthreads/worker"; +import { pthread_self } from "../../pthreads"; import { createPromiseController, mono_assert } from "../../globals"; import { threads_c_functions as cwraps } from "../../cwraps"; import { EventPipeSessionIDImpl } from "../shared/types"; diff --git a/src/mono/browser/runtime/diagnostics/shared/controller-commands.ts b/src/mono/browser/runtime/diagnostics/shared/controller-commands.ts index 5e08f56c627ea4..16aa6ad85944f2 100644 --- a/src/mono/browser/runtime/diagnostics/shared/controller-commands.ts +++ b/src/mono/browser/runtime/diagnostics/shared/controller-commands.ts @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import type { MonoThreadMessage } from "../../pthreads/shared"; -import { isMonoThreadMessage } from "../../pthreads/shared"; +import { isMonoThreadMessage } from "../../pthreads"; +import type { MonoThreadMessage } from "../../types/internal"; // Messages from the main thread to the diagnostic server thread export interface DiagnosticMessage extends MonoThreadMessage { diff --git a/src/mono/browser/runtime/dotnet.d.ts b/src/mono/browser/runtime/dotnet.d.ts index e60265f04a2813..7b901972e8b8f0 100644 --- a/src/mono/browser/runtime/dotnet.d.ts +++ b/src/mono/browser/runtime/dotnet.d.ts @@ -189,7 +189,15 @@ type MonoConfig = { /** * initial number of workers to add to the emscripten pthread pool */ - pthreadPoolSize?: number; + pthreadPoolInitialSize?: number; + /** + * number of unused workers kept in the emscripten pthread pool after startup + */ + pthreadPoolUnusedSize?: number; + /** + * Delay in milliseconds before starting the finalizer thread + */ + finalizerThreadStartDelayMs?: number; /** * If true, a list of the methods optimized by the interpreter will be saved and used for faster startup * on future runs of the application diff --git a/src/mono/browser/runtime/exports-binding.ts b/src/mono/browser/runtime/exports-binding.ts index c410dca9e75881..6ab2b430ab0481 100644 --- a/src/mono/browser/runtime/exports-binding.ts +++ b/src/mono/browser/runtime/exports-binding.ts @@ -10,8 +10,7 @@ import { mono_interp_tier_prepare_jiterpreter, mono_jiterp_free_method_data_js } import { mono_interp_jit_wasm_entry_trampoline, mono_interp_record_interp_entry } from "./jiterpreter-interp-entry"; import { mono_interp_jit_wasm_jit_call_trampoline, mono_interp_invoke_wasm_jit_call_trampoline, mono_interp_flush_jitcall_queue } from "./jiterpreter-jit-call"; import { mono_wasm_resolve_or_reject_promise } from "./marshal-to-js"; -import { mono_wasm_eventloop_has_unsettled_interop_promises } from "./pthreads/shared/eventloop"; -import { mono_wasm_pthread_on_pthread_attached, mono_wasm_pthread_on_pthread_unregistered, mono_wasm_pthread_on_pthread_registered, mono_wasm_pthread_set_name } from "./pthreads/worker"; +import { mono_wasm_eventloop_has_unsettled_interop_promises } from "./pthreads"; import { mono_wasm_schedule_timer, schedule_background_exec } from "./scheduling"; import { mono_wasm_asm_loaded } from "./startup"; import { mono_wasm_diagnostic_server_on_server_thread_created } from "./diagnostics/server_pthread"; @@ -22,13 +21,15 @@ import { mono_wasm_profiler_leave, mono_wasm_profiler_enter } from "./profiler"; import { mono_wasm_change_case, mono_wasm_change_case_invariant } from "./hybrid-globalization/change-case"; import { mono_wasm_compare_string, mono_wasm_ends_with, mono_wasm_starts_with, mono_wasm_index_of } from "./hybrid-globalization/collations"; import { mono_wasm_get_calendar_info } from "./hybrid-globalization/calendar"; -import { mono_wasm_install_js_worker_interop, mono_wasm_uninstall_js_worker_interop } from "./pthreads/shared"; import { mono_wasm_get_culture_info } from "./hybrid-globalization/culture-info"; import { mono_wasm_get_first_day_of_week, mono_wasm_get_first_week_of_year } from "./hybrid-globalization/locales"; import { mono_wasm_browser_entropy } from "./crypto"; import { mono_wasm_cancel_promise } from "./cancelable-promise"; +import { mono_wasm_pthread_on_pthread_attached, mono_wasm_pthread_on_pthread_unregistered, mono_wasm_pthread_on_pthread_registered, mono_wasm_pthread_set_name } from "./pthreads"; +import { mono_wasm_install_js_worker_interop, mono_wasm_uninstall_js_worker_interop } from "./pthreads"; + // the JS methods would be visible to EMCC linker and become imports of the WASM module export const mono_wasm_threads_imports = !WasmEnableThreads ? [] : [ diff --git a/src/mono/browser/runtime/exports-internal.ts b/src/mono/browser/runtime/exports-internal.ts index 5fe5773f975777..80c52669fb3ad6 100644 --- a/src/mono/browser/runtime/exports-internal.ts +++ b/src/mono/browser/runtime/exports-internal.ts @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import WasmEnableThreads from "consts:wasmEnableThreads"; + +import { MonoObjectNull, type MonoObject } from "./types/internal"; import cwraps, { profiler_c_functions } from "./cwraps"; import { mono_wasm_send_dbg_command_with_parms, mono_wasm_send_dbg_command, mono_wasm_get_dbg_command_info, mono_wasm_get_details, mono_wasm_release_object, mono_wasm_call_function_on, mono_wasm_debugger_resume, mono_wasm_detach_debugger, mono_wasm_raise_debug_event, mono_wasm_change_debugger_log_level, mono_wasm_debugger_attached } from "./debug"; import { http_wasm_supports_streaming_request, http_wasm_supports_streaming_response, http_wasm_create_controller, http_wasm_abort_request, http_wasm_abort_response, http_wasm_transform_stream_write, http_wasm_transform_stream_close, http_wasm_fetch, http_wasm_fetch_stream, http_wasm_fetch_bytes, http_wasm_get_response_header_names, http_wasm_get_response_header_values, http_wasm_get_response_bytes, http_wasm_get_response_length, http_wasm_get_streamed_response_bytes, http_wasm_get_response_type, http_wasm_get_response_status } from "./http"; @@ -17,16 +20,17 @@ import { loadLazyAssembly } from "./lazyLoading"; import { loadSatelliteAssemblies } from "./satelliteAssemblies"; import { forceDisposeProxies } from "./gc-handles"; import { mono_wasm_get_func_id_to_name_mappings } from "./logging"; -import { MonoObject, MonoObjectNull } from "./types/internal"; import { monoStringToStringUnsafe } from "./strings"; -import { thread_available } from "./pthreads/browser"; import { mono_wasm_bind_cs_function } from "./invoke-cs"; +import { dumpThreads, thread_available } from "./pthreads"; + export function export_internal(): any { return { // tests mono_wasm_exit: (exit_code: number) => { Module.err("early exit " + exit_code); }, forceDisposeProxies, + dumpThreads: WasmEnableThreads ? dumpThreads : undefined, // with mono_wasm_debugger_log and mono_wasm_trace_logger logging: undefined, @@ -57,7 +61,7 @@ export function export_internal(): any { get_global_this, get_dotnet_instance: () => exportedRuntimeAPI, dynamic_import, - thread_available, + thread_available: WasmEnableThreads ? thread_available : undefined, mono_wasm_bind_cs_function, // BrowserWebSocket diff --git a/src/mono/browser/runtime/exports.ts b/src/mono/browser/runtime/exports.ts index 2f3aa96a0ec6ba..8b2984f393b1f0 100644 --- a/src/mono/browser/runtime/exports.ts +++ b/src/mono/browser/runtime/exports.ts @@ -22,7 +22,7 @@ import { mono_wasm_stringify_as_error_with_stack } from "./logging"; import { instantiate_asset, instantiate_symbols_asset, instantiate_segmentation_rules_asset } from "./assets"; import { jiterpreter_dump_stats } from "./jiterpreter"; import { forceDisposeProxies } from "./gc-handles"; -import { dumpThreads } from "./pthreads/browser"; +import { dumpThreads } from "./pthreads"; export let runtimeList: RuntimeList; diff --git a/src/mono/browser/runtime/interp-pgo.ts b/src/mono/browser/runtime/interp-pgo.ts index dfe894f5698263..79ea1e29ab5df1 100644 --- a/src/mono/browser/runtime/interp-pgo.ts +++ b/src/mono/browser/runtime/interp-pgo.ts @@ -197,7 +197,9 @@ export async function getCacheKey(prefix: string): Promise { delete inputs.interopCleanupOnExit; delete inputs.dumpThreadsOnNonZeroExit; delete inputs.logExitCode; - delete inputs.pthreadPoolSize; + delete inputs.pthreadPoolInitialSize; + delete inputs.pthreadPoolUnusedSize; + delete inputs.finalizerThreadStartDelayMs; delete inputs.asyncFlushOnExit; delete inputs.remoteSources; delete inputs.ignorePdbLoadErrors; diff --git a/src/mono/browser/runtime/loader/assets.ts b/src/mono/browser/runtime/loader/assets.ts index 624244a63ff9c7..49d2f0f8ac6d8c 100644 --- a/src/mono/browser/runtime/loader/assets.ts +++ b/src/mono/browser/runtime/loader/assets.ts @@ -3,7 +3,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; -import type { AssetEntryInternal, PromiseAndController } from "../types/internal"; +import { PThreadPtrNull, type AssetEntryInternal, type PThreadWorker, type PromiseAndController } from "../types/internal"; import type { AssetBehaviors, AssetEntry, LoadingResource, ResourceList, SingleAssetBehaviors as SingleAssetBehaviors, WebAssemblyBootResourceType } from "../types"; import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, ENVIRONMENT_IS_WEB, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { createPromiseController } from "./promise-controller"; @@ -20,6 +20,9 @@ let parallel_count = 0; const assetsToLoad: AssetEntryInternal[] = []; const singleAssets: Map = new Map(); +// A duplicate in pthreads/shared.ts +const worker_empty_prefix = " - "; + const jsRuntimeModulesAssetTypes: { [k: string]: boolean } = { @@ -733,4 +736,24 @@ export async function streamingCompileWasm() { catch (err) { loaderHelpers.wasmCompilePromise.promise_control.reject(err); } -} \ No newline at end of file +} + +export function preloadWorkers() { + if (!WasmEnableThreads) return; + const jsModuleWorker = resolve_single_asset_path("js-module-threads"); + for (let i = 0; i < loaderHelpers.config.pthreadPoolInitialSize!; i++) { + const workerNumber = loaderHelpers.workerNextNumber++; + const worker: Partial = new Worker(jsModuleWorker.resolvedUrl!, { + name: "dotnet-worker-" + workerNumber.toString().padStart(3, "0"), + }); + worker.info = { + workerNumber, + pthreadId: PThreadPtrNull, + reuseCount: 0, + updateCount: 0, + threadPrefix: worker_empty_prefix, + threadName: "emscripten-pool", + } as any; + loaderHelpers.loadingWorkers.push(worker as any); + } +} diff --git a/src/mono/browser/runtime/loader/config.ts b/src/mono/browser/runtime/loader/config.ts index 6f64d327fe7add..8aec3557437212 100644 --- a/src/mono/browser/runtime/loader/config.ts +++ b/src/mono/browser/runtime/loader/config.ts @@ -187,9 +187,15 @@ export function normalizeConfig() { config.cachedResourcesPurgeDelay = 10000; } - if (WasmEnableThreads && !Number.isInteger(config.pthreadPoolSize)) { - // ActiveIssue https://github.com/dotnet/runtime/issues/75602 - config.pthreadPoolSize = 7; + // ActiveIssue https://github.com/dotnet/runtime/issues/75602 + if (WasmEnableThreads && !Number.isInteger(config.pthreadPoolInitialSize)) { + config.pthreadPoolInitialSize = 7; + } + if (WasmEnableThreads && !Number.isInteger(config.pthreadPoolUnusedSize)) { + config.pthreadPoolUnusedSize = 3; + } + if (WasmEnableThreads && !Number.isInteger(config.finalizerThreadStartDelayMs)) { + config.finalizerThreadStartDelayMs = 200; } // this is how long the Mono GC will try to wait for all threads to be suspended before it gives up and aborts the process diff --git a/src/mono/browser/runtime/loader/globals.ts b/src/mono/browser/runtime/loader/globals.ts index c9d8ffd8e8c9d8..7e9708845e65b0 100644 --- a/src/mono/browser/runtime/loader/globals.ts +++ b/src/mono/browser/runtime/loader/globals.ts @@ -92,6 +92,8 @@ export function setLoaderGlobals( loadedFiles: [], loadedAssemblies: [], libraryInitializers: [], + loadingWorkers: [], + workerNextNumber: 1, actual_downloaded_assets_count: 0, actual_instantiated_assets_count: 0, expected_downloaded_assets_count: 0, diff --git a/src/mono/browser/runtime/loader/run.ts b/src/mono/browser/runtime/loader/run.ts index 1f85b4e2412002..730a692b4ebf5b 100644 --- a/src/mono/browser/runtime/loader/run.ts +++ b/src/mono/browser/runtime/loader/run.ts @@ -10,7 +10,7 @@ import { ENVIRONMENT_IS_WEB, ENVIRONMENT_IS_WORKER, emscriptenModule, exportedRu import { deep_merge_config, deep_merge_module, mono_wasm_load_config } from "./config"; import { installUnhandledErrorHandler, mono_exit, registerEmscriptenExitHandlers } from "./exit"; import { setup_proxy_console, mono_log_info, mono_log_debug } from "./logging"; -import { mono_download_assets, prepareAssets, prepareAssetsWorker, resolve_single_asset_path, streamingCompileWasm } from "./assets"; +import { mono_download_assets, preloadWorkers, prepareAssets, prepareAssetsWorker, resolve_single_asset_path, streamingCompileWasm } from "./assets"; import { detect_features_and_polyfill } from "./polyfills"; import { runtimeHelpers, loaderHelpers } from "./globals"; import { init_globalization } from "./icu"; @@ -487,6 +487,7 @@ async function createEmscriptenMain(): Promise { setTimeout(async () => { try { init_globalization(); + preloadWorkers(); await mono_download_assets(); } catch (err) { diff --git a/src/mono/browser/runtime/loader/worker.ts b/src/mono/browser/runtime/loader/worker.ts index 81a9cecad6a741..baeeaf0165f673 100644 --- a/src/mono/browser/runtime/loader/worker.ts +++ b/src/mono/browser/runtime/loader/worker.ts @@ -1,10 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { MonoConfigInternal, WorkerToMainMessageType, monoMessageSymbol } from "../types/internal"; +import { MonoConfigInternal, PThreadInfo, WorkerToMainMessageType, monoMessageSymbol } from "../types/internal"; import { MonoConfig } from "../types"; import { deep_merge_config, normalizeConfig } from "./config"; -import { ENVIRONMENT_IS_WEB, loaderHelpers } from "./globals"; +import { ENVIRONMENT_IS_WEB, loaderHelpers, runtimeHelpers } from "./globals"; import { mono_log_debug } from "./logging"; export function setupPreloadChannelToMainThread() { @@ -13,7 +13,8 @@ export function setupPreloadChannelToMainThread() { const mainPort = channel.port2; workerPort.addEventListener("message", (event) => { const config = JSON.parse(event.data.config) as MonoConfig; - onMonoConfigReceived(config); + const monoThreadInfo = JSON.parse(event.data.monoThreadInfo) as PThreadInfo; + onMonoConfigReceived(config, monoThreadInfo); workerPort.close(); mainPort.close(); }, { once: true }); @@ -30,13 +31,13 @@ export function setupPreloadChannelToMainThread() { let workerMonoConfigReceived = false; // called when the main thread sends us the mono config -function onMonoConfigReceived(config: MonoConfigInternal): void { +function onMonoConfigReceived(config: MonoConfigInternal, monoThreadInfo: PThreadInfo): void { if (workerMonoConfigReceived) { mono_log_debug("mono config already received"); return; } - deep_merge_config(loaderHelpers.config, config); + runtimeHelpers.monoThreadInfo = monoThreadInfo; normalizeConfig(); mono_log_debug("mono config received"); workerMonoConfigReceived = true; diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts index 87da9ac8d259ef..c8aabae88e37b7 100644 --- a/src/mono/browser/runtime/managed-exports.ts +++ b/src/mono/browser/runtime/managed-exports.ts @@ -11,7 +11,7 @@ import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_bool_to_cs, mars import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begin_marshal_task_to_js, marshal_exception_to_js } from "./marshal-to-js"; import { do_not_force_dispose } from "./gc-handles"; import { assert_c_interop, assert_js_interop } from "./invoke-js"; -import { mono_wasm_main_thread_ptr } from "./pthreads/shared"; +import { mono_wasm_main_thread_ptr } from "./pthreads"; import { _zero_region } from "./memory"; import { stringToUTF8Ptr } from "./strings"; diff --git a/src/mono/browser/runtime/marshal-to-cs.ts b/src/mono/browser/runtime/marshal-to-cs.ts index fa0f299a5f2b80..a42eb724a92151 100644 --- a/src/mono/browser/runtime/marshal-to-cs.ts +++ b/src/mono/browser/runtime/marshal-to-cs.ts @@ -22,7 +22,7 @@ import { _zero_region, forceThreadMemoryViewRefresh, localHeapViewF64, localHeap import { stringToMonoStringRoot, stringToUTF16 } from "./strings"; import { JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerToCs, MarshalerToJs, BoundMarshalerToCs, MarshalerType } from "./types/internal"; import { TypedArray } from "./types/emscripten"; -import { addUnsettledPromise, settleUnsettledPromise } from "./pthreads/shared/eventloop"; +import { addUnsettledPromise, settleUnsettledPromise } from "./pthreads"; import { mono_log_debug } from "./logging"; import { complete_task } from "./managed-exports"; import { gc_locked } from "./gc-lock"; diff --git a/src/mono/browser/runtime/polyfills.ts b/src/mono/browser/runtime/polyfills.ts index 7aae92566b34a0..0f8700f84754ec 100644 --- a/src/mono/browser/runtime/polyfills.ts +++ b/src/mono/browser/runtime/polyfills.ts @@ -5,7 +5,8 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import type { EmscriptenReplacements } from "./types/internal"; import type { TypedArray } from "./types/emscripten"; import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_WORKER, INTERNAL, Module, loaderHelpers, runtimeHelpers } from "./globals"; -import { replaceEmscriptenPThreadLibrary } from "./pthreads/shared/emscripten-replacements"; +import { replaceEmscriptenPThreadWorker } from "./pthreads"; +import { replaceEmscriptenPThreadUI } from "./pthreads"; const dummyPerformance = { now: function () { @@ -34,7 +35,11 @@ export function initializeReplacements(replacements: EmscriptenReplacements): vo // threads if (WasmEnableThreads && replacements.modulePThread) { - replaceEmscriptenPThreadLibrary(replacements.modulePThread); + if (ENVIRONMENT_IS_WORKER) { + replaceEmscriptenPThreadWorker(replacements.modulePThread); + } else { + replaceEmscriptenPThreadUI(replacements.modulePThread); + } } } diff --git a/src/mono/browser/runtime/pthreads/README.md b/src/mono/browser/runtime/pthreads/README.md index 34f3508988cc9f..757cc73a85e420 100644 --- a/src/mono/browser/runtime/pthreads/README.md +++ b/src/mono/browser/runtime/pthreads/README.md @@ -17,11 +17,11 @@ On the other hand, pthreads in native code have a peer relationship: any two thr ## Main thread API -In the main thread, `pthreads/browser` provides a `getThread` function that returns a `{ pthread_ptr: pthread_ptr, worker: Worker, port: MessagePort }` object that can be used to communicate with the worker thread. +In the main thread, `pthreads/ui-thread` provides a `getThread` function that returns a `{ pthread_ptr: pthread_ptr, worker: Worker, port: MessagePort }` object that can be used to communicate with the worker thread. ## Worker thread API -In the worker threads, `pthread/worker` provides `currentWorkerThreadEvents` which is an [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) that fires `'dotnet:pthread:created'` and `'dotnet:pthread:attached'` events when a pthread is started on the worker, and when that pthread attaches to the Mono runtime. A good place to add event listeners is in `mono_wasm_pthread_worker_init` in `startup.ts`. +In the worker threads, `pthread/worker-*` provides `currentWorkerThreadEvents` which is an [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) that fires `'dotnet:pthread:created'` and `'dotnet:pthread:attached'` events when a pthread is started on the worker, and when that pthread attaches to the Mono runtime. A good place to add event listeners is in `mono_wasm_pthread_worker_init` in `startup.ts`. The events have a `portToMain` property which is a dotnet-specific `MessagePort` for posting messages to the main thread and for listening for messages from the main thread. ## Implementation diff --git a/src/mono/browser/runtime/pthreads/browser/index.ts b/src/mono/browser/runtime/pthreads/browser/index.ts deleted file mode 100644 index 84c58b694d13a1..00000000000000 --- a/src/mono/browser/runtime/pthreads/browser/index.ts +++ /dev/null @@ -1,219 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import WasmEnableThreads from "consts:wasmEnableThreads"; - -import { MonoWorkerToMainMessage, PThreadInfo, PThreadPtr, PThreadPtrNull } from "../shared/types"; -import { MonoThreadMessage, mono_wasm_pthread_ptr, update_thread_info } from "../shared"; -import { PThreadWorker, allocateUnusedWorker, getRunningWorkers, getUnusedWorkerPool, getWorker, loadWasmModuleToWorker } from "../shared/emscripten-internals"; -import { createPromiseController, mono_assert, runtimeHelpers } from "../../globals"; -import { MainToWorkerMessageType, PromiseAndController, PromiseController, WorkerToMainMessageType, monoMessageSymbol } from "../../types/internal"; -import { mono_log_info } from "../../logging"; -import { monoThreadInfo } from "../worker"; -import { mono_wasm_init_diagnostics } from "../../diagnostics"; - -const threadPromises: Map[]> = new Map(); - -export interface Thread { - readonly pthreadPtr: PThreadPtr; - readonly port: MessagePort; - postMessageToWorker(message: T): void; -} - -class ThreadImpl implements Thread { - constructor(readonly pthreadPtr: PThreadPtr, readonly worker: Worker, readonly port: MessagePort) { } - postMessageToWorker(message: T): void { - this.port.postMessage(message); - } -} - -/// wait until the thread with the given id has set up a message port to the runtime -export function waitForThread(pthreadPtr: PThreadPtr): Promise { - if (!WasmEnableThreads) return null as any; - const worker = getWorker(pthreadPtr); - if (worker?.thread) { - return Promise.resolve(worker?.thread); - } - const promiseAndController = createPromiseController(); - const arr = threadPromises.get(pthreadPtr); - if (arr === undefined) { - threadPromises.set(pthreadPtr, [promiseAndController.promise_control]); - } else { - arr.push(promiseAndController.promise_control); - } - return promiseAndController.promise; -} - -export function resolveThreadPromises(pthreadPtr: PThreadPtr, thread?: Thread): void { - if (!WasmEnableThreads) return; - const arr = threadPromises.get(pthreadPtr); - if (arr !== undefined) { - arr.forEach((controller) => { - if (thread) { - controller.resolve(thread); - } else { - controller.reject(); - } - }); - threadPromises.delete(pthreadPtr); - } -} - -// handler that runs in the main thread when a message is received from a pthread worker -function monoWorkerMessageHandler(worker: PThreadWorker, ev: MessageEvent): void { - if (!WasmEnableThreads) return; - let pthreadId: PThreadPtr; - // this is emscripten message - if (ev.data.cmd === "killThread") { - pthreadId = ev.data["thread"]; - mono_assert(pthreadId == worker.info.pthreadId, "expected pthreadId to match"); - worker.info.isRunning = false; - worker.info.pthreadId = PThreadPtrNull; - return; - } - - const message = ev.data[monoMessageSymbol] as MonoWorkerToMainMessage; - if (message === undefined) { - /// N.B. important to ignore messages we don't recognize - Emscripten uses the message event to send internal messages - return; - } - - let port: MessagePort; - let thread: Thread; - pthreadId = message.info?.pthreadId ?? 0; - - worker.info = Object.assign(worker.info, message.info, {}); - switch (message.monoCmd) { - case WorkerToMainMessageType.preload: - // this one shot port from setupPreloadChannelToMainThread - port = message.port!; - port.postMessage({ - type: "pthread", - cmd: MainToWorkerMessageType.applyConfig, - config: JSON.stringify(runtimeHelpers.config) - }); - port.close(); - break; - case WorkerToMainMessageType.pthreadCreated: - port = message.port!; - thread = new ThreadImpl(pthreadId, worker, port); - worker.thread = thread; - worker.info.isRunning = true; - resolveThreadPromises(pthreadId, thread); - break; - case WorkerToMainMessageType.monoRegistered: - case WorkerToMainMessageType.monoAttached: - case WorkerToMainMessageType.enabledInterop: - case WorkerToMainMessageType.monoUnRegistered: - case WorkerToMainMessageType.updateInfo: - // just worker.info updates above - break; - default: - throw new Error(`Unhandled message from worker: ${message.monoCmd}`); - } -} - -let pendingWorkerLoad: PromiseAndController | undefined; - -/// Called by Emscripten internals on the browser thread when a new pthread worker is created and added to the pthread worker pool. -/// At this point the worker doesn't have any pthread assigned to it, yet. -export function onWorkerLoadInitiated(worker: PThreadWorker, loaded: Promise): void { - if (!WasmEnableThreads) return; - worker.addEventListener("message", (ev) => monoWorkerMessageHandler(worker, ev)); - if (pendingWorkerLoad == undefined) { - pendingWorkerLoad = createPromiseController(); - } - loaded.then(() => { - worker.info.isLoaded = true; - if (pendingWorkerLoad != undefined) { - pendingWorkerLoad.promise_control.resolve(); - pendingWorkerLoad = undefined; - } - }); -} - -export function thread_available(): Promise { - if (!WasmEnableThreads) return null as any; - if (pendingWorkerLoad == undefined) { - return Promise.resolve(); - } - return pendingWorkerLoad.promise; -} - -export async function mono_wasm_init_threads() { - if (!WasmEnableThreads) return; - monoThreadInfo.pthreadId = mono_wasm_pthread_ptr(); - monoThreadInfo.threadName = "UI Thread"; - monoThreadInfo.isUI = true; - monoThreadInfo.isRunning = true; - update_thread_info(); - await instantiateWasmPThreadWorkerPool(); - await mono_wasm_init_diagnostics(); -} - -/// We call on the main thread this during startup to pre-allocate a pool of pthread workers. -/// At this point asset resolution needs to be working (ie we loaded MonoConfig). -/// This is used instead of the Emscripten PThread.initMainThread because we call it later. -export function preAllocatePThreadWorkerPool(pthreadPoolSize: number): void { - if (!WasmEnableThreads) return; - for (let i = 0; i < pthreadPoolSize; i++) { - allocateUnusedWorker(); - } -} - -/// We call this on the main thread during startup once we fetched WasmModule. -/// This sends a message to each pre-allocated worker to load the WasmModule and dotnet.js and to set up -/// message handling. -/// This is used instead of the Emscripten "receiveInstance" in "createWasm" because that code is -/// conditioned on a non-zero PTHREAD_POOL_SIZE (but we set it to 0 to avoid early worker allocation). -export async function instantiateWasmPThreadWorkerPool(): Promise { - if (!WasmEnableThreads) return null as any; - // this is largely copied from emscripten's "receiveInstance" in "createWasm" in "src/preamble.js" - const workers = getUnusedWorkerPool(); - if (workers.length > 0) { - const promises = workers.map(loadWasmModuleToWorker); - await Promise.all(promises); - } -} - -// when we create threads with browser event loop, it's not able to be joined by mono's thread join during shutdown and blocks process exit -export function cancelThreads() { - const workers: PThreadWorker[] = getRunningWorkers(); - for (const worker of workers) { - if (worker.info.isExternalEventLoop) { - worker.postMessage({ cmd: "cancel" }); - } - } -} - -export function dumpThreads(): void { - if (!WasmEnableThreads) return; - mono_log_info("Dumping web worker info as seen by UI thread, it could be stale: "); - const emptyInfo = { - pthreadId: 0, - threadPrefix: " - ", - threadName: "????", - isRunning: false, - isAttached: false, - isExternalEventLoop: false, - reuseCount: 0, - }; - const threadInfos: PThreadInfo[] = [ - Object.assign({}, emptyInfo, monoThreadInfo), // UI thread - ]; - for (const worker of getRunningWorkers()) { - threadInfos.push(Object.assign({}, emptyInfo, worker.info)); - } - for (const worker of getUnusedWorkerPool()) { - threadInfos.push(Object.assign({}, emptyInfo, worker.info)); - } - threadInfos.forEach((info, i) => { - const idx = (i + "").padStart(2, "0"); - const isRunning = (info.isRunning + "").padStart(5, " "); - const isAttached = (info.isAttached + "").padStart(5, " "); - const isEventLoop = (info.isExternalEventLoop + "").padStart(5, " "); - const reuseCount = (info.reuseCount + "").padStart(3, " "); - // eslint-disable-next-line no-console - console.info(`${idx} | ${info.threadPrefix}: isRunning:${isRunning} isAttached:${isAttached} isEventLoop:${isEventLoop} reuseCount:${reuseCount} - ${info.threadName}`); - }); -} diff --git a/src/mono/browser/runtime/pthreads/index.ts b/src/mono/browser/runtime/pthreads/index.ts new file mode 100644 index 00000000000000..a7b5da11e03fbe --- /dev/null +++ b/src/mono/browser/runtime/pthreads/index.ts @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +export { + mono_wasm_main_thread_ptr, mono_wasm_install_js_worker_interop, mono_wasm_uninstall_js_worker_interop, + mono_wasm_pthread_ptr, update_thread_info, isMonoThreadMessage, monoThreadInfo, +} from "./shared"; +export { + dumpThreads, thread_available, cancelThreads, is_thread_available, + populateEmscriptenPool, mono_wasm_init_threads, init_finalizer_thread, + waitForThread, replaceEmscriptenPThreadUI +} from "./ui-thread"; +export { addUnsettledPromise, settleUnsettledPromise, mono_wasm_eventloop_has_unsettled_interop_promises } from "./worker-eventloop"; +export { + mono_wasm_pthread_on_pthread_attached, mono_wasm_pthread_on_pthread_unregistered, + mono_wasm_pthread_on_pthread_registered, mono_wasm_pthread_set_name, currentWorkerThreadEvents, + dotnetPthreadCreated, initWorkerThreadEvents, replaceEmscriptenPThreadWorker, pthread_self +} from "./worker-thread"; diff --git a/src/mono/browser/runtime/pthreads/shared/index.ts b/src/mono/browser/runtime/pthreads/shared.ts similarity index 75% rename from src/mono/browser/runtime/pthreads/shared/index.ts rename to src/mono/browser/runtime/pthreads/shared.ts index aadab84fa12067..97c41197d35e04 100644 --- a/src/mono/browser/runtime/pthreads/shared/index.ts +++ b/src/mono/browser/runtime/pthreads/shared.ts @@ -4,23 +4,25 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import BuildConfiguration from "consts:configuration"; -import { ENVIRONMENT_IS_PTHREAD, Module, loaderHelpers, mono_assert, runtimeHelpers } from "../../globals"; -import { set_thread_prefix } from "../../logging"; -import { bindings_init } from "../../startup"; -import { forceDisposeProxies } from "../../gc-handles"; -import { GCHandle, GCHandleNull, WorkerToMainMessageType, monoMessageSymbol } from "../../types/internal"; -import { MonoWorkerToMainMessage, PThreadPtr, PThreadPtrNull } from "./types"; -import { monoThreadInfo } from "../worker"; - -/// Messages sent on the dedicated mono channel between a pthread and the browser thread - -// We use a namespacing scheme to avoid collisions: type/command should be unique. -export interface MonoThreadMessage { - // Type of message. Generally a subsystem like "diagnostic_server", or "event_pipe", "debugger", etc. - type: string; - // A particular kind of message. For example, "started", "stopped", "stopped_with_error", etc. - cmd: string; -} +import type { GCHandle, MonoThreadMessage, PThreadInfo, PThreadPtr } from "../types/internal"; + +import { ENVIRONMENT_IS_PTHREAD, Module, loaderHelpers, mono_assert, runtimeHelpers } from "../globals"; +import { set_thread_prefix } from "../logging"; +import { bindings_init } from "../startup"; +import { forceDisposeProxies } from "../gc-handles"; +import { monoMessageSymbol, GCHandleNull, PThreadPtrNull, WorkerToMainMessageType } from "../types/internal"; + +// A duplicate in loader/assets.ts +export const worker_empty_prefix = " - "; + +const monoThreadInfoPartial: Partial = { + pthreadId: PThreadPtrNull, + reuseCount: 0, + updateCount: 0, + threadPrefix: worker_empty_prefix, + threadName: "emscripten-loaded", +}; +export const monoThreadInfo: PThreadInfo = monoThreadInfoPartial as PThreadInfo; export function isMonoThreadMessage(x: unknown): x is MonoThreadMessage { if (typeof (x) !== "object" || x === null) { @@ -65,6 +67,7 @@ export function mono_wasm_uninstall_js_worker_interop(): void { // this is just for Debug build of the runtime, making it easier to debug worker threads export function update_thread_info(): void { + if (!WasmEnableThreads) return; const threadType = !monoThreadInfo.isRegistered ? "emsc" : monoThreadInfo.isUI ? "-UI-" : monoThreadInfo.isTimer ? "timr" @@ -110,4 +113,18 @@ export function postMessageToMain(message: MonoWorkerToMainMessage, transfer?: T self.postMessage({ [monoMessageSymbol]: message }, transfer ? transfer : []); -} \ No newline at end of file +} + +export interface MonoWorkerToMainMessage { + monoCmd: WorkerToMainMessageType; + info: PThreadInfo; + port?: MessagePort; +} + +/// Identification of the current thread executing on a worker +export interface PThreadSelf { + info: PThreadInfo; + portToBrowser: MessagePort; + postMessageToBrowser: (message: T, transfer?: Transferable[]) => void; + addEventListenerFromBrowser: (listener: (event: MessageEvent) => void) => void; +} diff --git a/src/mono/browser/runtime/pthreads/shared/emscripten-internals.ts b/src/mono/browser/runtime/pthreads/shared/emscripten-internals.ts deleted file mode 100644 index 0529744570efda..00000000000000 --- a/src/mono/browser/runtime/pthreads/shared/emscripten-internals.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import { Module } from "../../globals"; -import { Thread } from "../browser"; -import { PThreadInfo, PThreadPtr } from "./types"; - -/** @module emscripten-internals accessors to the functions in the emscripten PThreads library, including - * the low-level representations of {@linkcode PThreadPtr} thread info structs, etc. - * Additionally, note that some of these functions are replaced by {@linkcode file://./emscripten-replacements.ts}. - * These have a hard dependency on the version of Emscripten that we are using and may need to be kept in sync with - * {@linkcode file://./../../../emsdk/upstream/emscripten/src/library_pthread.js} - */ - -// This is what we know about the Emscripten PThread library -export interface PThreadLibrary { - unusedWorkers: PThreadWorker[]; - runningWorkers: PThreadWorker[]; - pthreads: PThreadInfoMap; - allocateUnusedWorker: () => void; - loadWasmModuleToWorker: (worker: PThreadWorker) => Promise; - threadInitTLS: () => void, - getNewWorker: () => PThreadWorker, - returnWorkerToPool: (worker: PThreadWorker) => void, -} - - -/// N.B. emscripten deletes the `pthread` property from the worker when it is not actively running a pthread -export interface PThreadWorker extends Worker { - pthread_ptr: PThreadPtr; - loaded: boolean; - // this info is updated via async messages from the worker, it could be stale - info: PThreadInfo; - thread?: Thread; -} - -interface PThreadInfoMap { - [key: number]: PThreadWorker; -} - - -export function getWorker(pthreadPtr: PThreadPtr): PThreadWorker | undefined { - return getModulePThread().pthreads[pthreadPtr as any]; -} - -export function allocateUnusedWorker(): void { - /// See library_pthread.js in Emscripten. - /// This function allocates a new worker and adds it to the pool of workers. - /// It's called when the pool of workers is empty and a new thread is created. - getModulePThread().allocateUnusedWorker(); -} -export function getUnusedWorkerPool(): PThreadWorker[] { - return getModulePThread().unusedWorkers; -} -export function getRunningWorkers(): PThreadWorker[] { - return getModulePThread().runningWorkers; -} - -export function loadWasmModuleToWorker(worker: PThreadWorker): Promise { - return getModulePThread().loadWasmModuleToWorker(worker); -} - -export function getModulePThread(): PThreadLibrary { - return (Module).PThread as PThreadLibrary; -} diff --git a/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts b/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts deleted file mode 100644 index 0acb8b6615c69d..00000000000000 --- a/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts +++ /dev/null @@ -1,137 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import WasmEnableThreads from "consts:wasmEnableThreads"; -import BuildConfiguration from "consts:configuration"; - -import { dumpThreads, onWorkerLoadInitiated, resolveThreadPromises } from "../browser"; -import { mono_wasm_pthread_on_pthread_created, onRunMessage } from "../worker"; -import { PThreadLibrary, PThreadWorker, getModulePThread, getUnusedWorkerPool } from "./emscripten-internals"; -import { Module, loaderHelpers, mono_assert } from "../../globals"; -import { mono_log_warn } from "../../logging"; -import { PThreadPtr, PThreadPtrNull } from "./types"; - -/** @module emscripten-replacements Replacements for individual functions in the emscripten PThreads library. - * These have a hard dependency on the version of Emscripten that we are using and may need to be kept in sync with - * {@linkcode file://./../../../emsdk/upstream/emscripten/src/library_pthread.js} - */ - -export function replaceEmscriptenPThreadLibrary(modulePThread: PThreadLibrary): void { - if (!WasmEnableThreads) return; - - const originalLoadWasmModuleToWorker = modulePThread.loadWasmModuleToWorker; - const originalThreadInitTLS = modulePThread.threadInitTLS; - const originalReturnWorkerToPool = modulePThread.returnWorkerToPool; - const original_emscripten_thread_init = (Module as any)["__emscripten_thread_init"]; - - - (Module as any)["__emscripten_thread_init"] = (pthread_ptr: PThreadPtr, isMainBrowserThread: number, isMainRuntimeThread: number, canBlock: number) => { - onRunMessage(pthread_ptr); - original_emscripten_thread_init(pthread_ptr, isMainBrowserThread, isMainRuntimeThread, canBlock); - }; - - modulePThread.loadWasmModuleToWorker = (worker: PThreadWorker): Promise => { - const afterLoaded = originalLoadWasmModuleToWorker(worker); - afterLoaded.then(() => { - availableThreadCount++; - }); - onWorkerLoadInitiated(worker, afterLoaded); - if (loaderHelpers.config.exitOnUnhandledError) { - worker.onerror = (e) => { - loaderHelpers.mono_exit(1, e); - }; - } - return afterLoaded; - }; - modulePThread.threadInitTLS = (): void => { - originalThreadInitTLS(); - mono_wasm_pthread_on_pthread_created(); - }; - modulePThread.allocateUnusedWorker = allocateUnusedWorker; - modulePThread.getNewWorker = () => getNewWorker(modulePThread); - modulePThread.returnWorkerToPool = (worker: PThreadWorker) => { - // when JS interop is installed on JSWebWorker - // we can't reuse the worker, because user code could leave the worker JS globals in a dirty state - worker.info.isRunning = false; - resolveThreadPromises(worker.pthread_ptr, undefined); - worker.info.pthreadId = PThreadPtrNull; - if (worker.thread?.port) { - worker.thread.port.close(); - } - worker.thread = undefined; - if (worker.info && worker.info.isDirtyBecauseOfInterop) { - // we are on UI thread, invoke the handler directly to destroy the dirty worker - worker.onmessage!(new MessageEvent("message", { - data: { - "cmd": "killThread", - thread: worker.pthread_ptr - } - })); - } else { - availableThreadCount++; - originalReturnWorkerToPool(worker); - } - }; - if (BuildConfiguration === "Debug") { - (globalThis as any).dumpThreads = dumpThreads; - (globalThis as any).getModulePThread = getModulePThread; - } -} - -let availableThreadCount = 0; -export function is_thread_available() { - if (!WasmEnableThreads) return true; - return availableThreadCount > 0; -} - -function getNewWorker(modulePThread: PThreadLibrary): PThreadWorker { - if (!WasmEnableThreads) return null as any; - - if (modulePThread.unusedWorkers.length == 0) { - mono_log_warn(`Failed to find unused WebWorker, this may deadlock. Please increase the pthreadPoolSize. Running threads ${modulePThread.runningWorkers.length}. Loading workers: ${modulePThread.unusedWorkers.length}`); - const worker = allocateUnusedWorker(); - modulePThread.loadWasmModuleToWorker(worker); - availableThreadCount--; - return worker; - } - - // keep them pre-allocated all the time, not just during startup - if (loaderHelpers.config.pthreadPoolSize && modulePThread.unusedWorkers.length <= loaderHelpers.config.pthreadPoolSize) { - const worker = allocateUnusedWorker(); - modulePThread.loadWasmModuleToWorker(worker); - } - - for (let i = 0; i < modulePThread.unusedWorkers.length; i++) { - const worker = modulePThread.unusedWorkers[i]; - if (worker.loaded) { - modulePThread.unusedWorkers.splice(i, 1); - availableThreadCount--; - return worker; - } - } - mono_log_warn(`Failed to find loaded WebWorker, this may deadlock. Please increase the pthreadPoolSize. Running threads ${modulePThread.runningWorkers.length}. Loading workers: ${modulePThread.unusedWorkers.length}`); - availableThreadCount--; // negative value - return modulePThread.unusedWorkers.pop()!; -} - -/// We replace Module["PThreads"].allocateUnusedWorker with this version that knows about assets -function allocateUnusedWorker(): PThreadWorker { - if (!WasmEnableThreads) return null as any; - - const asset = loaderHelpers.resolve_single_asset_path("js-module-threads"); - const uri = asset.resolvedUrl; - mono_assert(uri !== undefined, "could not resolve the uri for the js-module-threads asset"); - const worker = new Worker(uri) as PThreadWorker; - getUnusedWorkerPool().push(worker); - worker.loaded = false; - worker.info = { - pthreadId: PThreadPtrNull, - reuseCount: 0, - updateCount: 0, - threadPrefix: " - ", - threadName: "emscripten-pool", - }; - return worker; -} - - diff --git a/src/mono/browser/runtime/pthreads/shared/tsconfig.json b/src/mono/browser/runtime/pthreads/shared/tsconfig.json deleted file mode 100644 index 8986477dd8fc35..00000000000000 --- a/src/mono/browser/runtime/pthreads/shared/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.shared.json", - "include": [ - "../../**/*.ts", - "../../**/*.d.ts" - ] - -} diff --git a/src/mono/browser/runtime/pthreads/shared/types.ts b/src/mono/browser/runtime/pthreads/shared/types.ts deleted file mode 100644 index 1ea8e0c65e5796..00000000000000 --- a/src/mono/browser/runtime/pthreads/shared/types.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -import type { WorkerToMainMessageType } from "../../types/internal"; - -/// pthread_t in C -export type PThreadPtr = { - __brand: "PThreadPtr" -} -export const PThreadPtrNull: PThreadPtr = 0; - -export interface PThreadInfo { - pthreadId: PThreadPtr; - - reuseCount: number, - updateCount: number, - - threadName: string, - threadPrefix: string, - - isLoaded?: boolean, - isRegistered?: boolean, - isRunning?: boolean, - isAttached?: boolean, - isExternalEventLoop?: boolean, - isUI?: boolean; - isBackground?: boolean, - isDebugger?: boolean, - isThreadPoolWorker?: boolean, - isTimer?: boolean, - isLongRunning?: boolean, - isThreadPoolGate?: boolean, - isFinalizer?: boolean, - isDirtyBecauseOfInterop?: boolean, -} - -/// Messages sent from the main thread using Worker.postMessage or from the worker using DedicatedWorkerGlobalScope.postMessage -/// should use this interface. The message event is also used by emscripten internals (and possibly by 3rd party libraries targeting Emscripten). -/// We should just use this to establish a dedicated MessagePort for Mono's uses. -export interface MonoWorkerToMainMessage { - monoCmd: WorkerToMainMessageType; - info: PThreadInfo; - port?: MessagePort; -} diff --git a/src/mono/browser/runtime/pthreads/ui-thread.ts b/src/mono/browser/runtime/pthreads/ui-thread.ts new file mode 100644 index 00000000000000..878ee38eadbb9a --- /dev/null +++ b/src/mono/browser/runtime/pthreads/ui-thread.ts @@ -0,0 +1,351 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import WasmEnableThreads from "consts:wasmEnableThreads"; +import BuildConfiguration from "consts:configuration"; + +import { } from "../globals"; +import { mono_log_warn } from "../logging"; +import { MonoWorkerToMainMessage, monoThreadInfo, mono_wasm_pthread_ptr, update_thread_info, worker_empty_prefix } from "./shared"; +import { Module, ENVIRONMENT_IS_WORKER, createPromiseController, loaderHelpers, mono_assert, runtimeHelpers } from "../globals"; +import { PThreadLibrary, MainToWorkerMessageType, MonoThreadMessage, PThreadInfo, PThreadPtr, PThreadPtrNull, PThreadWorker, PromiseAndController, PromiseController, Thread, WorkerToMainMessageType, monoMessageSymbol } from "../types/internal"; +import { mono_log_error, mono_log_info } from "../logging"; +import { threads_c_functions as cwraps } from "../cwraps"; + +const threadPromises: Map[]> = new Map(); + +class ThreadImpl implements Thread { + constructor(readonly pthreadPtr: PThreadPtr, readonly worker: Worker, readonly port: MessagePort) { } + postMessageToWorker(message: T): void { + this.port.postMessage(message); + } +} + +/// wait until the thread with the given id has set up a message port to the runtime +export function waitForThread(pthreadPtr: PThreadPtr): Promise { + if (!WasmEnableThreads) return null as any; + mono_assert(!ENVIRONMENT_IS_WORKER, "waitForThread should only be called from the UI thread"); + const worker = getWorker(pthreadPtr); + if (worker?.thread) { + return Promise.resolve(worker?.thread); + } + const promiseAndController = createPromiseController(); + const arr = threadPromises.get(pthreadPtr); + if (arr === undefined) { + threadPromises.set(pthreadPtr, [promiseAndController.promise_control]); + } else { + arr.push(promiseAndController.promise_control); + } + return promiseAndController.promise; +} + +export function resolveThreadPromises(pthreadPtr: PThreadPtr, thread?: Thread): void { + if (!WasmEnableThreads) return; + const arr = threadPromises.get(pthreadPtr); + if (arr !== undefined) { + arr.forEach((controller) => { + if (thread) { + controller.resolve(thread); + } else { + controller.reject(); + } + }); + threadPromises.delete(pthreadPtr); + } +} + +// handler that runs in the main thread when a message is received from a pthread worker +function monoWorkerMessageHandler(worker: PThreadWorker, ev: MessageEvent): void { + if (!WasmEnableThreads) return; + let pthreadId: PThreadPtr; + // this is emscripten message + if (ev.data.cmd === "killThread") { + pthreadId = ev.data["thread"]; + mono_assert(pthreadId == worker.info.pthreadId, "expected pthreadId to match"); + worker.info.isRunning = false; + worker.info.pthreadId = PThreadPtrNull; + return; + } + + const message = ev.data[monoMessageSymbol] as MonoWorkerToMainMessage; + if (message === undefined) { + /// N.B. important to ignore messages we don't recognize - Emscripten uses the message event to send internal messages + return; + } + + let port: MessagePort; + let thread: Thread; + pthreadId = message.info?.pthreadId ?? 0; + worker.info = Object.assign({}, worker.info, message.info); + switch (message.monoCmd) { + case WorkerToMainMessageType.preload: + // this one shot port from setupPreloadChannelToMainThread + port = message.port!; + port.postMessage({ + type: "pthread", + cmd: MainToWorkerMessageType.applyConfig, + config: JSON.stringify(runtimeHelpers.config), + monoThreadInfo: JSON.stringify(worker.info), + }); + port.close(); + break; + case WorkerToMainMessageType.pthreadCreated: + port = message.port!; + thread = new ThreadImpl(pthreadId, worker, port); + worker.thread = thread; + worker.info.isRunning = true; + resolveThreadPromises(pthreadId, thread); + break; + case WorkerToMainMessageType.monoRegistered: + case WorkerToMainMessageType.monoAttached: + case WorkerToMainMessageType.enabledInterop: + case WorkerToMainMessageType.monoUnRegistered: + case WorkerToMainMessageType.updateInfo: + // just worker.info updates above + break; + default: + throw new Error(`Unhandled message from worker: ${message.monoCmd}`); + } +} + +let pendingWorkerLoad: PromiseAndController | undefined; + +/// Called by Emscripten internals on the browser thread when a new pthread worker is created and added to the pthread worker pool. +/// At this point the worker doesn't have any pthread assigned to it, yet. +export function onWorkerLoadInitiated(worker: PThreadWorker, loaded: Promise): void { + if (!WasmEnableThreads) return; + worker.addEventListener("message", (ev) => monoWorkerMessageHandler(worker, ev)); + if (pendingWorkerLoad == undefined) { + pendingWorkerLoad = createPromiseController(); + } + loaded.then(() => { + worker.info.isLoaded = true; + if (pendingWorkerLoad != undefined) { + pendingWorkerLoad.promise_control.resolve(); + pendingWorkerLoad = undefined; + } + }); +} + +export function thread_available(): Promise { + if (!WasmEnableThreads) return null as any; + if (pendingWorkerLoad == undefined) { + return Promise.resolve(); + } + return pendingWorkerLoad.promise; +} + +export function populateEmscriptenPool(): void { + if (!WasmEnableThreads) return; + const unused = getUnusedWorkerPool(); + for (const worker of loaderHelpers.loadingWorkers) { + unused.push(worker); + } + loaderHelpers.loadingWorkers = []; +} + +export async function mono_wasm_init_threads() { + if (!WasmEnableThreads) return; + + // setup the UI thread + monoThreadInfo.pthreadId = mono_wasm_pthread_ptr(); + monoThreadInfo.threadName = "UI Thread"; + monoThreadInfo.isUI = true; + monoThreadInfo.isRunning = true; + update_thread_info(); + + // wait until all workers in the pool are loaded - ready to be used as pthread synchronously + const workers = getUnusedWorkerPool(); + if (workers.length > 0) { + const promises = workers.map(loadWasmModuleToWorker); + await Promise.all(promises); + } +} + +// when we create threads with browser event loop, it's not able to be joined by mono's thread join during shutdown and blocks process exit +export function cancelThreads() { + if (!WasmEnableThreads) return; + const workers: PThreadWorker[] = getRunningWorkers(); + for (const worker of workers) { + if (worker.info.isExternalEventLoop) { + worker.postMessage({ cmd: "cancel" }); + } + } +} + +export function dumpThreads(): void { + if (!WasmEnableThreads) return; + mono_log_info("Dumping web worker info as seen by UI thread, it could be stale: "); + const emptyInfo: PThreadInfo = { + workerNumber: -1, + pthreadId: PThreadPtrNull, + threadPrefix: worker_empty_prefix, + threadName: "????", + isRunning: false, + isAttached: false, + isExternalEventLoop: false, + reuseCount: 0, + updateCount: 0, + }; + const threadInfos: PThreadInfo[] = [ + Object.assign({}, emptyInfo, monoThreadInfo), // UI thread + ]; + for (const worker of getRunningWorkers()) { + threadInfos.push(Object.assign({}, emptyInfo, worker.info)); + } + for (const worker of getUnusedWorkerPool()) { + threadInfos.push(Object.assign({}, emptyInfo, worker.info)); + } + threadInfos.forEach((info) => { + const idx = info.workerNumber.toString().padStart(3, "0"); + const isRunning = (info.isRunning + "").padStart(5, " "); + const isAttached = (info.isAttached + "").padStart(5, " "); + const isEventLoop = (info.isExternalEventLoop + "").padStart(5, " "); + const reuseCount = (info.reuseCount + "").padStart(3, " "); + // eslint-disable-next-line no-console + console.info(`${idx} | ${info.threadPrefix}: isRunning:${isRunning} isAttached:${isAttached} isEventLoop:${isEventLoop} reuseCount:${reuseCount} - ${info.threadName}`); + }); +} + +export function init_finalizer_thread() { + // we don't need it immediately, so we can wait a bit, to keep CPU working on normal startup + setTimeout(() => { + try { + cwraps.mono_wasm_init_finalizer_thread(); + } + catch (err) { + mono_log_error("init_finalizer_thread() failed", err); + loaderHelpers.mono_exit(1, err); + } + }, loaderHelpers.config.finalizerThreadStartDelayMs); +} + +export function replaceEmscriptenPThreadUI(modulePThread: PThreadLibrary): void { + if (!WasmEnableThreads) return; + + const originalLoadWasmModuleToWorker = modulePThread.loadWasmModuleToWorker; + const originalReturnWorkerToPool = modulePThread.returnWorkerToPool; + + modulePThread.loadWasmModuleToWorker = (worker: PThreadWorker): Promise => { + const afterLoaded = originalLoadWasmModuleToWorker(worker); + afterLoaded.then(() => { + availableThreadCount++; + }); + onWorkerLoadInitiated(worker, afterLoaded); + if (loaderHelpers.config.exitOnUnhandledError) { + worker.onerror = (e) => { + loaderHelpers.mono_exit(1, e); + }; + } + return afterLoaded; + }; + modulePThread.allocateUnusedWorker = allocateUnusedWorker; + modulePThread.getNewWorker = () => getNewWorker(modulePThread); + modulePThread.returnWorkerToPool = (worker: PThreadWorker) => { + // when JS interop is installed on JSWebWorker + // we can't reuse the worker, because user code could leave the worker JS globals in a dirty state + worker.info.isRunning = false; + resolveThreadPromises(worker.pthread_ptr, undefined); + worker.info.pthreadId = PThreadPtrNull; + if (worker.thread?.port) { + worker.thread.port.close(); + } + worker.thread = undefined; + if (worker.info && worker.info.isDirtyBecauseOfInterop) { + // we are on UI thread, invoke the handler directly to destroy the dirty worker + worker.onmessage!(new MessageEvent("message", { + data: { + "cmd": "killThread", + thread: worker.pthread_ptr + } + })); + } else { + availableThreadCount++; + originalReturnWorkerToPool(worker); + } + }; + if (BuildConfiguration === "Debug") { + (globalThis as any).dumpThreads = dumpThreads; + (globalThis as any).getModulePThread = getModulePThread; + } +} + +let availableThreadCount = 0; +export function is_thread_available() { + if (!WasmEnableThreads) return true; + return availableThreadCount > 0; +} + +function getNewWorker(modulePThread: PThreadLibrary): PThreadWorker { + if (!WasmEnableThreads) return null as any; + + if (modulePThread.unusedWorkers.length == 0) { + mono_log_warn(`Failed to find unused WebWorker, this may deadlock. Please increase the pthreadPoolReady. Running threads ${modulePThread.runningWorkers.length}. Loading workers: ${modulePThread.unusedWorkers.length}`); + const worker = allocateUnusedWorker(); + modulePThread.loadWasmModuleToWorker(worker); + availableThreadCount--; + return worker; + } + + // keep them pre-allocated all the time, not just during startup + if (modulePThread.unusedWorkers.length <= loaderHelpers.config.pthreadPoolUnusedSize!) { + const worker = allocateUnusedWorker(); + modulePThread.loadWasmModuleToWorker(worker); + } + + for (let i = 0; i < modulePThread.unusedWorkers.length; i++) { + const worker = modulePThread.unusedWorkers[i]; + if (worker.loaded) { + modulePThread.unusedWorkers.splice(i, 1); + availableThreadCount--; + return worker; + } + } + mono_log_warn(`Failed to find loaded WebWorker, this may deadlock. Please increase the pthreadPoolReady. Running threads ${modulePThread.runningWorkers.length}. Loading workers: ${modulePThread.unusedWorkers.length}`); + availableThreadCount--; // negative value + return modulePThread.unusedWorkers.pop()!; +} + +/// We replace Module["PThreads"].allocateUnusedWorker with this version that knows about assets +function allocateUnusedWorker(): PThreadWorker { + if (!WasmEnableThreads) return null as any; + + const asset = loaderHelpers.resolve_single_asset_path("js-module-threads"); + const uri = asset.resolvedUrl; + mono_assert(uri !== undefined, "could not resolve the uri for the js-module-threads asset"); + const workerNumber = loaderHelpers.workerNextNumber++; + const worker = new Worker(uri, { + name: "dotnet-worker-" + workerNumber.toString().padStart(3, "0"), + }) as PThreadWorker; + getUnusedWorkerPool().push(worker); + worker.loaded = false; + worker.info = { + workerNumber, + pthreadId: PThreadPtrNull, + reuseCount: 0, + updateCount: 0, + threadPrefix: worker_empty_prefix, + threadName: "emscripten-pool", + }; + return worker; +} + +export function getWorker(pthreadPtr: PThreadPtr): PThreadWorker | undefined { + return getModulePThread().pthreads[pthreadPtr as any]; +} + +export function getUnusedWorkerPool(): PThreadWorker[] { + return getModulePThread().unusedWorkers; +} + +export function getRunningWorkers(): PThreadWorker[] { + return getModulePThread().runningWorkers; +} + +export function loadWasmModuleToWorker(worker: PThreadWorker): Promise { + return getModulePThread().loadWasmModuleToWorker(worker); +} + +export function getModulePThread(): PThreadLibrary { + return (Module).PThread as PThreadLibrary; +} diff --git a/src/mono/browser/runtime/pthreads/shared/eventloop.ts b/src/mono/browser/runtime/pthreads/worker-eventloop.ts similarity index 99% rename from src/mono/browser/runtime/pthreads/shared/eventloop.ts rename to src/mono/browser/runtime/pthreads/worker-eventloop.ts index 602c3fb221fbd1..7b8a42b05ac660 100644 --- a/src/mono/browser/runtime/pthreads/shared/eventloop.ts +++ b/src/mono/browser/runtime/pthreads/worker-eventloop.ts @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - let perThreadUnsettledPromiseCount = 0; export function addUnsettledPromise() { diff --git a/src/mono/browser/runtime/pthreads/worker/events.ts b/src/mono/browser/runtime/pthreads/worker-events.ts similarity index 98% rename from src/mono/browser/runtime/pthreads/worker/events.ts rename to src/mono/browser/runtime/pthreads/worker-events.ts index ace256459d4393..709e1980d82f58 100644 --- a/src/mono/browser/runtime/pthreads/worker/events.ts +++ b/src/mono/browser/runtime/pthreads/worker-events.ts @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. import WasmEnableThreads from "consts:wasmEnableThreads"; -import { PThreadSelf } from "./index"; +import { PThreadSelf } from "./shared"; export const dotnetPthreadCreated = "dotnet:pthread:created" as const; export const dotnetPthreadAttached = "dotnet:pthread:attached" as const; diff --git a/src/mono/browser/runtime/pthreads/worker/index.ts b/src/mono/browser/runtime/pthreads/worker-thread.ts similarity index 83% rename from src/mono/browser/runtime/pthreads/worker/index.ts rename to src/mono/browser/runtime/pthreads/worker-thread.ts index 29229c23e5fd8a..083f87d7de025e 100644 --- a/src/mono/browser/runtime/pthreads/worker/index.ts +++ b/src/mono/browser/runtime/pthreads/worker-thread.ts @@ -5,22 +5,22 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; -import { ENVIRONMENT_IS_PTHREAD, loaderHelpers, mono_assert } from "../../globals"; -import { mono_wasm_pthread_ptr, postMessageToMain, update_thread_info } from "../shared"; -import { PThreadInfo, PThreadPtr, PThreadPtrNull } from "../shared/types"; -import { WorkerToMainMessageType, is_nullish } from "../../types/internal"; -import { MonoThreadMessage } from "../shared"; +import { Module } from "../globals"; + +import { ENVIRONMENT_IS_PTHREAD, loaderHelpers, mono_assert, runtimeHelpers } from "../globals"; +import { PThreadSelf, monoThreadInfo, mono_wasm_pthread_ptr, postMessageToMain, update_thread_info } from "./shared"; +import { PThreadLibrary, MonoThreadMessage, PThreadInfo, PThreadPtr, WorkerToMainMessageType, is_nullish } from "../types/internal"; import { makeWorkerThreadEvent, dotnetPthreadCreated, dotnetPthreadAttached, WorkerThreadEventTarget -} from "./events"; -import { postRunWorker, preRunWorker } from "../../startup"; -import { mono_log_debug, mono_log_error } from "../../logging"; -import { CharPtr } from "../../types/emscripten"; -import { utf8ToString } from "../../strings"; -import { forceThreadMemoryViewRefresh } from "../../memory"; +} from "./worker-events"; +import { postRunWorker, preRunWorker } from "../startup"; +import { mono_log_debug, mono_log_error } from "../logging"; +import { CharPtr } from "../types/emscripten"; +import { utf8ToString } from "../strings"; +import { forceThreadMemoryViewRefresh } from "../memory"; // re-export some of the events types export { @@ -29,15 +29,9 @@ export { dotnetPthreadCreated, WorkerThreadEvent, WorkerThreadEventTarget, -} from "./events"; - -/// Identification of the current thread executing on a worker -export interface PThreadSelf { - info: PThreadInfo; - portToBrowser: MessagePort; - postMessageToBrowser: (message: T, transfer?: Transferable[]) => void; - addEventListenerFromBrowser: (listener: (event: MessageEvent) => void) => void; -} +} from "./worker-events"; + +export let pthread_self: PThreadSelf = null as any as PThreadSelf; class WorkerSelf implements PThreadSelf { constructor(public info: PThreadInfo, public portToBrowser: MessagePort) { @@ -55,17 +49,6 @@ class WorkerSelf implements PThreadSelf { } } -// we are lying that this is never null, but afterThreadInit should be the first time we get to run any code -// in the worker, so this becomes non-null very early. -export let pthread_self: PThreadSelf = null as any as PThreadSelf; -export const monoThreadInfo: PThreadInfo = { - pthreadId: PThreadPtrNull, - reuseCount: 0, - updateCount: 0, - threadPrefix: " - ", - threadName: "emscripten-loaded", -}; - /// This is the "public internal" API for runtime subsystems that wish to be notified about /// pthreads that are running on the current worker. /// Example: @@ -78,6 +61,7 @@ export let currentWorkerThreadEvents: WorkerThreadEventTarget = undefined as any export function initWorkerThreadEvents() { // treeshake if threads are disabled currentWorkerThreadEvents = WasmEnableThreads ? new globalThis.EventTarget() : null as any as WorkerThreadEventTarget; + Object.assign(monoThreadInfo, runtimeHelpers.monoThreadInfo); } // this is the message handler for the worker that receives messages from the main thread @@ -86,7 +70,7 @@ function monoDedicatedChannelMessageFromMainToWorker(event: MessageEvent mono_log_debug("got message from main on the dedicated channel", event.data); } -export function onRunMessage(pthread_ptr: PThreadPtr) { +export function on_emscripten_thread_init(pthread_ptr: PThreadPtr) { monoThreadInfo.pthreadId = pthread_ptr; forceThreadMemoryViewRefresh(); } @@ -221,3 +205,19 @@ export function mono_wasm_pthread_on_pthread_unregistered(pthread_id: PThreadPtr throw err; } } + +export function replaceEmscriptenPThreadWorker(modulePThread: PThreadLibrary): void { + if (!WasmEnableThreads) return; + + const originalThreadInitTLS = modulePThread.threadInitTLS; + const original_emscripten_thread_init = (Module as any)["__emscripten_thread_init"]; + + (Module as any)["__emscripten_thread_init"] = (pthread_ptr: PThreadPtr, isMainBrowserThread: number, isMainRuntimeThread: number, canBlock: number) => { + on_emscripten_thread_init(pthread_ptr); + original_emscripten_thread_init(pthread_ptr, isMainBrowserThread, isMainRuntimeThread, canBlock); + }; + modulePThread.threadInitTLS = (): void => { + originalThreadInitTLS(); + mono_wasm_pthread_on_pthread_created(); + }; +} \ No newline at end of file diff --git a/src/mono/browser/runtime/pthreads/worker/tsconfig.json b/src/mono/browser/runtime/pthreads/worker/tsconfig.json deleted file mode 100644 index 071a4d824c62a4..00000000000000 --- a/src/mono/browser/runtime/pthreads/worker/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.worker.json", - "include": [ - "../../**/*.ts", - "../../**/*.d.ts" - ] -} diff --git a/src/mono/browser/runtime/run.ts b/src/mono/browser/runtime/run.ts index 8760aab6514cad..e35c6dc833aecd 100644 --- a/src/mono/browser/runtime/run.ts +++ b/src/mono/browser/runtime/run.ts @@ -8,7 +8,7 @@ import { mono_wasm_wait_for_debugger } from "./debug"; import { mono_wasm_set_main_args } from "./startup"; import cwraps from "./cwraps"; import { mono_log_info } from "./logging"; -import { cancelThreads } from "./pthreads/browser"; +import { cancelThreads } from "./pthreads"; import { call_entry_point } from "./managed-exports"; /** diff --git a/src/mono/browser/runtime/scheduling.ts b/src/mono/browser/runtime/scheduling.ts index 4b01536b735ee9..c9cbf205d42d41 100644 --- a/src/mono/browser/runtime/scheduling.ts +++ b/src/mono/browser/runtime/scheduling.ts @@ -5,8 +5,8 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import cwraps from "./cwraps"; import { ENVIRONMENT_IS_WORKER, Module, loaderHelpers } from "./globals"; -import { is_thread_available } from "./pthreads/shared/emscripten-replacements"; import { forceThreadMemoryViewRefresh } from "./memory"; +import { is_thread_available } from "./pthreads"; let spread_timers_maximum = 0; let pump_count = 0; diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index 127d65dea4e818..9759e879254b08 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -5,7 +5,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { DotnetModuleInternal, CharPtrNull } from "./types/internal"; import { ENVIRONMENT_IS_NODE, exportedRuntimeAPI, INTERNAL, loaderHelpers, Module, runtimeHelpers, createPromiseController, mono_assert, ENVIRONMENT_IS_WORKER } from "./globals"; -import cwraps, { init_c_exports, threads_c_functions as tcwraps } from "./cwraps"; +import cwraps, { init_c_exports } from "./cwraps"; import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug"; import { toBase64StringImpl } from "./base64"; import { mono_wasm_init_aot_profiler, mono_wasm_init_browser_profiler } from "./profiler"; @@ -23,14 +23,15 @@ import { interp_pgo_load_data, interp_pgo_save_data } from "./interp-pgo"; import { mono_log_debug, mono_log_error, mono_log_warn } from "./logging"; // threads -import { preAllocatePThreadWorkerPool, mono_wasm_init_threads } from "./pthreads/browser"; -import { currentWorkerThreadEvents, dotnetPthreadCreated, initWorkerThreadEvents, monoThreadInfo } from "./pthreads/worker"; -import { mono_wasm_pthread_ptr, update_thread_info } from "./pthreads/shared"; +import { populateEmscriptenPool, mono_wasm_init_threads, init_finalizer_thread } from "./pthreads"; +import { currentWorkerThreadEvents, dotnetPthreadCreated, initWorkerThreadEvents, monoThreadInfo } from "./pthreads"; +import { mono_wasm_pthread_ptr, update_thread_info } from "./pthreads"; import { jiterpreter_allocate_tables } from "./jiterpreter-support"; import { localHeapViewU8 } from "./memory"; import { assertNoProxies } from "./gc-handles"; import { runtimeList } from "./exports"; import { nativeAbort, nativeExit } from "./run"; +import { mono_wasm_init_diagnostics } from "./diagnostics"; export async function configureRuntimeStartup(): Promise { await init_polyfills_async(); @@ -268,7 +269,7 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { Module.runtimeKeepalivePush(); // load runtime and apply environment settings (if necessary) - start_runtime(); + await start_runtime(); if (runtimeHelpers.config.interpreterPgo) { await interp_pgo_load_data(); @@ -326,10 +327,6 @@ async function postRunAsync(userpostRun: (() => void)[]) { Module["FS_createPath"]("/", "usr", true, true); Module["FS_createPath"]("/", "usr/share", true, true); - if (WasmEnableThreads) { - tcwraps.mono_wasm_init_finalizer_thread(); - } - // all user Module.postRun callbacks userpostRun.map(fn => fn()); endMeasure(mark, MeasuredBlock.postRun); @@ -396,7 +393,7 @@ async function mono_wasm_pre_init_essential_async(): Promise { Module.addRunDependency("mono_wasm_pre_init_essential_async"); if (WasmEnableThreads) { - preAllocatePThreadWorkerPool(runtimeHelpers.config.pthreadPoolSize!); + populateEmscriptenPool(); } Module.removeRunDependency("mono_wasm_pre_init_essential_async"); @@ -483,7 +480,7 @@ async function ensureUsedWasmFeatures() { } } -export function start_runtime() { +export async function start_runtime() { try { const mark = startMeasure(); mono_log_debug("Initializing mono runtime"); @@ -503,6 +500,11 @@ export function start_runtime() { if (runtimeHelpers.config.browserProfilerOptions) mono_wasm_init_browser_profiler(runtimeHelpers.config.browserProfilerOptions); + if (WasmEnableThreads) { + // this is not mono-attached thread, so we can start it earlier + await mono_wasm_init_diagnostics(); + } + mono_wasm_load_runtime(); jiterpreter_allocate_tables(); @@ -514,10 +516,14 @@ export function start_runtime() { if (WasmEnableThreads) { monoThreadInfo.isAttached = true; monoThreadInfo.isRegistered = true; + monoThreadInfo.pthreadId = runtimeHelpers.managedThreadTID = mono_wasm_pthread_ptr(); + monoThreadInfo.workerNumber = 0; update_thread_info(); runtimeHelpers.proxyGCHandle = install_main_synchronization_context(); - runtimeHelpers.managedThreadTID = mono_wasm_pthread_ptr(); runtimeHelpers.isCurrentThread = true; + + // start finalizer thread, lazy + init_finalizer_thread(); } // get GCHandle of the ctx diff --git a/src/mono/browser/runtime/types/index.ts b/src/mono/browser/runtime/types/index.ts index e0f76ea3a546e8..8d9c8a28ba1480 100644 --- a/src/mono/browser/runtime/types/index.ts +++ b/src/mono/browser/runtime/types/index.ts @@ -143,7 +143,15 @@ export type MonoConfig = { /** * initial number of workers to add to the emscripten pthread pool */ - pthreadPoolSize?: number, + pthreadPoolInitialSize?: number, + /** + * number of unused workers kept in the emscripten pthread pool after startup + */ + pthreadPoolUnusedSize?: number, + /** + * Delay in milliseconds before starting the finalizer thread + */ + finalizerThreadStartDelayMs?: number, /** * If true, a list of the methods optimized by the interpreter will be saved and used for faster startup * on future runs of the application diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index 3f021a50a01ce6..ae4ce1ff4813a9 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. import type { AssetEntry, DotnetModuleConfig, LoadBootResourceCallback, LoadingResource, MonoConfig, RuntimeAPI, SingleAssetBehaviors } from "."; -import type { PThreadLibrary } from "../pthreads/shared/emscripten-internals"; -import { PThreadPtr } from "../pthreads/shared/types"; import type { CharPtr, EmscriptenModule, ManagedPointer, NativePointer, VoidPtr, Int32Ptr } from "./emscripten"; export type GCHandle = { @@ -15,6 +13,9 @@ export type JSHandle = { export type JSFnHandle = { __brand: "JSFnHandle" } +export type PThreadPtr = { + __brand: "PThreadPtr" // like pthread_t in C +} export interface MonoObject extends ManagedPointer { __brandMonoObject: "MonoObject" } @@ -61,6 +62,7 @@ export const GCHandleInvalid: GCHandle = -1; export const VoidPtrNull: VoidPtr = 0; export const CharPtrNull: CharPtr = 0; export const NativePointerNull: NativePointer = 0; +export const PThreadPtrNull: PThreadPtr = 0; export function coerceNull(ptr: T | null | undefined): T { if ((ptr === null) || (ptr === undefined)) @@ -129,6 +131,8 @@ export type LoaderHelpers = { scriptUrl: string modulesUniqueQuery?: string preferredIcuAsset?: string | null, + loadingWorkers: PThreadWorker[], + workerNextNumber: number, actual_downloaded_assets_count: number, actual_instantiated_assets_count: number, @@ -200,6 +204,7 @@ export type RuntimeHelpers = { getMemory(): WebAssembly.Memory, getWasmIndirectFunctionTable(): WebAssembly.Table, runtimeReady: boolean, + monoThreadInfo: PThreadInfo, proxyGCHandle: GCHandle | undefined, managedThreadTID: PThreadPtr, isCurrentThread: boolean, @@ -488,3 +493,65 @@ export const enum WorkerToMainMessageType { export const enum MainToWorkerMessageType { applyConfig = "apply_mono_config", } + +export interface PThreadWorker extends Worker { + pthread_ptr: PThreadPtr; + loaded: boolean; + // this info is updated via async messages from the worker, it could be stale + info: PThreadInfo; + thread?: Thread; +} + +export interface PThreadInfo { + pthreadId: PThreadPtr; + + workerNumber: number, + reuseCount: number, + updateCount: number, + + threadName: string, + threadPrefix: string, + + isLoaded?: boolean, + isRegistered?: boolean, + isRunning?: boolean, + isAttached?: boolean, + isExternalEventLoop?: boolean, + isUI?: boolean; + isBackground?: boolean, + isDebugger?: boolean, + isThreadPoolWorker?: boolean, + isTimer?: boolean, + isLongRunning?: boolean, + isThreadPoolGate?: boolean, + isFinalizer?: boolean, + isDirtyBecauseOfInterop?: boolean, +} + +export interface PThreadLibrary { + unusedWorkers: PThreadWorker[]; + runningWorkers: PThreadWorker[]; + pthreads: PThreadInfoMap; + allocateUnusedWorker: () => void; + loadWasmModuleToWorker: (worker: PThreadWorker) => Promise; + threadInitTLS: () => void, + getNewWorker: () => PThreadWorker, + returnWorkerToPool: (worker: PThreadWorker) => void, +} + +export interface PThreadInfoMap { + [key: number]: PThreadWorker; +} + +export interface Thread { + readonly pthreadPtr: PThreadPtr; + readonly port: MessagePort; + postMessageToWorker(message: T): void; +} + +export interface MonoThreadMessage { + // Type of message. Generally a subsystem like "diagnostic_server", or "event_pipe", "debugger", etc. + type: string; + // A particular kind of message. For example, "started", "stopped", "stopped_with_error", etc. + cmd: string; +} diff --git a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs index 97bd05110bfbfc..22edc2c68fbe1a 100644 --- a/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs +++ b/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/BootJsonData.cs @@ -108,9 +108,14 @@ public class BootJsonData public object diagnosticTracing { get; set; } /// - /// Gets or sets pthread pool size. + /// Gets or sets pthread pool initial size. /// - public int? pthreadPoolSize { get; set; } + public int? pthreadPoolInitialSize { get; set; } + + /// + /// Gets or sets pthread pool unused size. + /// + public int? pthreadPoolUnusedSize { get; set; } } public class ResourcesData diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs index 48ce5d34d04bbe..7847039163b11a 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs @@ -23,7 +23,8 @@ public class WasmAppBuilder : WasmAppBuilderBaseTask { public ITaskItem[]? RemoteSources { get; set; } public bool IncludeThreadsWorker { get; set; } - public int PThreadPoolSize { get; set; } + public int PThreadPoolInitialSize { get; set; } + public int PThreadPoolUnusedSize { get; set; } public bool UseWebcil { get; set; } public bool WasmIncludeFullIcuData { get; set; } public string? WasmIcuDataFileName { get; set; } @@ -334,13 +335,22 @@ protected override bool ExecuteInternal() var extraConfiguration = new Dictionary(); - if (PThreadPoolSize < -1) + if (PThreadPoolInitialSize < -1) { - throw new LogAsErrorException($"PThreadPoolSize must be -1, 0 or positive, but got {PThreadPoolSize}"); + throw new LogAsErrorException($"PThreadPoolInitialSize must be -1, 0 or positive, but got {PThreadPoolInitialSize}"); } - else if (PThreadPoolSize > -1) + else if (PThreadPoolInitialSize > -1) { - bootConfig.pthreadPoolSize = PThreadPoolSize; + bootConfig.pthreadPoolInitialSize = PThreadPoolInitialSize; + } + + if (PThreadPoolUnusedSize < -1) + { + throw new LogAsErrorException($"PThreadPoolUnusedSize must be -1, 0 or positive, but got {PThreadPoolUnusedSize}"); + } + else if (PThreadPoolUnusedSize > -1) + { + bootConfig.pthreadPoolUnusedSize = PThreadPoolUnusedSize; } foreach (ITaskItem extra in ExtraConfig ?? Enumerable.Empty()) From b3131382f30a1289342956a0111ccb8a4182c183 Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Mon, 19 Feb 2024 15:57:45 +0100 Subject: [PATCH 120/158] Disable Swift interop tests on macOS with Native AOT (#98651) --- src/tests/issues.targets | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 473e82bc3e9697..9efbe150a7cd1c 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1158,6 +1158,10 @@ Dynamic code generation is not supported on this platform + + + https://github.com/dotnet/runtime/issues/93631 + -----------+ -// | | | -// ^ ^ v -// | | | -// block-->asg-->cond1--+-->cond2--+-->helper--+-->remainder -// -// We expect to achieve the following codegen: -// mov rsi, rdx tmp2 = op1 // asgBlock -// test rsi, rsi goto skip if tmp2 == null ? // cond1Block -// je SKIP -// mov rcx, 0x76543210 cns = op2 // cond2Block -// cmp qword ptr [rsi], rcx goto skip if *tmp2 == op2 -// je SKIP -// call CORINFO_HELP_CHKCASTCLASS_SPECIAL tmp2 = helper(cns, tmp2) // helperBlock -// mov rsi, rax -// SKIP: // remainderBlock -// mov rdi, rsi tmp = tmp2 -// tmp has the result. -// -// Note that we can't use `tmp` during the computation of the result: we must create a new temp, -// and only assign `tmp` to the final value. This is because `tmp` may already have been annotated -// via lvaSetClass/lvaUpdateClass as having a known type. This is only true after the full expansion, -// where any other type gets converted to null. If we used `tmp` during the expansion, then it would -// appear to subsequent optimizations that cond2Block (where the type is checked) is unnecessary. -// -bool Compiler::fgExpandQmarkForCastInstOf(BasicBlock* block, Statement* stmt) -{ -#ifdef DEBUG - if (verbose) - { - printf("\nExpanding CastInstOf qmark in " FMT_BB " (before)\n", block->bbNum); - fgDispBasicBlocks(block, block, true); - } -#endif // DEBUG - - bool introducedThrow = false; - GenTree* expr = stmt->GetRootNode(); - - GenTree* dst = nullptr; - GenTreeQmark* qmark = fgGetTopLevelQmark(expr, &dst); - - noway_assert(dst != nullptr); - assert(dst->OperIsLocalStore()); - assert(qmark->gtFlags & GTF_QMARK_CAST_INSTOF); - - // Get cond, true, false exprs for the qmark. - GenTree* condExpr = qmark->gtGetOp1(); - GenTree* trueExpr = qmark->gtGetOp2()->AsColon()->ThenNode(); - GenTree* falseExpr = qmark->gtGetOp2()->AsColon()->ElseNode(); - - // Get cond, true, false exprs for the nested qmark. - GenTree* nestedQmark = falseExpr; - GenTree* cond2Expr; - GenTree* true2Expr; - GenTree* false2Expr; - - unsigned nestedQmarkElseLikelihood = 50; - if (nestedQmark->gtOper == GT_QMARK) - { - cond2Expr = nestedQmark->gtGetOp1(); - true2Expr = nestedQmark->gtGetOp2()->AsColon()->ThenNode(); - false2Expr = nestedQmark->gtGetOp2()->AsColon()->ElseNode(); - nestedQmarkElseLikelihood = nestedQmark->AsQmark()->ElseNodeLikelihood(); - } - else - { - // This is a rare case that arises when we are doing minopts and encounter isinst of null. - // gtFoldExpr was still able to optimize away part of the tree (but not all). - // That means it does not match our pattern. - // - // Rather than write code to handle this case, just fake up some nodes to make it match the common - // case. Synthesize a comparison that is always true, and for the result-on-true, use the - // entire subtree we expected to be the nested question op. - - cond2Expr = gtNewOperNode(GT_EQ, TYP_INT, gtNewIconNode(0, TYP_I_IMPL), gtNewIconNode(0, TYP_I_IMPL)); - true2Expr = nestedQmark; - false2Expr = gtNewIconNode(0, TYP_I_IMPL); - } - assert(false2Expr->OperGet() == trueExpr->OperGet()); - - // Create the chain of blocks. See method header comment. - // The order of blocks after this is the following: - // block ... asgBlock ... cond1Block ... cond2Block ... helperBlock ... remainderBlock - // - // We need to remember flags that exist on 'block' that we want to propagate to 'remainderBlock', - // if they are going to be cleared by fgSplitBlockAfterStatement(). We currently only do this - // for the GC safe point bit, the logic being that if 'block' was marked gcsafe, then surely - // remainderBlock will still be GC safe. - BasicBlockFlags propagateFlags = block->GetFlagsRaw() & BBF_GC_SAFE_POINT; - BasicBlock* remainderBlock = fgSplitBlockAfterStatement(block, stmt); - fgRemoveRefPred(remainderBlock, block); // We're going to put more blocks between block and remainderBlock. - - BasicBlock* helperBlock = fgNewBBafter(BBJ_ALWAYS, block, true, block->Next()); - BasicBlock* cond2Block = fgNewBBafter(BBJ_COND, block, true, remainderBlock); - BasicBlock* cond1Block = fgNewBBafter(BBJ_COND, block, true, remainderBlock); - BasicBlock* asgBlock = fgNewBBafter(BBJ_ALWAYS, block, true, block->Next()); - - block->RemoveFlags(BBF_NEEDS_GCPOLL); - remainderBlock->SetFlags(propagateFlags); - helperBlock->SetFlags(BBF_NONE_QUIRK); - asgBlock->SetFlags(BBF_NONE_QUIRK); - - // These blocks are only internal if 'block' is (but they've been set as internal by fgNewBBafter). - // If they're not internal, mark them as imported to avoid asserts about un-imported blocks. - if (!block->HasFlag(BBF_INTERNAL)) - { - helperBlock->RemoveFlags(BBF_INTERNAL); - cond2Block->RemoveFlags(BBF_INTERNAL); - cond1Block->RemoveFlags(BBF_INTERNAL); - asgBlock->RemoveFlags(BBF_INTERNAL); - helperBlock->SetFlags(BBF_IMPORTED); - cond2Block->SetFlags(BBF_IMPORTED); - cond1Block->SetFlags(BBF_IMPORTED); - asgBlock->SetFlags(BBF_IMPORTED); - } - - // Chain the flow correctly. - assert(block->KindIs(BBJ_ALWAYS)); - block->SetTarget(asgBlock); - fgAddRefPred(asgBlock, block); - fgAddRefPred(cond1Block, asgBlock); - fgAddRefPred(remainderBlock, helperBlock); - - cond1Block->SetFalseTarget(cond2Block); - cond2Block->SetFalseTarget(helperBlock); - fgAddRefPred(cond2Block, cond1Block); - fgAddRefPred(helperBlock, cond2Block); - fgAddRefPred(remainderBlock, cond1Block); - fgAddRefPred(remainderBlock, cond2Block); - - // Set the weights; some are guesses. - asgBlock->inheritWeight(block); - cond1Block->inheritWeight(block); - - // We only have likelihood for the fast path (and fallback), but we don't know - // how often we have null in the root QMARK (although, we might be able to guess it too) - // so leave 50/50 for now. Thus, we have: - // - // [weight 1.0] - // if (obj != null) - // { - // [weight 0.5] - // if (obj.GetType() == typeof(FastType)) - // { - // [weight 0.5 * ] - // } - // else - // { - // [weight 0.5 * <100 - likelihood of FastType>] - // } - // } - // - cond2Block->inheritWeightPercentage(cond1Block, 50); - helperBlock->inheritWeightPercentage(cond2Block, nestedQmarkElseLikelihood); - - // Append cond1 as JTRUE to cond1Block - GenTree* jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, condExpr); - Statement* jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo()); - fgInsertStmtAtEnd(cond1Block, jmpStmt); - - // Append cond2 as JTRUE to cond2Block - jmpTree = gtNewOperNode(GT_JTRUE, TYP_VOID, cond2Expr); - jmpStmt = fgNewStmtFromTree(jmpTree, stmt->GetDebugInfo()); - fgInsertStmtAtEnd(cond2Block, jmpStmt); - - unsigned tmp2 = lvaGrabTemp(false DEBUGARG("CastInstOf QMark result")); - lvaGetDesc(tmp2)->lvType = dst->TypeGet(); - - // AsgBlock should get tmp2 = op1. - GenTree* trueExprStore = gtNewStoreLclVarNode(tmp2, trueExpr)->AsLclVarCommon(); - Statement* trueStmt = fgNewStmtFromTree(trueExprStore, stmt->GetDebugInfo()); - fgInsertStmtAtEnd(asgBlock, trueStmt); - - // Since we are adding helper in the JTRUE false path, reverse the cond2 and add the helper. - gtReverseCond(cond2Expr); - - if (true2Expr->OperIs(GT_CALL) && (true2Expr->AsCall()->gtCallMoreFlags & GTF_CALL_M_DOES_NOT_RETURN)) - { - Statement* helperStmt = fgNewStmtFromTree(true2Expr, stmt->GetDebugInfo()); - fgInsertStmtAtEnd(helperBlock, helperStmt); - fgConvertBBToThrowBB(helperBlock); - setMethodHasNoReturnCalls(); - introducedThrow = true; - } - else - { - GenTree* helperExprStore = gtNewStoreLclVarNode(tmp2, true2Expr)->AsLclVarCommon(); - Statement* helperStmt = fgNewStmtFromTree(helperExprStore, stmt->GetDebugInfo()); - fgInsertStmtAtEnd(helperBlock, helperStmt); - } - - // RemainderBlock should get tmp = tmp2. - GenTree* tmp2CopyLcl = gtNewLclvNode(tmp2, dst->TypeGet()); - unsigned dstLclNum = dst->AsLclVarCommon()->GetLclNum(); - GenTree* resultCopy = - dst->OperIs(GT_STORE_LCL_FLD) - ? gtNewStoreLclFldNode(dstLclNum, dst->TypeGet(), dst->AsLclFld()->GetLclOffs(), tmp2CopyLcl) - : gtNewStoreLclVarNode(dstLclNum, tmp2CopyLcl)->AsLclVarCommon(); - Statement* resultCopyStmt = fgNewStmtFromTree(resultCopy, stmt->GetDebugInfo()); - fgInsertStmtAtBeg(remainderBlock, resultCopyStmt); - - // Finally remove the nested qmark stmt. - fgRemoveStmt(block, stmt); - -#ifdef DEBUG - if (verbose) - { - printf("\nExpanding CastInstOf qmark in " FMT_BB " (after)\n", block->bbNum); - fgDispBasicBlocks(block, remainderBlock, true); - } -#endif // DEBUG - - return introducedThrow; -} - //------------------------------------------------------------------------ // fgExpandQmarkStmt: expand a qmark into control flow // @@ -14813,11 +14578,6 @@ bool Compiler::fgExpandQmarkStmt(BasicBlock* block, Statement* stmt) return false; } - if (qmark->gtFlags & GTF_QMARK_CAST_INSTOF) - { - return fgExpandQmarkForCastInstOf(block, stmt); - } - #ifdef DEBUG if (verbose) { From de7dd43cc426da8df38eb9bc5e22ad8752ac5ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Mon, 19 Feb 2024 23:56:39 +0100 Subject: [PATCH 126/158] Remove FluentAssertions reference from ILCompiler.Trimming.Tests.csproj (#98674) It is not used. --- .../ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj | 2 -- .../TestCasesRunner/AssemblyChecker.cs | 1 - .../ILCompiler.Trimming.Tests/TestCasesRunner/ResultChecker.cs | 1 - 3 files changed, 4 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj index 139818eff2c584..935d178a29ef85 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/ILCompiler.Trimming.Tests.csproj @@ -15,8 +15,6 @@ - - diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs index 8dd1e4cb136732..c312bddba0afa2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Text; -using FluentAssertions; using ILCompiler; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ResultChecker.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ResultChecker.cs index d0ac4085e83c2e..770bc1e97dfa2b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/ResultChecker.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; -using FluentAssertions; using ILCompiler.Logging; using Internal.TypeSystem; using Mono.Cecil; From 922a04f99f414eb2b6f51e421398756e89564ca9 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 20 Feb 2024 00:20:58 +0100 Subject: [PATCH 127/158] Fix bug in arm64 LazyMachState unwind (#98635) * Fix bug in arm64 LazyMachState unwindFix bug in arm64 LazyMachStateunwind When we were enabling the mac M1 couple of years ago, a bug has creeped in that causes a race in lazy machine state unwinding. The LazyMachState::setLazyStateFromUnwind was incorrectly modifying the captured state, so in case multiple threads were racing for the unwind, a thread that came at a time when part of the captured state was modified and before the `_isValid` was set would start unwinding from a wrong state (the partial unwound one), effectively unwinding twice. I've hit this in CI for enabling the new exception handling, as it results in an assert when comparing the resulting address of the floating point registers unwind with the mach state. * Proper fix for macOS arm64 * Add comment on why Apple is different Also unify code for Windows / Linux since the ptrX19_X29 never contain null pointer on those platforms. They are initialized to point to the captureX19_X29. So the conditional code that has the null check doesn't make sense on Linux. --- src/coreclr/vm/arm64/asmconstants.h | 4 +++ src/coreclr/vm/arm64/gmscpu.h | 10 +++++- src/coreclr/vm/arm64/stubs.cpp | 54 ++++++++++++++--------------- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/coreclr/vm/arm64/asmconstants.h b/src/coreclr/vm/arm64/asmconstants.h index b52c164e1ab71f..cc19728d9e29ee 100644 --- a/src/coreclr/vm/arm64/asmconstants.h +++ b/src/coreclr/vm/arm64/asmconstants.h @@ -96,7 +96,11 @@ ASMCONSTANTS_C_ASSERT(MachState__isValid == offsetof(MachState, _isValid)) #define LazyMachState_captureX19_X29 MachState__captureX19_X29 ASMCONSTANTS_C_ASSERT(LazyMachState_captureX19_X29 == offsetof(LazyMachState, captureX19_X29)) +#ifdef __APPLE__ +#define LazyMachState_captureSp (MachState__isValid+8+88) // padding for alignment +#else // __APPLE__ #define LazyMachState_captureSp (MachState__isValid+8) // padding for alignment +#endif // __APPLE ASMCONSTANTS_C_ASSERT(LazyMachState_captureSp == offsetof(LazyMachState, captureSp)) #define LazyMachState_captureIp (LazyMachState_captureSp+8) diff --git a/src/coreclr/vm/arm64/gmscpu.h b/src/coreclr/vm/arm64/gmscpu.h index 887a41b4f07c10..000eed14b4743b 100644 --- a/src/coreclr/vm/arm64/gmscpu.h +++ b/src/coreclr/vm/arm64/gmscpu.h @@ -25,6 +25,11 @@ struct MachState { TADDR _pc; // program counter after the function returns TADDR _sp; // stack pointer after the function returns BOOL _isValid; +#ifdef __APPLE__ + // libunwind on macOS doesn't support context pointers and we cannot modify the captureX19_X29, + // so we store the unwound values in a separate array. + ULONG64 unwoundX19_X29[NUM_NONVOLATILE_CONTEXT_POINTERS]; // preserved registers +#endif // __APPLE__ BOOL isValid() { LIMITED_METHOD_DAC_CONTRACT; return _isValid; } TADDR GetRetAddr() { LIMITED_METHOD_DAC_CONTRACT; return _pc; } @@ -55,6 +60,10 @@ inline void LazyMachState::setLazyStateFromUnwind(MachState* copy) _sp = copy->_sp; _pc = copy->_pc; +#ifdef __APPLE__ + memcpy(unwoundX19_X29, copy->unwoundX19_X29, sizeof(unwoundX19_X29)); +#endif // __APPLE__ + // Capture* has already been set, so there is no need to touch it // loop over the nonvolatile context pointers and make @@ -80,7 +89,6 @@ inline void LazyMachState::setLazyStateFromUnwind(MachState* copy) } *pDst++ = valueSrc; - captureX19_X29[i] = copy->captureX19_X29[i]; } diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index 871a115097b602..9f0c9ae14e7156 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -378,19 +378,19 @@ void LazyMachState::unwindLazyState(LazyMachState* baseState, } } while (true); -#ifdef TARGET_UNIX - unwoundstate->captureX19_X29[0] = context.X19; - unwoundstate->captureX19_X29[1] = context.X20; - unwoundstate->captureX19_X29[2] = context.X21; - unwoundstate->captureX19_X29[3] = context.X22; - unwoundstate->captureX19_X29[4] = context.X23; - unwoundstate->captureX19_X29[5] = context.X24; - unwoundstate->captureX19_X29[6] = context.X25; - unwoundstate->captureX19_X29[7] = context.X26; - unwoundstate->captureX19_X29[8] = context.X27; - unwoundstate->captureX19_X29[9] = context.X28; - unwoundstate->captureX19_X29[10] = context.Fp; -#endif +#ifdef __APPLE__ + unwoundstate->unwoundX19_X29[0] = context.X19; + unwoundstate->unwoundX19_X29[1] = context.X20; + unwoundstate->unwoundX19_X29[2] = context.X21; + unwoundstate->unwoundX19_X29[3] = context.X22; + unwoundstate->unwoundX19_X29[4] = context.X23; + unwoundstate->unwoundX19_X29[5] = context.X24; + unwoundstate->unwoundX19_X29[6] = context.X25; + unwoundstate->unwoundX19_X29[7] = context.X26; + unwoundstate->unwoundX19_X29[8] = context.X27; + unwoundstate->unwoundX19_X29[9] = context.X28; + unwoundstate->unwoundX19_X29[10] = context.Fp; +#endif // __APPLE__ #ifdef DACCESS_COMPILE // For DAC builds, we update the registers directly since we dont have context pointers @@ -505,20 +505,20 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloat pRD->pCurrentContext->Pc = pRD->ControlPC; pRD->pCurrentContext->Sp = pRD->SP; -#ifdef TARGET_UNIX - pRD->pCurrentContext->X19 = m_MachState.ptrX19_X29[0] ? *m_MachState.ptrX19_X29[0] : m_MachState.captureX19_X29[0]; - pRD->pCurrentContext->X20 = m_MachState.ptrX19_X29[1] ? *m_MachState.ptrX19_X29[1] : m_MachState.captureX19_X29[1]; - pRD->pCurrentContext->X21 = m_MachState.ptrX19_X29[2] ? *m_MachState.ptrX19_X29[2] : m_MachState.captureX19_X29[2]; - pRD->pCurrentContext->X22 = m_MachState.ptrX19_X29[3] ? *m_MachState.ptrX19_X29[3] : m_MachState.captureX19_X29[3]; - pRD->pCurrentContext->X23 = m_MachState.ptrX19_X29[4] ? *m_MachState.ptrX19_X29[4] : m_MachState.captureX19_X29[4]; - pRD->pCurrentContext->X24 = m_MachState.ptrX19_X29[5] ? *m_MachState.ptrX19_X29[5] : m_MachState.captureX19_X29[5]; - pRD->pCurrentContext->X25 = m_MachState.ptrX19_X29[6] ? *m_MachState.ptrX19_X29[6] : m_MachState.captureX19_X29[6]; - pRD->pCurrentContext->X26 = m_MachState.ptrX19_X29[7] ? *m_MachState.ptrX19_X29[7] : m_MachState.captureX19_X29[7]; - pRD->pCurrentContext->X27 = m_MachState.ptrX19_X29[8] ? *m_MachState.ptrX19_X29[8] : m_MachState.captureX19_X29[8]; - pRD->pCurrentContext->X28 = m_MachState.ptrX19_X29[9] ? *m_MachState.ptrX19_X29[9] : m_MachState.captureX19_X29[9]; - pRD->pCurrentContext->Fp = m_MachState.ptrX19_X29[10] ? *m_MachState.ptrX19_X29[10] : m_MachState.captureX19_X29[10]; +#ifdef __APPLE__ + pRD->pCurrentContext->X19 = (DWORD64)(m_MachState.unwoundX19_X29[0]); + pRD->pCurrentContext->X20 = (DWORD64)(m_MachState.unwoundX19_X29[1]); + pRD->pCurrentContext->X21 = (DWORD64)(m_MachState.unwoundX19_X29[2]); + pRD->pCurrentContext->X22 = (DWORD64)(m_MachState.unwoundX19_X29[3]); + pRD->pCurrentContext->X23 = (DWORD64)(m_MachState.unwoundX19_X29[4]); + pRD->pCurrentContext->X24 = (DWORD64)(m_MachState.unwoundX19_X29[5]); + pRD->pCurrentContext->X25 = (DWORD64)(m_MachState.unwoundX19_X29[6]); + pRD->pCurrentContext->X26 = (DWORD64)(m_MachState.unwoundX19_X29[7]); + pRD->pCurrentContext->X27 = (DWORD64)(m_MachState.unwoundX19_X29[8]); + pRD->pCurrentContext->X28 = (DWORD64)(m_MachState.unwoundX19_X29[9]); + pRD->pCurrentContext->Fp = (DWORD64)(m_MachState.unwoundX19_X29[10]); pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC -#else // TARGET_UNIX +#else // __APPLE__ pRD->pCurrentContext->X19 = *m_MachState.ptrX19_X29[0]; pRD->pCurrentContext->X20 = *m_MachState.ptrX19_X29[1]; pRD->pCurrentContext->X21 = *m_MachState.ptrX19_X29[2]; @@ -531,7 +531,7 @@ void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloat pRD->pCurrentContext->X28 = *m_MachState.ptrX19_X29[9]; pRD->pCurrentContext->Fp = *m_MachState.ptrX19_X29[10]; pRD->pCurrentContext->Lr = NULL; // Unwind again to get Caller's PC -#endif +#endif // __APPLE__ #if !defined(DACCESS_COMPILE) pRD->pCurrentContextPointers->X19 = m_MachState.ptrX19_X29[0]; From bee4602cd5974c9abb2d83cc58d5342b75b4cdbe Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 20 Feb 2024 03:11:38 +0100 Subject: [PATCH 128/158] ObjWriter: Do not generate relocations within .debug_info for DW_AT_type (#98597) * Do not generate relocations within .debug_info for DW_AT_type * Fix WriteInfoAbsReference --- .../Compiler/ObjectWriter/Dwarf/DwarfInfoWriter.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfoWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfoWriter.cs index cda75df2c8e142..18b2c34a484685 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfoWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfoWriter.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; using ILCompiler.DependencyAnalysis; @@ -106,7 +107,7 @@ public void WriteStringReference(string value) public void WriteInfoAbsReference(long offset) { Debug.Assert(offset < uint.MaxValue); - _infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, ".debug_info", offset); + WriteUInt32((uint)offset); } public void WriteInfoReference(uint typeIndex) @@ -122,7 +123,7 @@ public void WriteInfoReference(uint typeIndex) } else { - _infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, ".debug_info", offset); + WriteUInt32(offset); } } @@ -195,17 +196,10 @@ public void Dispose() // Debug.Assert(_dieStack.Count == 0); // Flush late bound forward references - int streamOffset = (int)_infoSectionWriter.Position; foreach (var lateBoundReference in _lateBoundReferences) { uint offset = _builder.ResolveOffset(lateBoundReference.TypeIndex); - - _infoSectionWriter.EmitRelocation( - - streamOffset + lateBoundReference.Position, - lateBoundReference.Data, - RelocType.IMAGE_REL_BASED_HIGHLOW, - ".debug_info", - (int)offset); + BinaryPrimitives.WriteUInt32LittleEndian(lateBoundReference.Data, offset); } // Write abbreviation section From b8e20d3827757f4813a9e91e9c63d473d91944d2 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Tue, 20 Feb 2024 14:39:21 +0800 Subject: [PATCH 129/158] Improve Half conversion test coverage (#98642) * Fix copy-paste error for neg subnormal-ULP * Add test case for midpoint rounding close to zero --- .../tests/System.Runtime.Tests/System/HalfTests.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs index 600326b39d85f7..77c6917a138b29 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs @@ -522,12 +522,16 @@ public static IEnumerable ExplicitConversion_FromSingle_TestData() BitConverter.UInt16BitsToHalf(0b0_11001_0000000100)), // 1027.5 rounds to even (BitConverter.Int32BitsToSingle(0b0_10001001_00000000110111111111111), BitConverter.UInt16BitsToHalf(0b0_11001_0000000011)), // 1027.5-ULP rounds down + (BitConverter.Int32BitsToSingle(0b0_10001001_00000000101000000000000), + BitConverter.UInt16BitsToHalf(0b0_11001_0000000010)), // 1026.5 rounds to even (BitConverter.Int32BitsToSingle(unchecked((int)0b1_10001001_00000000110111111111111)), BitConverter.UInt16BitsToHalf(0b1_11001_0000000011)), // -1027.5+ULP rounds towards zero (BitConverter.Int32BitsToSingle(unchecked((int)0b1_10001001_00000000111000000000000)), BitConverter.UInt16BitsToHalf(0b1_11001_0000000100)), // -1027.5 rounds to even (BitConverter.Int32BitsToSingle(unchecked((int)0b1_10001001_00000000111000000000001)), BitConverter.UInt16BitsToHalf(0b1_11001_0000000100)), // -1027.5-ULP rounds away from zero + (BitConverter.Int32BitsToSingle(unchecked((int)0b1_10001001_00000000101000000000000)), + BitConverter.UInt16BitsToHalf(0b1_11001_0000000010)), // -1026.5 rounds to even (BitConverter.Int32BitsToSingle(0b0_01110000_00000001110000000000001), BitConverter.UInt16BitsToHalf(0b0_00000_1000000100)), // subnormal + ULP rounds up (BitConverter.Int32BitsToSingle(0b0_01110000_00000001110000000000000), @@ -538,8 +542,8 @@ public static IEnumerable ExplicitConversion_FromSingle_TestData() BitConverter.UInt16BitsToHalf(0b1_00000_1000000011)), // neg subnormal + ULP rounds higher (BitConverter.Int32BitsToSingle(unchecked((int)0b1_01110000_00000001110000000000000)), BitConverter.UInt16BitsToHalf(0b1_00000_1000000100)), // neg subnormal rounds to even - (BitConverter.Int32BitsToSingle(unchecked((int)0b1_01110000_00000001101111111111111)), - BitConverter.UInt16BitsToHalf(0b1_00000_1000000011)), // neg subnormal - ULP rounds lower, + (BitConverter.Int32BitsToSingle(unchecked((int)0b1_01110000_00000001110000000000001)), + BitConverter.UInt16BitsToHalf(0b1_00000_1000000100)), // neg subnormal - ULP rounds lower, (BitConverter.Int32BitsToSingle(0x33000000), BitConverter.UInt16BitsToHalf(0b0_00000_000000000)), // (half-precision minimum subnormal / 2) should underflow to zero }; @@ -616,12 +620,16 @@ public static IEnumerable ExplicitConversion_FromDouble_TestData() BitConverter.UInt16BitsToHalf(0b0_11001_0000000100)), // 1027.5 rounds to even (BitConverter.Int64BitsToDouble(0x40900DFFFFFFFFFF), BitConverter.UInt16BitsToHalf(0b0_11001_0000000011)), // 1027.5-ULP rounds down + (BitConverter.Int64BitsToDouble(0x40900A0000000000), + BitConverter.UInt16BitsToHalf(0b0_11001_0000000010)), // 1026.5 rounds to even (BitConverter.Int64BitsToDouble(unchecked((long)0xC0900DFFFFFFFFFF)), BitConverter.UInt16BitsToHalf(0b1_11001_0000000011)), // -1027.5+ULP rounds towards zero (BitConverter.Int64BitsToDouble(unchecked((long)0xC0900E0000000000)), BitConverter.UInt16BitsToHalf(0b1_11001_0000000100)), // -1027.5 rounds to even (BitConverter.Int64BitsToDouble(unchecked((long)0xC0900E0000000001)), BitConverter.UInt16BitsToHalf(0b1_11001_0000000100)), // -1027.5-ULP rounds away from zero + (BitConverter.Int64BitsToDouble(unchecked((long)0xC0900A0000000000)), + BitConverter.UInt16BitsToHalf(0b1_11001_0000000010)), // -1026.5 rounds to even (BitConverter.Int64BitsToDouble(0x3F001C0000000001), BitConverter.UInt16BitsToHalf(0b0_00000_1000000100)), // subnormal + ULP rounds up (BitConverter.Int64BitsToDouble(0x3F001C0000000001), From 2df467fc3d190b82a656611b2f6ab04bc916d131 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Tue, 20 Feb 2024 07:44:37 +0100 Subject: [PATCH 130/158] [browser][MT] fix GC boundaries (#98678) --- src/mono/browser/runtime/driver.c | 14 ++++++++++++-- src/mono/browser/runtime/runtime.c | 7 ++++--- src/mono/mono/mini/mini-wasm.c | 2 ++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c index d76a8bacd92823..c5135a7e44b346 100644 --- a/src/mono/browser/runtime/driver.c +++ b/src/mono/browser/runtime/driver.c @@ -409,7 +409,9 @@ mono_wasm_init_finalizer_thread (void) { // in the single threaded build, finalizers periodically run on the main thread instead. #ifndef DISABLE_THREADS + MONO_ENTER_GC_UNSAFE; mono_gc_init_finalizer_thread (); + MONO_EXIT_GC_UNSAFE; #endif } @@ -467,11 +469,19 @@ EMSCRIPTEN_KEEPALIVE int mono_wasm_f64_to_i52 (int64_t *destination, double valu // JS is responsible for freeing this EMSCRIPTEN_KEEPALIVE const char * mono_wasm_method_get_full_name (MonoMethod *method) { - return mono_method_get_full_name(method); + const char *res; + MONO_ENTER_GC_UNSAFE; + res = mono_method_get_full_name (method); + MONO_EXIT_GC_UNSAFE; + return res; } EMSCRIPTEN_KEEPALIVE const char * mono_wasm_method_get_name (MonoMethod *method) { - return mono_method_get_name(method); + const char *res; + MONO_ENTER_GC_UNSAFE; + res = mono_method_get_name (method); + MONO_EXIT_GC_UNSAFE; + return res; } EMSCRIPTEN_KEEPALIVE float mono_wasm_get_f32_unaligned (const float *src) { diff --git a/src/mono/browser/runtime/runtime.c b/src/mono/browser/runtime/runtime.c index 9030c4a52ee556..bbd645e21e821c 100644 --- a/src/mono/browser/runtime/runtime.c +++ b/src/mono/browser/runtime/runtime.c @@ -322,13 +322,14 @@ mono_wasm_load_runtime_common (int debug_level, MonoLogCallback log_callback, co EMSCRIPTEN_KEEPALIVE MonoAssembly* mono_wasm_assembly_load (const char *name) { + MonoAssembly *res; assert (name); MonoImageOpenStatus status; + MONO_ENTER_GC_UNSAFE; MonoAssemblyName* aname = mono_assembly_name_new (name); - - MonoAssembly *res = mono_assembly_load (aname, NULL, &status); + res = mono_assembly_load (aname, NULL, &status); mono_assembly_name_free (aname); - + MONO_EXIT_GC_UNSAFE; return res; } diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index af943e24adece4..0a80ab43ba9c47 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -594,7 +594,9 @@ mono_wasm_execute_timer (void) } background_job_cb cb = timer_handler; + MONO_ENTER_GC_UNSAFE; cb (); + MONO_EXIT_GC_UNSAFE; } #ifdef DISABLE_THREADS From 25b1249e4edb3fef50be35df88fcb363d42f8574 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 19 Feb 2024 22:56:51 -0800 Subject: [PATCH 131/158] Delete MemoryHelpers.Memset (#98615) --- .../Runtime/TypeLoader/EETypeCreator.cs | 71 ++++++++----------- .../Runtime/TypeLoader/GenericDictionary.cs | 22 +++--- .../Runtime/TypeLoader/TypeBuilder.cs | 2 +- ...peLoaderEnvironment.LdTokenResultLookup.cs | 38 ++++------ .../TypeSystem/TypeSystemContext.Runtime.cs | 4 +- 5 files changed, 57 insertions(+), 80 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs index 26137a0c61d551..411f41d323372c 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs @@ -104,25 +104,14 @@ public static int AlignUp(int val, int alignment) return result; } - public static unsafe void Memset(IntPtr destination, int length, byte value) + public static unsafe void* AllocateMemory(int cbBytes) { - byte* pbDest = (byte*)destination.ToPointer(); - while (length > 0) - { - *pbDest = value; - pbDest++; - length--; - } + return NativeMemory.Alloc((nuint)cbBytes); } - public static unsafe IntPtr AllocateMemory(int cbBytes) + public static unsafe void FreeMemory(void* memoryPtrToFree) { - return (IntPtr)NativeMemory.Alloc((nuint)cbBytes); - } - - public static unsafe void FreeMemory(IntPtr memoryPtrToFree) - { - NativeMemory.Free((void*)memoryPtrToFree); + NativeMemory.Free(memoryPtrToFree); } } @@ -132,12 +121,12 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo int arity, TypeBuilderState state) { bool successful = false; - IntPtr eeTypePtrPlusGCDesc = IntPtr.Zero; - IntPtr writableDataPtr = IntPtr.Zero; - IntPtr gcStaticData = IntPtr.Zero; - IntPtr nonGcStaticData = IntPtr.Zero; - IntPtr genericComposition = IntPtr.Zero; - IntPtr threadStaticIndex = IntPtr.Zero; + void* eeTypePlusGCDesc = null; + void* writableData = null; + void* nonGcStaticData = null; + void* genericComposition = null; + void* threadStaticIndex = null; + nint gcStaticData = 0; try { @@ -266,10 +255,10 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo int cbGCDescAligned = MemoryHelpers.AlignUp(cbGCDesc, IntPtr.Size); // Allocate enough space for the MethodTable + gcDescSize - eeTypePtrPlusGCDesc = MemoryHelpers.AllocateMemory(cbGCDescAligned + cbEEType + cbOptionalFieldsSize); + eeTypePlusGCDesc = MemoryHelpers.AllocateMemory(cbGCDescAligned + cbEEType + cbOptionalFieldsSize); // Get the MethodTable pointer, and the template MethodTable pointer - pEEType = (MethodTable*)(eeTypePtrPlusGCDesc + cbGCDescAligned); + pEEType = (MethodTable*)((byte*)eeTypePlusGCDesc + cbGCDescAligned); state.HalfBakedRuntimeTypeHandle = pEEType->ToRuntimeTypeHandle(); // Set basic MethodTable fields @@ -319,9 +308,9 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo *((void**)((byte*)pEEType + cbSealedVirtualSlotsTypeOffset)) = pTemplateEEType->GetSealedVirtualTable(); } - writableDataPtr = MemoryHelpers.AllocateMemory(WritableData.GetSize(IntPtr.Size)); - MemoryHelpers.Memset(writableDataPtr, WritableData.GetSize(IntPtr.Size), 0); - pEEType->WritableData = (void*)writableDataPtr; + writableData = MemoryHelpers.AllocateMemory(WritableData.GetSize(IntPtr.Size)); + NativeMemory.Clear(writableData, (nuint)WritableData.GetSize(IntPtr.Size)); + pEEType->WritableData = writableData; pEEType->DynamicTemplateType = pTemplateEEType; @@ -340,13 +329,13 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo if (arity > 1) { genericComposition = MemoryHelpers.AllocateMemory(MethodTable.GetGenericCompositionSize(arity)); - pEEType->SetGenericComposition(genericComposition); + pEEType->SetGenericComposition((IntPtr)genericComposition); } if (allocatedNonGCDataSize > 0) { nonGcStaticData = MemoryHelpers.AllocateMemory(allocatedNonGCDataSize); - MemoryHelpers.Memset(nonGcStaticData, allocatedNonGCDataSize, 0); + NativeMemory.Clear(nonGcStaticData, (nuint)allocatedNonGCDataSize); Debug.Assert(nonGCStaticDataOffset <= allocatedNonGCDataSize); pEEType->DynamicNonGcStaticsData = (IntPtr)((byte*)nonGcStaticData + nonGCStaticDataOffset); } @@ -359,7 +348,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo threadStaticIndex = MemoryHelpers.AllocateMemory(IntPtr.Size * 2); *(IntPtr*)threadStaticIndex = pEEType->PointerToTypeManager; *(((IntPtr*)threadStaticIndex) + 1) = (IntPtr)state.ThreadStaticOffset; - pEEType->DynamicThreadStaticsIndex = threadStaticIndex; + pEEType->DynamicThreadStaticsIndex = (IntPtr)threadStaticIndex; } if (state.GcDataSize != 0) @@ -368,7 +357,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo object obj = RuntimeAugments.RawNewObject(((MethodTable*)state.GcStaticDesc)->ToRuntimeTypeHandle()); gcStaticData = RuntimeAugments.RhHandleAlloc(obj, GCHandleType.Normal); - pEEType->DynamicGcStaticsData = gcStaticData; + pEEType->DynamicGcStaticsData = (IntPtr)gcStaticData; } if (state.Dictionary != null) @@ -383,20 +372,16 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo { if (!successful) { - if (eeTypePtrPlusGCDesc != IntPtr.Zero) - MemoryHelpers.FreeMemory(eeTypePtrPlusGCDesc); - if (state.HalfBakedDictionary != IntPtr.Zero) - MemoryHelpers.FreeMemory(state.HalfBakedDictionary); - if (gcStaticData != IntPtr.Zero) + if (gcStaticData != 0) RuntimeAugments.RhHandleFree(gcStaticData); - if (genericComposition != IntPtr.Zero) - MemoryHelpers.FreeMemory(genericComposition); - if (nonGcStaticData != IntPtr.Zero) - MemoryHelpers.FreeMemory(nonGcStaticData); - if (writableDataPtr != IntPtr.Zero) - MemoryHelpers.FreeMemory(writableDataPtr); - if (threadStaticIndex != IntPtr.Zero) - MemoryHelpers.FreeMemory(threadStaticIndex); + + MemoryHelpers.FreeMemory((void*)state.HalfBakedDictionary); + + MemoryHelpers.FreeMemory(threadStaticIndex); + MemoryHelpers.FreeMemory(nonGcStaticData); + MemoryHelpers.FreeMemory(genericComposition); + MemoryHelpers.FreeMemory(writableData); + MemoryHelpers.FreeMemory(eeTypePlusGCDesc); } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs index c660b63d0c31eb..7d2ead3b32aea2 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/GenericDictionary.cs @@ -10,10 +10,10 @@ namespace Internal.Runtime.TypeLoader { - internal abstract class GenericDictionary + internal abstract unsafe class GenericDictionary { protected GenericDictionaryCell[] _cells; - protected IntPtr _addressOfFirstCellSlot; + protected void* _addressOfFirstCellSlot; public GenericDictionary(GenericDictionaryCell[] cells) { @@ -23,9 +23,9 @@ public GenericDictionary(GenericDictionaryCell[] cells) public abstract IntPtr Allocate(); - public unsafe void Finish(TypeBuilder typeBuilder) + public void Finish(TypeBuilder typeBuilder) { - Debug.Assert(_cells.Length == 0 || _addressOfFirstCellSlot != IntPtr.Zero); + Debug.Assert(_cells.Length == 0 || _addressOfFirstCellSlot != null); IntPtr* realCells = (IntPtr*)_addressOfFirstCellSlot; for (int i = 0; i < _cells.Length; i++) @@ -41,9 +41,9 @@ public GenericTypeDictionary(GenericDictionaryCell[] cells) : base(cells) { } - public override IntPtr Allocate() + public override unsafe IntPtr Allocate() { - Debug.Assert(_addressOfFirstCellSlot == IntPtr.Zero); + Debug.Assert(_addressOfFirstCellSlot == null); if (_cells.Length > 0) { @@ -51,7 +51,7 @@ public override IntPtr Allocate() _addressOfFirstCellSlot = MemoryHelpers.AllocateMemory(checked((int)(_cells.Length * IntPtr.Size))); } - return _addressOfFirstCellSlot; + return (IntPtr)_addressOfFirstCellSlot; } } @@ -63,20 +63,20 @@ public GenericMethodDictionary(GenericDictionaryCell[] cells) public override unsafe IntPtr Allocate() { - Debug.Assert(_addressOfFirstCellSlot == IntPtr.Zero); + Debug.Assert(_addressOfFirstCellSlot == null); // Method dictionaries start with a header containing the hash code, which is not part of the native layout. // The real first slot is located after the header. // Use checked typecast to int to ensure there aren't any overflows/truncations - IntPtr dictionaryWithHeader = MemoryHelpers.AllocateMemory(checked((int)((_cells.Length + 1) * IntPtr.Size))); + void* dictionaryWithHeader = MemoryHelpers.AllocateMemory(checked((int)((_cells.Length + 1) * IntPtr.Size))); // Put a magic hash code to indicate dynamically allocated method dictionary for // debugging purposes. *(int*)dictionaryWithHeader = 0xD1CC0DE; // DICCODE - _addressOfFirstCellSlot = IntPtr.Add(dictionaryWithHeader, IntPtr.Size); + _addressOfFirstCellSlot = (byte*)dictionaryWithHeader + IntPtr.Size; - return _addressOfFirstCellSlot; + return (IntPtr)_addressOfFirstCellSlot; } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs index 0dbceb8be3c828..8ee004b11d42c7 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs @@ -1080,7 +1080,7 @@ private unsafe IntPtr BuildGenericLookupTarget(TypeSystemContext typeSystemConte // The first is a pointer that points to the TypeManager indirection cell. // The second is the offset into the native layout info blob in that TypeManager, where the native signature is encoded. - IntPtr** lazySignature = (IntPtr**)signature.ToPointer(); + IntPtr** lazySignature = (IntPtr**)signature; typeManager = new TypeManagerHandle(lazySignature[0][0]); offset = checked((uint)new IntPtr(lazySignature[1]).ToInt32()); reader = TypeLoaderEnvironment.GetNativeLayoutInfoReader(typeManager); diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs index d5e82d10d94cef..9ddec8a39d7aa3 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.LdTokenResultLookup.cs @@ -43,7 +43,7 @@ internal struct DynamicMethodHandleInfo #region String conversions private static unsafe string GetStringFromMemoryInNativeFormat(IntPtr pointerToDataStream) { - byte* dataStream = (byte*)pointerToDataStream.ToPointer(); + byte* dataStream = (byte*)pointerToDataStream; uint stringLen = NativePrimitiveDecoder.DecodeUnsigned(ref dataStream); return Encoding.UTF8.GetString(dataStream, checked((int)stringLen)); } @@ -54,7 +54,7 @@ private static unsafe string GetStringFromMemoryInNativeFormat(IntPtr pointerToD /// /// /// - public IntPtr GetNativeFormatStringForString(string str) + public unsafe IntPtr GetNativeFormatStringForString(string str) { using (_typeLoaderLock.EnterScope()) { @@ -69,13 +69,13 @@ public IntPtr GetNativeFormatStringForString(string str) foreach (byte b in utf8Bytes) stringEncoder.WriteByte(b); - IntPtr allocatedNativeFormatString = MemoryHelpers.AllocateMemory(stringEncoder.Size); + void* allocatedNativeFormatString = MemoryHelpers.AllocateMemory(stringEncoder.Size); unsafe { - stringEncoder.Save((byte*)allocatedNativeFormatString.ToPointer(), stringEncoder.Size); + stringEncoder.Save((byte*)allocatedNativeFormatString, stringEncoder.Size); } - _nativeFormatStrings.Add(str, allocatedNativeFormatString); - return allocatedNativeFormatString; + _nativeFormatStrings.Add(str, (IntPtr)allocatedNativeFormatString); + return (IntPtr)allocatedNativeFormatString; } } @@ -197,16 +197,12 @@ public unsafe RuntimeFieldHandle GetRuntimeFieldHandleForComponents(RuntimeTypeH { if (!_runtimeFieldHandles.TryGetValue(key, out runtimeFieldHandle)) { - IntPtr runtimeFieldHandleValue = MemoryHelpers.AllocateMemory(sizeof(DynamicFieldHandleInfo)); - if (runtimeFieldHandleValue == IntPtr.Zero) - throw new OutOfMemoryException(); - - DynamicFieldHandleInfo* fieldData = (DynamicFieldHandleInfo*)runtimeFieldHandleValue.ToPointer(); + DynamicFieldHandleInfo* fieldData = (DynamicFieldHandleInfo*)MemoryHelpers.AllocateMemory(sizeof(DynamicFieldHandleInfo)); fieldData->DeclaringType = *(IntPtr*)&declaringTypeHandle; fieldData->FieldName = fieldName; // Special flag (lowest bit set) in the handle value to indicate it was dynamically allocated - runtimeFieldHandleValue++; + IntPtr runtimeFieldHandleValue = (IntPtr)fieldData + 1; runtimeFieldHandle = *(RuntimeFieldHandle*)&runtimeFieldHandleValue; _runtimeFieldHandles.Add(key, runtimeFieldHandle); @@ -228,10 +224,9 @@ private unsafe bool TryGetDynamicRuntimeFieldHandleComponents(RuntimeFieldHandle IntPtr runtimeFieldHandleValue = *(IntPtr*)&runtimeFieldHandle; // Special flag in the handle value to indicate it was dynamically allocated - Debug.Assert((runtimeFieldHandleValue.ToInt64() & 0x1) == 0x1); - runtimeFieldHandleValue--; + Debug.Assert((runtimeFieldHandleValue & 0x1) == 0x1); - DynamicFieldHandleInfo* fieldData = (DynamicFieldHandleInfo*)runtimeFieldHandleValue.ToPointer(); + DynamicFieldHandleInfo* fieldData = (DynamicFieldHandleInfo*)(runtimeFieldHandleValue - 1); declaringTypeHandle = *(RuntimeTypeHandle*)&(fieldData->DeclaringType); // FieldName points to the field name in NativeLayout format, so we parse it using a NativeParser @@ -297,11 +292,8 @@ public unsafe RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTyp int numGenericMethodArgs = genericMethodArgs == null ? 0 : genericMethodArgs.Length; // Use checked arithmetics to ensure there aren't any overflows/truncations sizeToAllocate = checked(sizeToAllocate + (numGenericMethodArgs > 0 ? sizeof(IntPtr) * (numGenericMethodArgs - 1) : 0)); - IntPtr runtimeMethodHandleValue = MemoryHelpers.AllocateMemory(sizeToAllocate); - if (runtimeMethodHandleValue == IntPtr.Zero) - throw new OutOfMemoryException(); - DynamicMethodHandleInfo* methodData = (DynamicMethodHandleInfo*)runtimeMethodHandleValue.ToPointer(); + DynamicMethodHandleInfo* methodData = (DynamicMethodHandleInfo*)MemoryHelpers.AllocateMemory(sizeToAllocate); methodData->DeclaringType = *(IntPtr*)&declaringTypeHandle; methodData->MethodName = methodName; methodData->MethodSignature = methodSignature; @@ -314,7 +306,7 @@ public unsafe RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTyp } // Special flag in the handle value to indicate it was dynamically allocated, and doesn't point into the InvokeMap blob - runtimeMethodHandleValue++; + IntPtr runtimeMethodHandleValue = (IntPtr)methodData + 1; runtimeMethodHandle = *(RuntimeMethodHandle*)&runtimeMethodHandleValue; _runtimeMethodHandles.Add(key, runtimeMethodHandle); @@ -346,12 +338,12 @@ public bool TryGetRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMetho private unsafe bool TryGetDynamicRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgs) { IntPtr runtimeMethodHandleValue = *(IntPtr*)&runtimeMethodHandle; - Debug.Assert((runtimeMethodHandleValue.ToInt64() & 0x1) == 0x1); // Special flag in the handle value to indicate it was dynamically allocated, and doesn't point into the InvokeMap blob - runtimeMethodHandleValue--; + Debug.Assert((runtimeMethodHandleValue & 0x1) == 0x1); + + DynamicMethodHandleInfo* methodData = (DynamicMethodHandleInfo*)(runtimeMethodHandleValue - 1); - DynamicMethodHandleInfo* methodData = (DynamicMethodHandleInfo*)runtimeMethodHandleValue.ToPointer(); declaringTypeHandle = *(RuntimeTypeHandle*)&(methodData->DeclaringType); genericMethodArgs = null; diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs index 75252d276d49e7..464a370a5f7be3 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeSystemContext.Runtime.cs @@ -42,7 +42,7 @@ protected override RuntimeTypeHandle ConvertIntPtrToValue(IntPtr pointer) { unsafe { - return ((MethodTable*)pointer.ToPointer())->ToRuntimeTypeHandle(); + return ((MethodTable*)pointer)->ToRuntimeTypeHandle(); } } @@ -104,7 +104,7 @@ protected override RuntimeTypeHandle ConvertIntPtrToValue(IntPtr pointer) { unsafe { - return ((MethodTable*)pointer.ToPointer())->ToRuntimeTypeHandle(); + return ((MethodTable*)pointer)->ToRuntimeTypeHandle(); } } From 9dc6ea62a4d195ae4559f4609a56933c61889756 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 19 Feb 2024 23:36:14 -0800 Subject: [PATCH 132/158] Revert "[NativeAOT] Add null checks into memcpy/memset helpers (#98547)" (#98681) This reverts commit 6d4fc1ad4725ca4cc77214b93bc1d37a6fa3553d. --- .../Runtime/CompilerHelpers/MemoryHelpers.cs | 33 ------------------- .../src/System.Private.CoreLib.csproj | 1 - .../ILCompiler.Compiler/Compiler/JitHelper.cs | 4 +-- 3 files changed, 2 insertions(+), 36 deletions(-) delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MemoryHelpers.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MemoryHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MemoryHelpers.cs deleted file mode 100644 index 644fcf1a59940e..00000000000000 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MemoryHelpers.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime; - -namespace Internal.Runtime.CompilerHelpers -{ - /// - /// These methods are used to implement memcpy and memset intrinsics with null checks. - /// - internal static class MemoryHelpers - { - private static unsafe void MemSet(ref byte dest, byte value, nuint size) - { - if (size > 0) - { - _ = dest; - SpanHelpers.Fill(ref dest, size, value); - } - } - - private static unsafe void MemCopy(ref byte dest, ref byte src, nuint size) - { - if (size > 0) - { - _ = dest; - _ = src; - Buffer.Memmove(ref dest, ref src, size); - } - } - } -} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index d6e8c3a7e90274..4ca91458c70e71 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -100,7 +100,6 @@ - diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs index c55dc58175b05f..9e7285b751a3d3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -134,10 +134,10 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, break; case ReadyToRunHelper.MemCpy: - methodDesc = context.GetHelperEntryPoint("MemoryHelpers", "MemCopy"); + mangledName = "memcpy"; // TODO: Null reference handling break; case ReadyToRunHelper.MemSet: - methodDesc = context.GetHelperEntryPoint("MemoryHelpers", "MemSet"); + mangledName = "memset"; // TODO: Null reference handling break; case ReadyToRunHelper.GetRuntimeTypeHandle: From cf56f726ba7b77690c79146726a57f7be573ead5 Mon Sep 17 00:00:00 2001 From: Matt Mitchell Date: Tue, 20 Feb 2024 06:07:33 -0800 Subject: [PATCH 133/158] Propagate DotNetBuildOrchestrator (#98660) --- eng/DotNetBuild.props | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/DotNetBuild.props b/eng/DotNetBuild.props index 3c68bf354bae17..53d03c7f4dd1d6 100644 --- a/eng/DotNetBuild.props +++ b/eng/DotNetBuild.props @@ -65,6 +65,7 @@ $(InnerBuildArgs) /p:DotNetBuildRepo=true + $(InnerBuildArgs) /p:DotNetBuildOrchestrator=true $(InnerBuildArgs) /p:OfficialBuildId=$(OfficialBuildId) $(InnerBuildArgs) /p:ContinuousIntegrationBuild=$(ContinuousIntegrationBuild) $(InnerBuildArgs) /p:PortableBuild=$(PortableBuild) From 2e5d409bb21715ff2e5438c822b55dd1a8fb148d Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Tue, 20 Feb 2024 15:29:15 +0100 Subject: [PATCH 134/158] Disable building Swift interop tests on macOS with Native AOT (#98667) * Disable Swift interop tests on macOS with Native AOT * Disable building Swift interop tests with Native AOT * Remove redundant ExcludeList item * Update CLRTestTargetUnsupported property --- .../Swift/SwiftErrorHandling/SwiftErrorHandling.csproj | 4 +++- .../Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.csproj | 4 +++- .../Interop/Swift/SwiftSelfContext/SwiftSelfContext.csproj | 4 +++- src/tests/issues.targets | 4 ---- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.csproj b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.csproj index a57cd84cf8842c..49be10b9393911 100644 --- a/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.csproj +++ b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.csproj @@ -3,8 +3,10 @@ true true - + true + + true diff --git a/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.csproj b/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.csproj index a57cd84cf8842c..49be10b9393911 100644 --- a/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.csproj +++ b/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.csproj @@ -3,8 +3,10 @@ true true - + true + + true diff --git a/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.csproj b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.csproj index a57cd84cf8842c..49be10b9393911 100644 --- a/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.csproj +++ b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.csproj @@ -3,8 +3,10 @@ true true - + true + + true diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 9efbe150a7cd1c..473e82bc3e9697 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1158,10 +1158,6 @@ Dynamic code generation is not supported on this platform - - - https://github.com/dotnet/runtime/issues/93631 - - + https://github.com/dotnet/source-build-reference-packages - 8ee50f75f960fbfb20fce0fefc5a3b05d15b1d21 + 2f79f97b7a6a0cf2ca3297a8fa526e6f4ea98ce2 From 6a468fe5fec92a07e1ce10f7c448e44dac6fcf3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 21 Feb 2024 07:44:26 +0900 Subject: [PATCH 145/158] Reorder validation of instantiation length (#98720) Fixes #98703. --- .../Compiler/CompilerTypeSystemContext.Validation.cs | 12 ++++++------ .../nativeaot/SmokeTests/Reflection/Reflection.cs | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Validation.cs b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Validation.cs index 9326fe5978d6fc..6c0b5f9fe2e693 100644 --- a/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Validation.cs +++ b/src/coreclr/tools/Common/Compiler/CompilerTypeSystemContext.Validation.cs @@ -326,6 +326,12 @@ private static TypeDesc EnsureLoadableTypeUncached(TypeDesc type) return type; } + // Make sure instantiation length matches the expectation + if (type.Instantiation.Length != type.GetTypeDefinition().Instantiation.Length) + { + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); + } + // We need to be able to load interfaces foreach (var intf in type.RuntimeInterfaces) { @@ -343,12 +349,6 @@ private static TypeDesc EnsureLoadableTypeUncached(TypeDesc type) defType.ComputeInstanceLayout(InstanceLayoutKind.TypeAndFields); defType.ComputeStaticFieldLayout(StaticLayoutKind.StaticRegionSizesAndFields); - // Make sure instantiation length matches the expectation - if (defType.Instantiation.Length != defType.GetTypeDefinition().Instantiation.Length) - { - ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, type); - } - foreach (TypeDesc typeArg in defType.Instantiation) { // ByRefs, pointers, function pointers, and System.Void are never valid instantiation arguments diff --git a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs index c000d2687b57fd..5abac662bfe7ad 100644 --- a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs +++ b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs @@ -1509,6 +1509,7 @@ public static void Run() try { Type.GetType("System.Span`1[[System.Byte, System.Runtime]][], System.Runtime"); + Type.GetType("System.Collections.Generic.Dictionary`2[System.String]"); } catch { } From 95af2bcf9d84cac3955fe285d22e15e226cc05de Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 20 Feb 2024 14:52:54 -0800 Subject: [PATCH 146/158] JIT: ARM64 SVE format encodings, `SVE_HY_3A` to `SVE_IB_3A` (#98468) * Initial format work for prf* instructions * Formats done * Forgot to add CONST14 * Feedback * Feedback * Fix build * Fix the merge. Small cleanup. * Formatting --- src/coreclr/jit/codegenarm64test.cpp | 111 ++++ src/coreclr/jit/emit.h | 10 + src/coreclr/jit/emitarm64.cpp | 735 ++++++++++++++++++++++++++- src/coreclr/jit/emitarm64.h | 34 +- src/coreclr/jit/instr.h | 22 + src/coreclr/jit/instrsarm64sve.h | 8 +- 6 files changed, 907 insertions(+), 13 deletions(-) diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 47cc0fb6bfd194..6fc86ba66c5c37 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -7599,6 +7599,117 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_I(INS_sve_str, EA_SCALABLE, REG_V2, REG_R3, 255, INS_OPTS_NONE, INS_SCALABLE_OPTS_UNPREDICATED); + // IF_SVE_HY_3A + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfd, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P6, REG_R5, REG_V4, + INS_OPTS_SCALABLE_S_UXTW, + INS_SCALABLE_OPTS_MOD_N); // PRFD , , [, .S, #3] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfh, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P7, REG_R8, REG_V9, + INS_OPTS_SCALABLE_S_SXTW, + INS_SCALABLE_OPTS_MOD_N); // PRFH , , [, .S, #1] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfw, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P0, REG_R2, REG_V1, + INS_OPTS_SCALABLE_S_UXTW, + INS_SCALABLE_OPTS_MOD_N); // PRFW , , [, .S, #2] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL1STRM, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL2KEEP, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL2STRM, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL3KEEP, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL3STRM, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PSTL1KEEP, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PSTL1STRM, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PSTL2KEEP, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PSTL2STRM, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PSTL3KEEP, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PSTL3STRM, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_CONST6, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_SXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_CONST7, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_SXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_CONST14, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_CONST15, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_S_UXTW); // PRFB , , [, .S, ] + + // IF_SVE_HY_3A_A + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P1, REG_R2, REG_V3, + INS_OPTS_SCALABLE_D_UXTW); // PRFB , , [, .D, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfd, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P6, REG_R5, REG_V4, + INS_OPTS_SCALABLE_D_UXTW, + INS_SCALABLE_OPTS_MOD_N); // PRFD , , [, .D, #3] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfh, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P7, REG_R8, REG_V9, + INS_OPTS_SCALABLE_D_SXTW, + INS_SCALABLE_OPTS_MOD_N); // PRFH , , [, .D, #1] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfw, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P0, REG_R2, REG_V1, + INS_OPTS_SCALABLE_D_UXTW, + INS_SCALABLE_OPTS_MOD_N); // PRFW , , [, .D, #2] + + // IF_SVE_HY_3B + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P0, REG_R1, REG_V2, + INS_OPTS_SCALABLE_D); // PRFB , , [, .D] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfd, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P7, REG_R4, REG_V3, + INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_LSL_N); // PRFD , , [, .D, LSL #3] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfh, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P6, REG_R5, REG_V4, + INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_LSL_N); // PRFH , , [, .D, LSL #1] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfw, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P3, REG_R2, REG_V1, + INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_LSL_N); // PRFW , , [, .D, LSL #2] + + // IF_SVE_HZ_2A_B + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P1, REG_V2, 0, + INS_OPTS_SCALABLE_S); // PRFB , , [.S{, #}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfd, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P4, REG_V3, 248, + INS_OPTS_SCALABLE_S); // PRFD , , [.S{, #}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfh, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P0, REG_V4, 62, + INS_OPTS_SCALABLE_S); // PRFH , , [.S{, #}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfw, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P7, REG_V5, 124, + INS_OPTS_SCALABLE_S); // PRFW , , [.S{, #}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P1, REG_V2, 31, + INS_OPTS_SCALABLE_D); // PRFB , , [.D{, #}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfd, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P4, REG_V3, 248, + INS_OPTS_SCALABLE_D); // PRFD , , [.D{, #}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfh, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P0, REG_V4, 62, + INS_OPTS_SCALABLE_D); // PRFH , , [.D{, #}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfw, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P7, REG_V5, 124, + INS_OPTS_SCALABLE_D); // PRFW , , [.D{, #}] + + // IF_SVE_IA_2A + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P2, REG_R3, + -32); // PRFB , , [{, #, MUL VL}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfd, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P7, REG_R4, + 31); // PRFD , , [{, #, MUL VL}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfh, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P1, REG_R2, + 0); // PRFH , , [{, #, MUL VL}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfw, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P0, REG_R5, + -32); // PRFW , , [{, #, MUL VL}] + theEmitter->emitIns_PRFOP_R_R_I(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P2, REG_R3, + 17); // PRFB , , [{, #, MUL VL}] + + // IF_SVE_IB_3A + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfb, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P0, REG_R1, + REG_R2); // PRFB , , [, ] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfd, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P5, REG_R4, REG_R3, + INS_OPTS_NONE, + INS_SCALABLE_OPTS_LSL_N); // PRFD , , [, , LSL #3] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfh, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P6, REG_R7, REG_R8, + INS_OPTS_NONE, + INS_SCALABLE_OPTS_LSL_N); // PRFH , , [, , LSL #1] + theEmitter->emitIns_PRFOP_R_R_R(INS_sve_prfw, EA_SCALABLE, SVE_PRFOP_PLDL1KEEP, REG_P7, REG_R1, REG_R9, + INS_OPTS_NONE, + INS_SCALABLE_OPTS_LSL_N); // PRFW , , [, , LSL #2] // IF_SVE_HX_3A_B theEmitter->emitIns_R_R_R_I(INS_sve_ld1b, EA_SCALABLE, REG_V0, REG_P0, REG_V1, 0, INS_OPTS_SCALABLE_S); // LD1B {.S }, /Z, [.S{, #}] diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 45be55272f661f..8ef49e0d5215c6 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1478,6 +1478,16 @@ class emitter assert(!idIsSmallDsc()); idAddr()->_idSvePattern = idSvePattern; } + insSvePrfop idSvePrfop() const + { + assert(!idIsSmallDsc()); + return (insSvePrfop)(idAddr()->_idReg4); + } + void idSvePrfop(insSvePrfop idSvePrfop) + { + assert(!idIsSmallDsc()); + idAddr()->_idReg4 = (regNumber)idSvePrfop; + } #endif // TARGET_ARM64 #endif // TARGET_ARMARCH diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index dea80c05e6b8be..e09952558df63a 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -2355,6 +2355,53 @@ void emitter::emitInsSanityCheck(instrDesc* id) // iiiiii break; + case IF_SVE_HY_3A: // .........h.mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit scaled + // offsets) + case IF_SVE_HY_3A_A: // .........h.mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit + // scaled offsets) + elemsize = id->idOpSize(); + assert(insOptsScalable32bitExtends(id->idInsOpt())); + assert(isLowPredicateRegister(id->idReg1())); + assert(isGeneralRegister(id->idReg2())); + assert(isVectorRegister(id->idReg3())); + assert(isScalableVectorSize(elemsize)); + break; + + case IF_SVE_HY_3B: // ...........mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit scaled + // offsets) + elemsize = id->idOpSize(); + assert(id->idInsOpt() == INS_OPTS_SCALABLE_D); + assert(isLowPredicateRegister(id->idReg1())); + assert(isGeneralRegister(id->idReg2())); + assert(isVectorRegister(id->idReg3())); + assert(isScalableVectorSize(elemsize)); + break; + + case IF_SVE_IB_3A: // ...........mmmmm ...gggnnnnn.oooo -- SVE contiguous prefetch (scalar plus scalar) + elemsize = id->idOpSize(); + assert(insOptsNone(id->idInsOpt())); + assert(isLowPredicateRegister(id->idReg1())); + assert(isGeneralRegister(id->idReg2())); + assert(isGeneralRegister(id->idReg3())); + assert(isScalableVectorSize(elemsize)); + break; + + case IF_SVE_HZ_2A_B: // ...........iiiii ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (vector plus immediate) + elemsize = id->idOpSize(); + assert(insOptsScalableWords(id->idInsOpt())); + assert(isLowPredicateRegister(id->idReg1())); + assert(isVectorRegister(id->idReg2())); + assert(isScalableVectorSize(elemsize)); + break; + + case IF_SVE_IA_2A: // ..........iiiiii ...gggnnnnn.oooo -- SVE contiguous prefetch (scalar plus immediate) + elemsize = id->idOpSize(); + assert(insOptsNone(id->idInsOpt())); + assert(isLowPredicateRegister(id->idReg1())); + assert(isGeneralRegister(id->idReg2())); + assert(isScalableVectorSize(elemsize)); + break; + case IF_SVE_HX_3A_B: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) elemsize = id->idOpSize(); assert(insOptsScalableWords(id->idInsOpt())); @@ -15079,6 +15126,205 @@ void emitter::emitIns_R_PATTERN_I(instruction ins, emitAttr attr, regNumber reg1 appendToCurIG(id); } +/***************************************************************************** + * + * Add an instruction referencing three registers and a SVE 'prfop'. + */ + +void emitter::emitIns_PRFOP_R_R_R(instruction ins, + emitAttr attr, + insSvePrfop prfop, + regNumber reg1, + regNumber reg2, + regNumber reg3, + insOpts opt /* = INS_OPTS_NONE */, + insScalableOpts sopt /* = INS_SCALABLE_OPTS_NONE */) +{ + emitAttr size = EA_SIZE(attr); + emitAttr elemsize = EA_UNKNOWN; + insFormat fmt = IF_NONE; + + /* Figure out the encoding format of the instruction */ + switch (ins) + { + case INS_sve_prfb: + assert(insScalableOptsNone(sopt)); + assert(isLowPredicateRegister(reg1)); + assert(isGeneralRegister(reg2)); + assert(isScalableVectorSize(size)); + + if (insOptsScalable32bitExtends(opt)) + { + assert(isVectorRegister(reg3)); + + if (insOptsScalableSingleWord32bitExtends(opt)) + { + fmt = IF_SVE_HY_3A; + } + else + { + assert(insOptsScalableDoubleWord32bitExtends(opt)); + fmt = IF_SVE_HY_3A_A; + } + } + else if (isVectorRegister(reg3)) + { + assert(opt == INS_OPTS_SCALABLE_D); + fmt = IF_SVE_HY_3B; + } + else + { + assert(insOptsNone(opt)); + assert(isGeneralRegister(reg3)); + fmt = IF_SVE_IB_3A; + } + break; + + case INS_sve_prfh: + case INS_sve_prfw: + case INS_sve_prfd: + assert(isLowPredicateRegister(reg1)); + assert(isGeneralRegister(reg2)); + assert(isScalableVectorSize(size)); + + if (sopt == INS_SCALABLE_OPTS_MOD_N) + { + if (insOptsScalableSingleWord32bitExtends(opt)) + { + fmt = IF_SVE_HY_3A; + } + else + { + assert(insOptsScalableDoubleWord32bitExtends(opt)); + fmt = IF_SVE_HY_3A_A; + } + } + else + { + assert(sopt == INS_SCALABLE_OPTS_LSL_N); + if (isVectorRegister(reg3)) + { + assert(opt == INS_OPTS_SCALABLE_D); + fmt = IF_SVE_HY_3B; + } + else + { + assert(insOptsNone(opt)); + assert(isGeneralRegister(reg3)); + fmt = IF_SVE_IB_3A; + } + } + break; + + default: + unreached(); + break; + + } // end switch (ins) + assert(fmt != IF_NONE); + + instrDesc* id = emitNewInstr(attr); + + id->idIns(ins); + id->idInsOpt(opt); + id->idInsFmt(fmt); + + id->idReg1(reg1); + id->idReg2(reg2); + id->idReg3(reg3); + id->idSvePrfop(prfop); + + dispIns(id); + appendToCurIG(id); +} + +/***************************************************************************** + * + * Add an instruction referencing two registers, a SVE 'prfop' and an immediate. + */ + +void emitter::emitIns_PRFOP_R_R_I(instruction ins, + emitAttr attr, + insSvePrfop prfop, + regNumber reg1, + regNumber reg2, + int imm, + insOpts opt /* = INS_OPTS_NONE */) +{ + emitAttr size = EA_SIZE(attr); + emitAttr elemsize = EA_UNKNOWN; + insFormat fmt = IF_NONE; + + /* Figure out the encoding format of the instruction */ + switch (ins) + { + case INS_sve_prfb: + case INS_sve_prfh: + case INS_sve_prfw: + case INS_sve_prfd: + assert(isLowPredicateRegister(reg1)); + assert(isScalableVectorSize(size)); + + if (isVectorRegister(reg2)) + { + assert(insOptsScalableWords(opt)); + +#ifdef DEBUG + switch (ins) + { + case INS_sve_prfb: + assert(isValidUimm5(imm)); + break; + + case INS_sve_prfh: + assert(isValidUimm5_MultipleOf2(imm)); + break; + + case INS_sve_prfw: + assert(isValidUimm5_MultipleOf4(imm)); + break; + + case INS_sve_prfd: + assert(isValidUimm5_MultipleOf8(imm)); + break; + + default: + assert(!"Invalid instruction"); + break; + } +#endif // DEBUG + fmt = IF_SVE_HZ_2A_B; + } + else + { + assert(insOptsNone(opt)); + assert(isGeneralRegister(reg2)); + assert(isValidSimm6(imm)); + fmt = IF_SVE_IA_2A; + } + break; + + default: + unreached(); + break; + + } // end switch (ins) + assert(fmt != IF_NONE); + + instrDesc* id = emitNewInstrCns(attr, imm); + + id->idIns(ins); + id->idInsOpt(opt); + id->idInsFmt(fmt); + + id->idReg1(reg1); + id->idReg2(reg2); + id->idSvePrfop(prfop); + + dispIns(id); + appendToCurIG(id); +} + /***************************************************************************** * * Add a memory barrier instruction with a 'barrier' immediate @@ -18649,6 +18895,20 @@ void emitter::emitIns_Call(EmitCallType callType, } break; + case IF_SVE_HY_3B: + case IF_SVE_IB_3A: + switch (ins) + { + case INS_sve_prfh: + case INS_sve_prfw: + case INS_sve_prfd: + return true; + + default: + break; + } + break; + default: break; } @@ -18780,6 +19040,21 @@ void emitter::emitIns_Call(EmitCallType callType, } break; + case IF_SVE_HY_3A: + case IF_SVE_HY_3A_A: + switch (ins) + { + case INS_sve_prfb: + case INS_sve_prfh: + case INS_sve_prfw: + case INS_sve_prfd: + return true; + + default: + break; + } + break; + default: break; } @@ -19254,6 +19529,49 @@ void emitter::emitIns_Call(EmitCallType callType, } break; + case IF_SVE_HY_3A: + case IF_SVE_HY_3A_A: + assert(!insSveIsLslN(ins, fmt)); + assert(insSveIsModN(ins, fmt)); + switch (ins) + { + case INS_sve_prfb: + return 0; + + case INS_sve_prfh: + return 1; + + case INS_sve_prfw: + return 2; + + case INS_sve_prfd: + return 3; + + default: + break; + } + break; + + case IF_SVE_HY_3B: + case IF_SVE_IB_3A: + assert(insSveIsLslN(ins, fmt)); + assert(!insSveIsModN(ins, fmt)); + switch (ins) + { + case INS_sve_prfh: + return 1; + + case INS_sve_prfw: + return 2; + + case INS_sve_prfd: + return 3; + + default: + break; + } + break; + default: break; } @@ -19698,7 +20016,7 @@ void emitter::emitIns_Call(EmitCallType callType, /***************************************************************************** * - * // Returns the encoding for the immediate value that is a multiple of 2 as 5-bits at bit locations '20-16'. + * Returns the encoding for the immediate value that is a multiple of 2 as 5-bits at bit locations '20-16'. */ /*static*/ emitter::code_t emitter::insEncodeUimm5_MultipleOf2_20_to_16(ssize_t imm) @@ -19709,7 +20027,7 @@ void emitter::emitIns_Call(EmitCallType callType, /***************************************************************************** * - * // Returns the encoding for the immediate value that is a multiple of 4 as 5-bits at bit locations '20-16'. + * Returns the encoding for the immediate value that is a multiple of 4 as 5-bits at bit locations '20-16'. */ /*static*/ emitter::code_t emitter::insEncodeUimm5_MultipleOf4_20_to_16(ssize_t imm) @@ -19720,7 +20038,7 @@ void emitter::emitIns_Call(EmitCallType callType, /***************************************************************************** * - * // Returns the encoding for the immediate value that is a multiple of 8 as 5-bits at bit locations '20-16'. + * Returns the encoding for the immediate value that is a multiple of 8 as 5-bits at bit locations '20-16'. */ /*static*/ emitter::code_t emitter::insEncodeUimm5_MultipleOf8_20_to_16(ssize_t imm) @@ -19731,7 +20049,7 @@ void emitter::emitIns_Call(EmitCallType callType, /***************************************************************************** * - * // Returns the encoding for the immediate value that is a multiple of 2 as 6-bits at bit locations '21-16'. + * Returns the encoding for the immediate value that is a multiple of 2 as 6-bits at bit locations '21-16'. */ /*static*/ emitter::code_t emitter::insEncodeUimm6_MultipleOf2_21_to_16(ssize_t imm) @@ -19742,7 +20060,7 @@ void emitter::emitIns_Call(EmitCallType callType, /***************************************************************************** * - * // Returns the encoding for the immediate value that is a multiple of 4 as 6-bits at bit locations '21-16'. + * Returns the encoding for the immediate value that is a multiple of 4 as 6-bits at bit locations '21-16'. */ /*static*/ emitter::code_t emitter::insEncodeUimm6_MultipleOf4_21_to_16(ssize_t imm) @@ -19753,7 +20071,7 @@ void emitter::emitIns_Call(EmitCallType callType, /***************************************************************************** * - * // Returns the encoding for the immediate value that is a multiple of 8 as 6-bits at bit locations '21-16'. + * Returns the encoding for the immediate value that is a multiple of 8 as 6-bits at bit locations '21-16'. */ /*static*/ emitter::code_t emitter::insEncodeUimm6_MultipleOf8_21_to_16(ssize_t imm) @@ -19777,6 +20095,21 @@ void emitter::emitIns_Call(EmitCallType callType, return (code_t)imm << 16; } +/***************************************************************************** + * + * Returns the encoding for the immediate value as 6-bits at bit locations '20-16'. + */ + +/*static*/ emitter::code_t emitter::insEncodeSimm6_21_to_16(ssize_t imm) +{ + assert(isValidSimm6(imm)); + if (imm < 0) + { + imm = (imm & 0x3F); + } + return (code_t)imm << 16; +} + /***************************************************************************** * * Returns the encoding for the immediate value as 2-bits at bit locations '9-8'. @@ -23316,6 +23649,81 @@ BYTE* emitter::emitOutput_InstrSve(BYTE* dst, instrDesc* id) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_HY_3A: // .........h.mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit scaled + // offsets) + case IF_SVE_HY_3A_A: // .........h.mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit + // scaled offsets) + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_12_to_10(id->idReg1()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg2()); // nnnnn + code |= insEncodeReg_V_20_to_16(id->idReg3()); // mmmmm + code |= id->idSvePrfop(); // oooo + + switch (id->idInsOpt()) + { + case INS_OPTS_SCALABLE_S_SXTW: + case INS_OPTS_SCALABLE_D_SXTW: + code |= (1 << 22); // h + break; + + default: + break; + } + + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_HY_3B: // ...........mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit scaled + // offsets) + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_12_to_10(id->idReg1()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg2()); // nnnnn + code |= insEncodeReg_V_20_to_16(id->idReg3()); // mmmmm + code |= id->idSvePrfop(); // oooo + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_IB_3A: // ...........mmmmm ...gggnnnnn.oooo -- SVE contiguous prefetch (scalar plus scalar) + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_12_to_10(id->idReg1()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg2()); // nnnnn + code |= insEncodeReg_R_20_to_16(id->idReg3()); // mmmmm + code |= id->idSvePrfop(); // oooo + dst += emitOutput_Instr(dst, code); + break; + + case IF_SVE_HZ_2A_B: // ...........iiiii ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (vector plus immediate) + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_12_to_10(id->idReg1()); // ggg + code |= insEncodeReg_V_9_to_5(id->idReg2()); // nnnnn + code |= id->idSvePrfop(); // oooo + + if (id->idInsOpt() == INS_OPTS_SCALABLE_D) + { + code |= (1 << 30); // set bit '30' to make it a double-word + } + + switch (ins) + { + case INS_sve_prfh: + code |= insEncodeUimm5_MultipleOf2_20_to_16(imm); // iiiii + break; + + case INS_sve_prfw: + code |= insEncodeUimm5_MultipleOf4_20_to_16(imm); // iiiii + break; + + case INS_sve_prfd: + code |= insEncodeUimm5_MultipleOf8_20_to_16(imm); // iiiii + break; + + default: + assert(ins == INS_sve_prfb); + } + dst += emitOutput_Instr(dst, code); + break; + case IF_SVE_HX_3A_B: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) imm = emitGetInsSC(id); code = emitInsCodeSve(ins, fmt); @@ -23395,6 +23803,16 @@ BYTE* emitter::emitOutput_InstrSve(BYTE* dst, instrDesc* id) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_IA_2A: // ..........iiiiii ...gggnnnnn.oooo -- SVE contiguous prefetch (scalar plus immediate) + imm = emitGetInsSC(id); + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_12_to_10(id->idReg1()); // ggg + code |= insEncodeReg_R_9_to_5(id->idReg2()); // nnnnn + code |= id->idSvePrfop(); // oooo + code |= insEncodeSimm6_21_to_16(imm); // iiiiii + dst += emitOutput_Instr(dst, code); + break; + case IF_SVE_IC_3A: // ..........iiiiii ...gggnnnnnttttt -- SVE load and broadcast element imm = emitGetInsSC(id); code = emitInsCodeSve(ins, fmt); @@ -23844,6 +24262,24 @@ void emitter::emitDispSveModAddr(instruction ins, regNumber reg1, regNumber reg2 printf("]"); } +/***************************************************************************** + * + * Prints the encoding for format [.S{, #}] + */ +void emitter::emitDispSveImm(regNumber reg1, ssize_t imm, insOpts opt) +{ + printf("["); + emitDispSveReg(reg1, opt, imm != 0); + if (imm != 0) + { + // This does not have to be printed as hex. + // We only do it because the capstone disassembly displays this immediate as hex. + // We could not modify capstone without affecting other cases. + emitDispImm(imm, false, /* alwaysHex */ true); + } + printf("]"); +} + /***************************************************************************** * * Prints the encoding for format [{, #, MUL VL}] @@ -24413,6 +24849,89 @@ void emitter::emitDispSvePattern(insSvePattern pattern, bool addComma) } } +/***************************************************************************** + * + * Display an insSvePrfop + */ +void emitter::emitDispSvePrfop(insSvePrfop prfop, bool addComma) +{ + switch (prfop) + { + case SVE_PRFOP_PLDL1KEEP: + printf("pldl1keep"); + break; + + case SVE_PRFOP_PLDL1STRM: + printf("pldl1strm"); + break; + + case SVE_PRFOP_PLDL2KEEP: + printf("pldl2keep"); + break; + + case SVE_PRFOP_PLDL2STRM: + printf("pldl2strm"); + break; + + case SVE_PRFOP_PLDL3KEEP: + printf("pldl3keep"); + break; + + case SVE_PRFOP_PLDL3STRM: + printf("pldl3strm"); + break; + + case SVE_PRFOP_PSTL1KEEP: + printf("pstl1keep"); + break; + + case SVE_PRFOP_PSTL1STRM: + printf("pstl1strm"); + break; + + case SVE_PRFOP_PSTL2KEEP: + printf("pstl2keep"); + break; + + case SVE_PRFOP_PSTL2STRM: + printf("pstl2strm"); + break; + + case SVE_PRFOP_PSTL3KEEP: + printf("pstl3keep"); + break; + + case SVE_PRFOP_PSTL3STRM: + printf("pstl3strm"); + break; + + case SVE_PRFOP_CONST6: + printf("#6"); + break; + + case SVE_PRFOP_CONST7: + printf("#7"); + break; + + case SVE_PRFOP_CONST14: + printf("#0xE"); + break; + + case SVE_PRFOP_CONST15: + printf("#0xF"); + break; + + default: + assert(!"Invalid prfop"); + break; + } + + if (addComma) + { + emitDispComma(); + } +} + /***************************************************************************** * * Display (optionally) the instruction encoding in hex @@ -26981,6 +27500,51 @@ void emitter::emitDispInsHelp( emitDispSveReg(id->idReg4(), id->idInsOpt(), false); break; + // , , [, .S, ] + // , , [, .S, #1] + // , , [, .S, #2] + // , , [, .S, #3] + case IF_SVE_HY_3A: // .........h.mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit scaled + // offsets) + // , , [, .D, ] + // , , [, .D, #1] + // , , [, .D, #2] + // , , [, .D, #3] + case IF_SVE_HY_3A_A: // .........h.mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit + // scaled offsets) + // , , [, .D] + // , , [, .D, LSL #1] + // , , [, .D, LSL #2] + // , , [, .D, LSL #3] + case IF_SVE_HY_3B: // ...........mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit scaled + // offsets) + // , , [, ] + // , , [, , LSL #1] + // , , [, , LSL #2] + // , , [, , LSL #3] + case IF_SVE_IB_3A: // ...........mmmmm ...gggnnnnn.oooo -- SVE contiguous prefetch (scalar plus scalar) + emitDispSvePrfop(id->idSvePrfop(), true); + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt), id->idInsOpt(), true); + emitDispSveModAddr(ins, id->idReg2(), id->idReg3(), id->idInsOpt(), fmt); + break; + + // , , [.S{, #}] + // , , [.D{, #}] + case IF_SVE_HZ_2A_B: // ...........iiiii ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (vector plus immediate) + imm = emitGetInsSC(id); + emitDispSvePrfop(id->idSvePrfop(), true); + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt), id->idInsOpt(), true); + emitDispSveImm(id->idReg2(), imm, id->idInsOpt()); + break; + + // , , [{, #, MUL VL}] + case IF_SVE_IA_2A: // ..........iiiiii ...gggnnnnn.oooo -- SVE contiguous prefetch (scalar plus immediate) + imm = emitGetInsSC(id); + emitDispSvePrfop(id->idSvePrfop(), true); + emitDispPredicateReg(id->idReg1(), insGetPredicateType(fmt), id->idInsOpt(), true); + emitDispSveImmMulVl(id->idReg2(), imm); + break; + // {.S }, /Z, [.S{, #}] // {.D }, /Z, [.D{, #}] case IF_SVE_HX_3A_B: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) @@ -30690,6 +31254,165 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insLatency = PERFSCORE_LATENCY_2C; break; + case IF_SVE_HY_3A: // .........h.mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit scaled + // offsets) + switch (ins) + { + case INS_sve_prfb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfh: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfw: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfd: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + default: + // all other instructions + perfScoreUnhandledInstruction(id, &result); + break; + } + break; + + case IF_SVE_HY_3A_A: // .........h.mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit + // scaled offsets) + switch (ins) + { + case INS_sve_prfb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfh: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfw: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfd: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + default: + // all other instructions + perfScoreUnhandledInstruction(id, &result); + break; + } + break; + + case IF_SVE_HY_3B: // ...........mmmmm ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (scalar plus 32-bit scaled + // offsets) + switch (ins) + { + case INS_sve_prfb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfh: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfw: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfd: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + default: + // all other instructions + perfScoreUnhandledInstruction(id, &result); + break; + } + break; + + case IF_SVE_IB_3A: // ...........mmmmm ...gggnnnnn.oooo -- SVE contiguous prefetch (scalar plus scalar) + switch (ins) + { + case INS_sve_prfb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfh: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfw: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfd: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + default: + // all other instructions + perfScoreUnhandledInstruction(id, &result); + break; + } + break; + + case IF_SVE_HZ_2A_B: // ...........iiiii ...gggnnnnn.oooo -- SVE 32-bit gather prefetch (vector plus immediate) + switch (ins) + { + case INS_sve_prfb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfh: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfw: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfd: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + default: + // all other instructions + perfScoreUnhandledInstruction(id, &result); + break; + } + break; + + case IF_SVE_IA_2A: // ..........iiiiii ...gggnnnnn.oooo -- SVE contiguous prefetch (scalar plus immediate) + switch (ins) + { + case INS_sve_prfb: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfh: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfw: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + case INS_sve_prfd: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; // need to fix + result.insLatency = PERFSCORE_LATENCY_1C; // need to fix + break; + default: + // all other instructions + perfScoreUnhandledInstruction(id, &result); + break; + } + break; + case IF_SVE_HX_3A_B: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) case IF_SVE_HX_3A_E: // ...........iiiii ...gggnnnnnttttt -- SVE 32-bit gather load (vector plus immediate) case IF_SVE_IV_3A: // ...........iiiii ...gggnnnnnttttt -- SVE 64-bit gather load (vector plus immediate) diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index cd4b3bc9738198..3fa81419e96048 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -53,6 +53,7 @@ void emitDispExtendOpts(insOpts opt); void emitDispSveExtendOpts(insOpts opt); void emitDispSveExtendOptsModN(insOpts opt, int n); void emitDispSveModAddr(instruction ins, regNumber reg1, regNumber reg2, insOpts opt, insFormat fmt); +void emitDispSveImm(regNumber reg1, ssize_t imm, insOpts opt); void emitDispSveImmMulVl(regNumber reg1, ssize_t imm); void emitDispSveImmIndex(regNumber reg1, insOpts opt, ssize_t imm); void emitDispLSExtendOpts(insOpts opt); @@ -75,6 +76,7 @@ void emitDispExtendReg(regNumber reg, insOpts opt, ssize_t imm); void emitDispAddrRI(regNumber reg, insOpts opt, ssize_t imm); void emitDispAddrRRExt(regNumber reg1, regNumber reg2, insOpts opt, bool isScaled, emitAttr size); void emitDispSvePattern(insSvePattern pattern, bool addComma); +void emitDispSvePrfop(insSvePrfop prfop, bool addComma); /************************************************************************/ /* Private members that deal with target-dependent instr. descriptors */ @@ -619,6 +621,9 @@ static code_t insEncodeUimm5_MultipleOf4_20_to_16(ssize_t imm); // Returns the encoding for the immediate value that is a multiple of 8 as 5-bits at bit locations '20-16'. static code_t insEncodeUimm5_MultipleOf8_20_to_16(ssize_t imm); +// Returns the encoding for the immediate value as 6-bits at bit locations '21-16'. +static code_t insEncodeSimm6_21_to_16(ssize_t imm); + // Returns the encoding for the immediate value that is a multiple of 2 as 6-bits at bit locations '21-16'. static code_t insEncodeUimm6_MultipleOf2_21_to_16(ssize_t imm); @@ -735,19 +740,19 @@ static bool isValidSimm4_MultipleOf32(ssize_t value) return (-256 <= value) && (value <= 224) && (value % 32 == 0); }; -// Returns true if 'value' is a legal signed multiple of 2 immediate 5 bit encoding (such as for LD1H). +// Returns true if 'value' is a legal unsigned multiple of 2 immediate 5 bit encoding (such as for LD1H). static bool isValidUimm5_MultipleOf2(ssize_t value) { return (0 <= value) && (value <= 62) && (value % 2 == 0); }; -// Returns true if 'value' is a legal signed multiple of 4 immediate 5 bit encoding (such as for LD1W). +// Returns true if 'value' is a legal unsigned multiple of 4 immediate 5 bit encoding (such as for LD1W). static bool isValidUimm5_MultipleOf4(ssize_t value) { return (0 <= value) && (value <= 124) && (value % 4 == 0); }; -// Returns true if 'value' is a legal signed multiple of 8 immediate 5 bit encoding (such as for LD1D). +// Returns true if 'value' is a legal unsigned multiple of 8 immediate 5 bit encoding (such as for LD1D). static bool isValidUimm5_MultipleOf8(ssize_t value) { return (0 <= value) && (value <= 248) && (value % 8 == 0); @@ -879,6 +884,12 @@ static bool isValidSimm5(ssize_t value) return (-0x10LL <= value) && (value <= 0xFLL); }; +// Returns true if 'value' is a legal signed immediate 6 bit encoding (such as for PRFB). +static bool isValidSimm6(ssize_t value) +{ + return (-32 <= value) && (value <= 31); +}; + // Returns true if 'value' is a legal rotation value (such as for CDOT, CMLA). static bool isValidRot(ssize_t value) { @@ -1508,6 +1519,23 @@ void emitIns_R_PATTERN( void emitIns_R_PATTERN_I(instruction ins, emitAttr attr, regNumber reg1, insSvePattern pattern, int imm); +void emitIns_PRFOP_R_R_R(instruction ins, + emitAttr attr, + insSvePrfop prfop, + regNumber reg1, + regNumber reg2, + regNumber reg3, + insOpts opt = INS_OPTS_NONE, + insScalableOpts sopt = INS_SCALABLE_OPTS_NONE); + +void emitIns_PRFOP_R_R_I(instruction ins, + emitAttr attr, + insSvePrfop prfop, + regNumber reg1, + regNumber reg2, + int imm, + insOpts opt = INS_OPTS_NONE); + void emitIns_BARR(instruction ins, insBarrier barrier); void emitIns_C(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fdlHnd, int offs); diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index bdbddc60160cf0..3120b2ac87fc60 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -404,6 +404,28 @@ enum insSvePattern : unsigned SVE_PATTERN_ALL = 31 // All available (implicitly a multiple of two). }; +// Prefetch operation specifier for SVE instructions such as prfb. +enum insSvePrfop : unsigned +{ + SVE_PRFOP_PLDL1KEEP = 0b0000, + SVE_PRFOP_PLDL1STRM = 0b0001, + SVE_PRFOP_PLDL2KEEP = 0b0010, + SVE_PRFOP_PLDL2STRM = 0b0011, + SVE_PRFOP_PLDL3KEEP = 0b0100, + SVE_PRFOP_PLDL3STRM = 0b0101, + SVE_PRFOP_PSTL1KEEP = 0b1000, + SVE_PRFOP_PSTL1STRM = 0b1001, + SVE_PRFOP_PSTL2KEEP = 0b1010, + SVE_PRFOP_PSTL2STRM = 0b1011, + SVE_PRFOP_PSTL3KEEP = 0b1100, + SVE_PRFOP_PSTL3STRM = 0b1101, + + SVE_PRFOP_CONST6 = 0b0110, + SVE_PRFOP_CONST7 = 0b0111, + SVE_PRFOP_CONST14 = 0b1110, + SVE_PRFOP_CONST15 = 0b1111 +}; + enum insCond : unsigned { INS_COND_EQ, diff --git a/src/coreclr/jit/instrsarm64sve.h b/src/coreclr/jit/instrsarm64sve.h index 0c0a4019237149..9e385bca8d7db0 100644 --- a/src/coreclr/jit/instrsarm64sve.h +++ b/src/coreclr/jit/instrsarm64sve.h @@ -285,7 +285,7 @@ INST6(prfb, "prfb", 0, IF_SV // PRFB , , [, .S, ] SVE_HY_3A 100001000h1mmmmm 000gggnnnnn0oooo 8420 0000 // PRFB , , [, .D, ] SVE_HY_3A_A 110001000h1mmmmm 000gggnnnnn0oooo C420 0000 // PRFB , , [, .D] SVE_HY_3B 11000100011mmmmm 100gggnnnnn0oooo C460 8000 - // PRFB , , [.D{, #}] SVE_HZ_2A_B 10000100000iiiii 111gggnnnnn0oooo 8400 E000 + // PRFB , , [.S{, #}] SVE_HZ_2A_B 10000100000iiiii 111gggnnnnn0oooo 8400 E000 // PRFB , , [{, #, MUL VL}] SVE_IA_2A 1000010111iiiiii 000gggnnnnn0oooo 85C0 0000 // PRFB , , [, ] SVE_IB_3A 10000100000mmmmm 110gggnnnnn0oooo 8400 C000 @@ -293,7 +293,7 @@ INST6(prfd, "prfd", 0, IF_SV // PRFD , , [, .S, #3] SVE_HY_3A 100001000h1mmmmm 011gggnnnnn0oooo 8420 6000 // PRFD , , [, .D, #3] SVE_HY_3A_A 110001000h1mmmmm 011gggnnnnn0oooo C420 6000 // PRFD , , [, .D, LSL #3] SVE_HY_3B 11000100011mmmmm 111gggnnnnn0oooo C460 E000 - // PRFD , , [.D{, #}] SVE_HZ_2A_B 10000101100iiiii 111gggnnnnn0oooo 8580 E000 + // PRFD , , [.S{, #}] SVE_HZ_2A_B 10000101100iiiii 111gggnnnnn0oooo 8580 E000 // PRFD , , [{, #, MUL VL}] SVE_IA_2A 1000010111iiiiii 011gggnnnnn0oooo 85C0 6000 // PRFD , , [, , LSL #3] SVE_IB_3A 10000101100mmmmm 110gggnnnnn0oooo 8580 C000 @@ -301,7 +301,7 @@ INST6(prfh, "prfh", 0, IF_SV // PRFH , , [, .S, #1] SVE_HY_3A 100001000h1mmmmm 001gggnnnnn0oooo 8420 2000 // PRFH , , [, .D, #1] SVE_HY_3A_A 110001000h1mmmmm 001gggnnnnn0oooo C420 2000 // PRFH , , [, .D, LSL #1] SVE_HY_3B 11000100011mmmmm 101gggnnnnn0oooo C460 A000 - // PRFH , , [.D{, #}] SVE_HZ_2A_B 10000100100iiiii 111gggnnnnn0oooo 8480 E000 + // PRFH , , [.S{, #}] SVE_HZ_2A_B 10000100100iiiii 111gggnnnnn0oooo 8480 E000 // PRFH , , [{, #, MUL VL}] SVE_IA_2A 1000010111iiiiii 001gggnnnnn0oooo 85C0 2000 // PRFH , , [, , LSL #1] SVE_IB_3A 10000100100mmmmm 110gggnnnnn0oooo 8480 C000 @@ -309,7 +309,7 @@ INST6(prfw, "prfw", 0, IF_SV // PRFW , , [, .S, #2] SVE_HY_3A 100001000h1mmmmm 010gggnnnnn0oooo 8420 4000 // PRFW , , [, .D, #2] SVE_HY_3A_A 110001000h1mmmmm 010gggnnnnn0oooo C420 4000 // PRFW , , [, .D, LSL #2] SVE_HY_3B 11000100011mmmmm 110gggnnnnn0oooo C460 C000 - // PRFW , , [.D{, #}] SVE_HZ_2A_B 10000101000iiiii 111gggnnnnn0oooo 8500 E000 + // PRFW , , [.S{, #}] SVE_HZ_2A_B 10000101000iiiii 111gggnnnnn0oooo 8500 E000 // PRFW , , [{, #, MUL VL}] SVE_IA_2A 1000010111iiiiii 010gggnnnnn0oooo 85C0 4000 // PRFW , , [, , LSL #2] SVE_IB_3A 10000101000mmmmm 110gggnnnnn0oooo 8500 C000 From b8d5346340bde0fee93dd67c3ff0d28b57159acf Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Tue, 20 Feb 2024 16:49:03 -0800 Subject: [PATCH 147/158] [wasm] pinvoke improvements part 2 (#98250) * InlineArray is supported in wasm pinvokes, and in general works correctly in mono marshaling now (it was broken on all targets) * fixed arrays are supported in wasm pinvokes *struct arguments and return values are supported in more wasm pinvoke scenarios * wasm struct scalarization is more intelligent about noticing padding and size mismatches in structs (without this, some inlinearrays would be scalarized incorrectly) * Prevent infinite recursion in build task's TypeToChar * expand WBT coverage --- src/mono/mono/metadata/class-init.c | 1 + src/mono/mono/metadata/icall.c | 2 +- src/mono/mono/metadata/marshal.c | 23 +++++--- src/mono/mono/mini/aot-runtime-wasm.c | 53 +++++++++++++----- src/mono/mono/mini/interp/interp.c | 54 ++++++++++++++++--- src/mono/mono/mini/mini-wasm.c | 17 ++++-- .../PInvokeTableGeneratorTests.cs | 51 +++++++++++++++++- .../wasm/testassets/native-libs/wasm-abi.c | 42 +++++++++++++++ .../WasmAppBuilder/PInvokeTableGenerator.cs | 11 +++- src/tasks/WasmAppBuilder/SignatureMapper.cs | 35 ++++++++---- 10 files changed, 248 insertions(+), 41 deletions(-) diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c index 484eedb9038f17..8e064696fe7623 100644 --- a/src/mono/mono/metadata/class-init.c +++ b/src/mono/mono/metadata/class-init.c @@ -2272,6 +2272,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_ } size = mono_type_size (field->type, &align); + // keep in sync with marshal.c mono_marshal_load_type_info if (m_class_is_inlinearray (klass)) { // Limit the max size of array instance to 1MiB const guint32 struct_max_size = 1024 * 1024; diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 9c3ea1171106e3..3be82b80e03af2 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -3028,7 +3028,7 @@ ves_icall_RuntimeType_GetNamespace (MonoQCallTypeHandle type_handle, MonoObjectH MonoClass *klass = mono_class_from_mono_type_internal (type); MonoClass *elem; - while (!m_class_is_enumtype (klass) && + while (!m_class_is_enumtype (klass) && !mono_class_is_nullable (klass) && (klass != (elem = m_class_get_element_class (klass)))) klass = elem; diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index c57fa32127a9f7..1b7d0b4310a2bc 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -3294,7 +3294,7 @@ mono_emit_marshal (EmitMarshalContext *m, int argnum, MonoType *t, return mono_emit_disabled_marshal (m, argnum, t, spec, conv_arg, conv_arg_type, action); return mono_component_marshal_ilgen()->emit_marshal_ilgen(m, argnum, t, spec, conv_arg, conv_arg_type, action, get_marshal_cb()); -} +} static void mono_marshal_set_callconv_for_type(MonoType *type, MonoMethodSignature *csig, gboolean *skip_gc_trans /*out*/) @@ -3577,7 +3577,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, if (G_UNLIKELY (pinvoke && mono_method_has_unmanaged_callers_only_attribute (method))) { /* - * In AOT mode and embedding scenarios, it is possible that the icall is not registered in the runtime doing the AOT compilation. + * In AOT mode and embedding scenarios, it is possible that the icall is not registered in the runtime doing the AOT compilation. * Emit a wrapper that throws a NotSupportedException. */ get_marshal_cb ()->mb_emit_exception (mb, "System", "NotSupportedException", "Method canot be marked with both DllImportAttribute and UnmanagedCallersOnlyAttribute"); @@ -3757,7 +3757,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, } goto leave; - + emit_exception_for_error: mono_error_cleanup (emitted_error); info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); @@ -5231,7 +5231,7 @@ mono_marshal_get_unsafe_accessor_wrapper (MonoMethod *accessor_method, MonoUnsaf if (member_name == NULL && kind != MONO_UNSAFE_ACCESSOR_CTOR) member_name = accessor_method->name; - + /* * Check cache */ @@ -5827,11 +5827,20 @@ mono_marshal_load_type_info (MonoClass* klass) continue; } + size = mono_marshal_type_size (field->type, info->fields [j].mspec, + &align, TRUE, m_class_is_unicode (klass)); + + // Keep in sync with class-init.c mono_class_layout_fields + if (m_class_is_inlinearray (klass)) { + // Limit the max size of array instance to 1MiB + const int struct_max_size = 1024 * 1024; + size *= m_class_inlinearray_value (klass); + g_assert ((size > 0) && (size <= struct_max_size)); + } + switch (layout) { case TYPE_ATTRIBUTE_AUTO_LAYOUT: case TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT: - size = mono_marshal_type_size (field->type, info->fields [j].mspec, - &align, TRUE, m_class_is_unicode (klass)); align = m_class_get_packing_size (klass) ? MIN (m_class_get_packing_size (klass), align): align; min_align = MAX (align, min_align); info->fields [j].offset = info->native_size; @@ -5840,8 +5849,6 @@ mono_marshal_load_type_info (MonoClass* klass) info->native_size = info->fields [j].offset + size; break; case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT: - size = mono_marshal_type_size (field->type, info->fields [j].mspec, - &align, TRUE, m_class_is_unicode (klass)); min_align = MAX (align, min_align); info->fields [j].offset = m_field_get_offset (field) - MONO_ABI_SIZEOF (MonoObject); info->native_size = MAX (info->native_size, info->fields [j].offset + size); diff --git a/src/mono/mono/mini/aot-runtime-wasm.c b/src/mono/mono/mini/aot-runtime-wasm.c index cf1ab02392934d..30fde73c155bd5 100644 --- a/src/mono/mono/mini/aot-runtime-wasm.c +++ b/src/mono/mono/mini/aot-runtime-wasm.c @@ -15,8 +15,12 @@ #ifdef HOST_WASM static char -type_to_c (MonoType *t) +type_to_c (MonoType *t, gboolean *is_byref_return) { + g_assert (t); + + if (is_byref_return) + *is_byref_return = 0; if (m_type_is_byref (t)) return 'I'; @@ -48,7 +52,7 @@ type_to_c (MonoType *t) return 'L'; case MONO_TYPE_VOID: return 'V'; - case MONO_TYPE_VALUETYPE: + case MONO_TYPE_VALUETYPE: { if (m_class_is_enumtype (t->data.klass)) { t = mono_class_enum_basetype_internal (t->data.klass); goto handle_enum; @@ -60,13 +64,27 @@ type_to_c (MonoType *t) // FIXME: Handle the scenario where there are fields of struct types that contain no members MonoType *scalar_vtype; if (mini_wasm_is_scalar_vtype (t, &scalar_vtype)) - return type_to_c (scalar_vtype); + return type_to_c (scalar_vtype, NULL); + + if (is_byref_return) + *is_byref_return = 1; return 'I'; - case MONO_TYPE_GENERICINST: - if (m_class_is_valuetype (t->data.klass)) + } + case MONO_TYPE_GENERICINST: { + if (m_class_is_valuetype (t->data.klass)) { + MonoType *scalar_vtype; + if (mini_wasm_is_scalar_vtype (t, &scalar_vtype)) + return type_to_c (scalar_vtype, NULL); + + if (is_byref_return) + *is_byref_return = 1; + return 'S'; + } + return 'I'; + } default: g_warning ("CANT TRANSLATE %s", mono_type_full_name (t)); return 'X'; @@ -140,18 +158,29 @@ gpointer mono_wasm_get_interp_to_native_trampoline (MonoMethodSignature *sig) { char cookie [32]; - int c_count; + int c_count, offset = 1; + gboolean is_byref_return = 0; + + memset (cookie, 0, 32); + cookie [0] = type_to_c (sig->ret, &is_byref_return); - c_count = sig->param_count + sig->hasthis + 1; + c_count = sig->param_count + sig->hasthis + is_byref_return + 1; g_assert (c_count < sizeof (cookie)); //ensure we don't overflow the local - cookie [0] = type_to_c (sig->ret); - if (sig->hasthis) - cookie [1] = 'I'; + if (is_byref_return) { + cookie[0] = 'V'; + // return value address goes in arg0 + cookie[1] = 'I'; + offset += 1; + } + if (sig->hasthis) { + // thisptr goes in arg0/arg1 depending on return type + cookie [offset] = 'I'; + offset += 1; + } for (int i = 0; i < sig->param_count; ++i) { - cookie [1 + sig->hasthis + i] = type_to_c (sig->params [i]); + cookie [offset + i] = type_to_c (sig->params [i], NULL); } - cookie [c_count] = 0; void *p = mono_wasm_interp_to_native_callback (cookie); if (!p) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 411d4f1e6b3db0..c16afc494011fd 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1340,7 +1340,10 @@ typedef enum { PINVOKE_ARG_R8 = 3, PINVOKE_ARG_R4 = 4, PINVOKE_ARG_VTYPE = 5, - PINVOKE_ARG_SCALAR_VTYPE = 6 + PINVOKE_ARG_SCALAR_VTYPE = 6, + // This isn't ifdefed so it's easier to write code that handles it without sprinkling + // 800 ifdefs in this file + PINVOKE_ARG_WASM_VALUETYPE_RESULT = 7, } PInvokeArgType; typedef struct { @@ -1436,6 +1439,7 @@ get_build_args_from_sig_info (MonoMemoryManager *mem_manager, MonoMethodSignatur ilen++; break; case MONO_TYPE_GENERICINST: { + // FIXME: Should mini_wasm_is_scalar_vtype stuff go in here? MonoClass *container_class = type->data.generic_class->container_class; type = m_class_get_byval_arg (container_class); goto retry; @@ -1473,11 +1477,32 @@ get_build_args_from_sig_info (MonoMemoryManager *mem_manager, MonoMethodSignatur case MONO_TYPE_CLASS: case MONO_TYPE_OBJECT: case MONO_TYPE_STRING: + info->ret_pinvoke_type = PINVOKE_ARG_INT; + break; +#if SIZEOF_VOID_P == 8 + case MONO_TYPE_I8: + case MONO_TYPE_U8: +#endif + info->ret_pinvoke_type = PINVOKE_ARG_INT; + break; +#if SIZEOF_VOID_P == 4 case MONO_TYPE_I8: case MONO_TYPE_U8: + info->ret_pinvoke_type = PINVOKE_ARG_INT; + break; +#endif case MONO_TYPE_VALUETYPE: case MONO_TYPE_GENERICINST: info->ret_pinvoke_type = PINVOKE_ARG_INT; +#ifdef HOST_WASM + // This ISSTRUCT check is important, because the type could be an enum + if (MONO_TYPE_ISSTRUCT (info->ret_mono_type)) { + // The return type was already filtered previously, so if we get here + // we're returning a struct byref instead of as a scalar + info->ret_pinvoke_type = PINVOKE_ARG_WASM_VALUETYPE_RESULT; + info->ilen++; + } +#endif break; case MONO_TYPE_R4: case MONO_TYPE_R8: @@ -1503,6 +1528,15 @@ build_args_from_sig (InterpMethodArguments *margs, MonoMethodSignature *sig, Bui margs->ilen = info->ilen; margs->flen = info->flen; + size_t int_i = 0; + size_t int_f = 0; + + if (info->ret_pinvoke_type == PINVOKE_ARG_WASM_VALUETYPE_RESULT) { + // Allocate an empty arg0 for the address of the return value + // info->ilen was already increased earlier + int_i++; + } + if (margs->ilen > 0) { if (margs->ilen <= 8) margs->iargs = margs->iargs_buf; @@ -1517,9 +1551,6 @@ build_args_from_sig (InterpMethodArguments *margs, MonoMethodSignature *sig, Bui margs->fargs = g_malloc0 (sizeof (double) * margs->flen); } - size_t int_i = 0; - size_t int_f = 0; - for (int i = 0; i < sig->param_count; i++) { guint32 offset = get_arg_offset (frame->imethod, sig, i); stackval *sp_arg = STACK_ADD_BYTES (frame->stack, offset); @@ -1578,6 +1609,15 @@ build_args_from_sig (InterpMethodArguments *margs, MonoMethodSignature *sig, Bui } switch (info->ret_pinvoke_type) { + case PINVOKE_ARG_WASM_VALUETYPE_RESULT: + // We pass the return value address in arg0 so fill it in, we already + // reserved space for it earlier. + g_assert (frame->retval); + margs->iargs[0] = (gpointer*)frame->retval; + // The return type is void so retval should be NULL + margs->retval = NULL; + margs->is_float_ret = 0; + break; case PINVOKE_ARG_INT: margs->retval = (gpointer*)frame->retval; margs->is_float_ret = 0; @@ -1795,8 +1835,10 @@ ves_pinvoke_method ( g_free (ccontext.stack); #else // Only the vt address has been returned, we need to copy the entire content on interp stack - if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (call_info->ret_mono_type)) - stackval_from_data (call_info->ret_mono_type, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled); + if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (call_info->ret_mono_type)) { + if (call_info->ret_pinvoke_type != PINVOKE_ARG_WASM_VALUETYPE_RESULT) + stackval_from_data (call_info->ret_mono_type, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled); + } if (margs.iargs != margs.iargs_buf) g_free (margs.iargs); diff --git a/src/mono/mono/mini/mini-wasm.c b/src/mono/mono/mini/mini-wasm.c index 0a80ab43ba9c47..991135288f63de 100644 --- a/src/mono/mono/mini/mini-wasm.c +++ b/src/mono/mono/mini/mini-wasm.c @@ -75,17 +75,23 @@ get_storage (MonoType *type, MonoType **etype, gboolean is_return) case MONO_TYPE_R8: return ArgOnStack; - case MONO_TYPE_GENERICINST: + case MONO_TYPE_GENERICINST: { if (!mono_type_generic_inst_is_valuetype (type)) return ArgOnStack; if (mini_is_gsharedvt_variable_type (type)) return ArgGsharedVTOnStack; - /* fall through */ + + if (mini_wasm_is_scalar_vtype (type, etype)) + return ArgVtypeAsScalar; + + return is_return ? ArgValuetypeAddrInIReg : ArgValuetypeAddrOnStack; + } case MONO_TYPE_VALUETYPE: case MONO_TYPE_TYPEDBYREF: { if (mini_wasm_is_scalar_vtype (type, etype)) return ArgVtypeAsScalar; + return is_return ? ArgValuetypeAddrInIReg : ArgValuetypeAddrOnStack; } case MONO_TYPE_VAR: @@ -771,7 +777,12 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype) if (nfields > 1) return FALSE; MonoType *t = mini_get_underlying_type (field->type); - if (MONO_TYPE_ISSTRUCT (t)) { + int align, field_size = mono_type_size (t, &align); + // inlinearray and fixed both work by having a single field that is bigger than its element type. + // we also don't want to scalarize a struct that has padding in its metadata, even if it would fit. + if (field_size != size) { + return FALSE; + } else if (MONO_TYPE_ISSTRUCT (t)) { if (!mini_wasm_is_scalar_vtype (t, etype)) return FALSE; } else if (!((MONO_TYPE_IS_PRIMITIVE (t) || MONO_TYPE_IS_REFERENCE (t) || MONO_TYPE_IS_POINTER (t)))) { diff --git a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index 1d7a9740eba9e3..8c0442a1b0d680 100644 --- a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -739,6 +739,16 @@ public struct Nested1 { public struct SingleI64Struct { public Int64 Value; } + public struct PairStruct { + public int A, B; + } + public unsafe struct MyFixedArray { + public fixed int elements[2]; + } + [System.Runtime.CompilerServices.InlineArray(2)] + public struct MyInlineArray { + public int element0; + } public class Test { @@ -765,9 +775,35 @@ public static unsafe int Main(string[] argv) var res = indirect(sds); Console.WriteLine(""s (s)="" + res.Value); + var pair = new PairStruct { A = 1, B = 2 }; + var paires = accept_and_return_pair(pair); + Console.WriteLine(""paires.B="" + paires.B); + + // This test is split into methods to simplify debugging issues with it + var ia = InlineArrayTest1(); + var iares = InlineArrayTest2(ia); + Console.WriteLine($""iares[0]={iares[0]} iares[1]={iares[1]}""); + + MyFixedArray fa = new (); + for (int i = 0; i < 2; i++) + fa.elements[i] = i; + var fares = accept_and_return_fixedarray(fa); + Console.WriteLine(""fares.elements[1]="" + fares.elements[1]); + return (int)res.Value; } + public static unsafe MyInlineArray InlineArrayTest1 () { + MyInlineArray ia = new (); + for (int i = 0; i < 2; i++) + ia[i] = i; + return ia; + } + + public static unsafe MyInlineArray InlineArrayTest2 (MyInlineArray ia) { + return accept_and_return_inlinearray(ia); + } + [DllImport(""wasm-abi"", EntryPoint=""accept_double_struct_and_return_float_struct"")] public static extern SingleFloatStruct indirect(SingleDoubleStruct arg); @@ -782,9 +818,18 @@ public static unsafe int Main(string[] argv) [DllImport(""wasm-abi"", EntryPoint=""accept_and_return_i64_struct"")] public static extern Int64 direct64(Int64 arg); + + [DllImport(""wasm-abi"", EntryPoint=""accept_and_return_pair"")] + public static extern PairStruct accept_and_return_pair(PairStruct arg); + + [DllImport(""wasm-abi"", EntryPoint=""accept_and_return_fixedarray"")] + public static extern MyFixedArray accept_and_return_fixedarray(MyFixedArray arg); + + [DllImport(""wasm-abi"", EntryPoint=""accept_and_return_inlinearray"")] + public static extern MyInlineArray accept_and_return_inlinearray(MyInlineArray arg); }"; - var extraProperties = "true<_WasmDevel>true"; + var extraProperties = "true<_WasmDevel>falsefalse"; var extraItems = @""; buildArgs = ExpandBuildArgs(buildArgs, @@ -824,6 +869,10 @@ public static unsafe int Main(string[] argv) Assert.Contains("f (d)=3.14", runOutput); Assert.Contains("f (s)=3.14", runOutput); Assert.Contains("s (s)=3.14", runOutput); + Assert.Contains("paires.B=4", runOutput); + Assert.Contains("iares[0]=32", runOutput); + Assert.Contains("iares[1]=2", runOutput); + Assert.Contains("fares.elements[1]=2", runOutput); } [Theory] diff --git a/src/mono/wasm/testassets/native-libs/wasm-abi.c b/src/mono/wasm/testassets/native-libs/wasm-abi.c index 0ace2037daf2f9..083bce6abe0c59 100644 --- a/src/mono/wasm/testassets/native-libs/wasm-abi.c +++ b/src/mono/wasm/testassets/native-libs/wasm-abi.c @@ -1,5 +1,7 @@ #include +#define TRACING 0 + typedef struct { float value; } TRes; @@ -7,10 +9,12 @@ typedef struct { TRes accept_double_struct_and_return_float_struct ( struct { struct { double value; } value; } arg ) { +#if TRACING printf ( "&arg=%x (ulonglong)arg=%llx arg.value.value=%lf\n", (unsigned int)&arg, *(unsigned long long*)&arg, (double)arg.value.value ); +#endif TRes result = { arg.value.value }; return result; } @@ -20,10 +24,48 @@ typedef struct { } TResI64; TResI64 accept_and_return_i64_struct (TResI64 arg) { +#if TRACING printf ( "&arg=%x (ulonglong)arg=%llx\n", (unsigned int)&arg, *(unsigned long long*)&arg ); +#endif TResI64 result = { ~arg.value }; return result; } + +typedef struct { + int A, B; +} PairStruct; + +PairStruct accept_and_return_pair (PairStruct arg) { +#if TRACING + printf ( + "&arg=%d arg.A=%d arg.B=%d\n", + (unsigned int)&arg, arg.A, arg.B + ); +#endif + arg.A = 32; + arg.B *= 2; + return arg; +} + +typedef struct { + int elements[2]; +} MyInlineArray; + +MyInlineArray accept_and_return_inlinearray (MyInlineArray arg) { +#if TRACING + printf ( + "&arg=%d arg.elements[0]=%d arg.elements[1]=%d\n", + (unsigned int)&arg, arg.elements[0], arg.elements[1] + ); +#endif + arg.elements[0] = 32; + arg.elements[1] *= 2; + return arg; +} + +MyInlineArray accept_and_return_fixedarray (MyInlineArray arg) { + return accept_and_return_inlinearray (arg); +} diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index cd8535463bc349..e4aa070d88ea38 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -269,10 +269,19 @@ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotN return null; } + var realReturnType = method.ReturnType; + var realParameterTypes = method.GetParameters().Select(p => MapType(p.ParameterType)).ToList(); + + SignatureMapper.TypeToChar(realReturnType, Log, out bool resultIsByRef); + if (resultIsByRef) { + realReturnType = typeof(void); + realParameterTypes.Insert(0, "void *"); + } + return $$""" {{(pinvoke.WasmLinkage ? $"__attribute__((import_module(\"{EscapeLiteral(pinvoke.Module)}\"),import_name(\"{EscapeLiteral(pinvoke.EntryPoint)}\")))" : "")}} - {{(pinvoke.WasmLinkage ? "extern " : "")}}{{MapType(method.ReturnType)}} {{CEntryPoint(pinvoke)}} ({{string.Join(", ", method.GetParameters().Select(p => MapType(p.ParameterType)))}}); + {{(pinvoke.WasmLinkage ? "extern " : "")}}{{MapType(realReturnType)}} {{CEntryPoint(pinvoke)}} ({{string.Join(", ", realParameterTypes)}}); """; } diff --git a/src/tasks/WasmAppBuilder/SignatureMapper.cs b/src/tasks/WasmAppBuilder/SignatureMapper.cs index f3b7f17ad017be..3638e432f0ce38 100644 --- a/src/tasks/WasmAppBuilder/SignatureMapper.cs +++ b/src/tasks/WasmAppBuilder/SignatureMapper.cs @@ -11,8 +11,15 @@ internal static class SignatureMapper { - private static char? TypeToChar(Type t, LogAdapter log) + internal static char? TypeToChar(Type t, LogAdapter log, out bool isByRefStruct, int depth = 0) { + isByRefStruct = false; + + if (depth > 5) { + log.Warning("WASM0064", $"Unbounded recursion detected through parameter type '{t.Name}'"); + return null; + } + char? c = null; if (t.Namespace == "System") { c = t.Name switch @@ -20,6 +27,7 @@ internal static class SignatureMapper nameof(String) => 'I', nameof(Boolean) => 'I', nameof(Char) => 'I', + nameof(SByte) => 'I', nameof(Byte) => 'I', nameof(Int16) => 'I', nameof(UInt16) => 'I', @@ -51,19 +59,23 @@ internal static class SignatureMapper c = 'I'; else if (t.IsInterface) c = 'I'; - else if (t.IsEnum) - c = TypeToChar(t.GetEnumUnderlyingType(), log); - else if (t.IsPointer) + else if (t.IsEnum) { + Type underlyingType = t.GetEnumUnderlyingType(); + c = TypeToChar(underlyingType, log, out _, ++depth); + } else if (t.IsPointer) c = 'I'; else if (PInvokeTableGenerator.IsFunctionPointer(t)) c = 'I'; else if (t.IsValueType) { var fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - if (fields.Length == 1) - return TypeToChar(fields[0].FieldType, log); - else if (PInvokeTableGenerator.IsBlittable(t, log)) + if (fields.Length == 1) { + Type fieldType = fields[0].FieldType; + return TypeToChar(fieldType, log, out isByRefStruct, ++depth); + } else if (PInvokeTableGenerator.IsBlittable(t, log)) c = 'I'; + + isByRefStruct = true; } else log.Warning("WASM0064", $"Unsupported parameter type '{t.Name}'"); @@ -74,15 +86,20 @@ internal static class SignatureMapper public static string? MethodToSignature(MethodInfo method, LogAdapter log) { - string? result = TypeToChar(method.ReturnType, log)?.ToString(); + string? result = TypeToChar(method.ReturnType, log, out bool resultIsByRef)?.ToString(); if (result == null) { return null; } + if (resultIsByRef) { + // WASM abi passes a result-pointer in slot 0 instead of returning struct results + result = "VI"; + } + foreach (var parameter in method.GetParameters()) { - char? parameterChar = TypeToChar(parameter.ParameterType, log); + char? parameterChar = TypeToChar(parameter.ParameterType, log, out _); if (parameterChar == null) { return null; From 9c481caba96e4a6608f0d37ca71bf3c4a580bdbd Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:16:05 -0600 Subject: [PATCH 148/158] [main] Update dependencies from dotnet/emsdk (#98555) * Update dependencies from https://github.com/dotnet/emsdk build 20240215.1 Microsoft.SourceBuild.Intermediate.emsdk , Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-preview.2.24114.5 -> To Version 9.0.0-preview.2.24115.1 * Update dependencies from https://github.com/dotnet/emsdk build 20240215.1 Microsoft.SourceBuild.Intermediate.emsdk , Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-preview.2.24114.5 -> To Version 9.0.0-preview.2.24115.1 * Update dependencies from https://github.com/dotnet/emsdk build 20240215.1 Microsoft.SourceBuild.Intermediate.emsdk , Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-preview.2.24114.5 -> To Version 9.0.0-preview.2.24115.1 * Update dependencies from https://github.com/dotnet/emsdk build 20240215.1 Microsoft.SourceBuild.Intermediate.emsdk , Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-preview.2.24114.5 -> To Version 9.0.0-preview.2.24115.1 * Update dependencies from https://github.com/dotnet/emsdk build 20240219.1 Microsoft.SourceBuild.Intermediate.emsdk , Microsoft.NET.Runtime.Emscripten.3.1.34.Python.win-x64 , Microsoft.NET.Workload.Emscripten.Current.Manifest-9.0.100.Transport From Version 9.0.0-preview.2.24114.5 -> To Version 9.0.0-preview.2.24119.1 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 12 ++++++------ eng/Versions.props | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 23e747338afb79..50d5b70ef30021 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -12,9 +12,9 @@ https://github.com/dotnet/wcf 7f504aabb1988e9a093c1e74d8040bd52feb2f01 - + https://github.com/dotnet/emsdk - 8afd92448d03a80001c9cac5f2acb53b336263a4 + 11d59e39f4d3552fc1e37ad07e4a32f98c3d06af https://github.com/dotnet/llvm-project @@ -100,14 +100,14 @@ ca7e93445acbd94bfa696c16fa039f2a6130f2cb - + https://github.com/dotnet/emsdk - 8afd92448d03a80001c9cac5f2acb53b336263a4 + 11d59e39f4d3552fc1e37ad07e4a32f98c3d06af - + https://github.com/dotnet/emsdk - 8afd92448d03a80001c9cac5f2acb53b336263a4 + 11d59e39f4d3552fc1e37ad07e4a32f98c3d06af diff --git a/eng/Versions.props b/eng/Versions.props index bb1645abaf2b4c..5b4be0e1f552c1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -243,9 +243,9 @@ Note: when the name is updated, make sure to update dependency name in eng/pipelines/common/xplat-setup.yml like - DarcDependenciesChanged.Microsoft_NET_Workload_Emscripten_Current_Manifest-9_0_100_Transport --> - 9.0.0-preview.2.24114.5 + 9.0.0-preview.2.24119.1 $(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportVersion) - 9.0.0-preview.2.24114.5 + 9.0.0-preview.2.24119.1 1.1.87-gba258badda 1.0.0-v3.14.0.5722 From 1812df0ca8eb8bd5618d3e08a5201f27e2532869 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:17:30 -0600 Subject: [PATCH 149/158] [main] Update dependencies from dotnet/roslyn-analyzers (#98556) * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240215.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24109.1 -> To Version 3.11.0-beta1.24115.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240216.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24109.1 -> To Version 3.11.0-beta1.24116.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240217.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24109.1 -> To Version 3.11.0-beta1.24117.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240217.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24109.1 -> To Version 3.11.0-beta1.24117.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20240217.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.11.0-beta1.24109.1 -> To Version 3.11.0-beta1.24117.1 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 50d5b70ef30021..3c1a47cabf4227 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -405,13 +405,13 @@ https://github.com/dotnet/roslyn 77372c66fd54927312b5b0a2e399e192f74445c9 - + https://github.com/dotnet/roslyn-analyzers - 68c643b4667c6808bd21910ef32f7e2f7bd776c5 + ab13ac75d1195a27e3cf7fb990fc3a51615caa95 - + https://github.com/dotnet/roslyn-analyzers - 68c643b4667c6808bd21910ef32f7e2f7bd776c5 + ab13ac75d1195a27e3cf7fb990fc3a51615caa95 diff --git a/eng/Versions.props b/eng/Versions.props index 5b4be0e1f552c1..ae857e8494b35b 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -34,8 +34,8 @@ - 3.11.0-beta1.24109.1 - 9.0.0-preview.24109.1 + 3.11.0-beta1.24117.1 + 9.0.0-preview.24117.1 - 9.0.100-preview.2.24112.1 + 9.0.100-preview.2.24116.21 $(MicrosoftDotnetSdkInternalVersion) From 93bbc3e73fbcdaa1a2e7b172ac2b46500ecd8841 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:24:45 -0600 Subject: [PATCH 151/158] [main] Update dependencies from dotnet/cecil, dotnet/hotreload-utils, dotnet/icu, dotnet/runtime, dotnet/runtime-assets, dotnet/sdk, dotnet/xharness (#98655) * Update dependencies from https://github.com/dotnet/runtime build 20240216.2 Microsoft.DotNet.ILCompiler , Microsoft.NET.Sdk.IL , Microsoft.NETCore.App.Runtime.win-x64 , Microsoft.NETCore.ILAsm , runtime.native.System.IO.Ports , System.Reflection.Metadata , System.Reflection.MetadataLoadContext , System.Text.Json , Microsoft.SourceBuild.Intermediate.runtime.linux-x64 From Version 9.0.0-preview.2.24115.1 -> To Version 9.0.0-preview.2.24116.2 * Update dependencies from https://github.com/dotnet/sdk build 20240218.3 Microsoft.SourceBuild.Intermediate.sdk , Microsoft.DotNet.ApiCompat.Task From Version 9.0.100-preview.2.24112.1 -> To Version 9.0.100-preview.2.24118.3 * Update dependencies from https://github.com/dotnet/icu build 20240219.1 Microsoft.NETCore.Runtime.ICU.Transport From Version 9.0.0-preview.2.24112.1 -> To Version 9.0.0-preview.2.24119.1 * Update dependencies from https://github.com/dotnet/xharness build 20240219.1 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 9.0.0-prerelease.24112.4 -> To Version 9.0.0-prerelease.24119.1 * Update dependencies from https://github.com/dotnet/runtime-assets build 20240219.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 9.0.0-beta.24108.2 -> To Version 9.0.0-beta.24119.1 * Update dependencies from https://github.com/dotnet/hotreload-utils build 20240219.1 Microsoft.DotNet.HotReload.Utils.Generator.BuildTool From Version 9.0.0-alpha.0.24112.1 -> To Version 9.0.0-alpha.0.24119.1 * Update dependencies from https://github.com/dotnet/cecil build 20240219.1 Microsoft.SourceBuild.Intermediate.cecil , Microsoft.DotNet.Cecil From Version 0.11.4-alpha.24112.1 -> To Version 0.11.4-alpha.24119.1 --------- Co-authored-by: dotnet-maestro[bot] --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 128 +++++++++++++++++++------------------- eng/Versions.props | 56 ++++++++--------- global.json | 2 +- 4 files changed, 94 insertions(+), 94 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 2663dda99de3fe..1577b7e67fc075 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "9.0.0-prerelease.24112.4", + "version": "9.0.0-prerelease.24119.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index c0123718834754..c749d8cd4ac4a2 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/icu - 1b9c03a1103cf622ee2f7850d16aa1095a719e56 + c15a038f408fef6814e5f9c0bf8882bcdf53a290 https://github.com/dotnet/msquic @@ -90,14 +90,14 @@ a045dd54a4c44723c215d992288160eb1401bb7f - + https://github.com/dotnet/cecil - ca7e93445acbd94bfa696c16fa039f2a6130f2cb + 61250b0ed403b3f9b69a33f7d8f66f311338d6a1 - + https://github.com/dotnet/cecil - ca7e93445acbd94bfa696c16fa039f2a6130f2cb + 61250b0ed403b3f9b69a33f7d8f66f311338d6a1 @@ -206,57 +206,57 @@ https://github.com/dotnet/arcade c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 https://github.com/dotnet/llvm-project @@ -314,55 +314,55 @@ https://github.com/dotnet/llvm-project 9885e5aecc176ca701fc3527877d608bf7ccfb7d - + https://github.com/dotnet/runtime - 963626276e11bf5587aaed69826b62682b05d9c4 + d972a19c077e899d0b3fff97d955968e50906396 - + https://github.com/dotnet/runtime - 963626276e11bf5587aaed69826b62682b05d9c4 + d972a19c077e899d0b3fff97d955968e50906396 - + https://github.com/dotnet/runtime - 963626276e11bf5587aaed69826b62682b05d9c4 + d972a19c077e899d0b3fff97d955968e50906396 - + https://github.com/dotnet/runtime - 963626276e11bf5587aaed69826b62682b05d9c4 + d972a19c077e899d0b3fff97d955968e50906396 - + https://github.com/dotnet/runtime - 963626276e11bf5587aaed69826b62682b05d9c4 + d972a19c077e899d0b3fff97d955968e50906396 - + https://github.com/dotnet/runtime - 963626276e11bf5587aaed69826b62682b05d9c4 + d972a19c077e899d0b3fff97d955968e50906396 - + https://github.com/dotnet/runtime - 963626276e11bf5587aaed69826b62682b05d9c4 + d972a19c077e899d0b3fff97d955968e50906396 - + https://github.com/dotnet/runtime - 963626276e11bf5587aaed69826b62682b05d9c4 + d972a19c077e899d0b3fff97d955968e50906396 - + https://github.com/dotnet/runtime - 963626276e11bf5587aaed69826b62682b05d9c4 + d972a19c077e899d0b3fff97d955968e50906396 - + https://github.com/dotnet/xharness - 11ae3663fe3de366ea3566d7ae9b4731adee2ca3 + 8aa2a4cb80000ebb46ee61cd6ac29b2e63ebe87c - + https://github.com/dotnet/xharness - 11ae3663fe3de366ea3566d7ae9b4731adee2ca3 + 8aa2a4cb80000ebb46ee61cd6ac29b2e63ebe87c - + https://github.com/dotnet/xharness - 11ae3663fe3de366ea3566d7ae9b4731adee2ca3 + 8aa2a4cb80000ebb46ee61cd6ac29b2e63ebe87c https://github.com/dotnet/arcade @@ -384,13 +384,13 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization db9f1c2362565f3ef41c8e8feb5ed49ab11a6459 - + https://github.com/dotnet/hotreload-utils - 465874b5842702bf69bbb6bacd94a52d8ea2a073 + ec73ebf54c4ae98ac1450fcf95998180d4160f31 - + https://github.com/dotnet/runtime-assets - b5ac2d9031d4b2dc40683b31de86b05a20b670af + 596a5b1bf322034f548c3902e52b001bbc5ac646 https://github.com/dotnet/roslyn @@ -419,14 +419,14 @@ 77372c66fd54927312b5b0a2e399e192f74445c9 - + https://github.com/dotnet/sdk - 0962c1f89f5daf924a9fe876c80e80b0bde63b0d + b834bb25bdf308c4971d00cef6b726dfaa828c66 - + https://github.com/dotnet/sdk - 0962c1f89f5daf924a9fe876c80e80b0bde63b0d + b834bb25bdf308c4971d00cef6b726dfaa828c66 diff --git a/eng/Versions.props b/eng/Versions.props index bc16de2b22bec1..a918e52f3c88ce 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -81,7 +81,7 @@ 0.2.0 - 9.0.100-preview.2.24112.1 + 9.0.100-preview.2.24118.3 9.0.0-beta.24112.1 9.0.0-beta.24112.1 @@ -104,10 +104,10 @@ 6.0.0-preview.1.102 - 9.0.0-preview.2.24115.1 + 9.0.0-preview.2.24116.2 6.0.0 - 9.0.0-preview.2.24115.1 + 9.0.0-preview.2.24116.2 16.0.5-alpha.1.24112.1 16.0.5-alpha.1.24112.1 @@ -128,38 +128,38 @@ 8.0.0 5.0.0 4.5.5 - 9.0.0-preview.2.24115.1 - 9.0.0-preview.2.24115.1 + 9.0.0-preview.2.24116.2 + 9.0.0-preview.2.24116.2 6.0.0 5.0.0 5.0.0 5.0.0 7.0.0 - 9.0.0-preview.2.24115.1 + 9.0.0-preview.2.24116.2 6.0.0 7.0.0 4.5.4 4.5.0 - 9.0.0-preview.2.24115.1 + 9.0.0-preview.2.24116.2 8.0.0 8.0.0 8.0.0 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 - 9.0.0-beta.24108.2 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 + 9.0.0-beta.24119.1 1.0.0-prerelease.24104.2 1.0.0-prerelease.24104.2 @@ -187,10 +187,10 @@ 1.4.0 17.4.0-preview-20220707-01 - 9.0.0-prerelease.24112.4 - 9.0.0-prerelease.24112.4 - 9.0.0-prerelease.24112.4 - 9.0.0-alpha.0.24112.1 + 9.0.0-prerelease.24119.1 + 9.0.0-prerelease.24119.1 + 9.0.0-prerelease.24119.1 + 9.0.0-alpha.0.24119.1 3.12.0 4.5.0 6.0.0 @@ -216,11 +216,11 @@ 8.0.0-preview-20230918.1 - 0.11.4-alpha.24112.1 + 0.11.4-alpha.24119.1 - 9.0.0-preview.2.24115.1 + 9.0.0-preview.2.24116.2 - 9.0.0-preview.2.24112.1 + 9.0.0-preview.2.24119.1 2.2.3 9.0.0-alpha.1.24067.1 diff --git a/global.json b/global.json index dc5aff95f8db27..1e159da9c5464f 100644 --- a/global.json +++ b/global.json @@ -13,6 +13,6 @@ "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.24112.1", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", - "Microsoft.NET.Sdk.IL": "9.0.0-preview.2.24115.1" + "Microsoft.NET.Sdk.IL": "9.0.0-preview.2.24116.2" } } From f99cb734a8d3e0e58ff8236199e36ca0d2e161a4 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:25:26 -0600 Subject: [PATCH 152/158] [main] Update dependencies from dotnet/arcade (#98554) * Update dependencies from https://github.com/dotnet/arcade build 20240214.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.24112.1 -> To Version 9.0.0-beta.24114.1 * Update dependencies from https://github.com/dotnet/arcade build 20240214.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.24112.1 -> To Version 9.0.0-beta.24114.1 * Update dependencies from https://github.com/dotnet/arcade build 20240214.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.24112.1 -> To Version 9.0.0-beta.24114.1 * Update dependencies from https://github.com/dotnet/arcade build 20240214.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.24112.1 -> To Version 9.0.0-beta.24114.1 * Update dependencies from https://github.com/dotnet/arcade build 20240214.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.24112.1 -> To Version 9.0.0-beta.24114.1 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 84 ++++++++++++++++++++--------------------- eng/Versions.props | 32 ++++++++-------- global.json | 6 +-- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index c749d8cd4ac4a2..d5efb81f531583 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -124,87 +124,87 @@ - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c https://github.com/dotnet/runtime-assets @@ -364,9 +364,9 @@ https://github.com/dotnet/xharness 8aa2a4cb80000ebb46ee61cd6ac29b2e63ebe87c - + https://github.com/dotnet/arcade - c3f5cbfb2829795294f5c2d9fa5a0522f47e91fb + d5b02a4900c4d521cb48b8f0d7e3f28175268f7c https://dev.azure.com/dnceng/internal/_git/dotnet-optimization diff --git a/eng/Versions.props b/eng/Versions.props index a918e52f3c88ce..5dcf8495f36080 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -83,22 +83,22 @@ 9.0.100-preview.2.24118.3 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 - 2.6.7-beta.24112.1 - 9.0.0-beta.24112.1 - 2.6.7-beta.24112.1 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 - 9.0.0-beta.24112.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 + 2.6.7-beta.24114.1 + 9.0.0-beta.24114.1 + 2.6.7-beta.24114.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 + 9.0.0-beta.24114.1 1.4.0 diff --git a/global.json b/global.json index 1e159da9c5464f..0cb5dc6a57928a 100644 --- a/global.json +++ b/global.json @@ -8,9 +8,9 @@ "dotnet": "9.0.100-alpha.1.23615.4" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24112.1", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24112.1", - "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.24112.1", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24114.1", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24114.1", + "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.24114.1", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", "Microsoft.NET.Sdk.IL": "9.0.0-preview.2.24116.2" From 11bdd1b9fc38e137d7945c66790d05b0cd1966ab Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 20 Feb 2024 17:38:06 -0800 Subject: [PATCH 153/158] [NativeAOT] Use ld_classic in ILC build and in build integration (#97856) (#98726) Forward port of #97856 Workaround for #97745 --- .../Microsoft.NETCore.Native.Unix.targets | 13 +++++++++++++ src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index 32a946dd5839ad..9f710609a2644e 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -230,6 +230,19 @@ The .NET Foundation licenses this file to you under the MIT license. + + + + + + + <_XcodeVersion>$([System.Text.RegularExpressions.Regex]::Match($(_XcodeVersionString), '[1-9]\d*')) + + + + + + <_CommandProbe>command -v <_CommandProbe Condition="$([MSBuild]::IsOSPlatform('Windows'))">where /Q diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj b/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj index 7549877334cffc..bea5a3d9dd80a4 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler/ILCompiler.csproj @@ -72,6 +72,19 @@ + + + + + + + <_XcodeVersion>$([System.Text.RegularExpressions.Regex]::Match($(_XcodeVersionString), '[1-9]\d*')) + + + + + + $(_CC_LDFLAGS.SubString(0, $(_CC_LDFLAGS.IndexOf(';')))) <_LDFLAGS>$(_CC_LDFLAGS.SubString($([MSBuild]::Add($(_CC_LDFLAGS.IndexOf(';')), 1)))) From 3338d4a8bd5050e6fe0b5713608bb13d5bdc40b3 Mon Sep 17 00:00:00 2001 From: David Mason Date: Tue, 20 Feb 2024 18:16:07 -0800 Subject: [PATCH 154/158] Update multiple.cpp (#98685) --- src/tests/profiler/native/multiple/multiple.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/tests/profiler/native/multiple/multiple.cpp b/src/tests/profiler/native/multiple/multiple.cpp index 6b8fba571858aa..c36e4a8efa491f 100644 --- a/src/tests/profiler/native/multiple/multiple.cpp +++ b/src/tests/profiler/native/multiple/multiple.cpp @@ -24,7 +24,6 @@ HRESULT MultiplyLoaded::InitializeCommon(IUnknown* pICorProfilerInfoUnk) Profiler::Initialize(pICorProfilerInfoUnk); HRESULT hr = S_OK; - printf("Setting exception mask\n"); if (FAILED(hr = pCorProfilerInfo->SetEventMask2(COR_PRF_MONITOR_EXCEPTIONS, 0))) { _failures++; @@ -37,11 +36,13 @@ HRESULT MultiplyLoaded::InitializeCommon(IUnknown* pICorProfilerInfoUnk) HRESULT MultiplyLoaded::Initialize(IUnknown* pICorProfilerInfoUnk) { + printf("MultiplyLoaded::Initialize\n"); return InitializeCommon(pICorProfilerInfoUnk); } HRESULT MultiplyLoaded::InitializeForAttach(IUnknown* pICorProfilerInfoUnk, void* pvClientData, UINT cbClientData) { + printf("MultiplyLoaded::InitializeForAttach\n"); return InitializeCommon(pICorProfilerInfoUnk); } @@ -56,8 +57,8 @@ HRESULT MultiplyLoaded::ProfilerDetachSucceeded() ++_detachCount; printf("ProfilerDetachSucceeded _detachCount=%d\n", _detachCount.load()); - if (_detachCount == (MAX_PROFILERS - 1) - && _exceptionThrownSeenCount >= (MAX_PROFILERS - 1) + if (_detachCount == MAX_PROFILERS + && _exceptionThrownSeenCount >= MAX_PROFILERS && _failures == 0) { printf("PROFILER TEST PASSES\n"); @@ -69,9 +70,7 @@ HRESULT MultiplyLoaded::ProfilerDetachSucceeded() HRESULT MultiplyLoaded::ExceptionThrown(ObjectID thrownObjectId) { - int seen = _exceptionThrownSeenCount++; - - printf("MultiplyLoaded::ExceptionThrown, number seen = %d\n", seen); + printf("MultiplyLoaded::ExceptionThrown, number seen = %d\n", ++_exceptionThrownSeenCount); thread detachThread([&]() { From d62ae356f48884f84f7d4e1815cf31cd55a1351b Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Wed, 21 Feb 2024 03:24:08 +0100 Subject: [PATCH 155/158] JIT: Remove GenTreeBlk::BlkOpKindHelper (#98682) Co-authored-by: Jakob Botsch Nielsen --- src/coreclr/jit/codegen.h | 6 - src/coreclr/jit/codegenarmarch.cpp | 68 ---------- src/coreclr/jit/codegencommon.cpp | 15 ++- src/coreclr/jit/codegenloongarch64.cpp | 67 ---------- src/coreclr/jit/codegenriscv64.cpp | 67 ---------- src/coreclr/jit/codegenxarch.cpp | 55 -------- src/coreclr/jit/compiler.h | 2 + src/coreclr/jit/gentree.cpp | 25 +++- src/coreclr/jit/gentree.h | 3 - src/coreclr/jit/importercalls.cpp | 11 +- src/coreclr/jit/lower.cpp | 178 ++++++++++++++++++++++--- src/coreclr/jit/lower.h | 2 + src/coreclr/jit/lowerarmarch.cpp | 6 +- src/coreclr/jit/lowerloongarch64.cpp | 6 +- src/coreclr/jit/lowerriscv64.cpp | 6 +- src/coreclr/jit/lowerxarch.cpp | 26 ++-- src/coreclr/jit/lsraarmarch.cpp | 17 --- src/coreclr/jit/lsrabuild.cpp | 12 -- src/coreclr/jit/lsraloongarch64.cpp | 17 --- src/coreclr/jit/lsrariscv64.cpp | 17 --- src/coreclr/jit/lsraxarch.cpp | 16 --- 21 files changed, 218 insertions(+), 404 deletions(-) diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index afd9e42a9d2cdd..c36a6776a8cd9c 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -1182,9 +1182,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void genCodeForCpObj(GenTreeBlk* cpObjNode); void genCodeForCpBlkRepMovs(GenTreeBlk* cpBlkNode); void genCodeForCpBlkUnroll(GenTreeBlk* cpBlkNode); -#ifndef TARGET_X86 - void genCodeForCpBlkHelper(GenTreeBlk* cpBlkNode); -#endif void genCodeForPhysReg(GenTreePhysReg* tree); void genCodeForNullCheck(GenTreeIndir* tree); void genCodeForCmpXchg(GenTreeCmpXchg* tree); @@ -1257,9 +1254,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #endif // FEATURE_PUT_STRUCT_ARG_STK void genCodeForStoreBlk(GenTreeBlk* storeBlkNode); -#ifndef TARGET_X86 - void genCodeForInitBlkHelper(GenTreeBlk* initBlkNode); -#endif void genCodeForInitBlkLoop(GenTreeBlk* initBlkNode); void genCodeForInitBlkRepStos(GenTreeBlk* initBlkNode); void genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode); diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 92e99471956f0d..8eb8d71b617a5e 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -1925,37 +1925,6 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree) genProduceReg(tree); } -//---------------------------------------------------------------------------------- -// genCodeForCpBlkHelper - Generate code for a CpBlk node by the means of the VM memcpy helper call -// -// Arguments: -// cpBlkNode - the GT_STORE_[BLK|OBJ|DYN_BLK] -// -// Preconditions: -// The register assignments have been set appropriately. -// This is validated by genConsumeBlockOp(). -// -void CodeGen::genCodeForCpBlkHelper(GenTreeBlk* cpBlkNode) -{ - // Destination address goes in arg0, source address goes in arg1, and size goes in arg2. - // genConsumeBlockOp takes care of this for us. - genConsumeBlockOp(cpBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2); - - if (cpBlkNode->IsVolatile()) - { - // issue a full memory barrier before a volatile CpBlk operation - instGen_MemoryBarrier(); - } - - genEmitHelperCall(CORINFO_HELP_MEMCPY, 0, EA_UNKNOWN); - - if (cpBlkNode->IsVolatile()) - { - // issue a load barrier after a volatile CpBlk operation - instGen_MemoryBarrier(BARRIER_LOAD_ONLY); - } -} - #ifdef TARGET_ARM64 // The following classes @@ -3218,31 +3187,6 @@ void CodeGen::genCodeForMemmove(GenTreeBlk* tree) #endif } -//------------------------------------------------------------------------ -// genCodeForInitBlkHelper - Generate code for an InitBlk node by the means of the VM memcpy helper call -// -// Arguments: -// initBlkNode - the GT_STORE_[BLK|OBJ|DYN_BLK] -// -// Preconditions: -// The register assignments have been set appropriately. -// This is validated by genConsumeBlockOp(). -// -void CodeGen::genCodeForInitBlkHelper(GenTreeBlk* initBlkNode) -{ - // Size goes in arg2, source address goes in arg1, and size goes in arg2. - // genConsumeBlockOp takes care of this for us. - genConsumeBlockOp(initBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2); - - if (initBlkNode->IsVolatile()) - { - // issue a full memory barrier before a volatile initBlock Operation - instGen_MemoryBarrier(); - } - - genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN); -} - //------------------------------------------------------------------------ // genCodeForInitBlkLoop - Generate code for an InitBlk using an inlined for-loop. // It's needed for cases when size is too big to unroll and we're not allowed @@ -4630,18 +4574,6 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp) genCodeForInitBlkLoop(blkOp); break; - case GenTreeBlk::BlkOpKindHelper: - assert(!blkOp->gtBlkOpGcUnsafe); - if (isCopyBlk) - { - genCodeForCpBlkHelper(blkOp); - } - else - { - genCodeForInitBlkHelper(blkOp); - } - break; - case GenTreeBlk::BlkOpKindUnroll: case GenTreeBlk::BlkOpKindUnrollMemmove: if (isCopyBlk) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 825837fe45ef50..3c001249c9a054 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -6548,10 +6548,19 @@ void CodeGen::genDefinePendingCallLabel(GenTreeCall* call) // For certain indirect calls we may introduce helper calls before that we need to skip: // - CFG may introduce a call to the validator first // - Generic virtual methods may compute the target dynamically through a separate helper call - if (call->IsHelperCall(compiler, CORINFO_HELP_VALIDATE_INDIRECT_CALL) || - call->IsHelperCall(compiler, CORINFO_HELP_VIRTUAL_FUNC_PTR)) + // - memset/memcpy helper calls emitted for GT_STORE_DYN_BLK/GT_STORE_BLK + if (call->IsHelperCall()) { - return; + switch (compiler->eeGetHelperNum(call->gtCallMethHnd)) + { + case CORINFO_HELP_VALIDATE_INDIRECT_CALL: + case CORINFO_HELP_VIRTUAL_FUNC_PTR: + case CORINFO_HELP_MEMSET: + case CORINFO_HELP_MEMCPY: + return; + default: + break; + } } genDefineInlineTempLabel(genPendingCallLabel); diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index c850dad2ce765f..f2877859519649 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -6131,37 +6131,6 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree) genProduceReg(tree); } -//---------------------------------------------------------------------------------- -// genCodeForCpBlkHelper - Generate code for a CpBlk node by the means of the VM memcpy helper call -// -// Arguments: -// cpBlkNode - the GT_STORE_[BLK|OBJ|DYN_BLK] -// -// Preconditions: -// The register assignments have been set appropriately. -// This is validated by genConsumeBlockOp(). -// -void CodeGen::genCodeForCpBlkHelper(GenTreeBlk* cpBlkNode) -{ - // Destination address goes in arg0, source address goes in arg1, and size goes in arg2. - // genConsumeBlockOp takes care of this for us. - genConsumeBlockOp(cpBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2); - - if (cpBlkNode->IsVolatile()) - { - // issue a full memory barrier before a volatile CpBlk operation - instGen_MemoryBarrier(); - } - - genEmitHelperCall(CORINFO_HELP_MEMCPY, 0, EA_UNKNOWN); - - if (cpBlkNode->IsVolatile()) - { - // issue a INS_BARRIER_RMB after a volatile CpBlk operation - instGen_MemoryBarrier(BARRIER_FULL); - } -} - //---------------------------------------------------------------------------------- // genCodeForCpBlkUnroll: Generates CpBlk code by performing a loop unroll // @@ -6344,31 +6313,6 @@ void CodeGen::genCodeForCpBlkUnroll(GenTreeBlk* cpBlkNode) } } -//------------------------------------------------------------------------ -// genCodeForInitBlkHelper - Generate code for an InitBlk node by the means of the VM memcpy helper call -// -// Arguments: -// initBlkNode - the GT_STORE_[BLK|OBJ|DYN_BLK] -// -// Preconditions: -// The register assignments have been set appropriately. -// This is validated by genConsumeBlockOp(). -// -void CodeGen::genCodeForInitBlkHelper(GenTreeBlk* initBlkNode) -{ - // Size goes in arg2, source address goes in arg1, and size goes in arg2. - // genConsumeBlockOp takes care of this for us. - genConsumeBlockOp(initBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2); - - if (initBlkNode->IsVolatile()) - { - // issue a full memory barrier before a volatile initBlock Operation - instGen_MemoryBarrier(); - } - - genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN); -} - //------------------------------------------------------------------------ // genCodeForInitBlkLoop - Generate code for an InitBlk using an inlined for-loop. // It's needed for cases when size is too big to unroll and we're not allowed @@ -7333,17 +7277,6 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp) genCodeForInitBlkLoop(blkOp); break; - case GenTreeBlk::BlkOpKindHelper: - if (isCopyBlk) - { - genCodeForCpBlkHelper(blkOp); - } - else - { - genCodeForInitBlkHelper(blkOp); - } - break; - case GenTreeBlk::BlkOpKindUnroll: if (isCopyBlk) { diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 6d5a9a195024f3..fb6a4e2deea2cb 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -6166,37 +6166,6 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree) genProduceReg(tree); } -//---------------------------------------------------------------------------------- -// genCodeForCpBlkHelper - Generate code for a CpBlk node by the means of the VM memcpy helper call -// -// Arguments: -// cpBlkNode - the GT_STORE_[BLK|OBJ|DYN_BLK] -// -// Preconditions: -// The register assignments have been set appropriately. -// This is validated by genConsumeBlockOp(). -// -void CodeGen::genCodeForCpBlkHelper(GenTreeBlk* cpBlkNode) -{ - // Destination address goes in arg0, source address goes in arg1, and size goes in arg2. - // genConsumeBlockOp takes care of this for us. - genConsumeBlockOp(cpBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2); - - if (cpBlkNode->IsVolatile()) - { - // issue a full memory barrier before a volatile CpBlk operation - instGen_MemoryBarrier(); - } - - genEmitHelperCall(CORINFO_HELP_MEMCPY, 0, EA_UNKNOWN); - - if (cpBlkNode->IsVolatile()) - { - // issue a INS_BARRIER_RMB after a volatile CpBlk operation - instGen_MemoryBarrier(BARRIER_FULL); - } -} - //---------------------------------------------------------------------------------- // genCodeForCpBlkUnroll: Generates CpBlk code by performing a loop unroll // @@ -6434,31 +6403,6 @@ void CodeGen::genCodeForInitBlkLoop(GenTreeBlk* initBlkNode) } } -//------------------------------------------------------------------------ -// genCodeForInitBlkHelper - Generate code for an InitBlk node by the means of the VM memcpy helper call -// -// Arguments: -// initBlkNode - the GT_STORE_[BLK|OBJ|DYN_BLK] -// -// Preconditions: -// The register assignments have been set appropriately. -// This is validated by genConsumeBlockOp(). -// -void CodeGen::genCodeForInitBlkHelper(GenTreeBlk* initBlkNode) -{ - // Size goes in arg2, source address goes in arg1, and size goes in arg2. - // genConsumeBlockOp takes care of this for us. - genConsumeBlockOp(initBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2); - - if (initBlkNode->IsVolatile()) - { - // issue a full memory barrier before a volatile initBlock Operation - instGen_MemoryBarrier(); - } - - genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN); -} - //------------------------------------------------------------------------ // genCall: Produce code for a GT_CALL node // @@ -7328,17 +7272,6 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp) genCodeForInitBlkLoop(blkOp); break; - case GenTreeBlk::BlkOpKindHelper: - if (isCopyBlk) - { - genCodeForCpBlkHelper(blkOp); - } - else - { - genCodeForInitBlkHelper(blkOp); - } - break; - case GenTreeBlk::BlkOpKindUnroll: if (isCopyBlk) { diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 2aba181486435b..884ba901e5d835 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -3070,19 +3070,6 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* storeBlkNode) genCodeForInitBlkLoop(storeBlkNode); break; -#ifdef TARGET_AMD64 - case GenTreeBlk::BlkOpKindHelper: - assert(!storeBlkNode->gtBlkOpGcUnsafe); - if (isCopyBlk) - { - genCodeForCpBlkHelper(storeBlkNode); - } - else - { - genCodeForInitBlkHelper(storeBlkNode); - } - break; -#endif // TARGET_AMD64 case GenTreeBlk::BlkOpKindRepInstr: #ifndef JIT32_GCENCODER assert(!storeBlkNode->gtBlkOpGcUnsafe); @@ -3402,27 +3389,6 @@ void CodeGen::genCodeForInitBlkLoop(GenTreeBlk* initBlkNode) } } -#ifdef TARGET_AMD64 -//------------------------------------------------------------------------ -// genCodeForInitBlkHelper - Generate code for an InitBlk node by the means of the VM memcpy helper call -// -// Arguments: -// initBlkNode - the GT_STORE_[BLK|OBJ|DYN_BLK] -// -// Preconditions: -// The register assignments have been set appropriately. -// This is validated by genConsumeBlockOp(). -// -void CodeGen::genCodeForInitBlkHelper(GenTreeBlk* initBlkNode) -{ - // Destination address goes in arg0, source address goes in arg1, and size goes in arg2. - // genConsumeBlockOp takes care of this for us. - genConsumeBlockOp(initBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2); - - genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN); -} -#endif // TARGET_AMD64 - #ifdef FEATURE_PUT_STRUCT_ARG_STK // Generate code for a load from some address + offset // base: tree node which can be either a local or an indir @@ -4310,27 +4276,6 @@ void CodeGen::genCodeForCpObj(GenTreeBlk* cpObjNode) gcInfo.gcMarkRegSetNpt(RBM_RDI); } -#ifdef TARGET_AMD64 -//---------------------------------------------------------------------------------- -// genCodeForCpBlkHelper - Generate code for a CpBlk node by the means of the VM memcpy helper call -// -// Arguments: -// cpBlkNode - the GT_STORE_[BLK|OBJ|DYN_BLK] -// -// Preconditions: -// The register assignments have been set appropriately. -// This is validated by genConsumeBlockOp(). -// -void CodeGen::genCodeForCpBlkHelper(GenTreeBlk* cpBlkNode) -{ - // Destination address goes in arg0, source address goes in arg1, and size goes in arg2. - // genConsumeBlockOp takes care of this for us. - genConsumeBlockOp(cpBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2); - - genEmitHelperCall(CORINFO_HELP_MEMCPY, 0, EA_UNKNOWN); -} -#endif // TARGET_AMD64 - // generate code do a switch statement based on a table of ip-relative offsets void CodeGen::genTableBasedSwitch(GenTree* treeNode) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 9c2f667a0ed777..5360a474010b99 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3311,6 +3311,8 @@ class Compiler #endif #endif // FEATURE_HW_INTRINSICS + GenTree* gtNewMemoryBarrier(bool loadOnly = false); + GenTree* gtNewMustThrowException(unsigned helper, var_types type, CORINFO_CLASS_HANDLE clsHnd); GenTreeLclFld* gtNewLclFldNode(unsigned lnum, var_types type, unsigned offset); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 0a220b02d56770..33195fad712356 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -8651,6 +8651,26 @@ GenTreeBlk* Compiler::gtNewBlkIndir(ClassLayout* layout, GenTree* addr, GenTreeF return blkNode; } +//------------------------------------------------------------------------ +// gtNewMemoryBarrier: Create a memory barrier node +// +// Arguments: +// loadOnly - relaxes the full memory barrier to be load-only +// +// Return Value: +// The created GT_MEMORYBARRIER node. +// +GenTree* Compiler::gtNewMemoryBarrier(bool loadOnly) +{ + GenTree* tree = new (this, GT_MEMORYBARRIER) GenTree(GT_MEMORYBARRIER, TYP_VOID); + tree->gtFlags |= GTF_GLOB_REF | GTF_ASG; + if (loadOnly) + { + tree->gtFlags |= GTF_MEMORYBARRIER_LOAD; + } + return tree; +} + //------------------------------------------------------------------------------ // gtNewIndir : Create an indirection node. // @@ -12703,11 +12723,6 @@ void Compiler::gtDispTree(GenTree* tree, case GenTreeBlk::BlkOpKindUnrollMemmove: printf(" (Memmove)"); break; -#ifndef TARGET_X86 - case GenTreeBlk::BlkOpKindHelper: - printf(" (Helper)"); - break; -#endif case GenTreeBlk::BlkOpKindLoop: printf(" (Loop)"); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index ff99a9e7202af6..26ab5b2c705a3d 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -7389,9 +7389,6 @@ struct GenTreeBlk : public GenTreeIndir #ifdef TARGET_XARCH BlkOpKindCpObjRepInstr, #endif -#ifndef TARGET_X86 - BlkOpKindHelper, -#endif #ifdef TARGET_XARCH BlkOpKindRepInstr, #endif diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index e8e3ca495064f9..b557c629e35583 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -3543,18 +3543,9 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_Threading_Interlocked_ReadMemoryBarrier: { assert(sig->numArgs == 0); - - GenTree* op1 = new (this, GT_MEMORYBARRIER) GenTree(GT_MEMORYBARRIER, TYP_VOID); - op1->gtFlags |= GTF_GLOB_REF | GTF_ASG; - // On XARCH `NI_System_Threading_Interlocked_ReadMemoryBarrier` fences need not be emitted. // However, we still need to capture the effect on reordering. - if (ni == NI_System_Threading_Interlocked_ReadMemoryBarrier) - { - op1->gtFlags |= GTF_MEMORYBARRIER_LOAD; - } - - retNode = op1; + retNode = gtNewMemoryBarrier(ni == NI_System_Threading_Interlocked_ReadMemoryBarrier); break; } diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 2d699eb6bf81ce..5acb6ff95112e1 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3099,23 +3099,7 @@ void Lowering::LowerCFGCall(GenTreeCall* call) LowerNode(regNode); // Finally move all GT_PUTARG_* nodes - for (CallArg& arg : call->gtArgs.EarlyArgs()) - { - GenTree* node = arg.GetEarlyNode(); - // Non-value nodes in early args are setup nodes for late args. - if (node->IsValue()) - { - assert(node->OperIsPutArg() || node->OperIsFieldList()); - MoveCFGCallArg(call, node); - } - } - - for (CallArg& arg : call->gtArgs.LateArgs()) - { - GenTree* node = arg.GetLateNode(); - assert(node->OperIsPutArg() || node->OperIsFieldList()); - MoveCFGCallArg(call, node); - } + MoveCFGCallArgs(call); break; } case CFGCallKind::Dispatch: @@ -3262,6 +3246,38 @@ void Lowering::MoveCFGCallArg(GenTreeCall* call, GenTree* node) BlockRange().InsertBefore(call, node); } +//------------------------------------------------------------------------ +// MoveCFGCallArgs: Given a call that will be CFG transformed using the +// validate+call scheme, move all GT_PUTARG_* or GT_FIELD_LIST nodes right before the call. +// +// Arguments: +// call - The call that is being CFG transformed +// +// Remarks: +// See comments in MoveCFGCallArg for more details. +// +void Lowering::MoveCFGCallArgs(GenTreeCall* call) +{ + // Finally move all GT_PUTARG_* nodes + for (CallArg& arg : call->gtArgs.EarlyArgs()) + { + GenTree* node = arg.GetEarlyNode(); + // Non-value nodes in early args are setup nodes for late args. + if (node->IsValue()) + { + assert(node->OperIsPutArg() || node->OperIsFieldList()); + MoveCFGCallArg(call, node); + } + } + + for (CallArg& arg : call->gtArgs.LateArgs()) + { + GenTree* node = arg.GetLateNode(); + assert(node->OperIsPutArg() || node->OperIsFieldList()); + MoveCFGCallArg(call, node); + } +} + #ifndef TARGET_64BIT //------------------------------------------------------------------------ // Lowering::DecomposeLongCompare: Decomposes a TYP_LONG compare node. @@ -7845,6 +7861,131 @@ void Lowering::ContainCheckBitCast(GenTree* node) } } +//------------------------------------------------------------------------ +// LowerBlockStoreAsHelperCall: Lower a block store node as a memset/memcpy call +// +// Arguments: +// blkNode - The block store node to lower +// +void Lowering::LowerBlockStoreAsHelperCall(GenTreeBlk* blkNode) +{ + // We shouldn't be using helper calls for blocks on heap containing GC pointers. + // due to atomicity guarantees. + assert(!blkNode->IsZeroingGcPointersOnHeap()); + + LIR::Use use; + assert(!BlockRange().TryGetUse(blkNode, &use)); + + const bool isVolatile = blkNode->IsVolatile(); + + GenTree* dest = blkNode->Addr(); + GenTree* data = blkNode->Data(); + GenTree* size; + + CorInfoHelpFunc helper; + + // Is it Memset ... + if (blkNode->OperIsInitBlkOp()) + { + helper = CORINFO_HELP_MEMSET; + + // Drop GT_INIT_VAL nodes + if (data->OperIsInitVal()) + { + BlockRange().Remove(data); + data = data->gtGetOp1(); + } + } + else + { + // ... or Memcpy? + helper = CORINFO_HELP_MEMCPY; + + if (data->OperIs(GT_IND)) + { + // Drop GT_IND nodes + BlockRange().Remove(data); + data = data->AsIndir()->Addr(); + } + else + { + assert(data->OperIs(GT_LCL_VAR, GT_LCL_FLD)); + + // Convert local to LCL_ADDR + unsigned lclOffset = data->AsLclVarCommon()->GetLclOffs(); + + data->ChangeOper(GT_LCL_ADDR); + data->ChangeType(TYP_I_IMPL); + data->AsLclFld()->SetLclOffs(lclOffset); + data->ClearContained(); + } + } + + if (blkNode->OperIs(GT_STORE_DYN_BLK)) + { + // Size is not a constant + size = blkNode->AsStoreDynBlk()->gtDynamicSize; + } + else + { + // Size is a constant + size = comp->gtNewIconNode(blkNode->Size(), TYP_I_IMPL); + BlockRange().InsertBefore(data, size); + } + + // A hacky way to safely call fgMorphTree in Lower + GenTree* destPlaceholder = comp->gtNewZeroConNode(dest->TypeGet()); + GenTree* dataPlaceholder = comp->gtNewZeroConNode(genActualType(data)); + GenTree* sizePlaceholder = comp->gtNewZeroConNode(genActualType(size)); + + GenTreeCall* call = comp->gtNewHelperCallNode(helper, TYP_VOID, destPlaceholder, dataPlaceholder, sizePlaceholder); + comp->fgMorphArgs(call); + + LIR::Range range = LIR::SeqTree(comp, call); + GenTree* rangeStart = range.FirstNode(); + GenTree* rangeEnd = range.LastNode(); + + BlockRange().InsertBefore(blkNode, std::move(range)); + blkNode->gtBashToNOP(); + + LIR::Use destUse; + LIR::Use dataUse; + LIR::Use sizeUse; + BlockRange().TryGetUse(destPlaceholder, &destUse); + BlockRange().TryGetUse(dataPlaceholder, &dataUse); + BlockRange().TryGetUse(sizePlaceholder, &sizeUse); + destUse.ReplaceWith(dest); + dataUse.ReplaceWith(data); + sizeUse.ReplaceWith(size); + destPlaceholder->SetUnusedValue(); + dataPlaceholder->SetUnusedValue(); + sizePlaceholder->SetUnusedValue(); + + LowerRange(rangeStart, rangeEnd); + + // Finally move all GT_PUTARG_* nodes + // Re-use the existing logic for CFG call args here + MoveCFGCallArgs(call); + + BlockRange().Remove(destPlaceholder); + BlockRange().Remove(dataPlaceholder); + BlockRange().Remove(sizePlaceholder); + +// Wrap with memory barriers on weak memory models +// if the block store was volatile +#ifndef TARGET_XARCH + if (isVolatile) + { + GenTree* firstBarrier = comp->gtNewMemoryBarrier(); + GenTree* secondBarrier = comp->gtNewMemoryBarrier(/*loadOnly*/ true); + BlockRange().InsertBefore(call, firstBarrier); + BlockRange().InsertAfter(call, secondBarrier); + LowerNode(firstBarrier); + LowerNode(secondBarrier); + } +#endif +} + struct StoreCoalescingData { var_types targetType; @@ -8504,7 +8645,7 @@ bool Lowering::TryMakeIndirsAdjacent(GenTreeIndir* prevIndir, GenTreeIndir* indi // We can reorder indirs with some calls, but introducing a LIR edge // that spans a call can introduce spills (or callee-saves). - if (cur->IsCall() || (cur->OperIsStoreBlk() && (cur->AsBlk()->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper))) + if (cur->IsCall()) { JITDUMP(" ..but they are separated by node [%06u] that kills registers\n", Compiler::dspTreeID(cur)); return false; @@ -8841,7 +8982,6 @@ void Lowering::LowerLclHeap(GenTree* node) GenTreeBlk(GT_STORE_BLK, TYP_STRUCT, heapLcl, zero, comp->typGetBlkLayout((unsigned)alignedSize)); storeBlk->gtFlags |= (GTF_IND_UNALIGNED | GTF_ASG | GTF_EXCEPT | GTF_GLOB_REF); BlockRange().InsertAfter(use.Def(), heapLcl, zero, storeBlk); - LowerNode(storeBlk); } else { diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index d44f16ceca527b..5156b8899b804d 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -141,6 +141,7 @@ class Lowering final : public Phase bool LowerCallMemmove(GenTreeCall* call, GenTree** next); bool LowerCallMemcmp(GenTreeCall* call, GenTree** next); void LowerCFGCall(GenTreeCall* call); + void MoveCFGCallArgs(GenTreeCall* call); void MoveCFGCallArg(GenTreeCall* call, GenTree* node); #ifndef TARGET_64BIT GenTree* DecomposeLongCompare(GenTree* cmp); @@ -331,6 +332,7 @@ class Lowering final : public Phase GenTree* LowerSignedDivOrMod(GenTree* node); void LowerBlockStore(GenTreeBlk* blkNode); void LowerBlockStoreCommon(GenTreeBlk* blkNode); + void LowerBlockStoreAsHelperCall(GenTreeBlk* blkNode); void LowerLclHeap(GenTree* node); void ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenTree* addr, GenTree* addrParent); void LowerPutArgStkOrSplit(GenTreePutArgStk* putArgNode); diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 5c0acbbdc40115..4cc7144ad15d39 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -634,7 +634,8 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) } else { - blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper; + LowerBlockStoreAsHelperCall(blkNode); + return; } } else @@ -686,8 +687,7 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) else { assert(blkNode->OperIs(GT_STORE_BLK, GT_STORE_DYN_BLK)); - - blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper; + LowerBlockStoreAsHelperCall(blkNode); } } } diff --git a/src/coreclr/jit/lowerloongarch64.cpp b/src/coreclr/jit/lowerloongarch64.cpp index 5110442eda10d1..507ef59256ca4c 100644 --- a/src/coreclr/jit/lowerloongarch64.cpp +++ b/src/coreclr/jit/lowerloongarch64.cpp @@ -334,7 +334,8 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) } else { - blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper; + LowerBlockStoreAsHelperCall(blkNode); + return; } } else @@ -387,8 +388,7 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) else { assert(blkNode->OperIs(GT_STORE_BLK, GT_STORE_DYN_BLK)); - - blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper; + LowerBlockStoreAsHelperCall(blkNode); } } } diff --git a/src/coreclr/jit/lowerriscv64.cpp b/src/coreclr/jit/lowerriscv64.cpp index 4f60458fd25161..405b9707c41145 100644 --- a/src/coreclr/jit/lowerriscv64.cpp +++ b/src/coreclr/jit/lowerriscv64.cpp @@ -282,7 +282,8 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) } else { - blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper; + LowerBlockStoreAsHelperCall(blkNode); + return; } } else @@ -334,8 +335,7 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) else { assert(blkNode->OperIs(GT_STORE_BLK, GT_STORE_DYN_BLK)); - - blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper; + LowerBlockStoreAsHelperCall(blkNode); } } } diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 6709a57a49e8d7..ea4db9fedbac46 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -407,14 +407,20 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) else { TOO_BIG_TO_UNROLL: + if (blkNode->IsZeroingGcPointersOnHeap()) + { + blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindLoop; + } + else + { #ifdef TARGET_AMD64 - blkNode->gtBlkOpKind = - blkNode->IsZeroingGcPointersOnHeap() ? GenTreeBlk::BlkOpKindLoop : GenTreeBlk::BlkOpKindHelper; + LowerBlockStoreAsHelperCall(blkNode); + return; #else - // TODO-X86-CQ: Investigate whether a helper call would be beneficial on x86 - blkNode->gtBlkOpKind = - blkNode->IsZeroingGcPointersOnHeap() ? GenTreeBlk::BlkOpKindLoop : GenTreeBlk::BlkOpKindRepInstr; + // TODO-X86-CQ: Investigate whether a helper call would be beneficial on x86 + blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindRepInstr; #endif + } } } else @@ -513,7 +519,8 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) assert(blkNode->OperIs(GT_STORE_BLK, GT_STORE_DYN_BLK)); #ifdef TARGET_AMD64 - blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindHelper; + LowerBlockStoreAsHelperCall(blkNode); + return; #else // TODO-X86-CQ: Investigate whether a helper call would be beneficial on x86 blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindRepInstr; @@ -522,13 +529,6 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) } assert(blkNode->gtBlkOpKind != GenTreeBlk::BlkOpKindInvalid); - -#ifndef TARGET_X86 - if ((MIN_ARG_AREA_FOR_CALL > 0) && (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper)) - { - RequireOutgoingArgSpace(blkNode, MIN_ARG_AREA_FOR_CALL); - } -#endif } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/lsraarmarch.cpp b/src/coreclr/jit/lsraarmarch.cpp index a0f27ba65b3058..ad112a817220f8 100644 --- a/src/coreclr/jit/lsraarmarch.cpp +++ b/src/coreclr/jit/lsraarmarch.cpp @@ -644,13 +644,6 @@ int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) buildInternalIntRegisterDefForNode(blkNode, availableIntRegs); break; - case GenTreeBlk::BlkOpKindHelper: - assert(!src->isContained()); - dstAddrRegMask = RBM_ARG_0; - srcRegMask = RBM_ARG_1; - sizeRegMask = RBM_ARG_2; - break; - default: unreached(); } @@ -783,16 +776,6 @@ int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) } break; - case GenTreeBlk::BlkOpKindHelper: - dstAddrRegMask = RBM_ARG_0; - if (srcAddrOrFill != nullptr) - { - assert(!srcAddrOrFill->isContained()); - srcRegMask = RBM_ARG_1; - } - sizeRegMask = RBM_ARG_2; - break; - default: unreached(); } diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 0c1d3f74475c8d..3b9ec7f388aec1 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -907,18 +907,6 @@ regMaskTP LinearScan::getKillSetForBlockStore(GenTreeBlk* blkNode) killMask = compiler->compHelperCallKillSet(CORINFO_HELP_ASSIGN_BYREF); break; -#ifndef TARGET_X86 - case GenTreeBlk::BlkOpKindHelper: - if (isCopyBlk) - { - killMask = compiler->compHelperCallKillSet(CORINFO_HELP_MEMCPY); - } - else - { - killMask = compiler->compHelperCallKillSet(CORINFO_HELP_MEMSET); - } - break; -#endif #ifdef TARGET_XARCH case GenTreeBlk::BlkOpKindRepInstr: if (isCopyBlk) diff --git a/src/coreclr/jit/lsraloongarch64.cpp b/src/coreclr/jit/lsraloongarch64.cpp index 67b27aa51300c8..b6e3b53d63ee59 100644 --- a/src/coreclr/jit/lsraloongarch64.cpp +++ b/src/coreclr/jit/lsraloongarch64.cpp @@ -1104,13 +1104,6 @@ int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) buildInternalIntRegisterDefForNode(blkNode, availableIntRegs); break; - case GenTreeBlk::BlkOpKindHelper: - assert(!src->isContained()); - dstAddrRegMask = RBM_ARG_0; - srcRegMask = RBM_ARG_1; - sizeRegMask = RBM_ARG_2; - break; - default: unreached(); } @@ -1159,16 +1152,6 @@ int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) buildInternalIntRegisterDefForNode(blkNode); break; - case GenTreeBlk::BlkOpKindHelper: - dstAddrRegMask = RBM_ARG_0; - if (srcAddrOrFill != nullptr) - { - assert(!srcAddrOrFill->isContained()); - srcRegMask = RBM_ARG_1; - } - sizeRegMask = RBM_ARG_2; - break; - default: unreached(); } diff --git a/src/coreclr/jit/lsrariscv64.cpp b/src/coreclr/jit/lsrariscv64.cpp index ec4ca4a34972b8..0188c3c0627116 100644 --- a/src/coreclr/jit/lsrariscv64.cpp +++ b/src/coreclr/jit/lsrariscv64.cpp @@ -1260,13 +1260,6 @@ int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) buildInternalIntRegisterDefForNode(blkNode, availableIntRegs); break; - case GenTreeBlk::BlkOpKindHelper: - assert(!src->isContained()); - dstAddrRegMask = RBM_ARG_0; - srcRegMask = RBM_ARG_1; - sizeRegMask = RBM_ARG_2; - break; - default: unreached(); } @@ -1315,16 +1308,6 @@ int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) buildInternalIntRegisterDefForNode(blkNode); break; - case GenTreeBlk::BlkOpKindHelper: - dstAddrRegMask = RBM_ARG_0; - if (srcAddrOrFill != nullptr) - { - assert(!srcAddrOrFill->isContained()); - srcRegMask = RBM_ARG_1; - } - sizeRegMask = RBM_ARG_2; - break; - default: unreached(); } diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index cb2b82d5d8ce92..41121ee9bed28e 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -1434,14 +1434,6 @@ int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) buildInternalIntRegisterDefForNode(blkNode, availableIntRegs); break; -#ifdef TARGET_AMD64 - case GenTreeBlk::BlkOpKindHelper: - dstAddrRegMask = RBM_ARG_0; - srcRegMask = RBM_ARG_1; - sizeRegMask = RBM_ARG_2; - break; -#endif - default: unreached(); } @@ -1562,14 +1554,6 @@ int LinearScan::BuildBlockStore(GenTreeBlk* blkNode) sizeRegMask = RBM_RCX; break; -#ifdef TARGET_AMD64 - case GenTreeBlk::BlkOpKindHelper: - dstAddrRegMask = RBM_ARG_0; - srcRegMask = RBM_ARG_1; - sizeRegMask = RBM_ARG_2; - break; -#endif - default: unreached(); } From 956255252ad4158c16b4e457ca0940b575d7d702 Mon Sep 17 00:00:00 2001 From: Mario Pistrich Date: Wed, 21 Feb 2024 04:55:07 +0100 Subject: [PATCH 156/158] Check if AttributeData for InterfaceTypeAttribute is not null (#98677) --- ...omImportToGeneratedComInterfaceAnalyzer.cs | 2 +- .../ConvertToGeneratedComInterfaceTests.cs | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/ConvertComImportToGeneratedComInterfaceAnalyzer.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/ConvertComImportToGeneratedComInterfaceAnalyzer.cs index 3035552c59dd8c..f9b96eb7e30184 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/ConvertComImportToGeneratedComInterfaceAnalyzer.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/ConvertComImportToGeneratedComInterfaceAnalyzer.cs @@ -50,7 +50,7 @@ public override void Initialize(AnalysisContext context) INamedTypeSymbol type = (INamedTypeSymbol)context.Symbol; AttributeData? interfaceTypeAttributeData = type.GetAttributes().FirstOrDefault(a => a.AttributeClass.Equals(interfaceTypeAttribute, SymbolEqualityComparer.Default)); if (type is not { TypeKind: TypeKind.Interface, IsComImport: true } - || interfaceTypeAttributeData.ConstructorArguments.Length == 1 && (int)interfaceTypeAttributeData.ConstructorArguments[0].Value != (int)ComInterfaceType.InterfaceIsIUnknown) + || interfaceTypeAttributeData is not { ConstructorArguments: [{ Value: (int)ComInterfaceType.InterfaceIsIUnknown }] }) { return; } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ConvertToGeneratedComInterfaceTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ConvertToGeneratedComInterfaceTests.cs index 308cc66f48b9f8..7260a4d4d392c7 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ConvertToGeneratedComInterfaceTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ConvertToGeneratedComInterfaceTests.cs @@ -370,5 +370,43 @@ public struct HResult await VerifyCS.VerifyCodeFixAsync(source, fixedSource); } + + [Fact] + public async Task UnsupportedInterfaceTypes_DoesNotReportDiagnostic() + { + // This also tests the case where InterfaceType is missing (defaulting to ComInterfaceType.InterfaceIsDual). + string source = """ + using System.Runtime.InteropServices; + + [ComImport] + [Guid("73EB4AF8-BE9C-4b49-B3A4-24F4FF657B26")] + public interface IInterfaceIsDualMissingAttribute + { + } + + [ComImport] + [Guid("5DA39CDF-DCAD-447A-836E-EA80DB34D81B")] + [InterfaceType(ComInterfaceType.InterfaceIsDual)] + public interface IInterfaceIsDual + { + } + + [ComImport] + [Guid("F59AB2FE-523D-4B28-911C-21363808C51E")] + [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] + public interface IInterfaceIsIDispatch + { + } + + [ComImport] + [Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7")] + [InterfaceType(ComInterfaceType.InterfaceIsIInspectable)] + public interface IInterfaceIsIInspectable + { + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } } } From 21c0940bdca96b87acc0682e73fa19998e3d1c7a Mon Sep 17 00:00:00 2001 From: Fan Yang <52458914+fanyang-mono@users.noreply.github.com> Date: Tue, 20 Feb 2024 23:21:49 -0500 Subject: [PATCH 157/158] [Mono] Intrinsify StoreSelectedScalar (#98710) * Intrinsify StoreSelectedScalar * Update API usage to be aligned with other places --- .../Arm/AdvSimd.PlatformNotSupported.cs | 8 -- .../System/Runtime/Intrinsics/Arm/AdvSimd.cs | 8 -- .../ref/System.Runtime.Intrinsics.cs | 6 - src/mono/mono/mini/llvm-intrinsics.h | 6 + src/mono/mono/mini/mini-llvm.c | 96 ++++++++++++++-- src/mono/mono/mini/mini-ops.h | 1 + src/mono/mono/mini/simd-intrinsics.c | 10 +- .../GenerateHWIntrinsicTests_Arm.cs | 104 +++++++++--------- 8 files changed, 153 insertions(+), 86 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs index 7ccb2b70e6eb15..3202d6dc94a628 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.PlatformNotSupported.cs @@ -3776,9 +3776,6 @@ internal Arm64() { } /// public static unsafe void StorePairScalarNonTemporal(uint* address, Vector64 value1, Vector64 value2) { throw new PlatformNotSupportedException(); } -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - /// /// void vst2_lane_s8 (int8_t * ptr, int8x16x2_t val, const int lane) /// A64: ST2 { Vt.16B, Vt+1.16B }[index], [Xn] @@ -3949,7 +3946,6 @@ internal Arm64() { } /// A64: ST3 { Vt.2D, Vt+1.2D, Vt+2.2D, Vt+3.2D }[index], [Xn] /// public static unsafe void StoreSelectedScalar(double* address, (Vector128 value1, Vector128 value2, Vector128 value3, Vector128 value4) value, [ConstantExpected(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } -#endif /// /// A64: ST2 { Vn.16B, Vn+1.16B }, [Xn] @@ -15932,9 +15928,6 @@ internal Arm64() { } /// public static unsafe void StoreSelectedScalar(ulong* address, Vector128 value, [ConstantExpected(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - /// /// A64: ST2 { Vt.8B, Vt+1.8B }[index], [Xn] /// @@ -16039,7 +16032,6 @@ internal Arm64() { } /// A64: ST4 { Vt.2S, Vt+1.2S, Vt+2.2S, Vt+3.2S }[index], [Xn] /// public static unsafe void StoreSelectedScalar(float* address, (Vector64 value1, Vector64 value2, Vector64 value3, Vector64 value4) value, [ConstantExpected(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } -#endif /// /// A64: ST2 { Vn.8B, Vn+1.8B }, [Xn] diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs index 8ead33cfde5afc..d343750838cd6e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/AdvSimd.cs @@ -3774,9 +3774,6 @@ internal Arm64() { } /// public static unsafe void StorePairScalarNonTemporal(uint* address, Vector64 value1, Vector64 value2) => StorePairScalarNonTemporal(address, value1, value2); -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - /// /// void vst2_lane_s8 (int8_t * ptr, int8x16x2_t val, const int lane) /// A64: ST2 { Vt.16B, Vt+1.16B }[index], [Xn] @@ -3947,7 +3944,6 @@ internal Arm64() { } /// A64: ST4 { Vt.2D, Vt+1.2D, Vt+2.2D, Vt+3.2D }[index], [Xn] /// public static unsafe void StoreSelectedScalar(double* address, (Vector128 value1, Vector128 value2, Vector128 value3, Vector128 value4) value, [ConstantExpected(Max = (byte)(1))] byte index) => StoreSelectedScalar(address, value, index); -#endif /// /// A64: ST2 { Vn.16B, Vn+1.16B }, [Xn] @@ -15929,9 +15925,6 @@ internal Arm64() { } /// public static unsafe void StoreSelectedScalar(ulong* address, Vector128 value, [ConstantExpected(Max = (byte)(1))] byte index) => StoreSelectedScalar(address, value, index); -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - /// /// A64: ST2 { Vt.8B, Vt+1.8B }[index], [Xn] /// @@ -16036,7 +16029,6 @@ internal Arm64() { } /// A64: ST4 { Vt.2S, Vt+1.2S, Vt+2.2S, Vt+3.2S }[index], [Xn] /// public static unsafe void StoreSelectedScalar(float* address, (Vector64 value1, Vector64 value2, Vector64 value3, Vector64 value4) value, [ConstantExpected(Max = (byte)(1))] byte index) => StoreSelectedScalar(address, value, index); -#endif /// /// A64: ST2 { Vn.8B, Vn+1.8B }, [Xn] diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index 82e179628b11f4..e74646eed09f7e 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -2904,8 +2904,6 @@ public static unsafe void StoreSelectedScalar(ushort* address, System.Runtime.In public static unsafe void StoreSelectedScalar(uint* address, System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(3))] byte index) { } public static unsafe void StoreSelectedScalar(uint* address, System.Runtime.Intrinsics.Vector64 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { } public static unsafe void StoreSelectedScalar(ulong* address, System.Runtime.Intrinsics.Vector128 value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { } -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 public static unsafe void StoreSelectedScalar(byte* address, (System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(7))] byte index) { throw null; } public static unsafe void StoreSelectedScalar(sbyte* address, (System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(7))] byte index) { throw null; } public static unsafe void StoreSelectedScalar(short* address, (System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(3))] byte index) { throw null; } @@ -2927,7 +2925,6 @@ public static unsafe void StoreSelectedScalar(ulong* address, System.Runtime.Int public static unsafe void StoreSelectedScalar(int* address, (System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2, System.Runtime.Intrinsics.Vector64 value3, System.Runtime.Intrinsics.Vector64 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } public static unsafe void StoreSelectedScalar(uint* address, (System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2, System.Runtime.Intrinsics.Vector64 value3, System.Runtime.Intrinsics.Vector64 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } public static unsafe void StoreSelectedScalar(float* address, (System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2, System.Runtime.Intrinsics.Vector64 value3, System.Runtime.Intrinsics.Vector64 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { throw new PlatformNotSupportedException(); } -#endif public unsafe static void StoreVector64x2AndZip(byte* address, (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2) value) { throw null; } public unsafe static void StoreVector64x2AndZip(sbyte* address, (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2) value) { throw null; } public unsafe static void StoreVector64x2AndZip(short* address, (System.Runtime.Intrinsics.Vector64 Value1, System.Runtime.Intrinsics.Vector64 Value2) value) { throw null; } @@ -3748,8 +3745,6 @@ public static unsafe void StorePairScalar(uint* address, System.Runtime.Intrinsi public static unsafe void StorePairScalarNonTemporal(int* address, System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2) { } public static unsafe void StorePairScalarNonTemporal(float* address, System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2) { } public static unsafe void StorePairScalarNonTemporal(uint* address, System.Runtime.Intrinsics.Vector64 value1, System.Runtime.Intrinsics.Vector64 value2) { } -#if false - // Should be disabled until Mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 public static unsafe void StoreSelectedScalar(byte* address, (System.Runtime.Intrinsics.Vector128 value1, System.Runtime.Intrinsics.Vector128 value2) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(15))] byte index) { throw null; } public static unsafe void StoreSelectedScalar(sbyte* address, (System.Runtime.Intrinsics.Vector128 value1, System.Runtime.Intrinsics.Vector128 value2) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(15))] byte index) { throw null; } public static unsafe void StoreSelectedScalar(short* address, (System.Runtime.Intrinsics.Vector128 value1, System.Runtime.Intrinsics.Vector128 value2) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(7))] byte index) { throw null; } @@ -3780,7 +3775,6 @@ public static unsafe void StorePairScalarNonTemporal(uint* address, System.Runti public static unsafe void StoreSelectedScalar(ulong* address, (System.Runtime.Intrinsics.Vector128 value1, System.Runtime.Intrinsics.Vector128 value2, System.Runtime.Intrinsics.Vector128 value3, System.Runtime.Intrinsics.Vector128 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { throw null; } public static unsafe void StoreSelectedScalar(float* address, (System.Runtime.Intrinsics.Vector128 value1, System.Runtime.Intrinsics.Vector128 value2, System.Runtime.Intrinsics.Vector128 value3, System.Runtime.Intrinsics.Vector128 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(3))] byte index) { throw null; } public static unsafe void StoreSelectedScalar(double* address, (System.Runtime.Intrinsics.Vector128 value1, System.Runtime.Intrinsics.Vector128 value2, System.Runtime.Intrinsics.Vector128 value3, System.Runtime.Intrinsics.Vector128 value4) value, [System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute(Max = (byte)(1))] byte index) { throw null; } -#endif public unsafe static void StoreVector128x2AndZip(byte* address, (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2) value) { throw null; } public unsafe static void StoreVector128x2AndZip(sbyte* address, (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2) value) { throw null; } public unsafe static void StoreVector128x2AndZip(short* address, (System.Runtime.Intrinsics.Vector128 Value1, System.Runtime.Intrinsics.Vector128 Value2) value) { throw null; } diff --git a/src/mono/mono/mini/llvm-intrinsics.h b/src/mono/mono/mini/llvm-intrinsics.h index ff8dfa2998a335..d4c7deea9ee8a3 100644 --- a/src/mono/mono/mini/llvm-intrinsics.h +++ b/src/mono/mono/mini/llvm-intrinsics.h @@ -386,6 +386,12 @@ INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD4LANE_V64, aarch64_neon_ld4lane, Arm64, INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD2LANE_V128, aarch64_neon_ld2lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD3LANE_V128, aarch64_neon_ld3lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_LD4LANE_V128, aarch64_neon_ld4lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST2LANE_V64, aarch64_neon_st2lane, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST3LANE_V64, aarch64_neon_st3lane, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST4LANE_V64, aarch64_neon_st4lane, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST2LANE_V128, aarch64_neon_st2lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST3LANE_V128, aarch64_neon_st3lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) +INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST4LANE_V128, aarch64_neon_st4lane, Arm64, AddPointer, V128 | I1 | I2 | I4 | I8 | R4 | R8) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST1X2_V64, aarch64_neon_st1x2, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST1X3_V64, aarch64_neon_st1x3, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) INTRINS_OVR_TAG_KIND(AARCH64_ADV_SIMD_ST1X4_V64, aarch64_neon_st1x4, Arm64, AddPointer, V64 | I1 | I2 | I4 | R4) diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index 5db5828eaaa91d..f7e59ef5acbbf6 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -685,12 +685,12 @@ simd_valuetuple_to_llvm_type (EmitContext *ctx, MonoClass *klass) { const char *klass_name = m_class_get_name (klass); g_assert (strstr (klass_name, "ValueTuple") != NULL); - MonoGenericInst *classInst = mono_class_get_generic_class (klass)->context.class_inst; - MonoType *etype = classInst->type_argv [0]; + MonoGenericInst *class_inst = mono_class_get_generic_class (klass)->context.class_inst; + MonoType *etype = class_inst->type_argv [0]; g_assert (etype->type == MONO_TYPE_GENERICINST); MonoClass *eklass = etype->data.generic_class->cached_class; LLVMTypeRef ltype = simd_class_to_llvm_type (ctx, eklass); - return LLVMArrayType (ltype, classInst->type_argc); + return LLVMArrayType (ltype, class_inst->type_argc); } /* Return the 128 bit SIMD type corresponding to the mono type TYPE */ @@ -5473,7 +5473,9 @@ immediate_unroll_begin ( LLVMBasicBlockRef continuation = gen_bb (ctx, name); LLVMValueRef switch_ins = LLVMBuildSwitch (ctx->builder, switch_index, default_case, max_cases); LLVMPositionBuilderAtEnd (ctx->builder, continuation); - LLVMValueRef phi = LLVMBuildPhi (ctx->builder, return_type, name); + LLVMValueRef phi = NULL; + if (return_type != LLVMVoidType ()) + phi = LLVMBuildPhi (ctx->builder, return_type, name); ImmediateUnrollCtx ictx = { 0 }; ictx.ctx = ctx; ictx.bb = bb; @@ -5504,7 +5506,8 @@ immediate_unroll_commit (ImmediateUnrollCtx *ictx, int switch_const, LLVMValueRe { LLVMBuildBr (ictx->ctx->builder, ictx->continuation); LLVMAddCase (ictx->switch_ins, LLVMConstInt (ictx->switch_index_type, switch_const, FALSE), ictx->tmp_block); - LLVMAddIncoming (ictx->phi, &value, &ictx->tmp_block, 1); + if (ictx->phi) + LLVMAddIncoming (ictx->phi, &value, &ictx->tmp_block, 1); } static void @@ -5517,7 +5520,8 @@ static void immediate_unroll_commit_default (ImmediateUnrollCtx *ictx, LLVMValueRef value) { LLVMBuildBr (ictx->ctx->builder, ictx->continuation); - LLVMAddIncoming (ictx->phi, &value, &ictx->default_case, 1); + if (ictx->phi) + LLVMAddIncoming (ictx->phi, &value, &ictx->default_case, 1); } static void @@ -11633,7 +11637,7 @@ MONO_RESTORE_WARNING lhs = LLVMBuildLoad2 (builder, ret_t, addresses [ins->sreg1]->value, ""); - LLVMValueRef *args = g_new0(LLVMValueRef, n_elem_tuple + 2); + LLVMValueRef *args = g_newa0(LLVMValueRef, n_elem_tuple + 2); unsigned int idx = 0; for ( ; idx < n_elem_tuple; idx++) { args [idx] = LLVMBuildExtractValue (builder, lhs, idx, "extract_elem"); @@ -11750,7 +11754,7 @@ MONO_RESTORE_WARNING break; } case OP_ARM64_STM: { - LLVMTypeRef tuple_t = simd_valuetuple_to_llvm_type (ctx, ins->klass); + LLVMTypeRef tuple_t = simd_class_to_llvm_type (ctx, ins->klass); LLVMTypeRef vec_t = LLVMGetElementType (tuple_t); IntrinsicId iid = (IntrinsicId) ins->inst_c0; @@ -11786,6 +11790,82 @@ MONO_RESTORE_WARNING mono_llvm_build_aligned_store (builder, val, address, FALSE, alignment); break; } + case OP_ARM64_STM_SCALAR: { + LLVMTypeRef tuple_t = simd_class_to_llvm_type (ctx, ins->klass); + LLVMTypeRef vec_t = LLVMGetElementType (tuple_t); + unsigned int n_elem_tuple = LLVMGetArrayLength (tuple_t); + unsigned int n_elem_vector = LLVMGetVectorSize (vec_t); + LLVMTypeRef elem_t = LLVMGetElementType (vec_t); + unsigned int elem_bits = mono_llvm_get_prim_size_bits (elem_t); + unsigned int vector_size = n_elem_vector * elem_bits; + IntrinsicId iid; + switch (vector_size) { + case 64: { + switch (n_elem_tuple) { + case 2: + iid = INTRINS_AARCH64_ADV_SIMD_ST2LANE_V64; + break; + case 3: + iid = INTRINS_AARCH64_ADV_SIMD_ST3LANE_V64; + break; + case 4: + iid = INTRINS_AARCH64_ADV_SIMD_ST4LANE_V64; + break; + default: + g_assert_not_reached (); + break; + } + break; + } + case 128: { + switch (n_elem_tuple) { + case 2: + iid = INTRINS_AARCH64_ADV_SIMD_ST2LANE_V128; + break; + case 3: + iid = INTRINS_AARCH64_ADV_SIMD_ST3LANE_V128; + break; + case 4: + iid = INTRINS_AARCH64_ADV_SIMD_ST4LANE_V128; + break; + default: + g_assert_not_reached (); + break; + } + break; + } + default: + g_assert_not_reached (); + break; + + } + + rhs = LLVMBuildLoad2 (builder, tuple_t, addresses [ins->sreg2]->value, ""); + + LLVMValueRef *args = g_newa0(LLVMValueRef, n_elem_tuple + 2); + unsigned int idx = 0; + for ( ; idx < n_elem_tuple; idx++) { + args [idx] = LLVMBuildExtractValue (builder, rhs, idx, "extract_elem"); + } + args [idx++] = arg3; + args [idx] = lhs; + + llvm_ovr_tag_t ovr_tag = ovr_tag_from_llvm_type (vec_t); + + // convert arg3 to a constant + LLVMTypeRef ret_t = LLVMVoidType (); + ImmediateUnrollCtx ictx = immediate_unroll_begin (ctx, bb, 16, arg3, ret_t, ""); + int i = 0; + while (immediate_unroll_next (&ictx, &i)) { + args [idx - 1] = const_int64 (i); + call_overloaded_intrins (ctx, iid, ovr_tag, args, ""); + immediate_unroll_commit (&ictx, i, NULL); + } + immediate_unroll_default (&ictx); + immediate_unroll_commit_default (&ictx, NULL); + immediate_unroll_end (&ictx, &cbb); + break; + } case OP_ARM64_ADDHN: case OP_ARM64_ADDHN2: case OP_ARM64_SUBHN: diff --git a/src/mono/mono/mini/mini-ops.h b/src/mono/mono/mini/mini-ops.h index 81d9dd29332adf..3221458d2ae55e 100644 --- a/src/mono/mono/mini/mini-ops.h +++ b/src/mono/mono/mini/mini-ops.h @@ -1664,6 +1664,7 @@ MINI_OP(OP_ARM64_UZP2, "arm64_uzp2", XREG, XREG, XREG) MINI_OP(OP_ARM64_ZIP1, "arm64_zip1", XREG, XREG, XREG) MINI_OP(OP_ARM64_ZIP2, "arm64_zip2", XREG, XREG, XREG) MINI_OP3(OP_ARM64_ST1_SCALAR, "arm64_st1_scalar", NONE, IREG, XREG, IREG) +MINI_OP3(OP_ARM64_STM_SCALAR, "arm64_stm_scalar", NONE, IREG, VREG, IREG) MINI_OP3(OP_ARM64_STNP, "arm64_stnp", NONE, IREG, XREG, XREG) MINI_OP3(OP_ARM64_STNP_SCALAR, "arm64_stnp_scalar", NONE, IREG, XREG, XREG) MINI_OP3(OP_ARM64_STP, "arm64_stp", NONE, IREG, XREG, XREG) diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index bed77a0fe66e7d..60e58c745c081d 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -4184,9 +4184,13 @@ emit_arm64_intrinsics ( return ret; } case SN_StoreSelectedScalar: { - if (!is_intrinsics_vector_type (fsig->params [1])) - return NULL; - return emit_simd_ins_for_sig (cfg, klass, OP_ARM64_ST1_SCALAR, 0, arg0_type, fsig, args); + int store_op; + if (is_intrinsics_vector_type (fsig->params [1])) + store_op = OP_ARM64_ST1_SCALAR; + else + store_op = OP_ARM64_STM_SCALAR; + MonoClass* klass_tuple_var = mono_class_from_mono_type_internal (fsig->params [1]); + return emit_simd_ins_for_sig (cfg, klass_tuple_var, store_op, 0, arg0_type, fsig, args); } case SN_MultiplyRoundedDoublingBySelectedScalarSaturateHigh: case SN_MultiplyRoundedDoublingScalarBySelectedScalarSaturateHigh: diff --git a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs index 510948877cd8b9..fd27d934981926 100644 --- a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs +++ b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_Arm.cs @@ -1702,28 +1702,27 @@ ("StoreSelectedScalarTest.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128_UInt16_7", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["ElementIndex"] = "7", ["ValidateResult"] = "firstOp[ElementIndex] != result"}), ("StoreSelectedScalarTest.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128_UInt32_3", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["ElementIndex"] = "3", ["ValidateResult"] = "firstOp[ElementIndex] != result"}), ("StoreSelectedScalarTest.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128_UInt64_1", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["ElementIndex"] = "1", ["ValidateResult"] = "firstOp[ElementIndex] != result"}), - // Tests disabled until mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x2_Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x3_Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_Short", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_UInt32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_Int32", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector64x4_Float", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2SByte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2Byte", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector64x2UShort", ["Isa"] = "AdvSimd", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector64x2", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector64", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "8", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), @@ -2543,37 +2542,36 @@ ("StoreBinOpTest.template", new Dictionary { ["TestName"] = "StorePairNonTemporal_Vector128_UInt16", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StorePairNonTemporal", ["RetBaseType"] = "UInt16", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "32", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt16()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "Helpers.Concat(firstOp, secondOp, i) != result[i]"}), ("StoreBinOpTest.template", new Dictionary { ["TestName"] = "StorePairNonTemporal_Vector128_UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StorePairNonTemporal", ["RetBaseType"] = "UInt32", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "32", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt32()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateIterResult"] = "Helpers.Concat(firstOp, secondOp, i) != result[i]"}), ("StoreBinOpTest.template", new Dictionary { ["TestName"] = "StorePairNonTemporal_Vector128_UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StorePairNonTemporal", ["RetBaseType"] = "UInt64", ["Op1VectorType"] = "Vector128", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "32", ["NextValueOp1"] = "TestLibrary.Generator.GetUInt64()", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateIterResult"] = "Helpers.Concat(firstOp, secondOp, i) != result[i]"}), - // Tests disabled until mono implements these APIs. See https://github.com/dotnet/runtime/issues/93081 - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), - // ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx2Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x2_Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx3Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x3_Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Short", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt16()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_UInt32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Int32", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int32", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int32", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt32()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_UInt64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "UInt64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Int64", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "Int64", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Int64", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetInt64()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Float", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "float", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "float", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSingle()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), + ("StoreSelectedScalarx4Test.template", new Dictionary { ["TestName"] = "StoreSelectedScalar_Vector128x4_Double", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreSelectedScalar", ["Op1BaseType"] = "double", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "double", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetDouble()", ["ValidateResult"] = "input1[index] != result[0] || input2[index] != result[1] || input3[index] != result[2] || input4[index] != result[3]"}), ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2SByte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "SByte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "SByte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetSByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2Byte", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "Byte", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "Byte", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetByte()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), ("StoreVectorx2Test.template", new Dictionary { ["TestName"] = "StoreVector128x2UShort", ["Isa"] = "AdvSimd.Arm64", ["LoadIsa"] = "AdvSimd", ["Method"] = "StoreVector128x2", ["Op1BaseType"] = "UInt16", ["Op2VectorType"] = "Vector128", ["Op2BaseType"] = "UInt16", ["LargestVectorSize"] = "16", ["NextValueOp2"] = "TestLibrary.Generator.GetUInt16()", ["ValidateIterResult"] = "input1[i] != result[i] || input2[i] != result[OpElementCount + i]"}), From 8cc7586135856499ea5123bbfb71a5df3fa7f717 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 21 Feb 2024 12:22:35 +0800 Subject: [PATCH 158/158] Convert fast path of ValueType.GetHashCode to managed (#97590) * Access CanCompareBits from MethodTableAuxiliaryData * Switch to QCall * Fast path of GetHashCode * Call for RegularGetValueTypeHashCode * Cleanup unused FCall * Use HashCode type * Handle recursive case inside native code * Remove Double and Single from CoreLibBinder * Apply suggestions from code review Co-authored-by: Jan Kotas --------- Co-authored-by: Jan Kotas --- .../RuntimeHelpers.CoreCLR.cs | 36 ++- .../src/System/ValueType.cs | 88 ++++++- src/coreclr/vm/comutilnative.cpp | 247 ++++++------------ src/coreclr/vm/comutilnative.h | 8 +- src/coreclr/vm/corelib.h | 6 - src/coreclr/vm/ecalllist.h | 6 - src/coreclr/vm/qcallentrypoints.cpp | 2 + .../System/ValueTypeTests.cs | 21 ++ 8 files changed, 216 insertions(+), 198 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 4e75d7db895ce4..02ecf965689279 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -462,7 +462,13 @@ internal unsafe struct MethodTable // Additional conditional fields (see methodtable.h). // m_pModule - // m_pAuxiliaryData + + /// + /// A pointer to auxiliary data that is cold for method table. + /// + [FieldOffset(AuxiliaryDataOffset)] + public MethodTableAuxiliaryData* AuxiliaryData; + // union { // m_pEEClass (pointer to the EE class) // m_pCanonMT (pointer to the canonical method table) @@ -523,6 +529,12 @@ internal unsafe struct MethodTable private const int ParentMethodTableOffset = 0x10 + DebugClassNamePtr; +#if TARGET_64BIT + private const int AuxiliaryDataOffset = 0x20 + DebugClassNamePtr; +#else + private const int AuxiliaryDataOffset = 0x18 + DebugClassNamePtr; +#endif + #if TARGET_64BIT private const int ElementTypeOffset = 0x30 + DebugClassNamePtr; #else @@ -610,6 +622,28 @@ public TypeHandle GetArrayElementTypeHandle() public extern uint GetNumInstanceFieldBytes(); } + // Subset of src\vm\methodtable.h + [StructLayout(LayoutKind.Explicit)] + internal unsafe struct MethodTableAuxiliaryData + { + [FieldOffset(0)] + private uint Flags; + + private const uint enum_flag_CanCompareBitsOrUseFastGetHashCode = 0x0001; // Is any field type or sub field type overrode Equals or GetHashCode + private const uint enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode = 0x0002; // Whether we have checked the overridden Equals or GetHashCode + + public bool HasCheckedCanCompareBitsOrUseFastGetHashCode => (Flags & enum_flag_HasCheckedCanCompareBitsOrUseFastGetHashCode) != 0; + + public bool CanCompareBitsOrUseFastGetHashCode + { + get + { + Debug.Assert(HasCheckedCanCompareBitsOrUseFastGetHashCode); + return (Flags & enum_flag_CanCompareBitsOrUseFastGetHashCode) != 0; + } + } + } + /// /// A type handle, which can wrap either a pointer to a TypeDesc or to a . /// diff --git a/src/coreclr/System.Private.CoreLib/src/System/ValueType.cs b/src/coreclr/System.Private.CoreLib/src/System/ValueType.cs index cc13e37e083f01..78301866c36dce 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/ValueType.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/ValueType.cs @@ -10,15 +10,17 @@ ** ===========================================================*/ +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System { [Serializable] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public abstract class ValueType + public abstract partial class ValueType { [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", Justification = "Trimmed fields don't make a difference for equality")] @@ -36,7 +38,7 @@ public override unsafe bool Equals([NotNullWhen(true)] object? obj) // if there are no GC references in this object we can avoid reflection // and do a fast memcmp - if (CanCompareBits(this)) + if (CanCompareBitsOrUseFastGetHashCode(RuntimeHelpers.GetMethodTable(obj))) // MethodTable kept alive by access to object below { return SpanHelpers.SequenceEqual( ref RuntimeHelpers.GetRawData(this), @@ -66,8 +68,23 @@ ref RuntimeHelpers.GetRawData(obj), return true; } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool CanCompareBits(object obj); + // Return true if the valuetype does not contain pointer, is tightly packed, + // does not have floating point number field and does not override Equals method. + private static unsafe bool CanCompareBitsOrUseFastGetHashCode(MethodTable* pMT) + { + MethodTableAuxiliaryData* pAuxData = pMT->AuxiliaryData; + + if (pAuxData->HasCheckedCanCompareBitsOrUseFastGetHashCode) + { + return pAuxData->CanCompareBitsOrUseFastGetHashCode; + } + + return CanCompareBitsOrUseFastGetHashCodeHelper(pMT); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MethodTable_CanCompareBitsOrUseFastGetHashCode")] + [return: MarshalAs(UnmanagedType.Bool)] + private static unsafe partial bool CanCompareBitsOrUseFastGetHashCodeHelper(MethodTable* pMT); /*=================================GetHashCode================================== **Action: Our algorithm for returning the hashcode is a little bit complex. We look @@ -79,8 +96,67 @@ ref RuntimeHelpers.GetRawData(obj), **Arguments: None. **Exceptions: None. ==============================================================================*/ - [MethodImpl(MethodImplOptions.InternalCall)] - public extern override int GetHashCode(); + public override unsafe int GetHashCode() + { + // The default implementation of GetHashCode() for all value types. + // Note that this implementation reveals the value of the fields. + // So if the value type contains any sensitive information it should + // implement its own GetHashCode(). + + MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); + ref byte rawData = ref RuntimeHelpers.GetRawData(this); + HashCode hashCode = default; + + // To get less colliding and more evenly distributed hash codes, + // we munge the class index into the hashcode + hashCode.Add((IntPtr)pMT); + + if (CanCompareBitsOrUseFastGetHashCode(pMT)) + { + // this is a struct with no refs and no "strange" offsets + uint size = pMT->GetNumInstanceFieldBytes(); + hashCode.AddBytes(MemoryMarshal.CreateReadOnlySpan(ref rawData, (int)size)); + } + else + { + object thisRef = this; + switch (GetHashCodeStrategy(pMT, ObjectHandleOnStack.Create(ref thisRef), out uint fieldOffset, out uint fieldSize)) + { + case ValueTypeHashCodeStrategy.ReferenceField: + hashCode.Add(Unsafe.As(ref Unsafe.AddByteOffset(ref rawData, fieldOffset)).GetHashCode()); + break; + + case ValueTypeHashCodeStrategy.DoubleField: + hashCode.Add(Unsafe.As(ref Unsafe.AddByteOffset(ref rawData, fieldOffset)).GetHashCode()); + break; + + case ValueTypeHashCodeStrategy.SingleField: + hashCode.Add(Unsafe.As(ref Unsafe.AddByteOffset(ref rawData, fieldOffset)).GetHashCode()); + break; + + case ValueTypeHashCodeStrategy.FastGetHashCode: + Debug.Assert(fieldSize != 0); + hashCode.AddBytes(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AddByteOffset(ref rawData, fieldOffset), (int)fieldSize)); + break; + } + } + + return hashCode.ToHashCode(); + } + + // Must match the definition in src\vm\comutilnative.cpp + private enum ValueTypeHashCodeStrategy + { + None, + ReferenceField, + DoubleField, + SingleField, + FastGetHashCode, + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ValueType_GetHashCodeStrategy")] + private static unsafe partial ValueTypeHashCodeStrategy GetHashCodeStrategy( + MethodTable* pMT, ObjectHandleOnStack objHandle, out uint fieldOffset, out uint fieldSize); public override string? ToString() { diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index 027c4ae8903aed..612cb9d72dc0dd 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -1681,226 +1681,127 @@ BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt) return canCompareBitsOrUseFastGetHashCode; } -NOINLINE static FC_BOOL_RET CanCompareBitsHelper(MethodTable* mt, OBJECTREF objRef) +extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable * mt) { - FC_INNER_PROLOG(ValueTypeHelper::CanCompareBits); - - _ASSERTE(mt != NULL); - _ASSERTE(objRef != NULL); + QCALL_CONTRACT; BOOL ret = FALSE; - HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef); + BEGIN_QCALL; ret = CanCompareBitsOrUseFastGetHashCode(mt); - HELPER_METHOD_FRAME_END(); - FC_INNER_EPILOG(); + END_QCALL; - FC_RETURN_BOOL(ret); + return ret; } -// Return true if the valuetype does not contain pointer, is tightly packed, -// does not have floating point number field and does not override Equals method. -FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj) +enum ValueTypeHashCodeStrategy { - FCALL_CONTRACT; - - _ASSERTE(obj != NULL); - MethodTable* mt = obj->GetMethodTable(); + None, + ReferenceField, + DoubleField, + SingleField, + FastGetHashCode, +}; - if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode()) - { - FC_RETURN_BOOL(mt->CanCompareBitsOrUseFastGetHashCode()); - } - - OBJECTREF objRef(obj); - - FC_INNER_RETURN(FC_BOOL_RET, CanCompareBitsHelper(mt, objRef)); -} -FCIMPLEND - -static INT32 FastGetValueTypeHashCodeHelper(MethodTable *mt, void *pObjRef) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } CONTRACTL_END; - - INT32 hashCode = 0; - INT32 *pObj = (INT32*)pObjRef; - - // this is a struct with no refs and no "strange" offsets, just go through the obj and xor the bits - INT32 size = mt->GetNumInstanceFieldBytes(); - for (INT32 i = 0; i < (INT32)(size / sizeof(INT32)); i++) - hashCode ^= *pObj++; - - return hashCode; -} - -static INT32 RegularGetValueTypeHashCode(MethodTable *mt, void *pObjRef) +static ValueTypeHashCodeStrategy GetHashCodeStrategy(MethodTable* mt, QCall::ObjectHandleOnStack objHandle, UINT32* fieldOffset, UINT32* fieldSize) { CONTRACTL { THROWS; GC_TRIGGERS; - MODE_COOPERATIVE; + MODE_PREEMPTIVE; } CONTRACTL_END; - INT32 hashCode = 0; + // Should be handled by caller + _ASSERTE(!mt->CanCompareBitsOrUseFastGetHashCode()); - GCPROTECT_BEGININTERIOR(pObjRef); + ValueTypeHashCodeStrategy ret = ValueTypeHashCodeStrategy::None; - BOOL canUseFastGetHashCodeHelper = FALSE; - if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode()) - { - canUseFastGetHashCodeHelper = mt->CanCompareBitsOrUseFastGetHashCode(); - } - else - { - canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(mt); - } + // Grab the first non-null field and return its hash code or 'it' as hash code + ApproxFieldDescIterator fdIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS); - // While we should not get here directly from ValueTypeHelper::GetHashCode, if we recurse we need to - // be able to handle getting the hashcode for an embedded structure whose hashcode is computed by the fast path. - if (canUseFastGetHashCodeHelper) + FieldDesc *field; + while ((field = fdIterator.Next()) != NULL) { - hashCode = FastGetValueTypeHashCodeHelper(mt, pObjRef); - } - else - { - // it's looking ugly so we'll use the old behavior in managed code. Grab the first non-null - // field and return its hash code or 'it' as hash code - // Note that the old behavior has already been broken for value types - // that is qualified for CanUseFastGetHashCodeHelper. So maybe we should - // change the implementation here to use all fields instead of just the 1st one. - // - // - // check this approximation - we may be losing exact type information - ApproxFieldDescIterator fdIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS); - - FieldDesc *field; - while ((field = fdIterator.Next()) != NULL) + _ASSERTE(!field->IsRVA()); + if (field->IsObjRef()) { - _ASSERTE(!field->IsRVA()); - if (field->IsObjRef()) + GCX_COOP(); + // if we get an object reference we get the hash code out of that + if (*(Object**)((BYTE *)objHandle.Get()->UnBox() + *fieldOffset + field->GetOffsetUnsafe()) != NULL) { - // if we get an object reference we get the hash code out of that - if (*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe()) != NULL) - { - PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__OBJECT__GET_HASH_CODE, (*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe()))); - DECLARE_ARGHOLDER_ARRAY(args, 1); - args[ARGNUM_0] = PTR_TO_ARGHOLDER(*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe())); - CALL_MANAGED_METHOD(hashCode, INT32, args); - } - else - { - // null object reference, try next - continue; - } + *fieldOffset += field->GetOffsetUnsafe(); + ret = ValueTypeHashCodeStrategy::ReferenceField; } else { - CorElementType fieldType = field->GetFieldType(); - if (fieldType == ELEMENT_TYPE_R8) - { - PREPARE_NONVIRTUAL_CALLSITE(METHOD__DOUBLE__GET_HASH_CODE); - DECLARE_ARGHOLDER_ARRAY(args, 1); - args[ARGNUM_0] = PTR_TO_ARGHOLDER(((BYTE *)pObjRef + field->GetOffsetUnsafe())); - CALL_MANAGED_METHOD(hashCode, INT32, args); - } - else if (fieldType == ELEMENT_TYPE_R4) - { - PREPARE_NONVIRTUAL_CALLSITE(METHOD__SINGLE__GET_HASH_CODE); - DECLARE_ARGHOLDER_ARRAY(args, 1); - args[ARGNUM_0] = PTR_TO_ARGHOLDER(((BYTE *)pObjRef + field->GetOffsetUnsafe())); - CALL_MANAGED_METHOD(hashCode, INT32, args); - } - else if (fieldType != ELEMENT_TYPE_VALUETYPE) + // null object reference, try next + continue; + } + } + else + { + CorElementType fieldType = field->GetFieldType(); + if (fieldType == ELEMENT_TYPE_R8) + { + *fieldOffset += field->GetOffsetUnsafe(); + ret = ValueTypeHashCodeStrategy::DoubleField; + } + else if (fieldType == ELEMENT_TYPE_R4) + { + *fieldOffset += field->GetOffsetUnsafe(); + ret = ValueTypeHashCodeStrategy::SingleField; + } + else if (fieldType != ELEMENT_TYPE_VALUETYPE) + { + *fieldOffset += field->GetOffsetUnsafe(); + *fieldSize = field->LoadSize(); + ret = ValueTypeHashCodeStrategy::FastGetHashCode; + } + else + { + // got another value type. Get the type + TypeHandle fieldTH = field->GetFieldTypeHandleThrowing(); + _ASSERTE(!fieldTH.IsNull()); + MethodTable* fieldMT = fieldTH.GetMethodTable(); + if (CanCompareBitsOrUseFastGetHashCode(fieldMT)) { - UINT fieldSize = field->LoadSize(); - INT32 *pValue = (INT32*)((BYTE *)pObjRef + field->GetOffsetUnsafe()); - for (INT32 j = 0; j < (INT32)(fieldSize / sizeof(INT32)); j++) - hashCode ^= *pValue++; + *fieldOffset += field->GetOffsetUnsafe(); + *fieldSize = field->LoadSize(); + ret = ValueTypeHashCodeStrategy::FastGetHashCode; } else { - // got another value type. Get the type - TypeHandle fieldTH = field->GetFieldTypeHandleThrowing(); - _ASSERTE(!fieldTH.IsNull()); - hashCode = RegularGetValueTypeHashCode(fieldTH.GetMethodTable(), (BYTE *)pObjRef + field->GetOffsetUnsafe()); + *fieldOffset += field->GetOffsetUnsafe(); + ret = GetHashCodeStrategy(fieldMT, objHandle, fieldOffset, fieldSize); } } - break; } + break; } - GCPROTECT_END(); - - return hashCode; + return ret; } -// The default implementation of GetHashCode() for all value types. -// Note that this implementation reveals the value of the fields. -// So if the value type contains any sensitive information it should -// implement its own GetHashCode(). -FCIMPL1(INT32, ValueTypeHelper::GetHashCode, Object* objUNSAFE) +extern "C" INT32 QCALLTYPE ValueType_GetHashCodeStrategy(MethodTable* mt, QCall::ObjectHandleOnStack objHandle, UINT32* fieldOffset, UINT32* fieldSize) { - FCALL_CONTRACT; - - if (objUNSAFE == NULL) - FCThrow(kNullReferenceException); - - OBJECTREF obj = ObjectToOBJECTREF(objUNSAFE); - VALIDATEOBJECTREF(obj); + QCALL_CONTRACT; - INT32 hashCode = 0; - MethodTable *pMT = objUNSAFE->GetMethodTable(); + ValueTypeHashCodeStrategy ret = ValueTypeHashCodeStrategy::None; + *fieldOffset = 0; + *fieldSize = 0; - // We don't want to expose the method table pointer in the hash code - // Let's use the typeID instead. - UINT32 typeID = pMT->LookupTypeID(); - if (typeID == TypeIDProvider::INVALID_TYPE_ID) - { - // If the typeID has yet to be generated, fall back to GetTypeID - // This only needs to be done once per MethodTable - HELPER_METHOD_FRAME_BEGIN_RET_1(obj); - typeID = pMT->GetTypeID(); - HELPER_METHOD_FRAME_END(); - } + BEGIN_QCALL; - // To get less colliding and more evenly distributed hash codes, - // we munge the class index with two big prime numbers - hashCode = typeID * 711650207 + 2506965631U; - BOOL canUseFastGetHashCodeHelper = FALSE; - if (pMT->HasCheckedCanCompareBitsOrUseFastGetHashCode()) - { - canUseFastGetHashCodeHelper = pMT->CanCompareBitsOrUseFastGetHashCode(); - } - else - { - HELPER_METHOD_FRAME_BEGIN_RET_1(obj); - canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(pMT); - HELPER_METHOD_FRAME_END(); - } + ret = GetHashCodeStrategy(mt, objHandle, fieldOffset, fieldSize); - if (canUseFastGetHashCodeHelper) - { - hashCode ^= FastGetValueTypeHashCodeHelper(pMT, obj->UnBox()); - } - else - { - HELPER_METHOD_FRAME_BEGIN_RET_1(obj); - hashCode ^= RegularGetValueTypeHashCode(pMT, obj->UnBox()); - HELPER_METHOD_FRAME_END(); - } + END_QCALL; - return hashCode; + return ret; } -FCIMPLEND FCIMPL1(UINT32, MethodTableNative::GetNumInstanceFieldBytes, MethodTable* mt) { diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 80d35da7b72141..a3c5ea65c3ca7c 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -245,18 +245,14 @@ class COMInterlocked extern "C" void QCALLTYPE Interlocked_MemoryBarrierProcessWide(); -class ValueTypeHelper { -public: - static FCDECL1(FC_BOOL_RET, CanCompareBits, Object* obj); - static FCDECL1(INT32, GetHashCode, Object* objRef); -}; - class MethodTableNative { public: static FCDECL1(UINT32, GetNumInstanceFieldBytes, MethodTable* mt); }; extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb); +extern "C" BOOL QCALLTYPE MethodTable_CanCompareBitsOrUseFastGetHashCode(MethodTable* mt); +extern "C" INT32 QCALLTYPE ValueType_GetHashCodeStrategy(MethodTable* mt, QCall::ObjectHandleOnStack objHandle, UINT32* fieldOffset, UINT32* fieldSize); class StreamNative { public: diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index bd4a2090166522..8e68900686a7e0 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -569,12 +569,6 @@ DEFINE_METHOD(OBJECT, GET_TYPE, GetType, DEFINE_METHOD(OBJECT, GET_HASH_CODE, GetHashCode, IM_RetInt) DEFINE_METHOD(OBJECT, EQUALS, Equals, IM_Obj_RetBool) -// DEFINE_CLASS(DOUBLE, System, Double) -DEFINE_METHOD(DOUBLE, GET_HASH_CODE, GetHashCode, IM_RetInt) - -// DEFINE_CLASS(SINGLE, System, Single) -DEFINE_METHOD(SINGLE, GET_HASH_CODE, GetHashCode, IM_RetInt) - DEFINE_CLASS(__CANON, System, __Canon) BEGIN_ILLINK_FEATURE_SWITCH(System.Runtime.InteropServices.BuiltInComInterop.IsSupported, true, true) diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 1af6a5055e6c33..55b93f1f1ab683 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -76,11 +76,6 @@ FCFuncStart(gStringFuncs) FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrSByt_Int_Int_Encoding_RetVoid, ECall::CtorSBytePtrStartLengthEncodingManaged) FCFuncEnd() -FCFuncStart(gValueTypeFuncs) - FCFuncElement("CanCompareBits", ValueTypeHelper::CanCompareBits) - FCFuncElement("GetHashCode", ValueTypeHelper::GetHashCode) -FCFuncEnd() - FCFuncStart(gDiagnosticsDebugger) FCFuncElement("BreakInternal", DebugDebugger::Break) FCFuncElement("get_IsAttached", DebugDebugger::IsDebuggerAttached) @@ -619,7 +614,6 @@ FCClassElement("Thread", "System.Threading", gThreadFuncs) FCClassElement("ThreadPool", "System.Threading", gThreadPoolFuncs) FCClassElement("Type", "System", gSystem_Type) FCClassElement("TypedReference", "System", gTypedReferenceFuncs) -FCClassElement("ValueType", "System", gValueTypeFuncs) #ifdef FEATURE_COMINTEROP FCClassElement("Variant", "System", gVariantFuncs) #endif diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index ec72d3112f5f86..8e6a6702979439 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -100,6 +100,8 @@ static const Entry s_QCall[] = DllImportEntry(QCall_GetGCHandleForTypeHandle) DllImportEntry(QCall_FreeGCHandleForTypeHandle) DllImportEntry(MethodTable_AreTypesEquivalent) + DllImportEntry(MethodTable_CanCompareBitsOrUseFastGetHashCode) + DllImportEntry(ValueType_GetHashCodeStrategy) DllImportEntry(RuntimeTypeHandle_MakePointer) DllImportEntry(RuntimeTypeHandle_MakeByRef) DllImportEntry(RuntimeTypeHandle_MakeSZArray) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ValueTypeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ValueTypeTests.cs index 422f71e11c04f0..92a2c006ce2042 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ValueTypeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ValueTypeTests.cs @@ -299,6 +299,21 @@ public static void StructContainsPointerCompareTest() Assert.True(obj1.Equals(obj2)); Assert.Equal(obj1.GetHashCode(), obj2.GetHashCode()); } + + [Fact] + public static void StructContainsPointerNestedCompareTest() + { + StructContainsPointerNested obj1 = new StructContainsPointerNested(); + obj1.o = null; + obj1.value.value = 1; + + StructContainsPointerNested obj2 = new StructContainsPointerNested(); + obj2.o = null; + obj2.value.value = 1; + + Assert.True(obj1.Equals(obj2)); + Assert.Equal(obj1.GetHashCode(), obj2.GetHashCode()); + } public struct S { @@ -392,5 +407,11 @@ public struct StructContainsPointer public double value1; public double value2; } + + public struct StructContainsPointerNested + { + public object o; + public StructNonOverriddenEqualsOrGetHasCode value; + } } }