Skip to content

Commit b96ce9c

Browse files
committed
Merge branch 'main' into System.Private.Reflection.Core
2 parents c35447b + 5786435 commit b96ce9c

File tree

314 files changed

+3229
-1147
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

314 files changed

+3229
-1147
lines changed

docs/design/coreclr/botr/dac-notes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The DAC infrastructure (the macros and templates that control how host or target
1515

1616
When one has no understanding of the DAC, it's easy to find the use of the DAC infrastructure annoying. The `TADDR`s and `PTR_this` and `dac_casts`, etc. seem to clutter the code and make it harder to understand. With just a little work, though, you'll find that these are not really difficult to learn. Keeping host and target addresses explicitly different is really a form of strong typing. The more diligent we are, the easier it becomes to ensure our code is correct.
1717

18-
Because the DAC potentially operates on a dump, the part of the VM sources we build in clr.dll (msdaccore.dll) must be non-invasive. Specifically, we usually don't want to do anything that would cause writing to the target's address space, nor can we execute any code that might cause an immediate garbage collection. (If we can defer the GC, it may be possible to allocate.) Note that the _host_ state is always mutated (temporaries, stack or local heap values); it is only mutating the _target_ space that is problematic. To enforce this, we do two things: code factoring and conditional compilation. In an ideal world, we would factor the VM code so that we would strictly isolate invasive actions in functions that are separate from non-invasive functions.
18+
Because the DAC potentially operates on a dump, the part of the VM sources we build in mscordacwks.dll (msdaccore.dll) must be non-invasive. Specifically, we usually don't want to do anything that would cause writing to the target's address space, nor can we execute any code that might cause an immediate garbage collection. (If we can defer the GC, it may be possible to allocate.) Note that the _host_ state is always mutated (temporaries, stack or local heap values); it is only mutating the _target_ space that is problematic. To enforce this, we do two things: code factoring and conditional compilation. In an ideal world, we would factor the VM code so that we would strictly isolate invasive actions in functions that are separate from non-invasive functions.
1919

2020
Unfortunately, we have a large code base, most of which we wrote without ever thinking about the DAC at all. We have a significant number of functions with "find or create" semantics and many other functions that have some parts that just do inspection and other parts that write to the target. Sometimes we control this with a flag passed into the function. This is common in loader code, for example. To avoid having to complete the immense job of refactoring all the VM code before we can use the DAC, we have a second method to prevent executing invasive code from out of process. We have a defined pre-processor constant, `DACCESS_COMPILE` that we use to control what parts of the code we compile into the DAC. We would like to use the `DACCESS_COMPILE` constant as little as we can, so when we DACize a new code path, we prefer to refactor whenever possible. Thus, a function that has "find or create" semantics should become two functions: one that tries to find the information and a wrapper that calls this and creates if the find fails. That way, the DAC code path can call the find function directly and avoid the creation.
2121

eng/CodeAnalysis.src.globalconfig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,7 +1383,7 @@ dotnet_diagnostic.IDE0027.severity = silent
13831383
dotnet_diagnostic.IDE0028.severity = suggestion
13841384

13851385
# IDE0029: Use coalesce expression
1386-
dotnet_diagnostic.IDE0029.severity = suggestion
1386+
dotnet_diagnostic.IDE0029.severity = warning
13871387

13881388
# IDE0030: Use coalesce expression
13891389
dotnet_diagnostic.IDE0030.severity = suggestion
@@ -1404,7 +1404,7 @@ dotnet_diagnostic.IDE0034.severity = suggestion
14041404
dotnet_diagnostic.IDE0035.severity = suggestion
14051405

14061406
# IDE0036: Order modifiers
1407-
dotnet_diagnostic.IDE0036.severity = suggestion
1407+
dotnet_diagnostic.IDE0036.severity = warning
14081408

14091409
# IDE0037: Use inferred member name
14101410
dotnet_diagnostic.IDE0037.severity = silent

eng/pipelines/runtime.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ jobs:
261261
isSingleFile: true
262262
nameSuffix: NativeAOT
263263
buildArgs: -s clr.alljits+clr.tools+clr.nativeaotlibs+clr.nativeaotruntime+libs+libs.tests -c $(_BuildConfig) /p:TestNativeAot=true /p:ArchiveTests=true
264-
timeoutInMinutes: 120
264+
timeoutInMinutes: 180
265265
# extra steps, run tests
266266
extraStepsTemplate: /eng/pipelines/libraries/helix.yml
267267
extraStepsParameters:

src/coreclr/System.Private.CoreLib/src/System/ValueType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public abstract class ValueType
2424
{
2525
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
2626
Justification = "Trimmed fields don't make a difference for equality")]
27-
public unsafe override bool Equals([NotNullWhen(true)] object? obj)
27+
public override unsafe bool Equals([NotNullWhen(true)] object? obj)
2828
{
2929
if (null == obj)
3030
{

src/coreclr/jit/assertionprop.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3177,6 +3177,15 @@ GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* tree)
31773177

31783178
if (conValTree != nullptr)
31793179
{
3180+
if (tree->OperIs(GT_LCL_VAR))
3181+
{
3182+
if (!optIsProfitableToSubstitute(tree->AsLclVar(), block, conValTree))
3183+
{
3184+
// Not profitable to substitute
3185+
return nullptr;
3186+
}
3187+
}
3188+
31803189
// Were able to optimize.
31813190
conValTree->gtVNPair = vnPair;
31823191
GenTree* sideEffList = optExtractSideEffListFromConst(tree);
@@ -3199,6 +3208,55 @@ GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* tree)
31993208
}
32003209
}
32013210

