diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 4a3c73c10190f2..e188a4fe12a6a0 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/icu - 42787a07ecba682549f520293bb5a6941bdccb50 + c8db7547bc0a021ffd262b3449f5b0343894b32b https://github.com/dotnet/msquic @@ -68,14 +68,14 @@ 7e4af02521473d89d6144b3da58fef253e498974 - + https://github.com/dotnet/emsdk - d3583522209829d1ed0440662ba136c7b7700b16 + 99ea0c06b84d3084d090da537080dd35d2a193cf - + https://github.com/dotnet/emsdk - d3583522209829d1ed0440662ba136c7b7700b16 + 99ea0c06b84d3084d090da537080dd35d2a193cf @@ -92,87 +92,87 @@ - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b https://github.com/dotnet/runtime-assets @@ -332,9 +332,9 @@ https://github.com/dotnet/xharness 65d0584b517952962b7a79195b5d7606b52fcbfe - + https://github.com/dotnet/arcade - f20056daa31b4a08f2ce379cfe4610042c3bc26c + 731d793be2d0a66bafc96b1a79dc96b4d1f0301b https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -360,17 +360,17 @@ https://github.com/dotnet/runtime-assets 0cab6ca16f49b666163d4e1c0e3c080faf5a4e05 - + https://github.com/dotnet/roslyn - 19b5e961ecb97b008106f1b646c077e0bffde4a7 + cada394f99c521861c39e2a5334678e6aba1ac62 - + https://github.com/dotnet/roslyn - 19b5e961ecb97b008106f1b646c077e0bffde4a7 + cada394f99c521861c39e2a5334678e6aba1ac62 - + https://github.com/dotnet/roslyn - 19b5e961ecb97b008106f1b646c077e0bffde4a7 + cada394f99c521861c39e2a5334678e6aba1ac62 https://github.com/dotnet/roslyn-analyzers @@ -381,9 +381,9 @@ 43709af7570da7140fb3e9a5237f55ffb24677e7 - + https://github.com/dotnet/roslyn - 19b5e961ecb97b008106f1b646c077e0bffde4a7 + cada394f99c521861c39e2a5334678e6aba1ac62 diff --git a/eng/Versions.props b/eng/Versions.props index 75599d7c272da7..526206c9b3c20a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -42,9 +42,9 @@ Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure they do not break the local dev experience. --> - 4.12.0-1.24355.3 - 4.12.0-1.24355.3 - 4.12.0-1.24355.3 + 4.12.0-1.24362.11 + 4.12.0-1.24362.11 + 4.12.0-1.24362.11 9.0.100-preview.7.24358.3 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 - 2.8.1-beta.24360.1 - 9.0.0-beta.24360.1 - 2.8.1-beta.24360.1 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 - 9.0.0-beta.24360.1 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 + 2.8.1-beta.24360.4 + 9.0.0-beta.24360.4 + 2.8.1-beta.24360.4 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 + 9.0.0-beta.24360.4 1.4.0 @@ -216,7 +216,7 @@ 9.0.0-preview.7.24357.2 - 9.0.0-preview.7.24358.1 + 9.0.0-preview.7.24365.2 2.3.5 9.0.0-alpha.1.24167.3 @@ -239,7 +239,7 @@ Note: when the name is updated, make sure to update dependency name in eng/pipelines/common/xplat-setup.yml like - DarcDependenciesChanged.Microsoft_NET_Workload_Emscripten_Current_Manifest-9_0_100_Transport --> - 9.0.0-preview.7.24352.2 + 9.0.0-preview.7.24365.1 $(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportVersion) 9.0.0-preview.7.24352.2 @@ -262,6 +262,6 @@ 9.0.0-alpha.1.24175.1 $(MicrosoftNETRuntimeEmscriptenVersion) $(runtimewinx64MicrosoftNETCoreRuntimeWasmNodeTransportPackageVersion) - 3.1.34 + 3.1.56 diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index eb1a908046483f..6d99d1263c0be6 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -72,7 +72,7 @@ __AlpinePackages+=" krb5-dev" __AlpinePackages+=" openssl-dev" __AlpinePackages+=" zlib-dev" -__FreeBSDBase="13.2-RELEASE" +__FreeBSDBase="13.3-RELEASE" __FreeBSDPkg="1.17.0" __FreeBSDABI="13" __FreeBSDPackages="libunwind" diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index fce021e7b99702..db8aee3799b440 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -215,9 +215,9 @@ $(LibrariesNativeArtifactsPath)segmentation-rules.json;" IsNative="true" /> - full compacting")); - last_gc_before_oom = TRUE; + if (!try_get_new_free_region()) + { + dprintf (GTC_LOG, ("can't get an empty region -> full compacting")); + last_gc_before_oom = TRUE; + } } #endif //USE_REGIONS diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 2e5b463c63d8c8..6587421dde380e 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -3004,10 +3004,10 @@ GenTree* Compiler::optVNBasedFoldConstExpr(BasicBlock* block, GenTree* parent, G { simdmask_t value = vnStore->ConstantValue(vnCns); - GenTreeVecCon* vecCon = gtNewVconNode(tree->TypeGet()); - memcpy(&vecCon->gtSimdVal, &value, sizeof(simdmask_t)); + GenTreeMskCon* mskCon = gtNewMskConNode(tree->TypeGet()); + memcpy(&mskCon->gtSimdMaskVal, &value, sizeof(simdmask_t)); - conValTree = vecCon; + conValTree = mskCon; break; } break; @@ -3136,7 +3136,7 @@ bool Compiler::optIsProfitableToSubstitute(GenTree* dest, BasicBlock* destBlock, } #endif // FEATURE_HW_INTRINSICS } - else if (!value->IsCnsFltOrDbl()) + else if (!value->IsCnsFltOrDbl() && !value->IsCnsMsk()) { return true; } diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index ea793e4998bbd0..7b0c517aac9a15 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -782,6 +782,7 @@ class CodeGen final : public CodeGenInterface void genSetRegToConst(regNumber targetReg, var_types targetType, GenTree* tree); #if defined(FEATURE_SIMD) void genSetRegToConst(regNumber targetReg, var_types targetType, simd_t* val); + void genSetRegToConst(regNumber targetReg, var_types targetType, simdmask_t* val); #endif void genCodeForTreeNode(GenTree* treeNode); void genCodeForBinary(GenTreeOp* treeNode); diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index fd03222c395c00..81b82992385480 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -304,11 +304,6 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre } break; - case GT_CNS_VEC: - { - unreached(); - } - default: unreached(); } diff --git a/src/coreclr/jit/codegenarm64test.cpp b/src/coreclr/jit/codegenarm64test.cpp index 0406bc4a6e19a2..e136de5a075953 100644 --- a/src/coreclr/jit/codegenarm64test.cpp +++ b/src/coreclr/jit/codegenarm64test.cpp @@ -6096,6 +6096,7 @@ void CodeGen::genArm64EmitterUnitTestsSve() theEmitter->emitIns_R_PATTERN_I(INS_sve_uqincw, EA_SCALABLE, REG_V11, SVE_PATTERN_ALL, 16, INS_OPTS_SCALABLE_S); // UQINCW .S{, {, MUL #}} +#ifdef ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED // IF_SVE_BQ_2A theEmitter->emitIns_R_R_I(INS_sve_ext, EA_SCALABLE, REG_V0, REG_V1, 0, INS_OPTS_SCALABLE_B, INS_SCALABLE_OPTS_WITH_VECTOR_PAIR); // EXT .B, {.B, .B }, # @@ -6105,6 +6106,7 @@ void CodeGen::genArm64EmitterUnitTestsSve() INS_SCALABLE_OPTS_WITH_VECTOR_PAIR); // EXT .B, {.B, .B }, # theEmitter->emitIns_R_R_I(INS_sve_ext, EA_SCALABLE, REG_V6, REG_FP_LAST, 255, INS_OPTS_SCALABLE_B, INS_SCALABLE_OPTS_WITH_VECTOR_PAIR); // EXT .B, {.B, .B }, # +#endif // ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED // IF_SVE_BQ_2B theEmitter->emitIns_R_R_I(INS_sve_ext, EA_SCALABLE, REG_V0, REG_V1, 0, @@ -8391,23 +8393,23 @@ void CodeGen::genArm64EmitterUnitTestsSve() INS_OPTS_SCALABLE_D); // ST1B {.D }, , [, .D] // IF_SVE_GP_3A - theEmitter->emitIns_R_R_R_I(INS_sve_fcadd, EA_SCALABLE, REG_V0, REG_P1, REG_V2, 90, + theEmitter->emitIns_R_R_R_I(INS_sve_fcadd, EA_SCALABLE, REG_V0, REG_P1, REG_V2, 0, INS_OPTS_SCALABLE_H); // FCADD ., /M, ., ., - theEmitter->emitIns_R_R_R_I(INS_sve_fcadd, EA_SCALABLE, REG_V0, REG_P1, REG_V2, 270, + theEmitter->emitIns_R_R_R_I(INS_sve_fcadd, EA_SCALABLE, REG_V0, REG_P1, REG_V2, 1, INS_OPTS_SCALABLE_H); // FCADD ., /M, ., ., - theEmitter->emitIns_R_R_R_I(INS_sve_fcadd, EA_SCALABLE, REG_V0, REG_P1, REG_V2, 270, + theEmitter->emitIns_R_R_R_I(INS_sve_fcadd, EA_SCALABLE, REG_V0, REG_P1, REG_V2, 1, INS_OPTS_SCALABLE_S); // FCADD ., /M, ., ., - theEmitter->emitIns_R_R_R_I(INS_sve_fcadd, EA_SCALABLE, REG_V0, REG_P1, REG_V2, 270, + theEmitter->emitIns_R_R_R_I(INS_sve_fcadd, EA_SCALABLE, REG_V0, REG_P1, REG_V2, 1, INS_OPTS_SCALABLE_D); // FCADD ., /M, ., ., // IF_SVE_GT_4A theEmitter->emitIns_R_R_R_R_I(INS_sve_fcmla, EA_SCALABLE, REG_V2, REG_P1, REG_V3, REG_V4, 0, INS_OPTS_SCALABLE_H); // FCMLA ., /M, ., ., - theEmitter->emitIns_R_R_R_R_I(INS_sve_fcmla, EA_SCALABLE, REG_V0, REG_P2, REG_V1, REG_V5, 90, + theEmitter->emitIns_R_R_R_R_I(INS_sve_fcmla, EA_SCALABLE, REG_V0, REG_P2, REG_V1, REG_V5, 1, INS_OPTS_SCALABLE_S); // FCMLA ., /M, ., ., - theEmitter->emitIns_R_R_R_R_I(INS_sve_fcmla, EA_SCALABLE, REG_V2, REG_P3, REG_V0, REG_V6, 180, + theEmitter->emitIns_R_R_R_R_I(INS_sve_fcmla, EA_SCALABLE, REG_V2, REG_P3, REG_V0, REG_V6, 2, INS_OPTS_SCALABLE_D); // FCMLA ., /M, ., ., - theEmitter->emitIns_R_R_R_R_I(INS_sve_fcmla, EA_SCALABLE, REG_V2, REG_P3, REG_V0, REG_V6, 270, + theEmitter->emitIns_R_R_R_R_I(INS_sve_fcmla, EA_SCALABLE, REG_V2, REG_P3, REG_V0, REG_V6, 3, INS_OPTS_SCALABLE_D); // FCMLA ., /M, ., ., // IF_SVE_GI_4A diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index d89892b8898c59..16cad5618112b7 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -187,6 +187,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) case GT_CNS_INT: case GT_CNS_DBL: case GT_CNS_VEC: + case GT_CNS_MSK: genSetRegToConst(targetReg, targetType, treeNode); genProduceReg(treeNode); break; diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 3a05ac9799354b..dad1ba0b929b89 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -8327,7 +8327,7 @@ void CodeGen::genCodeForReuseVal(GenTree* treeNode) assert(treeNode->IsReuseRegVal()); // For now, this is only used for constant nodes. - assert(treeNode->OperIs(GT_CNS_INT, GT_CNS_DBL, GT_CNS_VEC)); + assert(treeNode->OperIs(GT_CNS_INT, GT_CNS_DBL, GT_CNS_VEC, GT_CNS_MSK)); JITDUMP(" TreeNode is marked ReuseReg\n"); if (treeNode->IsIntegralConst(0) && GetEmitter()->emitCurIGnonEmpty()) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 4484343a1e0580..dc9dd7c8a249bf 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -487,6 +487,7 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, simd_t } break; } + case TYP_SIMD12: { simd12_t val12 = *(simd12_t*)val; @@ -516,6 +517,7 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, simd_t } break; } + case TYP_SIMD16: { simd16_t val16 = *(simd16_t*)val; @@ -543,6 +545,7 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, simd_t } break; } + case TYP_SIMD32: { simd32_t val32 = *(simd32_t*)val; @@ -570,6 +573,7 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, simd_t } break; } + case TYP_SIMD64: { simd64_t val64 = *(simd64_t*)val; @@ -595,18 +599,49 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, simd_t } break; } + default: { unreached(); } } } + +//---------------------------------------------------------------------------------- +// genSetRegToConst: generate code to set target SIMD register to a given constant value +// +// Arguments: +// targetReg - target SIMD register +// targetType - target's type +// simdmask_t - constant data (its width depends on type) +// +void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, simdmask_t* val) +{ + assert(varTypeIsMask(targetType)); + + emitter* emit = GetEmitter(); + emitAttr attr = emitTypeSize(targetType); + + if (val->IsAllBitsSet()) + { + emit->emitIns_SIMD_R_R_R(INS_kxnorq, EA_8BYTE, targetReg, targetReg, targetReg, INS_OPTS_NONE); + } + else if (val->IsZero()) + { + emit->emitIns_SIMD_R_R_R(INS_kxorq, EA_8BYTE, targetReg, targetReg, targetReg, INS_OPTS_NONE); + } + else + { + CORINFO_FIELD_HANDLE hnd = emit->emitSimdMaskConst(*val); + emit->emitIns_R_C(ins_Load(targetType), attr, targetReg, hnd, 0); + } +} #endif // FEATURE_SIMD /*********************************************************************************** * * Generate code to set a register 'targetReg' of type 'targetType' to the constant - * specified by the constant (GT_CNS_INT, GT_CNS_DBL, or GT_CNS_VEC) in 'tree'. This + * specified by the constant (GT_CNS_INT, GT_CNS_DBL, GT_CNS_VEC, or GT_CNS_MSK) in 'tree'. This * does not call genProduceReg() on the target register. */ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTree* tree) @@ -700,6 +735,17 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre break; } + case GT_CNS_MSK: + { +#if defined(FEATURE_MASKED_HW_INTRINSICS) + GenTreeMskCon* mskCon = tree->AsMskCon(); + genSetRegToConst(mskCon->GetRegNum(), targetType, &mskCon->gtSimdMaskVal); +#else + unreached(); +#endif + break; + } + default: unreached(); } @@ -1860,11 +1906,8 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) FALLTHROUGH; case GT_CNS_DBL: - genSetRegToConst(targetReg, targetType, treeNode); - genProduceReg(treeNode); - break; - case GT_CNS_VEC: + case GT_CNS_MSK: genSetRegToConst(targetReg, targetType, treeNode); genProduceReg(treeNode); break; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index efd73852b1a1c1..e1db3ad7421ea6 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3055,6 +3055,8 @@ class Compiler GenTreeVecCon* gtNewVconNode(var_types type, void* data); + GenTreeMskCon* gtNewMskConNode(var_types type); + GenTree* gtNewAllBitsSetConNode(var_types type); GenTree* gtNewZeroConNode(var_types type); @@ -3232,7 +3234,9 @@ class Compiler CorInfoType simdBaseJitType, unsigned simdSize); +#if defined(FEATURE_MASKED_HW_INTRINSICS) GenTree* gtNewSimdCvtMaskToVectorNode(var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize); +#endif // FEATURE_MASKED_HW_INTRINSICS GenTree* gtNewSimdCvtNode(var_types type, GenTree* op1, @@ -3246,7 +3250,9 @@ class Compiler CorInfoType simdSourceBaseJitType, unsigned simdSize); +#if defined(FEATURE_MASKED_HW_INTRINSICS) GenTree* gtNewSimdCvtVectorToMaskNode(var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize); +#endif // FEATURE_MASKED_HW_INTRINSICS GenTree* gtNewSimdCreateBroadcastNode( var_types type, GenTree* op1, CorInfoType simdBaseJitType, unsigned simdSize); @@ -3648,10 +3654,10 @@ class Compiler void gtSetStmtInfo(Statement* stmt); // Returns "true" iff "node" has any of the side effects in "flags". - bool gtNodeHasSideEffects(GenTree* node, GenTreeFlags flags); + bool gtNodeHasSideEffects(GenTree* node, GenTreeFlags flags, bool ignoreCctors = false); // Returns "true" iff "tree" or its (transitive) children have any of the side effects in "flags". - bool gtTreeHasSideEffects(GenTree* tree, GenTreeFlags flags); + bool gtTreeHasSideEffects(GenTree* tree, GenTreeFlags flags, bool ignoreCctors = false); void gtExtractSideEffList(GenTree* expr, GenTree** pList, @@ -4541,6 +4547,8 @@ class Compiler GenTree* impDuplicateWithProfiledArg(GenTreeCall* call, IL_OFFSET ilOffset); + GenTree* impThrowIfNull(GenTreeCall* call); + #ifdef DEBUG var_types impImportJitTestLabelMark(int numArgs); #endif // DEBUG @@ -7249,6 +7257,7 @@ class Compiler void optValnumCSE_DataFlow(); void optValnumCSE_Availability(); void optValnumCSE_Heuristic(CSE_HeuristicCommon* heuristic); + GenTree* optExtractSideEffectsForCSE(GenTree* tree); bool optDoCSE; // True when we have found a duplicate CSE tree bool optValnumCSE_phase; // True when we are executing the optOptimizeValnumCSEs() phase @@ -11672,6 +11681,7 @@ class GenTreeVisitor case GT_CNS_DBL: case GT_CNS_STR: case GT_CNS_VEC: + case GT_CNS_MSK: case GT_MEMORYBARRIER: case GT_JMP: case GT_JCC: diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 69e95d60d58ae2..1d409190b12470 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4358,6 +4358,7 @@ void GenTree::VisitOperands(TVisitor visitor) case GT_CNS_DBL: case GT_CNS_STR: case GT_CNS_VEC: + case GT_CNS_MSK: case GT_MEMORYBARRIER: case GT_JMP: case GT_JCC: diff --git a/src/coreclr/jit/emitarm64sve.cpp b/src/coreclr/jit/emitarm64sve.cpp index 589cc1b29bae6a..6fb6723eb63811 100644 --- a/src/coreclr/jit/emitarm64sve.cpp +++ b/src/coreclr/jit/emitarm64sve.cpp @@ -4410,7 +4410,6 @@ void emitter::emitInsSve_R_R_R(instruction ins, /***************************************************************************** * * Add a SVE instruction referencing three registers and a constant. - * Do not call this directly. Use 'emitIns_R_R_R_I' instead. */ void emitter::emitInsSve_R_R_R_I(instruction ins, @@ -5577,7 +5576,7 @@ void emitter::emitInsSve_R_R_R_I(instruction ins, assert(isLowPredicateRegister(reg2)); assert(isVectorRegister(reg3)); assert(isScalableVectorSize(size)); - imm = emitEncodeRotationImm90_or_270(imm); + assert(emitIsValidEncodedRotationImm90_or_270(imm)); fmt = IF_SVE_GP_3A; break; @@ -5860,7 +5859,6 @@ void emitter::emitInsSve_R_R_R_I_I(instruction ins, /***************************************************************************** * * Add a SVE instruction referencing four registers. - * Do not call this directly. Use 'emitIns_R_R_R_R' instead. */ void emitter::emitInsSve_R_R_R_R(instruction ins, @@ -6991,7 +6989,7 @@ void emitter::emitInsSve_R_R_R_R_I(instruction ins, assert(isVectorRegister(reg3)); assert(isVectorRegister(reg4)); assert(isScalableVectorSize(size)); - imm = emitEncodeRotationImm0_to_270(imm); + assert(emitIsValidEncodedRotationImm0_to_270(imm)); fmt = IF_SVE_GT_4A; break; @@ -9798,7 +9796,7 @@ void emitter::emitIns_PRFOP_R_R_I(instruction ins, /*static*/ bool emitter::emitIsValidEncodedRotationImm90_or_270(ssize_t imm) { - return (imm == 0) || (imm == 1); + return isValidUimm<1>(imm); } /************************************************************************ @@ -9867,7 +9865,7 @@ void emitter::emitIns_PRFOP_R_R_I(instruction ins, /*static*/ bool emitter::emitIsValidEncodedRotationImm0_to_270(ssize_t imm) { - return (imm >= 0) && (imm <= 3); + return isValidUimm<2>(imm); } /***************************************************************************** diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index d47c42a0ade52f..f7da7729d23752 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2776,6 +2776,15 @@ bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK) break; } + case GT_CNS_MSK: + { + if (GenTreeMskCon::Equals(op1->AsMskCon(), op2->AsMskCon())) + { + return true; + } + break; + } + default: break; } @@ -3309,15 +3318,6 @@ unsigned Compiler::gtHashValue(GenTree* tree) switch (vecCon->TypeGet()) { #if defined(FEATURE_SIMD) -#if defined(FEATURE_MASKED_HW_INTRINSICS) - case TYP_MASK: - { - add = genTreeHashAdd(ulo32(add), vecCon->gtSimdVal.u32[1]); - add = genTreeHashAdd(ulo32(add), vecCon->gtSimdVal.u32[0]); - break; - } -#endif // FEATURE_MASKED_HW_INTRINSICS - #if defined(TARGET_XARCH) case TYP_SIMD64: { @@ -3370,6 +3370,20 @@ unsigned Compiler::gtHashValue(GenTree* tree) break; } + case GT_CNS_MSK: + { + GenTreeMskCon* mskCon = tree->AsMskCon(); + add = 0; + +#if defined(FEATURE_MASKED_HW_INTRINSICS) + add = genTreeHashAdd(ulo32(add), mskCon->gtSimdMaskVal.u32[1]); + add = genTreeHashAdd(ulo32(add), mskCon->gtSimdMaskVal.u32[0]); +#else + unreached(); +#endif // FEATURE_MASKED_HW_INTRINSICS + break; + } + case GT_JMP: add = tree->AsVal()->gtVal1; break; @@ -5266,6 +5280,23 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) break; } + case GT_CNS_MSK: + { + level = 0; + + if (tree->AsMskCon()->IsAllBitsSet() || tree->AsMskCon()->IsZero()) + { + costEx = 1; + costSz = 2; + } + else + { + costEx = IND_COST_EX; + costSz = 2; + } + break; + } + case GT_LCL_VAR: level = 1; gtGetLclVarNodeCost(tree->AsLclVar(), &costEx, &costSz, gtIsLikelyRegVar(tree)); @@ -6492,6 +6523,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) case GT_CNS_DBL: case GT_CNS_STR: case GT_CNS_VEC: + case GT_CNS_MSK: case GT_MEMORYBARRIER: case GT_JMP: case GT_JCC: @@ -7722,6 +7754,12 @@ GenTreeVecCon* Compiler::gtNewVconNode(var_types type, void* data) return vecCon; } +GenTreeMskCon* Compiler::gtNewMskConNode(var_types type) +{ + GenTreeMskCon* mskCon = new (this, GT_CNS_MSK) GenTreeMskCon(type); + return mskCon; +} + GenTree* Compiler::gtNewAllBitsSetConNode(var_types type) { #ifdef FEATURE_SIMD @@ -8393,7 +8431,7 @@ void Compiler::gtInitializeStoreNode(GenTree* store, GenTree* value) } #else // TARGET_X86 // TODO-Cleanup: merge into the all-arch. - if (varTypeIsSIMD(value) && value->OperIs(GT_HWINTRINSIC, GT_CNS_VEC)) + if (varTypeIsSIMD(value) && value->OperIs(GT_HWINTRINSIC, GT_CNS_VEC, GT_CNS_MSK)) { SetOpLclRelatedToSIMDIntrinsic(store); } @@ -9033,6 +9071,18 @@ GenTree* Compiler::gtClone(GenTree* tree, bool complexOK) break; } + case GT_CNS_MSK: + { +#if defined(FEATURE_MASKED_HW_INTRINSICS) + GenTreeMskCon* mskCon = gtNewMskConNode(tree->TypeGet()); + mskCon->gtSimdMaskVal = tree->AsMskCon()->gtSimdMaskVal; + copy = mskCon; +#else + unreached(); +#endif // FEATURE_MASKED_HW_INTRINSICS + break; + } + case GT_FTN_ADDR: { copy = new (this, tree->OperGet()) GenTreeFptrVal(tree->TypeGet(), tree->AsFptrVal()->gtFptrMethod); @@ -9212,6 +9262,18 @@ GenTree* Compiler::gtCloneExpr(GenTree* tree) goto DONE; } + case GT_CNS_MSK: + { +#if defined(FEATURE_MASKED_HW_INTRINSICS) + GenTreeMskCon* mskCon = gtNewMskConNode(tree->TypeGet()); + mskCon->gtSimdMaskVal = tree->AsMskCon()->gtSimdMaskVal; + copy = mskCon; + goto DONE; +#else + unreached(); +#endif // FEATURE_MASKED_HW_INTRINSICS + } + case GT_LCL_VAR: // Remember that the local node has been cloned. The flag will be set on 'copy' as well. @@ -9999,6 +10061,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_CNS_DBL: case GT_CNS_STR: case GT_CNS_VEC: + case GT_CNS_MSK: case GT_MEMORYBARRIER: case GT_JMP: case GT_JCC: @@ -12025,14 +12088,6 @@ void Compiler::gtDispConst(GenTree* tree) #endif // TARGET_XARCH -#if defined(FEATURE_MASKED_HW_INTRINSICS) - case TYP_MASK: - { - printf("<0x%08x, 0x%08x>", vecCon->gtSimdVal.u32[0], vecCon->gtSimdVal.u32[1]); - break; - } -#endif // FEATURE_MASKED_HW_INTRINSICS - default: { unreached(); @@ -12041,6 +12096,15 @@ void Compiler::gtDispConst(GenTree* tree) break; } + +#if defined(FEATURE_MASKED_HW_INTRINSICS) + case GT_CNS_MSK: + { + GenTreeMskCon* mskCon = tree->AsMskCon(); + printf("<0x%08x, 0x%08x>", mskCon->gtSimdMaskVal.u32[0], mskCon->gtSimdMaskVal.u32[1]); + break; + } +#endif // FEATURE_MASKED_HW_INTRINSICS #endif // FEATURE_SIMD default: @@ -16609,7 +16673,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, * It may return false even if the node has GTF_SIDE_EFFECT (because of its children). */ -bool Compiler::gtNodeHasSideEffects(GenTree* tree, GenTreeFlags flags) +bool Compiler::gtNodeHasSideEffects(GenTree* tree, GenTreeFlags flags, bool ignoreCctors) { if (flags & GTF_ASG) { @@ -16636,7 +16700,6 @@ bool Compiler::gtNodeHasSideEffects(GenTree* tree, GenTreeFlags flags) { GenTreeCall* const call = potentialCall->AsCall(); const bool ignoreExceptions = (flags & GTF_EXCEPT) == 0; - const bool ignoreCctors = (flags & GTF_IS_IN_CSE) != 0; // We can CSE helpers that run cctors. if (!call->HasSideEffects(this, ignoreExceptions, ignoreCctors)) { // If this call is otherwise side effect free, check its arguments. @@ -16644,12 +16707,13 @@ bool Compiler::gtNodeHasSideEffects(GenTree* tree, GenTreeFlags flags) { // I'm a little worried that args that assign to temps that are late args will look like // side effects...but better to be conservative for now. - if ((arg.GetEarlyNode() != nullptr) && gtTreeHasSideEffects(arg.GetEarlyNode(), flags)) + if ((arg.GetEarlyNode() != nullptr) && + gtTreeHasSideEffects(arg.GetEarlyNode(), flags, ignoreCctors)) { return true; } - if ((arg.GetLateNode() != nullptr) && gtTreeHasSideEffects(arg.GetLateNode(), flags)) + if ((arg.GetLateNode() != nullptr) && gtTreeHasSideEffects(arg.GetLateNode(), flags, ignoreCctors)) { return true; } @@ -16686,7 +16750,7 @@ bool Compiler::gtNodeHasSideEffects(GenTree* tree, GenTreeFlags flags) * Returns true if the expr tree has any side effects. */ -bool Compiler::gtTreeHasSideEffects(GenTree* tree, GenTreeFlags flags /* = GTF_SIDE_EFFECT*/) +bool Compiler::gtTreeHasSideEffects(GenTree* tree, GenTreeFlags flags /* = GTF_SIDE_EFFECT*/, bool ignoreCctors) { // These are the side effect flags that we care about for this tree GenTreeFlags sideEffectFlags = tree->gtFlags & flags; @@ -16709,22 +16773,22 @@ bool Compiler::gtTreeHasSideEffects(GenTree* tree, GenTreeFlags flags /* = GTF_S // If this node is a helper call we may not care about the side-effects. // Note that gtNodeHasSideEffects checks the side effects of the helper itself // as well as the side effects of its arguments. - return gtNodeHasSideEffects(tree, flags); + return gtNodeHasSideEffects(tree, flags, ignoreCctors); } } else if (tree->OperGet() == GT_INTRINSIC) { - if (gtNodeHasSideEffects(tree, flags)) + if (gtNodeHasSideEffects(tree, flags, ignoreCctors)) { return true; } - if (gtNodeHasSideEffects(tree->AsOp()->gtOp1, flags)) + if (gtNodeHasSideEffects(tree->AsOp()->gtOp1, flags, ignoreCctors)) { return true; } - if ((tree->AsOp()->gtOp2 != nullptr) && gtNodeHasSideEffects(tree->AsOp()->gtOp2, flags)) + if ((tree->AsOp()->gtOp2 != nullptr) && gtNodeHasSideEffects(tree->AsOp()->gtOp2, flags, ignoreCctors)) { return true; } @@ -17110,81 +17174,61 @@ void Compiler::gtExtractSideEffList(GenTree* expr, { GenTree* node = *use; - bool treeHasSideEffects = m_compiler->gtTreeHasSideEffects(node, m_flags); + if (!m_compiler->gtTreeHasSideEffects(node, m_flags)) + { + return Compiler::WALK_SKIP_SUBTREES; + } - if (treeHasSideEffects) + if (m_compiler->gtNodeHasSideEffects(node, m_flags)) { - if (m_compiler->gtNodeHasSideEffects(node, m_flags)) + if (node->OperIsBlk() && !node->OperIsStoreBlk()) { - if (node->OperIsBlk() && !node->OperIsStoreBlk()) - { - JITDUMP("Replace an unused BLK node [%06d] with a NULLCHECK\n", dspTreeID(node)); - m_compiler->gtChangeOperToNullCheck(node, m_compiler->compCurBB); - } - - Append(node); - return Compiler::WALK_SKIP_SUBTREES; + JITDUMP("Replace an unused BLK node [%06d] with a NULLCHECK\n", dspTreeID(node)); + m_compiler->gtChangeOperToNullCheck(node, m_compiler->compCurBB); } - if (node->OperIs(GT_QMARK)) - { - GenTree* prevSideEffects = m_result; - // Visit children out of order so we know if we can - // completely remove the qmark. We cannot modify the - // condition if we cannot completely remove the qmark, so - // we cannot visit it first. + Append(node); + return Compiler::WALK_SKIP_SUBTREES; + } - GenTreeQmark* qmark = node->AsQmark(); - GenTreeColon* colon = qmark->gtGetOp2()->AsColon(); + if (node->OperIs(GT_QMARK)) + { + GenTree* prevSideEffects = m_result; + // Visit children out of order so we know if we can + // completely remove the qmark. We cannot modify the + // condition if we cannot completely remove the qmark, so + // we cannot visit it first. - m_result = nullptr; - WalkTree(&colon->gtOp1, colon); - GenTree* thenSideEffects = m_result; + GenTreeQmark* qmark = node->AsQmark(); + GenTreeColon* colon = qmark->gtGetOp2()->AsColon(); - m_result = nullptr; - WalkTree(&colon->gtOp2, colon); - GenTree* elseSideEffects = m_result; + m_result = nullptr; + WalkTree(&colon->gtOp1, colon); + GenTree* thenSideEffects = m_result; - m_result = prevSideEffects; + m_result = nullptr; + WalkTree(&colon->gtOp2, colon); + GenTree* elseSideEffects = m_result; - if ((thenSideEffects == nullptr) && (elseSideEffects == nullptr)) - { - WalkTree(&qmark->gtOp1, qmark); - } - else - { - colon->gtOp1 = (thenSideEffects != nullptr) ? thenSideEffects : m_compiler->gtNewNothingNode(); - colon->gtOp2 = (elseSideEffects != nullptr) ? elseSideEffects : m_compiler->gtNewNothingNode(); - qmark->gtType = TYP_VOID; - colon->gtType = TYP_VOID; - Append(qmark); - } + m_result = prevSideEffects; - return Compiler::WALK_SKIP_SUBTREES; + if ((thenSideEffects == nullptr) && (elseSideEffects == nullptr)) + { + WalkTree(&qmark->gtOp1, qmark); } - - // Generally all GT_CALL nodes are considered to have side-effects. - // So if we get here it must be a helper call that we decided it does - // not have side effects that we needed to keep. - assert(!node->OperIs(GT_CALL) || node->AsCall()->IsHelperCall()); - } - - if ((m_flags & GTF_IS_IN_CSE) != 0) - { - // If we're doing CSE then we also need to unmark CSE nodes. This will fail for CSE defs, - // those need to be extracted as if they're side effects. - if (!UnmarkCSE(node)) + else { - Append(node); - return Compiler::WALK_SKIP_SUBTREES; + colon->gtOp1 = (thenSideEffects != nullptr) ? thenSideEffects : m_compiler->gtNewNothingNode(); + colon->gtOp2 = (elseSideEffects != nullptr) ? elseSideEffects : m_compiler->gtNewNothingNode(); + qmark->gtType = TYP_VOID; + colon->gtType = TYP_VOID; + Append(qmark); } - // The existence of CSE defs and uses is not propagated up the tree like side - // effects are. We need to continue visiting the tree as if it has side effects. - treeHasSideEffects = true; + return Compiler::WALK_SKIP_SUBTREES; } - return treeHasSideEffects ? Compiler::WALK_CONTINUE : Compiler::WALK_SKIP_SUBTREES; + return Compiler::WALK_CONTINUE; } void Append(GenTree* node) @@ -17196,7 +17240,6 @@ void Compiler::gtExtractSideEffList(GenTree* expr, } GenTree* comma = m_compiler->gtNewOperNode(GT_COMMA, TYP_VOID, m_result, node); - comma->gtFlags |= (m_result->gtFlags | node->gtFlags) & GTF_ALL_EFFECT; #ifdef DEBUG if (m_compiler->fgGlobalMorph) @@ -17218,52 +17261,12 @@ void Compiler::gtExtractSideEffList(GenTree* expr, // if ((m_compiler->vnStore != nullptr) && m_result->gtVNPair.BothDefined() && node->gtVNPair.BothDefined()) { - // The result of a GT_COMMA node is op2, the normal value number is op2vnp - // But we also need to include the union of side effects from op1 and op2. - // we compute this value into exceptions_vnp. - ValueNumPair op1vnp; - ValueNumPair op1Xvnp = ValueNumStore::VNPForEmptyExcSet(); - ValueNumPair op2vnp; - ValueNumPair op2Xvnp = ValueNumStore::VNPForEmptyExcSet(); - - m_compiler->vnStore->VNPUnpackExc(node->gtVNPair, &op1vnp, &op1Xvnp); - m_compiler->vnStore->VNPUnpackExc(m_result->gtVNPair, &op2vnp, &op2Xvnp); - - ValueNumPair exceptions_vnp = ValueNumStore::VNPForEmptyExcSet(); - - exceptions_vnp = m_compiler->vnStore->VNPExcSetUnion(exceptions_vnp, op1Xvnp); - exceptions_vnp = m_compiler->vnStore->VNPExcSetUnion(exceptions_vnp, op2Xvnp); - - comma->gtVNPair = m_compiler->vnStore->VNPWithExc(op2vnp, exceptions_vnp); + ValueNumPair op1Exceptions = m_compiler->vnStore->VNPExceptionSet(m_result->gtVNPair); + comma->gtVNPair = m_compiler->vnStore->VNPWithExc(node->gtVNPair, op1Exceptions); } m_result = comma; } - - private: - bool UnmarkCSE(GenTree* node) - { - assert(m_compiler->optValnumCSE_phase); - - if (m_compiler->optUnmarkCSE(node)) - { - // The call to optUnmarkCSE(node) should have cleared any CSE info. - assert(!IS_CSE_INDEX(node->gtCSEnum)); - return true; - } - else - { - assert(IS_CSE_DEF(node->gtCSEnum)); -#ifdef DEBUG - if (m_compiler->verbose) - { - printf("Preserving the CSE def #%02d at ", GET_CSE_INDEX(node->gtCSEnum)); - m_compiler->printTreeID(node); - } -#endif - return false; - } - } }; SideEffectExtractor extractor(this, flags); @@ -18638,6 +18641,48 @@ bool GenTreeVecCon::IsNegativeZero(var_types simdBaseType) const return true; } + +//------------------------------------------------------------------------ +// GenTreeMskCon::EvaluateUnaryInPlace: Evaluates this constant using the given operation +// +// Arguments: +// oper - the operation to use in the evaluation +// scalar - true if this is a scalar operation; otherwise, false +// baseType - the base type of the constant being checked +// simdSize - the size of the SIMD node the mask is for +// +void GenTreeMskCon::EvaluateUnaryInPlace(genTreeOps oper, bool scalar, var_types baseType, unsigned simdSize) +{ +#if defined(FEATURE_MASKED_HW_INTRINSICS) + simdmask_t result = {}; + EvaluateUnaryMask(oper, scalar, baseType, simdSize, &result, gtSimdMaskVal); + gtSimdMaskVal = result; +#else + unreached(); +#endif // FEATURE_MASKED_HW_INTRINSICS +} + +//------------------------------------------------------------------------ +// GenTreeMskCon::EvaluateUnaryInPlace: Evaluates this constant using the given operation +// +// Arguments: +// oper - the operation to use in the evaluation +// scalar - true if this is a scalar operation; otherwise, false +// baseType - the base type of the constant being checked +// other - the other vector constant to use in the evaluation +// simdSize - the size of the SIMD node the mask is for +// +void GenTreeMskCon::EvaluateBinaryInPlace( + genTreeOps oper, bool scalar, var_types baseType, unsigned simdSize, GenTreeMskCon* other) +{ +#if defined(FEATURE_MASKED_HW_INTRINSICS) + simdmask_t result = {}; + EvaluateBinaryMask(oper, scalar, baseType, simdSize, &result, gtSimdMaskVal, other->gtSimdMaskVal); + gtSimdMaskVal = result; +#else + unreached(); +#endif // FEATURE_MASKED_HW_INTRINSICS +} #endif // FEATURE_HW_INTRINSICS*/ //------------------------------------------------------------------------ @@ -21224,6 +21269,7 @@ GenTree* Compiler::gtNewSimdCeilNode(var_types type, GenTree* op1, CorInfoType s return gtNewSimdHWIntrinsicNode(type, op1, intrinsic, simdBaseJitType, simdSize); } +#if defined(FEATURE_MASKED_HW_INTRINSICS) //------------------------------------------------------------------------ // gtNewSimdCvtMaskToVectorNode: Convert a HW instrinsic mask node to a vector // @@ -21252,6 +21298,7 @@ GenTree* Compiler::gtNewSimdCvtMaskToVectorNode(var_types type, #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 } +#endif // FEATURE_MASKED_HW_INTRINSICS GenTree* Compiler::gtNewSimdCvtNode(var_types type, GenTree* op1, @@ -21614,6 +21661,7 @@ GenTree* Compiler::gtNewSimdCvtNativeNode(var_types type, return gtNewSimdHWIntrinsicNode(type, op1, hwIntrinsicID, simdSourceBaseJitType, simdSize); } +#if defined(FEATURE_MASKED_HW_INTRINSICS) //------------------------------------------------------------------------ // gtNewSimdCvtVectorToMaskNode: Convert a HW instrinsic vector node to a mask // @@ -21644,6 +21692,7 @@ GenTree* Compiler::gtNewSimdCvtVectorToMaskNode(var_types type, #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 } +#endif // FEATURE_MASKED_HW_INTRINSICS GenTree* Compiler::gtNewSimdCmpOpNode( genTreeOps op, var_types type, GenTree* op1, GenTree* op2, CorInfoType simdBaseJitType, unsigned simdSize) @@ -21700,11 +21749,15 @@ GenTree* Compiler::gtNewSimdCmpOpNode( if (intrinsic != NI_Illegal) { +#if defined(FEATURE_MASKED_HW_INTRINSICS) if (needsConvertMaskToVector) { GenTree* retNode = gtNewSimdHWIntrinsicNode(TYP_MASK, op1, op2, intrinsic, simdBaseJitType, simdSize); return gtNewSimdCvtMaskToVectorNode(type, retNode, simdBaseJitType, simdSize); } +#else + assert(!needsConvertMaskToVector); +#endif // !FEATURE_MASKED_HW_INTRINSICS return gtNewSimdHWIntrinsicNode(type, op1, op2, intrinsic, simdBaseJitType, simdSize); } @@ -24726,7 +24779,7 @@ GenTree* Compiler::gtNewSimdShuffleNode( assert(op2 != nullptr); assert(op2->TypeIs(type)); - assert(op2->IsVectorConst()); + assert(op2->IsCnsVec()); var_types simdBaseType = JitType2PreciseVarType(simdBaseJitType); assert(varTypeIsArithmetic(simdBaseType)); @@ -26732,6 +26785,7 @@ bool GenTreeHWIntrinsic::OperIsMemoryStore(GenTree** pAddr) const case NI_SSE2_MaskMove: addr = Op(3); break; + #elif defined(TARGET_ARM64) case NI_Sve_StoreAndZip: case NI_Sve_StoreAndZipx2: @@ -26743,9 +26797,14 @@ bool GenTreeHWIntrinsic::OperIsMemoryStore(GenTree** pAddr) const break; case NI_Sve_Scatter: + case NI_Sve_Scatter16BitNarrowing: + case NI_Sve_Scatter16BitWithByteOffsetsNarrowing: + case NI_Sve_Scatter32BitNarrowing: + case NI_Sve_Scatter32BitWithByteOffsetsNarrowing: + case NI_Sve_Scatter8BitNarrowing: + case NI_Sve_Scatter8BitWithByteOffsetsNarrowing: addr = Op(2); break; - #endif // TARGET_ARM64 default: @@ -27337,6 +27396,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_AVX512F_And: case NI_AVX512DQ_And: case NI_AVX10v1_V512_And: + case NI_EVEX_AndMask: #elif defined(TARGET_ARM64) case NI_AdvSimd_And: #endif @@ -27344,12 +27404,14 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty return GT_AND; } -#if defined(TARGET_ARM64) +#if defined(TARGET_XARCH) + case NI_EVEX_NotMask: +#elif defined(TARGET_ARM64) case NI_AdvSimd_Not: +#endif { return GT_NOT; } -#endif #if defined(TARGET_XARCH) case NI_SSE_Xor: @@ -27359,6 +27421,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_AVX512F_Xor: case NI_AVX512DQ_Xor: case NI_AVX10v1_V512_Xor: + case NI_EVEX_XorMask: #elif defined(TARGET_ARM64) case NI_AdvSimd_Xor: #endif @@ -27374,6 +27437,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_AVX512F_Or: case NI_AVX512DQ_Or: case NI_AVX10v1_V512_Or: + case NI_EVEX_OrMask: #elif defined(TARGET_ARM64) case NI_AdvSimd_Or: #endif @@ -27389,6 +27453,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_AVX512F_AndNot: case NI_AVX512DQ_AndNot: case NI_AVX10v1_V512_AndNot: + case NI_EVEX_AndNotMask: #elif defined(TARGET_ARM64) case NI_AdvSimd_BitwiseClear: #endif @@ -27561,9 +27626,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty } #endif // TARGET_XARCH -#ifdef TARGET_ARM64 - case NI_AdvSimd_ShiftLeftLogical: -#else +#if defined(TARGET_XARCH) case NI_SSE2_ShiftLeftLogical: case NI_AVX2_ShiftLeftLogical: case NI_AVX2_ShiftLeftLogicalVariable: @@ -27573,12 +27636,14 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_AVX512BW_ShiftLeftLogicalVariable: case NI_AVX512BW_VL_ShiftLeftLogicalVariable: case NI_AVX10v1_ShiftLeftLogicalVariable: +#elif defined(TARGET_ARM64) + case NI_AdvSimd_ShiftLeftLogical: #endif { return GT_LSH; } -#ifdef TARGET_ARM64 +#if defined(TARGET_ARM64) case NI_AdvSimd_ShiftLeftLogicalScalar: { if (genTypeSize(simdBaseType) != 8) @@ -27589,9 +27654,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty } #endif -#ifdef TARGET_ARM64 - case NI_AdvSimd_ShiftRightArithmetic: -#else +#if defined(TARGET_XARCH) case NI_SSE2_ShiftRightArithmetic: case NI_AVX2_ShiftRightArithmetic: case NI_AVX2_ShiftRightArithmeticVariable: @@ -27604,12 +27667,14 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_AVX512BW_VL_ShiftRightArithmeticVariable: case NI_AVX10v1_ShiftRightArithmetic: case NI_AVX10v1_ShiftRightArithmeticVariable: +#elif defined(TARGET_ARM64) + case NI_AdvSimd_ShiftRightArithmetic: #endif { return GT_RSH; } -#ifdef TARGET_ARM64 +#if defined(TARGET_ARM64) case NI_AdvSimd_ShiftRightArithmeticScalar: { if (genTypeSize(simdBaseType) != 8) @@ -27620,9 +27685,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty } #endif -#ifdef TARGET_ARM64 - case NI_AdvSimd_ShiftRightLogical: -#else +#if defined(TARGET_XARCH) case NI_SSE2_ShiftRightLogical: case NI_AVX2_ShiftRightLogical: case NI_AVX2_ShiftRightLogicalVariable: @@ -27632,12 +27695,14 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_AVX512BW_ShiftRightLogicalVariable: case NI_AVX512BW_VL_ShiftRightLogicalVariable: case NI_AVX10v1_ShiftRightLogicalVariable: +#elif defined(TARGET_ARM64) + case NI_AdvSimd_ShiftRightLogical: #endif { return GT_RSZ; } -#ifdef TARGET_ARM64 +#if defined(TARGET_ARM64) case NI_AdvSimd_ShiftRightLogicalScalar: { if (genTypeSize(simdBaseType) != 8) @@ -27691,6 +27756,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_SSE41_CompareEqual: case NI_AVX_CompareEqual: case NI_AVX2_CompareEqual: + case NI_EVEX_CompareEqualMask: #elif defined(TARGET_ARM64) case NI_AdvSimd_CompareEqual: case NI_AdvSimd_Arm64_CompareEqual: @@ -27725,6 +27791,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_SSE42_CompareGreaterThan: case NI_AVX_CompareGreaterThan: case NI_AVX2_CompareGreaterThan: + case NI_EVEX_CompareGreaterThanMask: #elif defined(TARGET_ARM64) case NI_AdvSimd_CompareGreaterThan: case NI_AdvSimd_Arm64_CompareGreaterThan: @@ -27757,6 +27824,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_SSE_CompareGreaterThanOrEqual: case NI_SSE2_CompareGreaterThanOrEqual: case NI_AVX_CompareGreaterThanOrEqual: + case NI_EVEX_CompareGreaterThanOrEqualMask: #elif defined(TARGET_ARM64) case NI_AdvSimd_CompareGreaterThanOrEqual: case NI_AdvSimd_Arm64_CompareGreaterThanOrEqual: @@ -27791,6 +27859,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_SSE42_CompareLessThan: case NI_AVX_CompareLessThan: case NI_AVX2_CompareLessThan: + case NI_EVEX_CompareLessThanMask: #elif defined(TARGET_ARM64) case NI_AdvSimd_CompareLessThan: case NI_AdvSimd_Arm64_CompareLessThan: @@ -27823,6 +27892,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_SSE_CompareLessThanOrEqual: case NI_SSE2_CompareLessThanOrEqual: case NI_AVX_CompareLessThanOrEqual: + case NI_EVEX_CompareLessThanOrEqualMask: #elif defined(TARGET_ARM64) case NI_AdvSimd_CompareLessThanOrEqual: case NI_AdvSimd_Arm64_CompareLessThanOrEqual: @@ -27855,6 +27925,7 @@ genTreeOps GenTreeHWIntrinsic::GetOperForHWIntrinsicId(NamedIntrinsic id, var_ty case NI_SSE_CompareNotEqual: case NI_SSE2_CompareNotEqual: case NI_AVX_CompareNotEqual: + case NI_EVEX_CompareNotEqualMask: { return GT_NE; } @@ -30120,6 +30191,10 @@ bool GenTree::IsVectorPerElementMask(var_types simdBaseType, unsigned simdSize) return false; } + else if (IsCnsMsk()) + { + return true; + } #endif // FEATURE_SIMD return false; @@ -30322,85 +30397,83 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) } } +#if defined(FEATURE_MASKED_HW_INTRINSICS) if (tree->OperIsConvertMaskToVector()) { GenTree* op = op1; - if (!op->OperIsHWIntrinsic()) + if (op->OperIsHWIntrinsic()) { - return tree; - } + unsigned simdBaseTypeSize = genTypeSize(simdBaseType); + GenTreeHWIntrinsic* cvtOp = op->AsHWIntrinsic(); - unsigned simdBaseTypeSize = genTypeSize(simdBaseType); - GenTreeHWIntrinsic* cvtOp = op->AsHWIntrinsic(); - - if (!cvtOp->OperIsConvertVectorToMask()) - { - return tree; - } - - if ((genTypeSize(cvtOp->GetSimdBaseType()) != simdBaseTypeSize)) - { - // We need the operand to be the same kind of mask; otherwise - // the bitwise operation can differ in how it performs - return tree; - } + if (cvtOp->OperIsConvertVectorToMask()) + { + if ((genTypeSize(cvtOp->GetSimdBaseType()) == simdBaseTypeSize)) + { + // We need the operand to be the same kind of mask; otherwise + // the bitwise operation can differ in how it performs #if defined(TARGET_XARCH) - GenTree* vectorNode = cvtOp->Op(1); + GenTree* vectorNode = cvtOp->Op(1); #elif defined(TARGET_ARM64) - GenTree* vectorNode = cvtOp->Op(2); + GenTree* vectorNode = cvtOp->Op(2); #else #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 - DEBUG_DESTROY_NODE(op, tree); - INDEBUG(vectorNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + DEBUG_DESTROY_NODE(op, tree); + INDEBUG(vectorNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - return vectorNode; + return vectorNode; + } + } + } } if (tree->OperIsConvertVectorToMask()) { - GenTree* op = op1; + GenTree* op = op1; + bool tryHandle = false; -#if defined(TARGET_ARM64) - if (!op->OperIsHWIntrinsic(NI_Sve_CreateTrueMaskAll)) +#if defined(TARGET_XARCH) + tryHandle = op->OperIsHWIntrinsic(); +#elif defined(TARGET_ARM64) + if (op->OperIsHWIntrinsic() && op->OperIsHWIntrinsic(NI_Sve_CreateTrueMaskAll)) { - return tree; + op = op2; + tryHandle = true; } - op = op2; #endif // TARGET_ARM64 - if (!op->OperIsHWIntrinsic()) + if (tryHandle) { - return tree; - } - - unsigned simdBaseTypeSize = genTypeSize(simdBaseType); - GenTreeHWIntrinsic* cvtOp = op->AsHWIntrinsic(); + unsigned simdBaseTypeSize = genTypeSize(simdBaseType); + GenTreeHWIntrinsic* cvtOp = op->AsHWIntrinsic(); - if (!cvtOp->OperIsConvertMaskToVector()) - { - return tree; - } - - if ((genTypeSize(cvtOp->GetSimdBaseType()) != simdBaseTypeSize)) - { - // We need the operand to be the same kind of mask; otherwise - // the bitwise operation can differ in how it performs - return tree; - } + if (cvtOp->OperIsConvertMaskToVector()) + { + if ((genTypeSize(cvtOp->GetSimdBaseType()) == simdBaseTypeSize)) + { + // We need the operand to be the same kind of mask; otherwise + // the bitwise operation can differ in how it performs - GenTree* maskNode = cvtOp->Op(1); + GenTree* maskNode = cvtOp->Op(1); #if defined(TARGET_ARM64) - DEBUG_DESTROY_NODE(op1); + DEBUG_DESTROY_NODE(op1); #endif // TARGET_ARM64 - DEBUG_DESTROY_NODE(op, tree); - return maskNode; + DEBUG_DESTROY_NODE(op, tree); + return maskNode; + } + } + } } +#else + assert(!tree->OperIsConvertMaskToVector()); + assert(!tree->OperIsConvertVectorToMask()); +#endif // FEATURE_MASKED_HW_INTRINSICS bool isScalar = false; genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar); @@ -30436,9 +30509,72 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) if (oper != GT_NONE) { - cnsNode->AsVecCon()->EvaluateUnaryInPlace(oper, isScalar, simdBaseType); + if (varTypeIsMask(retType)) + { + cnsNode->AsMskCon()->EvaluateUnaryInPlace(oper, isScalar, simdBaseType, simdSize); + } + else + { + cnsNode->AsVecCon()->EvaluateUnaryInPlace(oper, isScalar, simdBaseType); + } resultNode = cnsNode; } + else if (tree->OperIsConvertMaskToVector()) + { + GenTreeMskCon* mskCon = cnsNode->AsMskCon(); + + simd_t simdVal; + EvaluateSimdCvtMaskToVector(simdBaseType, &simdVal, mskCon->gtSimdMaskVal); + + resultNode = gtNewVconNode(retType, &simdVal); + } + else if (tree->OperIsConvertVectorToMask()) + { + GenTreeVecCon* vecCon = cnsNode->AsVecCon(); + GenTreeMskCon* mskCon = gtNewMskConNode(retType); + + switch (vecCon->TypeGet()) + { + case TYP_SIMD8: + { + EvaluateSimdCvtVectorToMask(simdBaseType, &mskCon->gtSimdMaskVal, vecCon->gtSimd8Val); + break; + } + + case TYP_SIMD12: + { + EvaluateSimdCvtVectorToMask(simdBaseType, &mskCon->gtSimdMaskVal, vecCon->gtSimd12Val); + break; + } + + case TYP_SIMD16: + { + EvaluateSimdCvtVectorToMask(simdBaseType, &mskCon->gtSimdMaskVal, vecCon->gtSimd16Val); + break; + } + +#if defined(TARGET_XARCH) + case TYP_SIMD32: + { + EvaluateSimdCvtVectorToMask(simdBaseType, &mskCon->gtSimdMaskVal, vecCon->gtSimd32Val); + break; + } + + case TYP_SIMD64: + { + EvaluateSimdCvtVectorToMask(simdBaseType, &mskCon->gtSimdMaskVal, vecCon->gtSimd64Val); + break; + } +#endif // TARGET_XARCH + + default: + { + unreached(); + } + } + + resultNode = mskCon; + } else { switch (ni) @@ -30468,6 +30604,8 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) uint32_t result = BitOperations::LeadingZeroCount(static_cast(value)); cnsNode->AsIntConCommon()->SetIconValue(static_cast(result)); + cnsNode->gtType = retType; + resultNode = cnsNode; break; } @@ -30636,41 +30774,57 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) { assert(op3 == nullptr); -#if defined(TARGET_XARCH) - if ((oper == GT_LSH) || (oper == GT_RSH) || (oper == GT_RSZ)) + if (varTypeIsMask(retType)) + { + if (varTypeIsMask(cnsNode)) + { + cnsNode->AsMskCon()->EvaluateBinaryInPlace(oper, isScalar, simdBaseType, simdSize, + otherNode->AsMskCon()); + } + else + { + cnsNode->AsVecCon()->EvaluateBinaryInPlace(oper, isScalar, simdBaseType, otherNode->AsVecCon()); + } + } + else { - if (otherNode->TypeIs(TYP_SIMD16)) +#if defined(TARGET_XARCH) + if ((oper == GT_LSH) || (oper == GT_RSH) || (oper == GT_RSZ)) { - if ((ni != NI_AVX2_ShiftLeftLogicalVariable) && (ni != NI_AVX2_ShiftRightArithmeticVariable) && - (ni != NI_AVX512F_VL_ShiftRightArithmeticVariable) && - (ni != NI_AVX10v1_ShiftRightArithmeticVariable) && (ni != NI_AVX2_ShiftRightLogicalVariable)) + if (otherNode->TypeIs(TYP_SIMD16)) { - // The xarch shift instructions support taking the shift amount as - // a simd16, in which case they take the shift amount from the lower - // 64-bits. + if ((ni != NI_AVX2_ShiftLeftLogicalVariable) && (ni != NI_AVX2_ShiftRightArithmeticVariable) && + (ni != NI_AVX512F_VL_ShiftRightArithmeticVariable) && + (ni != NI_AVX10v1_ShiftRightArithmeticVariable) && + (ni != NI_AVX2_ShiftRightLogicalVariable)) + { + // The xarch shift instructions support taking the shift amount as + // a simd16, in which case they take the shift amount from the lower + // 64-bits. - int64_t shiftAmount = otherNode->AsVecCon()->GetElementIntegral(TYP_LONG, 0); + int64_t shiftAmount = otherNode->AsVecCon()->GetElementIntegral(TYP_LONG, 0); - if ((genTypeSize(simdBaseType) != 8) && (shiftAmount > INT_MAX)) - { - // Ensure we don't lose track the the amount is an overshift - shiftAmount = -1; + if ((genTypeSize(simdBaseType) != 8) && (shiftAmount > INT_MAX)) + { + // Ensure we don't lose track the the amount is an overshift + shiftAmount = -1; + } + otherNode->AsVecCon()->EvaluateBroadcastInPlace(simdBaseType, shiftAmount); } - otherNode->AsVecCon()->EvaluateBroadcastInPlace(simdBaseType, shiftAmount); } } - } #endif // TARGET_XARCH - if (otherNode->IsIntegralConst()) - { - int64_t scalar = otherNode->AsIntConCommon()->IntegralValue(); + if (otherNode->IsIntegralConst()) + { + int64_t scalar = otherNode->AsIntConCommon()->IntegralValue(); - otherNode = gtNewVconNode(retType); - otherNode->AsVecCon()->EvaluateBroadcastInPlace(simdBaseType, scalar); - } + otherNode = gtNewVconNode(retType); + otherNode->AsVecCon()->EvaluateBroadcastInPlace(simdBaseType, scalar); + } - cnsNode->AsVecCon()->EvaluateBinaryInPlace(oper, isScalar, simdBaseType, otherNode->AsVecCon()); + cnsNode->AsVecCon()->EvaluateBinaryInPlace(oper, isScalar, simdBaseType, otherNode->AsVecCon()); + } resultNode = cnsNode; } else @@ -31318,6 +31472,8 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) case NI_Sve_ConditionalSelect: #endif { + assert(!varTypeIsMask(retType)); + if (cnsNode != op1) { break; @@ -31364,6 +31520,12 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) } } + if (varTypeIsMask(retType) && !varTypeIsMask(resultNode)) + { + resultNode = gtNewSimdCvtVectorToMaskNode(retType, resultNode, simdBaseJitType, simdSize); + return gtFoldExprHWIntrinsic(resultNode->AsHWIntrinsic()); + } + if (resultNode != tree) { if (fgGlobalMorph) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 5b7bc8278cab7e..757ce23912d732 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -404,15 +404,6 @@ enum GenTreeFlags : unsigned int // With operators: the specified node is an unsigned operator GTF_SPILL = 0x00020000, // Needs to be spilled here -// The extra flag GTF_IS_IN_CSE is used to tell the consumer of the side effect flags -// that we are calling in the context of performing a CSE, thus we -// should allow the run-once side effects of running a class constructor. -// -// The only requirement of this flag is that it not overlap any of the -// side-effect flags. The actual bit used is otherwise arbitrary. - - GTF_IS_IN_CSE = 0x00004000, - GTF_COMMON_MASK = 0x0003FFFF, // mask of all the flags above GTF_REUSE_REG_VAL = 0x00800000, // This is set by the register allocator on nodes whose value already exists in the @@ -904,7 +895,7 @@ struct GenTree bool isUsedFromMemory() const { - return ((isContained() && (isMemoryOp() || OperIs(GT_LCL_VAR, GT_CNS_DBL, GT_CNS_VEC))) || + return ((isContained() && (isMemoryOp() || OperIs(GT_LCL_VAR, GT_CNS_DBL, GT_CNS_VEC, GT_CNS_MSK))) || isUsedFromSpillTemp()); } @@ -1036,7 +1027,7 @@ struct GenTree { // These are the only operators which can produce either VOID or non-VOID results. assert(OperIs(GT_NOP, GT_CALL, GT_COMMA) || OperIsCompare() || OperIsLong() || OperIsHWIntrinsic() || - IsCnsVec()); + IsCnsVec() || IsCnsMsk()); return false; } @@ -1098,8 +1089,8 @@ struct GenTree static bool OperIsConst(genTreeOps gtOper) { - static_assert_no_msg(AreContiguous(GT_CNS_INT, GT_CNS_LNG, GT_CNS_DBL, GT_CNS_STR, GT_CNS_VEC)); - return (GT_CNS_INT <= gtOper) && (gtOper <= GT_CNS_VEC); + static_assert_no_msg(AreContiguous(GT_CNS_INT, GT_CNS_LNG, GT_CNS_DBL, GT_CNS_STR, GT_CNS_VEC, GT_CNS_MSK)); + return (GT_CNS_INT <= gtOper) && (gtOper <= GT_CNS_MSK); } bool OperIsConst() const @@ -1786,7 +1777,6 @@ struct GenTree inline bool IsVectorBroadcast(var_types simdBaseType) const; inline bool IsMaskAllBitsSet() const; inline bool IsMaskZero() const; - inline bool IsVectorConst(); inline uint64_t GetIntegralVectorConstElement(size_t index, var_types simdBaseType); @@ -2199,6 +2189,8 @@ struct GenTree inline bool IsCnsVec() const; + inline bool IsCnsMsk() const; + bool IsIconHandle() const { return (gtOper == GT_CNS_INT) && ((gtFlags & GTF_ICON_HDL_MASK) != 0); @@ -2265,6 +2257,7 @@ struct GenTree return OperGet() == GT_CALL; } inline bool IsHelperCall(); + inline bool IsHelperCall(Compiler* compiler, unsigned helper); bool gtOverflow() const; bool gtOverflowEx() const; @@ -6648,7 +6641,7 @@ struct GenTreeHWIntrinsic : public GenTreeJitIntrinsic }; #endif // FEATURE_HW_INTRINSICS -// GenTreeVecCon -- vector constant (GT_CNS_VEC) +// GenTreeVecCon -- vector constant (GT_CNS_VEC) // struct GenTreeVecCon : public GenTree { @@ -6663,10 +6656,6 @@ struct GenTreeVecCon : public GenTree simd64_t gtSimd64Val; #endif // TARGET_XARCH -#if defined(FEATURE_MASKED_HW_INTRINSICS) - simdmask_t gtSimdMaskVal; -#endif // FEATURE_MASKED_HW_INTRINSICS - simd_t gtSimdVal; }; @@ -7090,13 +7079,6 @@ struct GenTreeVecCon : public GenTree } #endif // TARGET_XARCH - -#if defined(FEATURE_MASKED_HW_INTRINSICS) - case TYP_MASK: - { - return gtSimdMaskVal.IsAllBitsSet(); - } -#endif // FEATURE_MASKED_HW_INTRINSICS #endif // FEATURE_SIMD default: @@ -7147,13 +7129,6 @@ struct GenTreeVecCon : public GenTree } #endif // TARGET_XARCH - -#if defined(FEATURE_MASKED_HW_INTRINSICS) - case TYP_MASK: - { - return left->gtSimdMaskVal == right->gtSimdMaskVal; - } -#endif // FEATURE_MASKED_HW_INTRINSICS #endif // FEATURE_SIMD default: @@ -7199,13 +7174,6 @@ struct GenTreeVecCon : public GenTree } #endif // TARGET_XARCH - -#if defined(FEATURE_MASKED_HW_INTRINSICS) - case TYP_MASK: - { - return gtSimdMaskVal.IsZero(); - } -#endif // FEATURE_MASKED_HW_INTRINSICS #endif // FEATURE_SIMD default: @@ -7377,6 +7345,67 @@ struct GenTreeVecCon : public GenTree #endif }; +// GenTreeMskCon -- mask constant (GT_CNS_MSK) +// +struct GenTreeMskCon : public GenTree +{ +#if defined(FEATURE_MASKED_HW_INTRINSICS) + simdmask_t gtSimdMaskVal; +#endif // FEATURE_MASKED_HW_INTRINSICS + + void EvaluateUnaryInPlace(genTreeOps oper, bool scalar, var_types baseType, unsigned simdSize); + void EvaluateBinaryInPlace( + genTreeOps oper, bool scalar, var_types baseType, unsigned simdSize, GenTreeMskCon* other); + + bool IsAllBitsSet() const + { +#if defined(FEATURE_MASKED_HW_INTRINSICS) + return gtSimdMaskVal.IsAllBitsSet(); +#else + unreached(); +#endif // FEATURE_MASKED_HW_INTRINSICS + } + + static bool Equals(const GenTreeMskCon* left, const GenTreeMskCon* right) + { +#if defined(FEATURE_MASKED_HW_INTRINSICS) + return left->gtSimdMaskVal == right->gtSimdMaskVal; +#else + unreached(); +#endif // FEATURE_MASKED_HW_INTRINSICS + } + + bool IsZero() const + { +#if defined(FEATURE_MASKED_HW_INTRINSICS) + return gtSimdMaskVal.IsZero(); +#else + unreached(); +#endif // FEATURE_MASKED_HW_INTRINSICS + } + + GenTreeMskCon(var_types type) + : GenTree(GT_CNS_MSK, type) + { + assert(varTypeIsMask(type)); + +#if defined(FEATURE_MASKED_HW_INTRINSICS) + // Some uses of GenTreeMskCon do not specify all bits in the mask they are using but failing to zero out the + // buffer will cause determinism issues with the compiler. + memset(>SimdMaskVal, 0, sizeof(gtSimdMaskVal)); +#else + unreached(); +#endif // FEATURE_MASKED_HW_INTRINSICS + } + +#if DEBUGGABLE_GENTREE + GenTreeMskCon() + : GenTree() + { + } +#endif +}; + // GenTreeIndexAddr: Given an array object and an index, checks that the index is within the bounds of the array if // necessary and produces the address of the value at that index of the array. // @@ -9720,24 +9749,6 @@ inline bool GenTree::IsMaskZero() const return false; } -//------------------------------------------------------------------- -// IsVectorConst: returns true if this node is a HWIntrinsic that represents a constant. -// -// Returns: -// True if this represents a HWIntrinsic node that represents a constant. -// -inline bool GenTree::IsVectorConst() -{ -#ifdef FEATURE_SIMD - if (IsCnsVec()) - { - return true; - } -#endif // FEATURE_SIMD - - return false; -} - //------------------------------------------------------------------- // GetIntegralVectorConstElement: Gets the value of a given element in an integral vector constant // @@ -10498,9 +10509,19 @@ inline bool GenTree::IsCnsVec() const return OperIs(GT_CNS_VEC); } +inline bool GenTree::IsCnsMsk() const +{ + return OperIs(GT_CNS_MSK); +} + inline bool GenTree::IsHelperCall() { - return OperGet() == GT_CALL && AsCall()->IsHelperCall(); + return IsCall() && AsCall()->IsHelperCall(); +} + +inline bool GenTree::IsHelperCall(Compiler* compiler, unsigned helper) +{ + return IsCall() && AsCall()->IsHelperCall(compiler, helper); } inline var_types GenTree::CastFromType() diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 2981e0c3947c5c..e1e1f909896276 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -47,6 +47,7 @@ GTNODE(CNS_LNG , GenTreeLngCon ,0,0,GTK_LEAF) GTNODE(CNS_DBL , GenTreeDblCon ,0,0,GTK_LEAF) GTNODE(CNS_STR , GenTreeStrCon ,0,0,GTK_LEAF) GTNODE(CNS_VEC , GenTreeVecCon ,0,0,GTK_LEAF) +GTNODE(CNS_MSK , GenTreeMskCon ,0,0,GTK_LEAF) //----------------------------------------------------------------------------- // Unary operators (1 operand): diff --git a/src/coreclr/jit/gtstructs.h b/src/coreclr/jit/gtstructs.h index 433a5c057c98b1..7dbf6af39c5d01 100644 --- a/src/coreclr/jit/gtstructs.h +++ b/src/coreclr/jit/gtstructs.h @@ -61,6 +61,7 @@ GTSTRUCT_1(LngCon , GT_CNS_LNG) GTSTRUCT_1(DblCon , GT_CNS_DBL) GTSTRUCT_1(StrCon , GT_CNS_STR) GTSTRUCT_1(VecCon , GT_CNS_VEC) +GTSTRUCT_1(MskCon , GT_CNS_MSK) GTSTRUCT_N(LclVarCommon, GT_LCL_VAR, GT_LCL_FLD, GT_PHI_ARG, GT_STORE_LCL_VAR, GT_STORE_LCL_FLD, GT_LCL_ADDR) GTSTRUCT_2(LclVar , GT_LCL_VAR, GT_STORE_LCL_VAR) GTSTRUCT_3(LclFld , GT_LCL_FLD, GT_STORE_LCL_FLD, GT_LCL_ADDR) diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index 5af8e67d676893..4d31cebad96b38 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -1954,13 +1954,13 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, } var_types nodeRetType = retType; -#if defined(TARGET_ARM64) +#if defined(FEATURE_MASKED_HW_INTRINSICS) && defined(TARGET_ARM64) if (HWIntrinsicInfo::ReturnsPerElementMask(intrinsic)) { // Ensure the result is generated to a mask. nodeRetType = TYP_MASK; } -#endif // defined(TARGET_ARM64) +#endif // FEATURE_MASKED_HW_INTRINSICS && TARGET_ARM64 // table-driven importer of simple intrinsics if (impIsTableDrivenHWIntrinsic(intrinsic, category)) @@ -2088,9 +2088,11 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, #elif defined(TARGET_ARM64) switch (intrinsic) { + case NI_Sve_ConvertToDouble: case NI_Sve_ConvertToInt32: - case NI_Sve_ConvertToUInt32: case NI_Sve_ConvertToInt64: + case NI_Sve_ConvertToSingle: + case NI_Sve_ConvertToUInt32: case NI_Sve_ConvertToUInt64: // Save the base type of return SIMD. It is used to contain this intrinsic inside // ConditionalSelect. @@ -2283,7 +2285,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, retNode->AsHWIntrinsic()->SetMethodHandle(this, method R2RARG(*entryPoint)); } -#if defined(TARGET_ARM64) +#if defined(FEATURE_MASKED_HW_INTRINSICS) && defined(TARGET_ARM64) if (HWIntrinsicInfo::IsExplicitMaskedOperation(intrinsic)) { assert(numArgs > 0); @@ -2365,7 +2367,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, retNode = gtNewSimdCvtMaskToVectorNode(retType, op, simdBaseJitType, simdSize); } -#endif // defined(TARGET_ARM64) +#endif // FEATURE_MASKED_HW_INTRINSICS && TARGET_ARM64 if ((retNode != nullptr) && retNode->OperIs(GT_HWINTRINSIC)) { diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index a3fed94ee71a89..abe215d80a552b 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -504,6 +504,21 @@ void HWIntrinsicInfo::lookupImmBounds( immUpperBound = (int)SVE_PRFOP_CONST15; break; + case NI_Sve_AddRotateComplex: + immLowerBound = 0; + immUpperBound = 1; + break; + + case NI_Sve_MultiplyAddRotateComplex: + immLowerBound = 0; + immUpperBound = 3; + break; + + case NI_Sve_TrigonometricMultiplyAddCoefficient: + immLowerBound = 0; + immUpperBound = 7; + break; + default: unreached(); } @@ -2063,7 +2078,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, GenTree* indices = impStackTop(0).val; - if (!indices->IsVectorConst() || !IsValidForShuffle(indices->AsVecCon(), simdSize, simdBaseType)) + if (!indices->IsCnsVec() || !IsValidForShuffle(indices->AsVecCon(), simdSize, simdBaseType)) { assert(sig->numArgs == 2); diff --git a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp index bb9e340d03d377..f8e77197916df8 100644 --- a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp @@ -480,10 +480,16 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) regNumber embMaskOp1Reg = REG_NA; regNumber embMaskOp2Reg = REG_NA; regNumber embMaskOp3Reg = REG_NA; + regNumber embMaskOp4Reg = REG_NA; regNumber falseReg = op3Reg; switch (intrinEmbMask.numOperands) { + case 4: + assert(intrinEmbMask.op4 != nullptr); + embMaskOp4Reg = intrinEmbMask.op4->GetRegNum(); + FALLTHROUGH; + case 3: assert(intrinEmbMask.op3 != nullptr); embMaskOp3Reg = intrinEmbMask.op3->GetRegNum(); @@ -503,6 +509,70 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) unreached(); } + // Shared code for setting up embedded mask arg for intrinsics with 3+ operands + auto emitEmbeddedMaskSetup = [&] { + if (intrin.op3->IsVectorZero()) + { + // If `falseReg` is zero, then move the first operand of `intrinEmbMask` in the + // destination using /Z. + + assert(targetReg != embMaskOp2Reg); + assert(intrin.op3->isContained() || !intrin.op1->IsMaskAllBitsSet()); + GetEmitter()->emitInsSve_R_R_R(INS_sve_movprfx, emitSize, targetReg, maskReg, embMaskOp1Reg, opt); + } + else + { + // Below are the considerations we need to handle: + // + // targetReg == falseReg && targetReg == embMaskOp1Reg + // fmla Zd, P/m, Zn, Zm + // + // targetReg == falseReg && targetReg != embMaskOp1Reg + // movprfx target, P/m, embMaskOp1Reg + // fmla target, P/m, embMaskOp2Reg, embMaskOp3Reg + // + // targetReg != falseReg && targetReg == embMaskOp1Reg + // sel target, P/m, embMaskOp1Reg, falseReg + // fmla target, P/m, embMaskOp2Reg, embMaskOp3Reg + // + // targetReg != falseReg && targetReg != embMaskOp1Reg + // sel target, P/m, embMaskOp1Reg, falseReg + // fmla target, P/m, embMaskOp2Reg, embMaskOp3Reg + // + // Note that, we just check if the targetReg/falseReg or targetReg/embMaskOp1Reg + // coincides or not. + + if (targetReg != falseReg) + { + if (falseReg == embMaskOp1Reg) + { + // If falseReg value and embMaskOp1Reg value are same, then just mov the value + // to the target. + + GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, embMaskOp1Reg, + /* canSkip */ true); + } + else + { + // If falseReg value is not present in targetReg yet, move the inactive lanes + // into the targetReg using `sel`. Since this is RMW, the active lanes should + // have the value from embMaskOp1Reg + + GetEmitter()->emitInsSve_R_R_R_R(INS_sve_sel, emitSize, targetReg, maskReg, embMaskOp1Reg, + falseReg, opt); + } + } + else if (targetReg != embMaskOp1Reg) + { + // If target already contains the values of `falseReg`, just merge the lanes from + // `embMaskOp1Reg`, again because this is RMW semantics. + + GetEmitter()->emitInsSve_R_R_R(INS_sve_movprfx, emitSize, targetReg, maskReg, embMaskOp1Reg, + opt, INS_SCALABLE_OPTS_PREDICATE_MERGE); + } + } + }; + switch (intrinEmbMask.numOperands) { case 1: @@ -516,6 +586,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) { case NI_Sve_ConvertToInt32: case NI_Sve_ConvertToUInt32: + case NI_Sve_ConvertToSingle: { embOpt = emitTypeSize(intrinEmbMask.baseType) == EA_8BYTE ? INS_OPTS_D_TO_S : INS_OPTS_SCALABLE_S; @@ -524,6 +595,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) case NI_Sve_ConvertToInt64: case NI_Sve_ConvertToUInt64: + case NI_Sve_ConvertToDouble: { embOpt = emitTypeSize(intrinEmbMask.baseType) == EA_4BYTE ? INS_OPTS_S_TO_D : INS_OPTS_SCALABLE_D; @@ -703,174 +775,151 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) break; } + case 3: { assert(instrIsRMW); - assert(HWIntrinsicInfo::IsFmaIntrinsic(intrinEmbMask.id)); - assert(falseReg != embMaskOp3Reg); - // For FMA, the operation we are trying to perform is: - // result = op1 + (op2 * op3) - // - // There are two instructions that can be used depending on which operand's register, - // optionally, will store the final result. - // - // 1. If the result is stored in the operand that was used as an "addend" in the operation, - // then we use `FMLA` format: - // reg1 = reg1 + (reg2 * reg3) - // - // 2. If the result is stored in the operand that was used as a "multiplicand" in the operation, - // then we use `FMAD` format: - // reg1 = (reg1 * reg2) + reg3 - // - // Check if the result's register is same as that of one of the operand's register and accordingly - // pick the appropriate format. Suppose `targetReg` holds the result, then we have following cases: - // - // Case# 1: Result is stored in the operand that held the "addend" - // targetReg == reg1 - // - // We generate the FMLA instruction format and no further changes are needed. - // - // Case# 2: Result is stored in the operand `op2` that held the "multiplicand" - // targetReg == reg2 - // - // So we basically have an operation: - // reg2 = reg1 + (reg2 * reg3) - // - // Since, the result will be stored in the "multiplicand", we pick format `FMAD`. - // Then, we rearrange the operands to ensure that the operation is done correctly. - // reg2 = reg1 + (reg2 * reg3) // to start with - // reg2 = reg3 + (reg2 * reg1) // swap reg1 <--> reg3 - // reg1 = reg3 + (reg1 * reg2) // swap reg1 <--> reg2 - // reg1 = (reg1 * reg2) + reg3 // rearrange to get FMAD format - // - // Case# 3: Result is stored in the operand `op3` that held the "multiplier" - // targetReg == reg3 - // - // So we basically have an operation: - // reg3 = reg1 + (reg2 * reg3) - // Since, the result will be stored in the "multiplier", we again pick format `FMAD`. - // Then, we rearrange the operands to ensure that the operation is done correctly. - // reg3 = reg1 + (reg2 * reg3) // to start with - // reg1 = reg3 + (reg2 * reg1) // swap reg1 <--> reg3 - // reg1 = (reg1 * reg2) + reg3 // rearrange to get FMAD format - - bool useAddend = true; - if (targetReg == embMaskOp2Reg) + if (HWIntrinsicInfo::IsFmaIntrinsic(intrinEmbMask.id)) { - // Case# 2 - useAddend = false; - std::swap(embMaskOp1Reg, embMaskOp3Reg); - std::swap(embMaskOp1Reg, embMaskOp2Reg); - } - else if (targetReg == embMaskOp3Reg) - { - // Case# 3 - useAddend = false; - std::swap(embMaskOp1Reg, embMaskOp3Reg); - } - else - { - // Case# 1 - } + assert(falseReg != embMaskOp3Reg); + // For FMA, the operation we are trying to perform is: + // result = op1 + (op2 * op3) + // + // There are two instructions that can be used depending on which operand's register, + // optionally, will store the final result. + // + // 1. If the result is stored in the operand that was used as an "addend" in the operation, + // then we use `FMLA` format: + // reg1 = reg1 + (reg2 * reg3) + // + // 2. If the result is stored in the operand that was used as a "multiplicand" in the operation, + // then we use `FMAD` format: + // reg1 = (reg1 * reg2) + reg3 + // + // Check if the result's register is same as that of one of the operand's register and + // accordingly pick the appropriate format. Suppose `targetReg` holds the result, then we have + // following cases: + // + // Case# 1: Result is stored in the operand that held the "addend" + // targetReg == reg1 + // + // We generate the FMLA instruction format and no further changes are needed. + // + // Case# 2: Result is stored in the operand `op2` that held the "multiplicand" + // targetReg == reg2 + // + // So we basically have an operation: + // reg2 = reg1 + (reg2 * reg3) + // + // Since, the result will be stored in the "multiplicand", we pick format `FMAD`. + // Then, we rearrange the operands to ensure that the operation is done correctly. + // reg2 = reg1 + (reg2 * reg3) // to start with + // reg2 = reg3 + (reg2 * reg1) // swap reg1 <--> reg3 + // reg1 = reg3 + (reg1 * reg2) // swap reg1 <--> reg2 + // reg1 = (reg1 * reg2) + reg3 // rearrange to get FMAD format + // + // Case# 3: Result is stored in the operand `op3` that held the "multiplier" + // targetReg == reg3 + // + // So we basically have an operation: + // reg3 = reg1 + (reg2 * reg3) + // Since, the result will be stored in the "multiplier", we again pick format `FMAD`. + // Then, we rearrange the operands to ensure that the operation is done correctly. + // reg3 = reg1 + (reg2 * reg3) // to start with + // reg1 = reg3 + (reg2 * reg1) // swap reg1 <--> reg3 + // reg1 = (reg1 * reg2) + reg3 // rearrange to get FMAD format + + bool useAddend = true; + if (targetReg == embMaskOp2Reg) + { + // Case# 2 + useAddend = false; + std::swap(embMaskOp1Reg, embMaskOp3Reg); + std::swap(embMaskOp1Reg, embMaskOp2Reg); + } + else if (targetReg == embMaskOp3Reg) + { + // Case# 3 + useAddend = false; + std::swap(embMaskOp1Reg, embMaskOp3Reg); + } + else + { + // Case# 1 + } - switch (intrinEmbMask.id) - { - case NI_Sve_FusedMultiplyAdd: - insEmbMask = useAddend ? INS_sve_fmla : INS_sve_fmad; - break; + switch (intrinEmbMask.id) + { + case NI_Sve_FusedMultiplyAdd: + insEmbMask = useAddend ? INS_sve_fmla : INS_sve_fmad; + break; - case NI_Sve_FusedMultiplyAddNegated: - insEmbMask = useAddend ? INS_sve_fnmla : INS_sve_fnmad; - break; + case NI_Sve_FusedMultiplyAddNegated: + insEmbMask = useAddend ? INS_sve_fnmla : INS_sve_fnmad; + break; - case NI_Sve_FusedMultiplySubtract: - insEmbMask = useAddend ? INS_sve_fmls : INS_sve_fmsb; - break; + case NI_Sve_FusedMultiplySubtract: + insEmbMask = useAddend ? INS_sve_fmls : INS_sve_fmsb; + break; - case NI_Sve_FusedMultiplySubtractNegated: - insEmbMask = useAddend ? INS_sve_fnmls : INS_sve_fnmsb; - break; + case NI_Sve_FusedMultiplySubtractNegated: + insEmbMask = useAddend ? INS_sve_fnmls : INS_sve_fnmsb; + break; - case NI_Sve_MultiplyAdd: - insEmbMask = useAddend ? INS_sve_mla : INS_sve_mad; - break; + case NI_Sve_MultiplyAdd: + insEmbMask = useAddend ? INS_sve_mla : INS_sve_mad; + break; - case NI_Sve_MultiplySubtract: - insEmbMask = useAddend ? INS_sve_mls : INS_sve_msb; - break; + case NI_Sve_MultiplySubtract: + insEmbMask = useAddend ? INS_sve_mls : INS_sve_msb; + break; - default: - unreached(); + default: + unreached(); + } } - if (intrin.op3->IsVectorZero()) - { - // If `falseReg` is zero, then move the first operand of `intrinEmbMask` in the - // destination using /Z. + emitEmbeddedMaskSetup(); - assert(targetReg != embMaskOp2Reg); - assert(intrin.op3->isContained() || !intrin.op1->IsMaskAllBitsSet()); - GetEmitter()->emitIns_R_R_R(INS_sve_movprfx, emitSize, targetReg, maskReg, embMaskOp1Reg, opt); + // Finally, perform the desired operation. + if (HWIntrinsicInfo::HasImmediateOperand(intrinEmbMask.id)) + { + HWIntrinsicImmOpHelper helper(this, intrinEmbMask.op3, op2->AsHWIntrinsic()); + for (helper.EmitBegin(); !helper.Done(); helper.EmitCaseEnd()) + { + GetEmitter()->emitInsSve_R_R_R_I(insEmbMask, emitSize, targetReg, maskReg, embMaskOp2Reg, + helper.ImmValue(), opt); + } } else { - // Below are the considerations we need to handle: - // - // targetReg == falseReg && targetReg == embMaskOp1Reg - // fmla Zd, P/m, Zn, Zm - // - // targetReg == falseReg && targetReg != embMaskOp1Reg - // movprfx target, P/m, embMaskOp1Reg - // fmla target, P/m, embMaskOp2Reg, embMaskOp3Reg - // - // targetReg != falseReg && targetReg == embMaskOp1Reg - // sel target, P/m, embMaskOp1Reg, falseReg - // fmla target, P/m, embMaskOp2Reg, embMaskOp3Reg - // - // targetReg != falseReg && targetReg != embMaskOp1Reg - // sel target, P/m, embMaskOp1Reg, falseReg - // fmla target, P/m, embMaskOp2Reg, embMaskOp3Reg - // - // Note that, we just check if the targetReg/falseReg or targetReg/embMaskOp1Reg - // coincides or not. + assert(HWIntrinsicInfo::IsFmaIntrinsic(intrinEmbMask.id)); + GetEmitter()->emitInsSve_R_R_R_R(insEmbMask, emitSize, targetReg, maskReg, embMaskOp2Reg, + embMaskOp3Reg, opt); + } - if (targetReg != falseReg) - { - if (falseReg == embMaskOp1Reg) - { - // If falseReg value and embMaskOp1Reg value are same, then just mov the value - // to the target. + break; + } - GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, embMaskOp1Reg, - /* canSkip */ true); - } - else - { - // If falseReg value is not present in targetReg yet, move the inactive lanes - // into the targetReg using `sel`. Since this is RMW, the active lanes should - // have the value from embMaskOp1Reg + case 4: + { + assert(instrIsRMW); + assert(intrinEmbMask.op4->isContained() == (embMaskOp4Reg == REG_NA)); + assert(HWIntrinsicInfo::HasImmediateOperand(intrinEmbMask.id)); - GetEmitter()->emitIns_R_R_R_R(INS_sve_sel, emitSize, targetReg, maskReg, embMaskOp1Reg, - falseReg, opt); - } - } - else if (targetReg != embMaskOp1Reg) - { - // If target already contains the values of `falseReg`, just merge the lanes from - // `embMaskOp1Reg`, again because this is RMW semantics. + emitEmbeddedMaskSetup(); - GetEmitter()->emitIns_R_R_R(INS_sve_movprfx, emitSize, targetReg, maskReg, embMaskOp1Reg, - opt, INS_SCALABLE_OPTS_PREDICATE_MERGE); - } + HWIntrinsicImmOpHelper helper(this, intrinEmbMask.op4, op2->AsHWIntrinsic()); + for (helper.EmitBegin(); !helper.Done(); helper.EmitCaseEnd()) + { + GetEmitter()->emitInsSve_R_R_R_R_I(insEmbMask, emitSize, targetReg, maskReg, embMaskOp2Reg, + embMaskOp3Reg, helper.ImmValue(), opt); } - // Finally, perform the desired operation. - GetEmitter()->emitIns_R_R_R_R(insEmbMask, emitSize, targetReg, maskReg, embMaskOp2Reg, - embMaskOp3Reg, opt); - break; } + default: unreached(); } @@ -2053,27 +2102,35 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) break; case NI_Sve_Scatter: + case NI_Sve_Scatter16BitNarrowing: + case NI_Sve_Scatter16BitWithByteOffsetsNarrowing: + case NI_Sve_Scatter32BitNarrowing: + case NI_Sve_Scatter32BitWithByteOffsetsNarrowing: + case NI_Sve_Scatter8BitNarrowing: + case NI_Sve_Scatter8BitWithByteOffsetsNarrowing: { if (!varTypeIsSIMD(intrin.op2->gtType)) { // Scatter(Vector mask, T1* address, Vector indicies, Vector data) assert(intrin.numOperands == 4); - emitAttr baseSize = emitActualTypeSize(intrin.baseType); + emitAttr baseSize = emitActualTypeSize(intrin.baseType); + insScalableOpts sopt; if (baseSize == EA_8BYTE) { // Index is multiplied by 8 - GetEmitter()->emitIns_R_R_R_R(ins, emitSize, op4Reg, op1Reg, op2Reg, op3Reg, opt, - INS_SCALABLE_OPTS_LSL_N); + sopt = (ins == INS_sve_st1b) ? INS_SCALABLE_OPTS_NONE : INS_SCALABLE_OPTS_LSL_N; + GetEmitter()->emitIns_R_R_R_R(ins, emitSize, op4Reg, op1Reg, op2Reg, op3Reg, opt, sopt); } else { // Index is sign or zero extended to 64bits, then multiplied by 4 assert(baseSize == EA_4BYTE); - opt = varTypeIsUnsigned(node->GetAuxiliaryType()) ? INS_OPTS_SCALABLE_S_UXTW - : INS_OPTS_SCALABLE_S_SXTW; - GetEmitter()->emitIns_R_R_R_R(ins, emitSize, op4Reg, op1Reg, op2Reg, op3Reg, opt, - INS_SCALABLE_OPTS_MOD_N); + opt = varTypeIsUnsigned(node->GetAuxiliaryType()) ? INS_OPTS_SCALABLE_S_UXTW + : INS_OPTS_SCALABLE_S_SXTW; + sopt = (ins == INS_sve_st1b) ? INS_SCALABLE_OPTS_NONE : INS_SCALABLE_OPTS_MOD_N; + + GetEmitter()->emitIns_R_R_R_R(ins, emitSize, op4Reg, op1Reg, op2Reg, op3Reg, opt, sopt); } } else @@ -2330,6 +2387,26 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) break; } + case NI_Sve_TrigonometricMultiplyAddCoefficient: + { + assert(isRMW); + + if (targetReg != op1Reg) + { + assert(targetReg != op2Reg); + + GetEmitter()->emitInsSve_R_R(INS_sve_movprfx, EA_SCALABLE, targetReg, op1Reg); + } + + HWIntrinsicImmOpHelper helper(this, intrin.op3, node); + + for (helper.EmitBegin(); !helper.Done(); helper.EmitCaseEnd()) + { + GetEmitter()->emitInsSve_R_R_I(ins, emitSize, targetReg, op2Reg, helper.ImmValue(), opt); + } + break; + } + default: unreached(); } diff --git a/src/coreclr/jit/hwintrinsiclistarm64sve.h b/src/coreclr/jit/hwintrinsiclistarm64sve.h index ab78d2f0f1af60..961076326818a3 100644 --- a/src/coreclr/jit/hwintrinsiclistarm64sve.h +++ b/src/coreclr/jit/hwintrinsiclistarm64sve.h @@ -24,6 +24,7 @@ HARDWARE_INTRINSIC(Sve, AbsoluteCompareLessThanOrEqual, HARDWARE_INTRINSIC(Sve, AbsoluteDifference, -1, -1, false, {INS_sve_sabd, INS_sve_uabd, INS_sve_sabd, INS_sve_uabd, INS_sve_sabd, INS_sve_uabd, INS_sve_sabd, INS_sve_uabd, INS_sve_fabd, INS_sve_fabd}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, Add, -1, -1, false, {INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_add, INS_sve_fadd, INS_sve_fadd}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, AddAcross, -1, 1, true, {INS_sve_saddv, INS_sve_uaddv, INS_sve_saddv, INS_sve_uaddv, INS_sve_saddv, INS_sve_uaddv, INS_sve_uaddv, INS_sve_uaddv, INS_sve_faddv, INS_sve_faddv}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, AddRotateComplex, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcadd, INS_sve_fcadd}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_HasImmediateOperand) HARDWARE_INTRINSIC(Sve, AddSaturate, -1, 2, true, {INS_sve_sqadd, INS_sve_uqadd, INS_sve_sqadd, INS_sve_uqadd, INS_sve_sqadd, INS_sve_uqadd, INS_sve_sqadd, INS_sve_uqadd, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, AddSequentialAcross, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fadda, INS_sve_fadda}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, And, -1, -1, false, {INS_sve_and, INS_sve_and, INS_sve_and, INS_sve_and, INS_sve_and, INS_sve_and, INS_sve_and, INS_sve_and, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) @@ -47,8 +48,10 @@ HARDWARE_INTRINSIC(Sve, ConditionalExtractAfterLastActiveElementAndRep HARDWARE_INTRINSIC(Sve, ConditionalExtractLastActiveElement, -1, 3, true, {INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_HasScalarInputVariant|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, ConditionalExtractLastActiveElementAndReplicate, -1, 3, true, {INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb, INS_sve_clastb}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, ConditionalSelect, -1, 3, true, {INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel, INS_sve_sel}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_SupportsContainment) +HARDWARE_INTRINSIC(Sve, ConvertToDouble, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_scvtf, INS_sve_ucvtf, INS_sve_scvtf, INS_sve_ucvtf, INS_sve_fcvt, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, ConvertToInt32, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcvtzs, INS_sve_fcvtzs}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, ConvertToInt64, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcvtzs, INS_sve_fcvtzs}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, ConvertToSingle, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_scvtf, INS_sve_ucvtf, INS_sve_scvtf, INS_sve_ucvtf, INS_invalid, INS_sve_fcvt}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, ConvertToUInt32, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcvtzu, INS_sve_fcvtzu}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, ConvertToUInt64, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcvtzu, INS_sve_fcvtzu}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, Count16BitElements, 0, 1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_cnth, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_Scalable|HW_Flag_HasEnumOperand|HW_Flag_SpecialCodeGen|HW_Flag_NoFloatingPointUsed) @@ -186,6 +189,7 @@ HARDWARE_INTRINSIC(Sve, MinNumber, HARDWARE_INTRINSIC(Sve, MinNumberAcross, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fminnmv, INS_sve_fminnmv}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, Multiply, -1, 2, true, {INS_sve_mul, INS_sve_mul, INS_sve_mul, INS_sve_mul, INS_sve_mul, INS_sve_mul, INS_sve_mul, INS_sve_mul, INS_sve_fmul, INS_sve_fmul}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_OptionalEmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, MultiplyAdd, -1, -1, false, {INS_sve_mla, INS_sve_mla, INS_sve_mla, INS_sve_mla, INS_sve_mla, INS_sve_mla, INS_sve_mla, INS_sve_mla, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation|HW_Flag_FmaIntrinsic|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, MultiplyAddRotateComplex, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fcmla, INS_sve_fcmla}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_HasImmediateOperand) HARDWARE_INTRINSIC(Sve, MultiplyBySelectedScalar, -1, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fmul, INS_sve_fmul}, HW_Category_SIMDByIndexedElement, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_LowVectorOperation) HARDWARE_INTRINSIC(Sve, MultiplyExtended, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fmulx, INS_sve_fmulx}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, MultiplySubtract, -1, -1, false, {INS_sve_mls, INS_sve_mls, INS_sve_mls, INS_sve_mls, INS_sve_mls, INS_sve_mls, INS_sve_mls, INS_sve_mls, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_LowMaskedOperation|HW_Flag_FmaIntrinsic|HW_Flag_SpecialCodeGen) @@ -225,6 +229,12 @@ HARDWARE_INTRINSIC(Sve, SaturatingIncrementBy8BitElementCount, HARDWARE_INTRINSIC(Sve, SaturatingIncrementByActiveElementCount, -1, 2, true, {INS_invalid, INS_sve_sqincp, INS_sve_sqincp, INS_sve_sqincp, INS_sve_sqincp, INS_sve_sqincp, INS_sve_sqincp, INS_sve_sqincp, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen|HW_Flag_SpecialImport|HW_Flag_BaseTypeFromSecondArg|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, Scale, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_fscale, INS_sve_fscale}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, Scatter, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_st1w, INS_sve_st1w, INS_sve_st1d, INS_sve_st1d, INS_sve_st1w, INS_sve_st1d}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, Scatter16BitNarrowing, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_st1h, INS_sve_st1h, INS_sve_st1h, INS_sve_st1h, INS_invalid, INS_invalid}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, Scatter16BitWithByteOffsetsNarrowing, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_st1h, INS_sve_st1h, INS_sve_st1h, INS_sve_st1h, INS_invalid, INS_invalid}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, Scatter32BitNarrowing, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_st1w, INS_sve_st1w, INS_invalid, INS_invalid}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, Scatter32BitWithByteOffsetsNarrowing, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_st1w, INS_sve_st1w, INS_invalid, INS_invalid}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, Scatter8BitNarrowing, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_st1b, INS_sve_st1b, INS_sve_st1b, INS_sve_st1b, INS_invalid, INS_invalid}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) +HARDWARE_INTRINSIC(Sve, Scatter8BitWithByteOffsetsNarrowing, -1, -1, false, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_st1b, INS_sve_st1b, INS_sve_st1b, INS_sve_st1b, INS_invalid, INS_invalid}, HW_Category_MemoryStore, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation) HARDWARE_INTRINSIC(Sve, ShiftLeftLogical, -1, -1, false, {INS_sve_lsl, INS_sve_lsl, INS_sve_lsl, INS_sve_lsl, INS_sve_lsl, INS_sve_lsl, INS_sve_lsl, INS_sve_lsl, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, ShiftRightArithmetic, -1, -1, false, {INS_sve_asr, INS_invalid, INS_sve_asr, INS_invalid, INS_sve_asr, INS_invalid, INS_sve_asr, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics) HARDWARE_INTRINSIC(Sve, ShiftRightArithmeticForDivide, -1, -1, false, {INS_sve_asrd, INS_invalid, INS_sve_asrd, INS_invalid, INS_sve_asrd, INS_invalid, INS_sve_asrd, INS_invalid, INS_invalid, INS_invalid}, HW_Category_ShiftRightByImmediate, HW_Flag_Scalable|HW_Flag_EmbeddedMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_HasRMWSemantics|HW_Flag_HasImmediateOperand) @@ -246,6 +256,7 @@ HARDWARE_INTRINSIC(Sve, TestFirstTrue, HARDWARE_INTRINSIC(Sve, TestLastTrue, -1, 2, true, {INS_sve_ptest, INS_sve_ptest, INS_sve_ptest, INS_sve_ptest, INS_sve_ptest, INS_sve_ptest, INS_sve_ptest, INS_sve_ptest, INS_invalid, INS_invalid}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_ExplicitMaskedOperation|HW_Flag_LowMaskedOperation|HW_Flag_BaseTypeFromFirstArg|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, TransposeEven, -1, 2, true, {INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1, INS_sve_trn1}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, TransposeOdd, -1, 2, true, {INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2, INS_sve_trn2}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) +HARDWARE_INTRINSIC(Sve, TrigonometricMultiplyAddCoefficient, -1, 3, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ftmad, INS_sve_ftmad}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_HasImmediateOperand|HW_Flag_HasRMWSemantics|HW_Flag_SpecialCodeGen) HARDWARE_INTRINSIC(Sve, TrigonometricSelectCoefficient, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ftssel, INS_sve_ftssel}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Sve, TrigonometricStartingValue, -1, 2, true, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sve_ftsmul, INS_sve_ftsmul}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_BaseTypeFromFirstArg) HARDWARE_INTRINSIC(Sve, UnzipEven, -1, 2, true, {INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1, INS_sve_uzp1}, HW_Category_SIMD, HW_Flag_Scalable|HW_Flag_SpecialCodeGen) diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index 74566386c2c294..348291ece8ce67 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -3452,7 +3452,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, GenTree* indices = impStackTop(0).val; - if (!indices->IsVectorConst() || !IsValidForShuffle(indices->AsVecCon(), simdSize, simdBaseType)) + if (!indices->IsCnsVec() || !IsValidForShuffle(indices->AsVecCon(), simdSize, simdBaseType)) { assert(sig->numArgs == 2); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index a84f3db3ff473d..d22721824939d1 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -233,6 +233,7 @@ void Compiler::impSaveStackState(SavedStack* savePtr, bool copy) case GT_CNS_DBL: case GT_CNS_STR: case GT_CNS_VEC: + case GT_CNS_MSK: case GT_LCL_VAR: table->val = gtCloneExpr(tree); break; diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index dc205c60c2fd0c..0d2045d553c826 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -1330,6 +1330,10 @@ var_types Compiler::impImportCall(OPCODE opcode, } else { + if (call->IsCall() && call->AsCall()->IsSpecialIntrinsic(this, NI_System_ArgumentNullException_ThrowIfNull)) + { + call = impThrowIfNull(call->AsCall()); + } impAppendTree(call, CHECK_SPILL_ALL, impCurStmtDI); } } @@ -1528,6 +1532,97 @@ var_types Compiler::impImportCall(OPCODE opcode, #pragma warning(pop) #endif +//------------------------------------------------------------------------ +// impThrowIfNull: Remove redundandant boxing from ArgumentNullException_ThrowIfNull +// it is done for Tier0 where we can't remove it without inlining otherwise. +// +// Arguments: +// call -- call representing ArgumentNullException_ThrowIfNull +// +// Return Value: +// Optimized tree (or the original call tree if we can't optimize it). +// +GenTree* Compiler::impThrowIfNull(GenTreeCall* call) +{ + // We have two overloads: + // + // void ThrowIfNull(object argument, string paramName = null) + // void ThrowIfNull(object argument, ExceptionArgument paramName) + // + assert(call->IsSpecialIntrinsic(this, NI_System_ArgumentNullException_ThrowIfNull)); + assert(call->gtArgs.CountUserArgs() == 2); + assert(call->TypeIs(TYP_VOID)); + + if (opts.compDbgCode || opts.jitFlags->IsSet(JitFlags::JIT_FLAG_MIN_OPT)) + { + // Don't fold it for debug code or forced MinOpts + return call; + } + + GenTree* value = call->gtArgs.GetUserArgByIndex(0)->GetNode(); + GenTree* valueName = call->gtArgs.GetUserArgByIndex(1)->GetNode(); + + // Case 1: value-type (non-nullable): + // + // ArgumentNullException_ThrowIfNull(GT_BOX(value), valueName) + // -> + // NOP (with side-effects if any) + // + if (value->OperIs(GT_BOX)) + { + // Now we need to spill the addr and argName arguments in the correct order + // to preserve possible side effects. + unsigned boxedValTmp = lvaGrabTemp(true DEBUGARG("boxedVal spilled")); + unsigned boxedArgNameTmp = lvaGrabTemp(true DEBUGARG("boxedArg spilled")); + impStoreToTemp(boxedValTmp, value, CHECK_SPILL_ALL); + impStoreToTemp(boxedArgNameTmp, valueName, CHECK_SPILL_ALL); + gtTryRemoveBoxUpstreamEffects(value, BR_REMOVE_AND_NARROW); + return gtNewNothingNode(); + } + + // Case 2: nullable: + // + // ArgumentNullException.ThrowIfNull(CORINFO_HELP_BOX_NULLABLE(classHandle, addr), valueName); + // -> + // addr->hasValue != 0 ? NOP : ArgumentNullException.ThrowIfNull(null, valueNameTmp) + // + if (opts.OptimizationEnabled() || !value->IsHelperCall(this, CORINFO_HELP_BOX_NULLABLE)) + { + // We're not boxing - bail out. + // NOTE: when opts are enabled, we remove the box as is (with better CQ) + return call; + } + + GenTree* boxHelperClsArg = value->AsCall()->gtArgs.GetUserArgByIndex(0)->GetNode(); + GenTree* boxHelperAddrArg = value->AsCall()->gtArgs.GetUserArgByIndex(1)->GetNode(); + + if ((boxHelperClsArg->gtFlags & GTF_SIDE_EFFECT) != 0) + { + // boxHelperClsArg is always just a class handle constant, so we don't bother spilling it. + return call; + } + + // Now we need to spill the addr and argName arguments in the correct order + // to preserve possible side effects. + unsigned boxedValTmp = lvaGrabTemp(true DEBUGARG("boxedVal spilled")); + unsigned boxedArgNameTmp = lvaGrabTemp(true DEBUGARG("boxedArg spilled")); + impStoreToTemp(boxedValTmp, boxHelperAddrArg, CHECK_SPILL_ALL); + impStoreToTemp(boxedArgNameTmp, valueName, CHECK_SPILL_ALL); + + // Change arguments to 'ThrowIfNull(null, valueNameTmp)' + call->gtArgs.GetUserArgByIndex(0)->SetEarlyNode(gtNewNull()); + call->gtArgs.GetUserArgByIndex(1)->SetEarlyNode(gtNewLclvNode(boxedArgNameTmp, valueName->TypeGet())); + + // This is Tier0 specific, so we create a raw indir node to access Nullable.hasValue field + // which is the first field of Nullable struct and is of type 'bool'. + // + static_assert_no_msg(OFFSETOF__CORINFO_NullableOfT__hasValue == 0); + GenTree* hasValueField = gtNewIndir(TYP_UBYTE, gtNewLclvNode(boxedValTmp, boxHelperAddrArg->TypeGet())); + GenTreeOp* cond = gtNewOperNode(GT_NE, TYP_INT, hasValueField, gtNewIconNode(0)); + + return gtNewQmarkNode(TYP_VOID, cond, gtNewColonNode(TYP_VOID, gtNewNothingNode(), call)); +} + //------------------------------------------------------------------------ // impDuplicateWithProfiledArg: duplicates a call with a profiled argument, e.g.: // Given `Buffer.Memmove(dst, src, len)` call, @@ -3257,6 +3352,9 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, // to avoid some unnecessary boxing case NI_System_Enum_HasFlag: + // This one is made intrinsic specifically to avoid boxing in Tier0 + case NI_System_ArgumentNullException_ThrowIfNull: + // Most atomics are compiled to single instructions case NI_System_Threading_Interlocked_And: case NI_System_Threading_Interlocked_Or: @@ -3787,6 +3885,10 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, break; } + case NI_System_ArgumentNullException_ThrowIfNull: + isSpecial = true; + break; + case NI_System_Enum_HasFlag: { GenTree* thisOp = impStackTop(1).val; @@ -9826,6 +9928,13 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = NI_System_Activator_DefaultConstructorOf; } } + else if (strcmp(className, "ArgumentNullException") == 0) + { + if (strcmp(methodName, "ThrowIfNull") == 0) + { + result = NI_System_ArgumentNullException_ThrowIfNull; + } + } else if (strcmp(className, "Array") == 0) { if (strcmp(methodName, "Clone") == 0) diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp index 382350ebfc5fc9..f31e8b364c6ed1 100644 --- a/src/coreclr/jit/instr.cpp +++ b/src/coreclr/jit/instr.cpp @@ -991,15 +991,6 @@ CodeGen::OperandDesc CodeGen::genOperandDesc(GenTree* op) } #endif // TARGET_XARCH - -#if defined(FEATURE_MASKED_HW_INTRINSICS) - case TYP_MASK: - { - simdmask_t constValue; - memcpy(&constValue, &op->AsVecCon()->gtSimdVal, sizeof(simdmask_t)); - return OperandDesc(emit->emitSimdMaskConst(constValue)); - } -#endif // FEATURE_MASKED_HW_INTRINSICS #endif // FEATURE_SIMD default: @@ -1009,6 +1000,17 @@ CodeGen::OperandDesc CodeGen::genOperandDesc(GenTree* op) } } + case GT_CNS_MSK: + { +#if defined(FEATURE_MASKED_HW_INTRINSICS) + simdmask_t constValue; + memcpy(&constValue, &op->AsMskCon()->gtSimdMaskVal, sizeof(simdmask_t)); + return OperandDesc(emit->emitSimdMaskConst(constValue)); +#else + unreached(); +#endif // FEATURE_MASKED_HW_INTRINSICS + } + default: unreached(); } @@ -2038,13 +2040,13 @@ instruction CodeGen::ins_Copy(regNumber srcReg, var_types dstType) return ins_Copy(dstType); } -#if defined(TARGET_XARCH) && defined(FEATURE_SIMD) +#if defined(FEATURE_MASKED_HW_INTRINSICS) && defined(TARGET_XARCH) if (genIsValidMaskReg(srcReg)) { // mask to int return INS_kmovq_gpr; } -#endif // TARGET_XARCH && FEATURE_SIMD +#endif // FEATURE_MASKED_HW_INTRINSICS && TARGET_XARCH // float to int assert(genIsValidFloatReg(srcReg)); diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index c20fa8f54c6ec4..d0ce193114529b 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -1407,6 +1407,7 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR case GT_CNS_DBL: case GT_CNS_STR: case GT_CNS_VEC: + case GT_CNS_MSK: case GT_PHYSREG: // These are all side-effect-free leaf nodes. if (node->IsUnusedValue()) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 2d62deea2cbdd1..d57d1b893d68af 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -10030,7 +10030,7 @@ GenTree* Lowering::InsertNewSimdCreateScalarUnsafeNode(var_types simdType, GenTree* result = comp->gtNewSimdCreateScalarUnsafeNode(simdType, op1, simdBaseJitType, simdSize); BlockRange().InsertAfter(op1, result); - if (result->IsVectorConst()) + if (result->IsCnsVec()) { BlockRange().Remove(op1); } diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index b04e97844f1f27..e64b134192ae28 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -3434,6 +3434,8 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node) case NI_Sve_PrefetchInt32: case NI_Sve_PrefetchInt64: case NI_Sve_ExtractVector: + case NI_Sve_AddRotateComplex: + case NI_Sve_TrigonometricMultiplyAddCoefficient: assert(hasImmediateOperand); assert(varTypeIsIntegral(intrin.op3)); if (intrin.op3->IsCnsIntOrI()) @@ -3597,9 +3599,11 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node) // For now, make sure that we get here only for intrinsics that we are // sure about to rely on auxiliary type's size. - assert((embOp->GetHWIntrinsicId() == NI_Sve_ConvertToInt32) || - (embOp->GetHWIntrinsicId() == NI_Sve_ConvertToUInt32) || + assert((embOp->GetHWIntrinsicId() == NI_Sve_ConvertToDouble) || + (embOp->GetHWIntrinsicId() == NI_Sve_ConvertToInt32) || (embOp->GetHWIntrinsicId() == NI_Sve_ConvertToInt64) || + (embOp->GetHWIntrinsicId() == NI_Sve_ConvertToSingle) || + (embOp->GetHWIntrinsicId() == NI_Sve_ConvertToUInt32) || (embOp->GetHWIntrinsicId() == NI_Sve_ConvertToUInt64)); uint32_t auxSize = genTypeSize(embOp->GetAuxiliaryType()); @@ -3637,6 +3641,7 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node) case NI_Sve_FusedMultiplyAddBySelectedScalar: case NI_Sve_FusedMultiplySubtractBySelectedScalar: + case NI_Sve_MultiplyAddRotateComplex: assert(hasImmediateOperand); assert(varTypeIsIntegral(intrin.op4)); if (intrin.op4->IsCnsIntOrI()) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index d34595399a56ff..6bf852a49fb386 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -1418,7 +1418,7 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) bool isScalar = false; genTreeOps oper = node->GetOperForHWIntrinsicId(&isScalar); - if (GenTreeHWIntrinsic::OperIsBitwiseHWIntrinsic(oper)) + if (GenTreeHWIntrinsic::OperIsBitwiseHWIntrinsic(oper) && !varTypeIsMask(node)) { // These are the control bytes used for TernaryLogic diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 001bc22e299e1a..763a28989b3a8e 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -2991,6 +2991,11 @@ bool LinearScan::isMatchingConstant(RegRecord* physRegRecord, RefPosition* refPo GenTreeVecCon::Equals(refPosition->treeNode->AsVecCon(), otherTreeNode->AsVecCon()); } + case GT_CNS_MSK: + { + return GenTreeMskCon::Equals(refPosition->treeNode->AsMskCon(), otherTreeNode->AsMskCon()); + } + default: break; } diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index d1fb48fc16d939..266cb47537d50a 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -727,6 +727,29 @@ int LinearScan::BuildNode(GenTree* tree) break; } + case GT_CNS_MSK: + { + GenTreeMskCon* mskCon = tree->AsMskCon(); + + if (mskCon->IsAllBitsSet() || mskCon->IsZero()) + { + // Directly encode constant to instructions. + } + else + { + // Reserve int to load constant from memory (IF_LARGELDC) + buildInternalIntRegisterDefForNode(tree); + buildInternalRegisterUses(); + } + + srcCount = 0; + assert(dstCount == 1); + + RefPosition* def = BuildDef(tree); + def->getInterval()->isConstant = true; + break; + } + case GT_BOX: case GT_COMMA: case GT_QMARK: @@ -1450,6 +1473,7 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou case NI_Sve_PrefetchInt32: case NI_Sve_PrefetchInt64: case NI_Sve_ExtractVector: + case NI_Sve_TrigonometricMultiplyAddCoefficient: needBranchTargetReg = !intrin.op3->isContainedIntOrIImmed(); break; @@ -1919,18 +1943,44 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou } else { - assert((numArgs == 1) || (numArgs == 2) || (numArgs == 3)); + const bool embHasImmediateOperand = HWIntrinsicInfo::HasImmediateOperand(intrinEmb.id); + assert((numArgs == 1) || (numArgs == 2) || (numArgs == 3) || (embHasImmediateOperand && (numArgs == 4))); - // Special handling for ShiftRightArithmeticForDivide: + // Special handling for embedded intrinsics with immediates: // We might need an additional register to hold branch targets into the switch table // that encodes the immediate - if (intrinEmb.id == NI_Sve_ShiftRightArithmeticForDivide) + switch (intrinEmb.id) { - assert(embOp2Node->GetOperandCount() == 2); - if (!embOp2Node->Op(2)->isContainedIntOrIImmed()) - { - buildInternalIntRegisterDefForNode(embOp2Node); - } + case NI_Sve_ShiftRightArithmeticForDivide: + assert(embHasImmediateOperand); + assert(numArgs == 2); + if (!embOp2Node->Op(2)->isContainedIntOrIImmed()) + { + buildInternalIntRegisterDefForNode(embOp2Node); + } + break; + + case NI_Sve_AddRotateComplex: + assert(embHasImmediateOperand); + assert(numArgs == 3); + if (!embOp2Node->Op(3)->isContainedIntOrIImmed()) + { + buildInternalIntRegisterDefForNode(embOp2Node); + } + break; + + case NI_Sve_MultiplyAddRotateComplex: + assert(embHasImmediateOperand); + assert(numArgs == 4); + if (!embOp2Node->Op(4)->isContainedIntOrIImmed()) + { + buildInternalIntRegisterDefForNode(embOp2Node); + } + break; + + default: + assert(!embHasImmediateOperand); + break; } tgtPrefUse = BuildUse(embOp2Node->Op(1)); diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 9fb350800686a8..9352b819d236cd 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -152,6 +152,7 @@ int LinearScan::BuildNode(GenTree* tree) case GT_CNS_LNG: case GT_CNS_DBL: case GT_CNS_VEC: + case GT_CNS_MSK: { srcCount = 0; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index d44ace570f921a..8e510cdb99fc11 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -9917,6 +9917,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) default: { +#if defined(FEATURE_MASKED_HW_INTRINSICS) bool isScalar = false; genTreeOps actualOper = node->GetOperForHWIntrinsicId(&isScalar); genTreeOps oper = actualOper; @@ -10062,6 +10063,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) INDEBUG(node->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); return node; } +#endif // FEATURE_MASKED_HW_INTRINSICS break; } } @@ -11391,7 +11393,7 @@ GenTree* Compiler::fgMorphHWIntrinsic(GenTreeHWIntrinsic* tree) { operand->SetDoNotCSE(); } - else if (canBenefitFromConstantProp && operand->IsVectorConst()) + else if (canBenefitFromConstantProp && operand->IsCnsVec()) { if (tree->ShouldConstantProp(operand, operand->AsVecCon())) { @@ -11436,12 +11438,40 @@ GenTree* Compiler::fgMorphHWIntrinsic(GenTreeHWIntrinsic* tree) assert(tree->GetOperandCount() == 2); GenTree*& op1 = tree->Op(1); - if (op1->IsVectorConst()) + if (op1->IsCnsVec()) { // Move constant vectors from op1 to op2 for commutative operations std::swap(op1, tree->Op(2)); } } + else if (!optValnumCSE_phase) + { + bool isScalar = false; + genTreeOps oper = tree->GetOperForHWIntrinsicId(&isScalar); + + // We can't handle scalar operations since they can copy upper bits from op1 + if (GenTree::OperIsCompare(oper) && !isScalar) + { + assert(tree->GetOperandCount() == 2); + + GenTree* op1 = tree->Op(1); + GenTree* op2 = tree->Op(2); + + if (op1->IsCnsVec()) + { + // Move constant vectors from op1 to op2 for comparison operations + + var_types simdBaseType = tree->GetSimdBaseType(); + unsigned simdSize = tree->GetSimdSize(); + + genTreeOps newOper = GenTree::SwapRelop(oper); + NamedIntrinsic newId = GenTreeHWIntrinsic::GetHWIntrinsicIdForCmpOp(this, newOper, op2, op1, + simdBaseType, simdSize, false); + + tree->ResetHWIntrinsicId(newId, op2, op1); + } + } + } // Try to fold it, maybe we get lucky, GenTree* foldedTree = gtFoldExpr(tree); diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 0ec8fd2496ba38..93e4ce7893f98b 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -13,6 +13,8 @@ enum NamedIntrinsic : unsigned short { NI_Illegal = 0, + NI_System_ArgumentNullException_ThrowIfNull, + NI_System_Enum_HasFlag, NI_System_BitConverter_DoubleToInt64Bits, diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index b3d8e2610168a1..f633a5e3272bb5 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -1878,7 +1878,7 @@ bool CSE_HeuristicCommon::CanConsiderTree(GenTree* tree, bool isReturn) // If we have a simple helper call with no other persistent side-effects // then we allow this tree to be a CSE candidate // - if (m_pCompiler->gtTreeHasSideEffects(tree, GTF_PERSISTENT_SIDE_EFFECTS | GTF_IS_IN_CSE)) + if (m_pCompiler->gtTreeHasSideEffects(tree, GTF_PERSISTENT_SIDE_EFFECTS, /* ignoreCctors */ true)) { return false; } @@ -1906,6 +1906,7 @@ bool CSE_HeuristicCommon::CanConsiderTree(GenTree* tree, bool isReturn) case GT_CNS_DBL: case GT_CNS_STR: case GT_CNS_VEC: + case GT_CNS_MSK: break; case GT_ARR_ELEM: @@ -5131,8 +5132,7 @@ void CSE_HeuristicCommon::PerformCSE(CSE_Candidate* successfulCandidate) // exp->gtCSEnum = NO_CSE; // clear the gtCSEnum field - GenTree* sideEffList = nullptr; - m_pCompiler->gtExtractSideEffList(exp, &sideEffList, GTF_PERSISTENT_SIDE_EFFECTS | GTF_IS_IN_CSE); + GenTree* sideEffList = m_pCompiler->optExtractSideEffectsForCSE(exp); // If we have any side effects or extracted CSE defs then we need to create a GT_COMMA tree instead // @@ -5414,6 +5414,113 @@ void CSE_HeuristicCommon::ConsiderCandidates() } } +//------------------------------------------------------------------------ +// optExtractSideEffectsForCSE: Extract side effects from a tree that is going +// to be CSE'd. This requires unmarking CSE uses and preserving CSE defs as if +// they were side effects. +// +// Parameters: +// tree - The tree containing side effects +// +// Return Value: +// Tree of side effects. +// +// Remarks: +// Unlike gtExtractSideEffList, this considers CSE defs to be side effects +// and also unmarks CSE uses as it proceeds. Additionally, for CSE we are ok +// with not treating cctor invocations as side effects because we have +// already handled those specially during CSE. +// +GenTree* Compiler::optExtractSideEffectsForCSE(GenTree* tree) +{ + class Extractor final : public GenTreeVisitor + { + GenTree* m_result = nullptr; + + public: + enum + { + DoPreOrder = true, + UseExecutionOrder = true + }; + + GenTree* GetResult() + { + return m_result; + } + + Extractor(Compiler* compiler) + : GenTreeVisitor(compiler) + { + } + + fgWalkResult PreOrderVisit(GenTree** use, GenTree* user) + { + GenTree* node = *use; + + if (m_compiler->gtTreeHasSideEffects(node, GTF_PERSISTENT_SIDE_EFFECTS, /* ignoreCctors */ true)) + { + if (m_compiler->gtNodeHasSideEffects(node, GTF_PERSISTENT_SIDE_EFFECTS, /* ignoreCctors */ true)) + { + Append(node); + return Compiler::WALK_SKIP_SUBTREES; + } + + // Generally all GT_CALL nodes are considered to have side-effects. + // So if we get here it must be a helper call that we decided it does + // not have side effects that we needed to keep. + assert(!node->OperIs(GT_CALL) || node->AsCall()->IsHelperCall()); + } + + // We also need to unmark CSE nodes. This will fail for CSE defs, + // those need to be extracted as if they're side effects. + if (m_compiler->optUnmarkCSE(node)) + { + // The call to optUnmarkCSE(node) should have cleared any CSE info. + assert(!IS_CSE_INDEX(node->gtCSEnum)); + return Compiler::WALK_CONTINUE; + } + + assert(IS_CSE_DEF(node->gtCSEnum)); +#ifdef DEBUG + if (m_compiler->verbose) + { + printf("Preserving the CSE def #%02d at ", GET_CSE_INDEX(node->gtCSEnum)); + m_compiler->printTreeID(node); + } +#endif + Append(node); + return Compiler::WALK_SKIP_SUBTREES; + } + + void Append(GenTree* node) + { + if (m_result == nullptr) + { + m_result = node; + return; + } + + GenTree* comma = m_compiler->gtNewOperNode(GT_COMMA, TYP_VOID, m_result, node); + + // Set the ValueNumber 'gtVNPair' for the new GT_COMMA node + // + if ((m_compiler->vnStore != nullptr) && m_result->gtVNPair.BothDefined() && node->gtVNPair.BothDefined()) + { + ValueNumPair op1Exceptions = m_compiler->vnStore->VNPExceptionSet(m_result->gtVNPair); + comma->gtVNPair = m_compiler->vnStore->VNPWithExc(node->gtVNPair, op1Exceptions); + } + + m_result = comma; + } + }; + + Extractor extractor(this); + extractor.WalkTree(&tree, nullptr); + + return extractor.GetResult(); +} + //------------------------------------------------------------------------ // optValnumCSE_Heuristic: Perform common sub-expression elimination // based on profitabiliy heuristic diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index aa4bd68dcd0dda..ad1c0a3be01d5c 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -103,7 +103,7 @@ void Rationalizer::RewriteNodeAsCall(GenTree** use, if (varTypeIsMask(operand->TypeGet())) { -#if defined(FEATURE_HW_INTRINSICS) +#if defined(FEATURE_MASKED_HW_INTRINSICS) // No managed call takes TYP_MASK, so convert it back to a TYP_SIMD unsigned simdSize; @@ -164,7 +164,7 @@ void Rationalizer::RewriteNodeAsCall(GenTree** use, if (varTypeIsMask(tree->TypeGet())) { -#if defined(FEATURE_HW_INTRINSICS) +#if defined(FEATURE_MASKED_HW_INTRINSICS) // No managed call returns TYP_MASK, so convert it from a TYP_SIMD unsigned simdSize; @@ -340,7 +340,7 @@ void Rationalizer::RewriteHWIntrinsicAsUserCall(GenTree** use, ArrayStackIsVectorConst() && comp->IsValidForShuffle(op2->AsVecCon(), simdSize, simdBaseType)) + if (op2->IsCnsVec() && comp->IsValidForShuffle(op2->AsVecCon(), simdSize, simdBaseType)) { result = comp->gtNewSimdShuffleNode(retType, op1, op2, simdBaseJitType, simdSize); } diff --git a/src/coreclr/jit/simd.h b/src/coreclr/jit/simd.h index 22f773a4d6f339..f1294cb1a95473 100644 --- a/src/coreclr/jit/simd.h +++ b/src/coreclr/jit/simd.h @@ -429,6 +429,136 @@ TBase EvaluateUnaryScalar(genTreeOps oper, TBase arg0) } } +#if defined(FEATURE_MASKED_HW_INTRINSICS) +template +void EvaluateUnaryMask(genTreeOps oper, bool scalar, unsigned simdSize, simdmask_t* result, const simdmask_t& arg0) +{ + uint32_t count = simdSize / sizeof(TBase); + +#if defined(TARGET_XARCH) + // For xarch we have count sequential bits, but an 8 count minimum + + if (count < 8) + { + count = 8; + } + assert((count == 8) || (count == 16) || (count == 32) || (count == 64)); + + uint64_t bitMask = static_cast((static_cast(1) << count) - 1); +#elif defined(TARGET_ARM64) + // For Arm64 we have count total bits to write, but they are sizeof(TBase) bits apart + uint64_t bitMask; + + switch (sizeof(TBase)) + { + case 1: + { + bitMask = 0xFFFFFFFFFFFFFFFF; + break; + } + + case 2: + { + bitMask = 0x5555555555555555; + break; + } + + case 4: + { + bitMask = 0x1111111111111111; + break; + } + + case 8: + { + bitMask = 0x0101010101010101; + break; + } + + default: + { + unreached(); + } + } +#else +#error Unsupported platform +#endif + + uint64_t arg0Value; + memcpy(&arg0Value, &arg0.u64[0], sizeof(simdmask_t)); + + // We're only considering these bits + arg0Value &= bitMask; + + uint64_t resultValue = 0; + + switch (oper) + { + case GT_NOT: + { + resultValue = ~arg0Value; + break; + } + + default: + { + unreached(); + } + } + + resultValue &= bitMask; + + if (resultValue == bitMask) + { + // Output is equivalent to AllBitsSet, so normalize + memset(&resultValue, 0xFF, sizeof(uint64_t)); + } + memcpy(&result->u64[0], &resultValue, sizeof(uint64_t)); +} + +inline void EvaluateUnaryMask( + genTreeOps oper, bool scalar, var_types baseType, unsigned simdSize, simdmask_t* result, const simdmask_t& arg0) +{ + switch (baseType) + { + case TYP_FLOAT: + case TYP_INT: + case TYP_UINT: + { + EvaluateUnaryMask(oper, scalar, simdSize, result, arg0); + break; + } + + case TYP_DOUBLE: + case TYP_LONG: + case TYP_ULONG: + { + EvaluateUnaryMask(oper, scalar, simdSize, result, arg0); + break; + } + + case TYP_BYTE: + case TYP_UBYTE: + { + EvaluateUnaryMask(oper, scalar, simdSize, result, arg0); + break; + } + + case TYP_SHORT: + case TYP_USHORT: + { + EvaluateUnaryMask(oper, scalar, simdSize, result, arg0); + break; + } + + default: + { + unreached(); + } + } +} +#endif // FEATURE_MASKED_HW_INTRINSICS + template void EvaluateUnarySimd(genTreeOps oper, bool scalar, TSimd* result, const TSimd& arg0) { @@ -841,6 +971,164 @@ TBase EvaluateBinaryScalar(genTreeOps oper, TBase arg0, TBase arg1) } } +#if defined(FEATURE_MASKED_HW_INTRINSICS) +template +void EvaluateBinaryMask( + genTreeOps oper, bool scalar, unsigned simdSize, simdmask_t* result, const simdmask_t& arg0, const simdmask_t& arg1) +{ + uint32_t count = simdSize / sizeof(TBase); + +#if defined(TARGET_XARCH) + // For xarch we have count sequential bits, but an 8 count minimum + + if (count < 8) + { + count = 8; + } + assert((count == 8) || (count == 16) || (count == 32) || (count == 64)); + + uint64_t bitMask = static_cast((static_cast(1) << count) - 1); +#elif defined(TARGET_ARM64) + // For Arm64 we have count total bits to write, but they are sizeof(TBase) bits apart + uint64_t bitMask; + + switch (sizeof(TBase)) + { + case 1: + { + bitMask = 0xFFFFFFFFFFFFFFFF; + break; + } + + case 2: + { + bitMask = 0x5555555555555555; + break; + } + + case 4: + { + bitMask = 0x1111111111111111; + break; + } + + case 8: + { + bitMask = 0x0101010101010101; + break; + } + + default: + { + unreached(); + } + } +#else +#error Unsupported platform +#endif + + uint64_t arg0Value; + memcpy(&arg0Value, &arg0.u64[0], sizeof(simdmask_t)); + + uint64_t arg1Value; + memcpy(&arg1Value, &arg1.u64[0], sizeof(simdmask_t)); + + // We're only considering these bits + arg0Value &= bitMask; + arg1Value &= bitMask; + + uint64_t resultValue = 0; + + switch (oper) + { + case GT_AND_NOT: + { + resultValue = arg0Value & ~arg1Value; + break; + } + + case GT_AND: + { + resultValue = arg0Value & arg1Value; + break; + } + + case GT_OR: + { + resultValue = arg0Value | arg1Value; + break; + } + + case GT_XOR: + { + resultValue = arg0Value ^ arg1Value; + break; + } + + default: + { + unreached(); + } + } + + resultValue &= bitMask; + + if (resultValue == bitMask) + { + // Output is equivalent to AllBitsSet, so normalize + memset(&resultValue, 0xFF, sizeof(uint64_t)); + } + memcpy(&result->u64[0], &resultValue, sizeof(uint64_t)); +} + +inline void EvaluateBinaryMask(genTreeOps oper, + bool scalar, + var_types baseType, + unsigned simdSize, + simdmask_t* result, + const simdmask_t& arg0, + const simdmask_t& arg1) +{ + switch (baseType) + { + case TYP_FLOAT: + case TYP_INT: + case TYP_UINT: + { + EvaluateBinaryMask(oper, scalar, simdSize, result, arg0, arg1); + break; + } + + case TYP_DOUBLE: + case TYP_LONG: + case TYP_ULONG: + { + EvaluateBinaryMask(oper, scalar, simdSize, result, arg0, arg1); + break; + } + + case TYP_BYTE: + case TYP_UBYTE: + { + EvaluateBinaryMask(oper, scalar, simdSize, result, arg0, arg1); + break; + } + + case TYP_SHORT: + case TYP_USHORT: + { + EvaluateBinaryMask(oper, scalar, simdSize, result, arg0, arg1); + break; + } + + default: + { + unreached(); + } + } +} +#endif // FEATURE_MASKED_HW_INTRINSICS + template void EvaluateBinarySimd(genTreeOps oper, bool scalar, TSimd* result, const TSimd& arg0, const TSimd& arg1) { @@ -1121,6 +1409,173 @@ void BroadcastConstantToSimd(TSimd* result, TBase arg0) } } +#if defined(FEATURE_MASKED_HW_INTRINSICS) +template +void EvaluateSimdCvtMaskToVector(TSimd* result, simdmask_t arg0) +{ + uint32_t count = sizeof(TSimd) / sizeof(TBase); + + uint64_t mask; + memcpy(&mask, &arg0.u8[0], sizeof(uint64_t)); + + for (uint32_t i = 0; i < count; i++) + { + bool isSet; + +#if defined(TARGET_XARCH) + // For xarch we have count sequential bits to read + // setting the result element to AllBitsSet or Zero + // depending on the corresponding mask bit + + isSet = ((mask >> i) & 1) != 0; +#elif defined(TARGET_ARM64) + // For Arm64 we have count total bits to read, but + // they are sizeof(TBase) bits apart. We still set + // the result element to AllBitsSet or Zero depending + // on the corresponding mask bit + + isSet = ((mask >> (i * sizeof(TBase))) & 1) != 0; +#else + unreached(); +#endif + + TBase output; + + if (isSet) + { + memset(&output, 0xFF, sizeof(TBase)); + } + else + { + memset(&output, 0x00, sizeof(TBase)); + } + + memcpy(&result->u8[i * sizeof(TBase)], &output, sizeof(TBase)); + } +} + +template +void EvaluateSimdCvtMaskToVector(var_types baseType, TSimd* result, simdmask_t arg0) +{ + switch (baseType) + { + case TYP_FLOAT: + case TYP_INT: + case TYP_UINT: + { + EvaluateSimdCvtMaskToVector(result, arg0); + break; + } + + case TYP_DOUBLE: + case TYP_LONG: + case TYP_ULONG: + { + EvaluateSimdCvtMaskToVector(result, arg0); + break; + } + + case TYP_BYTE: + case TYP_UBYTE: + { + EvaluateSimdCvtMaskToVector(result, arg0); + break; + } + + case TYP_SHORT: + case TYP_USHORT: + { + EvaluateSimdCvtMaskToVector(result, arg0); + break; + } + + default: + { + unreached(); + } + } +} + +template +void EvaluateSimdCvtVectorToMask(simdmask_t* result, TSimd arg0) +{ + uint32_t count = sizeof(TSimd) / sizeof(TBase); + uint64_t mask = 0; + + TBase mostSignificantBit = static_cast(1) << ((sizeof(TBase) * 8) - 1); + + for (uint32_t i = 0; i < count; i++) + { + TBase input0; + memcpy(&input0, &arg0.u8[i * sizeof(TBase)], sizeof(TBase)); + + if ((input0 & mostSignificantBit) != 0) + { +#if defined(TARGET_XARCH) + // For xarch we have count sequential bits to write + // depending on if the corresponding the input element + // has its most significant bit set + + mask |= static_cast(1) << i; +#elif defined(TARGET_ARM64) + // For Arm64 we have count total bits to write, but + // they are sizeof(TBase) bits apart. We still set + // depending on if the corresponding input element + // has its most significant bit set + + mask |= static_cast(1) << (i * sizeof(TBase)); +#else + unreached(); +#endif + } + } + + memcpy(&result->u8[0], &mask, sizeof(uint64_t)); +} + +template +void EvaluateSimdCvtVectorToMask(var_types baseType, simdmask_t* result, TSimd arg0) +{ + switch (baseType) + { + case TYP_FLOAT: + case TYP_INT: + case TYP_UINT: + { + EvaluateSimdCvtVectorToMask(result, arg0); + break; + } + + case TYP_DOUBLE: + case TYP_LONG: + case TYP_ULONG: + { + EvaluateSimdCvtVectorToMask(result, arg0); + break; + } + + case TYP_BYTE: + case TYP_UBYTE: + { + EvaluateSimdCvtVectorToMask(result, arg0); + break; + } + + case TYP_SHORT: + case TYP_USHORT: + { + EvaluateSimdCvtVectorToMask(result, arg0); + break; + } + + default: + { + unreached(); + } + } +} +#endif // FEATURE_MASKED_HW_INTRINSICS + #ifdef FEATURE_SIMD #ifdef TARGET_XARCH diff --git a/src/coreclr/jit/typelist.h b/src/coreclr/jit/typelist.h index bf5acb5ee014a5..865c177bc7bc32 100644 --- a/src/coreclr/jit/typelist.h +++ b/src/coreclr/jit/typelist.h @@ -64,9 +64,9 @@ DEF_TP(SIMD16 ,"simd16" , TYP_SIMD16, 16,16, 16, 4,16, VTR_FLOAT, available DEF_TP(SIMD32 ,"simd32" , TYP_SIMD32, 32,32, 32, 8,16, VTR_FLOAT, availableDoubleRegs, RBM_FLT_CALLEE_SAVED, RBM_FLT_CALLEE_TRASH, VTF_S|VTF_VEC) DEF_TP(SIMD64 ,"simd64" , TYP_SIMD64, 64,64, 64, 16,16, VTR_FLOAT, availableDoubleRegs, RBM_FLT_CALLEE_SAVED, RBM_FLT_CALLEE_TRASH, VTF_S|VTF_VEC) #endif // TARGET_XARCH -#if defined(TARGET_XARCH) || defined(TARGET_ARM64) +#if defined(FEATURE_MASKED_HW_INTRINSICS) DEF_TP(MASK ,"mask" , TYP_MASK, 8, 8, 8, 2, 8, VTR_MASK, availableMaskRegs, RBM_MSK_CALLEE_SAVED, RBM_MSK_CALLEE_TRASH, VTF_S) -#endif // TARGET_XARCH || TARGET_ARM64 +#endif // FEATURE_MASKED_HW_INTRINSICS #endif // FEATURE_SIMD DEF_TP(UNKNOWN ,"unknown" ,TYP_UNKNOWN, 0, 0, 0, 0, 0, VTR_INT, availableIntRegs, RBM_INT_CALLEE_SAVED, RBM_INT_CALLEE_TRASH, VTF_ANY) diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 1b795678e7b000..f3a21beb95e38f 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -2334,13 +2334,6 @@ ValueNum ValueNumStore::VNBroadcastForSimdType(var_types simdType, var_types sim #endif // TARGET_XARCH -#if defined(FEATURE_MASKED_HW_INTRINSICS) - case TYP_MASK: - { - unreached(); - } -#endif // FEATURE_MASKED_HW_INTRINSICS - default: { unreached(); @@ -2408,13 +2401,6 @@ bool ValueNumStore::VNIsVectorNaN(var_types simdType, var_types simdBaseType, Va } #endif // TARGET_XARCH -#if defined(FEATURE_MASKED_HW_INTRINSICS) - case TYP_MASK: - { - unreached(); - } -#endif // FEATURE_MASKED_HW_INTRINSICS - default: { unreached(); @@ -2479,11 +2465,6 @@ bool ValueNumStore::VNIsVectorNegativeZero(var_types simdType, var_types simdBas memcpy(&vector, &tmp, genTypeSize(simdType)); break; } - - case TYP_MASK: - { - unreached(); - } #endif // TARGET_XARCH default: @@ -7553,11 +7534,114 @@ ValueNum EvaluateSimdGetElement( } } +ValueNum EvaluateSimdCvtMaskToVector(ValueNumStore* vns, var_types simdType, var_types baseType, ValueNum arg0VN) +{ + simdmask_t arg0 = vns->GetConstantSimdMask(arg0VN); + + switch (simdType) + { + case TYP_SIMD8: + { + simd8_t result = {}; + EvaluateSimdCvtMaskToVector(baseType, &result, arg0); + return vns->VNForSimd8Con(result); + } + + case TYP_SIMD12: + { + simd12_t result = {}; + EvaluateSimdCvtMaskToVector(baseType, &result, arg0); + return vns->VNForSimd12Con(result); + } + + case TYP_SIMD16: + { + simd16_t result = {}; + EvaluateSimdCvtMaskToVector(baseType, &result, arg0); + return vns->VNForSimd16Con(result); + } + +#if defined(TARGET_XARCH) + case TYP_SIMD32: + { + simd32_t result = {}; + EvaluateSimdCvtMaskToVector(baseType, &result, arg0); + return vns->VNForSimd32Con(result); + } + + case TYP_SIMD64: + { + simd64_t result = {}; + EvaluateSimdCvtMaskToVector(baseType, &result, arg0); + return vns->VNForSimd64Con(result); + } +#endif // TARGET_XARCH + + default: + { + unreached(); + } + } +} + +ValueNum EvaluateSimdCvtVectorToMask(ValueNumStore* vns, var_types simdType, var_types baseType, ValueNum arg0VN) +{ + simdmask_t result = {}; + + switch (simdType) + { + case TYP_SIMD8: + { + simd8_t arg0 = GetConstantSimd8(vns, baseType, arg0VN); + EvaluateSimdCvtVectorToMask(baseType, &result, arg0); + break; + } + + case TYP_SIMD12: + { + simd12_t arg0 = GetConstantSimd12(vns, baseType, arg0VN); + EvaluateSimdCvtVectorToMask(baseType, &result, arg0); + break; + } + + case TYP_SIMD16: + { + simd16_t arg0 = GetConstantSimd16(vns, baseType, arg0VN); + EvaluateSimdCvtVectorToMask(baseType, &result, arg0); + break; + } + +#if defined(TARGET_XARCH) + case TYP_SIMD32: + { + simd32_t arg0 = GetConstantSimd32(vns, baseType, arg0VN); + EvaluateSimdCvtVectorToMask(baseType, &result, arg0); + break; + } + + case TYP_SIMD64: + { + simd64_t arg0 = GetConstantSimd64(vns, baseType, arg0VN); + EvaluateSimdCvtVectorToMask(baseType, &result, arg0); + break; + } +#endif // TARGET_XARCH + + default: + { + unreached(); + } + } + + return vns->VNForSimdMaskCon(result); +} + ValueNum ValueNumStore::EvalHWIntrinsicFunUnary( GenTreeHWIntrinsic* tree, VNFunc func, ValueNum arg0VN, bool encodeResultType, ValueNum resultTypeVN) { var_types type = tree->TypeGet(); var_types baseType = tree->GetSimdBaseType(); + unsigned simdSize = tree->GetSimdSize(); NamedIntrinsic ni = tree->GetHWIntrinsicId(); if (IsVNConstant(arg0VN)) @@ -7567,8 +7651,25 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunUnary( if (oper != GT_NONE) { + if (varTypeIsMask(type)) + { + simdmask_t arg0 = GetConstantSimdMask(arg0VN); + + simdmask_t result = {}; + EvaluateUnaryMask(oper, isScalar, baseType, simdSize, &result, arg0); + return VNForSimdMaskCon(result); + } return EvaluateUnarySimd(this, oper, isScalar, type, baseType, arg0VN); } + else if (tree->OperIsConvertMaskToVector()) + { + return EvaluateSimdCvtMaskToVector(this, type, baseType, arg0VN); + } + else if (tree->OperIsConvertVectorToMask()) + { + var_types simdType = Compiler::getSIMDTypeForSize(simdSize); + return EvaluateSimdCvtVectorToMask(this, simdType, baseType, arg0VN); + } switch (ni) { @@ -7886,6 +7987,7 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(GenTreeHWIntrinsic* tree, { var_types type = tree->TypeGet(); var_types baseType = tree->GetSimdBaseType(); + unsigned simdSize = tree->GetSimdSize(); NamedIntrinsic ni = tree->GetHWIntrinsicId(); ValueNum cnsVN = NoVN; @@ -7922,6 +8024,25 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(GenTreeHWIntrinsic* tree, // We shouldn't find AND_NOT nodes since it should only be produced in lowering assert(oper != GT_AND_NOT); + if (varTypeIsMask(type)) + { + if (varTypeIsMask(TypeOfVN(arg0VN))) + { + simdmask_t arg0 = GetConstantSimdMask(arg0VN); + simdmask_t arg1 = GetConstantSimdMask(arg1VN); + + simdmask_t result = {}; + EvaluateBinaryMask(oper, isScalar, baseType, simdSize, &result, arg0, arg1); + return VNForSimdMaskCon(result); + } + else + { + var_types simdType = Compiler::getSIMDTypeForSize(simdSize); + ValueNum simdResult = EvaluateBinarySimd(this, oper, isScalar, simdType, baseType, arg0VN, arg1VN); + return EvaluateSimdCvtVectorToMask(this, simdType, baseType, simdResult); + } + } + #if defined(TARGET_XARCH) if ((oper == GT_LSH) || (oper == GT_RSH) || (oper == GT_RSZ)) { @@ -10976,7 +11097,7 @@ void Compiler::fgValueNumberTreeConst(GenTree* tree) case TYP_MASK: { simdmask_t simdmaskVal; - memcpy(&simdmaskVal, &tree->AsVecCon()->gtSimdVal, sizeof(simdmask_t)); + memcpy(&simdmaskVal, &tree->AsMskCon()->gtSimdMaskVal, sizeof(simdmask_t)); tree->gtVNPair.SetBoth(vnStore->VNForSimdMaskCon(simdmaskVal)); break; diff --git a/src/coreclr/jit/vartype.h b/src/coreclr/jit/vartype.h index 642ab159360350..34668fd545b713 100644 --- a/src/coreclr/jit/vartype.h +++ b/src/coreclr/jit/vartype.h @@ -327,7 +327,7 @@ inline bool varTypeUsesMaskReg(T vt) // However, we only have one type that uses VTR_MASK today // and so its quite a bit cheaper to just check that directly -#if defined(FEATURE_SIMD) && (defined(TARGET_XARCH) || defined(TARGET_ARM64)) +#if defined(FEATURE_MASKED_HW_INTRINSICS) assert((TypeGet(vt) == TYP_MASK) || (varTypeRegister[TypeGet(vt)] != VTR_MASK)); return TypeGet(vt) == TYP_MASK; #else diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs index fd8094bc702831..e7682e267b3446 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs @@ -706,7 +706,7 @@ private static bool ScanMethodBodyForFieldAccess(MethodIL body, bool write, out } } - internal void ValidateMethodAnnotationsAreSame(MethodDesc method, MethodDesc baseMethod) + internal void ValidateMethodAnnotationsAreSame(MethodDesc method, MethodDesc baseMethod, TypeSystemEntity origin) { method = method.GetTypicalMethodDefinition(); baseMethod = baseMethod.GetTypicalMethodDefinition(); @@ -715,14 +715,14 @@ internal void ValidateMethodAnnotationsAreSame(MethodDesc method, MethodDesc bas GetAnnotations(baseMethod.OwningType).TryGetAnnotation(baseMethod, out var baseMethodAnnotations); if (methodAnnotations.ReturnParameterAnnotation != baseMethodAnnotations.ReturnParameterAnnotation) - LogValidationWarning(method.Signature.ReturnType, baseMethod, method); + LogValidationWarning((method.Signature.ReturnType, method), baseMethod, origin); if (methodAnnotations.ParameterAnnotations != null || baseMethodAnnotations.ParameterAnnotations != null) { if (methodAnnotations.ParameterAnnotations == null) - ValidateMethodParametersHaveNoAnnotations(baseMethodAnnotations.ParameterAnnotations!, method, baseMethod, method); + ValidateMethodParametersHaveNoAnnotations(baseMethodAnnotations.ParameterAnnotations!, method, baseMethod, origin); else if (baseMethodAnnotations.ParameterAnnotations == null) - ValidateMethodParametersHaveNoAnnotations(methodAnnotations.ParameterAnnotations, method, baseMethod, method); + ValidateMethodParametersHaveNoAnnotations(methodAnnotations.ParameterAnnotations, method, baseMethod, origin); else { if (methodAnnotations.ParameterAnnotations.Length != baseMethodAnnotations.ParameterAnnotations.Length) @@ -734,7 +734,7 @@ internal void ValidateMethodAnnotationsAreSame(MethodDesc method, MethodDesc bas LogValidationWarning( (new MethodProxy(method)).GetParameter((ParameterIndex)parameterIndex), (new MethodProxy(baseMethod)).GetParameter((ParameterIndex)parameterIndex), - method); + origin); } } } @@ -742,9 +742,9 @@ internal void ValidateMethodAnnotationsAreSame(MethodDesc method, MethodDesc bas if (methodAnnotations.GenericParameterAnnotations != null || baseMethodAnnotations.GenericParameterAnnotations != null) { if (methodAnnotations.GenericParameterAnnotations == null) - ValidateMethodGenericParametersHaveNoAnnotations(baseMethodAnnotations.GenericParameterAnnotations!, method, baseMethod, method); + ValidateMethodGenericParametersHaveNoAnnotations(baseMethodAnnotations.GenericParameterAnnotations!, method, baseMethod, origin); else if (baseMethodAnnotations.GenericParameterAnnotations == null) - ValidateMethodGenericParametersHaveNoAnnotations(methodAnnotations.GenericParameterAnnotations, method, baseMethod, method); + ValidateMethodGenericParametersHaveNoAnnotations(methodAnnotations.GenericParameterAnnotations, method, baseMethod, origin); else { if (methodAnnotations.GenericParameterAnnotations.Length != baseMethodAnnotations.GenericParameterAnnotations.Length) @@ -757,14 +757,14 @@ internal void ValidateMethodAnnotationsAreSame(MethodDesc method, MethodDesc bas LogValidationWarning( method.Instantiation[genericParameterIndex], baseMethod.Instantiation[genericParameterIndex], - method); + origin); } } } } } - private void ValidateMethodParametersHaveNoAnnotations(DynamicallyAccessedMemberTypes[] parameterAnnotations, MethodDesc method, MethodDesc baseMethod, MethodDesc origin) + private void ValidateMethodParametersHaveNoAnnotations(DynamicallyAccessedMemberTypes[] parameterAnnotations, MethodDesc method, MethodDesc baseMethod, TypeSystemEntity origin) { for (int parameterIndex = 0; parameterIndex < parameterAnnotations.Length; parameterIndex++) { @@ -777,7 +777,7 @@ private void ValidateMethodParametersHaveNoAnnotations(DynamicallyAccessedMember } } - private void ValidateMethodGenericParametersHaveNoAnnotations(DynamicallyAccessedMemberTypes[] genericParameterAnnotations, MethodDesc method, MethodDesc baseMethod, MethodDesc origin) + private void ValidateMethodGenericParametersHaveNoAnnotations(DynamicallyAccessedMemberTypes[] genericParameterAnnotations, MethodDesc method, MethodDesc baseMethod, TypeSystemEntity origin) { for (int genericParameterIndex = 0; genericParameterIndex < genericParameterAnnotations.Length; genericParameterIndex++) { @@ -791,7 +791,7 @@ private void ValidateMethodGenericParametersHaveNoAnnotations(DynamicallyAccesse } } - private void LogValidationWarning(object provider, object baseProvider, MethodDesc origin) + private void LogValidationWarning(object provider, object baseProvider, TypeSystemEntity origin) { switch (provider) { @@ -810,9 +810,9 @@ private void LogValidationWarning(object provider, object baseProvider, MethodDe genericParameterOverride.Name, DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(genericParameterOverride), ((GenericParameterDesc)baseProvider).Name, DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName((GenericParameterDesc)baseProvider)); break; - case TypeDesc: + case (TypeDesc, MethodDesc method): _logger.LogWarning(origin, DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodReturnValueBetweenOverrides, - DiagnosticUtilities.GetMethodSignatureDisplayName(origin), DiagnosticUtilities.GetMethodSignatureDisplayName((MethodDesc)baseProvider)); + DiagnosticUtilities.GetMethodSignatureDisplayName(method), DiagnosticUtilities.GetMethodSignatureDisplayName((MethodDesc)baseProvider)); break; // No fields - it's not possible to have a virtual field and override it default: diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index 10bac5be472fe2..8b8f34ada6c83f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -534,7 +534,8 @@ public sealed override IEnumerable GetConditionalSt result.Add(new CombinedDependencyListEntry(factory.VirtualMethodUse(interfaceMethod), factory.VariantInterfaceMethodUse(typicalInterfaceMethod), "Interface method")); } - factory.MetadataManager.NoteOverridingMethod(interfaceMethod, implMethod); + TypeSystemEntity origin = (implMethod.OwningType != defType) ? defType : null; + factory.MetadataManager.NoteOverridingMethod(interfaceMethod, implMethod, origin); factory.MetadataManager.GetDependenciesForOverridingMethod(ref result, factory, interfaceMethod, implMethod); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GVMDependenciesNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GVMDependenciesNode.cs index 26526cc5628d7a..618d7e68c9b7b1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GVMDependenciesNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GVMDependenciesNode.cs @@ -149,7 +149,8 @@ public override IEnumerable SearchDynamicDependenci else dynamicDependencies.Add(new CombinedDependencyListEntry(factory.GVMDependencies(implementingMethodInstantiation.GetCanonMethodTarget(CanonicalFormKind.Specific)), null, "ImplementingMethodInstantiation")); - factory.MetadataManager.NoteOverridingMethod(_method, implementingMethodInstantiation); + TypeSystemEntity origin = (implementingMethodInstantiation.OwningType != potentialOverrideType) ? potentialOverrideType : null; + factory.MetadataManager.NoteOverridingMethod(_method, implementingMethodInstantiation, origin); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 2aaa93c9efdcc0..835b601cf64964 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -1186,7 +1186,7 @@ public virtual DependencyList GetDependenciesForCustomAttribute(NodeFactory fact return null; } - public virtual void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc overridingMethod) + public virtual void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc overridingMethod, TypeSystemEntity origin = null) { } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 991fe7aa624010..7748b5969ce6d2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -784,7 +784,7 @@ public bool GeneratesAttributeMetadata(TypeDesc attributeType) return true; } - public override void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc overridingMethod) + public override void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc overridingMethod, TypeSystemEntity origin) { baseMethod = baseMethod.GetTypicalMethodDefinition(); overridingMethod = overridingMethod.GetTypicalMethodDefinition(); @@ -792,6 +792,8 @@ public override void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc over if (baseMethod == overridingMethod) return; + origin ??= overridingMethod; + bool baseMethodTypeIsInterface = baseMethod.OwningType.IsInterface; foreach (var requiresAttribute in _requiresAttributeMismatchNameAndId) { @@ -803,7 +805,7 @@ public override void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc over string message = MessageFormat.FormatRequiresAttributeMismatch(overridingMethod.DoesMethodRequire(requiresAttribute.AttributeName, out _), baseMethodTypeIsInterface, requiresAttribute.AttributeName, overridingMethodName, baseMethodName); - Logger.LogWarning(overridingMethod, requiresAttribute.Id, message); + Logger.LogWarning(origin, requiresAttribute.Id, message); } } @@ -811,7 +813,7 @@ public override void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc over bool overridingMethodRequiresDataflow = FlowAnnotations.RequiresVirtualMethodDataflowAnalysis(overridingMethod); if (baseMethodRequiresDataflow || overridingMethodRequiresDataflow) { - FlowAnnotations.ValidateMethodAnnotationsAreSame(overridingMethod, baseMethod); + FlowAnnotations.ValidateMethodAnnotationsAreSame(overridingMethod, baseMethod, origin); } } diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 65b8ea95e544e2..fcb86fc36c2940 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -2975,7 +2975,7 @@ void StackTraceInfo::EnsureKeepAliveArray(PTRARRAYREF *ppKeepAliveArray, size_t { memmoveGCRefs(pNewKeepAliveArray->GetDataPtr(), (*ppKeepAliveArray)->GetDataPtr(), - neededSize * sizeof(Object *)); + (*ppKeepAliveArray)->GetNumComponents() * sizeof(Object *)); } // Update the keepAlive array *ppKeepAliveArray = pNewKeepAliveArray; diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 33b474c4ee3495..31a143db99d0f1 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -235,7 +235,7 @@ - + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/PackageOverrides.txt b/src/installer/pkg/sfx/Microsoft.NETCore.App/PackageOverrides.txt index c4c594f90f86d7..6fff123a00f419 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/PackageOverrides.txt +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/PackageOverrides.txt @@ -1,83 +1,86 @@ -Microsoft.CSharp|4.4.0 +Microsoft.CSharp|4.7.0 +Microsoft.VisualBasic|10.4.0 Microsoft.Win32.Primitives|4.3.0 -Microsoft.Win32.Registry|4.4.0 -runtime.debian.8-x64.runtime.native.System|4.3.0 -runtime.debian.8-x64.runtime.native.System.IO.Compression|4.3.0 -runtime.debian.8-x64.runtime.native.System.Net.Http|4.3.0 -runtime.debian.8-x64.runtime.native.System.Net.Security|4.3.0 -runtime.debian.8-x64.runtime.native.System.Security.Cryptography|4.3.0 -runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.0 -runtime.fedora.23-x64.runtime.native.System|4.3.0 -runtime.fedora.23-x64.runtime.native.System.IO.Compression|4.3.0 -runtime.fedora.23-x64.runtime.native.System.Net.Http|4.3.0 -runtime.fedora.23-x64.runtime.native.System.Net.Security|4.3.0 -runtime.fedora.23-x64.runtime.native.System.Security.Cryptography|4.3.0 -runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.0 -runtime.fedora.24-x64.runtime.native.System|4.3.0 -runtime.fedora.24-x64.runtime.native.System.IO.Compression|4.3.0 -runtime.fedora.24-x64.runtime.native.System.Net.Http|4.3.0 -runtime.fedora.24-x64.runtime.native.System.Net.Security|4.3.0 -runtime.fedora.24-x64.runtime.native.System.Security.Cryptography|4.3.0 -runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.0 -runtime.opensuse.13.2-x64.runtime.native.System|4.3.0 -runtime.opensuse.13.2-x64.runtime.native.System.IO.Compression|4.3.0 -runtime.opensuse.13.2-x64.runtime.native.System.Net.Http|4.3.0 -runtime.opensuse.13.2-x64.runtime.native.System.Net.Security|4.3.0 -runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography|4.3.0 -runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.0 -runtime.opensuse.42.1-x64.runtime.native.System|4.3.0 -runtime.opensuse.42.1-x64.runtime.native.System.IO.Compression|4.3.0 -runtime.opensuse.42.1-x64.runtime.native.System.Net.Http|4.3.0 -runtime.opensuse.42.1-x64.runtime.native.System.Net.Security|4.3.0 -runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography|4.3.0 -runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.0 -runtime.osx.10.10-x64.runtime.native.System|4.3.0 -runtime.osx.10.10-x64.runtime.native.System.IO.Compression|4.3.0 -runtime.osx.10.10-x64.runtime.native.System.Net.Http|4.3.0 -runtime.osx.10.10-x64.runtime.native.System.Net.Security|4.3.0 -runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography|4.3.0 -runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple|4.3.0 -runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.0 -runtime.rhel.7-x64.runtime.native.System|4.3.0 -runtime.rhel.7-x64.runtime.native.System.IO.Compression|4.3.0 -runtime.rhel.7-x64.runtime.native.System.Net.Http|4.3.0 -runtime.rhel.7-x64.runtime.native.System.Net.Security|4.3.0 -runtime.rhel.7-x64.runtime.native.System.Security.Cryptography|4.3.0 -runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.0 -runtime.ubuntu.14.04-x64.runtime.native.System|4.3.0 -runtime.ubuntu.14.04-x64.runtime.native.System.IO.Compression|4.3.0 -runtime.ubuntu.14.04-x64.runtime.native.System.Net.Http|4.3.0 -runtime.ubuntu.14.04-x64.runtime.native.System.Net.Security|4.3.0 -runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography|4.3.0 -runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.0 -runtime.ubuntu.16.04-x64.runtime.native.System|4.3.0 -runtime.ubuntu.16.04-x64.runtime.native.System.IO.Compression|4.3.0 -runtime.ubuntu.16.04-x64.runtime.native.System.Net.Http|4.3.0 -runtime.ubuntu.16.04-x64.runtime.native.System.Net.Security|4.3.0 -runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography|4.3.0 -runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.0 -runtime.ubuntu.16.10-x64.runtime.native.System|4.3.0 -runtime.ubuntu.16.10-x64.runtime.native.System.IO.Compression|4.3.0 -runtime.ubuntu.16.10-x64.runtime.native.System.Net.Http|4.3.0 -runtime.ubuntu.16.10-x64.runtime.native.System.Net.Security|4.3.0 -runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography|4.3.0 -runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.0 +Microsoft.Win32.Registry|5.0.0 +runtime.debian.8-x64.runtime.native.System|4.3.1 +runtime.debian.8-x64.runtime.native.System.IO.Compression|4.3.2 +runtime.debian.8-x64.runtime.native.System.Net.Http|4.3.1 +runtime.debian.8-x64.runtime.native.System.Net.Security|4.3.1 +runtime.debian.8-x64.runtime.native.System.Security.Cryptography|4.3.4 +runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.3 +runtime.fedora.23-x64.runtime.native.System|4.3.1 +runtime.fedora.23-x64.runtime.native.System.IO.Compression|4.3.2 +runtime.fedora.23-x64.runtime.native.System.Net.Http|4.3.1 +runtime.fedora.23-x64.runtime.native.System.Net.Security|4.3.1 +runtime.fedora.23-x64.runtime.native.System.Security.Cryptography|4.3.4 +runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.3 +runtime.fedora.24-x64.runtime.native.System|4.3.1 +runtime.fedora.24-x64.runtime.native.System.IO.Compression|4.3.2 +runtime.fedora.24-x64.runtime.native.System.Net.Http|4.3.1 +runtime.fedora.24-x64.runtime.native.System.Net.Security|4.3.1 +runtime.fedora.24-x64.runtime.native.System.Security.Cryptography|4.3.4 +runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.3 +runtime.opensuse.13.2-x64.runtime.native.System|4.3.1 +runtime.opensuse.13.2-x64.runtime.native.System.IO.Compression|4.3.2 +runtime.opensuse.13.2-x64.runtime.native.System.Net.Http|4.3.1 +runtime.opensuse.13.2-x64.runtime.native.System.Net.Security|4.3.1 +runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography|4.3.4 +runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.3 +runtime.opensuse.42.1-x64.runtime.native.System|4.3.1 +runtime.opensuse.42.1-x64.runtime.native.System.IO.Compression|4.3.2 +runtime.opensuse.42.1-x64.runtime.native.System.Net.Http|4.3.1 +runtime.opensuse.42.1-x64.runtime.native.System.Net.Security|4.3.1 +runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography|4.3.4 +runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.3 +runtime.osx.10.10-x64.runtime.native.System|4.3.1 +runtime.osx.10.10-x64.runtime.native.System.IO.Compression|4.3.2 +runtime.osx.10.10-x64.runtime.native.System.Net.Http|4.3.1 +runtime.osx.10.10-x64.runtime.native.System.Net.Security|4.3.1 +runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography|4.3.4 +runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple|4.3.1 +runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.3 +runtime.rhel.7-x64.runtime.native.System|4.3.1 +runtime.rhel.7-x64.runtime.native.System.IO.Compression|4.3.2 +runtime.rhel.7-x64.runtime.native.System.Net.Http|4.3.1 +runtime.rhel.7-x64.runtime.native.System.Net.Security|4.3.1 +runtime.rhel.7-x64.runtime.native.System.Security.Cryptography|4.3.4 +runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.3 +runtime.ubuntu.14.04-x64.runtime.native.System|4.3.1 +runtime.ubuntu.14.04-x64.runtime.native.System.IO.Compression|4.3.2 +runtime.ubuntu.14.04-x64.runtime.native.System.Net.Http|4.3.1 +runtime.ubuntu.14.04-x64.runtime.native.System.Net.Security|4.3.1 +runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography|4.3.4 +runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.3 +runtime.ubuntu.16.04-x64.runtime.native.System|4.3.1 +runtime.ubuntu.16.04-x64.runtime.native.System.IO.Compression|4.3.2 +runtime.ubuntu.16.04-x64.runtime.native.System.Net.Http|4.3.1 +runtime.ubuntu.16.04-x64.runtime.native.System.Net.Security|4.3.1 +runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography|4.3.4 +runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.3 +runtime.ubuntu.16.10-x64.runtime.native.System|4.3.1 +runtime.ubuntu.16.10-x64.runtime.native.System.IO.Compression|4.3.2 +runtime.ubuntu.16.10-x64.runtime.native.System.Net.Http|4.3.1 +runtime.ubuntu.16.10-x64.runtime.native.System.Net.Security|4.3.1 +runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography|4.3.4 +runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl|4.3.3 System.AppContext|4.3.0 -System.Buffers|4.4.0 +System.Buffers|5.0.0 System.Collections|4.3.0 System.Collections.Concurrent|4.3.0 -System.Collections.Immutable|1.4.0 +System.Collections.Immutable|9.0.0 System.Collections.NonGeneric|4.3.0 System.Collections.Specialized|4.3.0 System.ComponentModel|4.3.0 +System.ComponentModel.Annotations|4.3.0 System.ComponentModel.EventBasedAsync|4.3.0 System.ComponentModel.Primitives|4.3.0 System.ComponentModel.TypeConverter|4.3.0 -System.Console|4.3.0 +System.Console|4.3.1 System.Data.Common|4.3.0 +System.Data.DataSetExtensions|4.4.0 System.Diagnostics.Contracts|4.3.0 System.Diagnostics.Debug|4.3.0 -System.Diagnostics.DiagnosticSource|4.4.0 +System.Diagnostics.DiagnosticSource|9.0.0 System.Diagnostics.FileVersionInfo|4.3.0 System.Diagnostics.Process|4.3.0 System.Diagnostics.StackTrace|4.3.0 @@ -85,7 +88,10 @@ System.Diagnostics.TextWriterTraceListener|4.3.0 System.Diagnostics.Tools|4.3.0 System.Diagnostics.TraceSource|4.3.0 System.Diagnostics.Tracing|4.3.0 +System.Drawing.Primitives|4.3.0 System.Dynamic.Runtime|4.3.0 +System.Formats.Asn1|9.0.0 +System.Formats.Tar|9.0.0 System.Globalization|4.3.0 System.Globalization.Calendars|4.3.0 System.Globalization.Extensions|4.3.0 @@ -94,36 +100,52 @@ System.IO.Compression|4.3.0 System.IO.Compression.ZipFile|4.3.0 System.IO.FileSystem|4.3.0 System.IO.FileSystem.AccessControl|4.4.0 -System.IO.FileSystem.DriveInfo|4.3.0 +System.IO.FileSystem.DriveInfo|4.3.1 System.IO.FileSystem.Primitives|4.3.0 System.IO.FileSystem.Watcher|4.3.0 System.IO.IsolatedStorage|4.3.0 System.IO.MemoryMappedFiles|4.3.0 System.IO.Pipes|4.3.0 +System.IO.Pipes.AccessControl|5.0.0 +System.IO.Pipelines|9.0.0 System.IO.UnmanagedMemoryStream|4.3.0 System.Linq|4.3.0 System.Linq.Expressions|4.3.0 +System.Linq.Parallel|4.3.0 System.Linq.Queryable|4.3.0 -System.Net.Http|4.3.0 +System.Memory|5.0.0 +System.Net.Http|4.3.4 +System.Net.Http.Json|9.0.0 System.Net.NameResolution|4.3.0 -System.Net.Primitives|4.3.0 +System.Net.NetworkInformation|4.3.0 +System.Net.Ping|4.3.0 +System.Net.Primitives|4.3.1 System.Net.Requests|4.3.0 -System.Net.Security|4.3.0 +System.Net.Security|4.3.2 System.Net.Sockets|4.3.0 System.Net.WebHeaderCollection|4.3.0 +System.Net.WebSockets|4.3.0 +System.Net.WebSockets.Client|4.3.2 +System.Numerics.Vectors|5.0.0 System.ObjectModel|4.3.0 System.Private.DataContractSerialization|4.3.0 +System.Private.Uri|4.3.2 System.Reflection|4.3.0 -System.Reflection.Emit|4.3.0 -System.Reflection.Emit.ILGeneration|4.3.0 -System.Reflection.Emit.Lightweight|4.3.0 +System.Reflection.DispatchProxy|6.0.0 +System.Reflection.Emit|4.7.0 +System.Reflection.Emit.ILGeneration|4.7.0 +System.Reflection.Emit.Lightweight|4.7.0 System.Reflection.Extensions|4.3.0 -System.Reflection.Metadata|1.5.0 +System.Reflection.Metadata|9.0.0 System.Reflection.Primitives|4.3.0 System.Reflection.TypeExtensions|4.3.0 +System.Resources.Reader|4.3.0 System.Resources.ResourceManager|4.3.0 -System.Runtime|4.3.0 -System.Runtime.Extensions|4.3.0 +System.Resources.Writer|4.3.0 +System.Runtime|4.3.1 +System.Runtime.CompilerServices.Unsafe|7.0.0 +System.Runtime.CompilerServices.VisualC|4.3.0 +System.Runtime.Extensions|4.3.1 System.Runtime.Handles|4.3.0 System.Runtime.InteropServices|4.3.0 System.Runtime.InteropServices.RuntimeInformation|4.3.0 @@ -132,33 +154,39 @@ System.Runtime.Numerics|4.3.0 System.Runtime.Serialization.Formatters|4.3.0 System.Runtime.Serialization.Json|4.3.0 System.Runtime.Serialization.Primitives|4.3.0 -System.Security.AccessControl|4.4.0 +System.Runtime.Serialization.Xml|4.3.0 +System.Security.AccessControl|6.0.1 System.Security.Claims|4.3.0 -System.Security.Cryptography.Algorithms|4.3.0 -System.Security.Cryptography.Cng|4.4.0 +System.Security.Cryptography.Algorithms|4.3.1 +System.Security.Cryptography.Cng|5.0.0 System.Security.Cryptography.Csp|4.3.0 System.Security.Cryptography.Encoding|4.3.0 -System.Security.Cryptography.OpenSsl|4.4.0 +System.Security.Cryptography.OpenSsl|5.0.0 System.Security.Cryptography.Primitives|4.3.0 -System.Security.Cryptography.X509Certificates|4.3.0 -System.Security.Cryptography.Xml|4.4.0 +System.Security.Cryptography.X509Certificates|4.3.2 System.Security.Principal|4.3.0 -System.Security.Principal.Windows|4.4.0 +System.Security.Principal.Windows|5.0.0 +System.Security.SecureString|4.3.0 System.Text.Encoding|4.3.0 +System.Text.Encoding.CodePages|9.0.0 System.Text.Encoding.Extensions|4.3.0 -System.Text.RegularExpressions|4.3.0 +System.Text.Encodings.Web|9.0.0 +System.Text.Json|9.0.0 +System.Text.RegularExpressions|4.3.1 System.Threading|4.3.0 +System.Threading.Channels|9.0.0 System.Threading.Overlapped|4.3.0 System.Threading.Tasks|4.3.0 -System.Threading.Tasks.Extensions|4.3.0 +System.Threading.Tasks.Dataflow|9.0.0 +System.Threading.Tasks.Extensions|5.0.0 System.Threading.Tasks.Parallel|4.3.0 System.Threading.Thread|4.3.0 System.Threading.ThreadPool|4.3.0 System.Threading.Timer|4.3.0 -System.ValueTuple|4.3.0 -System.Xml.ReaderWriter|4.3.0 +System.ValueTuple|4.5.0 +System.Xml.ReaderWriter|4.3.1 System.Xml.XDocument|4.3.0 System.Xml.XmlDocument|4.3.0 System.Xml.XmlSerializer|4.3.0 System.Xml.XPath|4.3.0 -System.Xml.XPath.XDocument|4.3.0 +System.Xml.XPath.XDocument|5.0.0 diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs index e622a9df0a456e..c9b308fbabbc75 100644 --- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs +++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.ParseMapModules.cs @@ -15,9 +15,11 @@ internal static partial class @procfs { private const string MapsFileName = "/maps"; - private static string GetMapsFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{MapsFileName}"); + private static string GetMapsFilePathForProcess(ProcPid pid) => + pid == ProcPid.Self ? $"{RootPath}{Self}{MapsFileName}" : + string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{MapsFileName}"); - internal static ProcessModuleCollection? ParseMapsModules(int pid) + internal static ProcessModuleCollection? ParseMapsModules(ProcPid pid) { try { diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs index 7663b4ab15d394..7ffdbb4d7d2e69 100644 --- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs +++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.TryReadStatusFile.cs @@ -15,8 +15,28 @@ internal static partial class Interop internal static partial class @procfs { internal const string RootPath = "/proc/"; + internal const string Self = "self"; private const string StatusFileName = "/status"; + // Normally the '/proc' filesystem uses the same pid namespace as the process. + // With rootless containers, it may happen that these pid namespaces do not match + // because the container doesn't have permissions to change '/proc' but it can + // create a new pid namespace. + // + // When that happens, the numeric ids used by the '/proc' filesystem no longer match with + // the process pid namespace. We can still access information for the current process + // using '/proc/self'. For other processes, we can't map pids to the proc pids so we musn't + // use '/proc' as that would return information for non-existing/wrong/inaccessible processes. + // + // The 'ProcPid' type represents a pid used by the '/proc' filesystem. + // This type provides a type-safe way to distingish the proc pids from the process pid namespace pids, + // which are passed as regular 'int's. + internal enum ProcPid : int + { + Invalid = -1, + Self = 0, // Information for the current process, accessible through '/proc/self'. + } + internal struct ParsedStatus { #if DEBUG @@ -30,13 +50,15 @@ internal struct ParsedStatus internal ulong VmPeak; } - internal static string GetStatusFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{StatusFileName}"); + internal static string GetStatusFilePathForProcess(ProcPid pid) => + pid == ProcPid.Self ? $"{RootPath}{Self}{StatusFileName}" : + string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{StatusFileName}"); - internal static bool TryReadStatusFile(int pid, out ParsedStatus result) + internal static bool TryReadStatusFile(ProcPid pid, out ParsedStatus result) { bool b = TryParseStatusFile(GetStatusFilePathForProcess(pid), out result); #if DEBUG - Debug.Assert(!b || result.Pid == pid, "Expected process ID from status file to match supplied pid"); + Debug.Assert(!b || (ProcPid)result.Pid == pid || pid == ProcPid.Self, "Expected process ID from status file to match supplied pid"); #endif return b; } diff --git a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs index ee9c2b75971888..7c2ff54520bd46 100644 --- a/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs +++ b/src/libraries/Common/src/Interop/Linux/procfs/Interop.ProcFsStat.cs @@ -72,26 +72,38 @@ internal struct ParsedStat //internal long cguest_time; } - internal static string GetExeFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{ExeFileName}"); + internal static string GetExeFilePathForProcess(ProcPid pid) => + pid == ProcPid.Self ? $"{RootPath}{Self}{ExeFileName}" : + string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{ExeFileName}"); - internal static string GetCmdLinePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{CmdLineFileName}"); + internal static string GetCmdLinePathForProcess(ProcPid pid) => + pid == ProcPid.Self ? $"{RootPath}{Self}{CmdLineFileName}" : + string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{CmdLineFileName}"); - internal static string GetStatFilePathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{StatFileName}"); + internal static string GetStatFilePathForProcess(ProcPid pid) => + pid == ProcPid.Self ? $"{RootPath}{Self}{StatFileName}" : + string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{StatFileName}"); - internal static string GetTaskDirectoryPathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{TaskDirectoryName}"); + internal static string GetTaskDirectoryPathForProcess(ProcPid pid) => + pid == ProcPid.Self ? $"{RootPath}{Self}{TaskDirectoryName}" : + string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{TaskDirectoryName}"); - internal static string GetFileDescriptorDirectoryPathForProcess(int pid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{FileDescriptorDirectoryName}"); + internal static string GetFileDescriptorDirectoryPathForProcess(ProcPid pid) => + pid == ProcPid.Self ? $"{RootPath}{Self}{FileDescriptorDirectoryName}" : + string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{FileDescriptorDirectoryName}"); - private static string GetStatFilePathForThread(int pid, int tid) => string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{TaskDirectoryName}{(uint)tid}{StatFileName}"); + private static string GetStatFilePathForThread(ProcPid pid, int tid) => + pid == ProcPid.Self ? string.Create(null, stackalloc char[256], $"{RootPath}{Self}{TaskDirectoryName}{(uint)tid}{StatFileName}") : + string.Create(null, stackalloc char[256], $"{RootPath}{(uint)pid}{TaskDirectoryName}{(uint)tid}{StatFileName}"); - internal static bool TryReadStatFile(int pid, out ParsedStat result) + internal static bool TryReadStatFile(ProcPid pid, out ParsedStat result) { bool b = TryParseStatFile(GetStatFilePathForProcess(pid), out result); - Debug.Assert(!b || result.pid == pid, "Expected process ID from stat file to match supplied pid"); + Debug.Assert(!b || pid == ProcPid.Self|| (ProcPid)result.pid == pid, "Expected process ID from stat file to match supplied pid"); return b; } - internal static bool TryReadStatFile(int pid, int tid, out ParsedStat result) + internal static bool TryReadStatFile(ProcPid pid, int tid, out ParsedStat result) { bool b = TryParseStatFile(GetStatFilePathForThread(pid, tid), out result); Debug.Assert(!b || result.pid == tid, "Expected thread ID from stat file to match supplied tid"); diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs index 77b80634bd0ce1..4e8659b5653b78 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs @@ -217,7 +217,8 @@ internal static ArraySegment RentEncodeSubjectPublicKeyInfo(SafeEvpPKeyHan [LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)] private static partial SafeEvpPKeyHandle CryptoNative_LoadPrivateKeyFromEngine( string engineName, - string keyName); + string keyName, + [MarshalAs(UnmanagedType.Bool)] out bool haveEngine); internal static SafeEvpPKeyHandle LoadPrivateKeyFromEngine( string engineName, @@ -226,7 +227,13 @@ internal static SafeEvpPKeyHandle LoadPrivateKeyFromEngine( Debug.Assert(engineName is not null); Debug.Assert(keyName is not null); - SafeEvpPKeyHandle pkey = CryptoNative_LoadPrivateKeyFromEngine(engineName, keyName); + SafeEvpPKeyHandle pkey = CryptoNative_LoadPrivateKeyFromEngine(engineName, keyName, out bool haveEngine); + + if (!haveEngine) + { + pkey.Dispose(); + throw new CryptographicException(SR.Cryptography_EnginesNotSupported); + } if (pkey.IsInvalid) { @@ -240,7 +247,8 @@ internal static SafeEvpPKeyHandle LoadPrivateKeyFromEngine( [LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)] private static partial SafeEvpPKeyHandle CryptoNative_LoadPublicKeyFromEngine( string engineName, - string keyName); + string keyName, + [MarshalAs(UnmanagedType.Bool)] out bool haveEngine); internal static SafeEvpPKeyHandle LoadPublicKeyFromEngine( string engineName, @@ -249,7 +257,13 @@ internal static SafeEvpPKeyHandle LoadPublicKeyFromEngine( Debug.Assert(engineName is not null); Debug.Assert(keyName is not null); - SafeEvpPKeyHandle pkey = CryptoNative_LoadPublicKeyFromEngine(engineName, keyName); + SafeEvpPKeyHandle pkey = CryptoNative_LoadPublicKeyFromEngine(engineName, keyName, out bool haveEngine); + + if (!haveEngine) + { + pkey.Dispose(); + throw new CryptographicException(SR.Cryptography_EnginesNotSupported); + } if (pkey.IsInvalid) { diff --git a/src/libraries/Common/tests/System/Net/ActivityRecorder.cs b/src/libraries/Common/tests/System/Net/ActivityRecorder.cs index 6647076d4bac53..ebeef8b4bc4e38 100644 --- a/src/libraries/Common/tests/System/Net/ActivityRecorder.cs +++ b/src/libraries/Common/tests/System/Net/ActivityRecorder.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Linq; using System.Text; +using System.Threading; using Xunit; namespace System.Net.Test.Common @@ -17,12 +18,16 @@ internal class ActivityRecorder : IDisposable private readonly ActivityListener _listener; private List _finishedActivities = new(); + private int _started; + private int _stopped; + + public int Started => _started; + public int Stopped => _stopped; + public Predicate Filter { get; set; } = _ => true; public bool VerifyParent { get; set; } = true; public Activity ExpectedParent { get; set; } - public int Started { get; private set; } - public int Stopped { get; private set; } public Activity LastStartedActivity { get; private set; } public Activity LastFinishedActivity { get; private set; } public IEnumerable FinishedActivities => _finishedActivities; @@ -35,7 +40,8 @@ public ActivityRecorder(string activitySourceName, string activityName) { ShouldListenTo = (activitySource) => activitySource.Name == _activitySourceName, Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData, - ActivityStarted = (activity) => { + ActivityStarted = (activity) => + { if (activity.OperationName == _activityName && Filter(activity)) { if (VerifyParent) @@ -43,11 +49,13 @@ public ActivityRecorder(string activitySourceName, string activityName) Assert.Same(ExpectedParent, activity.Parent); } - Started++; + Interlocked.Increment(ref _started); + LastStartedActivity = activity; } }, - ActivityStopped = (activity) => { + ActivityStopped = (activity) => + { if (activity.OperationName == _activityName && Filter(activity)) { if (VerifyParent) @@ -55,14 +63,18 @@ public ActivityRecorder(string activitySourceName, string activityName) Assert.Same(ExpectedParent, activity.Parent); } - Stopped++; - LastFinishedActivity = activity; - _finishedActivities.Add(activity); + Interlocked.Increment(ref _stopped); + + lock (_finishedActivities) + { + LastFinishedActivity = activity; + _finishedActivities.Add(activity); + } } } }; - ActivitySource.AddActivityListener(_listener); + ActivitySource.AddActivityListener(_listener); } public void Dispose() => _listener.Dispose(); diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs index b474c5ea54a150..73e347d067112e 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Parser.cs @@ -652,6 +652,10 @@ private static string GenerateClassName(TypeDeclarationSyntax typeDeclaration) { continue; } + if (!fs.CanBeReferencedByName) + { + continue; + } if (IsBaseOrIdentity(fs.Type, loggerSymbol)) { if (loggerField == null) diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithLoggerFromPrimaryConstructorWithParameterUsedInMethod.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithLoggerFromPrimaryConstructorWithParameterUsedInMethod.generated.txt new file mode 100644 index 00000000000000..4fc0f50d5c735b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithLoggerFromPrimaryConstructorWithParameterUsedInMethod.generated.txt @@ -0,0 +1,21 @@ +// +#nullable enable + +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + partial class TestWithLoggerFromPrimaryConstructorWithParameterUsedInMethod + { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "%VERSION%")] + private static readonly global::System.Action __M0Callback = + global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Debug, new global::Microsoft.Extensions.Logging.EventId(0, nameof(M0)), "M0", new global::Microsoft.Extensions.Logging.LogDefineOptions() { SkipEnabledCheck = true }); + + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "%VERSION%")] + public partial void M0() + { + if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Debug)) + { + __M0Callback(logger, null); + } + } + } +} \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index 1013af8ae36722..ceac90cc93c3ba 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -48,6 +48,17 @@ public void FindsLoggerInPrimaryConstructorParameter() Assert.Equal("Test.", logger.LastFormattedString); } + [Fact] + public void FindsLoggerInPrimaryConstructorParameterUsedInMethod() + { + var logger = new MockLogger(); + + logger.Reset(); + + new ClassWithPrimaryConstructorWithParameterUsedInMethod(logger).Test(); + Assert.Equal("Test.", logger.LastFormattedString); + } + [Fact] public void FindsLoggerInPrimaryConstructorParameterInDifferentPartialDeclaration() { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs index 0952f66a8adabb..5f8dc5f9ca20ed 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorEmitterTests.cs @@ -203,6 +203,26 @@ internal partial class TestWithLoggerFromPrimaryConstructor(ILogger logger) await VerifyAgainstBaselineUsingFile("TestWithLoggerFromPrimaryConstructor.generated.txt", testSourceCode); } + [Fact] + public async Task TestBaseline_TestWithLoggerFromPrimaryConstructorWithParameterUsedInMethod_Success() + { + string testSourceCode = @" +namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses +{ + internal partial class TestWithLoggerFromPrimaryConstructorWithParameterUsedInMethod(ILogger logger) + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M0"")] + public partial void M0(); + + private void M1() + { + logger.LogInformation(""M1""); + } + } +}"; + await VerifyAgainstBaselineUsingFile("TestWithLoggerFromPrimaryConstructorWithParameterUsedInMethod.generated.txt", testSourceCode); + } + [Fact] public async Task TestBaseline_TestWithLoggerInFieldAndFromPrimaryConstructor_UsesField() { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs index 15a4f965074775..7249600f515c43 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratorParserTests.cs @@ -456,6 +456,25 @@ partial class C(ILogger logger) Assert.Empty(diagnostics); } + [Fact] + public async Task PrimaryConstructorWithParameterUsedInMethodOK() + { + IReadOnlyList diagnostics = await RunGenerator(@" + partial class C(ILogger logger) + { + [LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1"")] + public partial void M1(); + + private void M2() + { + logger.LogInformation(""M2""); + } + } + "); + + Assert.Empty(diagnostics); + } + [Fact] public async Task PrimaryConstructorOnOtherPartialDeclarationOK() { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs index 5f4fec36ac7b77..09e481f99badad 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/MiscTestExtensions.cs @@ -64,6 +64,17 @@ public partial class ClassWithPrimaryConstructor(ILogger logger) public partial void Test(); } +public partial class ClassWithPrimaryConstructorWithParameterUsedInMethod(ILogger logger) +{ + [LoggerMessage(0, LogLevel.Debug, "Test.")] + public partial void Test(); + + private void OtherLoggerUse() + { + logger.LogInformation("Other logger use."); + } +} + public partial class ClassWithPrimaryConstructorInDifferentPartialDeclaration(ILogger logger); public partial class ClassWithPrimaryConstructorInDifferentPartialDeclaration diff --git a/src/libraries/System.Console/tests/ReadAndWrite.cs b/src/libraries/System.Console/tests/ReadAndWrite.cs index 913d85835dbb18..3e165d38263926 100644 --- a/src/libraries/System.Console/tests/ReadAndWrite.cs +++ b/src/libraries/System.Console/tests/ReadAndWrite.cs @@ -158,9 +158,11 @@ public static async Task OutWriteAndWriteLineOverloads() Console.SetOut(sw); TextWriter writer = Console.Out; Assert.NotNull(writer); - // Browser bypasses SyncTextWriter for faster startup - if (!OperatingSystem.IsBrowser()) + // single-threaded WASM bypasses SyncTextWriter for faster startup + if (PlatformDetection.IsThreadingSupported) Assert.NotEqual(writer, sw); // the writer we provide gets wrapped + else + Assert.Equal(writer, sw); // the writer we provide does not get wrapped // We just want to ensure none of these throw exceptions, we don't actually validate // what was written. diff --git a/src/libraries/System.Console/tests/SyncTextWriter.cs b/src/libraries/System.Console/tests/SyncTextWriter.cs index 772e1e2b44a915..bcd0b12ddb426d 100644 --- a/src/libraries/System.Console/tests/SyncTextWriter.cs +++ b/src/libraries/System.Console/tests/SyncTextWriter.cs @@ -12,7 +12,7 @@ public class SyncTextWriter { - // Browser bypasses SyncTextWriter for faster startup + // single-threaded WASM bypasses SyncTextWriter for faster startup [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void SyncTextWriterLockedOnThis() { diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs index 01fc4cf0a141c8..dd1b40fd9a570c 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs @@ -31,13 +31,14 @@ public static Process[] GetProcessesByName(string? processName, string machineNa ArrayBuilder processes = default; foreach (int pid in ProcessManager.EnumerateProcessIds()) { - if (Interop.procfs.TryReadStatFile(pid, out Interop.procfs.ParsedStat parsedStat)) + if (ProcessManager.TryGetProcPid(pid, out Interop.procfs.ProcPid procPid) && + Interop.procfs.TryReadStatFile(procPid, out Interop.procfs.ParsedStat parsedStat)) { - string actualProcessName = GetUntruncatedProcessName(ref parsedStat); + string actualProcessName = GetUntruncatedProcessName(procPid, ref parsedStat); if ((processName == "" || string.Equals(processName, actualProcessName, StringComparison.OrdinalIgnoreCase)) && - Interop.procfs.TryReadStatusFile(pid, out Interop.procfs.ParsedStatus parsedStatus)) + Interop.procfs.TryReadStatusFile(procPid, out Interop.procfs.ParsedStatus parsedStatus)) { - ProcessInfo processInfo = ProcessManager.CreateProcessInfo(ref parsedStat, ref parsedStatus, actualProcessName); + ProcessInfo processInfo = ProcessManager.CreateProcessInfo(procPid, ref parsedStat, ref parsedStatus, actualProcessName); processes.Add(new Process(machineName, isRemoteMachine: false, pid, processInfo)); } } @@ -160,7 +161,11 @@ partial void EnsureHandleCountPopulated() { return; } - string path = Interop.procfs.GetFileDescriptorDirectoryPathForProcess(_processId); + if (!ProcessManager.TryGetProcPid(_processId, out Interop.procfs.ProcPid procPid)) + { + return; + } + string path = Interop.procfs.GetFileDescriptorDirectoryPathForProcess(procPid); if (Directory.Exists(path)) { try @@ -250,18 +255,19 @@ private static void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out #pragma warning restore IDE0060 /// Gets the path to the executable for the process, or null if it could not be retrieved. - /// The pid for the target process, or -1 for the current process. - internal static string? GetExePath(int processId = -1) + /// The pid for the target process. + internal static string? GetExePath(Interop.procfs.ProcPid procPid) { - return processId == -1 ? Environment.ProcessPath : - Interop.Sys.ReadLink(Interop.procfs.GetExeFilePathForProcess(processId)); + return procPid == Interop.procfs.ProcPid.Self ? Environment.ProcessPath : + Interop.Sys.ReadLink(Interop.procfs.GetExeFilePathForProcess(procPid)); } /// Gets the name that was used to start the process, or null if it could not be retrieved. + /// The pid for the target process. /// The stat for the target process. - internal static string GetUntruncatedProcessName(ref Interop.procfs.ParsedStat stat) + internal static string GetUntruncatedProcessName(Interop.procfs.ProcPid procPid, ref Interop.procfs.ParsedStat stat) { - string cmdLineFilePath = Interop.procfs.GetCmdLinePathForProcess(stat.pid); + string cmdLineFilePath = Interop.procfs.GetCmdLinePathForProcess(procPid); byte[]? rentedArray = null; try @@ -362,7 +368,7 @@ private Interop.procfs.ParsedStat GetStat() { EnsureState(State.HaveNonExitedId); Interop.procfs.ParsedStat stat; - if (!Interop.procfs.TryReadStatFile(_processId, out stat)) + if (!ProcessManager.TryReadStatFile(_processId, out stat)) { throw new Win32Exception(SR.ProcessInformationUnavailable); } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs index 34088e34bdac39..b578bc241d8430 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs @@ -9,6 +9,8 @@ namespace System.Diagnostics { internal static partial class ProcessManager { + private static volatile int _procMatchesPidNamespace; + /// Gets the IDs of all processes on the current machine. public static int[] GetProcessIds() => new List(EnumerateProcessIds()).ToArray(); @@ -41,28 +43,31 @@ public static ProcessInfo[] GetProcessInfos(string? processNameFilter, string ma /// The array of modules. internal static ProcessModuleCollection GetModules(int processId) { - ProcessModuleCollection modules = Interop.procfs.ParseMapsModules(processId) ?? new(capacity: 0); - - // Move the main executable module to be the first in the list if it's not already - if (Process.GetExePath(processId) is string exePath) + ProcessModuleCollection? modules = null; + if (TryGetProcPid(processId, out Interop.procfs.ProcPid procPid)) { - for (int i = 0; i < modules.Count; i++) + modules = Interop.procfs.ParseMapsModules(procPid); + + // Move the main executable module to be the first in the list if it's not already + if (modules is not null && Process.GetExePath(procPid) is string exePath) { - ProcessModule module = modules[i]; - if (module.FileName == exePath) + for (int i = 0; i < modules.Count; i++) { - if (i > 0) + ProcessModule module = modules[i]; + if (module.FileName == exePath) { - modules.RemoveAt(i); - modules.Insert(0, module); + if (i > 0) + { + modules.RemoveAt(i); + modules.Insert(0, module); + } + break; } - break; } } } - // Return the set of modules found - return modules; + return modules ?? new(capacity: 0); } /// @@ -70,10 +75,11 @@ internal static ProcessModuleCollection GetModules(int processId) /// internal static ProcessInfo? CreateProcessInfo(int pid) { - if (Interop.procfs.TryReadStatFile(pid, out Interop.procfs.ParsedStat stat)) + if (TryGetProcPid(pid, out Interop.procfs.ProcPid procPid) && + Interop.procfs.TryReadStatFile(procPid, out Interop.procfs.ParsedStat stat)) { - Interop.procfs.TryReadStatusFile(pid, out Interop.procfs.ParsedStatus status); - return CreateProcessInfo(ref stat, ref status); + Interop.procfs.TryReadStatusFile(procPid, out Interop.procfs.ParsedStatus status); + return CreateProcessInfo(procPid, ref stat, ref status); } return null; } @@ -81,14 +87,14 @@ internal static ProcessModuleCollection GetModules(int processId) /// /// Creates a ProcessInfo from the data parsed from a /proc/pid/stat file and the associated tasks directory. /// - internal static ProcessInfo CreateProcessInfo(ref Interop.procfs.ParsedStat procFsStat, ref Interop.procfs.ParsedStatus procFsStatus, string? processName = null) + internal static ProcessInfo CreateProcessInfo(Interop.procfs.ProcPid procPid, ref Interop.procfs.ParsedStat procFsStat, ref Interop.procfs.ParsedStatus procFsStatus, string? processName = null) { int pid = procFsStat.pid; var pi = new ProcessInfo() { ProcessId = pid, - ProcessName = processName ?? Process.GetUntruncatedProcessName(ref procFsStat) ?? string.Empty, + ProcessName = processName ?? Process.GetUntruncatedProcessName(procPid, ref procFsStat) ?? string.Empty, BasePriority = (int)procFsStat.nice, SessionId = procFsStat.session, PoolPagedBytes = (long)procFsStatus.VmSwap, @@ -105,7 +111,7 @@ internal static ProcessInfo CreateProcessInfo(ref Interop.procfs.ParsedStat proc }; // Then read through /proc/pid/task/ to find each thread in the process... - string tasksDir = Interop.procfs.GetTaskDirectoryPathForProcess(pid); + string tasksDir = Interop.procfs.GetTaskDirectoryPathForProcess(procPid); try { foreach (string taskDir in Directory.EnumerateDirectories(tasksDir)) @@ -115,7 +121,7 @@ internal static ProcessInfo CreateProcessInfo(ref Interop.procfs.ParsedStat proc int tid; Interop.procfs.ParsedStat stat; if (int.TryParse(dirName, NumberStyles.Integer, CultureInfo.InvariantCulture, out tid) && - Interop.procfs.TryReadStatFile(pid, tid, out stat)) + Interop.procfs.TryReadStatFile(procPid, tid, out stat)) { pi._threadInfoList.Add(new ThreadInfo() { @@ -147,18 +153,26 @@ internal static ProcessInfo CreateProcessInfo(ref Interop.procfs.ParsedStat proc /// Enumerates the IDs of all processes on the current machine. internal static IEnumerable EnumerateProcessIds() { - // Parse /proc for any directory that's named with a number. Each such - // directory represents a process. - foreach (string procDir in Directory.EnumerateDirectories(Interop.procfs.RootPath)) + if (ProcMatchesPidNamespace) { - string dirName = Path.GetFileName(procDir); - int pid; - if (int.TryParse(dirName, NumberStyles.Integer, CultureInfo.InvariantCulture, out pid)) + // Parse /proc for any directory that's named with a number. Each such + // directory represents a process. + foreach (string procDir in Directory.EnumerateDirectories(Interop.procfs.RootPath)) { - Debug.Assert(pid >= 0); - yield return pid; + string dirName = Path.GetFileName(procDir); + int pid; + if (int.TryParse(dirName, NumberStyles.Integer, CultureInfo.InvariantCulture, out pid)) + { + Debug.Assert(pid >= 0); + yield return pid; + } } } + else + { + // Limit to our own process. For other processes, the pids from /proc don't match with those in the process namespace. + yield return Environment.ProcessId; + } } /// Gets a ThreadState to represent the value returned from the status field of /proc/pid/stat. @@ -198,5 +212,80 @@ private static ThreadState ProcFsStateToThreadState(char c) } } + internal static bool TryReadStatFile(int pid, out Interop.procfs.ParsedStat stat) + { + if (!TryGetProcPid(pid, out Interop.procfs.ProcPid procPid)) + { + stat = default; + return false; + } + return Interop.procfs.TryReadStatFile(procPid, out stat); + } + + internal static bool TryReadStatusFile(int pid, out Interop.procfs.ParsedStatus status) + { + if (!TryGetProcPid(pid, out Interop.procfs.ProcPid procPid)) + { + status = default; + return false; + } + return Interop.procfs.TryReadStatusFile(procPid, out status); + } + + internal static bool TryReadStatFile(int pid, int tid, out Interop.procfs.ParsedStat stat) + { + if (!TryGetProcPid(pid, out Interop.procfs.ProcPid procPid)) + { + stat = default; + return false; + } + return Interop.procfs.TryReadStatFile(procPid, tid, out stat); + } + + internal static bool TryGetProcPid(int pid, out Interop.procfs.ProcPid procPid) + { + // Use '/proc/self' for the current process. + if (pid == Environment.ProcessId) + { + procPid = Interop.procfs.ProcPid.Self; + return true; + } + + if (ProcMatchesPidNamespace) + { + procPid = (Interop.procfs.ProcPid)pid; + return true; + } + + // We can't map a process namespace pid to a procfs pid. + procPid = Interop.procfs.ProcPid.Invalid; + return false; + } + + internal static bool ProcMatchesPidNamespace + { + get + { + // _procMatchesPidNamespace is set to: + // - 0: when uninitialized, + // - 1: '/proc' and the process pid namespace match, + // - 2: when they don't match. + if (_procMatchesPidNamespace == 0) + { + // '/proc/self' is a symlink to the pid used by '/proc' for the current process. + // We compare it with the pid of the current process to see if the '/proc' and pid namespace match up. + int? procSelfPid = null; + if (Interop.Sys.ReadLink($"{Interop.procfs.RootPath}{Interop.procfs.Self}") is string target && + int.TryParse(target, out int pid)) + { + procSelfPid = pid; + } + Debug.Assert(procSelfPid.HasValue); + + _procMatchesPidNamespace = !procSelfPid.HasValue || procSelfPid == Environment.ProcessId ? 1 : 2; + } + return _procMatchesPidNamespace == 1; + } + } } } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.Linux.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.Linux.cs index e9a8bd92e04a9f..c25f9d9de582f5 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.Linux.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessThread.Linux.cs @@ -83,7 +83,7 @@ public TimeSpan UserProcessorTime private Interop.procfs.ParsedStat GetStat() { Interop.procfs.ParsedStat stat; - if (!Interop.procfs.TryReadStatFile(pid: _processId, tid: Id, result: out stat)) + if (!ProcessManager.TryReadStatFile(_processId, tid: Id, out stat)) { throw new InvalidOperationException(SR.Format(SR.ThreadExited, Id)); } diff --git a/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs index 834fcdad006d7d..73983cf87fbde8 100644 --- a/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs @@ -52,17 +52,21 @@ public override TResult[] ToArray() public override List ToList() { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); foreach (object? item in _source) { if (item is TResult castItem) { - list.Add(castItem); + builder.Add(castItem); } } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override TResult? TryGetFirst(out bool found) diff --git a/src/libraries/System.Linq/src/System/Linq/SegmentedArrayBuilder.cs b/src/libraries/System.Linq/src/System/Linq/SegmentedArrayBuilder.cs index 0abaab4156bb8a..e6b72d08885369 100644 --- a/src/libraries/System.Linq/src/System/Linq/SegmentedArrayBuilder.cs +++ b/src/libraries/System.Linq/src/System/Linq/SegmentedArrayBuilder.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Collections.Generic { @@ -251,6 +252,27 @@ public readonly T[] ToArray() return result; } + /// Creates a list containing all of the elements in the builder. + public readonly List ToList() + { + List result; + int count = Count; + + if (count != 0) + { + result = new List(count); + + CollectionsMarshal.SetCount(result, count); + ToSpanInlined(CollectionsMarshal.AsSpan(result)); + } + else + { + result = []; + } + + return result; + } + /// Creates an array containing all of the elements in the builder. /// The number of extra elements of room to allocate in the resulting array. public readonly T[] ToArray(int additionalLength) diff --git a/src/libraries/System.Linq/src/System/Linq/Select.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Select.SpeedOpt.cs index f491f1f0de015a..98c686e514b68c 100644 --- a/src/libraries/System.Linq/src/System/Linq/Select.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Select.SpeedOpt.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static System.Linq.Utilities; @@ -31,15 +32,19 @@ public override TResult[] ToArray() public override List ToList() { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); Func selector = _selector; foreach (TSource item in _source) { - list.Add(selector(item)); + builder.Add(selector(item)); } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override int GetCount(bool onlyIfCheap) @@ -657,7 +662,7 @@ public override IEnumerable Select(Func s return sourceFound ? _selector(input!) : default!; } - private TResult[] LazyToArray() + private TResult[] ToArrayNoPresizing() { Debug.Assert(_source.GetCount(onlyIfCheap: true) == -1); @@ -691,12 +696,31 @@ public override TResult[] ToArray() int count = _source.GetCount(onlyIfCheap: true); return count switch { - -1 => LazyToArray(), + -1 => ToArrayNoPresizing(), 0 => [], _ => PreallocatingToArray(count), }; } + private List ToListNoPresizing() + { + Debug.Assert(_source.GetCount(onlyIfCheap: true) == -1); + + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); + + Func selector = _selector; + foreach (TSource input in _source) + { + builder.Add(selector(input)); + } + + List result = builder.ToList(); + builder.Dispose(); + + return result; + } + public override List ToList() { int count = _source.GetCount(onlyIfCheap: true); @@ -704,11 +728,7 @@ public override List ToList() switch (count) { case -1: - list = new List(); - foreach (TSource input in _source) - { - list.Add(_selector(input)); - } + list = ToListNoPresizing(); break; case 0: list = new List(); diff --git a/src/libraries/System.Linq/src/System/Linq/SelectMany.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/SelectMany.SpeedOpt.cs index ae0bf35ef8f1af..a521ab37295dbd 100644 --- a/src/libraries/System.Linq/src/System/Linq/SelectMany.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/SelectMany.SpeedOpt.cs @@ -48,15 +48,19 @@ public override TResult[] ToArray() public override List ToList() { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); Func> selector = _selector; - foreach (TSource element in _source) + foreach (TSource item in _source) { - list.AddRange(selector(element)); + builder.AddRange(selector(item)); } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } } } diff --git a/src/libraries/System.Linq/src/System/Linq/SkipTake.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/SkipTake.SpeedOpt.cs index 1a49488b09fbb7..5cb53aa2997e06 100644 --- a/src/libraries/System.Linq/src/System/Linq/SkipTake.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/SkipTake.SpeedOpt.cs @@ -491,8 +491,6 @@ public override TSource[] ToArray() public override List ToList() { - var list = new List(); - using (IEnumerator en = _source.GetEnumerator()) { if (SkipBeforeFirst(en) && en.MoveNext()) @@ -500,16 +498,23 @@ public override List ToList() int remaining = Limit - 1; // Max number of items left, not counting the current element. int comparand = HasLimit ? 0 : int.MinValue; // If we don't have an upper bound, have the comparison always return true. + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); do { remaining--; - list.Add(en.Current); + builder.Add(en.Current); } while (remaining >= comparand && en.MoveNext()); + + List result = builder.ToList(); + builder.Dispose(); + + return result; } } - return list; + return []; } private bool SkipBeforeFirst(IEnumerator en) => SkipBefore(_minIndexInclusive, en); diff --git a/src/libraries/System.Linq/src/System/Linq/Where.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Where.SpeedOpt.cs index 40a05db9abf8ff..ecc7be996b1e45 100644 --- a/src/libraries/System.Linq/src/System/Linq/Where.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Where.SpeedOpt.cs @@ -55,18 +55,22 @@ public override TSource[] ToArray() public override List ToList() { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); Func predicate = _predicate; foreach (TSource item in _source) { if (predicate(item)) { - list.Add(item); + builder.Add(item); } } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override TSource? TryGetFirst(out bool found) @@ -199,17 +203,21 @@ public static TSource[] ToArray(ReadOnlySpan source, Func ToList(ReadOnlySpan source, Func predicate) { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); foreach (TSource item in source) { if (predicate(item)) { - list.Add(item); + builder.Add(item); } } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override TSource? TryGetFirst(out bool found) @@ -398,17 +406,21 @@ public static TResult[] ToArray(ReadOnlySpan source, Func ToList(ReadOnlySpan source, Func predicate, Func selector) { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); foreach (TSource item in source) { if (predicate(item)) { - list.Add(selector(item)); + builder.Add(selector(item)); } } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override TResult? TryGetFirst(out bool found) => TryGetFirst(_source, _predicate, _selector, out found); @@ -538,7 +550,8 @@ public override TResult[] ToArray() public override List ToList() { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); Func predicate = _predicate; Func selector = _selector; @@ -546,11 +559,14 @@ public override List ToList() { if (predicate(item)) { - list.Add(selector(item)); + builder.Add(selector(item)); } } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override TResult? TryGetFirst(out bool found) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs index bf69967404ccfe..317420b6380922 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs @@ -21,6 +21,7 @@ namespace System.Net.Http.Functional.Tests { + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class HttpClientHandlerTest_Http3 : HttpClientHandlerTestBase diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs index a95599571b1303..aed29820ca11d3 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientMiniStressTest.cs @@ -31,6 +31,7 @@ public void CreateAndDestroyManyClients(int numClients) } } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandler_HttpClientMiniStress_Http3 : HttpClientMiniStress diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs index 19003fddfff51d..ce990298d9ae95 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs @@ -1163,6 +1163,7 @@ public HttpMetricsTest_Http20_HttpMessageInvoker(ITestOutputHelper output) : bas } } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public class HttpMetricsTest_Http30 : HttpMetricsTest @@ -1173,6 +1174,7 @@ public HttpMetricsTest_Http30(ITestOutputHelper output) : base(output) } } + [Collection(nameof(DisableParallelization))] public class HttpMetricsTest_Http30_HttpMessageInvoker : HttpMetricsTest_Http30 { protected override bool TestHttpMessageInvoker => true; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/ResponseStreamZeroByteReadTests.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/ResponseStreamZeroByteReadTests.cs index 3768f388242800..e37af4a0e3cafc 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/ResponseStreamZeroByteReadTests.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/ResponseStreamZeroByteReadTests.cs @@ -181,6 +181,7 @@ public Http2ResponseStreamZeroByteReadTest(ITestOutputHelper output) : base(outp protected override Version UseVersion => HttpVersion.Version20; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class Http3ResponseStreamZeroByteReadTest : ResponseStreamZeroByteReadTestBase diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 18d3ee156e7858..5a515e9ca8c34d 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -292,6 +292,7 @@ public SocketsHttpHandler_DiagnosticsTest_Http2(ITestOutputHelper output) : base protected override Version UseVersion => HttpVersion.Version20; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandler_DiagnosticsTest_Http3 : DiagnosticsTest @@ -1710,6 +1711,7 @@ public SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Http2(ITest protected override Version UseVersion => HttpVersion.Version20; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength_Http3 : SocketsHttpHandler_HttpClientHandler_MaxResponseHeadersLength @@ -4090,6 +4092,7 @@ public SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http2(ITestOutputH protected override Version UseVersion => HttpVersion.Version20; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandlerTest_HttpClientHandlerTest_Http3 : HttpClientHandlerTest @@ -4098,6 +4101,7 @@ public SocketsHttpHandlerTest_HttpClientHandlerTest_Http3(ITestOutputHelper outp protected override Version UseVersion => HttpVersion.Version30; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandlerTest_Cookies_Http3 : HttpClientHandlerTest_Cookies @@ -4106,6 +4110,7 @@ public SocketsHttpHandlerTest_Cookies_Http3(ITestOutputHelper output) : base(out protected override Version UseVersion => HttpVersion.Version30; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandlerTest_HttpClientHandlerTest_Headers_Http3 : HttpClientHandlerTest_Headers @@ -4114,6 +4119,7 @@ public SocketsHttpHandlerTest_HttpClientHandlerTest_Headers_Http3(ITestOutputHel protected override Version UseVersion => HttpVersion.Version30; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http3 : SocketsHttpHandler_Cancellation_Test @@ -4122,6 +4128,7 @@ public SocketsHttpHandler_HttpClientHandler_Cancellation_Test_Http3(ITestOutputH protected override Version UseVersion => HttpVersion.Version30; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandler_HttpClientHandler_AltSvc_Test_Http3 : HttpClientHandler_AltSvc_Test @@ -4130,6 +4137,7 @@ public SocketsHttpHandler_HttpClientHandler_AltSvc_Test_Http3(ITestOutputHelper protected override Version UseVersion => HttpVersion.Version30; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandler_HttpClientHandler_Finalization_Http3 : HttpClientHandler_Finalization_Test @@ -4288,6 +4296,7 @@ public SocketsHttpHandler_RequestContentLengthMismatchTest_Http2(ITestOutputHelp protected override Version UseVersion => HttpVersion.Version20; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandler_RequestContentLengthMismatchTest_Http3 : SocketsHttpHandler_RequestContentLengthMismatchTest @@ -4465,6 +4474,7 @@ public SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http2(ITestOutputHelpe protected override Version UseVersion => HttpVersion.Version20; } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandler_SocketsHttpHandler_SecurityTest_Http3 : SocketsHttpHandler_SecurityTest @@ -4594,6 +4604,7 @@ await Http11LoopbackServerFactory.Singleton.CreateClientAndServerAsync(async uri } } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class SocketsHttpHandler_HttpRequestErrorTest_Http30 : SocketsHttpHandler_HttpRequestErrorTest diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs index 1e22d2ea4b1aaa..081c09ae69336f 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs @@ -980,6 +980,7 @@ public sealed class TelemetryTest_Http20 : TelemetryTest public TelemetryTest_Http20(ITestOutputHelper output) : base(output) { } } + [Collection(nameof(DisableParallelization))] [ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/103703", typeof(PlatformDetection), nameof(PlatformDetection.IsArmProcess))] public sealed class TelemetryTest_Http30 : TelemetryTest diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ThrowHelper.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ThrowHelper.cs index 456370e3d15d4d..fa86818619f75c 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ThrowHelper.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ThrowHelper.cs @@ -1,12 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Net.Security; using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Security.Authentication; using System.Threading; +using System.Threading.Tasks; using static Microsoft.Quic.MsQuic; namespace System.Net.Quic; @@ -28,27 +30,14 @@ internal static QuicException GetOperationAbortedException(string? message = nul return new QuicException(QuicError.OperationAborted, null, message ?? SR.net_quic_operationaborted); } - internal static bool TryGetStreamExceptionForMsQuicStatus(int status, [NotNullWhen(true)] out Exception? exception, bool streamWasSuccessfullyStarted = true, string? message = null) + internal static bool TryGetStreamExceptionForMsQuicStatus(int status, [NotNullWhen(true)] out Exception? exception) { if (status == QUIC_STATUS_ABORTED) { - // Connection has been closed by the peer (either at transport or application level), - if (streamWasSuccessfullyStarted) - { - // we will receive an event later, which will complete the stream with concrete - // information why the connection was aborted. - exception = null; - return false; - } - else - { - // we won't be receiving any event callback for shutdown on this stream, so we don't - // necessarily know which error to report. So we throw an exception which we can distinguish - // at the caller (ConnectionAborted normally has App error code) and throw the correct - // exception from there. - exception = new QuicException(QuicError.ConnectionAborted, null, ""); - return true; - } + // If status == QUIC_STATUS_ABORTED, the connection was closed by transport or the peer. + // We will receive an event later with details for ConnectionAborted exception to complete the task source with. + exception = null; + return false; } else if (status == QUIC_STATUS_INVALID_STATE) { @@ -58,16 +47,13 @@ internal static bool TryGetStreamExceptionForMsQuicStatus(int status, [NotNullWh } else if (StatusFailed(status)) { - exception = GetExceptionForMsQuicStatus(status, message: message); + exception = GetExceptionForMsQuicStatus(status); return true; } exception = null; return false; } - // see TryGetStreamExceptionForMsQuicStatus for explanation - internal static bool IsConnectionAbortedWhenStartingStreamException(Exception ex) => ex is QuicException qe && qe.QuicError == QuicError.ConnectionAborted && qe.ApplicationErrorCode is null; - internal static Exception GetExceptionForMsQuicStatus(int status, long? errorCode = default, string? message = null) { Exception ex = GetExceptionInternal(status, errorCode, message); @@ -229,4 +215,26 @@ public static void ValidateNotNull(string argumentName, string resourceName, obj throw new ArgumentNullException(argumentName, SR.Format(resourceName, propertyName)); } } + + public static void ObserveException(this Task task) + { + if (task.IsCompleted) + { + ObserveExceptionCore(task); + } + else + { + task.ContinueWith(static (t) => ObserveExceptionCore(t), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default); + } + + static void ObserveExceptionCore(Task task) + { + Debug.Assert(task.IsCompleted); + if (task.IsFaulted) + { + // Access Exception to avoid TaskScheduler.UnobservedTaskException firing. + Exception? e = task.Exception!.InnerException; + } + } + } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs index 46325cf2e14867..d5c76ed4b09498 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs @@ -87,10 +87,10 @@ static async ValueTask StartConnectAsync(QuicClientConnectionOpt { await connection.DisposeAsync().ConfigureAwait(false); - // throw OCE with correct token if cancellation requested by user + // Throw OCE with correct token if cancellation requested by user. cancellationToken.ThrowIfCancellationRequested(); - // cancellation by the linkedCts.CancelAfter. Convert to Timeout + // Cancellation by the linkedCts.CancelAfter, convert to timeout. throw new QuicException(QuicError.ConnectionTimeout, null, SR.Format(SR.net_quic_handshake_timeout, options.HandshakeTimeout)); } catch @@ -113,11 +113,6 @@ static async ValueTask StartConnectAsync(QuicClientConnectionOpt /// private int _disposed; - /// - /// Completed when connection shutdown is initiated. - /// - private TaskCompletionSource _connectionCloseTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - private readonly ValueTaskSource _connectedTcs = new ValueTaskSource(); private readonly ResettableValueTaskSource _shutdownTcs = new ResettableValueTaskSource() { @@ -140,6 +135,11 @@ static async ValueTask StartConnectAsync(QuicClientConnectionOpt } }; + /// + /// Completed when connection shutdown is initiated. + /// + private readonly TaskCompletionSource _connectionCloseTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + private readonly CancellationTokenSource _shutdownTokenSource = new CancellationTokenSource(); // Token that fires when the connection is closed. @@ -369,7 +369,7 @@ private async ValueTask FinishConnectAsync(QuicClientConnectionOptions options, { Debug.Assert(host is not null); - // Given just a ServerName to connect to, msquic would also use the first address after the resolution + // Given just a ServerName to connect to, MsQuic would also use the first address after the resolution // (https://github.com/microsoft/msquic/issues/1181) and it would not return a well-known error code // for resolution failures we could rely on. By doing the resolution in managed code, we can guarantee // that a SocketException will surface to the user if the name resolution fails. @@ -526,13 +526,9 @@ public async ValueTask OpenOutboundStreamAsync(QuicStreamType type, // Propagate ODE if disposed in the meantime. ObjectDisposedException.ThrowIf(_disposed == 1, this); - // In case of an incoming race when the connection is closed by the peer just before we open the stream, - // we receive QUIC_STATUS_ABORTED from MsQuic, but we don't know how the connection was closed. We throw - // special exception and handle it here where we can determine the shutdown reason. - bool connectionAbortedByPeer = ThrowHelper.IsConnectionAbortedWhenStartingStreamException(ex); - - // Propagate connection error if present. - if (_connectionCloseTcs.Task.IsFaulted || connectionAbortedByPeer) + // Propagate connection error when the connection was closed (remotely = ABORTED / locally = INVALID_STATE). + if (ex is QuicException qex && qex.QuicError == QuicError.InternalError && + (qex.HResult == QUIC_STATUS_ABORTED || qex.HResult == QUIC_STATUS_INVALID_STATE)) { await _connectionCloseTcs.Task.ConfigureAwait(false); } @@ -822,8 +818,10 @@ public async ValueTask DisposeAsync() // Wait for SHUTDOWN_COMPLETE, the last event, so that all resources can be safely released. await _shutdownTcs.GetFinalTask(this).ConfigureAwait(false); Debug.Assert(_connectedTcs.IsCompleted); + Debug.Assert(_connectionCloseTcs.Task.IsCompleted); _handle.Dispose(); _shutdownTokenSource.Dispose(); + _connectionCloseTcs.Task.ObserveException(); _configuration?.Dispose(); // Dispose remote certificate only if it hasn't been accessed via getter, in which case the accessing code becomes the owner of the certificate lifetime. diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs index a0713d3b8f9bb8..66568b937ed65a 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs @@ -169,20 +169,17 @@ internal unsafe QuicStream(MsQuicContextSafeHandle connectionHandle, QuicStreamT try { QUIC_HANDLE* handle; - int status = MsQuicApi.Api.StreamOpen( + ThrowHelper.ThrowIfMsQuicError(MsQuicApi.Api.StreamOpen( connectionHandle, type == QuicStreamType.Unidirectional ? QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL : QUIC_STREAM_OPEN_FLAGS.NONE, &NativeCallback, (void*)GCHandle.ToIntPtr(context), - &handle); - - if (ThrowHelper.TryGetStreamExceptionForMsQuicStatus(status, out Exception? ex, streamWasSuccessfullyStarted: false, message: "StreamOpen failed")) + &handle), + "StreamOpen failed"); + _handle = new MsQuicContextSafeHandle(handle, context, SafeHandleType.Stream, connectionHandle) { - throw ex; - } - - _handle = new MsQuicContextSafeHandle(handle, context, SafeHandleType.Stream, connectionHandle); - _handle.Disposable = _sendBuffers; + Disposable = _sendBuffers + }; } catch { @@ -213,8 +210,10 @@ internal unsafe QuicStream(MsQuicContextSafeHandle connectionHandle, QUIC_HANDLE GCHandle context = GCHandle.Alloc(this, GCHandleType.Weak); try { - _handle = new MsQuicContextSafeHandle(handle, context, SafeHandleType.Stream, connectionHandle); - _handle.Disposable = _sendBuffers; + _handle = new MsQuicContextSafeHandle(handle, context, SafeHandleType.Stream, connectionHandle) + { + Disposable = _sendBuffers + }; delegate* unmanaged[Cdecl] nativeCallback = &NativeCallback; MsQuicApi.Api.SetCallbackHandler( _handle, @@ -261,14 +260,12 @@ internal ValueTask StartAsync(Action decrementStreamCapacity, Ca int status = MsQuicApi.Api.StreamStart( _handle, QUIC_STREAM_START_FLAGS.SHUTDOWN_ON_FAIL | QUIC_STREAM_START_FLAGS.INDICATE_PEER_ACCEPT); - - if (ThrowHelper.TryGetStreamExceptionForMsQuicStatus(status, out Exception? exception, streamWasSuccessfullyStarted: false)) + if (StatusFailed(status)) { _decrementStreamCapacity = null; - _startedTcs.TrySetException(exception); + _startedTcs.TrySetException(ThrowHelper.GetExceptionForMsQuicStatus(status)); } } - return valueTask; } @@ -637,7 +634,7 @@ private unsafe int HandleEventShutdownComplete(ref SHUTDOWN_COMPLETE_DATA data) // It's local shutdown by app, this side called QuicConnection.CloseAsync, throw QuicError.OperationAborted. (shutdownByApp: true, closedRemotely: false) => ThrowHelper.GetOperationAbortedException(), // It's remote shutdown by transport, we received a CONNECTION_CLOSE frame with a QUIC transport error code, throw error based on the status. - (shutdownByApp: false, closedRemotely: true) => ThrowHelper.GetExceptionForMsQuicStatus(data.ConnectionCloseStatus, (long)data.ConnectionErrorCode, $"Shutdown by transport {data.ConnectionErrorCode}"), + (shutdownByApp: false, closedRemotely: true) => ThrowHelper.GetExceptionForMsQuicStatus(data.ConnectionCloseStatus, (long)data.ConnectionErrorCode), // It's local shutdown by transport, most likely due to a timeout, throw error based on the status. (shutdownByApp: false, closedRemotely: false) => ThrowHelper.GetExceptionForMsQuicStatus(data.ConnectionCloseStatus, (long)data.ConnectionErrorCode), }; diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index bcfa3202708bb0..670ea7c1ef32f3 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -580,7 +580,7 @@ public async Task ConnectWithCertificate_MissingTargetHost_Succeeds() return true; }; - await CreateQuicConnection(clientOptions); + await using QuicConnection connection = await CreateQuicConnection(clientOptions); } finally { diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs index 668711c68a2e89..5fbd95caabef64 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs @@ -117,6 +117,67 @@ await RunClientServer( }); } + [Theory] + [InlineData(true)] + [InlineData(false)] + [InlineData(null)] + public async Task CloseAsync_PendingOpenStream_Throws(bool? localClose) + { + byte[] data = new byte[10]; + + await using QuicListener listener = await CreateQuicListener(changeServerOptions: localClose is null ? options => options.IdleTimeout = TimeSpan.FromSeconds(10) : null); + + // Allow client to accept a stream, one will be accepted and another will be pending while we close the server connection. + QuicClientConnectionOptions clientOptions = CreateQuicClientOptions(listener.LocalEndPoint); + clientOptions.MaxInboundBidirectionalStreams = 1; + await using QuicConnection clientConnection = await CreateQuicConnection(clientOptions); + + await using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); + + // Put one stream into server stream queue. + QuicStream queuedStream = await clientConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); + await queuedStream.WriteAsync(data.AsMemory(), completeWrites: true); + + // Open one stream to the client that is allowed. + QuicStream firstStream = await serverConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); + await firstStream.WriteAsync(data.AsMemory(), completeWrites: true); + + // Try to open another stream which should wait on capacity. + ValueTask secondStreamTask = serverConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); + Assert.False(secondStreamTask.IsCompleted); + + // Close the connection, second stream task should complete with appropriate error. + if (localClose is true) + { + await serverConnection.CloseAsync(123); + await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async () => await secondStreamTask); + + // Try to open yet another stream which should fail because of already closed connection. + await AssertThrowsQuicExceptionAsync(QuicError.OperationAborted, async () => await serverConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional)); + } + else if (localClose is false) + { + await clientConnection.CloseAsync(456); + QuicException ex1 = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async () => await secondStreamTask); + Assert.Equal(456, ex1.ApplicationErrorCode); + + // Try to open yet another stream which should fail because of already closed connection. + QuicException ex2 = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionAborted, async () => await serverConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional)); + Assert.Equal(456, ex2.ApplicationErrorCode); + } + else + { + await Task.Delay(TimeSpan.FromSeconds(15)); + + QuicException ex1 = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionIdle, async () => await secondStreamTask); + Assert.Equal(1, ex1.TransportErrorCode); + + // Try to open yet another stream which should fail because of already closed connection. + QuicException ex2 = await AssertThrowsQuicExceptionAsync(QuicError.ConnectionIdle, async () => await serverConnection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional)); + Assert.Equal(1, ex2.TransportErrorCode); + } + } + [Fact] public async Task Dispose_WithPendingAcceptAndConnect_PendingAndSubsequentThrowOperationAbortedException() { @@ -228,6 +289,9 @@ public async Task GetStreamCapacity_OpenCloseStream_CountsCorrectly() await streamsAvailableFired.WaitAsync(); Assert.Equal(0, bidiIncrement); Assert.Equal(1, unidiIncrement); + + await clientConnection.DisposeAsync(); + await serverConnection.DisposeAsync(); } [Theory] @@ -298,6 +362,9 @@ public async Task GetStreamCapacity_OpenCloseStreamIntoNegative_CountsCorrectly( Assert.False(streamsAvailableFired.CurrentCount > 0); Assert.Equal(unidirectional ? QuicDefaults.DefaultServerMaxInboundBidirectionalStreams : QuicDefaults.DefaultServerMaxInboundBidirectionalStreams * 3, bidiTotal); Assert.Equal(unidirectional ? QuicDefaults.DefaultServerMaxInboundUnidirectionalStreams * 3 : QuicDefaults.DefaultServerMaxInboundUnidirectionalStreams, unidiTotal); + + await clientConnection.DisposeAsync(); + await serverConnection.DisposeAsync(); } [Theory] @@ -368,6 +435,9 @@ public async Task GetStreamCapacity_OpenCloseStreamCanceledIntoNegative_CountsCo Assert.False(streamsAvailableFired.CurrentCount > 0); Assert.Equal(unidirectional ? QuicDefaults.DefaultServerMaxInboundBidirectionalStreams : QuicDefaults.DefaultServerMaxInboundBidirectionalStreams * 3, bidiTotal); Assert.Equal(unidirectional ? QuicDefaults.DefaultServerMaxInboundUnidirectionalStreams * 3 : QuicDefaults.DefaultServerMaxInboundUnidirectionalStreams, unidiTotal); + + await clientConnection.DisposeAsync(); + await serverConnection.DisposeAsync(); } [Fact] @@ -434,6 +504,9 @@ public async Task GetStreamCapacity_SumInvariant() // by now, we opened and closed 2 * Limit, and expect a budget of 'Limit' more Assert.Equal(3 * Limit, maxStreamIndex); + + await clientConnection.DisposeAsync(); + await serverConnection.DisposeAsync(); } [Fact] @@ -634,6 +707,8 @@ public async Task AcceptStreamAsync_ConnectionDisposed_Throws() var accept1Exception = await Assert.ThrowsAsync(async () => await acceptTask1); var accept2Exception = await Assert.ThrowsAsync(async () => await acceptTask2); + + await clientConnection.DisposeAsync(); } [Theory] diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs index c65b38c73c7467..85855ea4534bce 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs @@ -27,9 +27,9 @@ await Task.Run(async () => { await using QuicListener listener = await CreateQuicListener(); - var clientStreamTask = CreateQuicConnection(listener.LocalEndPoint); + var clientConnectionTask = CreateQuicConnection(listener.LocalEndPoint); await using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await using QuicConnection clientConnection = await clientStreamTask; + await using QuicConnection clientConnection = await clientConnectionTask; }).WaitAsync(TimeSpan.FromSeconds(6)); } @@ -57,9 +57,9 @@ await Task.Run(async () => await using QuicListener listener = await CreateQuicListener(new IPEndPoint(IPv6Any, 0)); - var clientStreamTask = CreateQuicConnection(new IPEndPoint(IPAddress.Loopback, listener.LocalEndPoint.Port)); + var clientConnectionTask = CreateQuicConnection(new IPEndPoint(IPAddress.Loopback, listener.LocalEndPoint.Port)); await using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await using QuicConnection clientConnection = await clientStreamTask; + await using QuicConnection clientConnection = await clientConnectionTask; }).WaitAsync(TimeSpan.FromSeconds(6)); } @@ -306,7 +306,7 @@ private async Task Listener_BacklogLimitRefusesConnection_ParallelClients_Client await using QuicListener listener = await CreateQuicListener(listenerOptions); // Kick off requested number of parallel connects. - List connectTasks = new List(); + List> connectTasks = new List>(); for (int i = 0; i < connectCount; ++i) { connectTasks.Add(CreateQuicConnection(listener.LocalEndPoint).AsTask()); @@ -319,7 +319,7 @@ await Parallel.ForEachAsync(connectTasks, async (connectTask, cancellationToken) { try { - await connectTask; + await using var connection = await connectTask; Interlocked.Increment(ref success); } catch (QuicException qex) when (qex.QuicError == QuicError.ConnectionRefused) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index c3e0e4e7372aba..0cc5c418719b61 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -120,30 +120,46 @@ internal ValueTask CreateQuicConnection(QuicClientConnectionOpti return QuicConnection.ConnectAsync(clientOptions); } - internal QuicListenerOptions CreateQuicListenerOptions(IPAddress address = null) + internal QuicListenerOptions CreateQuicListenerOptions(IPAddress address = null, Action changeServerOptions = null) { address ??= IPAddress.Loopback; return new QuicListenerOptions() { ListenEndPoint = new IPEndPoint(address, 0), ApplicationProtocols = new List() { ApplicationProtocol }, - ConnectionOptionsCallback = (_, _, _) => ValueTask.FromResult(CreateQuicServerOptions()) + ConnectionOptionsCallback = (_, _, _) => + { + var options = CreateQuicServerOptions(); + if (changeServerOptions is not null) + { + changeServerOptions(options); + } + return ValueTask.FromResult(options); + } }; } - internal ValueTask CreateQuicListener(IPAddress address = null) + internal ValueTask CreateQuicListener(IPAddress address = null, Action changeServerOptions = null) { - var options = CreateQuicListenerOptions(address); + var options = CreateQuicListenerOptions(address, changeServerOptions); return CreateQuicListener(options); } - internal ValueTask CreateQuicListener(IPEndPoint endpoint) + internal ValueTask CreateQuicListener(IPEndPoint endpoint, Action changeServerOptions = null) { var options = new QuicListenerOptions() { ListenEndPoint = endpoint, ApplicationProtocols = new List() { ApplicationProtocol }, - ConnectionOptionsCallback = (_, _, _) => ValueTask.FromResult(CreateQuicServerOptions()) + ConnectionOptionsCallback = (_, _, _) => + { + var options = CreateQuicServerOptions(); + if (changeServerOptions is not null) + { + changeServerOptions(options); + } + return ValueTask.FromResult(options); + } }; return CreateQuicListener(options); } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs index 699a41fcd1042b..3ee181108b42df 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestCollection.cs @@ -14,7 +14,7 @@ namespace System.Net.Quic.Tests; -[CollectionDefinition(nameof(QuicTestCollection))] +[CollectionDefinition(nameof(QuicTestCollection), DisableParallelization = true)] public unsafe class QuicTestCollection : ICollectionFixture, IDisposable { public static bool IsSupported => QuicListener.IsSupported && QuicConnection.IsSupported; diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs index 03eb9a35ffdf4e..3ee864e71cc8c5 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs @@ -11,6 +11,7 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; +using System.Text.Unicode; using System.Threading; using System.Threading.Tasks; @@ -1546,22 +1547,31 @@ private void ThrowIfOperationInProgress(bool operationCompleted, [CallerMemberNa // It checks for valid formatting, overlong encodings, surrogates, and value ranges. private static bool TryValidateUtf8(ReadOnlySpan span, bool endOfMessage, Utf8MessageState state) { + // If no prior segment spilled over and this one is the last, we can validate it efficiently as a complete message. + if (endOfMessage && !state.SequenceInProgress) + { + return Utf8.IsValid(span); + } + for (int i = 0; i < span.Length;) { // Have we started a character sequence yet? if (!state.SequenceInProgress) { + // Skip past ASCII bytes. + int firstNonAscii = span.Slice(i).IndexOfAnyExceptInRange((byte)0, (byte)127); + if (firstNonAscii < 0) + { + break; + } + i += firstNonAscii; + // The first byte tells us how many bytes are in the sequence. state.SequenceInProgress = true; byte b = span[i]; i++; - if ((b & 0x80) == 0) // 0bbbbbbb, single byte - { - state.AdditionalBytesExpected = 0; - state.CurrentDecodeBits = b & 0x7F; - state.ExpectedValueMin = 0; - } - else if ((b & 0xC0) == 0x80) + Debug.Assert((b & 0x80) != 0, "Should have already skipped past ASCII"); + if ((b & 0xC0) == 0x80) { // Misplaced 10bbbbbb continuation byte. This cannot be the first byte. return false; @@ -1589,6 +1599,7 @@ private static bool TryValidateUtf8(ReadOnlySpan span, bool endOfMessage, return false; } } + while (state.AdditionalBytesExpected > 0 && i < span.Length) { byte b = span[i]; @@ -1608,12 +1619,14 @@ private static bool TryValidateUtf8(ReadOnlySpan span, bool endOfMessage, // This is going to end up in the range of 0xD800-0xDFFF UTF-16 surrogates that are not allowed in UTF-8; return false; } + if (state.AdditionalBytesExpected == 2 && state.CurrentDecodeBits >= 0x110) { // This is going to be out of the upper Unicode bound 0x10FFFF. return false; } } + if (state.AdditionalBytesExpected == 0) { state.SequenceInProgress = false; @@ -1624,11 +1637,8 @@ private static bool TryValidateUtf8(ReadOnlySpan span, bool endOfMessage, } } } - if (endOfMessage && state.SequenceInProgress) - { - return false; - } - return true; + + return !endOfMessage || !state.SequenceInProgress; } private sealed class Utf8MessageState diff --git a/src/libraries/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj b/src/libraries/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj index f49bdcbbac0dee..807da709ea7552 100644 --- a/src/libraries/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj +++ b/src/libraries/System.Net.WebSockets/tests/System.Net.WebSockets.Tests.csproj @@ -14,11 +14,12 @@ - - - + + + + + + + diff --git a/src/libraries/System.Net.WebSockets/tests/WebSocketUtf8Tests.cs b/src/libraries/System.Net.WebSockets/tests/WebSocketUtf8Tests.cs new file mode 100644 index 00000000000000..ce449febeb2b4d --- /dev/null +++ b/src/libraries/System.Net.WebSockets/tests/WebSocketUtf8Tests.cs @@ -0,0 +1,144 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.Net.WebSockets.Tests +{ + public class WebSocketUtf8Tests + { + [Theory] + [InlineData(new byte[] { })] + [InlineData(new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64 })] // Hello World + [InlineData(new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2D, 0xC2, 0xB5, 0x40, 0xC3, 0x9F, 0xC3, 0xB6, 0xC3, 0xA4, 0xC3, 0xBC, 0xC3, 0xA0, 0xC3, 0xA1 })] // "Hello-µ@ßöäüàá"; + [InlineData(new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xf0, 0xa4, 0xad, 0xa2, 0x77, 0x6f, 0x72, 0x6c, 0x64 })] // "hello\U00024b62world" + [InlineData(new byte[] { 0xf0, 0xa4, 0xad, 0xa2 })] // "\U00024b62" + public async Task ValidateSingleValidSegments_Valid(byte[] data) + { + await WithConnectedWebSockets(async (ws1, ws2) => + { + Assert.True(await IsValidUtf8Async(ws1, ws2, data, endOfMessage: true)); + + for (int i = 0 ; i < data.Length; i++) + { + Assert.True(await IsValidUtf8Async(ws1, ws2, data.AsMemory(i, 1), endOfMessage: i == data.Length - 1)); + } + }); + } + + [Theory] + [InlineData(new byte[] { }, new byte[] { }, new byte[] { })] + [InlineData(new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20 }, new byte[] { }, new byte[] { 0x57, 0x6F, 0x72, 0x6C, 0x64 })] // Hello ,, World + [InlineData(new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2D, 0xC2, }, new byte[] { 0xB5, 0x40, 0xC3, 0x9F, 0xC3, 0xB6, 0xC3, 0xA4, }, new byte[] { 0xC3, 0xBC, 0xC3, 0xA0, 0xC3, 0xA1 })] // "Hello-µ@ßöäüàá"; + public async Task ValidateMultipleValidSegments_Valid(byte[] data1, byte[] data2, byte[] data3) + { + await WithConnectedWebSockets(async (ws1, ws2) => + { + Assert.True(await IsValidUtf8Async(ws1, ws2, data1, endOfMessage: false)); + Assert.True(await IsValidUtf8Async(ws1, ws2, data2, endOfMessage: false)); + Assert.True(await IsValidUtf8Async(ws1, ws2, data3, endOfMessage: false)); + + for (int i = 0; i < data1.Length; i++) + { + Assert.True(await IsValidUtf8Async(ws1, ws2, data1.AsMemory(i, 1), endOfMessage: false)); + } + for (int i = 0; i < data2.Length; i++) + { + Assert.True(await IsValidUtf8Async(ws1, ws2, data2.AsMemory(i, 1), endOfMessage: false)); + } + for (int i = 0; i < data3.Length; i++) + { + Assert.True(await IsValidUtf8Async(ws1, ws2, data3.AsMemory(i, 1), endOfMessage: i == data3.Length - 1)); + } + }); + } + + [Theory] + [InlineData(new byte[] { 0xfe })] + [InlineData(new byte[] { 0xff })] + [InlineData(new byte[] { 0xfe, 0xfe, 0xff, 0xff })] + [InlineData(new byte[] { 0xc0, 0xb1 })] // Overlong Ascii + [InlineData(new byte[] { 0xc1, 0xb1 })] // Overlong Ascii + [InlineData(new byte[] { 0xe0, 0x80, 0xaf })] // Overlong + [InlineData(new byte[] { 0xf0, 0x80, 0x80, 0xaf })] // Overlong + [InlineData(new byte[] { 0xf8, 0x80, 0x80, 0x80, 0xaf })] // Overlong + [InlineData(new byte[] { 0xfc, 0x80, 0x80, 0x80, 0x80, 0xaf })] // Overlong + [InlineData(new byte[] { 0xed, 0xa0, 0x80, 0x65, 0x64, 0x69, 0x74, 0x65, 0x64 })] // 0xEDA080 decodes to 0xD800, which is a reserved high surrogate character. + public async Task ValidateSingleInvalidSegment_Invalid(byte[] data) + { + await WithConnectedWebSockets(async (ws1, ws2) => + { + Assert.False(await IsValidUtf8Async(ws1, ws2, data, endOfMessage: true)); + }); + } + + [Fact] + public async Task ValidateIndividualInvalidSegments_Invalid() + { + byte[] data = [0xce, 0xba, 0xe1, 0xbd, 0xb9, 0xcf, 0x83, 0xce, 0xbc, 0xce, 0xb5, 0xed, 0xa0, 0x80, 0x65, 0x64, 0x69, 0x74, 0x65, 0x64]; + + await WithConnectedWebSockets(async (ws1, ws2) => + { + Assert.False(await IsValidUtf8Async(ws1, ws2, data, endOfMessage: false)); + }); + + await WithConnectedWebSockets(async (ws1, ws2) => + { + for (int i = 0; i < 12; i++) + { + Assert.True(await IsValidUtf8Async(ws1, ws2, data.AsMemory(i, 1), endOfMessage: false), i.ToString()); + } + + Assert.False(await IsValidUtf8Async(ws1, ws2, data.AsMemory(12, 1), endOfMessage: false), 12.ToString()); + }); + } + + [Fact] + public async Task ValidateMultipleInvalidSegments_Invalid() + { + byte[] data0 = [0xce, 0xba, 0xe1, 0xbd, 0xb9, 0xcf, 0x83, 0xce, 0xbc, 0xce, 0xb5, 0xf4]; + byte[] data1 = [0x90]; + + await WithConnectedWebSockets(async (ws1, ws2) => + { + Assert.True(await IsValidUtf8Async(ws1, ws2, data0, endOfMessage: false)); + Assert.False(await IsValidUtf8Async(ws1, ws2, data1, endOfMessage: false)); + }); + + await WithConnectedWebSockets(async (ws1, ws2) => + { + for (int i = 0; i < data0.Length; i++) + { + Assert.True(await IsValidUtf8Async(ws1, ws2, data0.AsMemory(i, 1), endOfMessage: false)); + } + + Assert.False(await IsValidUtf8Async(ws1, ws2, data1, endOfMessage: false)); + }); + } + + private static async ValueTask IsValidUtf8Async(WebSocket sender, WebSocket receiver, Memory buffer, bool endOfMessage) + { + await sender.SendAsync(buffer, WebSocketMessageType.Text, endOfMessage, CancellationToken.None).ConfigureAwait(false); + try + { + await receiver.ReceiveAsync(buffer, CancellationToken.None).ConfigureAwait(false); + return true; + } + catch (WebSocketException ex) when (ex.WebSocketErrorCode == WebSocketError.Faulted) + { + return false; + } + } + + private static async Task WithConnectedWebSockets(Func callback) + { + (Stream stream1, Stream stream2) = ConnectedStreams.CreateBidirectional(); + using WebSocket ws1 = WebSocket.CreateFromStream(stream1, isServer: false, subProtocol: null, Timeout.InfiniteTimeSpan); + using WebSocket ws2 = WebSocket.CreateFromStream(stream2, isServer: true, subProtocol: null, Timeout.InfiniteTimeSpan); + await callback(ws1, ws2).ConfigureAwait(false); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 406f3caecba30e..9045c03a920b96 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -22,8 +22,6 @@ true true true - true - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS $(DefineConstants);BIGENDIAN diff --git a/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs b/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs index b563c12dfa3567..8094b882ef5486 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs @@ -51,6 +51,7 @@ protected ArgumentNullException(SerializationInfo info, StreamingContext context /// Throws an if is null. /// The reference type argument to validate as non-null. /// The name of the parameter with which corresponds. + [Intrinsic] // Tier0 intrinsic to avoid redundant boxing in generics public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) { if (argument is null) @@ -59,6 +60,7 @@ public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpres } } + [Intrinsic] // Tier0 intrinsic to avoid redundant boxing in generics internal static void ThrowIfNull([NotNull] object? argument, ExceptionArgument paramName) { if (argument is null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Linux.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Linux.cs index 4f488f43a84aed..765ef5ab64e22c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Linux.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Linux.cs @@ -7,6 +7,6 @@ namespace System { public static partial class Environment { - public static long WorkingSet => (long)(Interop.procfs.TryReadStatusFile(ProcessId, out Interop.procfs.ParsedStatus status) ? status.VmRSS : 0); + public static long WorkingSet => (long)(Interop.procfs.TryReadStatusFile(Interop.procfs.ProcPid.Self, out Interop.procfs.ParsedStatus status) ? status.VmRSS : 0); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs index fb7ff75cb2e6c6..84199a3d306d28 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.SunOS.cs @@ -8,6 +8,6 @@ namespace System public static partial class Environment { public static long WorkingSet => - (long)(Interop.procfs.TryReadProcessStatusInfo(ProcessId, out Interop.procfs.ProcessStatusInfo status) ? status.ResidentSetSize : 0); + (long)(Interop.procfs.TryReadProcessStatusInfo(Interop.procfs.ProcPid.Self, out Interop.procfs.ProcessStatusInfo status) ? status.ResidentSetSize : 0); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 4d3419236ec5f8..8dea2d581b2b6e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -303,9 +303,11 @@ public Guid(string g) /// public static Guid CreateVersion7(DateTimeOffset timestamp) { - // This isn't the most optimal way, but we don't have an easy way to get - // secure random bytes in corelib without doing this since the secure rng - // is in a different layer. + // NewGuid uses CoCreateGuid on Windows and Interop.GetCryptographicallySecureRandomBytes on Unix to get + // cryptographically-secure random bytes. We could use Interop.BCrypt.BCryptGenRandom to generate the random + // bytes on Windows, as is done in RandomNumberGenerator, but that's measurably slower than using CoCreateGuid. + // And while CoCreateGuid only generates 122 bits of randomness, the other 6 bits being for the version / variant + // fields, this method also needs those bits to be non-random, so we can just use NewGuid for efficiency. Guid result = NewGuid(); // 2^48 is roughly 8925.5 years, which from the Unix Epoch means we won't diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs index d949017809f825..c328f858f1edca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs @@ -759,7 +759,7 @@ public static TextWriter Synchronized(TextWriter writer) { ArgumentNullException.ThrowIfNull(writer); -#if !TARGET_BROWSER || FEATURE_WASM_MANAGED_THREADS +#if (!TARGET_BROWSER && !TARGET_WASI) || FEATURE_WASM_MANAGED_THREADS return writer is SyncTextWriter ? writer : new SyncTextWriter(writer); #else return writer; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs index bfb5314bb97a7e..3d52204ba67f2f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.PlatformNotSupported.cs @@ -347,6 +347,26 @@ internal Arm64() { } /// public static unsafe Vector AddAcross(Vector value) { throw new PlatformNotSupportedException(); } + /// Complex add with rotate + + /// + /// svfloat64_t svcadd[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2, uint64_t imm_rotation) + /// FCADD Ztied1.D, Pg/M, Ztied1.D, Zop2.D, #imm_rotation + /// svfloat64_t svcadd[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2, uint64_t imm_rotation) + /// FCADD Ztied1.D, Pg/M, Ztied1.D, Zop2.D, #imm_rotation + /// svfloat64_t svcadd[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2, uint64_t imm_rotation) + /// + public static unsafe Vector AddRotateComplex(Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(1))] byte rotation) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svcadd[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2, uint64_t imm_rotation) + /// FCADD Ztied1.S, Pg/M, Ztied1.S, Zop2.S, #imm_rotation + /// svfloat32_t svcadd[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2, uint64_t imm_rotation) + /// FCADD Ztied1.S, Pg/M, Ztied1.S, Zop2.S, #imm_rotation + /// svfloat32_t svcadd[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2, uint64_t imm_rotation) + /// + public static unsafe Vector AddRotateComplex(Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(1))] byte rotation) { throw new PlatformNotSupportedException(); } + /// AddSaturate : Saturating add /// @@ -1859,6 +1879,54 @@ internal Arm64() { } public static unsafe Vector ConditionalSelect(Vector mask, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + /// ConvertToDouble : Floating-point convert + + /// + /// svfloat64_t svcvt_f64[_s32]_m(svfloat64_t inactive, svbool_t pg, svint32_t op) + /// SCVTF Ztied.D, Pg/M, Zop.S + /// svfloat64_t svcvt_f64[_s32]_x(svbool_t pg, svint32_t op) + /// SCVTF Ztied.D, Pg/M, Ztied.S + /// svfloat64_t svcvt_f64[_s32]_z(svbool_t pg, svint32_t op) + /// + public static unsafe Vector ConvertToDouble(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svcvt_f64[_s64]_m(svfloat64_t inactive, svbool_t pg, svint64_t op) + /// SCVTF Ztied.D, Pg/M, Zop.D + /// svfloat64_t svcvt_f64[_s64]_x(svbool_t pg, svint64_t op) + /// SCVTF Ztied.D, Pg/M, Ztied.D + /// svfloat64_t svcvt_f64[_s64]_z(svbool_t pg, svint64_t op) + /// + public static unsafe Vector ConvertToDouble(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svcvt_f64[_f32]_m(svfloat64_t inactive, svbool_t pg, svfloat32_t op) + /// FCVT Ztied.D, Pg/M, Zop.S + /// svfloat64_t svcvt_f64[_f32]_x(svbool_t pg, svfloat32_t op) + /// FCVT Ztied.D, Pg/M, Ztied.S + /// svfloat64_t svcvt_f64[_f32]_z(svbool_t pg, svfloat32_t op) + /// + public static unsafe Vector ConvertToDouble(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svcvt_f64[_u32]_m(svfloat64_t inactive, svbool_t pg, svuint32_t op) + /// UCVTF Ztied.D, Pg/M, Zop.S + /// svfloat64_t svcvt_f64[_u32]_x(svbool_t pg, svuint32_t op) + /// UCVTF Ztied.D, Pg/M, Ztied.S + /// svfloat64_t svcvt_f64[_u32]_z(svbool_t pg, svuint32_t op) + /// + public static unsafe Vector ConvertToDouble(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat64_t svcvt_f64[_u64]_m(svfloat64_t inactive, svbool_t pg, svuint64_t op) + /// UCVTF Ztied.D, Pg/M, Zop.D + /// svfloat64_t svcvt_f64[_u64]_x(svbool_t pg, svuint64_t op) + /// UCVTF Ztied.D, Pg/M, Ztied.D + /// svfloat64_t svcvt_f64[_u64]_z(svbool_t pg, svuint64_t op) + /// + public static unsafe Vector ConvertToDouble(Vector value) { throw new PlatformNotSupportedException(); } + + /// ConvertToInt32 : Floating-point convert /// @@ -1900,6 +1968,55 @@ internal Arm64() { } /// public static unsafe Vector ConvertToInt64(Vector value) { throw new PlatformNotSupportedException(); } + + /// ConvertToSingle : Floating-point convert + + /// + /// svfloat32_t svcvt_f32[_f64]_m(svfloat32_t inactive, svbool_t pg, svfloat64_t op) + /// FCVT Ztied.S, Pg/M, Zop.D + /// svfloat32_t svcvt_f32[_f64]_x(svbool_t pg, svfloat64_t op) + /// FCVT Ztied.S, Pg/M, Ztied.D + /// svfloat32_t svcvt_f32[_f64]_z(svbool_t pg, svfloat64_t op) + /// + public static unsafe Vector ConvertToSingle(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svcvt_f32[_s32]_m(svfloat32_t inactive, svbool_t pg, svint32_t op) + /// SCVTF Ztied.S, Pg/M, Zop.S + /// svfloat32_t svcvt_f32[_s32]_x(svbool_t pg, svint32_t op) + /// SCVTF Ztied.S, Pg/M, Ztied.S + /// svfloat32_t svcvt_f32[_s32]_z(svbool_t pg, svint32_t op) + /// + public static unsafe Vector ConvertToSingle(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svcvt_f32[_s64]_m(svfloat32_t inactive, svbool_t pg, svint64_t op) + /// SCVTF Ztied.S, Pg/M, Zop.D + /// svfloat32_t svcvt_f32[_s64]_x(svbool_t pg, svint64_t op) + /// SCVTF Ztied.S, Pg/M, Ztied.D + /// svfloat32_t svcvt_f32[_s64]_z(svbool_t pg, svint64_t op) + /// + public static unsafe Vector ConvertToSingle(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svcvt_f32[_u32]_m(svfloat32_t inactive, svbool_t pg, svuint32_t op) + /// UCVTF Ztied.S, Pg/M, Zop.S + /// svfloat32_t svcvt_f32[_u32]_x(svbool_t pg, svuint32_t op) + /// UCVTF Ztied.S, Pg/M, Ztied.S + /// svfloat32_t svcvt_f32[_u32]_z(svbool_t pg, svuint32_t op) + /// + public static unsafe Vector ConvertToSingle(Vector value) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svcvt_f32[_u64]_m(svfloat32_t inactive, svbool_t pg, svuint64_t op) + /// UCVTF Ztied.S, Pg/M, Zop.D + /// svfloat32_t svcvt_f32[_u64]_x(svbool_t pg, svuint64_t op) + /// UCVTF Ztied.S, Pg/M, Ztied.D + /// svfloat32_t svcvt_f32[_u64]_z(svbool_t pg, svuint64_t op) + /// + public static unsafe Vector ConvertToSingle(Vector value) { throw new PlatformNotSupportedException(); } + + /// ConvertToUInt32 : Floating-point convert /// @@ -5717,6 +5834,26 @@ internal Arm64() { } /// public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) { throw new PlatformNotSupportedException(); } + /// Complex multiply-add with rotate + + /// + /// svfloat64_t svcmla[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2, svfloat64_t op3, uint64_t imm_rotation) + /// FCMLA Ztied1.D, Pg/M, Zop2.D, Zop3.D, #imm_rotation + /// svfloat64_t svcmla[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2, svfloat64_t op3, uint64_t imm_rotation) + /// FCMLA Ztied1.D, Pg/M, Zop2.D, Zop3.D, #imm_rotation + /// svfloat64_t svcmla[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2, svfloat64_t op3, uint64_t imm_rotation) + /// + public static unsafe Vector MultiplyAddRotateComplex(Vector addend, Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(3))] byte rotation) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svcmla[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2, svfloat32_t op3, uint64_t imm_rotation) + /// FCMLA Ztied1.S, Pg/M, Zop2.S, Zop3.S, #imm_rotation + /// svfloat32_t svcmla[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2, svfloat32_t op3, uint64_t imm_rotation) + /// FCMLA Ztied1.S, Pg/M, Zop2.S, Zop3.S, #imm_rotation + /// svfloat32_t svcmla[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2, svfloat32_t op3, uint64_t imm_rotation) + /// + public static unsafe Vector MultiplyAddRotateComplex(Vector addend, Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(3))] byte rotation) { throw new PlatformNotSupportedException(); } + /// MultiplyBySelectedScalar : Multiply /// @@ -7287,6 +7424,208 @@ internal Arm64() { } public static unsafe void Scatter(Vector mask, ulong* address, Vector indicies, Vector data) { throw new PlatformNotSupportedException(); } + // Truncate to 16 bits and store + + // + // void svst1h_scatter[_u32base_s32](svbool_t pg, svuint32_t bases, svint32_t data) + // ST1H Zdata.S, Pg, [Zbases.S, #0] + // + // Removed as per #103297 + // public static unsafe void Scatter16BitNarrowing(Vector mask, Vector addresses, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1h_scatter[_u64base_s64](svbool_t pg, svuint64_t bases, svint64_t data) + // ST1H Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter16BitNarrowing(Vector mask, Vector addresses, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1h_scatter[_u32base_u32](svbool_t pg, svuint32_t bases, svuint32_t data) + // ST1H Zdata.S, Pg, [Zbases.S, #0] + // + // Removed as per #103297 + // public static unsafe void Scatter16BitNarrowing(Vector mask, Vector addresses, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1h_scatter[_u64base_u64](svbool_t pg, svuint64_t bases, svuint64_t data) + // ST1H Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter16BitNarrowing(Vector mask, Vector addresses, Vector data) { throw new PlatformNotSupportedException(); } + + + // Truncate to 16 bits and store + + // + // void svst1h_scatter_[s32]offset[_s32](svbool_t pg, int16_t *base, svint32_t offsets, svint32_t data) + // ST1H Zdata.S, Pg, [Xbase, Zoffsets.S, SXTW] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, short* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1h_scatter_[u32]offset[_s32](svbool_t pg, int16_t *base, svuint32_t offsets, svint32_t data) + // ST1H Zdata.S, Pg, [Xbase, Zoffsets.S, UXTW] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, short* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1h_scatter_[s64]offset[_s64](svbool_t pg, int16_t *base, svint64_t offsets, svint64_t data) + // ST1H Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, short* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1h_scatter_[u64]offset[_s64](svbool_t pg, int16_t *base, svuint64_t offsets, svint64_t data) + // ST1H Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, short* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1h_scatter_[s32]offset[_u32](svbool_t pg, uint16_t *base, svint32_t offsets, svuint32_t data) + // ST1H Zdata.S, Pg, [Xbase, Zoffsets.S, SXTW] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, ushort* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1h_scatter_[u32]offset[_u32](svbool_t pg, uint16_t *base, svuint32_t offsets, svuint32_t data) + // ST1H Zdata.S, Pg, [Xbase, Zoffsets.S, UXTW] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, ushort* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1h_scatter_[s64]offset[_u64](svbool_t pg, uint16_t *base, svint64_t offsets, svuint64_t data) + // ST1H Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, ushort* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1h_scatter_[u64]offset[_u64](svbool_t pg, uint16_t *base, svuint64_t offsets, svuint64_t data) + // ST1H Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, ushort* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + + // Truncate to 32 bits and store + + // + // void svst1w_scatter[_u64base_s64](svbool_t pg, svuint64_t bases, svint64_t data) + // ST1W Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter32BitNarrowing(Vector mask, Vector addresses, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1w_scatter[_u64base_u64](svbool_t pg, svuint64_t bases, svuint64_t data) + // ST1W Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter32BitNarrowing(Vector mask, Vector addresses, Vector data) { throw new PlatformNotSupportedException(); } + + + // Truncate to 32 bits and store, non-temporal + + // + // void svstnt1w_scatter_[s64]offset[_s64](svbool_t pg, int32_t *base, svint64_t offsets, svint64_t data) + // STNT1W Zdata.D, Pg, [Zoffsets.D, Xbase] + // + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(Vector mask, int* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svstnt1w_scatter_[u64]offset[_s64](svbool_t pg, int32_t *base, svuint64_t offsets, svint64_t data) + // STNT1W Zdata.D, Pg, [Zoffsets.D, Xbase] + // + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(Vector mask, int* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svstnt1w_scatter_[s64]offset[_u64](svbool_t pg, uint32_t *base, svint64_t offsets, svuint64_t data) + // STNT1W Zdata.D, Pg, [Zoffsets.D, Xbase] + // + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(Vector mask, uint* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svstnt1w_scatter_[u64]offset[_u64](svbool_t pg, uint32_t *base, svuint64_t offsets, svuint64_t data) + // STNT1W Zdata.D, Pg, [Zoffsets.D, Xbase] + // + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(Vector mask, uint* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + + // Truncate to 8 bits and store + + // + // void svst1b_scatter[_u32base_s32](svbool_t pg, svuint32_t bases, svint32_t data) + // ST1B Zdata.S, Pg, [Zbases.S, #0] + // + // Removed as per #103297 + // public static unsafe void Scatter8BitNarrowing(Vector mask, Vector addresses, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1b_scatter[_u64base_s64](svbool_t pg, svuint64_t bases, svint64_t data) + // ST1B Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter8BitNarrowing(Vector mask, Vector addresses, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1b_scatter[_u32base_u32](svbool_t pg, svuint32_t bases, svuint32_t data) + // ST1B Zdata.S, Pg, [Zbases.S, #0] + // + // Removed as per #103297 + // public static unsafe void Scatter8BitNarrowing(Vector mask, Vector addresses, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1b_scatter[_u64base_u64](svbool_t pg, svuint64_t bases, svuint64_t data) + // ST1B Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter8BitNarrowing(Vector mask, Vector addresses, Vector data) { throw new PlatformNotSupportedException(); } + + + // Truncate to 8 bits and store + + // + // void svst1b_scatter_[s32]offset[_s32](svbool_t pg, int8_t *base, svint32_t offsets, svint32_t data) + // ST1B Zdata.S, Pg, [Xbase, Zoffsets.S, SXTW] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, sbyte* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1b_scatter_[u32]offset[_s32](svbool_t pg, int8_t *base, svuint32_t offsets, svint32_t data) + // ST1B Zdata.S, Pg, [Xbase, Zoffsets.S, UXTW] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, sbyte* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1b_scatter_[s64]offset[_s64](svbool_t pg, int8_t *base, svint64_t offsets, svint64_t data) + // ST1B Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, sbyte* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1b_scatter_[u64]offset[_s64](svbool_t pg, int8_t *base, svuint64_t offsets, svint64_t data) + // ST1B Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, sbyte* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1b_scatter_[s32]offset[_u32](svbool_t pg, uint8_t *base, svint32_t offsets, svuint32_t data) + // ST1B Zdata.S, Pg, [Xbase, Zoffsets.S, SXTW] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, byte* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1b_scatter_[u32]offset[_u32](svbool_t pg, uint8_t *base, svuint32_t offsets, svuint32_t data) + // ST1B Zdata.S, Pg, [Xbase, Zoffsets.S, UXTW] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, byte* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1b_scatter_[s64]offset[_u64](svbool_t pg, uint8_t *base, svint64_t offsets, svuint64_t data) + // ST1B Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, byte* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + // + // void svst1b_scatter_[u64]offset[_u64](svbool_t pg, uint8_t *base, svuint64_t offsets, svuint64_t data) + // ST1B Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, byte* address, Vector offsets, Vector data) { throw new PlatformNotSupportedException(); } + + /// Logical shift left /// @@ -8631,6 +8970,21 @@ internal Arm64() { } public static unsafe Vector TransposeOdd(Vector left, Vector right) { throw new PlatformNotSupportedException(); } + /// Trigonometric multiply-add coefficient + + /// + /// svfloat64_t svtmad[_f64](svfloat64_t op1, svfloat64_t op2, uint64_t imm3) + /// FTMAD Ztied1.D, Ztied1.D, Zop2.D, #imm3 + /// + public static unsafe Vector TrigonometricMultiplyAddCoefficient(Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(7))] byte control) { throw new PlatformNotSupportedException(); } + + /// + /// svfloat32_t svtmad[_f32](svfloat32_t op1, svfloat32_t op2, uint64_t imm3) + /// FTMAD Ztied1.S, Ztied1.S, Zop2.S, #imm3 + /// + public static unsafe Vector TrigonometricMultiplyAddCoefficient(Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(7))] byte control) { throw new PlatformNotSupportedException(); } + + /// Trigonometric select coefficient /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs index a0b8086b679921..e7dc14f3a3f2cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Sve.cs @@ -377,6 +377,26 @@ internal Arm64() { } /// public static unsafe Vector AddAcross(Vector value) => AddAcross(value); + /// Complex add with rotate + + /// + /// svfloat64_t svcadd[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2, uint64_t imm_rotation) + /// FCADD Ztied1.D, Pg/M, Ztied1.D, Zop2.D, #imm_rotation + /// svfloat64_t svcadd[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2, uint64_t imm_rotation) + /// FCADD Ztied1.D, Pg/M, Ztied1.D, Zop2.D, #imm_rotation + /// svfloat64_t svcadd[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2, uint64_t imm_rotation) + /// + public static unsafe Vector AddRotateComplex(Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(1))] byte rotation) => AddRotateComplex(left, right, rotation); + + /// + /// svfloat32_t svcadd[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2, uint64_t imm_rotation) + /// FCADD Ztied1.S, Pg/M, Ztied1.S, Zop2.S, #imm_rotation + /// svfloat32_t svcadd[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2, uint64_t imm_rotation) + /// FCADD Ztied1.S, Pg/M, Ztied1.S, Zop2.S, #imm_rotation + /// svfloat32_t svcadd[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2, uint64_t imm_rotation) + /// + public static unsafe Vector AddRotateComplex(Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(1))] byte rotation) => AddRotateComplex(left, right, rotation); + /// AddSaturate : Saturating add /// @@ -1917,6 +1937,54 @@ internal Arm64() { } public static unsafe Vector ConditionalSelect(Vector mask, Vector left, Vector right) => ConditionalSelect(mask, left, right); + /// ConvertToDouble : Floating-point convert + + /// + /// svfloat64_t svcvt_f64[_s32]_m(svfloat64_t inactive, svbool_t pg, svint32_t op) + /// SCVTF Ztied.D, Pg/M, Zop.S + /// svfloat64_t svcvt_f64[_s32]_x(svbool_t pg, svint32_t op) + /// SCVTF Ztied.D, Pg/M, Ztied.S + /// svfloat64_t svcvt_f64[_s32]_z(svbool_t pg, svint32_t op) + /// + public static unsafe Vector ConvertToDouble(Vector value) => ConvertToDouble(value); + + /// + /// svfloat64_t svcvt_f64[_s64]_m(svfloat64_t inactive, svbool_t pg, svint64_t op) + /// SCVTF Ztied.D, Pg/M, Zop.D + /// svfloat64_t svcvt_f64[_s64]_x(svbool_t pg, svint64_t op) + /// SCVTF Ztied.D, Pg/M, Ztied.D + /// svfloat64_t svcvt_f64[_s64]_z(svbool_t pg, svint64_t op) + /// + public static unsafe Vector ConvertToDouble(Vector value) => ConvertToDouble(value); + + /// + /// svfloat64_t svcvt_f64[_f32]_m(svfloat64_t inactive, svbool_t pg, svfloat32_t op) + /// FCVT Ztied.D, Pg/M, Zop.S + /// svfloat64_t svcvt_f64[_f32]_x(svbool_t pg, svfloat32_t op) + /// FCVT Ztied.D, Pg/M, Ztied.S + /// svfloat64_t svcvt_f64[_f32]_z(svbool_t pg, svfloat32_t op) + /// + public static unsafe Vector ConvertToDouble(Vector value) => ConvertToDouble(value); + + /// + /// svfloat64_t svcvt_f64[_u32]_m(svfloat64_t inactive, svbool_t pg, svuint32_t op) + /// UCVTF Ztied.D, Pg/M, Zop.S + /// svfloat64_t svcvt_f64[_u32]_x(svbool_t pg, svuint32_t op) + /// UCVTF Ztied.D, Pg/M, Ztied.S + /// svfloat64_t svcvt_f64[_u32]_z(svbool_t pg, svuint32_t op) + /// + public static unsafe Vector ConvertToDouble(Vector value) => ConvertToDouble(value); + + /// + /// svfloat64_t svcvt_f64[_u64]_m(svfloat64_t inactive, svbool_t pg, svuint64_t op) + /// UCVTF Ztied.D, Pg/M, Zop.D + /// svfloat64_t svcvt_f64[_u64]_x(svbool_t pg, svuint64_t op) + /// UCVTF Ztied.D, Pg/M, Ztied.D + /// svfloat64_t svcvt_f64[_u64]_z(svbool_t pg, svuint64_t op) + /// + public static unsafe Vector ConvertToDouble(Vector value) => ConvertToDouble(value); + + /// ConvertToInt32 : Floating-point convert /// @@ -1959,6 +2027,54 @@ internal Arm64() { } public static unsafe Vector ConvertToInt64(Vector value) => ConvertToInt64(value); + /// ConvertToSingle : Floating-point convert + + /// + /// svfloat32_t svcvt_f32[_f64]_m(svfloat32_t inactive, svbool_t pg, svfloat64_t op) + /// FCVT Ztied.S, Pg/M, Zop.D + /// svfloat32_t svcvt_f32[_f64]_x(svbool_t pg, svfloat64_t op) + /// FCVT Ztied.S, Pg/M, Ztied.D + /// svfloat32_t svcvt_f32[_f64]_z(svbool_t pg, svfloat64_t op) + /// + public static unsafe Vector ConvertToSingle(Vector value) => ConvertToSingle(value); + + /// + /// svfloat32_t svcvt_f32[_s32]_m(svfloat32_t inactive, svbool_t pg, svint32_t op) + /// SCVTF Ztied.S, Pg/M, Zop.S + /// svfloat32_t svcvt_f32[_s32]_x(svbool_t pg, svint32_t op) + /// SCVTF Ztied.S, Pg/M, Ztied.S + /// svfloat32_t svcvt_f32[_s32]_z(svbool_t pg, svint32_t op) + /// + public static unsafe Vector ConvertToSingle(Vector value) => ConvertToSingle(value); + + /// + /// svfloat32_t svcvt_f32[_s64]_m(svfloat32_t inactive, svbool_t pg, svint64_t op) + /// SCVTF Ztied.S, Pg/M, Zop.D + /// svfloat32_t svcvt_f32[_s64]_x(svbool_t pg, svint64_t op) + /// SCVTF Ztied.S, Pg/M, Ztied.D + /// svfloat32_t svcvt_f32[_s64]_z(svbool_t pg, svint64_t op) + /// + public static unsafe Vector ConvertToSingle(Vector value) => ConvertToSingle(value); + + /// + /// svfloat32_t svcvt_f32[_u32]_m(svfloat32_t inactive, svbool_t pg, svuint32_t op) + /// UCVTF Ztied.S, Pg/M, Zop.S + /// svfloat32_t svcvt_f32[_u32]_x(svbool_t pg, svuint32_t op) + /// UCVTF Ztied.S, Pg/M, Ztied.S + /// svfloat32_t svcvt_f32[_u32]_z(svbool_t pg, svuint32_t op) + /// + public static unsafe Vector ConvertToSingle(Vector value) => ConvertToSingle(value); + + /// + /// svfloat32_t svcvt_f32[_u64]_m(svfloat32_t inactive, svbool_t pg, svuint64_t op) + /// UCVTF Ztied.S, Pg/M, Zop.D + /// svfloat32_t svcvt_f32[_u64]_x(svbool_t pg, svuint64_t op) + /// UCVTF Ztied.S, Pg/M, Ztied.D + /// svfloat32_t svcvt_f32[_u64]_z(svbool_t pg, svuint64_t op) + /// + public static unsafe Vector ConvertToSingle(Vector value) => ConvertToSingle(value); + + /// ConvertToUInt32 : Floating-point convert /// @@ -5773,6 +5889,26 @@ internal Arm64() { } /// public static unsafe Vector MultiplyAdd(Vector addend, Vector left, Vector right) => MultiplyAdd(addend, left, right); + /// Complex multiply-add with rotate + + /// + /// svfloat64_t svcmla[_f64]_m(svbool_t pg, svfloat64_t op1, svfloat64_t op2, svfloat64_t op3, uint64_t imm_rotation) + /// FCMLA Ztied1.D, Pg/M, Zop2.D, Zop3.D, #imm_rotation + /// svfloat64_t svcmla[_f64]_x(svbool_t pg, svfloat64_t op1, svfloat64_t op2, svfloat64_t op3, uint64_t imm_rotation) + /// FCMLA Ztied1.D, Pg/M, Zop2.D, Zop3.D, #imm_rotation + /// svfloat64_t svcmla[_f64]_z(svbool_t pg, svfloat64_t op1, svfloat64_t op2, svfloat64_t op3, uint64_t imm_rotation) + /// + public static unsafe Vector MultiplyAddRotateComplex(Vector addend, Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(3))] byte rotation) => MultiplyAddRotateComplex(addend, left, right, rotation); + + /// + /// svfloat32_t svcmla[_f32]_m(svbool_t pg, svfloat32_t op1, svfloat32_t op2, svfloat32_t op3, uint64_t imm_rotation) + /// FCMLA Ztied1.S, Pg/M, Zop2.S, Zop3.S, #imm_rotation + /// svfloat32_t svcmla[_f32]_x(svbool_t pg, svfloat32_t op1, svfloat32_t op2, svfloat32_t op3, uint64_t imm_rotation) + /// FCMLA Ztied1.S, Pg/M, Zop2.S, Zop3.S, #imm_rotation + /// svfloat32_t svcmla[_f32]_z(svbool_t pg, svfloat32_t op1, svfloat32_t op2, svfloat32_t op3, uint64_t imm_rotation) + /// + public static unsafe Vector MultiplyAddRotateComplex(Vector addend, Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(3))] byte rotation) => MultiplyAddRotateComplex(addend, left, right, rotation); + /// MultiplyBySelectedScalar : Multiply /// @@ -7332,6 +7468,208 @@ internal Arm64() { } public static unsafe void Scatter(Vector mask, ulong* address, Vector indicies, Vector data) => Scatter(mask, address, indicies, data); + // Truncate to 16 bits and store + + // + // void svst1h_scatter[_u32base_s32](svbool_t pg, svuint32_t bases, svint32_t data) + // ST1H Zdata.S, Pg, [Zbases.S, #0] + // + // Removed as per #103297 + // public static unsafe void Scatter16BitNarrowing(Vector mask, Vector addresses, Vector data) => Scatter16BitNarrowing(mask, addresses, data); + + // + // void svst1h_scatter[_u64base_s64](svbool_t pg, svuint64_t bases, svint64_t data) + // ST1H Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter16BitNarrowing(Vector mask, Vector addresses, Vector data) => Scatter16BitNarrowing(mask, addresses, data); + + // + // void svst1h_scatter[_u32base_u32](svbool_t pg, svuint32_t bases, svuint32_t data) + // ST1H Zdata.S, Pg, [Zbases.S, #0] + // + // Removed as per #103297 + // public static unsafe void Scatter16BitNarrowing(Vector mask, Vector addresses, Vector data) => Scatter16BitNarrowing(mask, addresses, data); + + // + // void svst1h_scatter[_u64base_u64](svbool_t pg, svuint64_t bases, svuint64_t data) + // ST1H Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter16BitNarrowing(Vector mask, Vector addresses, Vector data) => Scatter16BitNarrowing(mask, addresses, data); + + + // Truncate to 16 bits and store + + // + // void svst1h_scatter_[s32]offset[_s32](svbool_t pg, int16_t *base, svint32_t offsets, svint32_t data) + // ST1H Zdata.S, Pg, [Xbase, Zoffsets.S, SXTW] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, short* address, Vector offsets, Vector data) => Scatter16BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1h_scatter_[u32]offset[_s32](svbool_t pg, int16_t *base, svuint32_t offsets, svint32_t data) + // ST1H Zdata.S, Pg, [Xbase, Zoffsets.S, UXTW] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, short* address, Vector offsets, Vector data) => Scatter16BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1h_scatter_[s64]offset[_s64](svbool_t pg, int16_t *base, svint64_t offsets, svint64_t data) + // ST1H Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, short* address, Vector offsets, Vector data) => Scatter16BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1h_scatter_[u64]offset[_s64](svbool_t pg, int16_t *base, svuint64_t offsets, svint64_t data) + // ST1H Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, short* address, Vector offsets, Vector data) => Scatter16BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1h_scatter_[s32]offset[_u32](svbool_t pg, uint16_t *base, svint32_t offsets, svuint32_t data) + // ST1H Zdata.S, Pg, [Xbase, Zoffsets.S, SXTW] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, ushort* address, Vector offsets, Vector data) => Scatter16BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1h_scatter_[u32]offset[_u32](svbool_t pg, uint16_t *base, svuint32_t offsets, svuint32_t data) + // ST1H Zdata.S, Pg, [Xbase, Zoffsets.S, UXTW] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, ushort* address, Vector offsets, Vector data) => Scatter16BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1h_scatter_[s64]offset[_u64](svbool_t pg, uint16_t *base, svint64_t offsets, svuint64_t data) + // ST1H Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, ushort* address, Vector offsets, Vector data) => Scatter16BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1h_scatter_[u64]offset[_u64](svbool_t pg, uint16_t *base, svuint64_t offsets, svuint64_t data) + // ST1H Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(Vector mask, ushort* address, Vector offsets, Vector data) => Scatter16BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + + // Truncate to 32 bits and store + + // + // void svst1w_scatter[_u64base_s64](svbool_t pg, svuint64_t bases, svint64_t data) + // ST1W Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter32BitNarrowing(Vector mask, Vector addresses, Vector data) => Scatter32BitNarrowing(mask, addresses, data); + + // + // void svst1w_scatter[_u64base_u64](svbool_t pg, svuint64_t bases, svuint64_t data) + // ST1W Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter32BitNarrowing(Vector mask, Vector addresses, Vector data) => Scatter32BitNarrowing(mask, addresses, data); + + + // Truncate to 32 bits and store + + // + // void svst1w_scatter_[s64]offset[_s64](svbool_t pg, int32_t *base, svint64_t offsets, svint64_t data) + // ST1W Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(Vector mask, int* address, Vector offsets, Vector data) => Scatter32BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1w_scatter_[u64]offset[_s64](svbool_t pg, int32_t *base, svuint64_t offsets, svint64_t data) + // ST1W Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(Vector mask, int* address, Vector offsets, Vector data) => Scatter32BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1w_scatter_[s64]offset[_u64](svbool_t pg, uint32_t *base, svint64_t offsets, svuint64_t data) + // ST1W Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(Vector mask, uint* address, Vector offsets, Vector data) => Scatter32BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1w_scatter_[u64]offset[_u64](svbool_t pg, uint32_t *base, svuint64_t offsets, svuint64_t data) + // ST1W Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(Vector mask, uint* address, Vector offsets, Vector data) => Scatter32BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + + // Truncate to 8 bits and store + + // + // void svst1b_scatter[_u32base_s32](svbool_t pg, svuint32_t bases, svint32_t data) + // ST1B Zdata.S, Pg, [Zbases.S, #0] + // + // Removed as per #103297 + // public static unsafe void Scatter8BitNarrowing(Vector mask, Vector addresses, Vector data) => Scatter8BitNarrowing(mask, addresses, data); + + // + // void svst1b_scatter[_u64base_s64](svbool_t pg, svuint64_t bases, svint64_t data) + // ST1B Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter8BitNarrowing(Vector mask, Vector addresses, Vector data) => Scatter8BitNarrowing(mask, addresses, data); + + // + // void svst1b_scatter[_u32base_u32](svbool_t pg, svuint32_t bases, svuint32_t data) + // ST1B Zdata.S, Pg, [Zbases.S, #0] + // + // Removed as per #103297 + // public static unsafe void Scatter8BitNarrowing(Vector mask, Vector addresses, Vector data) => Scatter8BitNarrowing(mask, addresses, data); + + // + // void svst1b_scatter[_u64base_u64](svbool_t pg, svuint64_t bases, svuint64_t data) + // ST1B Zdata.D, Pg, [Zbases.D, #0] + // + public static unsafe void Scatter8BitNarrowing(Vector mask, Vector addresses, Vector data) => Scatter8BitNarrowing(mask, addresses, data); + + + // Truncate to 8 bits and store + + // + // void svst1b_scatter_[s32]offset[_s32](svbool_t pg, int8_t *base, svint32_t offsets, svint32_t data) + // ST1B Zdata.S, Pg, [Xbase, Zoffsets.S, SXTW] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, sbyte* address, Vector offsets, Vector data) => Scatter8BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1b_scatter_[u32]offset[_s32](svbool_t pg, int8_t *base, svuint32_t offsets, svint32_t data) + // ST1B Zdata.S, Pg, [Xbase, Zoffsets.S, UXTW] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, sbyte* address, Vector offsets, Vector data) => Scatter8BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1b_scatter_[s64]offset[_s64](svbool_t pg, int8_t *base, svint64_t offsets, svint64_t data) + // ST1B Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, sbyte* address, Vector offsets, Vector data) => Scatter8BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1b_scatter_[u64]offset[_s64](svbool_t pg, int8_t *base, svuint64_t offsets, svint64_t data) + // ST1B Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, sbyte* address, Vector offsets, Vector data) => Scatter8BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1b_scatter_[s32]offset[_u32](svbool_t pg, uint8_t *base, svint32_t offsets, svuint32_t data) + // ST1B Zdata.S, Pg, [Xbase, Zoffsets.S, SXTW] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, byte* address, Vector offsets, Vector data) => Scatter8BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1b_scatter_[u32]offset[_u32](svbool_t pg, uint8_t *base, svuint32_t offsets, svuint32_t data) + // ST1B Zdata.S, Pg, [Xbase, Zoffsets.S, UXTW] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, byte* address, Vector offsets, Vector data) => Scatter8BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1b_scatter_[s64]offset[_u64](svbool_t pg, uint8_t *base, svint64_t offsets, svuint64_t data) + // ST1B Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, byte* address, Vector offsets, Vector data) => Scatter8BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + // + // void svst1b_scatter_[u64]offset[_u64](svbool_t pg, uint8_t *base, svuint64_t offsets, svuint64_t data) + // ST1B Zdata.D, Pg, [Xbase, Zoffsets.D] + // + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(Vector mask, byte* address, Vector offsets, Vector data) => Scatter8BitWithByteOffsetsNarrowing(mask, address, offsets, data); + + /// Logical shift left /// @@ -8676,6 +9014,21 @@ internal Arm64() { } public static unsafe Vector TransposeOdd(Vector left, Vector right) => TransposeOdd(left, right); + /// Trigonometric multiply-add coefficient + + /// + /// svfloat64_t svtmad[_f64](svfloat64_t op1, svfloat64_t op2, uint64_t imm3) + /// FTMAD Ztied1.D, Ztied1.D, Zop2.D, #imm3 + /// + public static unsafe Vector TrigonometricMultiplyAddCoefficient(Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(7))] byte control) => TrigonometricMultiplyAddCoefficient(left, right, control); + + /// + /// svfloat32_t svtmad[_f32](svfloat32_t op1, svfloat32_t op2, uint64_t imm3) + /// FTMAD Ztied1.S, Ztied1.S, Zop2.S, #imm3 + /// + public static unsafe Vector TrigonometricMultiplyAddCoefficient(Vector left, Vector right, [ConstantExpected(Min = 0, Max = (byte)(7))] byte control) => TrigonometricMultiplyAddCoefficient(left, right, control); + + /// Trigonometric select coefficient /// diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index d0ba5fc6e9316b..4951f7829e7943 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -16,7 +16,11 @@ namespace System { public sealed partial class TimeZoneInfo { +#if TARGET_ILLUMOS || TARGET_SOLARIS + private const string DefaultTimeZoneDirectory = "/usr/share/lib/zoneinfo/"; +#else private const string DefaultTimeZoneDirectory = "/usr/share/zoneinfo/"; +#endif // Set fallback values using abbreviations, base offset, and id // These are expected in environments without time zone globalization data diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index 1afa8c2c7f7506..fc632d99b3387d 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -4364,6 +4364,9 @@ internal Arm64() { } public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector AddAcross(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector AddRotateComplex(System.Numerics.Vector left, System.Numerics.Vector right, [ConstantExpected(Min = 0, Max = (byte)(1))] byte rotation) { throw null; } + public static System.Numerics.Vector AddRotateComplex(System.Numerics.Vector left, System.Numerics.Vector right, [ConstantExpected(Min = 0, Max = (byte)(1))] byte rotation) { throw null; } + public static System.Numerics.Vector AddSaturate(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector AddSaturate(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector AddSaturate(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } @@ -4607,10 +4610,20 @@ internal Arm64() { } public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector ConditionalSelect(System.Numerics.Vector mask, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector ConvertToDouble(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToDouble(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToDouble(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToDouble(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToDouble(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector ConvertToInt32(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector ConvertToInt32(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector ConvertToInt64(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector ConvertToInt64(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToSingle(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToSingle(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToSingle(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToSingle(System.Numerics.Vector value) { throw null; } + public static System.Numerics.Vector ConvertToSingle(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector ConvertToUInt32(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector ConvertToUInt32(System.Numerics.Vector value) { throw null; } public static System.Numerics.Vector ConvertToUInt64(System.Numerics.Vector value) { throw null; } @@ -5178,6 +5191,9 @@ internal Arm64() { } public static System.Numerics.Vector MultiplyAdd(System.Numerics.Vector addend, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector MultiplyAdd(System.Numerics.Vector addend, System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector MultiplyAddRotateComplex(System.Numerics.Vector addend, System.Numerics.Vector left, System.Numerics.Vector right, [ConstantExpected(Min = 0, Max = (byte)(3))] byte rotation) { throw null; } + public static System.Numerics.Vector MultiplyAddRotateComplex(System.Numerics.Vector addend, System.Numerics.Vector left, System.Numerics.Vector right, [ConstantExpected(Min = 0, Max = (byte)(3))] byte rotation) { throw null; } + public static System.Numerics.Vector MultiplyBySelectedScalar(System.Numerics.Vector left, System.Numerics.Vector right, [ConstantExpected] byte rightIndex) { throw null; } public static System.Numerics.Vector MultiplyBySelectedScalar(System.Numerics.Vector left, System.Numerics.Vector right, [ConstantExpected] byte rightIndex) { throw null; } @@ -5411,6 +5427,43 @@ internal Arm64() { } public static unsafe void Scatter(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } public static unsafe void Scatter(System.Numerics.Vector mask, ulong* address, System.Numerics.Vector indicies, System.Numerics.Vector data) { throw null; } + + // public static unsafe void Scatter16BitNarrowing(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter16BitNarrowing(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } + // public static unsafe void Scatter16BitNarrowing(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter16BitNarrowing(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } + + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, short* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, short* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, short* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, short* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, ushort* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, ushort* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, ushort* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter16BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, ushort* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + + public static unsafe void Scatter32BitNarrowing(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter32BitNarrowing(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } + + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, int* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, int* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter32BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, uint* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + + // public static unsafe void Scatter8BitNarrowing(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter8BitNarrowing(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } + // public static unsafe void Scatter8BitNarrowing(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter8BitNarrowing(System.Numerics.Vector mask, System.Numerics.Vector addresses, System.Numerics.Vector data) { throw null; } + + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, sbyte* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, sbyte* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, sbyte* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, sbyte* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, byte* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, byte* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, byte* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static unsafe void Scatter8BitWithByteOffsetsNarrowing(System.Numerics.Vector mask, byte* address, System.Numerics.Vector offsets, System.Numerics.Vector data) { throw null; } + public static System.Numerics.Vector ShiftLeftLogical(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector ShiftLeftLogical(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector ShiftLeftLogical(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } @@ -5603,6 +5656,8 @@ internal Arm64() { } public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } public static System.Numerics.Vector TransposeOdd(System.Numerics.Vector left, System.Numerics.Vector right) { throw null; } + public static System.Numerics.Vector TrigonometricMultiplyAddCoefficient(System.Numerics.Vector left, System.Numerics.Vector right, [ConstantExpected(Min = 0, Max = (byte)(7))] byte control) { throw null; } + public static System.Numerics.Vector TrigonometricMultiplyAddCoefficient(System.Numerics.Vector left, System.Numerics.Vector right, [ConstantExpected(Min = 0, Max = (byte)(7))] byte control) { throw null; } public static System.Numerics.Vector TrigonometricSelectCoefficient(System.Numerics.Vector value, System.Numerics.Vector selector) { throw null; } public static System.Numerics.Vector TrigonometricSelectCoefficient(System.Numerics.Vector value, System.Numerics.Vector selector) { throw null; } public static System.Numerics.Vector TrigonometricStartingValue(System.Numerics.Vector value, System.Numerics.Vector sign) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Buffers.Tests/System.Buffers.Tests.csproj b/src/libraries/System.Runtime/tests/System.Buffers.Tests/System.Buffers.Tests.csproj index 66ae6833a50eb5..113d56e6f43635 100644 --- a/src/libraries/System.Runtime/tests/System.Buffers.Tests/System.Buffers.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Buffers.Tests/System.Buffers.Tests.csproj @@ -6,6 +6,7 @@ $(NetCoreAppCurrent) + true 1 diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/StreamReader/StreamReader.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/StreamReader/StreamReader.cs index 29677f512d9f73..efdf66257c31c7 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/StreamReader/StreamReader.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/StreamReader/StreamReader.cs @@ -29,7 +29,7 @@ public void ObjectClosedReadLineBaseStream() Assert.Throws(() => sr.ReadLine()); } - // Browser bypasses SyncTextWriter for faster startup + // single-threaded WASM bypasses SyncTextWriter for faster startup [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Synchronized_NewObject() { diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs index cc09f1c0d2032c..0abbc4d6add34d 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs @@ -7,7 +7,7 @@ namespace System.IO.Tests { public partial class WriteTests { - // Browser bypasses SyncTextWriter for faster startup + // single-threaded WASM bypasses SyncTextWriter for faster startup [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Synchronized_NewObject() { diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs index 47541635c8eef6..08dc045e4a5b5b 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs @@ -690,7 +690,7 @@ public void DisposeAsync_ExceptionReturnedInTask() Assert.Same(e, vt.AsTask().Exception.InnerException); } - // Browser bypasses SyncTextWriter for faster startup + // single-threaded WASM bypasses SyncTextWriter for faster startup [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task FlushAsync_Precanceled() { diff --git a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj index 4b916c09bb4b35..2f832312d7e2df 100644 --- a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj @@ -6,6 +6,7 @@ <_WasmPThreadPoolUnusedSize>10 + true diff --git a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx index 62b608677b74fa..630f2f89bc6ff6 100644 --- a/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography/src/Resources/Strings.resx @@ -327,6 +327,9 @@ {0} unexpectedly produced a ciphertext with the incorrect length. + + OpenSSL ENGINE is not available on this platform. + The total number of bytes extracted cannot exceed UInt32.MaxValue * hash length. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs index 9547edf23b06be..8d6f19f1a37ab2 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.cs @@ -355,48 +355,55 @@ public static bool AreEqualJsonNumbers(ReadOnlySpan left, ReadOnlySpan leftFirst; + ReadOnlySpan leftMiddle; + ReadOnlySpan leftLast; + + ReadOnlySpan rightFirst; + ReadOnlySpan rightMiddle; + ReadOnlySpan rightLast; + + int diff = leftIntegral.Length - rightIntegral.Length; + switch (diff) { - return leftIntegral.SequenceEqual(rightIntegral) && - leftFractional.SequenceEqual(rightFractional); + case < 0: + leftFirst = leftIntegral; + leftMiddle = leftFractional.Slice(0, -diff); + leftLast = leftFractional.Slice(-diff); + int rightOffset = rightIntegral.Length + diff; + rightFirst = rightIntegral.Slice(0, rightOffset); + rightMiddle = rightIntegral.Slice(rightOffset); + rightLast = rightFractional; + break; + + case 0: + leftFirst = leftIntegral; + leftMiddle = default; + leftLast = leftFractional; + rightFirst = rightIntegral; + rightMiddle = default; + rightLast = rightFractional; + break; + + case > 0: + int leftOffset = leftIntegral.Length - diff; + leftFirst = leftIntegral.Slice(0, leftOffset); + leftMiddle = leftIntegral.Slice(leftOffset); + leftLast = leftFractional; + rightFirst = rightIntegral; + rightMiddle = rightFractional.Slice(0, diff); + rightLast = rightFractional.Slice(diff); + break; } - // There is differentiation in the integral and fractional lengths, - // concatenate both into singular buffers and compare them. - scoped Span leftDigits; - scoped Span rightDigits; - byte[]? rentedLeftBuffer; - byte[]? rentedRightBuffer; - - if (nDigits <= JsonConstants.StackallocByteThreshold) - { - leftDigits = stackalloc byte[JsonConstants.StackallocByteThreshold]; - rightDigits = stackalloc byte[JsonConstants.StackallocByteThreshold]; - rentedLeftBuffer = rentedRightBuffer = null; - } - else - { - leftDigits = (rentedLeftBuffer = ArrayPool.Shared.Rent(nDigits)); - rightDigits = (rentedRightBuffer = ArrayPool.Shared.Rent(nDigits)); - } - - leftIntegral.CopyTo(leftDigits); - leftFractional.CopyTo(leftDigits.Slice(leftIntegral.Length)); - rightIntegral.CopyTo(rightDigits); - rightFractional.CopyTo(rightDigits.Slice(rightIntegral.Length)); - - bool result = leftDigits.Slice(0, nDigits).SequenceEqual(rightDigits.Slice(0, nDigits)); - - if (rentedLeftBuffer != null) - { - Debug.Assert(rentedRightBuffer != null); - rentedLeftBuffer.AsSpan(0, nDigits).Clear(); - rentedRightBuffer.AsSpan(0, nDigits).Clear(); - ArrayPool.Shared.Return(rentedLeftBuffer); - ArrayPool.Shared.Return(rentedRightBuffer); - } - - return result; + Debug.Assert(leftFirst.Length == rightFirst.Length); + Debug.Assert(leftMiddle.Length == rightMiddle.Length); + Debug.Assert(leftLast.Length == rightLast.Length); + return leftFirst.SequenceEqual(rightFirst) && + leftMiddle.SequenceEqual(rightMiddle) && + leftLast.SequenceEqual(rightLast); static void ParseNumber( ReadOnlySpan span, diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index 3608f1cae63d0d..81a3ac4d710978 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -39,6 +39,7 @@ '$(Scenario)' == 'gcstress0xc_jitminopts_heapverify1'">06:00:00 <_workItemTimeout Condition="'$(_workItemTimeout)' == '' and '$(TargetsAppleMobile)' == 'true'">01:15:00 <_workItemTimeout Condition="'$(_workItemTimeout)' == '' and '$(TargetOS)' == 'android'">00:30:00 + <_workItemTimeout Condition="'$(_workItemTimeout)' == '' and '$(TargetOS)' == 'browser'">00:30:00 <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">00:45:00 <_workItemTimeout Condition="'$(Scenario)' != '' and '$(_workItemTimeout)' == '' and ('$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">01:00:00 <_workItemTimeout Condition="'$(Scenario)' == '' and '$(_workItemTimeout)' == '' and ('$(TestScope)' == 'outerloop' or '$(TestScope)' == 'all')">00:20:00 diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 9137c694aa4ab8..ed02811e9b39a2 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -13,8 +13,6 @@ x64;x86;arm;armv6;arm64;riscv64;s390x;wasm;ppc64le true - true - $(DefineConstants);FEATURE_WASM_MANAGED_THREADS diff --git a/src/mono/browser/browser.proj b/src/mono/browser/browser.proj index 47a351fb53b415..e9923f10d4831a 100644 --- a/src/mono/browser/browser.proj +++ b/src/mono/browser/browser.proj @@ -217,6 +217,8 @@ + + @@ -277,6 +279,7 @@ 5MB + <_EmccLinkFlags Include="-Wno-unused-command-line-argument" /> <_EmccLinkFlags Include="-s INITIAL_MEMORY=$(EmccInitialHeapSize)" /> <_EmccLinkFlags Include="-s STACK_SIZE=$(EmccStackSize)" /> <_EmccCommonFlags Condition="'$(WasmEnableThreads)' == 'true'" Include="-s USE_PTHREADS=1" /> @@ -294,7 +297,7 @@ <_EmccLinkFlags Include="--source-map-base http://example.com" /> <_EmccLinkFlags Include="-s WASM_BIGINT=1" /> <_EmccLinkFlags Include="-s EXPORT_NAME="'createDotnetRuntime'"" /> - <_EmccLinkFlags Include="-s MODULARIZE=1"/> + <_EmccLinkFlags Include="-s MODULARIZE=1" /> <_EmccLinkFlags Include="-s ENVIRONMENT="web,webview,worker,node,shell"" /> + true + @executable_path/.. diff --git a/src/tests/Interop/Swift/SwiftCallbackAbiStress/SwiftCallbackAbiStress.cs b/src/tests/Interop/Swift/SwiftCallbackAbiStress/SwiftCallbackAbiStress.cs index cd00caec4667f4..e94c5c37570c4f 100644 --- a/src/tests/Interop/Swift/SwiftCallbackAbiStress/SwiftCallbackAbiStress.cs +++ b/src/tests/Interop/Swift/SwiftCallbackAbiStress/SwiftCallbackAbiStress.cs @@ -375,6 +375,7 @@ private static F3_Ret SwiftCallbackFunc3Callback(F3_S0 a0, float a1, ushort a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc3() { Console.Write("Running SwiftCallbackFunc3: "); @@ -460,6 +461,7 @@ private static F4_Ret SwiftCallbackFunc4Callback(double a0, F4_S0 a1, byte a2, i } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc4() { Console.Write("Running SwiftCallbackFunc4: "); @@ -588,6 +590,7 @@ private static F5_Ret SwiftCallbackFunc5Callback(byte a0, short a1, ulong a2, nu } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc5() { Console.Write("Running SwiftCallbackFunc5: "); @@ -712,6 +715,7 @@ private static F6_Ret SwiftCallbackFunc6Callback(float a0, F6_S0 a1, long a2, sb } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc6() { Console.Write("Running SwiftCallbackFunc6: "); @@ -866,6 +870,7 @@ private static F8_Ret SwiftCallbackFunc8Callback(F8_S0 a0, F8_S1 a1, SwiftSelf s } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc8() { Console.Write("Running SwiftCallbackFunc8: "); @@ -1061,6 +1066,7 @@ private static F10_Ret SwiftCallbackFunc10Callback(short a0, SwiftSelf self) } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc10() { Console.Write("Running SwiftCallbackFunc10: "); @@ -1169,6 +1175,7 @@ private static F11_Ret SwiftCallbackFunc11Callback(uint a0, nuint a1, ulong a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc11() { Console.Write("Running SwiftCallbackFunc11: "); @@ -1251,6 +1258,7 @@ private static F12_Ret SwiftCallbackFunc12Callback(F12_S0 a0, short a1, ulong a2 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc12() { Console.Write("Running SwiftCallbackFunc12: "); @@ -1742,6 +1750,7 @@ private static F18_Ret SwiftCallbackFunc18Callback(F18_S0 a0, F18_S1 a1, F18_S2 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc18() { Console.Write("Running SwiftCallbackFunc18: "); @@ -1855,6 +1864,7 @@ private static F19_Ret SwiftCallbackFunc19Callback(long a0, byte a1, F19_S0 a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc19() { Console.Write("Running SwiftCallbackFunc19: "); @@ -1960,6 +1970,7 @@ private static F20_Ret SwiftCallbackFunc20Callback(F20_S0 a0, F20_S1 a1, float a } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc20() { Console.Write("Running SwiftCallbackFunc20: "); @@ -2034,6 +2045,7 @@ private static F21_Ret SwiftCallbackFunc21Callback(int a0, short a1, F21_S0 a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc21() { Console.Write("Running SwiftCallbackFunc21: "); @@ -2157,6 +2169,7 @@ private static F22_Ret SwiftCallbackFunc22Callback(int a0, F22_S0 a1, F22_S1 a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc22() { Console.Write("Running SwiftCallbackFunc22: "); @@ -2415,6 +2428,7 @@ private static F25_Ret SwiftCallbackFunc25Callback(F25_S0 a0, ushort a1, nuint a } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc25() { Console.Write("Running SwiftCallbackFunc25: "); @@ -2511,6 +2525,7 @@ private static F26_Ret SwiftCallbackFunc26Callback(sbyte a0, byte a1, uint a2, F } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc26() { Console.Write("Running SwiftCallbackFunc26: "); @@ -2701,6 +2716,7 @@ private static F28_Ret SwiftCallbackFunc28Callback(uint a0, ushort a1, sbyte a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc28() { Console.Write("Running SwiftCallbackFunc28: "); @@ -2828,6 +2844,7 @@ private static F29_Ret SwiftCallbackFunc29Callback(F29_S0 a0, nint a1, ulong a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc29() { Console.Write("Running SwiftCallbackFunc29: "); @@ -2981,6 +2998,7 @@ private static F31_Ret SwiftCallbackFunc31Callback(F31_S0 a0, double a1, SwiftSe } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc31() { Console.Write("Running SwiftCallbackFunc31: "); @@ -3034,6 +3052,7 @@ private static F32_Ret SwiftCallbackFunc32Callback(ushort a0, short a1, SwiftSel } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc32() { Console.Write("Running SwiftCallbackFunc32: "); @@ -3413,6 +3432,7 @@ private static F37_Ret SwiftCallbackFunc37Callback(ulong a0, F37_S0 a1, double a } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc37() { Console.Write("Running SwiftCallbackFunc37: "); @@ -3730,6 +3750,7 @@ private static F41_Ret SwiftCallbackFunc41Callback(F41_S0 a0, SwiftSelf self) } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc41() { Console.Write("Running SwiftCallbackFunc41: "); @@ -3848,6 +3869,7 @@ private static F43_Ret SwiftCallbackFunc43Callback(F43_S0 a0, F43_S1 a1, SwiftSe } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc43() { Console.Write("Running SwiftCallbackFunc43: "); @@ -3952,6 +3974,7 @@ private static F44_Ret SwiftCallbackFunc44Callback(double a0, F44_S0 a1, F44_S1 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc44() { Console.Write("Running SwiftCallbackFunc44: "); @@ -4036,6 +4059,7 @@ private static F45_Ret SwiftCallbackFunc45Callback(F45_S0 a0, F45_S1 a1, byte a2 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc45() { Console.Write("Running SwiftCallbackFunc45: "); @@ -4095,6 +4119,7 @@ private static F46_Ret SwiftCallbackFunc46Callback(nint a0, nuint a1, ushort a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc46() { Console.Write("Running SwiftCallbackFunc46: "); @@ -4211,6 +4236,7 @@ private static F47_Ret SwiftCallbackFunc47Callback(nint a0, float a1, uint a2, F } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc47() { Console.Write("Running SwiftCallbackFunc47: "); @@ -4354,6 +4380,7 @@ private static F49_Ret SwiftCallbackFunc49Callback(F49_S0 a0, long a1, SwiftSelf } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc49() { Console.Write("Running SwiftCallbackFunc49: "); @@ -4531,6 +4558,7 @@ private static F51_Ret SwiftCallbackFunc51Callback(short a0, nuint a1, F51_S0 a2 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc51() { Console.Write("Running SwiftCallbackFunc51: "); @@ -4604,6 +4632,7 @@ private static F52_Ret SwiftCallbackFunc52Callback(nint a0, F52_S0 a1, short a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc52() { Console.Write("Running SwiftCallbackFunc52: "); @@ -4763,6 +4792,7 @@ private static F53_Ret SwiftCallbackFunc53Callback(F53_S0 a0, byte a1, long a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc53() { Console.Write("Running SwiftCallbackFunc53: "); @@ -4882,6 +4912,7 @@ private static F54_Ret SwiftCallbackFunc54Callback(ushort a0, F54_S0 a1, float a } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc54() { Console.Write("Running SwiftCallbackFunc54: "); @@ -4980,6 +5011,7 @@ private static F55_Ret SwiftCallbackFunc55Callback(F55_S0 a0, long a1, F55_S1 a2 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc55() { Console.Write("Running SwiftCallbackFunc55: "); @@ -5114,6 +5146,7 @@ private static F57_Ret SwiftCallbackFunc57Callback(sbyte a0, nuint a1, uint a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc57() { Console.Write("Running SwiftCallbackFunc57: "); @@ -5434,6 +5467,7 @@ private static F62_Ret SwiftCallbackFunc62Callback(F62_S0 a0, SwiftSelf self) } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc62() { Console.Write("Running SwiftCallbackFunc62: "); @@ -5560,6 +5594,7 @@ private static F64_Ret SwiftCallbackFunc64Callback(sbyte a0, F64_S0 a1, F64_S1 a } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc64() { Console.Write("Running SwiftCallbackFunc64: "); @@ -5659,6 +5694,7 @@ private static F65_Ret SwiftCallbackFunc65Callback(F65_S0 a0, short a1, double a } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc65() { Console.Write("Running SwiftCallbackFunc65: "); @@ -5726,6 +5762,7 @@ private static F66_Ret SwiftCallbackFunc66Callback(long a0, SwiftSelf self) } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc66() { Console.Write("Running SwiftCallbackFunc66: "); @@ -5933,6 +5970,7 @@ private static F68_Ret SwiftCallbackFunc68Callback(byte a0, float a1, int a2, ni } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc68() { Console.Write("Running SwiftCallbackFunc68: "); @@ -6032,6 +6070,7 @@ private static F69_Ret SwiftCallbackFunc69Callback(F69_S0 a0, nint a1, int a2, F } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc69() { Console.Write("Running SwiftCallbackFunc69: "); @@ -6148,6 +6187,7 @@ private static F70_Ret SwiftCallbackFunc70Callback(short a0, byte a1, nint a2, u } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc70() { Console.Write("Running SwiftCallbackFunc70: "); @@ -6260,6 +6300,7 @@ private static F72_Ret SwiftCallbackFunc72Callback(F72_S0 a0, long a1, sbyte a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc72() { Console.Write("Running SwiftCallbackFunc72: "); @@ -6511,6 +6552,7 @@ private static F75_Ret SwiftCallbackFunc75Callback(sbyte a0, sbyte a1, sbyte a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc75() { Console.Write("Running SwiftCallbackFunc75: "); @@ -6709,6 +6751,7 @@ private static F77_Ret SwiftCallbackFunc77Callback(double a0, F77_S0 a1, F77_S1 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc77() { Console.Write("Running SwiftCallbackFunc77: "); @@ -6867,6 +6910,7 @@ private static F79_Ret SwiftCallbackFunc79Callback(F79_S0 a0, float a1, SwiftSel } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc79() { Console.Write("Running SwiftCallbackFunc79: "); @@ -7007,6 +7051,7 @@ private static F81_Ret SwiftCallbackFunc81Callback(byte a0, uint a1, byte a2, F8 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc81() { Console.Write("Running SwiftCallbackFunc81: "); @@ -7156,6 +7201,7 @@ private static F83_Ret SwiftCallbackFunc83Callback(sbyte a0, F83_S0 a1, short a2 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc83() { Console.Write("Running SwiftCallbackFunc83: "); @@ -7382,6 +7428,7 @@ private static F85_Ret SwiftCallbackFunc85Callback(F85_S0 a0, F85_S1 a1, uint a2 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc85() { Console.Write("Running SwiftCallbackFunc85: "); @@ -7486,6 +7533,7 @@ private static F86_Ret SwiftCallbackFunc86Callback(float a0, short a1, nint a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc86() { Console.Write("Running SwiftCallbackFunc86: "); @@ -7634,6 +7682,7 @@ private static F88_Ret SwiftCallbackFunc88Callback(F88_S0 a0, F88_S1 a1, float a } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc88() { Console.Write("Running SwiftCallbackFunc88: "); @@ -7703,6 +7752,7 @@ private static F89_Ret SwiftCallbackFunc89Callback(F89_S0 a0, SwiftSelf self) } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc89() { Console.Write("Running SwiftCallbackFunc89: "); @@ -7811,6 +7861,7 @@ private static F90_Ret SwiftCallbackFunc90Callback(long a0, float a1, F90_S0 a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc90() { Console.Write("Running SwiftCallbackFunc90: "); @@ -7931,6 +7982,7 @@ private static F91_Ret SwiftCallbackFunc91Callback(F91_S0 a0, short a1, uint a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc91() { Console.Write("Running SwiftCallbackFunc91: "); @@ -8024,6 +8076,7 @@ private static F92_Ret SwiftCallbackFunc92Callback(uint a0, long a1, F92_S0 a2, } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc92() { Console.Write("Running SwiftCallbackFunc92: "); @@ -8087,6 +8140,7 @@ private static F93_Ret SwiftCallbackFunc93Callback(nuint a0, ushort a1, double a } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc93() { Console.Write("Running SwiftCallbackFunc93: "); @@ -8196,6 +8250,7 @@ private static F94_Ret SwiftCallbackFunc94Callback(F94_S0 a0, short a1, F94_S1 a } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc94() { Console.Write("Running SwiftCallbackFunc94: "); @@ -8285,6 +8340,7 @@ private static F95_Ret SwiftCallbackFunc95Callback(F95_S0 a0, nuint a1, F95_S1 a } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc95() { Console.Write("Running SwiftCallbackFunc95: "); @@ -8448,6 +8504,7 @@ private static F97_Ret SwiftCallbackFunc97Callback(F97_S0 a0, F97_S1 a1, F97_S2 } [Fact] + [SkipOnMono("Struct lowering for reverse pinvokes returns not supported on Mono")] public static void TestSwiftCallbackFunc97() { Console.Write("Running SwiftCallbackFunc97: "); diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs index 95619d36dfe3b9..1e3b4c342b1a3e 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/Helpers.cs @@ -5234,6 +5234,28 @@ private static (ulong val, bool ovf) ShiftOvf(ulong value, int shift) public static float MinNumberPairwise(float[] op1, float[] op2, int i) => Pairwise(MinNumber, op1, op2, i); + public static float[] MultiplyAddRotateComplex(float[] op1, float[] op2, float[] op3, byte imm) + { + for (int i = 0; i < op1.Length; i += 2) + { + int real = i; + int img = i + 1; + (float ans1, float ans2) = imm switch + { + 0 => (FusedMultiplyAdd(op1[real], op2[real], op3[real]), FusedMultiplyAdd(op1[img], op2[real], op3[img])), + 1 => (FusedMultiplySubtract(op1[real], op2[img], op3[img]), FusedMultiplyAdd(op1[img], op2[img], op3[i])), + 2 => (FusedMultiplySubtract(op1[real], op2[real], op3[real]), FusedMultiplySubtract(op1[img], op2[real], op3[img])), + 3 => (FusedMultiplyAdd(op1[real], op2[img], op3[img]), FusedMultiplySubtract(op1[img], op2[img], op3[real])), + _ => (0.0f, 0.0f) + }; + + op1[real] = ans1; + op1[img] = ans2; + } + + return op1; + } + public static float MultiplyExtended(float op1, float op2) { bool inf1 = float.IsInfinity(op1); @@ -5252,6 +5274,33 @@ public static float MultiplyExtended(float op1, float op2) } } + public static float TrigonometricMultiplyAddCoefficient(float op1, float op2, byte imm) + { + int index = (op2 < 0) ? (imm + 8) : imm; + uint coeff = index switch + { + 0 => 0x3f800000, + 1 => 0xbe2aaaab, + 2 => 0x3c088886, + 3 => 0xb95008b9, + 4 => 0x36369d6d, + 5 => 0x00000000, + 6 => 0x00000000, + 7 => 0x00000000, + 8 => 0x3f800000, + 9 => 0xbf000000, + 10 => 0x3d2aaaa6, + 11 => 0xbab60705, + 12 => 0x37cd37cc, + 13 => 0x00000000, + 14 => 0x00000000, + 15 => 0x00000000, + _ => 0x00000000 + }; + + return MathF.FusedMultiplyAdd(op1, Math.Abs(op2), BitConverter.UInt32BitsToSingle(coeff)); + } + public static float TrigonometricSelectCoefficient(float op1, uint op2) { float result = ((op2 % 2) == 0) ? op1 : (float)1.0; @@ -5384,6 +5433,28 @@ public static float FPExponentialAccelerator(uint op1) public static double MinNumberPairwise(double[] op1, double[] op2, int i) => Pairwise(MinNumber, op1, op2, i); + public static double[] MultiplyAddRotateComplex(double[] op1, double[] op2, double[] op3, byte imm) + { + for (int i = 0; i < op1.Length; i += 2) + { + int real = i; + int img = i + 1; + (double ans1, double ans2) = imm switch + { + 0 => (FusedMultiplyAdd(op1[real], op2[real], op3[real]), FusedMultiplyAdd(op1[img], op2[real], op3[img])), + 1 => (FusedMultiplySubtract(op1[real], op2[img], op3[img]), FusedMultiplyAdd(op1[img], op2[img], op3[i])), + 2 => (FusedMultiplySubtract(op1[real], op2[real], op3[real]), FusedMultiplySubtract(op1[img], op2[real], op3[img])), + 3 => (FusedMultiplyAdd(op1[real], op2[img], op3[img]), FusedMultiplySubtract(op1[img], op2[img], op3[real])), + _ => (0.0, 0.0) + }; + + op1[real] = ans1; + op1[img] = ans2; + } + + return op1; + } + public static double MultiplyExtended(double op1, double op2) { bool inf1 = double.IsInfinity(op1); @@ -5402,6 +5473,33 @@ public static double MultiplyExtended(double op1, double op2) } } + public static double TrigonometricMultiplyAddCoefficient(double op1, double op2, byte imm) + { + int index = (op2 < 0) ? (imm + 8) : imm; + ulong coeff = index switch + { + 0 => 0x3ff0000000000000, + 1 => 0xbfc5555555555543, + 2 => 0x3f8111111110f30c, + 3 => 0xbf2a01a019b92fc6, + 4 => 0x3ec71de351f3d22b, + 5 => 0xbe5ae5e2b60f7b91, + 6 => 0x3de5d8408868552f, + 7 => 0x0000000000000000, + 8 => 0x3ff0000000000000, + 9 => 0xbfe0000000000000, + 10 => 0x3fa5555555555536, + 11 => 0xbf56c16c16c13a0b, + 12 => 0x3efa01a019b1e8d8, + 13 => 0xbe927e4f7282f468, + 14 => 0x3e21ee96d2641b13, + 15 => 0xbda8f76380fbb401, + _ => 0x0000000000000000 + }; + + return Math.FusedMultiplyAdd(op1, Math.Abs(op2), BitConverter.UInt64BitsToDouble(coeff)); + } + public static double TrigonometricSelectCoefficient(double op1, ulong op2) { double result = ((op2 % 2) == 0) ? op1 : 1.0; @@ -6030,6 +6128,28 @@ private static ulong Pairwise(Func pairOp, ulong[] op1, ulo public static float AddPairwise(float[] op1, float[] op2, int i) => Pairwise(Add, op1, op2, i); + public static float[] AddRotateComplex(float[] op1, float[] op2, byte rot) + { + for (int i = 0; i < op1.Length; i += 2) + { + int real = i; + int img = i + 1; + + if (rot == 0) + { + op1[real] -= op2[img]; + op1[img] += op2[real]; + } + else + { + op1[real] += op2[img]; + op1[img] -= op2[real]; + } + } + + return op1; + } + public static float Max(float op1, float op2) => Math.Max(op1, op2); public static float MaxPairwise(float[] op1, int i) => Pairwise(Max, op1, i); @@ -6080,6 +6200,28 @@ private static float Pairwise(Func pairOp, float[] op1, flo public static double AddPairwise(double[] op1, double[] op2, int i) => Pairwise(Add, op1, op2, i); + public static double[] AddRotateComplex(double[] op1, double[] op2, byte rot) + { + for (int i = 0; i < op1.Length; i += 2) + { + int real = i; + int img = i + 1; + + if (rot == 0) + { + op1[real] -= op2[img]; + op1[img] += op2[real]; + } + else + { + op1[real] += op2[img]; + op1[img] -= op2[real]; + } + } + + return op1; + } + public static double Max(double op1, double op2) => Math.Max(op1, op2); public static double MaxPairwise(double[] op1, int i) => Pairwise(Max, op1, i); @@ -6939,21 +7081,75 @@ public static ulong ShiftRightAndInsert(ulong left, ulong right, byte shift) public static float RoundToZero(float op1) => MathF.Round(op1, MidpointRounding.ToZero); - public static int ConvertDoubleToInt32(double op1) => (int)Math.Clamp(op1, long.MinValue, long.MaxValue); + private static int ConvertToInt32(double op1) => (int)Math.Clamp(op1, int.MinValue, int.MaxValue); - public static int ConvertToInt32(float op1) => (int)Math.Clamp(op1, int.MinValue, int.MaxValue); + public static int[] ConvertToInt32(double[] op1) + { + int[] result = new int[op1.Length * 2]; + + for (int i = 0; i < op1.Length; i++) + { + int index = i * 2; + result[index] = ConvertToInt32(op1[i]); + if (op1[i] < 0) + { + // Sign-extend next lane with all ones + result[index + 1] = -1; + } + } + + return result; + } + + public static int[] ConvertToInt32(float[] op1) => Array.ConvertAll(op1, num => ConvertToInt32(num)); + + private static long ConvertToInt64(double op1) => (long)Math.Clamp(op1, long.MinValue, long.MaxValue); - public static long ConvertToInt64(double op1) => (long)Math.Clamp(op1, long.MinValue, long.MaxValue); + public static long[] ConvertToInt64(double[] op1) => Array.ConvertAll(op1, num => ConvertToInt64(num)); - public static long ConvertFloatToInt64(float op1) => (long)Math.Clamp(op1, int.MinValue, int.MaxValue); + public static long[] ConvertToInt64(float[] op1) + { + long[] result = new long[op1.Length / 2]; - public static uint ConvertDoubleToUInt32(double op1) => (uint)Math.Clamp(op1, ulong.MinValue, ulong.MaxValue); + for (int i = 0; i < result.Length; i++) + { + result[i] = ConvertToInt64(op1[i * 2]); + } - public static uint ConvertToUInt32(float op1) => (uint)Math.Clamp(op1, uint.MinValue, uint.MaxValue); + return result; + } - public static ulong ConvertToUInt64(double op1) => (ulong)Math.Clamp(op1, ulong.MinValue, ulong.MaxValue); + private static uint ConvertToUInt32(double op1) => (uint)Math.Clamp(op1, uint.MinValue, uint.MaxValue); - public static ulong ConvertFloatToUInt64(float op1) => (ulong)Math.Clamp(op1, uint.MinValue, uint.MaxValue); + public static uint[] ConvertToUInt32(double[] op1) + { + uint[] result = new uint[op1.Length * 2]; + + for (int i = 0; i < op1.Length; i++) + { + result[i * 2] = ConvertToUInt32(op1[i]); + } + + return result; + } + + public static uint[] ConvertToUInt32(float[] op1) => Array.ConvertAll(op1, num => ConvertToUInt32(num)); + + private static ulong ConvertToUInt64(double op1) => (ulong)Math.Clamp(op1, ulong.MinValue, ulong.MaxValue); + + public static ulong[] ConvertToUInt64(double[] op1) => Array.ConvertAll(op1, num => ConvertToUInt64(num)); + + public static ulong[] ConvertToUInt64(float[] op1) + { + ulong[] result = new ulong[op1.Length / 2]; + + for (int i = 0; i < result.Length; i++) + { + result[i] = ConvertToUInt64(op1[i * 2]); + } + + return result; + } public static Int32 ConvertToInt32RoundAwayFromZero(float op1) => ConvertToInt32(RoundAwayFromZero(op1)); @@ -7001,16 +7197,96 @@ public static ulong ShiftRightAndInsert(ulong left, ulong right, byte shift) public static float ConvertToSingle(double op1) => (float)op1; + public static float[] ConvertToSingle(double[] op1) + { + float[] result = new float[op1.Length * 2]; + + for (int i = 0; i < op1.Length; i++) + { + result[i * 2] = (float)op1[i]; + } + + return result; + } + + public static float[] ConvertToSingle(int[] op1) => Array.ConvertAll(op1, num => (float)num); + + public static float[] ConvertToSingle(long[] op1) + { + float[] result = new float[op1.Length * 2]; + + for (int i = 0; i < op1.Length; i++) + { + result[i * 2] = (float)op1[i]; + } + + return result; + } + + public static float[] ConvertToSingle(uint[] op1) => Array.ConvertAll(op1, num => (float)num); + + public static float[] ConvertToSingle(ulong[] op1) + { + float[] result = new float[op1.Length * 2]; + + for (int i = 0; i < op1.Length; i++) + { + result[i * 2] = (float)op1[i]; + } + + return result; + } + public static float ConvertToSingleUpper(float[] op1, double[] op2, int i) => i < op1.Length ? op1[i] : ConvertToSingle(op2[i - op1.Length]); public static double ConvertToDouble(float op1) => op1; - public static double ConvertToDoubleUpper(float[] op1, int i) => ConvertToDouble(op1[i + op1.Length / 2]); - public static double ConvertToDouble(long op1) => op1; public static double ConvertToDouble(ulong op1) => op1; + public static double[] ConvertToDouble(float[] op1) + { + double[] result = new double[op1.Length / 2]; + + for (int i = 0; i < result.Length; i++) + { + result[i] = op1[i * 2]; + } + + return result; + } + + public static double[] ConvertToDouble(int[] op1) + { + double[] result = new double[op1.Length / 2]; + + for (int i = 0; i < result.Length; i++) + { + result[i] = op1[i * 2]; + } + + return result; + } + + public static double[] ConvertToDouble(long[] op1) => Array.ConvertAll(op1, num => (double)num); + + public static double[] ConvertToDouble(uint[] op1) + { + double[] result = new double[op1.Length / 2]; + + for (int i = 0; i < result.Length; i++) + { + result[i] = op1[i * 2]; + } + + return result; + } + + public static double[] ConvertToDouble(ulong[] op1) => Array.ConvertAll(op1, num => (double)num); + + public static double ConvertToDoubleUpper(float[] op1, int i) => ConvertToDouble(op1[i + op1.Length / 2]); + public static short ReverseElement8(short val) { ulong result = 0UL; diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveScatterVectorIndices.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveScatterVectorOffsets.template similarity index 98% rename from src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveScatterVectorIndices.template rename to src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveScatterVectorOffsets.template index 903e79e1f376b1..9b27b962166151 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveScatterVectorIndices.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/SveScatterVectorOffsets.template @@ -150,7 +150,7 @@ namespace JIT.HardwareIntrinsics.Arm._Sve if(!dict.Add(input[i])) { // value already exist - input[i] += 1; + input[i] = (input[i] + 1) % ({Op3BaseType})OutElementCount; continue; } i++; @@ -421,7 +421,7 @@ private void ValidateResult({Op1BaseType}[] mask, {Op1BaseType}[] data, {Op2Base { bool succeeded = true; - var actualResult = new {Op1BaseType}[DataElementCount]; + var actualResult = new {Op2BaseType}[DataElementCount]; for (var i = 0; i < DataElementCount; i++) { actualResult[i] = *({Op2BaseType}*)(baseAddr + offset[i]); @@ -430,7 +430,7 @@ private void ValidateResult({Op1BaseType}[] mask, {Op1BaseType}[] data, {Op2Base for (var i = 0; i < DataElementCount; i++) { {Op1BaseType} expectedResult = mask[i] == 0 ? 0 : data[i]; - if (actualResult[i] != expectedResult) + if (actualResult[i] != ({Op2BaseType})expectedResult) { succeeded = false; break; @@ -475,7 +475,7 @@ private void ValidateZeroResult(void* data, void* baseAddr, void* offset, [Calle private void ValidateZeroResult({Op1BaseType}[] data, {Op2BaseType}* baseAddr, {Op3BaseType}[] offset, [CallerMemberName] string method = "") { bool succeeded = true; - var actualResult = new {Op1BaseType}[DataElementCount]; + var actualResult = new {Op2BaseType}[DataElementCount]; for (var i = 0; i < DataElementCount; i++) { actualResult[i] = *({Op2BaseType}*)(baseAddr + offset[i]); diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImmBinaryOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImmBinaryOpTestTemplate.template index 33d9c59e8dde18..e3c3957aa01fd6 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImmBinaryOpTestTemplate.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImmBinaryOpTestTemplate.template @@ -9,6 +9,7 @@ ******************************************************************************/ using System; +using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -62,6 +63,9 @@ namespace JIT.HardwareIntrinsics.Arm // Validates executing the test inside conditional, with op3 as zero test.ConditionalSelect_ZeroOp(); + + // Validates basic functionality fails with an invalid imm, using Unsafe.ReadUnaligned + test.RunBasicScenario_UnsafeRead_InvalidImm(); } else { @@ -142,7 +146,7 @@ namespace JIT.HardwareIntrinsics.Arm for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); - for (var i = 0; i < Op1ElementCount; i++) { _data2[i] = {NextValueOp1}; } + for (var i = 0; i < Op1ElementCount; i++) { _data2[i] = {NextValueOp2}; } Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref testStruct._fld2), ref Unsafe.As<{Op1BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); return testStruct; @@ -170,7 +174,7 @@ namespace JIT.HardwareIntrinsics.Arm private {Op1VectorType}<{Op1BaseType}> _mask; private {Op1VectorType}<{Op1BaseType}> _fld1; private {Op1VectorType}<{Op1BaseType}> _fld2; - private {Op1VectorType}<{Op1BaseType}> _falseFld; + private {Op1VectorType}<{Op1BaseType}> _falseFld; private DataTable _dataTable; @@ -178,16 +182,16 @@ namespace JIT.HardwareIntrinsics.Arm { Succeeded = true; - for (var i = 0; i < Op1ElementCount; i++) { _maskData[i] = ({Op1BaseType})({NextValueOp1} % 2); } + for (var i = 0; i < Op1ElementCount; i++) { _maskData[i] = ({Op1BaseType})({NextValueMask} % 2); } Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _mask), ref Unsafe.As<{Op1BaseType}, byte>(ref _maskData[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); - for (var i = 0; i < Op1ElementCount; i++) { _data2[i] = {NextValueOp1}; } + for (var i = 0; i < Op1ElementCount; i++) { _data2[i] = {NextValueOp2}; } Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld2), ref Unsafe.As<{Op1BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _falseFld), ref Unsafe.As<{Op1BaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } - for (var i = 0; i < Op1ElementCount; i++) { _data2[i] = {NextValueOp1}; } + for (var i = 0; i < Op1ElementCount; i++) { _data2[i] = {NextValueOp2}; } _dataTable = new DataTable(_data1, _data2, new {RetBaseType}[RetElementCount], LargestVectorSize); } @@ -209,6 +213,31 @@ namespace JIT.HardwareIntrinsics.Arm ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr); } + public void RunBasicScenario_UnsafeRead_InvalidImm() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead_InvalidImm)); + + bool succeeded = false; + try + { + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray2Ptr), + {InvalidImm} + ); + Console.WriteLine(result); + } + catch (ArgumentOutOfRangeException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + public void RunBasicScenario_Load() { TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); @@ -368,7 +397,7 @@ namespace JIT.HardwareIntrinsics.Arm [method: MethodImpl(MethodImplOptions.AggressiveInlining)] private void ConditionalSelectScenario_TrueValue({Op1VectorType}<{Op1BaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op1VectorType}<{Op1BaseType}> op2, {Op1VectorType}<{Op1BaseType}> falseOp) { - var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, Imm), falseOp); + var result = Sve.ConditionalSelect(mask, {Isa}.{Method}(op1, op2, {Imm}), falseOp); Unsafe.Write(_dataTable.outArrayPtr, result); ValidateConditionalSelectResult_TrueValue(mask, op1, op2, falseOp, _dataTable.outArrayPtr); } @@ -376,7 +405,7 @@ namespace JIT.HardwareIntrinsics.Arm [method: MethodImpl(MethodImplOptions.AggressiveInlining)] private void ConditionalSelectScenario_FalseValue({Op1VectorType}<{Op1BaseType}> mask, {Op1VectorType}<{Op1BaseType}> op1, {Op1VectorType}<{Op1BaseType}> op2, {Op1VectorType}<{Op1BaseType}> trueOp) { - var result = Sve.ConditionalSelect(mask, trueOp, {Isa}.{Method}(op1, op2, Imm)); + var result = Sve.ConditionalSelect(mask, trueOp, {Isa}.{Method}(op1, op2, {Imm})); Unsafe.Write(_dataTable.outArrayPtr, result); ValidateConditionalSelectResult_FalseValue(mask, op1, op2, trueOp, _dataTable.outArrayPtr); } diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImmTernOpTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImmTernOpTestTemplate.template index c1bcca1eb32c65..9ce3a334f7d7ad 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImmTernOpTestTemplate.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveImmTernOpTestTemplate.template @@ -9,6 +9,7 @@ ******************************************************************************/ using System; +using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -65,6 +66,9 @@ namespace JIT.HardwareIntrinsics.Arm // Validates executing the test inside conditional, with op3 as zero test.ConditionalSelect_ZeroOp(); + + // Validates basic functionality fails with an invalid imm, using Unsafe.ReadUnaligned + test.RunBasicScenario_UnsafeRead_InvalidImm(); } else { @@ -194,7 +198,7 @@ namespace JIT.HardwareIntrinsics.Arm { Succeeded = true; - for (var i = 0; i < Op1ElementCount; i++) { _maskData[i] = ({Op1BaseType})({NextValueOp1} % 2); } + for (var i = 0; i < Op1ElementCount; i++) { _maskData[i] = ({Op1BaseType})({NextValueMask} % 2); } Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _mask), ref Unsafe.As<{Op1BaseType}, byte>(ref _maskData[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{Op1BaseType}>, byte>(ref _fld1), ref Unsafe.As<{Op1BaseType}, byte>(ref _data1[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); @@ -229,6 +233,32 @@ namespace JIT.HardwareIntrinsics.Arm ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.inArray3Ptr, _dataTable.outArrayPtr); } + public void RunBasicScenario_UnsafeRead_InvalidImm() + { + TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead_InvalidImm)); + + bool succeeded = false; + try + { + var result = {Isa}.{Method}( + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray1Ptr), + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray2Ptr), + Unsafe.Read<{Op1VectorType}<{Op1BaseType}>>(_dataTable.inArray3Ptr), + {InvalidImm} + ); + Console.WriteLine(result); + } + catch (ArgumentOutOfRangeException) + { + succeeded = true; + } + + if (!succeeded) + { + Succeeded = false; + } + } + public void RunBasicScenario_Load() { TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load)); diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveUnaryOpDifferentRetTypeTestTemplate.template b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveUnaryOpDifferentRetTypeTestTemplate.template index e08a215ae18f04..56a0d8bfb3a2fc 100644 --- a/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveUnaryOpDifferentRetTypeTestTemplate.template +++ b/src/tests/JIT/HardwareIntrinsics/Arm/Shared/_SveUnaryOpDifferentRetTypeTestTemplate.template @@ -9,6 +9,7 @@ ******************************************************************************/ using System; +using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -51,11 +52,14 @@ namespace JIT.HardwareIntrinsics.Arm // Validates passing an instance member of a struct works test.RunStructFldScenario(); - // Validates executing the test inside conditional, with op3 as falseValue - test.ConditionalSelect_FalseOp(); + if (sizeof({Op1BaseType}) == sizeof({RetBaseType})) + { + // Validates executing the test inside conditional, with op3 as falseValue + test.ConditionalSelect_FalseOp(); - // Validates executing the test inside conditional, with op3 as zero - test.ConditionalSelect_ZeroOp(); + // Validates executing the test inside conditional, with op3 as zero + test.ConditionalSelect_ZeroOp(); + } } else { @@ -143,6 +147,7 @@ namespace JIT.HardwareIntrinsics.Arm private static readonly int LargestVectorSize = {LargestVectorSize}; private static readonly int Op1ElementCount = Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>() / sizeof({Op1BaseType}); + private static readonly int RetElementCount = Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>() / sizeof({RetBaseType}); private static {Op1BaseType}[] _data1 = new {Op1BaseType}[Op1ElementCount]; private static {RetBaseType}[] _data2 = new {RetBaseType}[Op1ElementCount]; @@ -165,7 +170,7 @@ namespace JIT.HardwareIntrinsics.Arm Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1VectorType}<{RetBaseType}>, byte>(ref _falseFld), ref Unsafe.As<{RetBaseType}, byte>(ref _data2[0]), (uint)Unsafe.SizeOf<{Op1VectorType}<{RetBaseType}>>()); for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = {NextValueOp1}; } - _dataTable = new DataTable(_data1, new {RetBaseType}[Op1ElementCount], LargestVectorSize); + _dataTable = new DataTable(_data1, new {RetBaseType}[RetElementCount], LargestVectorSize); } public bool IsSupported => {Isa}.IsSupported; @@ -334,10 +339,10 @@ namespace JIT.HardwareIntrinsics.Arm private void ValidateConditionalSelectResult_TrueValue({Op1VectorType}<{RetBaseType}> maskOp, {Op1VectorType}<{Op1BaseType}> leftOp, {Op1VectorType}<{RetBaseType}> falseOp, void* output, [CallerMemberName] string method = "") { - {RetBaseType}[] mask = new {RetBaseType}[Op1ElementCount]; + {RetBaseType}[] mask = new {RetBaseType}[RetElementCount]; {Op1BaseType}[] left = new {Op1BaseType}[Op1ElementCount]; - {RetBaseType}[] falseVal = new {RetBaseType}[Op1ElementCount]; - {RetBaseType}[] result = new {RetBaseType}[Op1ElementCount]; + {RetBaseType}[] falseVal = new {RetBaseType}[RetElementCount]; + {RetBaseType}[] result = new {RetBaseType}[RetElementCount]; Unsafe.WriteUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref mask[0]), maskOp); Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref left[0]), leftOp); @@ -350,7 +355,7 @@ namespace JIT.HardwareIntrinsics.Arm if (!succeeded) { - TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>, {Op1VectorType}<{Op1BaseType}>): {method} failed:"); + TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<{RetBaseType}>({Op1VectorType}<{Op1BaseType}>): {method} failed:"); TestLibrary.TestFramework.LogInformation($" mask: ({string.Join(", ", mask)})"); TestLibrary.TestFramework.LogInformation($" left: ({string.Join(", ", left)})"); TestLibrary.TestFramework.LogInformation($" falseOp: ({string.Join(", ", falseVal)})"); @@ -363,10 +368,10 @@ namespace JIT.HardwareIntrinsics.Arm private void ValidateConditionalSelectResult_FalseValue({Op1VectorType}<{RetBaseType}> maskOp, {Op1VectorType}<{Op1BaseType}> leftOp, {Op1VectorType}<{RetBaseType}> trueOp, void* output, [CallerMemberName] string method = "") { - {RetBaseType}[] mask = new {RetBaseType}[Op1ElementCount]; + {RetBaseType}[] mask = new {RetBaseType}[RetElementCount]; {Op1BaseType}[] left = new {Op1BaseType}[Op1ElementCount]; - {RetBaseType}[] trueVal = new {RetBaseType}[Op1ElementCount]; - {RetBaseType}[] result = new {RetBaseType}[Op1ElementCount]; + {RetBaseType}[] trueVal = new {RetBaseType}[RetElementCount]; + {RetBaseType}[] result = new {RetBaseType}[RetElementCount]; Unsafe.WriteUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref mask[0]), maskOp); Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref left[0]), leftOp); @@ -393,7 +398,7 @@ namespace JIT.HardwareIntrinsics.Arm private void ValidateResult({Op1VectorType}<{Op1BaseType}> op1, void* result, [CallerMemberName] string method = "") { {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; - {RetBaseType}[] outArray = new {RetBaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; Unsafe.WriteUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), op1); Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); @@ -404,7 +409,7 @@ namespace JIT.HardwareIntrinsics.Arm private void ValidateResult(void* op1, void* result, [CallerMemberName] string method = "") { {Op1BaseType}[] inArray1 = new {Op1BaseType}[Op1ElementCount]; - {RetBaseType}[] outArray = new {RetBaseType}[Op1ElementCount]; + {RetBaseType}[] outArray = new {RetBaseType}[RetElementCount]; Unsafe.CopyBlockUnaligned(ref Unsafe.As<{Op1BaseType}, byte>(ref inArray1[0]), ref Unsafe.AsRef(op1), (uint)Unsafe.SizeOf<{Op1VectorType}<{Op1BaseType}>>()); Unsafe.CopyBlockUnaligned(ref Unsafe.As<{RetBaseType}, byte>(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf<{RetVectorType}<{RetBaseType}>>()); diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 793ac714bc17b0..1ceab90a73d64d 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1908,9 +1908,6 @@ https://github.com/dotnet/runtime/issues/98628 - - https://github.com/dotnet/runtime/issues/93631: Swift reverse pinvokes are not implemented on Mono yet - diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs index c7f7eb2eb6aa0b..072433e7925d95 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs @@ -75,7 +75,12 @@ void AddRange (DiagnosticId first, DiagnosticId last) public override ImmutableArray SupportedDiagnostics => GetSupportedDiagnostics (); - static Location GetPrimaryLocation (ImmutableArray locations) => locations.Length > 0 ? locations[0] : Location.None; + static Location GetPrimaryLocation (ImmutableArray? locations) { + if (locations is null) + return Location.None; + + return locations.Value.Length > 0 ? locations.Value[0] : Location.None; + } public override void Initialize (AnalysisContext context) { @@ -167,7 +172,7 @@ static void VerifyDamOnDerivedAndBaseMethodsMatch (SymbolAnalysisContext context } } - static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbol overrideMethod, IMethodSymbol baseMethod) + static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbol overrideMethod, IMethodSymbol baseMethod, ISymbol? origin = null) { var overrideMethodReturnAnnotation = FlowAnnotations.GetMethodReturnValueAnnotation (overrideMethod); var baseMethodReturnAnnotation = FlowAnnotations.GetMethodReturnValueAnnotation (baseMethod); @@ -184,9 +189,10 @@ static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbo && baseMethod.TryGetReturnAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) ) ? (null, null) : CreateArguments (attributableSymbolLocation, missingAttribute); + var returnOrigin = origin ??= overrideMethod; context.ReportDiagnostic (Diagnostic.Create ( DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodReturnValueBetweenOverrides), - GetPrimaryLocation (overrideMethod.Locations), sourceLocation, DAMArgs?.ToImmutableDictionary (), overrideMethod.GetDisplayName (), baseMethod.GetDisplayName ())); + GetPrimaryLocation (returnOrigin.Locations), sourceLocation, DAMArgs?.ToImmutableDictionary (), overrideMethod.GetDisplayName (), baseMethod.GetDisplayName ())); } foreach (var overrideParam in overrideMethod.GetMetadataParameters ()) { @@ -205,9 +211,10 @@ static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbo && baseParam.ParameterSymbol!.TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) ) ? (null, null) : CreateArguments (attributableSymbolLocation, missingAttribute); + var parameterOrigin = origin ?? overrideParam.ParameterSymbol; context.ReportDiagnostic (Diagnostic.Create ( DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodParameterBetweenOverrides), - overrideParam.Location, sourceLocation, DAMArgs?.ToImmutableDictionary (), + GetPrimaryLocation (parameterOrigin?.Locations), sourceLocation, DAMArgs?.ToImmutableDictionary (), overrideParam.GetDisplayName (), overrideMethod.GetDisplayName (), baseParam.GetDisplayName (), baseMethod.GetDisplayName ())); } } @@ -228,27 +235,38 @@ static void VerifyDamOnMethodsMatch (SymbolAnalysisContext context, IMethodSymbo && baseMethod.TypeParameters[i].TryGetAttribute (DynamicallyAccessedMembersAnalyzer.DynamicallyAccessedMembersAttribute, out var _)) ) ? (null, null) : CreateArguments (attributableSymbolLocation, missingAttribute); + var typeParameterOrigin = origin ?? overrideMethod.TypeParameters[i]; context.ReportDiagnostic (Diagnostic.Create ( DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnGenericParameterBetweenOverrides), - GetPrimaryLocation (overrideMethod.TypeParameters[i].Locations), sourceLocation, DAMArgs?.ToImmutableDictionary (), + GetPrimaryLocation (typeParameterOrigin.Locations), sourceLocation, DAMArgs?.ToImmutableDictionary (), overrideMethod.TypeParameters[i].GetDisplayName (), overrideMethod.GetDisplayName (), baseMethod.TypeParameters[i].GetDisplayName (), baseMethod.GetDisplayName ())); } } - if (!overrideMethod.IsStatic && overrideMethod.GetDynamicallyAccessedMemberTypes () != baseMethod.GetDynamicallyAccessedMemberTypes ()) + if (!overrideMethod.IsStatic && overrideMethod.GetDynamicallyAccessedMemberTypes () != baseMethod.GetDynamicallyAccessedMemberTypes ()) { + var methodOrigin = origin ?? overrideMethod; context.ReportDiagnostic (Diagnostic.Create ( DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnImplicitThisBetweenOverrides), - GetPrimaryLocation (overrideMethod.Locations), + GetPrimaryLocation (methodOrigin.Locations), overrideMethod.GetDisplayName (), baseMethod.GetDisplayName ())); + } } static void VerifyDamOnInterfaceAndImplementationMethodsMatch (SymbolAnalysisContext context, INamedTypeSymbol type) { foreach (var (interfaceMember, implementationMember) in type.GetMemberInterfaceImplementationPairs ()) { - if (implementationMember is IMethodSymbol implementationMethod - && interfaceMember is IMethodSymbol interfaceMethod) - VerifyDamOnMethodsMatch (context, implementationMethod, interfaceMethod); + if (implementationMember is IMethodSymbol implementationMethod && interfaceMember is IMethodSymbol interfaceMethod) { + ISymbol origin = implementationMethod; + INamedTypeSymbol implementationType = implementationMethod.ContainingType; + + // If this type implements an interface method through a base class, the origin of the warning is this type, + // not the member on the base class. + if (!implementationType.IsInterface () && !SymbolEqualityComparer.Default.Equals (implementationType, type)) + origin = type; + + VerifyDamOnMethodsMatch (context, implementationMethod, interfaceMethod, origin); + } } } diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs index 28118233b656a4..b104fc7dd8ead4 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs @@ -123,8 +123,21 @@ void CheckMatchingAttributesInInterfaces ( INamedTypeSymbol type) { foreach (var memberpair in type.GetMemberInterfaceImplementationPairs ()) { + var implementationType = memberpair.ImplementationMember switch { + IMethodSymbol method => method.ContainingType, + IPropertySymbol property => property.ContainingType, + IEventSymbol @event => @event.ContainingType, + _ => throw new NotSupportedException () + }; + ISymbol origin = memberpair.ImplementationMember; + + // If this type implements an interface method through a base class, the origin of the warning is this type, + // not the member on the base class. + if (!implementationType.IsInterface () && !SymbolEqualityComparer.Default.Equals (implementationType, type)) + origin = type; + if (HasMismatchingAttributes (memberpair.InterfaceMember, memberpair.ImplementationMember)) { - ReportMismatchInAttributesDiagnostic (symbolAnalysisContext, memberpair.ImplementationMember, memberpair.InterfaceMember, isInterface: true); + ReportMismatchInAttributesDiagnostic (symbolAnalysisContext, memberpair.ImplementationMember, memberpair.InterfaceMember, isInterface: true, origin); } } } @@ -230,12 +243,13 @@ private void ReportRequiresOnStaticCtorDiagnostic (SymbolAnalysisContext symbolA ctor.GetDisplayName ())); } - private void ReportMismatchInAttributesDiagnostic (SymbolAnalysisContext symbolAnalysisContext, ISymbol member, ISymbol baseMember, bool isInterface = false) + private void ReportMismatchInAttributesDiagnostic (SymbolAnalysisContext symbolAnalysisContext, ISymbol member, ISymbol baseMember, bool isInterface = false, ISymbol? origin = null) { + origin ??= member; string message = MessageFormat.FormatRequiresAttributeMismatch (member.HasAttribute (RequiresAttributeName), isInterface, RequiresAttributeName, member.GetDisplayName (), baseMember.GetDisplayName ()); symbolAnalysisContext.ReportDiagnostic (Diagnostic.Create ( RequiresAttributeMismatch, - member.Locations[0], + origin.Locations[0], message)); } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/DynamicallyAccessedMembersTypeHierarchy.cs b/src/tools/illink/src/linker/Linker.Dataflow/DynamicallyAccessedMembersTypeHierarchy.cs index 014d42cd884e94..943030c7f35061 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/DynamicallyAccessedMembersTypeHierarchy.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/DynamicallyAccessedMembersTypeHierarchy.cs @@ -14,6 +14,7 @@ sealed class DynamicallyAccessedMembersTypeHierarchy { readonly LinkContext _context; readonly MarkStep _markStep; + readonly ReflectionMarker _reflectionMarker; // Cache of DynamicallyAccessedMembers annotations applied to types and their hierarchies // Values @@ -43,6 +44,7 @@ public DynamicallyAccessedMembersTypeHierarchy (LinkContext context, MarkStep ma _context = context; _markStep = markStep; _typesInDynamicallyAccessedMembersHierarchy = new Dictionary (); + _reflectionMarker = new ReflectionMarker (_context, _markStep, enabled: true); } public (DynamicallyAccessedMemberTypes annotation, bool applied) ProcessMarkedTypeForDynamicallyAccessedMembersHierarchy (TypeDefinition type) @@ -107,10 +109,8 @@ public DynamicallyAccessedMembersTypeHierarchy (LinkContext context, MarkStep ma if (apply) { // One of the base/interface types is already marked as having the annotation applied // so we need to apply the annotation to this type as well - var origin = new MessageOrigin (type); - var reflectionMarker = new ReflectionMarker (_context, _markStep, enabled: true); // Report warnings on access to annotated members, with the annotated type as the origin. - ApplyDynamicallyAccessedMembersToType (reflectionMarker, origin, type, annotation); + ApplyDynamicallyAccessedMembersToType (type, annotation); } return (annotation, apply); @@ -126,10 +126,8 @@ public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHiera return annotation; // Apply the effective annotation for the type - var origin = new MessageOrigin (type); - var reflectionMarker = new ReflectionMarker (_context, _markStep, enabled: true); // Report warnings on access to annotated members, with the annotated type as the origin. - ApplyDynamicallyAccessedMembersToType (reflectionMarker, origin, type, annotation); + ApplyDynamicallyAccessedMembersToType (type, annotation); // Mark it as applied in the cache _typesInDynamicallyAccessedMembersHierarchy[type] = (annotation, true); @@ -161,16 +159,14 @@ public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHiera break; foreach (var candidateType in candidateTypes) { - ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMarker, candidateType); + ApplyDynamicallyAccessedMembersToTypeHierarchyInner (candidateType); } } return annotation; } - bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner ( - in ReflectionMarker reflectionMarker, - TypeDefinition type) + bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner (TypeDefinition type) { (var annotation, var applied) = GetCachedInfoForTypeInHierarchy (type); @@ -182,13 +178,13 @@ bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner ( TypeDefinition? baseType = _context.TryResolve (type.BaseType); if (baseType != null) - applied = ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMarker, baseType); + applied = ApplyDynamicallyAccessedMembersToTypeHierarchyInner (baseType); if (!applied && type.HasInterfaces) { foreach (InterfaceImplementation iface in type.Interfaces) { var interfaceType = _context.TryResolve (iface.InterfaceType); if (interfaceType != null) { - if (ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMarker, interfaceType)) { + if (ApplyDynamicallyAccessedMembersToTypeHierarchyInner (interfaceType)) { applied = true; break; } @@ -197,31 +193,33 @@ bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner ( } if (applied) { - var origin = new MessageOrigin (type); // Report warnings on access to annotated members, with the annotated type as the origin. - ApplyDynamicallyAccessedMembersToType (reflectionMarker, origin, type, annotation); + ApplyDynamicallyAccessedMembersToType (type, annotation); _typesInDynamicallyAccessedMembersHierarchy[type] = (annotation, true); } return applied; } - void ApplyDynamicallyAccessedMembersToType (in ReflectionMarker reflectionMarker, in MessageOrigin origin, TypeDefinition type, DynamicallyAccessedMemberTypes annotation) + void ApplyDynamicallyAccessedMembersToType (TypeDefinition type, DynamicallyAccessedMemberTypes annotation) { + var origin = new MessageOrigin (type); Debug.Assert (annotation != DynamicallyAccessedMemberTypes.None); // We need to apply annotations to this type, and its base/interface types (recursively) - // But the annotations on base/interfaces are already applied so we don't need to apply those + // But the annotations on base will be applied so we don't need to apply those // again (and should avoid doing so as it would produce extra warnings). var baseType = _context.TryResolve (type.BaseType); if (baseType != null) { var baseAnnotation = GetCachedInfoForTypeInHierarchy (baseType); - var annotationToApplyToBase = baseAnnotation.applied ? Annotations.GetMissingMemberTypes (annotation, baseAnnotation.annotation) : annotation; + if (!baseAnnotation.applied && baseAnnotation.annotation != DynamicallyAccessedMemberTypes.None) + ApplyDynamicallyAccessedMembersToType (baseType, baseAnnotation.annotation); + var annotationToApplyToBase = Annotations.GetMissingMemberTypes (annotation, baseAnnotation.annotation); // Apply any annotations that didn't exist on the base type to the base type. // This may produce redundant warnings when the annotation is DAMT.All or DAMT.PublicConstructors and the base already has a // subset of those annotations. - reflectionMarker.MarkTypeForDynamicallyAccessedMembers (origin, baseType, annotationToApplyToBase, DependencyKind.DynamicallyAccessedMemberOnType, declaredOnly: false); + _reflectionMarker.MarkTypeForDynamicallyAccessedMembers (origin, baseType, annotationToApplyToBase, DependencyKind.DynamicallyAccessedMemberOnType, declaredOnly: false); } // Most of the DynamicallyAccessedMemberTypes don't select members on interfaces. We only need to apply @@ -234,19 +232,21 @@ void ApplyDynamicallyAccessedMembersToType (in ReflectionMarker reflectionMarker continue; var interfaceAnnotation = GetCachedInfoForTypeInHierarchy (interfaceType); - if (interfaceAnnotation.applied && interfaceAnnotation.annotation.HasFlag (annotationToApplyToInterfaces)) - continue; - - // Apply All or Interfaces to the interface type. - // DAMT.All may produce redundant warnings from implementing types, when the interface type already had some annotations. - reflectionMarker.MarkTypeForDynamicallyAccessedMembers (origin, interfaceType, annotationToApplyToInterfaces, DependencyKind.DynamicallyAccessedMemberOnType, declaredOnly: false); + if (interfaceAnnotation.annotation.HasFlag (annotationToApplyToInterfaces)) { + if (!interfaceAnnotation.applied) + ApplyDynamicallyAccessedMembersToType (interfaceType, interfaceAnnotation.annotation); + } else { + // Apply All or Interfaces to the interface type. + // DAMT.All may produce redundant warnings from implementing types, when the interface type already had some annotations. + _reflectionMarker.MarkTypeForDynamicallyAccessedMembers (origin, interfaceType, annotationToApplyToInterfaces, DependencyKind.DynamicallyAccessedMemberOnType, declaredOnly: false); + } } } // The annotations this type inherited from its base types or interfaces should not produce // warnings on the respective base/interface members, since those are already covered by applying // the annotations to those types. So we only need to handle the members directly declared on this type. - reflectionMarker.MarkTypeForDynamicallyAccessedMembers (origin, type, annotation, DependencyKind.DynamicallyAccessedMemberOnType, declaredOnly: true); + _reflectionMarker.MarkTypeForDynamicallyAccessedMembers (origin, type, annotation, DependencyKind.DynamicallyAccessedMemberOnType, declaredOnly: true); } (DynamicallyAccessedMemberTypes annotation, bool applied) GetCachedInfoForTypeInHierarchy (TypeDefinition type) diff --git a/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs b/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs index 3b148cf3a1ac59..14617b0de9ca16 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs @@ -461,19 +461,21 @@ bool ScanMethodBodyForFieldAccess (MethodBody body, bool write, out FieldDefinit return true; } - internal void ValidateMethodAnnotationsAreSame (MethodDefinition method, MethodDefinition baseMethod) + internal void ValidateMethodAnnotationsAreSame (OverrideInformation ov) { + var method = ov.Override; + var baseMethod = ov.Base; GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var methodAnnotations); GetAnnotations (baseMethod.DeclaringType).TryGetAnnotation (baseMethod, out var baseMethodAnnotations); if (methodAnnotations.ReturnParameterAnnotation != baseMethodAnnotations.ReturnParameterAnnotation) - LogValidationWarning (method.MethodReturnType, baseMethod.MethodReturnType, method); + LogValidationWarning (method.MethodReturnType, baseMethod.MethodReturnType, ov); if (methodAnnotations.ParameterAnnotations != null || baseMethodAnnotations.ParameterAnnotations != null) { if (methodAnnotations.ParameterAnnotations == null) - ValidateMethodParametersHaveNoAnnotations (baseMethodAnnotations.ParameterAnnotations!, method, baseMethod, method); + ValidateMethodParametersHaveNoAnnotations (baseMethodAnnotations.ParameterAnnotations!, ov); else if (baseMethodAnnotations.ParameterAnnotations == null) - ValidateMethodParametersHaveNoAnnotations (methodAnnotations.ParameterAnnotations, method, baseMethod, method); + ValidateMethodParametersHaveNoAnnotations (methodAnnotations.ParameterAnnotations, ov); else { if (methodAnnotations.ParameterAnnotations.Length != baseMethodAnnotations.ParameterAnnotations.Length) return; @@ -483,16 +485,16 @@ internal void ValidateMethodAnnotationsAreSame (MethodDefinition method, MethodD LogValidationWarning ( method.TryGetParameter ((ParameterIndex) parameterIndex)?.GetCustomAttributeProvider ()!, baseMethod.TryGetParameter ((ParameterIndex) parameterIndex)?.GetCustomAttributeProvider ()!, - method); + ov); } } } if (methodAnnotations.GenericParameterAnnotations != null || baseMethodAnnotations.GenericParameterAnnotations != null) { if (methodAnnotations.GenericParameterAnnotations == null) - ValidateMethodGenericParametersHaveNoAnnotations (baseMethodAnnotations.GenericParameterAnnotations!, method, baseMethod, method); + ValidateMethodGenericParametersHaveNoAnnotations (baseMethodAnnotations.GenericParameterAnnotations!, ov); else if (baseMethodAnnotations.GenericParameterAnnotations == null) - ValidateMethodGenericParametersHaveNoAnnotations (methodAnnotations.GenericParameterAnnotations, method, baseMethod, method); + ValidateMethodGenericParametersHaveNoAnnotations (methodAnnotations.GenericParameterAnnotations, ov); else { if (methodAnnotations.GenericParameterAnnotations.Length != baseMethodAnnotations.GenericParameterAnnotations.Length) return; @@ -502,39 +504,42 @@ internal void ValidateMethodAnnotationsAreSame (MethodDefinition method, MethodD LogValidationWarning ( method.GenericParameters[genericParameterIndex], baseMethod.GenericParameters[genericParameterIndex], - method); + ov); } } } } } - void ValidateMethodParametersHaveNoAnnotations (DynamicallyAccessedMemberTypes[] parameterAnnotations, MethodDefinition method, MethodDefinition baseMethod, IMemberDefinition origin) + void ValidateMethodParametersHaveNoAnnotations (DynamicallyAccessedMemberTypes[] parameterAnnotations, OverrideInformation ov) { for (int parameterIndex = 0; parameterIndex < parameterAnnotations.Length; parameterIndex++) { var annotation = parameterAnnotations[parameterIndex]; if (annotation != DynamicallyAccessedMemberTypes.None) LogValidationWarning ( - method.GetParameter ((ParameterIndex) parameterIndex).GetCustomAttributeProvider ()!, - baseMethod.GetParameter ((ParameterIndex) parameterIndex).GetCustomAttributeProvider ()!, - origin); + ov.Override.GetParameter ((ParameterIndex) parameterIndex).GetCustomAttributeProvider ()!, + ov.Base.GetParameter ((ParameterIndex) parameterIndex).GetCustomAttributeProvider ()!, + ov); } } - void ValidateMethodGenericParametersHaveNoAnnotations (DynamicallyAccessedMemberTypes[] genericParameterAnnotations, MethodDefinition method, MethodDefinition baseMethod, IMemberDefinition origin) + void ValidateMethodGenericParametersHaveNoAnnotations (DynamicallyAccessedMemberTypes[] genericParameterAnnotations, OverrideInformation ov) { for (int genericParameterIndex = 0; genericParameterIndex < genericParameterAnnotations.Length; genericParameterIndex++) { if (genericParameterAnnotations[genericParameterIndex] != DynamicallyAccessedMemberTypes.None) { LogValidationWarning ( - method.GenericParameters[genericParameterIndex], - baseMethod.GenericParameters[genericParameterIndex], - origin); + ov.Override.GenericParameters[genericParameterIndex], + ov.Base.GenericParameters[genericParameterIndex], + ov); } } } - void LogValidationWarning (IMetadataTokenProvider provider, IMetadataTokenProvider baseProvider, IMemberDefinition origin) + void LogValidationWarning (IMetadataTokenProvider provider, IMetadataTokenProvider baseProvider, OverrideInformation ov) { + IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.InterfaceImplementor.Implementor != ov.Override.DeclaringType) + ? ov.InterfaceImplementor.Implementor + : ov.Override; Debug.Assert (provider.GetType () == baseProvider.GetType ()); Debug.Assert (!(provider is GenericParameter genericParameter) || genericParameter.DeclaringMethod != null); switch (provider) { diff --git a/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs b/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs index ac4b07e6681b4a..aa9c7ee0d9f09e 100644 --- a/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs @@ -16,8 +16,8 @@ protected override void Process () var baseOverrideInformations = annotations.GetBaseMethods (method); if (baseOverrideInformations != null) { foreach (var baseOv in baseOverrideInformations) { - annotations.FlowAnnotations.ValidateMethodAnnotationsAreSame (method, baseOv.Base); - ValidateMethodRequiresUnreferencedCodeAreSame (method, baseOv.Base); + annotations.FlowAnnotations.ValidateMethodAnnotationsAreSame (baseOv); + ValidateMethodRequiresUnreferencedCodeAreSame (baseOv); } } @@ -30,15 +30,17 @@ protected override void Process () if (annotations.VirtualMethodsWithAnnotationsToValidate.Contains (overrideInformation.Override)) continue; - annotations.FlowAnnotations.ValidateMethodAnnotationsAreSame (overrideInformation.Override, method); - ValidateMethodRequiresUnreferencedCodeAreSame (overrideInformation.Override, method); + annotations.FlowAnnotations.ValidateMethodAnnotationsAreSame (overrideInformation); + ValidateMethodRequiresUnreferencedCodeAreSame (overrideInformation); } } } } - void ValidateMethodRequiresUnreferencedCodeAreSame (MethodDefinition method, MethodDefinition baseMethod) + void ValidateMethodRequiresUnreferencedCodeAreSame (OverrideInformation ov) { + var method = ov.Override; + var baseMethod = ov.Base; var annotations = Context.Annotations; bool methodSatisfies = annotations.IsInRequiresUnreferencedCodeScope (method, out _); bool baseRequires = annotations.DoesMethodRequireUnreferencedCode (baseMethod, out _); @@ -49,7 +51,10 @@ void ValidateMethodRequiresUnreferencedCodeAreSame (MethodDefinition method, Met nameof (RequiresUnreferencedCodeAttribute), method.GetDisplayName (), baseMethod.GetDisplayName ()); - Context.LogWarning (method, DiagnosticId.RequiresUnreferencedCodeAttributeMismatch, message); + IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.InterfaceImplementor.Implementor != method.DeclaringType) + ? ov.InterfaceImplementor.Implementor + : method; + Context.LogWarning (origin, DiagnosticId.RequiresUnreferencedCodeAttributeMismatch, message); } } } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs index 5799627098f509..c3cae702e3e929 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/ReflectionTests.cs @@ -181,14 +181,14 @@ public Task TypeDelegator () [Fact] public Task TypeHierarchyReflectionWarnings () { - // https://github.com/dotnet/linker/issues/2578 + // https://github.com/dotnet/runtime/issues/104742 return RunTest (allowMissingWarnings: true); } [Fact] public Task TypeHierarchySuppressions () { - // https://github.com/dotnet/linker/issues/2578 + // https://github.com/dotnet/runtime/issues/104742 return RunTest (allowMissingWarnings: true); } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/InterfaceImplementedThroughBaseValidation.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/InterfaceImplementedThroughBaseValidation.cs new file mode 100644 index 00000000000000..b92d3fa3943f2d --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/InterfaceImplementedThroughBaseValidation.cs @@ -0,0 +1,339 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + class InterfaceImplementedThroughBaseValidation + { + public static void Main () + { + RUCOnInterfaceMethod.Test (); + RUCOnBaseMethod.Test (); + DAMOnInterfaceMethod.Test (); + DAMOnBaseMethod.Test (); + + RUCOnInterfaceWithDIM.Test (); + RUCOnDIM.Test (); + DAMOnInterfaceWithDIM.Test (); + DAMOnDIM.Test (); + + GenericVirtualMethod.Test (); + GenericVirtualMethodWithDIM.Test (); + GenericVirtualMethodWithStaticDIM.Test (); + + CalledThroughConstraint.Test (); + CalledThroughConstraintWithDIM.Test (); + } + + class RUCOnInterfaceMethod + { + interface Interface { + [RequiresUnreferencedCode (nameof (Method))] + void Method (); + } + + class Base { + public void Method () {} + } + + [ExpectedWarning ("IL2046")] + class Derived : Base, Interface {} + + [ExpectedWarning ("IL2026")] + public static void Test () { + Interface i = new Derived (); + i.Method (); + } + } + + class RUCOnBaseMethod + { + interface Interface { + void Method (); + } + + class Base { + [RequiresUnreferencedCode (nameof (Method))] + public void Method () {} + } + + [ExpectedWarning ("IL2046")] + class Derived : Base, Interface {} + + public static void Test () { + Interface i = new Derived (); + i.Method (); + } + } + + class DAMOnInterfaceMethod + { + interface Interface { + void Method ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t); + } + + class Base { + public void Method (Type t) {} + } + + [ExpectedWarning ("IL2092")] + class Derived : Base, Interface {} + + public static void Test () { + Interface i = new Derived (); + i.Method (typeof (int)); + } + } + + class DAMOnBaseMethod + { + interface Interface { + void Method (Type t); + } + + class Base { + public void Method ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) {} + } + + [ExpectedWarning ("IL2092")] + class Derived : Base, Interface {} + + public static void Test () { + Interface i = new Derived (); + i.Method (typeof (int)); + } + } + + class RUCOnInterfaceWithDIM + { + interface Interface { + [RequiresUnreferencedCode (nameof (Method))] + void Method (); + } + + interface InterfaceImpl : Interface { + [ExpectedWarning ("IL2046")] + [ExpectedWarning ("IL2046", Tool.Analyzer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/104627")] + [ExpectedWarning ("IL2046", Tool.Analyzer, "https://github.com/dotnet/runtime/issues/104627")] + void Interface.Method() {} + } + + class C : InterfaceImpl {} + + class D : InterfaceImpl {} + + [ExpectedWarning ("IL2026")] + public static void Test () { + Interface i = new C (); + i = new D (); + i.Method (); + } + } + + class RUCOnDIM + { + interface Interface { + void Method (); + } + + interface InterfaceImpl : Interface { + [ExpectedWarning ("IL2046")] + [ExpectedWarning ("IL2046", Tool.Analyzer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/104627")] + [ExpectedWarning ("IL2046", Tool.Analyzer, "https://github.com/dotnet/runtime/issues/104627")] + [RequiresUnreferencedCode (nameof (Method))] + void Interface.Method() {} + } + + class C : InterfaceImpl {} + + class D : InterfaceImpl {} + + public static void Test () { + Interface i = new C (); + i = new D (); + i.Method (); + } + } + + class DAMOnInterfaceWithDIM + { + interface Interface { + void Method ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t); + } + + interface InterfaceImpl : Interface { + [ExpectedWarning ("IL2092")] + [ExpectedWarning ("IL2092", Tool.Analyzer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/104627")] + [ExpectedWarning ("IL2092", Tool.Analyzer, "https://github.com/dotnet/runtime/issues/104627")] + void Interface.Method (Type t) {} + } + + class C : InterfaceImpl {} + + class D : InterfaceImpl {} + + public static void Test () { + Interface i = new C (); + i = new D (); + i.Method (typeof (int)); + } + } + + class DAMOnDIM + { + interface Interface { + void Method (Type t); + } + + interface InterfaceImpl : Interface { + [ExpectedWarning ("IL2092")] + [ExpectedWarning ("IL2092", Tool.Analyzer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/104627")] + [ExpectedWarning ("IL2092", Tool.Analyzer, "https://github.com/dotnet/runtime/issues/104627")] + void Interface.Method ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) {} + } + + class C : InterfaceImpl {} + + class D : InterfaceImpl {} + + public static void Test () { + Interface i = new C (); + i = new D (); + i.Method (typeof (int)); + } + } + + class CalledThroughConstraint + { + interface Interface { + [RequiresUnreferencedCode (nameof (Method))] + void Method (); + } + + struct Struct : Interface { + [ExpectedWarning ("IL2046")] + [ExpectedWarning ("IL2046", Tool.Analyzer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/104627")] + public void Method () {} + } + + [ExpectedWarning ("IL2026")] + [ExpectedWarning ("IL2026")] + static void Call (T t) where T : Interface { + t.Method (); + t.Method (); + } + + public static void Test () { + Call(new Struct ()); + } + } + + class CalledThroughConstraintWithDIM + { + interface Interface { + [RequiresUnreferencedCode (nameof (Method))] + static abstract void Method (); + } + + interface InterfaceImpl : Interface { + [ExpectedWarning ("IL2046")] + [ExpectedWarning ("IL2046", Tool.Analyzer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/104702")] + static void Interface.Method() {} + } + + struct Struct : InterfaceImpl {} + + [ExpectedWarning ("IL2026")] + [ExpectedWarning ("IL2026")] + static void Call () where T : Interface { + T.Method (); + T.Method (); + } + + public static void Test () { + Call (); + } + } + + class GenericVirtualMethod + { + interface Interface { + [RequiresUnreferencedCode (nameof (Method))] + void Method (); + } + + class Base { + public void Method () {} + } + + [ExpectedWarning ("IL2046")] + class Derived : Base, Interface {} + + [ExpectedWarning ("IL2026")] + [ExpectedWarning ("IL2026")] + public static void Test () { + Interface i = new Derived (); + i.Method (); + i.Method (); + } + } + + class GenericVirtualMethodWithDIM + { + interface Interface { + [RequiresUnreferencedCode (nameof (Method))] + void Method (); + } + + class Impl : Interface { + [ExpectedWarning ("IL2046")] + void Interface.Method () {} + } + + class Class : Impl {} + + [ExpectedWarning ("IL2026")] + [ExpectedWarning ("IL2026")] + public static void Test () { + Interface i = new Class (); + i.Method (); + i.Method (); + } + } + + class GenericVirtualMethodWithStaticDIM + { + interface Interface { + [RequiresUnreferencedCode (nameof (Method))] + static abstract void Method (); + } + + interface Impl : Interface { + [ExpectedWarning ("IL2046")] + [ExpectedWarning ("IL2046", Tool.Analyzer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/104702")] + static void Interface.Method () {} + } + + class Class : Impl {} + + [ExpectedWarning ("IL2026")] + [ExpectedWarning ("IL2026")] + static void Call () where T : Interface { + T.Method (); + T.Method (); + } + + public static void Test () { + Call (); + } + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs index d19bcfe349aacd..ea71c1f250abf7 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs @@ -574,17 +574,17 @@ interface IBaseImplementedInterface class BaseImplementsInterfaceViaDerived { - [LogContains ( - "'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the return value of method 'Mono.Linker.Tests.Cases.DataFlow.VirtualMethodHierarchyDataflowAnnotationValidation.BaseImplementsInterfaceViaDerived.ReturnValueBaseWithInterfaceWithout()' " + - "don't match overridden return value of method 'Mono.Linker.Tests.Cases.DataFlow.VirtualMethodHierarchyDataflowAnnotationValidation.IBaseImplementedInterface.ReturnValueBaseWithInterfaceWithout()'. " + - "All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.")] [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public virtual Type ReturnValueBaseWithInterfaceWithout () => null; - [ExpectedWarning ("IL2046", "BaseImplementsInterfaceViaDerived.RequiresUnreferencedCodeBaseWithoutInterfaceWith")] public virtual void RequiresUnreferencedCodeBaseWithoutInterfaceWith () { } } + [ExpectedWarning ("IL2046", "BaseImplementsInterfaceViaDerived.RequiresUnreferencedCodeBaseWithoutInterfaceWith")] + [LogContains ( + "'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the return value of method 'Mono.Linker.Tests.Cases.DataFlow.VirtualMethodHierarchyDataflowAnnotationValidation.BaseImplementsInterfaceViaDerived.ReturnValueBaseWithInterfaceWithout()' " + + "don't match overridden return value of method 'Mono.Linker.Tests.Cases.DataFlow.VirtualMethodHierarchyDataflowAnnotationValidation.IBaseImplementedInterface.ReturnValueBaseWithInterfaceWithout()'. " + + "All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.")] class DerivedWithInterfaceImplementedByBase : BaseImplementsInterfaceViaDerived, IBaseImplementedInterface { } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs index cd3ae16955e94d..75ebd5d6edf7a3 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs @@ -58,6 +58,8 @@ public static void Main () RUCOnVirtualOnAnnotatedBase.Test (); RUCOnVirtualOnAnnotatedBaseUsedByDerived.Test (); + RUCOnVirtualOnAnnotatedInterface.Test (); + RucOnVirtualOnAnnotatedInterfaceUsedByImplementation.Test (); UseByDerived.Test (); CompilerGeneratedCodeRUC.Test (null); @@ -699,16 +701,13 @@ public class Base [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] [RequiresUnreferencedCode ("--RUCOnVirtualMethodDerivedAnnotated.Base.RUCVirtualMethod--")] - // Compare to the case above - the only difference is the type of the field - // and it causes different warnings to be produced. - [ExpectedWarning ("IL2112", "--RUCOnVirtualMethodDerivedAnnotated.Base.RUCVirtualMethod--", Tool.None, "https://github.com/dotnet/runtime/issues/86580")] + [ExpectedWarning ("IL2112", "--RUCOnVirtualMethodDerivedAnnotated.Base.RUCVirtualMethod--", Tool.Trimmer, "https://github.com/dotnet/runtime/issues/104740")] public virtual void RUCVirtualMethod () { } } [Kept] [KeptMember (".ctor()")] [KeptBaseType (typeof (Base))] - [UnexpectedWarning ("IL2113", "--RUCOnVirtualMethodDerivedAnnotated.Base.RUCVirtualMethod--", Tool.Trimmer, "https://github.com/dotnet/runtime/issues/86580")] public class Derived : Base { [Kept] @@ -729,6 +728,82 @@ public static void Test () } } + [Kept] + class RUCOnVirtualOnAnnotatedInterface + { + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + public interface Interface + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--RUCOnVirtualOnAnnotatedInterface.Interface.RUCVirtualMethod--")] + [ExpectedWarning ("IL2112", "--RUCOnVirtualOnAnnotatedInterface.Interface.RUCVirtualMethod--", Tool.Trimmer, "https://github.com/dotnet/runtime/issues/104740")] + void RUCVirtualMethod () { } + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (Interface))] + public class Implementation : Interface + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--RUCOnVirtualOnAnnotatedInterface.Implementation.RUCVirtualMethod--")] + [ExpectedWarning ("IL2112", "--RUCOnVirtualOnAnnotatedInterface.Implementation.RUCVirtualMethod--")] + public void RUCVirtualMethod () { } + } + + [Kept] + static Interface _interfaceInstance; + + [Kept] + public static void Test () + { + _interfaceInstance = new Implementation (); + _interfaceInstance.GetType ().RequiresAll (); + } + } + + [Kept] + class RucOnVirtualOnAnnotatedInterfaceUsedByImplementation + { + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + public interface Interface + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--RucOnVirtualOnAnnotatedInterfaceUsedByImplementation.Interface.RUCVirtualMethod--")] + [ExpectedWarning ("IL2112", "--RucOnVirtualOnAnnotatedInterfaceUsedByImplementation.Interface.RUCVirtualMethod--", Tool.Trimmer, "https://github.com/dotnet/runtime/issues/104740")] + void RUCVirtualMethod () { } + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (Interface))] + public class Implementation : Interface + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--RucOnVirtualOnAnnotatedInterfaceUsedByImplementation.Implementation.RUCVirtualMethod--")] + [ExpectedWarning ("IL2112", "--RucOnVirtualOnAnnotatedInterfaceUsedByImplementation.Implementation.RUCVirtualMethod--")] + public void RUCVirtualMethod () { } + } + + [Kept] + static Implementation _implementationInstance; + + [Kept] + public static void Test () + { + _implementationInstance = new Implementation (); + _implementationInstance.GetType ().RequiresAll (); + } + } + [Kept] class UseByDerived { @@ -745,13 +820,13 @@ class AnnotatedBase [RequiresUnreferencedCode ("--AnnotatedBase.VirtualMethodWithRequires--")] [RequiresDynamicCode ("--AnnotatedBase.VirtualMethodWithRequires--")] [RequiresAssemblyFiles ("--AnnotatedBase.VirtualMethodWithRequires--")] + [ExpectedWarning ("IL2112", "--AnnotatedBase.VirtualMethodWithRequires--", Tool.Trimmer, "https://github.com/dotnet/runtime/issues/104740")] public virtual void VirtualMethodWithRequires () { } } [Kept] [KeptBaseType (typeof (AnnotatedBase))] [KeptMember (".ctor()")] - [UnexpectedWarning ("IL2113", "--AnnotatedBase.VirtualMethodWithRequires--", Tool.Trimmer, "https://github.com/dotnet/runtime/issues/86580")] class Derived : AnnotatedBase { [Kept]