diff --git a/eng/Subsets.props b/eng/Subsets.props index d693e2a58f9ff..9088b48f285de 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -79,6 +79,8 @@ '$(BuildTargetFramework)' == '' or '$(BuildAllConfigurations)' == 'true'">libs.native+ $(DefaultLibrariesSubsets)libs.sfx+libs.oob+libs.pretest + + $(DefaultLibrariesSubsets)+libs.tests tools.illink diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ceb14e14b0603..33eace4f218ce 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -84,6 +84,7 @@ https://github.com/dotnet/command-line-api a045dd54a4c44723c215d992288160eb1401bb7f + https://github.com/dotnet/command-line-api a045dd54a4c44723c215d992288160eb1401bb7f @@ -92,21 +93,33 @@ https://github.com/dotnet/cecil b8c2293cd1cbd9d0fe6f32d7b5befbd526b5a175 + + + + https://github.com/dotnet/cecil + b8c2293cd1cbd9d0fe6f32d7b5befbd526b5a175 https://github.com/dotnet/emsdk c7b4dbc857259968a0892cf94cfa9ae4f2ca53cd + + + + https://github.com/dotnet/emsdk + c7b4dbc857259968a0892cf94cfa9ae4f2ca53cd + https://github.com/dotnet/source-build-reference-packages e659f328bf255d3e17e81296117c3aed1d461f2f - + + https://github.com/dotnet/source-build-externals - 9f553c88e8a6787c560ab3e7adec226311de7e2c + 949db2fd23b687c0d545e954943feada8b361ed6 @@ -114,6 +127,11 @@ https://github.com/dotnet/arcade 438a8e2488313fb3aa1b24a741a85c2669ee7e0d + + + + https://github.com/dotnet/arcade + 438a8e2488313fb3aa1b24a741a85c2669ee7e0d @@ -315,6 +333,11 @@ https://github.com/dotnet/runtime 205ef031e0fe5152dede0bd9f99d0f6f9e7f1e45 + + + + https://github.com/dotnet/runtime + 205ef031e0fe5152dede0bd9f99d0f6f9e7f1e45 @@ -368,7 +391,6 @@ https://github.com/dotnet/roslyn 2fe96bca1092f880e91eea6eb17ea3487d89309a - https://github.com/dotnet/roslyn @@ -382,9 +404,20 @@ https://github.com/dotnet/roslyn-analyzers e39798fc8357615ab319c81b20acfb036ef7b513 + + + https://github.com/dotnet/roslyn + 2fe96bca1092f880e91eea6eb17ea3487d89309a + + https://github.com/dotnet/sdk de4f12b8ab6692b01776d362f4fa609fd3f1154a + + + + https://github.com/dotnet/sdk + de4f12b8ab6692b01776d362f4fa609fd3f1154a diff --git a/eng/Versions.props b/eng/Versions.props index 5aa0451a132ab..a7085f29db6b5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -24,6 +24,8 @@ false false true + + true true diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index 542edf3518737..0c21487300836 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -297,7 +297,7 @@ bool BasicBlock::IsFirstColdBlock(Compiler* compiler) const bool BasicBlock::CanRemoveJumpToNext(Compiler* compiler) const { assert(KindIs(BBJ_ALWAYS)); - return JumpsToNext() && !hasAlign() && !compiler->fgInDifferentRegions(this, bbTarget); + return JumpsToNext() && (bbNext != compiler->fgFirstColdBlock); } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 8fe561e2f64e5..53b35cc2011b3 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -5273,6 +5273,20 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_PATTERN_I(INS_sve_cnth, EA_8BYTE, REG_R5, SVE_PATTERN_ALL, 13); // CNTH {, {, MUL #}} + // IF_SVE_CI_3A + theEmitter->emitIns_R_R_R(INS_sve_trn1, EA_SCALABLE, REG_P1, REG_P3, REG_P4, + INS_OPTS_SCALABLE_B); // TRN1 ., ., . + theEmitter->emitIns_R_R_R(INS_sve_trn2, EA_SCALABLE, REG_P5, REG_P2, REG_P7, + INS_OPTS_SCALABLE_H); // TRN2 ., ., . + theEmitter->emitIns_R_R_R(INS_sve_uzp1, EA_SCALABLE, REG_P0, REG_P0, REG_P0, + INS_OPTS_SCALABLE_S); // UZP1 ., ., . + theEmitter->emitIns_R_R_R(INS_sve_uzp2, EA_SCALABLE, REG_P0, REG_P0, REG_P0, + INS_OPTS_SCALABLE_D); // UZP2 ., ., . + theEmitter->emitIns_R_R_R(INS_sve_zip1, EA_SCALABLE, REG_P0, REG_P0, REG_P0, + INS_OPTS_SCALABLE_B); // ZIP1 ., ., . + theEmitter->emitIns_R_R_R(INS_sve_zip2, EA_SCALABLE, REG_P0, REG_P0, REG_P0, + INS_OPTS_SCALABLE_H); // ZIP2 ., ., . + // IF_SVE_CL_3A theEmitter->emitIns_R_R_R(INS_sve_compact, EA_SCALABLE, REG_V16, REG_P7, REG_V13, INS_OPTS_SCALABLE_S); // COMPACT ., , . @@ -5665,6 +5679,26 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_R_R(INS_sve_whilewr, EA_8BYTE, REG_P7, REG_R14, REG_R15, INS_OPTS_SCALABLE_D); // WHILEWR ., , + // IF_SVE_DW_2A + theEmitter->emitIns_R_R_I(INS_sve_pext, EA_SCALABLE, REG_P0, REG_P8, 0, + INS_OPTS_SCALABLE_B); // PEXT ., [] + theEmitter->emitIns_R_R_I(INS_sve_pext, EA_SCALABLE, REG_P1, REG_P9, 1, + INS_OPTS_SCALABLE_H); // PEXT ., [] + theEmitter->emitIns_R_R_I(INS_sve_pext, EA_SCALABLE, REG_P2, REG_P10, 2, + INS_OPTS_SCALABLE_S); // PEXT ., [] + theEmitter->emitIns_R_R_I(INS_sve_pext, EA_SCALABLE, REG_P3, REG_P11, 3, + INS_OPTS_SCALABLE_D); // PEXT ., [] + + // IF_SVE_DW_2B + theEmitter->emitIns_R_R_I(INS_sve_pext, EA_SCALABLE, REG_P8, REG_P12, 0, INS_OPTS_SCALABLE_B, + INS_SCALABLE_OPTS_WITH_PREDICATE_PAIR); // PEXT {., .}, [] + theEmitter->emitIns_R_R_I(INS_sve_pext, EA_SCALABLE, REG_P9, REG_P13, 1, INS_OPTS_SCALABLE_H, + INS_SCALABLE_OPTS_WITH_PREDICATE_PAIR); // PEXT {., .}, [] + theEmitter->emitIns_R_R_I(INS_sve_pext, EA_SCALABLE, REG_P10, REG_P14, 0, INS_OPTS_SCALABLE_S, + INS_SCALABLE_OPTS_WITH_PREDICATE_PAIR); // PEXT {., .}, [] + theEmitter->emitIns_R_R_I(INS_sve_pext, EA_SCALABLE, REG_P11, REG_P15, 1, INS_OPTS_SCALABLE_D, + INS_SCALABLE_OPTS_WITH_PREDICATE_PAIR); // PEXT {., .}, [] + // IF_SVE_DX_3A theEmitter->emitIns_R_R_R(INS_sve_whilege, EA_8BYTE, REG_P0, REG_R0, REG_R1, INS_OPTS_SCALABLE_B, INS_SCALABLE_OPTS_WITH_PREDICATE_PAIR); // WHILEGE {., .}, , @@ -5750,6 +5784,12 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_I(INS_sve_mov, EA_SCALABLE, REG_V7, 127, INS_OPTS_SCALABLE_D, INS_SCALABLE_OPTS_SHIFT); // MOV ., #{, } + // IF_SVE_EB_1B + theEmitter->emitIns_R(INS_sve_fmov, EA_SCALABLE, REG_V0, INS_OPTS_SCALABLE_B); // FMOV ., #0.0 + theEmitter->emitIns_R(INS_sve_fmov, EA_SCALABLE, REG_V1, INS_OPTS_SCALABLE_H); // FMOV ., #0.0 + theEmitter->emitIns_R(INS_sve_fmov, EA_SCALABLE, REG_V2, INS_OPTS_SCALABLE_S); // FMOV ., #0.0 + theEmitter->emitIns_R(INS_sve_fmov, EA_SCALABLE, REG_V3, INS_OPTS_SCALABLE_D); // FMOV ., #0.0 + // IF_SVE_EC_1A theEmitter->emitIns_R_I(INS_sve_add, EA_SCALABLE, REG_V0, 0, INS_OPTS_SCALABLE_B); // ADD ., ., #{, } diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 309b9d0a316b2..913f3a47002a5 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -677,6 +677,7 @@ void CodeGen::genCodeForBBlist() /* Do we need to generate a jump or return? */ + bool removedJmp = false; switch (block->GetKind()) { case BBJ_RETURN: @@ -765,6 +766,7 @@ void CodeGen::genCodeForBBlist() } #endif // TARGET_AMD64 + removedJmp = true; break; } #ifdef TARGET_XARCH @@ -814,7 +816,7 @@ void CodeGen::genCodeForBBlist() assert(!block->KindIs(BBJ_CALLFINALLY)); #endif // FEATURE_EH_CALLFINALLY_THUNKS - GetEmitter()->emitLoopAlignment(DEBUG_ARG1(block->KindIs(BBJ_ALWAYS))); + GetEmitter()->emitLoopAlignment(DEBUG_ARG1(block->KindIs(BBJ_ALWAYS) && !removedJmp)); } if (!block->IsLast() && block->Next()->isLoopAlign()) diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 1d7b9f7e95974..ebe22ad5515b4 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -1089,6 +1089,14 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isValidUimm4From1(emitGetInsSC(id))); break; + case IF_SVE_CI_3A: // ........xx..MMMM .......NNNN.DDDD -- SVE permute predicate elements + elemsize = id->idOpSize(); + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isPredicateRegister(id->idReg1())); // DDDD + assert(isPredicateRegister(id->idReg2())); // NNNN + assert(isPredicateRegister(id->idReg3())); // MMMM + break; + // Scalable, 4 regs, to predicate register. case IF_SVE_CX_4A: // ........xx.mmmmm ...gggnnnnn.DDDD -- SVE integer compare vectors elemsize = id->idOpSize(); @@ -1484,6 +1492,18 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx break; + case IF_SVE_DW_2B: // ........xx...... .......iNNN.DDDD -- SVE extract mask predicate from predicate-as-counter + assert(isValidImm1(emitGetInsSC(id))); // i + + FALLTHROUGH; + case IF_SVE_DW_2A: // ........xx...... ......iiNNN.DDDD -- SVE extract mask predicate from predicate-as-counter + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isPredicateRegister(id->idReg1())); // DDDD + assert(isHighPredicateRegister(id->idReg2())); // NNN + assert(isValidUimm2(emitGetInsSC(id))); // ii + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + break; + case IF_SVE_DX_3A: // ........xx.mmmmm ......nnnnn.DDD. -- SVE integer compare scalar count and limit (predicate // pair) assert(insOptsScalableStandard(id->idInsOpt())); @@ -1533,6 +1553,12 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx break; + case IF_SVE_EB_1B: // ........xx...... ...........ddddd -- SVE broadcast integer immediate (unpredicated) + assert(insOptsScalableStandard(id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); // ddddd + assert(isValidVectorElemsize(optGetSveElemsize(id->idInsOpt()))); // xx + break; + case IF_SVE_ED_1A: // ........xx...... ...iiiiiiiiddddd -- SVE integer min/max immediate (unpredicated) assert(insOptsScalableStandard(id->idInsOpt())); assert(isVectorRegister(id->idReg1())); // ddddd @@ -2299,9 +2325,10 @@ const char* emitter::emitPredicateRegName(regNumber reg, PredicateType ptype) { assert((reg >= REG_P0) && (reg <= REG_P15)); - int index = (int)reg - (int)REG_P0; + const int index = (int)reg - (int)REG_P0; + const bool usePnRegs = (ptype == PREDICATE_N) || (ptype == PREDICATE_N_SIZED); - return (ptype == PREDICATE_N_SIZED) ? pnRegNames[index] : pRegNames[index]; + return usePnRegs ? pnRegNames[index] : pRegNames[index]; } /***************************************************************************** @@ -6248,6 +6275,19 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg, insOpts o fmt = IF_SVE_DZ_1A; break; + case INS_sve_fmov: + assert(insOptsScalableStandard(opt)); + assert(isVectorRegister(reg)); // ddddd + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + id->idReg1(reg); + id->idInsOpt(opt); + fmt = IF_SVE_EB_1B; + + // FMOV is a pseudo-instruction for DUP, which is aliased by MOV; + // MOV is the preferred disassembly + ins = INS_sve_mov; + break; + default: unreached(); } @@ -7829,8 +7869,13 @@ void emitter::emitIns_R_I_I(instruction ins, * Add an instruction referencing two registers and a constant. */ -void emitter::emitIns_R_R_I( - instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, ssize_t imm, insOpts opt /* = INS_OPTS_NONE */) +void emitter::emitIns_R_R_I(instruction ins, + emitAttr attr, + regNumber reg1, + regNumber reg2, + ssize_t imm, + insOpts opt /* = INS_OPTS_NONE */, + insScalableOpts sopt /* = INS_SCALABLE_OPTS_NONE */) { emitAttr size = EA_SIZE(attr); emitAttr elemsize = EA_UNKNOWN; @@ -8423,6 +8468,25 @@ void emitter::emitIns_R_R_I( fmt = IF_SVE_GA_2A; break; + case INS_sve_pext: + assert(insOptsScalableStandard(opt)); + assert(isPredicateRegister(reg1)); // DDDD + assert(isHighPredicateRegister(reg2)); // NNN + assert(isValidVectorElemsize(optGetSveElemsize(opt))); // xx + + if (sopt == INS_SCALABLE_OPTS_WITH_PREDICATE_PAIR) + { + assert(isValidImm1(imm)); // i + fmt = IF_SVE_DW_2B; + } + else + { + assert(insScalableOptsNone(sopt)); + assert(isValidUimm2(imm)); // ii + fmt = IF_SVE_DW_2A; + } + break; + default: unreached(); break; @@ -9479,6 +9543,19 @@ void emitter::emitIns_R_R_R(instruction ins, } break; + case INS_sve_uzp1: + case INS_sve_trn1: + case INS_sve_zip1: + case INS_sve_uzp2: + case INS_sve_trn2: + case INS_sve_zip2: + assert(insOptsScalableStandard(opt)); + assert(isPredicateRegister(reg1)); // DDDD + assert(isPredicateRegister(reg2)); // NNNN + assert(isPredicateRegister(reg3)); // MMMM + fmt = IF_SVE_CI_3A; + break; + case INS_sve_clz: case INS_sve_cls: case INS_sve_cnt: @@ -13795,7 +13872,7 @@ void emitter::emitIns_Call(EmitCallType callType, assert(isPredicateRegister(reg)); emitter::code_t ureg = (emitter::code_t)reg - (emitter::code_t)REG_P0; assert((ureg >= 0) && (ureg <= 15)); - return ureg << 0; + return ureg; } /***************************************************************************** @@ -13844,9 +13921,9 @@ void emitter::emitIns_Call(EmitCallType callType, /*static*/ emitter::code_t emitter::insEncodeReg_P_7_to_5(regNumber reg) { - assert(isLowPredicateRegister(reg)); - emitter::code_t ureg = (emitter::code_t)reg - (emitter::code_t)REG_P0; - assert((ureg >= 0) && (ureg <= 15)); + assert(isHighPredicateRegister(reg)); + emitter::code_t ureg = (emitter::code_t)reg - (emitter::code_t)REG_P8; + assert((ureg >= 0) && (ureg <= 7)); return ureg << 5; } @@ -15843,6 +15920,17 @@ void emitter::emitIns_Call(EmitCallType callType, return (code_t)imm << 16; } +/***************************************************************************** + * + * Returns the encoding for the immediate value as 2-bits at bit locations '9-8'. + */ + +/*static*/ emitter::code_t emitter::insEncodeUimm2_9_to_8(ssize_t imm) +{ + assert(isValidUimm2(imm)); + return (code_t)imm << 8; +} + /***************************************************************************** * * Returns the encoding for the immediate value as 7-bits at bit locations '20-14'. @@ -17978,6 +18066,15 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_CI_3A: // ........xx..MMMM .......NNNN.DDDD -- SVE permute predicate elements + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_3_to_0(id->idReg1()); // DDDD + code |= insEncodeReg_P_8_to_5(id->idReg2()); // NNNN + code |= insEncodeReg_P_19_to_16(id->idReg3()); // MMMM + code |= insEncodeElemsize(optGetSveElemsize(id->idInsOpt())); // xx + 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 @@ -18246,6 +18343,16 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutput_Instr(dst, code); break; + case IF_SVE_DW_2A: // ........xx...... ......iiNNN.DDDD -- SVE extract mask predicate from predicate-as-counter + case IF_SVE_DW_2B: // ........xx...... .......iNNN.DDDD -- SVE extract mask predicate from predicate-as-counter + code = emitInsCodeSve(ins, fmt); + code |= insEncodeReg_P_3_to_0(id->idReg1()); // DDDD + code |= insEncodeReg_P_7_to_5(id->idReg2()); // NNN + code |= insEncodeUimm2_9_to_8(emitGetInsSC(id)); // ii (or i) + code |= insEncodeElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + case IF_SVE_DX_3A: // ........xx.mmmmm ......nnnnn.DDD. -- SVE integer compare scalar count and limit (predicate // pair) code = emitInsCodeSve(ins, fmt); @@ -18300,6 +18407,14 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) break; } + case IF_SVE_EB_1B: // ........xx...... ...........ddddd -- SVE broadcast integer immediate (unpredicated) + // ins is MOV for this encoding, as it is the preferred disassembly, so pass FMOV to emitInsCodeSve + code = emitInsCodeSve(INS_sve_fmov, fmt); + code |= insEncodeReg_V_4_to_0(id->idReg1()); // ddddd + code |= insEncodeElemsize(optGetSveElemsize(id->idInsOpt())); // xx + dst += emitOutput_Instr(dst, code); + break; + case IF_SVE_DU_3A: // ........xx.mmmmm ......nnnnn.DDDD -- SVE pointer conflict compare code = emitInsCodeSve(ins, fmt); code |= insEncodeReg_P_3_to_0(id->idReg1()); // DDDD @@ -18822,6 +18937,15 @@ void emitter::emitDispImm(ssize_t imm, bool addComma, bool alwaysHex /* =false * emitDispComma(); } +/***************************************************************************** + * + * Display an immediate value as an index operation + */ +void emitter::emitDispElementIndex(ssize_t imm) +{ + printf("[%d]", imm); +} + /***************************************************************************** * * Display a float zero constant @@ -19251,6 +19375,17 @@ void emitter::emitDispPredicateReg(regNumber reg, PredicateType ptype, insOpts o emitDispComma(); } +//------------------------------------------------------------------------ +// emitDispPredicateRegPair: Display a pair of predicate registers +// +void emitter::emitDispPredicateRegPair(regNumber reg, insOpts opt) +{ + printf("{ "); + emitDispPredicateReg(reg, PREDICATE_SIZED, opt, true); + emitDispPredicateReg((regNumber)((unsigned)reg + 1), PREDICATE_SIZED, opt, false); + printf(" }, "); +} + //------------------------------------------------------------------------ // emitDispLowPredicateReg: Display a low predicate register name with with an arrangement suffix // @@ -21077,6 +21212,13 @@ void emitter::emitDispInsHelp( emitDispSveReg(id->idReg3(), id->idInsOpt(), false); // mmmmm break; + // ., ., . + case IF_SVE_CI_3A: // ........xx..MMMM .......NNNN.DDDD -- SVE permute predicate elements + 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 + break; + // ., , . case IF_SVE_CL_3A: // ........xx...... ...gggnnnnnddddd -- SVE compress active elements emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd @@ -21335,6 +21477,20 @@ void emitter::emitDispInsHelp( emitDispReg(id->idReg3(), id->idOpSize(), false); // mmmmm 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 + emitDispPredicateReg(id->idReg2(), PREDICATE_N, id->idInsOpt(), false); // NNN + emitDispElementIndex(emitGetInsSC(id)); // ii + break; + + // {., .}, [] + case IF_SVE_DW_2B: // ........xx...... .......iNNN.DDDD -- SVE extract mask predicate from predicate-as-counter + emitDispPredicateRegPair(id->idReg1(), id->idInsOpt()); // DDDD + emitDispPredicateReg(id->idReg2(), PREDICATE_N, id->idInsOpt(), false); // NNN + emitDispElementIndex(emitGetInsSC(id)); // i + break; + // {., .}, , case IF_SVE_DX_3A: // ........xx.mmmmm ......nnnnn.DDD. -- SVE integer compare scalar count and limit (predicate // pair) @@ -21382,6 +21538,13 @@ void emitter::emitDispInsHelp( emitDispImmOptsLSL(emitGetInsSC(id), id->idOptionalShift(), 8); // iiiiiiii, h break; + // FMOV ., #0.0 + // (Preferred disassembly: FMOV ., #0) + case IF_SVE_EB_1B: // ........xx...... ...........ddddd -- SVE broadcast integer immediate (unpredicated) + emitDispSveReg(id->idReg1(), id->idInsOpt(), true); // ddddd + emitDispImm(0, false); + break; + // SMAX ., ., # // SMIN ., ., # // UMAX ., ., # @@ -23935,6 +24098,11 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insLatency = PERFSCORE_LATENCY_2C; break; + case IF_SVE_CI_3A: // ........xx..MMMM .......NNNN.DDDD -- SVE permute predicate elements + result.insThroughput = PERFSCORE_THROUGHPUT_2C; + result.insLatency = PERFSCORE_LATENCY_2C; + break; + // Conditional extract operations, SIMD&FP scalar and vector forms case IF_SVE_CL_3A: // ........xx...... ...gggnnnnnddddd -- SVE compress active elements case IF_SVE_CM_3A: // ........xx...... ...gggmmmmmddddd -- SVE conditionally broadcast element to vector @@ -24315,6 +24483,8 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insLatency = PERFSCORE_LATENCY_2C; break; + case IF_SVE_DW_2A: // ........xx...... ......iiNNN.DDDD -- SVE extract mask predicate from predicate-as-counter + case IF_SVE_DW_2B: // ........xx...... .......iNNN.DDDD -- SVE extract mask predicate from predicate-as-counter case IF_SVE_DS_2A: // .........x.mmmmm ......nnnnn..... -- SVE conditionally terminate scalars result.insThroughput = PERFSCORE_THROUGHPUT_1C; result.insLatency = PERFSCORE_LATENCY_1C; @@ -24360,6 +24530,7 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins case IF_SVE_EA_1A: // ........xx...... ...iiiiiiiiddddd -- SVE broadcast floating-point immediate (unpredicated) case IF_SVE_EB_1A: // ........xx...... ..hiiiiiiiiddddd -- SVE broadcast integer immediate (unpredicated) case IF_SVE_EC_1A: // ........xx...... ..hiiiiiiiiddddd -- SVE integer add/subtract immediate (unpredicated) + case IF_SVE_EB_1B: // ........xx...... ...........ddddd -- SVE broadcast integer immediate (unpredicated) result.insThroughput = PERFSCORE_THROUGHPUT_2C; result.insLatency = PERFSCORE_LATENCY_2C; break; diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index 5755b9515762c..2dcdf13bb9379 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -25,7 +25,8 @@ enum PredicateType PREDICATE_MERGE, // Predicate printed with /m PREDICATE_ZERO, // Predicate printed with /z PREDICATE_SIZED, // Predicate printed with element size - PREDICATE_N_SIZED, // Predicate printed printed as counter with element size + PREDICATE_N, // Predicate printed as counter + PREDICATE_N_SIZED, // Predicate printed as counter with element size }; const char* emitSveRegName(regNumber reg); @@ -39,6 +40,7 @@ void emitDispLargeJmp( void emitDispComma(); void emitDispInst(instruction ins); void emitDispImm(ssize_t imm, bool addComma, bool alwaysHex = false, bool isAddrOffset = false); +void emitDispElementIndex(ssize_t imm); void emitDispFloatZero(); void emitDispFloatImm(ssize_t imm8); void emitDispImmOptsLSL(ssize_t imm, bool hasShift, unsigned shiftAmount); @@ -59,6 +61,7 @@ void emitDispVectorRegList(regNumber firstReg, unsigned listSize, insOpts opt, b void emitDispVectorElemList(regNumber firstReg, unsigned listSize, emitAttr elemsize, unsigned index, bool addComma); void emitDispSveConsecutiveRegList(regNumber firstReg, unsigned listSize, insOpts opt, bool addComma); void emitDispPredicateReg(regNumber reg, PredicateType ptype, insOpts opt, bool addComma); +void emitDispPredicateRegPair(regNumber reg, insOpts opt); void emitDispLowPredicateReg(regNumber reg, PredicateType ptype, insOpts opt, bool addComma); void emitDispLowPredicateRegPair(regNumber reg, insOpts opt); void emitDispVectorLengthSpecifier(instrDesc* id); @@ -550,6 +553,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 immediate value as 2-bits at bit locations '9-8'. +static code_t insEncodeUimm2_9_to_8(ssize_t imm); + // Returns the encoding for the immediate value as 7-bits at bit locations '20-14'. static code_t insEncodeUimm7_20_to_14(ssize_t imm); @@ -618,6 +624,18 @@ static bool isValidSimm4_MultipleOf32(ssize_t value) return (-256 <= value) && (value <= 224) && (value % 32 == 0); }; +// Returns true if 'value' is a legal immediate 1 bit encoding (such as for PEXT). +static bool isValidImm1(ssize_t value) +{ + return (value == 0) || (value == 1); +}; + +// Returns true if 'value' is a legal unsigned immediate 2 bit encoding (such as for PEXT). +static bool isValidUimm2(ssize_t value) +{ + return (0 <= value) || (value <= 3); +}; + // 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) { @@ -1184,8 +1202,13 @@ void emitIns_R_I_I(instruction ins, insOpts opt = INS_OPTS_NONE DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); -void emitIns_R_R_I( - instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, ssize_t imm, insOpts opt = INS_OPTS_NONE); +void emitIns_R_R_I(instruction ins, + emitAttr attr, + regNumber reg1, + regNumber reg2, + ssize_t imm, + insOpts opt = INS_OPTS_NONE, + insScalableOpts sopt = INS_SCALABLE_OPTS_NONE); // Checks for a large immediate that needs a second instruction void emitIns_R_R_Imm(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, ssize_t imm); diff --git a/src/coreclr/jit/redundantbranchopts.cpp b/src/coreclr/jit/redundantbranchopts.cpp index bd136cbf4d94f..edcbf34152644 100644 --- a/src/coreclr/jit/redundantbranchopts.cpp +++ b/src/coreclr/jit/redundantbranchopts.cpp @@ -47,43 +47,43 @@ PhaseStatus Compiler::optRedundantBranches() { bool madeChangesThisBlock = m_compiler->optRedundantRelop(block); - BasicBlock* const bbNext = block->GetFalseTarget(); - BasicBlock* const bbJump = block->GetTrueTarget(); + BasicBlock* const bbFalse = block->GetFalseTarget(); + BasicBlock* const bbTrue = block->GetTrueTarget(); madeChangesThisBlock |= m_compiler->optRedundantBranch(block); - // If we modified some flow out of block but it's still + // If we modified some flow out of block but it's still referenced and // a BBJ_COND, retry; perhaps one of the later optimizations // we can do has enabled one of the earlier optimizations. // - if (madeChangesThisBlock && block->KindIs(BBJ_COND)) + if (madeChangesThisBlock && block->KindIs(BBJ_COND) && (block->countOfInEdges() > 0)) { JITDUMP("Will retry RBO in " FMT_BB " after partial optimization\n", block->bbNum); madeChangesThisBlock |= m_compiler->optRedundantBranch(block); } - // It's possible that the changed flow into bbNext or bbJump may unblock + // It's possible that the changed flow into bbFalse or bbTrue may unblock // further optimizations there. // // Note this misses cascading retries, consider reworking the overall // strategy here to iterate until closure. // - if (madeChangesThisBlock && (bbNext->countOfInEdges() == 0)) + if (madeChangesThisBlock && (bbFalse->countOfInEdges() == 0)) { - for (BasicBlock* succ : bbNext->Succs()) + for (BasicBlock* succ : bbFalse->Succs()) { JITDUMP("Will retry RBO in " FMT_BB "; pred " FMT_BB " now unreachable\n", succ->bbNum, - bbNext->bbNum); + bbFalse->bbNum); m_compiler->optRedundantBranch(succ); } } - if (madeChangesThisBlock && (bbJump->countOfInEdges() == 0)) + if (madeChangesThisBlock && (bbTrue->countOfInEdges() == 0)) { - for (BasicBlock* succ : bbJump->Succs()) + for (BasicBlock* succ : bbTrue->Succs()) { JITDUMP("Will retry RBO in " FMT_BB "; pred " FMT_BB " now unreachable\n", succ->bbNum, - bbNext->bbNum); + bbFalse->bbNum); m_compiler->optRedundantBranch(succ); } } @@ -828,10 +828,10 @@ bool Compiler::optRedundantBranch(BasicBlock* const block) } else if (trueReaches && !falseReaches && rii.canInferFromTrue) { - // Taken jump in dominator reaches, fall through doesn't; relop must be true/false. + // True path in dominator reaches, false path doesn't; relop must be true/false. // const bool relopIsTrue = rii.reverseSense ^ (domIsSameRelop | domIsInferredRelop); - JITDUMP("Jump successor " FMT_BB " of " FMT_BB " reaches, relop [%06u] must be %s\n", + JITDUMP("True successor " FMT_BB " of " FMT_BB " reaches, relop [%06u] must be %s\n", domBlock->GetTrueTarget()->bbNum, domBlock->bbNum, dspTreeID(tree), relopIsTrue ? "true" : "false"); relopValue = relopIsTrue ? 1 : 0; @@ -839,10 +839,10 @@ bool Compiler::optRedundantBranch(BasicBlock* const block) } else if (falseReaches && !trueReaches && rii.canInferFromFalse) { - // Fall through from dominator reaches, taken jump doesn't; relop must be false/true. + // False path from dominator reaches, true path doesn't; relop must be false/true. // const bool relopIsFalse = rii.reverseSense ^ (domIsSameRelop | domIsInferredRelop); - JITDUMP("Fall through successor " FMT_BB " of " FMT_BB " reaches, relop [%06u] must be %s\n", + JITDUMP("False successor " FMT_BB " of " FMT_BB " reaches, relop [%06u] must be %s\n", domBlock->GetFalseTarget()->bbNum, domBlock->bbNum, dspTreeID(tree), relopIsFalse ? "false" : "true"); relopValue = relopIsFalse ? 0 : 1; @@ -942,7 +942,6 @@ struct JumpThreadInfo : m_block(block) , m_trueTarget(block->GetTrueTarget()) , m_falseTarget(block->GetFalseTarget()) - , m_fallThroughPred(nullptr) , m_ambiguousVNBlock(nullptr) , m_truePreds(BlockSetOps::MakeEmpty(comp)) , m_ambiguousPreds(BlockSetOps::MakeEmpty(comp)) @@ -961,8 +960,6 @@ struct JumpThreadInfo BasicBlock* const m_trueTarget; // Block successor if predicate is false BasicBlock* const m_falseTarget; - // Unique pred that falls through to block, if any - BasicBlock* m_fallThroughPred; // Block that brings in the ambiguous VN BasicBlock* m_ambiguousVNBlock; // Pred blocks for which the predicate will be true @@ -1239,35 +1236,9 @@ bool Compiler::optJumpThreadDom(BasicBlock* const block, BasicBlock* const domBl // * It's also possible that the pred is a switch; we will treat switch // preds as ambiguous as well. // - // * We note if there is an un-ambiguous pred that falls through to block. - // This is the "fall through pred", and the (true/false) pred set it belongs to - // is the "fall through set". - // - // Now for some case analysis. - // - // (1) If we have both an ambiguous pred and a fall through pred, we treat - // the fall through pred as an ambiguous pred (we can't reroute its flow to - // avoid block, and we need to keep block intact), and jump thread the other - // preds per (2) below. - // - // (2) If we have an ambiguous pred and no fall through, we reroute the true and - // false preds to branch to the true and false successors, respectively. - // - // (3) If we don't have an ambiguous pred and don't have a fall through pred, - // we choose one of the pred sets to be treated as if it was the fall through set. - // For now the choice is arbitrary, so we chose the true preds, and proceed - // per (4) below. - // - // (4) If we don't have an ambiguous pred, and we have a fall through, we leave - // all preds in the fall through set alone -- they continue branching to block. - // We modify block to branch to the appropriate successor for the fall through set. - // Note block will be empty other than phis and the branch, so this is ok. - // The preds in the other set target the other successor. - // - // The goal of the above is to maximize the number of cases where we jump thread, - // and to maximize the number of jump threads that reuse the original block. This - // latter should prove useful in subsequent work, where we aim to enable jump - // threading in cases where block has side effects. + // If there are ambiguous preds they will continue to flow into the + // unaltered block, while true and false preds will flow to the appropriate + // successors directly. // BasicBlock* const domTrueSuccessor = domIsSameRelop ? domBlock->GetTrueTarget() : domBlock->GetFalseTarget(); BasicBlock* const domFalseSuccessor = domIsSameRelop ? domBlock->GetFalseTarget() : domBlock->GetTrueTarget(); @@ -1337,15 +1308,6 @@ bool Compiler::optJumpThreadDom(BasicBlock* const block, BasicBlock* const domBl jti.m_numFalsePreds++; JITDUMP(FMT_BB " is a false pred\n", predBlock->bbNum); } - - // Note if the true or false pred is the fall through pred. - // - if (predBlock->NextIs(block)) - { - JITDUMP(FMT_BB " is the fall-through pred\n", predBlock->bbNum); - assert(jti.m_fallThroughPred == nullptr); - jti.m_fallThroughPred = predBlock; - } } // Do the optimization. @@ -1597,15 +1559,6 @@ bool Compiler::optJumpThreadPhi(BasicBlock* block, GenTree* tree, ValueNum treeN continue; } - - // Note if the true or false pred is the fall through pred. - // - if (predBlock->NextIs(block)) - { - JITDUMP(FMT_BB " is the fall-through pred\n", predBlock->bbNum); - assert(jti.m_fallThroughPred == nullptr); - jti.m_fallThroughPred = predBlock; - } } // Do the optimization. @@ -1638,102 +1591,10 @@ bool Compiler::optJumpThreadCore(JumpThreadInfo& jti) return false; } - if ((jti.m_numAmbiguousPreds > 0) && (jti.m_fallThroughPred != nullptr)) - { - // TODO: Simplify jti.m_fallThroughPred logic, now that implicit fallthrough is disallowed. - const bool fallThroughIsTruePred = BlockSetOps::IsMember(this, jti.m_truePreds, jti.m_fallThroughPred->bbNum); - const bool predJumpsToNext = jti.m_fallThroughPred->KindIs(BBJ_ALWAYS) && jti.m_fallThroughPred->JumpsToNext(); - - if (predJumpsToNext && ((fallThroughIsTruePred && (jti.m_numFalsePreds == 0)) || - (!fallThroughIsTruePred && (jti.m_numTruePreds == 0)))) - { - JITDUMP(FMT_BB " has ambiguous preds and a (%s) fall through pred and no (%s) preds.\n" - "Fall through pred " FMT_BB " is BBJ_ALWAYS\n", - jti.m_block->bbNum, fallThroughIsTruePred ? "true" : "false", - fallThroughIsTruePred ? "false" : "true", jti.m_fallThroughPred->bbNum); - - assert(jti.m_fallThroughPred->TargetIs(jti.m_block)); - } - else - { - // Treat the fall through pred as an ambiguous pred. - JITDUMP(FMT_BB " has both ambiguous preds and a fall through pred\n", jti.m_block->bbNum); - JITDUMP("Treating fall through pred " FMT_BB " as an ambiguous pred\n", jti.m_fallThroughPred->bbNum); - - if (fallThroughIsTruePred) - { - BlockSetOps::RemoveElemD(this, jti.m_truePreds, jti.m_fallThroughPred->bbNum); - assert(jti.m_numTruePreds > 0); - jti.m_numTruePreds--; - } - else - { - assert(jti.m_numFalsePreds > 0); - jti.m_numFalsePreds--; - } - - assert(!(BlockSetOps::IsMember(this, jti.m_ambiguousPreds, jti.m_fallThroughPred->bbNum))); - BlockSetOps::AddElemD(this, jti.m_ambiguousPreds, jti.m_fallThroughPred->bbNum); - jti.m_numAmbiguousPreds++; - } - - jti.m_fallThroughPred = nullptr; - } - - // There still should be at least one pred that can bypass block. - // - if ((jti.m_numTruePreds == 0) && (jti.m_numFalsePreds == 0)) - { - // This is possible, but also should be rare. - // - JITDUMP(FMT_BB " now only has ambiguous preds, not jump threading\n", jti.m_block->bbNum); - return false; - } - - // Determine if either set of preds will route via block. - // - bool truePredsWillReuseBlock = false; - bool falsePredsWillReuseBlock = false; - - if (jti.m_fallThroughPred != nullptr) - { - assert(jti.m_numAmbiguousPreds == 0); - truePredsWillReuseBlock = BlockSetOps::IsMember(this, jti.m_truePreds, jti.m_fallThroughPred->bbNum); - falsePredsWillReuseBlock = !truePredsWillReuseBlock; - } - else if (jti.m_numAmbiguousPreds == 0) - { - truePredsWillReuseBlock = true; - falsePredsWillReuseBlock = !truePredsWillReuseBlock; - } - - assert(!(truePredsWillReuseBlock && falsePredsWillReuseBlock)); - // We should be good to go // JITDUMP("Optimizing via jump threading\n"); - // Fix block, if we're reusing it. - // - if (truePredsWillReuseBlock) - { - Statement* const lastStmt = jti.m_block->lastStmt(); - fgRemoveStmt(jti.m_block, lastStmt); - JITDUMP(" repurposing " FMT_BB " to always jump to " FMT_BB "\n", jti.m_block->bbNum, jti.m_trueTarget->bbNum); - fgRemoveRefPred(jti.m_falseTarget, jti.m_block); - jti.m_block->SetKind(BBJ_ALWAYS); - } - else if (falsePredsWillReuseBlock) - { - Statement* const lastStmt = jti.m_block->lastStmt(); - fgRemoveStmt(jti.m_block, lastStmt); - JITDUMP(" repurposing " FMT_BB " to always jump to " FMT_BB "\n", jti.m_block->bbNum, - jti.m_falseTarget->bbNum); - fgRemoveRefPred(jti.m_trueTarget, jti.m_block); - jti.m_block->SetKindAndTarget(BBJ_ALWAYS, jti.m_falseTarget); - jti.m_block->SetFlags(BBF_NONE_QUIRK); - } - // Now reroute the flow from the predecessors. // If this pred is in the set that will reuse block, do nothing. // Else revise pred to branch directly to the appropriate successor of block. @@ -1749,22 +1610,8 @@ bool Compiler::optJumpThreadCore(JumpThreadInfo& jti) const bool isTruePred = BlockSetOps::IsMember(this, jti.m_truePreds, predBlock->bbNum); - // Do we need to alter flow from this pred? + // Jump to the appropriate successor. // - if ((isTruePred && truePredsWillReuseBlock) || (!isTruePred && falsePredsWillReuseBlock)) - { - // No, we can leave as is. - // - JITDUMP("%s pred " FMT_BB " will continue to target " FMT_BB "\n", isTruePred ? "true" : "false", - predBlock->bbNum, jti.m_block->bbNum); - continue; - } - - // Yes, we need to jump to the appropriate successor. - // Note we should not be altering flow for the fall-through pred. - // - assert(predBlock != jti.m_fallThroughPred); - if (isTruePred) { JITDUMP("Jump flow from pred " FMT_BB " -> " FMT_BB diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index 117c31fb99a99..bd83b0ac3dbf1 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -186,7 +186,6 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF #ifdef TARGET_ARM m_RegDisplay.pLR = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_RIP); - m_RegDisplay.pR11 = (PTR_UIntNative)PTR_HOST_MEMBER(PInvokeTransitionFrame, pFrame, m_ChainPointer); if (pFrame->m_Flags & PTFF_SAVE_R4) { m_RegDisplay.pR4 = pPreservedRegsCursor++; } if (pFrame->m_Flags & PTFF_SAVE_R5) { m_RegDisplay.pR5 = pPreservedRegsCursor++; } @@ -632,6 +631,7 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, NATIVE_CONTEXT* pC m_RegDisplay.pR9 = (PTR_UIntNative)PTR_TO_REG(pCtx, R9); m_RegDisplay.pR10 = (PTR_UIntNative)PTR_TO_REG(pCtx, R10); m_RegDisplay.pR11 = (PTR_UIntNative)PTR_TO_REG(pCtx, R11); + m_RegDisplay.pR12 = (PTR_UIntNative)PTR_TO_REG(pCtx, R12); m_RegDisplay.pLR = (PTR_UIntNative)PTR_TO_REG(pCtx, Lr); #else PORTABILITY_ASSERT("StackFrameIterator::InternalInit"); diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp index 4087e2ae6ac4f..8e587901f60a3 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixContext.cpp @@ -266,6 +266,7 @@ #define MCREG_R9(mc) ((mc).arm_r9) #define MCREG_R10(mc) ((mc).arm_r10) #define MCREG_R11(mc) ((mc).arm_fp) +#define MCREG_R12(mc) ((mc).arm_ip) #elif defined(HOST_X86) @@ -526,6 +527,7 @@ uint64_t GetPC(void* context) uint64_t& UNIX_CONTEXT::R9(){ return (uint64_t&)MCREG_R9(ctx.uc_mcontext); } uint64_t& UNIX_CONTEXT::R10(){ return (uint64_t&)MCREG_R10(ctx.uc_mcontext); } uint64_t& UNIX_CONTEXT::R11(){ return (uint64_t&)MCREG_R11(ctx.uc_mcontext); } + uint64_t& UNIX_CONTEXT::R12(){ return (uint64_t&)MCREG_R12(ctx.uc_mcontext); } #else PORTABILITY_ASSERT("UNIX_CONTEXT"); diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixContext.h b/src/coreclr/nativeaot/Runtime/unix/UnixContext.h index 471f395bef1f5..caddff419d373 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixContext.h +++ b/src/coreclr/nativeaot/Runtime/unix/UnixContext.h @@ -136,6 +136,7 @@ struct UNIX_CONTEXT uint64_t& R9(); uint64_t& R10(); uint64_t& R11(); + uint64_t& R12(); uintptr_t GetIp() { return (uintptr_t)Pc(); } uintptr_t GetSp() { return (uintptr_t)Sp(); } @@ -155,6 +156,7 @@ struct UNIX_CONTEXT lambda((size_t*)&R9()); lambda((size_t*)&R10()); lambda((size_t*)&R11()); + lambda((size_t*)&R12()); } #else PORTABILITY_ASSERT("UNIX_CONTEXT"); diff --git a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp index 9e6ec0af36e16..3bd963a7cd0e8 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnwindHelpers.cpp @@ -335,14 +335,14 @@ struct Registers_REGDISPLAY : REGDISPLAY inline static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM; } bool validRegister(int num) const; - bool validFloatRegister(int num) { return false; }; + bool validFloatRegister(int num) const; bool validVectorRegister(int num) const { return false; }; uint32_t getRegister(int num) const; void setRegister(int num, uint32_t value, uint32_t location); - double getFloatRegister(int num) const {abort();} - void setFloatRegister(int num, double value) {abort();} + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); libunwind::v128 getVectorRegister(int num) const {abort();} void setVectorRegister(int num, libunwind::v128 value) {abort();} @@ -393,6 +393,10 @@ inline bool Registers_REGDISPLAY::validRegister(int num) const { return false; } +inline bool Registers_REGDISPLAY::validFloatRegister(int num) const { + return num >= UNW_ARM_D0 && num <= UNW_ARM_D31; +} + inline uint32_t Registers_REGDISPLAY::getRegister(int regNum) const { if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) return SP; @@ -499,6 +503,24 @@ void Registers_REGDISPLAY::setRegister(int num, uint32_t value, uint32_t locatio } } +double Registers_REGDISPLAY::getFloatRegister(int num) const +{ + if (num >= UNW_ARM_D8 && num <= UNW_ARM_D15) + { + return D[num - UNW_ARM_D8]; + } + + PORTABILITY_ASSERT("unsupported arm register"); +} + +void Registers_REGDISPLAY::setFloatRegister(int num, double value) +{ + if (num >= UNW_ARM_D8 && num <= UNW_ARM_D15) + { + D[num - UNW_ARM_D8] = value; + } +} + #endif // TARGET_ARM #if defined(TARGET_ARM64) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index e4d7121c6023b..5355c4be9ff12 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -419,6 +419,7 @@ private sealed class ScannedDevirtualizationManager : DevirtualizationManager private Dictionary> _implementators = new(); private HashSet _disqualifiedTypes = new(); private HashSet _overridenMethods = new(); + private HashSet _generatedVirtualMethods = new(); public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray> markedNodes) { @@ -426,6 +427,12 @@ public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray eetypeNode.Type, @@ -564,7 +571,6 @@ static List BuildVTable(NodeFactory factory, TypeDesc currentType, T _overridenMethods.Add(baseVtable[i]); } } - } } } @@ -642,10 +648,25 @@ public override bool IsEffectivelySealed(TypeDesc type) public override bool IsEffectivelySealed(MethodDesc method) { - if (method.IsFinal || IsEffectivelySealed(method.OwningType)) + // First try to answer using metadata + if (method.IsFinal || method.OwningType.IsSealed()) return true; - return !_overridenMethods.Contains(method.GetCanonMethodTarget(CanonicalFormKind.Specific)); + // Now let's see if we can seal through whole program view + + // Sealing abstract methods or methods on interface can't lead to anything good + if (method.IsAbstract || method.OwningType.IsInterface) + return false; + + // If we want to make something final, we better have a method body for it. + // Sometimes we might have optimized it away so don't let codegen make direct calls. + // NOTE: this check naturally also rejects generic virtual methods since we don't track them. + MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific); + if (!_generatedVirtualMethods.Contains(canonMethod)) + return false; + + // If we haven't seen any other method override this, this method is sealed + return !_overridenMethods.Contains(canonMethod); } protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType, out CORINFO_DEVIRTUALIZATION_DETAIL devirtualizationDetail) 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 39a03aafa7e28..0e6dd63b49fc1 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -1329,9 +1329,27 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO } else { - if (!targetMethod.IsVirtual || - // Final/sealed has no meaning for interfaces, but lets us devirtualize otherwise - !targetMethod.OwningType.IsInterface && (targetMethod.IsFinal || targetMethod.OwningType.IsSealed())) + // We can devirtualize the callvirt if the method is not virtual to begin with + bool canDevirt = !targetMethod.IsVirtual; + + // Final/sealed has no meaning for interfaces, but might let us devirtualize otherwise + if (!canDevirt && !targetMethod.OwningType.IsInterface) + { + // Check if we can devirt per metadata + canDevirt = targetMethod.IsFinal || targetMethod.OwningType.IsSealed(); + + // We might be able to devirt based on whole program view + if (!canDevirt + // Do not devirt if devirtualization would need a generic dictionary entry that we didn't predict + // during scanning (i.e. compiling a shared method body and we need to call another shared body + // with a method generic dictionary argument). + && (!pResult->exactContextNeedsRuntimeLookup || !targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstMethodDescArg())) + { + canDevirt = _compilation.IsEffectivelySealed(targetMethod); + } + } + + if (canDevirt) { resolvedCallVirt = true; directCall = true; 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 41540f8b6da59..8dd1e4cb13673 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs @@ -80,6 +80,16 @@ public AssemblyChecker ( } public void Verify () + { + var errors = VerifyImpl().ToList(); + if (errors.Any()) + { + Assert.Fail(string.Join(Environment.NewLine, errors)); + } + + } + + IEnumerable VerifyImpl() { // There are no type forwarders left after compilation in Native AOT // VerifyExportedTypes (originalAssembly, linkedAssembly); @@ -114,7 +124,8 @@ public void Verify () token, out LinkedEntity? linkedMember); - VerifyTypeDefinition (td, linkedMember); + foreach(var err in VerifyTypeDefinition (td, linkedMember)) + yield return err; linkedMembers.Remove (token); continue; @@ -123,22 +134,22 @@ public void Verify () throw new NotImplementedException ($"Don't know how to check member of type {originalMember.GetType ()}"); } - // Verify anything not in the main assembly - VerifyLinkingOfOtherAssemblies(this.originalAssembly); + // Verify anything not in the main assembly + foreach(var err in VerifyLinkingOfOtherAssemblies(this.originalAssembly)) + yield return err; - // Filter out all members which are not from the main assembly - // The Kept attributes are "optional" for non-main assemblies - string mainModuleName = originalAssembly.Name.Name; + // Filter out all members which are not from the main assembly + // The Kept attributes are "optional" for non-main assemblies + string mainModuleName = originalAssembly.Name.Name; List externalMembers = linkedMembers.Where (m => GetModuleName (m.Value.Entity) != mainModuleName).Select (m => m.Key).ToList (); foreach (var externalMember in externalMembers) { linkedMembers.Remove (externalMember); } if (linkedMembers.Count != 0) - Assert.Fail( - "Linked output includes unexpected member:\n " + - string.Join ("\n ", linkedMembers.Values.Select (e => e.Entity.GetDisplayName ()))); - } + yield return "Linked output includes unexpected member:\n " + + string.Join ("\n ", linkedMembers.Values.Select (e => e.Entity.GetDisplayName ())); + } private void PopulateLinkedMembers () { @@ -278,32 +289,32 @@ static bool ShouldIncludeType (TypeDesc type) static bool ShouldIncludeMethod (MethodDesc method) => ShouldIncludeType (method.OwningType) && ShouldIncludeEntityByDisplayName (method); } - private static MetadataType? GetOwningType (TypeSystemEntity? entity) - { - return entity switch - { - MetadataType type => type.ContainingType as MetadataType, - MethodDesc method => method.OwningType as MetadataType, - PropertyPseudoDesc prop => prop.OwningType, - EventPseudoDesc e => e.OwningType, - _ => null - }; - } + private static MetadataType? GetOwningType (TypeSystemEntity? entity) + { + return entity switch + { + MetadataType type => type.ContainingType as MetadataType, + MethodDesc method => method.OwningType as MetadataType, + PropertyPseudoDesc prop => prop.OwningType, + EventPseudoDesc e => e.OwningType, + _ => null + }; + } private static string? GetModuleName (TypeSystemEntity entity) { return entity switch { MetadataType type => type.Module.ToString (), - _ => GetOwningType(entity)?.Module.ToString() + _ => GetOwningType(entity)?.Module.ToString() }; } - protected virtual void VerifyModule (ModuleDefinition original, ModuleDefinition? linked) + protected virtual IEnumerable VerifyModule (ModuleDefinition original, ModuleDefinition? linked) { // We never link away a module today so let's make sure the linked one isn't null if (linked == null) { - Assert.Fail($"Linked assembly `{original.Assembly.Name.Name}` is missing module `{original.Name}`"); - return; + yield return $"Linked assembly `{original.Assembly.Name.Name}` is missing module `{original.Name}`"; + yield break; } var expected = original.Assembly.MainModule.AllDefinedTypes () @@ -314,16 +325,18 @@ protected virtual void VerifyModule (ModuleDefinition original, ModuleDefinition .Select (name => name.Name) .ToArray (); - Assert.Equal (expected, actual); + if (!expected.Equals(actual)) + yield return "Module references do not match"; - VerifyCustomAttributes (original, linked); + foreach(var err in VerifyCustomAttributes (original, linked)) + yield return err; } - void VerifyTypeDefinition (TypeDefinition original, LinkedEntity? linkedEntity) + IEnumerable VerifyTypeDefinition (TypeDefinition original, LinkedEntity? linkedEntity) { TypeDesc? linked = linkedEntity?.Entity as TypeDesc; if (linked != null && NameUtils.GetActualOriginDisplayName (linked) is string linkedDisplayName && verifiedGeneratedTypes.Contains (linkedDisplayName)) - return; + yield break; EcmaModule? linkedModule = (linked as MetadataType)?.Module as EcmaModule; @@ -340,7 +353,7 @@ void VerifyTypeDefinition (TypeDefinition original, LinkedEntity? linkedEntity) if (!expectedKept) { if (linked == null) - return; + yield break; // Compiler generated members can't be annotated with `Kept` attributes directly // For some of them we have special attributes (backing fields for example), but it's impractical to define @@ -351,13 +364,14 @@ void VerifyTypeDefinition (TypeDefinition original, LinkedEntity? linkedEntity) // we do want to validate. There's no specific use case right now, but I can easily imagine one // for more detailed testing of for example custom attributes on local functions, or similar. if (!IsCompilerGeneratedMember (original)) - Assert.Fail($"Type `{original}' should have been removed"); + yield return $"Type `{original}' should have been removed"; } bool prev = checkNames; checkNames |= original.HasAttribute (nameof (VerifyMetadataNamesAttribute)); - VerifyTypeDefinitionKept (original, linked); + foreach(var err in VerifyTypeDefinitionKept (original, linked)) + yield return err; checkNames = prev; @@ -370,7 +384,7 @@ void VerifyTypeDefinition (TypeDefinition original, LinkedEntity? linkedEntity) var linkedMemberName = linkedMembers.Keys.FirstOrDefault (l => l.Contains (newName)); if (linkedMemberName == null) - Assert.Fail($"Newly created member '{newName}' was not found"); + yield return $"Newly created member '{newName}' was not found"; linkedMembers.Remove (linkedMemberName); } @@ -378,33 +392,43 @@ void VerifyTypeDefinition (TypeDefinition original, LinkedEntity? linkedEntity) } } - protected virtual void VerifyTypeDefinitionKept (TypeDefinition original, TypeDesc? linked) + protected virtual IEnumerable VerifyTypeDefinitionKept (TypeDefinition original, TypeDesc? linked) { // NativeAOT will not keep delegate backing field type information, it's compiled down to a set of static fields // this infra currently doesn't track fields in any way. // Same goes for private implementation detail type. if (IsDelegateBackingFieldsType (original) || IsPrivateImplementationDetailsType(original)) - return; + yield break; if (linked == null) { - Assert.Fail($"Type `{original}' should have been kept"); - return; + yield return $"Type `{original}' should have been kept"; + yield break; } #if false // Skip verification of type metadata for compiler generated types (we don't currently need it yet) if (!IsCompilerGeneratedMember (original)) { - VerifyKeptByAttributes (original, linked); + foreach(var err in VerifyKeptByAttributes (original, linked)) + yield return err; if (!original.IsInterface) - VerifyBaseType (original, linked); + { + foreach(var err in VerifyBaseType (original, linked)) + yield return err; + } - VerifyInterfaces (original, linked); - VerifyPseudoAttributes (original, linked); - VerifyGenericParameters (original, linked); - VerifyCustomAttributes (original, linked); - VerifySecurityAttributes (original, linked); + foreach(var err in VerifyInterfaces (original, linked)) + yield return err; + foreach(var err in VerifyPseudoAttributes (original, linked)) + yield return err; + foreach(var err in VerifyGenericParameters (original, linked)) + yield return err; + foreach(var err in VerifyCustomAttributes (original, linked)) + yield return err; + foreach(var err in VerifySecurityAttributes (original, linked)) + yield return err; - VerifyFixedBufferFields (original, linked); + foreach(var err in VerifyFixedBufferFields (original, linked)) + yield return err; } #endif @@ -414,7 +438,8 @@ protected virtual void VerifyTypeDefinitionKept (TypeDefinition original, TypeDe token, out LinkedEntity? linkedMember); - VerifyTypeDefinition (td, linkedMember); + foreach(var err in VerifyTypeDefinition (td, linkedMember)) + yield return err; linkedMembers.Remove (token); } @@ -425,7 +450,8 @@ protected virtual void VerifyTypeDefinitionKept (TypeDefinition original, TypeDe linkedMembers.TryGetValue ( token, out LinkedEntity? linkedMember); - VerifyProperty (p, linkedMember, linked); + foreach(var err in VerifyProperty (p, linkedMember, linked)) + yield return err; linkedMembers.Remove (token); } // Need to check events before fields so that the KeptBackingFieldAttribute is handled correctly @@ -435,7 +461,8 @@ protected virtual void VerifyTypeDefinitionKept (TypeDefinition original, TypeDe linkedMembers.TryGetValue ( token, out LinkedEntity? linkedMember); - VerifyEvent (e, linkedMember, linked); + foreach(var err in VerifyEvent (e, linkedMember, linked)) + yield return err; linkedMembers.Remove (token); } @@ -460,12 +487,13 @@ protected virtual void VerifyTypeDefinitionKept (TypeDefinition original, TypeDe token, out LinkedEntity? linkedMember); - VerifyMethod (m, linkedMember); + foreach(var err in VerifyMethod (m, linkedMember)) + yield return err; linkedMembers.Remove (token); } } - private void VerifyBaseType (TypeDefinition src, TypeDefinition linked) + private IEnumerable VerifyBaseType (TypeDefinition src, TypeDefinition linked) { string expectedBaseName; var expectedBaseGenericAttr = src.CustomAttributes.FirstOrDefault (w => w.AttributeType.Name == nameof (KeptBaseTypeAttribute) && w.ConstructorArguments.Count > 1); @@ -477,26 +505,28 @@ private void VerifyBaseType (TypeDefinition src, TypeDefinition linked) } if (expectedBaseName != linked.BaseType?.FullName) { - Assert.Fail($"Incorrect base type on : {linked.Name}. Expected {expectedBaseName}, actual {linked.BaseType?.FullName}"); + yield return $"Incorrect base type on : {linked.Name}. Expected {expectedBaseName}, actual {linked.BaseType?.FullName}"; } } - private void VerifyInterfaces (TypeDefinition src, TypeDefinition linked) + private IEnumerable VerifyInterfaces (TypeDefinition src, TypeDefinition linked) { var expectedInterfaces = new HashSet (src.CustomAttributes .Where (w => w.AttributeType.Name == nameof (KeptInterfaceAttribute)) .Select (FormatBaseOrInterfaceAttributeValue)); if (expectedInterfaces.Count == 0) { - Assert.False (linked.HasInterfaces, $"Type `{src}' has unexpected interfaces"); + if (linked.HasInterfaces) + yield return $"Type `{src}' has unexpected interfaces"; } else { foreach (var iface in linked.Interfaces) { if (!expectedInterfaces.Remove (iface.InterfaceType.FullName)) { - Assert.True (expectedInterfaces.Remove (iface.InterfaceType.Resolve ().FullName), $"Type `{src}' interface `{iface.InterfaceType.Resolve ().FullName}' should have been removed"); + if (true != expectedInterfaces.Remove (iface.InterfaceType.Resolve ().FullName)) + yield return $"Type `{src}' interface `{iface.InterfaceType.Resolve ().FullName}' should have been removed"; } } if (expectedInterfaces.Count != 0) - Assert.Fail($"Expected interfaces were not found on {src}"); + yield return $"Expected interfaces were not found on {src}"; } } @@ -523,26 +553,27 @@ private static string FormatBaseOrInterfaceAttributeValue (CustomAttribute attr) return builder.ToString (); } - private void VerifyField (FieldDefinition src, FieldDesc? linked) + private IEnumerable VerifyField (FieldDefinition src, FieldDesc? linked) { bool compilerGenerated = IsCompilerGeneratedMember (src); bool expectedKept = ShouldBeKept (src) | compilerGenerated; if (!expectedKept) { if (linked != null) - Assert.Fail($"Field `{src}' should have been removed"); + yield return $"Field `{src}' should have been removed"; - return; + yield break; } - VerifyFieldKept (src, linked, compilerGenerated); + foreach(var err in VerifyFieldKept (src, linked, compilerGenerated)) + yield return err; } - private static void VerifyFieldKept (FieldDefinition src, FieldDesc? linked, bool compilerGenerated) + private static IEnumerable VerifyFieldKept (FieldDefinition src, FieldDesc? linked, bool compilerGenerated) { if (linked == null) { - Assert.Fail($"Field `{src}' should have been kept"); - return; + yield return $"Field `{src}' should have been kept"; + yield break; } @@ -550,18 +581,20 @@ private static void VerifyFieldKept (FieldDefinition src, FieldDesc? linked, boo throw new NotImplementedException ("Constant value for a field is not yet supported by the test infra."); #if false if (!Equals (src.Constant, linked.Constant)) { - Assert.Fail($"Field '{src}' value doesn's match. Expected {src.Constant}, actual {linked.Constant}"); + yield return $"Field '{src}' value doesn's match. Expected {src.Constant}, actual {linked.Constant}"; } #endif #if false - VerifyPseudoAttributes (src, linked); + foreach(var err in VerifyPseudoAttributes (src, linked)) + yield return err; if (!compilerGenerated) - VerifyCustomAttributes (src, linked); + foreach(var err in VerifyCustomAttributes (src, linked)) + yield return err; #endif } - private void VerifyProperty (PropertyDefinition src, LinkedEntity? linkedEntity, TypeDesc linkedType) + private IEnumerable VerifyProperty (PropertyDefinition src, LinkedEntity? linkedEntity, TypeDesc linkedType) { PropertyPseudoDesc? linked = linkedEntity?.Entity as PropertyPseudoDesc; VerifyMemberBackingField (src, linkedType); @@ -571,55 +604,61 @@ private void VerifyProperty (PropertyDefinition src, LinkedEntity? linkedEntity, if (!expectedKept) { if (linked is not null) - Assert.Fail($"Property `{src}' should have been removed"); + yield return $"Property `{src}' should have been removed"; - return; + yield break; } if (linked is null) { - Assert.Fail($"Property `{src}' should have been kept"); - return; + yield return $"Property `{src}' should have been kept"; + yield break; } if (src.HasConstant) throw new NotSupportedException ("Constant value for a property is not yet supported by the test infra."); #if false if (src.Constant != linked.Constant) { - Assert.Fail($"Property '{src}' value doesn's match. Expected {src.Constant}, actual {linked.Constant}"); + yield return $"Property '{src}' value doesn's match. Expected {src.Constant}, actual {linked.Constant}"; } #endif #if false - VerifyPseudoAttributes (src, linked); + foreach(var err in VerifyPseudoAttributes (src, linked)) + yield return err; if (!compilerGenerated) - VerifyCustomAttributes (src, linked); + { + foreach(var err in VerifyCustomAttributes (src, linked)) + yield return err; + } #endif } - private void VerifyEvent (EventDefinition src, LinkedEntity? linkedEntity, TypeDesc linkedType) + private IEnumerable VerifyEvent (EventDefinition src, LinkedEntity? linkedEntity, TypeDesc linkedType) { EventPseudoDesc? linked = linkedEntity?.Entity as EventPseudoDesc; - VerifyMemberBackingField (src, linkedType); + foreach(var err in VerifyMemberBackingField (src, linkedType)) + yield return err; bool compilerGenerated = IsCompilerGeneratedMember (src); bool expectedKept = ShouldBeKept (src) | compilerGenerated; if (!expectedKept) { if (linked is not null) - Assert.Fail($"Event `{src}' should have been removed"); + yield return $"Event `{src}' should have been removed"; - return; + yield break; } if (linked is null) { - Assert.Fail($"Event `{src}' should have been kept"); - return; + yield return $"Event `{src}' should have been kept"; + yield break; } if (src.CustomAttributes.Any (attr => attr.AttributeType.Name == nameof (KeptEventAddMethodAttribute))) { // TODO: This is wrong - we can't validate that the method is present by looking at linked (as that is not actually linked) // we need to look into linkedMembers to see if the method was actually preserved by the compiler (and has an entry point) - VerifyMethodInternal (src.AddMethod, new LinkedMethodEntity(linked.AddMethod, false), true, compilerGenerated); + foreach(var err in VerifyMethodInternal (src.AddMethod, new LinkedMethodEntity(linked.AddMethod, false), true, compilerGenerated)) + yield return err; verifiedEventMethods.Add (src.AddMethod.FullName); linkedMembers.Remove (new AssemblyQualifiedToken (src.AddMethod)); } @@ -627,47 +666,54 @@ private void VerifyEvent (EventDefinition src, LinkedEntity? linkedEntity, TypeD if (src.CustomAttributes.Any (attr => attr.AttributeType.Name == nameof (KeptEventRemoveMethodAttribute))) { // TODO: This is wrong - we can't validate that the method is present by looking at linked (as that is not actually linked) // we need to look into linkedMembers to see if the method was actually preserved by the compiler (and has an entry point) - VerifyMethodInternal (src.RemoveMethod, new LinkedMethodEntity(linked.RemoveMethod, false), true, compilerGenerated); + foreach(var err in VerifyMethodInternal (src.RemoveMethod, new LinkedMethodEntity(linked.RemoveMethod, false), true, compilerGenerated)) + yield return err; verifiedEventMethods.Add (src.RemoveMethod.FullName); linkedMembers.Remove (new AssemblyQualifiedToken (src.RemoveMethod)); } #if false - VerifyPseudoAttributes (src, linked); + foreach(var err in VerifyPseudoAttributes (src, linked)) + yield return err; if (!compilerGenerated) - VerifyCustomAttributes (src, linked); + { + foreach(var err in VerifyCustomAttributes (src, linned)) + yield return err; + } #endif } - private void VerifyMethod (MethodDefinition src, LinkedEntity? linkedEntity) + private IEnumerable VerifyMethod (MethodDefinition src, LinkedEntity? linkedEntity) { LinkedMethodEntity? linked = linkedEntity as LinkedMethodEntity; bool compilerGenerated = IsCompilerGeneratedMember (src); bool expectedKept = ShouldMethodBeKept (src); - VerifyMethodInternal (src, linked, expectedKept, compilerGenerated); + foreach(var err in VerifyMethodInternal (src, linked, expectedKept, compilerGenerated)) + yield return err; } - private void VerifyMethodInternal (MethodDefinition src, LinkedMethodEntity? linked, bool expectedKept, bool compilerGenerated) + private IEnumerable VerifyMethodInternal (MethodDefinition src, LinkedMethodEntity? linked, bool expectedKept, bool compilerGenerated) { if (!expectedKept) { if (linked == null) - return; + yield break; // Similar to comment on types, compiler-generated methods can't be annotated with Kept attribute directly // so we're not going to validate kept/remove on them. Note that we're still going to go validate "into" them // to check for other properties (like parameter name presence/removal for example) if (!compilerGenerated) - Assert.Fail($"Method `{NameUtils.GetExpectedOriginDisplayName (src)}' should have been removed"); + yield return $"Method `{NameUtils.GetExpectedOriginDisplayName (src)}' should have been removed"; } - VerifyMethodKept (src, linked, compilerGenerated); + foreach(var err in VerifyMethodKept (src, linked, compilerGenerated)) + yield return err; } - private void VerifyMemberBackingField (IMemberDefinition src, TypeDesc? linkedType) + private IEnumerable VerifyMemberBackingField (IMemberDefinition src, TypeDesc? linkedType) { var keptBackingFieldAttribute = src.CustomAttributes.FirstOrDefault (attr => attr.AttributeType.Name == nameof (KeptBackingFieldAttribute)); if (keptBackingFieldAttribute == null) - return; + yield break; var backingFieldName = src.MetadataToken.TokenType == TokenType.Property ? $"<{src.Name}>k__BackingField" : src.Name; @@ -683,60 +729,75 @@ private void VerifyMemberBackingField (IMemberDefinition src, TypeDesc? linkedTy } if (srcField == null) { - Assert.Fail($"{src.MetadataToken.TokenType} `{src}', could not locate the expected backing field {backingFieldName}"); - return; + yield return $"{src.MetadataToken.TokenType} `{src}', could not locate the expected backing field {backingFieldName}"; + yield break; } - VerifyFieldKept (srcField, linkedType?.GetFields ()?.FirstOrDefault (l => srcField.Name == l.Name), compilerGenerated: true); + foreach(var err in VerifyFieldKept (srcField, linkedType?.GetFields ()?.FirstOrDefault (l => srcField.Name == l.Name), compilerGenerated: true)) + yield return err; verifiedGeneratedFields.Add (srcField.FullName); linkedMembers.Remove (new AssemblyQualifiedToken (srcField)); } - void VerifyMethodKept (MethodDefinition src, LinkedMethodEntity? linked, bool compilerGenerated) + IEnumerable VerifyMethodKept (MethodDefinition src, LinkedMethodEntity? linked, bool compilerGenerated) { if (linked == null) { - Assert.Fail($"Method `{NameUtils.GetExpectedOriginDisplayName (src)}' should have been kept"); - return; + yield return $"Method `{NameUtils.GetExpectedOriginDisplayName (src)}' should have been kept"; + yield break; } #if false - VerifyPseudoAttributes (src, linked); - VerifyGenericParameters (src, linked); + foreach(var err in VerifyPseudoAttributes (src, linked)) + yield return err; + foreach(var err in VerifyGenericParameters (src, linked)) + yield return err; if (!compilerGenerated) { - VerifyCustomAttributes (src, linked); - VerifyCustomAttributes (src.MethodReturnType, linked.MethodReturnType); + foreach(var err in VerifyCustomAttributes (src, linked)) + yield return err; + foreach(var err in VerifyCustomAttributes (src.MethodReturnType, linked.MethodReturnType)) + yield return err; } #endif - VerifyParameters (src, linked); + foreach(var err in VerifyParameters (src, linked)) + yield return err; #if false - VerifySecurityAttributes (src, linked); - VerifyArrayInitializers (src, linked); + foreach(var err in VerifySecurityAttributes (src, linked)) + yield return err; + foreach(var err in VerifyArrayInitializers (src, linked)) + yield return err; // Method bodies are not very different in Native AOT - VerifyMethodBody (src, linked); - VerifyKeptByAttributes (src, linked); + foreach(var err in VerifyMethodBody (src, linked)) + yield return err; + foreach(var err in VerifyKeptByAttributes (src, linked)) + yield return err; #endif } - protected virtual void VerifyMethodBody (MethodDefinition src, MethodDefinition linked) + protected virtual IEnumerable VerifyMethodBody (MethodDefinition src, MethodDefinition linked) { if (!src.HasBody) - return; + yield break; - VerifyInstructions (src, linked); - VerifyLocals (src, linked); + foreach(var err in VerifyInstructions (src, linked)) + yield return err; + foreach(var err in VerifyLocals (src, linked)) + yield return err; } - protected static void VerifyInstructions (MethodDefinition src, MethodDefinition linked) + protected static IEnumerable VerifyInstructions (MethodDefinition src, MethodDefinition linked) { - VerifyBodyProperties ( + foreach (var err in VerifyBodyProperties ( src, linked, nameof (ExpectedInstructionSequenceAttribute), nameof (ExpectBodyModifiedAttribute), "instructions", m => FormatMethodBody (m.Body), - attr => GetStringArrayAttributeValue (attr)!.ToArray ()); + attr => GetStringArrayAttributeValue (attr)!.ToArray ())) + { + yield return err; + } } public static string[] FormatMethodBody (MethodBody body) @@ -848,19 +909,22 @@ private static string FormatInstruction (Instruction instr) } } - private static void VerifyLocals (MethodDefinition src, MethodDefinition linked) + private static IEnumerable VerifyLocals (MethodDefinition src, MethodDefinition linked) { - VerifyBodyProperties ( + foreach(var err in VerifyBodyProperties ( src, linked, nameof (ExpectedLocalsSequenceAttribute), nameof (ExpectLocalsModifiedAttribute), "locals", m => m.Body.Variables.Select (v => v.VariableType.ToString ()).ToArray (), - attr => GetStringOrTypeArrayAttributeValue (attr).ToArray ()); + attr => GetStringOrTypeArrayAttributeValue (attr).ToArray ())) + { + yield return err; + } } - public static void VerifyBodyProperties (MethodDefinition src, MethodDefinition linked, string sequenceAttributeName, string expectModifiedAttributeName, + public static IEnumerable VerifyBodyProperties (MethodDefinition src, MethodDefinition linked, string sequenceAttributeName, string expectModifiedAttributeName, string propertyDescription, Func valueCollector, Func getExpectFromSequenceAttribute) @@ -870,16 +934,25 @@ public static void VerifyBodyProperties (MethodDefinition src, MethodDefinition var srcValues = valueCollector (src); if (src.CustomAttributes.Any (attr => attr.AttributeType.Name == expectModifiedAttributeName)) { - linkedValues.Should ().BeEquivalentTo (srcValues, $"Expected method `{src} to have {propertyDescription} modified, however, the {propertyDescription} were the same as the original\n{FormattingUtils.FormatSequenceCompareFailureMessage (linkedValues, srcValues)}"); + if (linkedValues.SequenceEqual(srcValues)) + { + yield return $"Expected method `{src} to have {propertyDescription} modified, however, the {propertyDescription} were the same as the original\n{FormattingUtils.FormatSequenceCompareFailureMessage (linkedValues, srcValues)}"; + } } else if (expectedSequenceAttribute != null) { var expected = getExpectFromSequenceAttribute (expectedSequenceAttribute).ToArray (); - linkedValues.Should ().BeEquivalentTo (expected, $"Expected method `{src} to have it's {propertyDescription} modified, however, the sequence of {propertyDescription} does not match the expected value\n{FormattingUtils.FormatSequenceCompareFailureMessage2 (linkedValues, expected, srcValues)}"); + if (!linkedValues.SequenceEqual(expected)) + { + yield return $"Expected method `{src} to have it's {propertyDescription} modified, however, the sequence of {propertyDescription} does not match the expected value\n{FormattingUtils.FormatSequenceCompareFailureMessage2 (linkedValues, expected, srcValues)}"; + } } else { - linkedValues.Should ().BeEquivalentTo (srcValues, $"Expected method `{src} to have it's {propertyDescription} unchanged, however, the {propertyDescription} differ from the original\n{FormattingUtils.FormatSequenceCompareFailureMessage (linkedValues, srcValues)}"); + if (!linkedValues.SequenceEqual(srcValues)) + { + yield return $"Expected method `{src} to have it's {propertyDescription} unchanged, however, the {propertyDescription} differ from the original\n{FormattingUtils.FormatSequenceCompareFailureMessage (linkedValues, srcValues)}"; + } } } - private void VerifyReferences (AssemblyDefinition original, AssemblyDefinition linked) + private IEnumerable VerifyReferences (AssemblyDefinition original, AssemblyDefinition linked) { var expected = original.MainModule.AllDefinedTypes () .SelectMany (t => GetCustomAttributeCtorValues (t, nameof (KeptReferenceAttribute))) @@ -896,13 +969,14 @@ private void VerifyReferences (AssemblyDefinition original, AssemblyDefinition l Once 1 kept reference attribute is used, the test will need to define all of of it's expected references */ if (expected.Length == 0) - return; + yield break; var actual = linked.MainModule.AssemblyReferences .Select (name => name.Name) .ToArray (); - actual.Should ().BeEquivalentTo (expected); + if (!actual.SequenceEqual(expected)) + yield return $"Expected references do not match actual references:\n\tExpected: {string.Join(", ", expected)}\n\tActual: {string.Join(", ", actual)}"; } private string? ReduceAssemblyFileNameOrNameToNameOnly (string? fileNameOrAssemblyName) @@ -917,75 +991,97 @@ private void VerifyReferences (AssemblyDefinition original, AssemblyDefinition l return fileNameOrAssemblyName; } - private void VerifyResources (AssemblyDefinition original, AssemblyDefinition linked) + private IEnumerable VerifyResources (AssemblyDefinition original, AssemblyDefinition linked) { - var expectedResourceNames = original.MainModule.AllDefinedTypes () + List expectedResourceNames = original.MainModule.AllDefinedTypes () .SelectMany (t => GetCustomAttributeCtorValues (t, nameof (KeptResourceAttribute))) .ToList (); foreach (var resource in linked.MainModule.Resources) { if (!expectedResourceNames.Remove (resource.Name)) - Assert.Fail($"Resource '{resource.Name}' should be removed."); + yield return $"Resource '{resource.Name}' should be removed."; EmbeddedResource embeddedResource = (EmbeddedResource) resource; var expectedResource = (EmbeddedResource) original.MainModule.Resources.First (r => r.Name == resource.Name); - embeddedResource.GetResourceData ().Should ().BeEquivalentTo (expectedResource.GetResourceData (), $"Resource '{resource.Name}' data doesn't match."); + if (!embeddedResource.GetResourceData().SequenceEqual(expectedResource.GetResourceData())) + yield return $"Resource '{resource.Name}' data doesn't match."; } if (expectedResourceNames.Count > 0) { - Assert.Fail($"Resource '{expectedResourceNames.First ()}' should be kept."); + yield return $"Resource '{expectedResourceNames.First ()}' should be kept."; } } - private void VerifyExportedTypes (AssemblyDefinition original, AssemblyDefinition linked) + private IEnumerable VerifyExportedTypes (AssemblyDefinition original, AssemblyDefinition linked) { var expectedTypes = original.MainModule.AllDefinedTypes () .SelectMany (t => GetCustomAttributeCtorValues (t, nameof (KeptExportedTypeAttribute)).Select (l => l?.FullName ?? "")).ToArray (); - linked.MainModule.ExportedTypes.Select (l => l.FullName).Should ().BeEquivalentTo (expectedTypes); + if (!linked.MainModule.ExportedTypes.Select (l => l.FullName).SequenceEqual(expectedTypes)) + yield return $"Exported types do not match expected values"; } - protected virtual void VerifyPseudoAttributes (MethodDefinition src, MethodDefinition linked) + protected virtual IEnumerable VerifyPseudoAttributes (MethodDefinition src, MethodDefinition linked) { var expected = (MethodAttributes) GetExpectedPseudoAttributeValue (src, (uint) src.Attributes); - linked.Attributes.Should ().Be (expected, $"Method `{src}' pseudo attributes did not match expected"); + if(!linked.Attributes.Equals(expected)) + { + yield return $"Method `{src}' pseudo attributes did not match expected"; + } } - protected virtual void VerifyPseudoAttributes (TypeDefinition src, TypeDefinition linked) + protected virtual IEnumerable VerifyPseudoAttributes (TypeDefinition src, TypeDefinition linked) { var expected = (TypeAttributes) GetExpectedPseudoAttributeValue (src, (uint) src.Attributes); - linked.Attributes.Should ().Be (expected, $"Type `{src}' pseudo attributes did not match expected"); + + if(!linked.Attributes.Equals(expected)) + { + yield return $"Type `{src}' pseudo attributes did not match expected"; + } } - protected virtual void VerifyPseudoAttributes (FieldDefinition src, FieldDefinition linked) + protected virtual IEnumerable VerifyPseudoAttributes (FieldDefinition src, FieldDefinition linked) { var expected = (FieldAttributes) GetExpectedPseudoAttributeValue (src, (uint) src.Attributes); - linked.Attributes.Should ().Be (expected, $"Field `{src}' pseudo attributes did not match expected"); + if(!linked.Attributes.Equals(expected)) + { + yield return $"Field `{src}' pseudo attributes did not match expected"; + } } - protected virtual void VerifyPseudoAttributes (PropertyDefinition src, PropertyDefinition linked) + protected virtual IEnumerable VerifyPseudoAttributes (PropertyDefinition src, PropertyDefinition linked) { var expected = (PropertyAttributes) GetExpectedPseudoAttributeValue (src, (uint) src.Attributes); - linked.Attributes.Should ().Be (expected, $"Property `{src}' pseudo attributes did not match expected"); + if(!linked.Attributes.Equals(expected)) + { + yield return $"Property `{src}' pseudo attributes did not match expected"; + } + } - protected virtual void VerifyPseudoAttributes (EventDefinition src, EventDefinition linked) + protected virtual IEnumerable VerifyPseudoAttributes (EventDefinition src, EventDefinition linked) { var expected = (EventAttributes) GetExpectedPseudoAttributeValue (src, (uint) src.Attributes); - linked.Attributes.Should ().Be (expected, $"Event `{src}' pseudo attributes did not match expected"); + if(!linked.Attributes.Equals(expected)) + { + yield return $"Event `{src}' pseudo attributes did not match expected"; + } } - protected virtual void VerifyCustomAttributes (ICustomAttributeProvider src, ICustomAttributeProvider linked) + protected virtual IEnumerable VerifyCustomAttributes (ICustomAttributeProvider src, ICustomAttributeProvider linked) { var expectedAttrs = GetExpectedAttributes (src).ToList (); var linkedAttrs = FilterLinkedAttributes (linked).ToList (); - linkedAttrs.Should ().BeEquivalentTo (expectedAttrs, $"Custom attributes on `{src}' are not matching"); + if(!linkedAttrs.SequenceEqual(expectedAttrs)) + { + yield return $"Custom attributes on `{src}' are not matching"; + } } - protected virtual void VerifySecurityAttributes (ICustomAttributeProvider src, ISecurityDeclarationProvider linked) + protected virtual IEnumerable VerifySecurityAttributes (ICustomAttributeProvider src, ISecurityDeclarationProvider linked) { var expectedAttrs = GetCustomAttributeCtorValues (src, nameof (KeptSecurityAttribute)) .Select (attr => attr?.ToString () ?? "") @@ -993,11 +1089,14 @@ protected virtual void VerifySecurityAttributes (ICustomAttributeProvider src, I var linkedAttrs = FilterLinkedSecurityAttributes (linked).ToList (); - linkedAttrs.Should ().BeEquivalentTo (expectedAttrs, $"Security attributes on `{src}' are not matching"); + if(!linkedAttrs.SequenceEqual(expectedAttrs)) + { + yield return $"Security attributes on `{src}' are not matching"; + } } #if false - protected virtual void VerifyArrayInitializers (MethodDefinition src, MethodDefinition linked) + protected virtual IEnumerable VerifyArrayInitializers (MethodDefinition src, MethodDefinition linked) { var expectedIndices = GetCustomAttributeCtorValues (src, nameof (KeptInitializerData)) .Cast () @@ -1009,19 +1108,19 @@ protected virtual void VerifyArrayInitializers (MethodDefinition src, MethodDefi return; if (!src.HasBody) - Assert.Fail($"`{nameof (KeptInitializerData)}` cannot be used on methods that don't have bodies"); + yield return $"`{nameof (KeptInitializerData)}` cannot be used on methods that don't have bodies"; var srcImplementationDetails = src.Module.Types.FirstOrDefault (t => string.IsNullOrEmpty (t.Namespace) && t.Name.StartsWith ("")); if (srcImplementationDetails == null) { - Assert.Fail("Could not locate in the original assembly. Does your test use initializers?"); + yield return "Could not locate in the original assembly. Does your test use initializers?"; return; } var linkedImplementationDetails = linked.Module.Types.FirstOrDefault (t => string.IsNullOrEmpty (t.Namespace) && t.Name.StartsWith ("")); if (linkedImplementationDetails == null) { - Assert.Fail("Could not locate in the linked assembly"); + yield return "Could not locate in the linked assembly"; return; } @@ -1031,32 +1130,36 @@ protected virtual void VerifyArrayInitializers (MethodDefinition src, MethodDefi .ToArray (); if (possibleInitializerFields.Length == 0) - Assert.Fail($"`{src}` does not make use of any initializers"); + yield return $"`{src}` does not make use of any initializers"; if (expectKeptAll) { foreach (var srcField in possibleInitializerFields) { var linkedField = linkedImplementationDetails.Fields.FirstOrDefault (f => f.InitialValue.SequenceEqual (srcField.InitialValue)); - VerifyInitializerField (srcField, linkedField); + foreach(var err in VerifyInitializerField (srcField, linkedField)) + yield return err; } } else { foreach (var index in expectedIndices) { if (index < 0 || index > possibleInitializerFields.Length) - Assert.Fail($"Invalid expected index `{index}` in {src}. Value must be between 0 and {expectedIndices.Length}"); + yield return $"Invalid expected index `{index}` in {src}. Value must be between 0 and {expectedIndices.Length}"; var srcField = possibleInitializerFields[index]; var linkedField = linkedImplementationDetails.Fields.FirstOrDefault (f => f.InitialValue.SequenceEqual (srcField.InitialValue)); - VerifyInitializerField (srcField, linkedField); + foreach(var err in VerifyInitializerField (srcField, linkedField)) + yield return err; } } } - private void VerifyInitializerField (FieldDefinition src, FieldDefinition? linked) + private IEnumerable VerifyInitializerField (FieldDefinition src, FieldDefinition? linked) { - VerifyFieldKept (src, linked); + foreach(var err in VerifyFieldKept (src, linked)) + yield return err; verifiedGeneratedFields.Add (linked!.FullName); linkedMembers.Remove (new (linked)); - //VerifyTypeDefinitionKept (src.FieldType.Resolve (), linked.FieldType.Resolve ()); + // foreach(var err in VerifyTypeDefinitionKept (src.FieldType.Resolve (), linked.FieldType.Resolve ())) + // yield return err; linkedMembers.Remove (new (linked.FieldType.Resolve ())); linkedMembers.Remove (new (linked.DeclaringType.Resolve ())); verifiedGeneratedTypes.Add (linked.DeclaringType.FullName); @@ -1131,7 +1234,7 @@ protected virtual IEnumerable FilterLinkedSecurityAttributes (ISecurityD } #if false - private void VerifyFixedBufferFields (TypeDefinition src, TypeDefinition linked) + private IEnumerable VerifyFixedBufferFields (TypeDefinition src, TypeDefinition linked) { var fields = src.Fields.Where (f => f.CustomAttributes.Any (attr => attr.AttributeType.Name == nameof (KeptFixedBufferAttribute))); @@ -1141,93 +1244,109 @@ private void VerifyFixedBufferFields (TypeDefinition src, TypeDefinition linked) // while mcs and other versions of csc name it `__FixedBuffer0` var originalCompilerGeneratedBufferType = src.NestedTypes.FirstOrDefault (t => t.FullName.Contains ($"<{field.Name}>") && t.FullName.Contains ("__FixedBuffer")); if (originalCompilerGeneratedBufferType == null) { - Assert.Fail($"Could not locate original compiler generated fixed buffer type for field {field}"); - return; + yield return $"Could not locate original compiler generated fixed buffer type for field {field}"; + yield break; } var linkedCompilerGeneratedBufferType = linked.NestedTypes.FirstOrDefault (t => t.Name == originalCompilerGeneratedBufferType.Name); if (linkedCompilerGeneratedBufferType == null) { - Assert.Fail($"Missing expected type {originalCompilerGeneratedBufferType}"); - return; + yield return $"Missing expected type {originalCompilerGeneratedBufferType}"; + yield break; } // Have to verify the field before the type var originalElementField = originalCompilerGeneratedBufferType.Fields.FirstOrDefault (); if (originalElementField == null) { - Assert.Fail($"Could not locate original compiler generated FixedElementField on {originalCompilerGeneratedBufferType}"); - return; + yield return $"Could not locate original compiler generated FixedElementField on {originalCompilerGeneratedBufferType}"; + yield break; } var linkedField = linkedCompilerGeneratedBufferType?.Fields.FirstOrDefault (); - VerifyFieldKept (originalElementField, linkedField); + foreach(var err in VerifyFieldKept (originalElementField, linkedField)) + yield return err; verifiedGeneratedFields.Add (originalElementField.FullName); linkedMembers.Remove (new (linkedField!)); - //VerifyTypeDefinitionKept (originalCompilerGeneratedBufferType, linkedCompilerGeneratedBufferType); + // foreach(var err in VerifyTypeDefinitionKept (originalCompilerGeneratedBufferType, linkedCompilerGeneratedBufferType)) + // yield return err; verifiedGeneratedTypes.Add (originalCompilerGeneratedBufferType.FullName); } } - private void VerifyDelegateBackingFields (TypeDefinition src, TypeDefinition linked) + private IEnumerable VerifyDelegateBackingFields (TypeDefinition src, TypeDefinition linked) { var expectedFieldNames = GetCustomAttributeCtorValues (src, nameof (KeptDelegateCacheFieldAttribute)) .Select (unique => $"<>f__mg$cache{unique}") .ToList (); if (expectedFieldNames.Count == 0) - return; + yield break; foreach (var srcField in src.Fields) { if (!expectedFieldNames.Contains (srcField.Name)) continue; var linkedField = linked?.Fields.FirstOrDefault (l => l.Name == srcField.Name); - VerifyFieldKept (srcField, linkedField); + foreach(var err in VerifyFieldKept (srcField, linkedField)) + yield return err; verifiedGeneratedFields.Add (srcField.FullName); linkedMembers.Remove (new (srcField)); } } #endif - private void VerifyGenericParameters (IGenericParameterProvider src, IGenericParameterProvider linked) + private IEnumerable VerifyGenericParameters (IGenericParameterProvider src, IGenericParameterProvider linked) { - Assert.Equal (src.HasGenericParameters, linked.HasGenericParameters); + if (src.HasGenericParameters != linked.HasGenericParameters) + yield return $"Mismatch in having generic paramters. Expected {src.HasGenericParameters}, actual {linked.HasGenericParameters}"; + if (src.HasGenericParameters) { for (int i = 0; i < src.GenericParameters.Count; ++i) { // TODO: Verify constraints var srcp = src.GenericParameters[i]; var lnkp = linked.GenericParameters[i]; - VerifyCustomAttributes (srcp, lnkp); + foreach(var err in VerifyCustomAttributes (srcp, lnkp)) + yield return err; if (checkNames) { if (srcp.CustomAttributes.Any (attr => attr.AttributeType.Name == nameof (RemovedNameValueAttribute))) { string name = (src.GenericParameterType == GenericParameterType.Method ? "!!" : "!") + srcp.Position; - lnkp.Name.Should ().Be (name, "Expected empty generic parameter name"); + if (lnkp.Name != name) + yield return "Expected empty generic parameter name"; } else { - lnkp.Name.Should ().Be (srcp.Name, "Mismatch in generic parameter name"); + if (lnkp.Name != srcp.Name) + yield return "Mismatch in generic parameter name"; } } } } } - private void VerifyParameters (IMethodSignature src, LinkedMethodEntity linked) + private IEnumerable VerifyParameters (IMethodSignature src, LinkedMethodEntity linked) { - Assert.Equal (src.HasParameters, linked.Method.Signature.Length > 0); + if (src.HasParameters != linked.Method.Signature.Length > 0) + yield return $"Mismatch in having parameters in {src as MethodDefinition}: Expected {src.HasParameters}, actual {linked.Method.Signature.Length > 0}"; if (src.HasParameters) { for (int i = 0; i < src.Parameters.Count; ++i) { var srcp = src.Parameters[i]; //var lnkp = linked.Parameters[i]; #if false - VerifyCustomAttributes (srcp, lnkp); + foreach(var err in VerifyCustomAttributes (srcp, lnkp)) + yield return err; #endif if (checkNames) { if (srcp.CustomAttributes.Any (attr => attr.AttributeType.Name == nameof (RemovedNameValueAttribute))) - linked.IsReflected.Should ().BeFalse ($"Expected no parameter name (non-reflectable). Parameter {i} of {(src as MethodDefinition)}"); + { + if (linked.IsReflected != false) + yield return $"Expected no parameter name (non-reflectable). Parameter {i} of {src as MethodDefinition}"; + } else - linked.IsReflected.Should ().BeTrue ($"Expected accessible parameter name (reflectable). Parameter {i} of {(src as MethodDefinition)}"); + { + if (linked.IsReflected != true) + yield return $"Expected accessible parameter name (reflectable). Parameter {i} of {(src as MethodDefinition)}"; + } } } } @@ -1323,38 +1442,39 @@ private static bool HasActiveKeptDerivedAttribute (ICustomAttributeProvider prov return GetActiveKeptDerivedAttributes (provider).Any (); } - internal void VerifyLinkingOfOtherAssemblies (AssemblyDefinition original) + internal IEnumerable VerifyLinkingOfOtherAssemblies (AssemblyDefinition original) { var checks = BuildOtherAssemblyCheckTable (original); + List errs = []; try { foreach (var assemblyName in checks.Keys) { var linkedMembersInAssembly = ResolveLinkedMembersForAssembly (assemblyName); - var originalTargetAssembly = ResolveOriginalsAssembly(assemblyName); + var originalTargetAssembly = ResolveOriginalsAssembly(assemblyName); foreach (var checkAttrInAssembly in checks[assemblyName]) { var attributeTypeName = checkAttrInAssembly.AttributeType.Name; switch (attributeTypeName) { case nameof (KeptAllTypesAndMembersInAssemblyAttribute): - VerifyKeptAllTypesAndMembersInAssembly (assemblyName, linkedMembersInAssembly); + errs.AddRange(VerifyKeptAllTypesAndMembersInAssembly (assemblyName, linkedMembersInAssembly)); continue; case nameof (KeptAttributeInAssemblyAttribute): - // VerifyKeptAttributeInAssembly (checkAttrInAssembly, linkedAssembly); + // errs.AddRange(VerifyKeptAttributeInAssembly (checkAttrInAssembly, linkedAssembly)) continue; case nameof (RemovedAttributeInAssembly): - // VerifyRemovedAttributeInAssembly (checkAttrInAssembly, linkedAssembly); + // errs.AddRange(VerifyRemovedAttributeInAssembly (checkAttrInAssembly, linkedAssembly)) continue; default: break; } - var expectedTypeName = checkAttrInAssembly.ConstructorArguments[1].Value.ToString ()!; - var expectedType = originalTargetAssembly.MainModule.GetType(expectedTypeName); - linkedMembersInAssembly.TryGetValue(new AssemblyQualifiedToken(expectedType), out LinkedEntity? linkedTypeEntity); - MetadataType? linkedType = linkedTypeEntity?.Entity as MetadataType; + var expectedTypeName = checkAttrInAssembly.ConstructorArguments[1].Value.ToString ()!; + var expectedType = originalTargetAssembly.MainModule.GetType(expectedTypeName); + linkedMembersInAssembly.TryGetValue(new AssemblyQualifiedToken(expectedType), out LinkedEntity? linkedTypeEntity); + MetadataType? linkedType = linkedTypeEntity?.Entity as MetadataType; #if false - if (linkedType == null && linkedAssembly.MainModule.HasExportedTypes) { + if (linkedType == null && linkedAssembly.MainModule.HasExportedTypes) { ExportedType? exportedType = linkedAssembly.MainModule.ExportedTypes .FirstOrDefault (exported => exported.FullName == expectedTypeName); @@ -1371,92 +1491,108 @@ internal void VerifyLinkingOfOtherAssemblies (AssemblyDefinition original) switch (attributeTypeName) { case nameof (RemovedTypeInAssemblyAttribute): if (linkedType != null) - Assert.Fail ($"Type `{expectedTypeName}' should have been removed from assembly {assemblyName}"); + errs.Add($"Type `{expectedTypeName}' should have been removed from assembly {assemblyName}"); GetOriginalTypeFromInAssemblyAttribute (checkAttrInAssembly); break; case nameof (KeptTypeInAssemblyAttribute): if (linkedType == null) - Assert.Fail ($"Type `{expectedTypeName}' should have been kept in assembly {assemblyName}"); + errs.Add($"Type `{expectedTypeName}' should have been kept in assembly {assemblyName}"); break; #if false case nameof (RemovedInterfaceOnTypeInAssemblyAttribute): if (linkedType == null) - Assert.Fail ($"Type `{expectedTypeName}' should have been kept in assembly {assemblyName}"); - VerifyRemovedInterfaceOnTypeInAssembly (checkAttrInAssembly, linkedType); + { + errs.Add($"Type `{expectedTypeName}' should have been kept in assembly {assemblyName}"); + break; + } + errs.AddRange(VerifyRemovedInterfaceOnTypeInAssembly (checkAttrInAssembly, linkedType)); break; case nameof (KeptInterfaceOnTypeInAssemblyAttribute): if (linkedType == null) - Assert.Fail ($"Type `{expectedTypeName}' should have been kept in assembly {assemblyName}"); - VerifyKeptInterfaceOnTypeInAssembly (checkAttrInAssembly, linkedType); + { + errs.Add($"Type `{expectedTypeName}' should have been kept in assembly {assemblyName}"); + break; + } + errs.AddRange(VerifyKeptInterfaceOnTypeInAssembly (checkAttrInAssembly, linkedType)); break; case nameof (RemovedMemberInAssemblyAttribute): if (linkedType == null) continue; - VerifyRemovedMemberInAssembly (checkAttrInAssembly, linkedType); + errs.AddRange(VerifyRemovedMemberInAssembly (checkAttrInAssembly, linkedType)); break; case nameof (KeptBaseOnTypeInAssemblyAttribute): if (linkedType == null) - Assert.Fail ($"Type `{expectedTypeName}' should have been kept in assembly {assemblyName}"); - VerifyKeptBaseOnTypeInAssembly (checkAttrInAssembly, linkedType); + { + errs.Add($"Type `{expectedTypeName}' should have been kept in assembly {assemblyName}"); + break; + } + errs.AddRange(VerifyKeptBaseOnTypeInAssembly (checkAttrInAssembly, linkedType)); break; case nameof (KeptMemberInAssemblyAttribute): if (linkedType == null) - Assert.Fail ($"Type `{expectedTypeName}' should have been kept in assembly {assemblyName}"); + { + errs.Add($"Type `{expectedTypeName}' should have been kept in assembly {assemblyName}"); + break; + } - VerifyKeptMemberInAssembly (checkAttrInAssembly, linkedType); + errs.AddRange(VerifyKeptMemberInAssembly (checkAttrInAssembly, linkedType)); break; case nameof (RemovedForwarderAttribute): if (linkedAssembly.MainModule.ExportedTypes.Any (l => l.Name == expectedTypeName)) - Assert.Fail ($"Forwarder `{expectedTypeName}' should have been removed from assembly {assemblyName}"); + errs.Add($"Forwarder `{expectedTypeName}' should have been removed from assembly {assemblyName}"); break; case nameof (RemovedAssemblyReferenceAttribute): - Assert.False (linkedAssembly.MainModule.AssemblyReferences.Any (l => l.Name == expectedTypeName), - $"AssemblyRef '{expectedTypeName}' should have been removed from assembly {assemblyName}"); + if (linkedAssembly.MainModule.AssemblyReferences.Any (l => l.Name == expectedTypeName) != false) + errs.Add($"AssemblyRef '{expectedTypeName}' should have been removed from assembly {assemblyName}"); break; case nameof (KeptResourceInAssemblyAttribute): - VerifyKeptResourceInAssembly (checkAttrInAssembly); + errs.AddRange(VerifyKeptResourceInAssembly (checkAttrInAssembly)); break; case nameof (RemovedResourceInAssemblyAttribute): - VerifyRemovedResourceInAssembly (checkAttrInAssembly); + errs.AddRange(VerifyRemovedResourceInAssembly (checkAttrInAssembly)); break; case nameof (KeptReferencesInAssemblyAttribute): - VerifyKeptReferencesInAssembly (checkAttrInAssembly); + errs.AddRange(VerifyKeptReferencesInAssembly (checkAttrInAssembly)) break; case nameof (ExpectedInstructionSequenceOnMemberInAssemblyAttribute): if (linkedType == null) - Assert.Fail ($"Type `{expectedTypeName}` should have been kept in assembly {assemblyName}"); - VerifyExpectedInstructionSequenceOnMemberInAssembly (checkAttrInAssembly, linkedType); + { + errs.Add($"Type `{expectedTypeName}` should have been kept in assembly {assemblyName}"); + break; + } + errs.AddRange(VerifyExpectedInstructionSequenceOnMemberInAssembly (checkAttrInAssembly, linkedType)); break; - default: + default: UnhandledOtherAssemblyAssertion (expectedTypeName, checkAttrInAssembly, linkedType); break; #else - default: - break; + default: + break; #endif - } - } + } + } } } catch (AssemblyResolutionException e) { - Assert.Fail ($"Failed to resolve linked assembly `{e.AssemblyReference.Name}`. It must not exist in the output."); + errs.Add($"Failed to resolve linked assembly `{e.AssemblyReference.Name}`. It must not exist in the output."); } + return errs; } - private void VerifyKeptAttributeInAssembly (CustomAttribute inAssemblyAttribute, AssemblyDefinition linkedAssembly) + private IEnumerable VerifyKeptAttributeInAssembly (CustomAttribute inAssemblyAttribute, AssemblyDefinition linkedAssembly) { - VerifyAttributeInAssembly (inAssemblyAttribute, linkedAssembly, VerifyCustomAttributeKept); + return VerifyAttributeInAssembly (inAssemblyAttribute, linkedAssembly, VerifyCustomAttributeKept); } - private void VerifyRemovedAttributeInAssembly (CustomAttribute inAssemblyAttribute, AssemblyDefinition linkedAssembly) + private IEnumerable VerifyRemovedAttributeInAssembly (CustomAttribute inAssemblyAttribute, AssemblyDefinition linkedAssembly) { - VerifyAttributeInAssembly (inAssemblyAttribute, linkedAssembly, VerifyCustomAttributeRemoved); + return VerifyAttributeInAssembly (inAssemblyAttribute, linkedAssembly, VerifyCustomAttributeRemoved); } - private void VerifyAttributeInAssembly (CustomAttribute inAssemblyAttribute, AssemblyDefinition linkedAssembly, Action assertExpectedAttribute) + private IEnumerable VerifyAttributeInAssembly (CustomAttribute inAssemblyAttribute, AssemblyDefinition linkedAssembly, Func> assertExpectedAttribute) { var assemblyName = (string) inAssemblyAttribute.ConstructorArguments[0].Value!; string expectedAttributeTypeName; @@ -1469,23 +1605,30 @@ private void VerifyAttributeInAssembly (CustomAttribute inAssemblyAttribute, Ass if (inAssemblyAttribute.ConstructorArguments.Count == 2) { // Assembly - assertExpectedAttribute (linkedAssembly, expectedAttributeTypeName); - return; + foreach(var err in assertExpectedAttribute (linkedAssembly, expectedAttributeTypeName)) + yield return err; + yield break; } // We are asserting on type or member var typeOrTypeName = inAssemblyAttribute.ConstructorArguments[2].Value; var originalType = GetOriginalTypeFromInAssemblyAttribute (inAssemblyAttribute.ConstructorArguments[0].Value.ToString ()!, typeOrTypeName); if (originalType == null) - Assert.Fail ($"Invalid test assertion. The original `{assemblyName}` does not contain a type `{typeOrTypeName}`"); + { + yield return $"Invalid test assertion. The original `{assemblyName}` does not contain a type `{typeOrTypeName}`"; + yield break; + } var linkedType = linkedAssembly.MainModule.GetType (originalType.FullName); if (linkedType == null) - Assert.Fail ($"Missing expected type `{typeOrTypeName}` in `{assemblyName}`"); + { + yield return $"Missing expected type `{typeOrTypeName}` in `{assemblyName}`"; + yield break; + } if (inAssemblyAttribute.ConstructorArguments.Count == 3) { assertExpectedAttribute (linkedType, expectedAttributeTypeName); - return; + yield break; } // we are asserting on a member @@ -1497,61 +1640,71 @@ private void VerifyAttributeInAssembly (CustomAttribute inAssemblyAttribute, Ass if (originalFieldMember != null) { var linkedField = linkedType.Fields.FirstOrDefault (m => m.Name == memberName); if (linkedField == null) - Assert.Fail ($"Field `{memberName}` on Type `{originalType}` should have been kept"); + { + yield return $"Field `{memberName}` on Type `{originalType}` should have been kept"; + yield break; + } assertExpectedAttribute (linkedField, expectedAttributeTypeName); - return; + yield break; } var originalPropertyMember = originalType.Properties.FirstOrDefault (m => m.Name == memberName); if (originalPropertyMember != null) { var linkedProperty = linkedType.Properties.FirstOrDefault (m => m.Name == memberName); if (linkedProperty == null) - Assert.Fail ($"Property `{memberName}` on Type `{originalType}` should have been kept"); + { + yield return $"Property `{memberName}` on Type `{originalType}` should have been kept"; + yield break; + } - assertExpectedAttribute (linkedProperty, expectedAttributeTypeName); - return; + foreach(var err in assertExpectedAttribute (linkedProperty, expectedAttributeTypeName)) + yield return err; + yield break; } var originalMethodMember = originalType.Methods.FirstOrDefault (m => m.GetSignature () == memberName); if (originalMethodMember != null) { var linkedMethod = linkedType.Methods.FirstOrDefault (m => m.GetSignature () == memberName); if (linkedMethod == null) - Assert.Fail ($"Method `{memberName}` on Type `{originalType}` should have been kept"); + { + yield return $"Method `{memberName}` on Type `{originalType}` should have been kept"; + yield break; + } assertExpectedAttribute (linkedMethod, expectedAttributeTypeName); - return; + yield break; } - Assert.Fail ($"Invalid test assertion. No member named `{memberName}` exists on the original type `{originalType}`"); + yield return $"Invalid test assertion. No member named `{memberName}` exists on the original type `{originalType}`"; } - private static void VerifyCopyAssemblyIsKeptUnmodified (NPath outputDirectory, string assemblyName) + private static IEnumerable VerifyCopyAssemblyIsKeptUnmodified (NPath outputDirectory, string assemblyName) { string inputAssemblyPath = Path.Combine (Directory.GetParent (outputDirectory)!.ToString (), "input", assemblyName); string outputAssemblyPath = Path.Combine (outputDirectory, assemblyName); - Assert.True (File.ReadAllBytes (inputAssemblyPath).SequenceEqual (File.ReadAllBytes (outputAssemblyPath)), - $"Expected assemblies\n" + - $"\t{inputAssemblyPath}\n" + - $"\t{outputAssemblyPath}\n" + - $"binaries to be equal, since the input assembly has copy action."); + if (true != File.ReadAllBytes (inputAssemblyPath).SequenceEqual (File.ReadAllBytes (outputAssemblyPath))) + yield return $"Expected assemblies\n" + + $"\t{inputAssemblyPath}\n" + + $"\t{outputAssemblyPath}\n" + + $"binaries to be equal, since the input assembly has copy action."; } - private void VerifyCustomAttributeKept (ICustomAttributeProvider provider, string expectedAttributeTypeName) + private IEnumerable VerifyCustomAttributeKept (ICustomAttributeProvider provider, string expectedAttributeTypeName) { var match = provider.CustomAttributes.FirstOrDefault (attr => attr.AttributeType.FullName == expectedAttributeTypeName); if (match == null) - Assert.Fail ($"Expected `{provider}` to have an attribute of type `{expectedAttributeTypeName}`"); + yield return $"Expected `{provider}` to have an attribute of type `{expectedAttributeTypeName}`"; } - private void VerifyCustomAttributeRemoved (ICustomAttributeProvider provider, string expectedAttributeTypeName) + private IEnumerable VerifyCustomAttributeRemoved (ICustomAttributeProvider provider, string expectedAttributeTypeName) { var match = provider.CustomAttributes.FirstOrDefault (attr => attr.AttributeType.FullName == expectedAttributeTypeName); if (match != null) - Assert.Fail ($"Expected `{provider}` to no longer have an attribute of type `{expectedAttributeTypeName}`"); + yield return $"Expected `{provider}` to no longer have an attribute of type `{expectedAttributeTypeName}`"; } - private void VerifyRemovedInterfaceOnTypeInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) + private IEnumerable VerifyRemovedInterfaceOnTypeInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) { var originalType = GetOriginalTypeFromInAssemblyAttribute (inAssemblyAttribute); @@ -1560,18 +1713,18 @@ private void VerifyRemovedInterfaceOnTypeInAssembly (CustomAttribute inAssemblyA var originalInterface = GetOriginalTypeFromInAssemblyAttribute (interfaceAssemblyName, interfaceType); if (!originalType.HasInterfaces) - Assert.Fail ("Invalid assertion. Original type does not have any interfaces"); + yield return "Invalid assertion. Original type does not have any interfaces"; var originalInterfaceImpl = GetMatchingInterfaceImplementationOnType (originalType, originalInterface.FullName); if (originalInterfaceImpl == null) - Assert.Fail ($"Invalid assertion. Original type never had an interface of type `{originalInterface}`"); + yield return $"Invalid assertion. Original type never had an interface of type `{originalInterface}`"; var linkedInterfaceImpl = GetMatchingInterfaceImplementationOnType (linkedType, originalInterface.FullName); if (linkedInterfaceImpl != null) - Assert.Fail ($"Expected `{linkedType}` to no longer have an interface of type {originalInterface.FullName}"); + yield return $"Expected `{linkedType}` to no longer have an interface of type {originalInterface.FullName}"; } - private void VerifyKeptInterfaceOnTypeInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) + private IEnumerable VerifyKeptInterfaceOnTypeInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) { var originalType = GetOriginalTypeFromInAssemblyAttribute (inAssemblyAttribute); @@ -1580,18 +1733,18 @@ private void VerifyKeptInterfaceOnTypeInAssembly (CustomAttribute inAssemblyAttr var originalInterface = GetOriginalTypeFromInAssemblyAttribute (interfaceAssemblyName, interfaceType); if (!originalType.HasInterfaces) - Assert.Fail ("Invalid assertion. Original type does not have any interfaces"); + yield return "Invalid assertion. Original type does not have any interfaces"; var originalInterfaceImpl = GetMatchingInterfaceImplementationOnType (originalType, originalInterface.FullName); if (originalInterfaceImpl == null) - Assert.Fail ($"Invalid assertion. Original type never had an interface of type `{originalInterface}`"); + yield return $"Invalid assertion. Original type never had an interface of type `{originalInterface}`"; var linkedInterfaceImpl = GetMatchingInterfaceImplementationOnType (linkedType, originalInterface.FullName); if (linkedInterfaceImpl == null) - Assert.Fail ($"Expected `{linkedType}` to have interface of type {originalInterface.FullName}"); + yield return $"Expected `{linkedType}` to have interface of type {originalInterface.FullName}"; } - private void VerifyKeptBaseOnTypeInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) + private IEnumerable VerifyKeptBaseOnTypeInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) { var originalType = GetOriginalTypeFromInAssemblyAttribute (inAssemblyAttribute); @@ -1600,10 +1753,10 @@ private void VerifyKeptBaseOnTypeInAssembly (CustomAttribute inAssemblyAttribute var originalBase = GetOriginalTypeFromInAssemblyAttribute (baseAssemblyName, baseType); if (originalType.BaseType.Resolve () != originalBase) - Assert.Fail ("Invalid assertion. Original type's base does not match the expected base"); + yield return "Invalid assertion. Original type's base does not match the expected base"; - Assert.True (originalBase.FullName == linkedType.BaseType.FullName, - $"Incorrect base on `{linkedType.FullName}`. Expected `{originalBase.FullName}` but was `{linkedType.BaseType.FullName}`"); + if (originalBase.FullName != linkedType.BaseType.FullName) + yield return $"Incorrect base on `{linkedType.FullName}`. Expected `{originalBase.FullName}` but was `{linkedType.BaseType.FullName}`"; } private static InterfaceImplementation? GetMatchingInterfaceImplementationOnType (TypeDefinition type, string expectedInterfaceTypeName) @@ -1618,7 +1771,7 @@ private void VerifyKeptBaseOnTypeInAssembly (CustomAttribute inAssemblyAttribute }); } - private void VerifyRemovedMemberInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) + private IEnumerable VerifyRemovedMemberInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) { var originalType = GetOriginalTypeFromInAssemblyAttribute (inAssemblyAttribute); foreach (var memberNameAttr in (CustomAttributeArgument[]) inAssemblyAttribute.ConstructorArguments[2].Value) { @@ -1630,7 +1783,7 @@ private void VerifyRemovedMemberInAssembly (CustomAttribute inAssemblyAttribute, if (originalFieldMember != null) { var linkedField = linkedType.Fields.FirstOrDefault (m => m.Name == memberName); if (linkedField != null) - Assert.Fail ($"Field `{memberName}` on Type `{originalType}` should have been removed"); + yield return $"Field `{memberName}` on Type `{originalType}` should have been removed"; continue; } @@ -1639,7 +1792,7 @@ private void VerifyRemovedMemberInAssembly (CustomAttribute inAssemblyAttribute, if (originalPropertyMember != null) { var linkedProperty = linkedType.Properties.FirstOrDefault (m => m.Name == memberName); if (linkedProperty != null) - Assert.Fail ($"Property `{memberName}` on Type `{originalType}` should have been removed"); + yield return $"Property `{memberName}` on Type `{originalType}` should have been removed"; continue; } @@ -1648,20 +1801,21 @@ private void VerifyRemovedMemberInAssembly (CustomAttribute inAssemblyAttribute, if (originalMethodMember != null) { var linkedMethod = linkedType.Methods.FirstOrDefault (m => m.GetSignature () == memberName); if (linkedMethod != null) - Assert.Fail ($"Method `{memberName}` on Type `{originalType}` should have been removed"); + yield return $"Method `{memberName}` on Type `{originalType}` should have been removed"; continue; } - Assert.Fail ($"Invalid test assertion. No member named `{memberName}` exists on the original type `{originalType}`"); + yield return $"Invalid test assertion. No member named `{memberName}` exists on the original type `{originalType}`"; } } - private void VerifyKeptMemberInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) + private IEnumerable VerifyKeptMemberInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) { var originalType = GetOriginalTypeFromInAssemblyAttribute (inAssemblyAttribute); var memberNames = (CustomAttributeArgument[]) inAssemblyAttribute.ConstructorArguments[2].Value; - Assert.True (memberNames.Length > 0, "Invalid KeptMemberInAssemblyAttribute. Expected member names."); + if (!(memberNames.Length > 0)) + yield return "Invalid KeptMemberInAssemblyAttribute. Expected member names."; foreach (var memberNameAttr in memberNames) { string memberName = (string) memberNameAttr.Value; @@ -1677,7 +1831,7 @@ private void VerifyKeptMemberInAssembly (CustomAttribute inAssemblyAttribute, Ty if (TryVerifyKeptMemberInAssemblyAsMethod (memberName, originalType, linkedType)) continue; - Assert.Fail ($"Invalid test assertion. No member named `{memberName}` exists on the original type `{originalType}`"); + yield return $"Invalid test assertion. No member named `{memberName}` exists on the original type `{originalType}`"; } } @@ -1729,10 +1883,10 @@ protected virtual bool TryVerifyKeptMemberInAssemblyAsMethod (string memberName, return false; } - private void VerifyKeptReferencesInAssembly (CustomAttribute inAssemblyAttribute) + private IEnumerable VerifyKeptReferencesInAssembly (CustomAttribute inAssemblyAttribute) { #if false - var assembly = ResolveLinkedAssembly (inAssemblyAttribute.ConstructorArguments[0].Value.ToString ()!); + var assembly = ResolveLinkedAssembly (inAssemblyAttribute.ConstructorArguments[0].Value.ToString ()!); var expectedReferenceNames = ((CustomAttributeArgument[]) inAssemblyAttribute.ConstructorArguments[1].Value).Select (attr => (string) attr.Value).ToList (); for (int i = 0; i < expectedReferenceNames.Count; i++) if (expectedReferenceNames[i].EndsWith (".dll")) @@ -1740,53 +1894,61 @@ private void VerifyKeptReferencesInAssembly (CustomAttribute inAssemblyAttribute Assert.Equal (assembly.MainModule.AssemblyReferences.Select (asm => asm.Name), expectedReferenceNames); #endif + yield break; } - private void VerifyKeptResourceInAssembly (CustomAttribute inAssemblyAttribute) + private IEnumerable VerifyKeptResourceInAssembly (CustomAttribute inAssemblyAttribute) { #if false - var assembly = ResolveLinkedAssembly (inAssemblyAttribute.ConstructorArguments[0].Value.ToString ()!); + var assembly = ResolveLinkedAssembly (inAssemblyAttribute.ConstructorArguments[0].Value.ToString ()!); var resourceName = inAssemblyAttribute.ConstructorArguments[1].Value.ToString (); Assert.Contains (resourceName, assembly.MainModule.Resources.Select (r => r.Name)); #endif + yield break; } - private void VerifyRemovedResourceInAssembly (CustomAttribute inAssemblyAttribute) + private IEnumerable VerifyRemovedResourceInAssembly (CustomAttribute inAssemblyAttribute) { #if false - var assembly = ResolveLinkedAssembly (inAssemblyAttribute.ConstructorArguments[0].Value.ToString ()!); + var assembly = ResolveLinkedAssembly (inAssemblyAttribute.ConstructorArguments[0].Value.ToString ()!); var resourceName = inAssemblyAttribute.ConstructorArguments[1].Value.ToString (); Assert.DoesNotContain (resourceName, assembly.MainModule.Resources.Select (r => r.Name)); #endif + yield break; } - private void VerifyKeptAllTypesAndMembersInAssembly (string assemblyName, Dictionary linkedMembers) + private IEnumerable VerifyKeptAllTypesAndMembersInAssembly (string assemblyName, Dictionary linkedMembers) { var original = ResolveOriginalsAssembly (assemblyName); if (original == null) - Assert.Fail ($"Failed to resolve original assembly {assemblyName}"); + { + yield return $"Failed to resolve original assembly {assemblyName}"; + yield break; + } var originalTypes = original.AllDefinedTypes ().ToDictionary (t => new AssemblyQualifiedToken(t)); - var linkedTypes = linkedMembers.Where(t => t.Value.Entity is TypeDesc).ToDictionary(); + var linkedTypes = linkedMembers.Where(t => t.Value.Entity is TypeDesc).ToDictionary(); var missingInLinked = originalTypes.Keys.Except (linkedTypes.Keys); - Assert.False (missingInLinked.Any (), $"Expected all types to exist in the linked assembly {assemblyName}, but one or more were missing"); + if (missingInLinked.Any ()) + yield return $"Expected all types to exist in the linked assembly {assemblyName}, but one or more were missing"; foreach (var originalKvp in originalTypes) { var linkedType = linkedTypes[originalKvp.Key]; - TypeDesc linkedTypeDesc = (TypeDesc)linkedType.Entity; + TypeDesc linkedTypeDesc = (TypeDesc)linkedType.Entity; - // NativeAOT field trimming is very different (it basically doesn't trim fields, not in the same way trimmer does) + // NativeAOT field trimming is very different (it basically doesn't trim fields, not in the same way trimmer does) var originalMembers = originalKvp.Value.AllMembers ().Where(m => m is not FieldDefinition).Select (m => new AssemblyQualifiedToken(m)); - var linkedMembersOnType = linkedMembers.Where(t => GetOwningType(t.Value.Entity) == linkedTypeDesc).Select(t => t.Key); + var linkedMembersOnType = linkedMembers.Where(t => GetOwningType(t.Value.Entity) == linkedTypeDesc).Select(t => t.Key); var missingMembersInLinked = originalMembers.Except (linkedMembersOnType); - Assert.False (missingMembersInLinked.Any (), $"Expected all members of `{linkedTypeDesc.GetDisplayName()}`to exist in the linked assembly, but one or more were missing"); + if (missingMembersInLinked.Any ()) + yield return $"Expected all members of `{linkedTypeDesc.GetDisplayName()}`to exist in the linked assembly, but one or more were missing"; } } @@ -1823,9 +1985,9 @@ private static Dictionary> BuildOtherAssemblyCheck foreach (var attr in typeWithRemoveInAssembly.CustomAttributes.Where (IsTypeInOtherAssemblyAssertion)) { var assemblyName = (string) attr.ConstructorArguments[0].Value; - Tool? toolTarget = (Tool?)(int?)attr.GetPropertyValue("Tool"); - if (toolTarget is not null && !toolTarget.Value.HasFlag(Tool.NativeAot)) - continue; + Tool? toolTarget = (Tool?)(int?)attr.GetPropertyValue("Tool"); + if (toolTarget is not null && !toolTarget.Value.HasFlag(Tool.NativeAot)) + continue; if (!checks.TryGetValue (assemblyName, out List? checksForAssembly)) checks[assemblyName] = checksForAssembly = new List (); @@ -1839,11 +2001,11 @@ private static Dictionary> BuildOtherAssemblyCheck private Dictionary ResolveLinkedMembersForAssembly (string assemblyName) { - var cleanAssemblyName = assemblyName; - if (assemblyName.EndsWith(".exe") || assemblyName.EndsWith(".dll")) - cleanAssemblyName = System.IO.Path.GetFileNameWithoutExtension(assemblyName); + var cleanAssemblyName = assemblyName; + if (assemblyName.EndsWith(".exe") || assemblyName.EndsWith(".dll")) + cleanAssemblyName = System.IO.Path.GetFileNameWithoutExtension(assemblyName); - return this.linkedMembers.Where(e => GetModuleName(e.Value.Entity) == cleanAssemblyName).ToDictionary(); + return this.linkedMembers.Where(e => GetModuleName(e.Value.Entity) == cleanAssemblyName).ToDictionary(); } protected AssemblyDefinition ResolveOriginalsAssembly (string assemblyName) @@ -1859,7 +2021,7 @@ private static bool IsTypeInOtherAssemblyAssertion (CustomAttribute attr) return attr.AttributeType.Resolve ()?.DerivesFrom (nameof (BaseInAssemblyAttribute)) ?? false; } - private void VerifyExpectedInstructionSequenceOnMemberInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) + private IEnumerable VerifyExpectedInstructionSequenceOnMemberInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType) { var originalType = GetOriginalTypeFromInAssemblyAttribute (inAssemblyAttribute); var memberName = (string) inAssemblyAttribute.ConstructorArguments[2].Value; @@ -1870,14 +2032,13 @@ private void VerifyExpectedInstructionSequenceOnMemberInAssembly (CustomAttribut var srcValues = valueCollector (originalMethod!); var expected = ((CustomAttributeArgument[]) inAssemblyAttribute.ConstructorArguments[3].Value)?.Select (arg => arg.Value.ToString ()).ToArray (); - Assert.Equal ( - linkedValues, - expected); + if (!linkedValues.Equals(expected)) + yield return "Expected instruction sequence does not match"; - return; + yield break; } - Assert.Fail ($"Invalid test assertion. No method named `{memberName}` exists on the original type `{originalType}`"); + yield return $"Invalid test assertion. No method named `{memberName}` exists on the original type `{originalType}`"; } protected virtual void UnhandledOtherAssemblyAssertion (string expectedTypeName, CustomAttribute checkAttrInAssembly, TypeDefinition? linkedType) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs index f3c3bd49c3b11..49b7dd144c0e9 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/CoreBindingHelpers.cs @@ -724,15 +724,14 @@ private void EmitBindingLogicForEnumerableWithAdd(TypeRef elementTypeRef, string } // EmitBindCheckForSectionValue produce the following code: - // if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().GetEnumerator().MoveNext()) { continue; } + // if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().Any()) { continue; } // // If a section possesses a null or empty string value and lacks any children, we bind to the default value of the type. // In the case of a non-null or non-empty string value without any section children, binding cannot be performed at that moment, // and this section should be skipped. private void EmitBindCheckForSectionValue() { - // We utilize GetEnumerator().MoveNext() instead of employing Linq's Any() since there is no assurance that the System.Linq reference is included. - EmitStartBlock($"if (!string.IsNullOrEmpty({Expression.sectionValue}) && !{Identifier.section}.{Identifier.GetChildren}().{Identifier.GetEnumerator}().{Identifier.MoveNext}())"); + EmitStartBlock($"if (!string.IsNullOrEmpty({Expression.sectionValue}) && !{Identifier.section}.{Identifier.GetChildren}().{Identifier.Any}())"); _writer.WriteLine($@"continue;"); EmitEndBlock(); } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/Helpers.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/Helpers.cs index 7d3611f4ad2e8..3a0200d684307 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/Helpers.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Emitter/Helpers.cs @@ -107,7 +107,6 @@ private static class Identifier public const string Get = nameof(Get); public const string GetBinderOptions = nameof(GetBinderOptions); public const string GetChildren = nameof(GetChildren); - public const string GetEnumerator = nameof(GetEnumerator); public const string GetSection = nameof(GetSection); public const string GetValue = nameof(GetValue); public const string HasConfig = nameof(HasConfig); @@ -122,7 +121,6 @@ private static class Identifier public const string IOptionsChangeTokenSource = nameof(IOptionsChangeTokenSource); public const string IServiceCollection = nameof(IServiceCollection); public const string Length = nameof(Length); - public const string MoveNext = nameof(MoveNext); public const string Name = nameof(Name); public const string NumberStyles = nameof(NumberStyles); public const string Parse = nameof(Parse); diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/BindingHelperInfo.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/BindingHelperInfo.cs index 316b904de8897..dfa3c5f169048 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/BindingHelperInfo.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Specs/BindingHelperInfo.cs @@ -145,6 +145,11 @@ bool TryRegisterCore() } case CollectionSpec collectionSpec: { + if (_typeIndex.GetTypeSpec(collectionSpec.ElementTypeRef) is ComplexTypeSpec) + { + _namespaces.Add("System.Linq"); + } + return TryRegisterTransitiveTypesForMethodGen(collectionSpec.ElementTypeRef) && TryRegisterTypeForBindCoreGen(collectionSpec); } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/EmptyConfigType.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/EmptyConfigType.generated.txt index d435dc06d606d..027b41bb5e13d 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/EmptyConfigType.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/EmptyConfigType.generated.txt @@ -28,6 +28,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Runtime.CompilerServices; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T.generated.txt index 43a67cbba507e..a3f236c191046 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T.generated.txt @@ -30,6 +30,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Runtime.CompilerServices; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] @@ -119,7 +120,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().GetEnumerator().MoveNext()) + if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().Any()) { continue; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_BinderOptions.generated.txt index 0d3f585b1c0c9..b7207282c9490 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_BinderOptions.generated.txt @@ -30,6 +30,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Runtime.CompilerServices; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] @@ -119,7 +120,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().GetEnumerator().MoveNext()) + if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().Any()) { continue; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name.generated.txt index 2ed94a259710f..3238545870fac 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name.generated.txt @@ -30,6 +30,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Runtime.CompilerServices; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] @@ -119,7 +120,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().GetEnumerator().MoveNext()) + if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().Any()) { continue; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name_BinderOptions.generated.txt index 73d44acf71db8..634a091f2755e 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/net462/ServiceCollection/Configure_T_name_BinderOptions.generated.txt @@ -30,6 +30,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Runtime.CompilerServices; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] @@ -113,7 +114,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().GetEnumerator().MoveNext()) + if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().Any()) { continue; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/EmptyConfigType.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/EmptyConfigType.generated.txt index 026724498ac82..3783be58dc161 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/EmptyConfigType.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/EmptyConfigType.generated.txt @@ -28,6 +28,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Runtime.CompilerServices; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T.generated.txt index 0bf082a43c6fb..b26d7ebc0bc32 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T.generated.txt @@ -30,6 +30,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Runtime.CompilerServices; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] @@ -113,7 +114,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().GetEnumerator().MoveNext()) + if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().Any()) { continue; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_BinderOptions.generated.txt index b62b1bd9f5f66..89d3a8714e2fc 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_BinderOptions.generated.txt @@ -30,6 +30,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Runtime.CompilerServices; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] @@ -113,7 +114,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().GetEnumerator().MoveNext()) + if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().Any()) { continue; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name.generated.txt index c7d2a1d1e271b..eb23ee05417bb 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name.generated.txt @@ -30,6 +30,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Runtime.CompilerServices; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] @@ -113,7 +114,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().GetEnumerator().MoveNext()) + if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().Any()) { continue; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name_BinderOptions.generated.txt b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name_BinderOptions.generated.txt index 2d03fa0229752..d2a398bbc9042 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name_BinderOptions.generated.txt +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/Baselines/netcoreapp/ServiceCollection/Configure_T_name_BinderOptions.generated.txt @@ -30,6 +30,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration using System.CodeDom.Compiler; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Runtime.CompilerServices; [GeneratedCode("Microsoft.Extensions.Configuration.Binder.SourceGeneration", "42.42.42.42")] @@ -107,7 +108,7 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration { foreach (IConfigurationSection section in configuration.GetChildren()) { - if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().GetEnumerator().MoveNext()) + if (!string.IsNullOrEmpty(section.Value) && !section.GetChildren().Any()) { continue; } 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 381e5f25011e8..ea2b365760085 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 @@ -14722,7 +14722,7 @@ private static Vector512 ElementWiseSelect(Vector512 mask, Vector512 /// 1 / (1 + T.Exp(-x)) internal readonly struct SigmoidOperator : IUnaryOperator where T : IExponentialFunctions { - public static bool Vectorizable => typeof(T) == typeof(float); + public static bool Vectorizable => ExpOperator.Vectorizable; public static T Invoke(T x) => T.One / (T.One + T.Exp(-x)); public static Vector128 Invoke(Vector128 x) => Vector128.Create(T.One) / (Vector128.Create(T.One) + ExpOperator.Invoke(-x)); public static Vector256 Invoke(Vector256 x) => Vector256.Create(T.One) / (Vector256.Create(T.One) + ExpOperator.Invoke(-x)); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj index 6514d08c8f284..6bcf41fe129bd 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj @@ -16,6 +16,8 @@ true true + + false