3211+
//------------------------------------------------------------------------------
3212+
// optIsProfitableToSubstitute: Checks if value worth substituting to lcl location
3213+
//
3214+
// Arguments:
3215+
// lcl - lcl to replace with value if profitable
3216+
// lclBlock - Basic block lcl located in
3217+
// value - value we plan to substitute to lcl
3218+
//
3219+
// Returns:
3220+
// False if it's likely not profitable to do substitution, True otherwise
3221+
//
3222+
bool Compiler::optIsProfitableToSubstitute(GenTreeLclVarCommon* lcl, BasicBlock* lclBlock, GenTree* value)
3223+
{
3224+
// A simple heuristic: If the constant is defined outside of a loop (not far from its head)
3225+
// and is used inside it - don't propagate.
3226+
3227+
// TODO: Extend on more kinds of trees
3228+
if (!value->OperIs(GT_CNS_VEC, GT_CNS_DBL))
3229+
{
3230+
return true;
3231+
}
3232+
3233+
gtPrepareCost(value);
3234+
3235+
if ((value->GetCostEx() > 1) && (value->GetCostSz() > 1))
3236+
{
3237+
// Try to find the block this constant was originally defined in
3238+
if (lcl->HasSsaName())
3239+
{
3240+
BasicBlock* defBlock = lvaGetDesc(lcl)->GetPerSsaData(lcl->GetSsaNum())->GetBlock();
3241+
if (defBlock != nullptr)
3242+
{
3243+
// Avoid propagating if the weighted use cost is significantly greater than the def cost.
3244+
// NOTE: this currently does not take "a float living across a call" case into account
3245+
// where we might end up with spill/restore on ABIs without callee-saved registers
3246+
const weight_t defBlockWeight = defBlock->getBBWeight(this);
3247+
const weight_t lclblockWeight = lclBlock->getBBWeight(this);
3248+
3249+
if ((defBlockWeight > 0) && ((lclblockWeight / defBlockWeight) >= BB_LOOP_WEIGHT_SCALE))
3250+
{
3251+
JITDUMP("Constant propagation inside loop " FMT_BB " is not profitable\n", lclBlock->bbNum);
3252+
return false;
3253+
}
3254+
}
3255+
}
3256+
}
3257+
return true;
3258+
}
3259+
32023260
//------------------------------------------------------------------------------
32033261
// optConstantAssertionProp: Possibly substitute a constant for a local use
32043262
//

src/coreclr/jit/codegenarm64.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2178,7 +2178,7 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size,
21782178
{
21792179
if (emitter::emitIns_valid_imm_for_mov(imm, size))
21802180
{
2181-
GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm);
2181+
GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm, INS_OPTS_NONE DEBUGARG(targetHandle) DEBUGARG(gtFlags));
21822182
}
21832183
else
21842184
{
@@ -2224,7 +2224,9 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size,
22242224
imm16 = ~imm16;
22252225
}
22262226

2227-
GetEmitter()->emitIns_R_I_I(ins, size, reg, imm16, i, INS_OPTS_LSL);
2227+
GetEmitter()->emitIns_R_I_I(ins, size, reg, imm16, i,
2228+
INS_OPTS_LSL DEBUGARG(i == 0 ? targetHandle : 0)
2229+
DEBUGARG(i == 0 ? gtFlags : GTF_EMPTY));
22282230

22292231
// Once the initial movz/movn is emitted the remaining instructions will all use movk
22302232
ins = INS_movk;
@@ -2258,8 +2260,8 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
22582260
{
22592261
case GT_CNS_INT:
22602262
{
2261-
GenTreeIntConCommon* con = tree->AsIntConCommon();
2262-
ssize_t cnsVal = con->IconValue();
2263+
GenTreeIntCon* con = tree->AsIntCon();
2264+
ssize_t cnsVal = con->IconValue();
22632265

22642266
emitAttr attr = emitActualTypeSize(targetType);
22652267
// TODO-CQ: Currently we cannot do this for all handles because of
@@ -2275,8 +2277,7 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
22752277
}
22762278

22772279
instGen_Set_Reg_To_Imm(attr, targetReg, cnsVal,
2278-
INS_FLAGS_DONT_CARE DEBUGARG(tree->AsIntCon()->gtTargetHandle)
2279-
DEBUGARG(tree->AsIntCon()->gtFlags));
2280+
INS_FLAGS_DONT_CARE DEBUGARG(con->gtTargetHandle) DEBUGARG(con->gtFlags));
22802281
regSet.verifyRegUsed(targetReg);
22812282
}
22822283
break;

src/coreclr/jit/codegenloongarch64.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,18 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
16551655
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
16561656
*/
16571657

1658+
//------------------------------------------------------------------------
1659+
// inst_JMP: Generate a jump instruction.
1660+
//
1661+
void CodeGen::inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock)
1662+
{
1663+
#if !FEATURE_FIXED_OUT_ARGS
1664+
assert((tgtBlock->bbTgtStkDepth * sizeof(int) == genStackLevel) || isFramePointerUsed());
1665+
#endif // !FEATURE_FIXED_OUT_ARGS
1666+
1667+
GetEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmp), tgtBlock);
1668+
}
1669+
16581670
BasicBlock* CodeGen::genCallFinally(BasicBlock* block)
16591671
{
16601672
// Generate a call to the finally, like this:

src/coreclr/jit/codegenxarch.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size,
442442
}
443443
else
444444
{
445-
GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm DEBUGARG(gtFlags));
445+
GetEmitter()->emitIns_R_I(INS_mov, size, reg, imm DEBUGARG(targetHandle) DEBUGARG(gtFlags));
446446
}
447447
}
448448
regSet.verifyRegUsed(reg);
@@ -462,8 +462,8 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
462462
{
463463
// relocatable values tend to come down as a CNS_INT of native int type
464464
// so the line between these two opcodes is kind of blurry
465-
GenTreeIntConCommon* con = tree->AsIntConCommon();
466-
ssize_t cnsVal = con->IconValue();
465+
GenTreeIntCon* con = tree->AsIntCon();
466+
ssize_t cnsVal = con->IconValue();
467467

468468
emitAttr attr = emitActualTypeSize(targetType);
469469
// Currently this cannot be done for all handles due to
@@ -482,7 +482,8 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre
482482
attr = EA_SET_FLG(attr, EA_BYREF_FLG);
483483
}
484484

485-
instGen_Set_Reg_To_Imm(attr, targetReg, cnsVal, INS_FLAGS_DONT_CARE DEBUGARG(0) DEBUGARG(tree->gtFlags));
485+
instGen_Set_Reg_To_Imm(attr, targetReg, cnsVal,
486+
INS_FLAGS_DONT_CARE DEBUGARG(con->gtTargetHandle) DEBUGARG(con->gtFlags));
486487
regSet.verifyRegUsed(targetReg);
487488
}
488489
break;

src/coreclr/jit/compiler.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2269,7 +2269,7 @@ class Compiler
22692269

22702270
GenTree* gtNewIndOfIconHandleNode(var_types indType, size_t value, GenTreeFlags iconFlags, bool isInvariant);
22712271

2272-
GenTree* gtNewIconHandleNode(size_t value, GenTreeFlags flags, FieldSeqNode* fields = nullptr);
2272+
GenTreeIntCon* gtNewIconHandleNode(size_t value, GenTreeFlags flags, FieldSeqNode* fields = nullptr);
22732273

22742274
GenTreeFlags gtTokenToIconFlags(unsigned token);
22752275

@@ -7264,6 +7264,7 @@ class Compiler
72647264
GenTree* optConstantAssertionProp(AssertionDsc* curAssertion,
72657265
GenTreeLclVarCommon* tree,
72667266
Statement* stmt DEBUGARG(AssertionIndex index));
7267+
bool optIsProfitableToSubstitute(GenTreeLclVarCommon* lcl, BasicBlock* lclBlock, GenTree* value);
72677268
bool optZeroObjAssertionProp(GenTree* tree, ASSERT_VALARG_TP assertions);
72687269

72697270
// Assertion propagation functions.

src/coreclr/jit/compiler.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -902,9 +902,8 @@ inline GenTree* Compiler::gtNewLargeOperNode(genTreeOps oper, var_types type, Ge
902902
* that may need to be fixed up).
903903
*/
904904

905-
inline GenTree* Compiler::gtNewIconHandleNode(size_t value, GenTreeFlags flags, FieldSeqNode* fields)
905+
inline GenTreeIntCon* Compiler::gtNewIconHandleNode(size_t value, GenTreeFlags flags, FieldSeqNode* fields)
906906
{
907-
GenTree* node;
908907
assert((flags & (GTF_ICON_HDL_MASK | GTF_ICON_FIELD_OFF)) != 0);
909908

910909
// Interpret "fields == NULL" as "not a field."
@@ -913,6 +912,7 @@ inline GenTree* Compiler::gtNewIconHandleNode(size_t value, GenTreeFlags flags,
913912
fields = FieldSeqStore::NotAField();
914913
}
915914

915+
GenTreeIntCon* node;
916916
#if defined(LATE_DISASM)
917917
node = new (this, LargeOpOpcode()) GenTreeIntCon(TYP_I_IMPL, value, fields DEBUGARG(/*largeNode*/ true));
918918
#else
@@ -1370,6 +1370,7 @@ inline void GenTree::SetOper(genTreeOps oper, ValueNumberUpdate vnUpdate)
13701370
{
13711371
case GT_CNS_INT:
13721372
AsIntCon()->gtFieldSeq = FieldSeqStore::NotAField();
1373+
INDEBUG(AsIntCon()->gtTargetHandle = 0);
13731374
break;
13741375
#if defined(TARGET_ARM)
13751376
case GT_MUL_LONG:

src/coreclr/jit/emit.cpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4045,25 +4045,48 @@ void emitter::emitRecomputeIGoffsets()
40454045
//
40464046
// Arguments:
40474047
// handle - a constant value to display a comment for
4048+
// cookie - the cookie stored with the handle
40484049
// flags - a flag that the describes the handle
40494050
//
4050-
void emitter::emitDispCommentForHandle(size_t handle, GenTreeFlags flag)
4051+
void emitter::emitDispCommentForHandle(size_t handle, size_t cookie, GenTreeFlags flag)
40514052
{
40524053
#ifdef DEBUG
4053-
if (handle == 0)
4054-
{
4055-
return;
4056-
}
4057-
40584054
#ifdef TARGET_XARCH
40594055
const char* commentPrefix = " ;";
40604056
#else
40614057
const char* commentPrefix = " //";
40624058
#endif
40634059

40644060
flag &= GTF_ICON_HDL_MASK;
4065-
const char* str = nullptr;
40664061

4062+
if (cookie != 0)
4063+
{
4064+
if (flag == GTF_ICON_FTN_ADDR)
4065+
{
4066+
const char* className = nullptr;
4067+
const char* methName =
4068+
emitComp->eeGetMethodName(reinterpret_cast<CORINFO_METHOD_HANDLE>(cookie), &className);
4069+
printf("%s code for %s:%s", commentPrefix, className, methName);
4070+
return;
4071+
}
4072+
4073+
if ((flag == GTF_ICON_STATIC_HDL) || (flag == GTF_ICON_STATIC_BOX_PTR))
4074+
{
4075+
const char* className = nullptr;
4076+
const char* fieldName =
4077+
emitComp->eeGetFieldName(reinterpret_cast<CORINFO_FIELD_HANDLE>(cookie), &className);
4078+
printf("%s %s for %s%s%s", commentPrefix, flag == GTF_ICON_STATIC_HDL ? "data" : "box", className,
4079+
className != nullptr ? ":" : "", fieldName);
4080+
return;
4081+
}
4082+
}
4083+
4084+
if (handle == 0)
4085+
{
4086+
return;
4087+
}
4088+
4089+
const char* str = nullptr;
40674090
if (flag == GTF_ICON_STR_HDL)
40684091
{
40694092
const WCHAR* wstr = emitComp->eeGetCPString(handle);
@@ -4103,8 +4126,6 @@ void emitter::emitDispCommentForHandle(size_t handle, GenTreeFlags flag)
41034126
{
41044127
str = emitComp->eeGetClassName(reinterpret_cast<CORINFO_CLASS_HANDLE>(handle));
41054128
}
4106-
#ifndef TARGET_XARCH
4107-
// These are less useful for xarch:
41084129
else if (flag == GTF_ICON_CONST_PTR)
41094130
{
41104131
str = "const ptr";
@@ -4133,11 +4154,6 @@ void emitter::emitDispCommentForHandle(size_t handle, GenTreeFlags flag)
41334154
{
41344155
str = "token handle";
41354156
}
4136-
else
4137-
{
4138-
str = "unknown";
4139-
}
4140-
#endif // TARGET_XARCH
41414157

41424158
if (str != nullptr)
41434159
{

src/coreclr/jit/emit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ class emitter
523523

524524
void emitRecomputeIGoffsets();
525525

526-
void emitDispCommentForHandle(size_t handle, GenTreeFlags flags);
526+
void emitDispCommentForHandle(size_t handle, size_t cookie, GenTreeFlags flags);
527527

528528
/************************************************************************/
529529
/* The following describes a single instruction */
@@ -554,7 +554,7 @@ class emitter
554554

555555
#endif // TARGET_XARCH
556556

557-
#ifdef DEBUG // This information is used in DEBUG builds to display the method name for call instructions
557+
#ifdef DEBUG // This information is used in DEBUG builds for additional diagnostics
558558

559559
struct instrDesc;
560560

0 commit comments

Comments
 (0)