Skip to content

Commit c032e0d

Browse files
authored
Structs: Do not mark "Hidden buffer pointer" as address-exposed (#64130)
* Preliminary changes * Add the missing push of ssadef * Fix an issue for call tree cloning * formatting * minor review comments incorporation * Make GT_CALL as full-def * Merge RenameCall into RenameDef * Fix DefinesLocalAddr and lateuse arg * Minor comments * fix the recently added code to account that GT_CALL can also have defintion: * clone the return buffer arg * Another round of review comments * Handle return buffer calls in optIsVarAssgCB * Update locals defined by calls in NodeInfo * Fix a case where arg can be putarg * Restructure the cases in GetLclRetBufArgNode() * move ivaMaskCall outside the condition * Add back to call for DoNotEnregister which was missed for Release builds * Retrieve the appropriate node in case of copyReload * Added an assert * remove assert and add comment
1 parent ac84ea6 commit c032e0d

16 files changed

Lines changed: 400 additions & 100 deletions

src/coreclr/jit/clrjit.natvis

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,26 @@ Documentation for VS debugger format specifiers: https://docs.microsoft.com/en-u
3939
<!-- GenTree -->
4040

4141
<Type Name="GenTree">
42-
<DisplayString>[{gtOper,en}, {gtType,en}]</DisplayString>
42+
<DisplayString>{gtTreeID, d}: [[{gtOper,en}, {gtType,en}]</DisplayString>
4343
</Type>
4444
<Type Name="GenTreeIntCon">
45-
<DisplayString>[IntCon={((GenTreeIntCon*)this)-&gt;gtIconVal, d}]</DisplayString>
45+
<DisplayString>{gtTreeID, d}: [[IntCon={((GenTreeIntCon*)this)-&gt;gtIconVal, d}]</DisplayString>
4646
</Type>
4747
<Type Name="GenTreeDblCon">
48-
<DisplayString>[DblCon={((GenTreeDblCon*)this)-&gt;gtDconVal, g}]</DisplayString>
48+
<DisplayString>{gtTreeID, d}: [[DblCon={((GenTreeDblCon*)this)-&gt;gtDconVal, g}]</DisplayString>
4949
</Type>
5050
<Type Name="GenTreeStrCon">
5151
<DisplayString>CNS_STR</DisplayString>
5252
</Type>
5353
<Type Name="GenTreeLngCon">
54-
<DisplayString>[LngCon={((GenTreeLngCon*)this)-&gt;gtLconVal, l}]</DisplayString>
54+
<DisplayString>{gtTreeID, d}: [[LngCon={((GenTreeLngCon*)this)-&gt;gtLconVal, l}]</DisplayString>
5555
</Type>
5656
<Type Name="GenTreeOp">
57-
<DisplayString Condition="this->gtOper==GT_ASG">[{this-&gt;gtOp1,na}={this-&gt;gtOp2,na}]</DisplayString>
58-
<DisplayString Condition="this->gtOper==GT_CAST">[{((GenTreeCast*)this)-&gt;gtCastType,en} &lt;- {((GenTreeUnOp*)this)-&gt;gtOp1-&gt;gtType,en}]</DisplayString>
59-
<DisplayString Condition="this->gtOper==GT_SIMD">[{((GenTreeSIMD*)this)-&gt;gtSIMDIntrinsicID,en}, {gtType,en}]</DisplayString>
60-
<DisplayString Condition="this->gtOper==GT_HWINTRINSIC">[{((GenTreeHWIntrinsic*)this)-&gt;gtHWIntrinsicId,en}, {gtType,en}]</DisplayString>
61-
<DisplayString>[{gtOper,en}, {gtType,en}]</DisplayString>
57+
<DisplayString Condition="this->gtOper==GT_ASG">{gtTreeID, d}: [[{this-&gt;gtOp1,na}={this-&gt;gtOp2,na}]</DisplayString>
58+
<DisplayString Condition="this->gtOper==GT_CAST">{gtTreeID, d}: [[{((GenTreeCast*)this)-&gt;gtCastType,en} &lt;- {((GenTreeUnOp*)this)-&gt;gtOp1-&gt;gtType,en}]</DisplayString>
59+
<DisplayString Condition="this->gtOper==GT_SIMD">{gtTreeID, d}: [[{((GenTreeSIMD*)this)-&gt;gtSIMDIntrinsicID,en}, {gtType,en}]</DisplayString>
60+
<DisplayString Condition="this->gtOper==GT_HWINTRINSIC">{gtTreeID, d}: [[{((GenTreeHWIntrinsic*)this)-&gt;gtHWIntrinsicId,en}, {gtType,en}]</DisplayString>
61+
<DisplayString>{gtTreeID, d}: [[{gtOper,en}, {gtType,en}]</DisplayString>
6262
</Type>
6363

6464
<Type Name="Statement">
@@ -77,11 +77,11 @@ Documentation for VS debugger format specifiers: https://docs.microsoft.com/en-u
7777
</Type>
7878

7979
<Type Name="GenTreeLclVar" Inheritable="false">
80-
<DisplayString>[{gtOper,en}, {gtType,en} V{((GenTreeLclVar*)this)-&gt;_gtLclNum,u}]</DisplayString>
80+
<DisplayString>{gtTreeID, d}: [[{gtOper,en}, {gtType,en} V{((GenTreeLclVar*)this)-&gt;_gtLclNum,u}]</DisplayString>
8181
</Type>
8282

8383
<Type Name="GenTreeLclFld" Inheritable="false">
84-
<DisplayString>[{gtOper,en}, {gtType,en} V{((GenTreeLclFld*)this)-&gt;_gtLclNum,u}[+{((GenTreeLclFld*)this)-&gt;m_lclOffs,u}]]</DisplayString>
84+
<DisplayString>{gtTreeID, d}: [[{gtOper,en}, {gtType,en} V{((GenTreeLclFld*)this)-&gt;_gtLclNum,u}[+{((GenTreeLclFld*)this)-&gt;m_lclOffs,u}]]</DisplayString>
8585
</Type>
8686

8787
<!-- Register allocation -->

src/coreclr/jit/compiler.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2234,7 +2234,7 @@ void Compiler::compSetProcessor()
22342234
opts.compUseCMOV = jitFlags.IsSet(JitFlags::JIT_FLAG_USE_CMOV);
22352235
#ifdef DEBUG
22362236
if (opts.compUseCMOV)
2237-
opts.compUseCMOV = !compStressCompile(STRESS_USE_CMOV, 50);
2237+
opts.compUseCMOV = !compStressCompile(STRESS_USE_CMOV, 50);
22382238
#endif // DEBUG
22392239

22402240
#endif // TARGET_X86
@@ -2409,15 +2409,17 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
24092409
opts.compJitAlignLoopBoundary = (unsigned short)JitConfig.JitAlignLoopBoundary();
24102410
opts.compJitAlignLoopMinBlockWeight = (unsigned short)JitConfig.JitAlignLoopMinBlockWeight();
24112411

2412-
opts.compJitAlignLoopForJcc = JitConfig.JitAlignLoopForJcc() == 1;
2413-
opts.compJitAlignLoopMaxCodeSize = (unsigned short)JitConfig.JitAlignLoopMaxCodeSize();
2414-
opts.compJitHideAlignBehindJmp = JitConfig.JitHideAlignBehindJmp() == 1;
2412+
opts.compJitAlignLoopForJcc = JitConfig.JitAlignLoopForJcc() == 1;
2413+
opts.compJitAlignLoopMaxCodeSize = (unsigned short)JitConfig.JitAlignLoopMaxCodeSize();
2414+
opts.compJitHideAlignBehindJmp = JitConfig.JitHideAlignBehindJmp() == 1;
2415+
opts.compJitOptimizeStructHiddenBuffer = JitConfig.JitOptimizeStructHiddenBuffer() == 1;
24152416
#else
2416-
opts.compJitAlignLoopAdaptive = true;
2417-
opts.compJitAlignLoopBoundary = DEFAULT_ALIGN_LOOP_BOUNDARY;
2418-
opts.compJitAlignLoopMinBlockWeight = DEFAULT_ALIGN_LOOP_MIN_BLOCK_WEIGHT;
2419-
opts.compJitAlignLoopMaxCodeSize = DEFAULT_MAX_LOOPSIZE_FOR_ALIGN;
2420-
opts.compJitHideAlignBehindJmp = true;
2417+
opts.compJitAlignLoopAdaptive = true;
2418+
opts.compJitAlignLoopBoundary = DEFAULT_ALIGN_LOOP_BOUNDARY;
2419+
opts.compJitAlignLoopMinBlockWeight = DEFAULT_ALIGN_LOOP_MIN_BLOCK_WEIGHT;
2420+
opts.compJitAlignLoopMaxCodeSize = DEFAULT_MAX_LOOPSIZE_FOR_ALIGN;
2421+
opts.compJitHideAlignBehindJmp = true;
2422+
opts.compJitOptimizeStructHiddenBuffer = true;
24212423
#endif
24222424

24232425
#ifdef TARGET_XARCH
@@ -9880,6 +9882,9 @@ void Compiler::EnregisterStats::RecordLocal(const LclVarDsc* varDsc)
98809882
case DoNotEnregisterReason::AddrExposed:
98819883
m_addrExposed++;
98829884
break;
9885+
case DoNotEnregisterReason::HiddenBufferStructArg:
9886+
m_hiddenStructArg++;
9887+
break;
98839888
case DoNotEnregisterReason::DontEnregStructs:
98849889
m_dontEnregStructs++;
98859890
break;
@@ -10050,6 +10055,7 @@ void Compiler::EnregisterStats::Dump(FILE* fout) const
1005010055
}
1005110056

1005210057
PRINT_STATS(m_addrExposed, notEnreg);
10058+
PRINT_STATS(m_hiddenStructArg, notEnreg);
1005310059
PRINT_STATS(m_dontEnregStructs, notEnreg);
1005410060
PRINT_STATS(m_notRegSizeStruct, notEnreg);
1005510061
PRINT_STATS(m_localField, notEnreg);

src/coreclr/jit/compiler.h

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ class LclSsaVarDsc
218218
{
219219
}
220220

221+
LclSsaVarDsc(BasicBlock* block) : m_block(block), m_asg(nullptr)
222+
{
223+
}
224+
221225
LclSsaVarDsc(BasicBlock* block, GenTreeOp* asg) : m_block(block), m_asg(asg)
222226
{
223227
assert((asg == nullptr) || asg->OperIs(GT_ASG));
@@ -392,12 +396,13 @@ enum class DoNotEnregisterReason
392396
#endif
393397
LclAddrNode, // the local is accessed with LCL_ADDR_VAR/FLD.
394398
CastTakesAddr,
395-
StoreBlkSrc, // the local is used as STORE_BLK source.
396-
OneAsgRetyping, // fgMorphOneAsgBlockOp prevents this local from being enregister.
397-
SwizzleArg, // the local is passed using LCL_FLD as another type.
398-
BlockOpRet, // the struct is returned and it promoted or there is a cast.
399-
ReturnSpCheck, // the local is used to do SP check
400-
SimdUserForcesDep // a promoted struct was used by a SIMD/HWI node; it must be dependently promoted
399+
StoreBlkSrc, // the local is used as STORE_BLK source.
400+
OneAsgRetyping, // fgMorphOneAsgBlockOp prevents this local from being enregister.
401+
SwizzleArg, // the local is passed using LCL_FLD as another type.
402+
BlockOpRet, // the struct is returned and it promoted or there is a cast.
403+
ReturnSpCheck, // the local is used to do SP check
404+
SimdUserForcesDep, // a promoted struct was used by a SIMD/HWI node; it must be dependently promoted
405+
HiddenBufferStructArg // the argument is a hidden return buffer passed to a method.
401406
};
402407

403408
enum class AddressExposedReason
@@ -527,6 +532,11 @@ class LclVarDsc
527532
unsigned char lvIsMultiRegArg : 1; // true if this is a multireg LclVar struct used in an argument context
528533
unsigned char lvIsMultiRegRet : 1; // true if this is a multireg LclVar struct assigned from a multireg call
529534

535+
#ifdef DEBUG
536+
unsigned char lvHiddenBufferStructArg : 1; // True when this struct (or its field) are passed as hidden buffer
537+
// pointer.
538+
#endif
539+
530540
#ifdef FEATURE_HFA_FIELDS_PRESENT
531541
CorInfoHFAElemType _lvHfaElemKind : 3; // What kind of an HFA this is (CORINFO_HFA_ELEM_NONE if it is not an HFA).
532542
#endif // FEATURE_HFA_FIELDS_PRESENT
@@ -752,6 +762,18 @@ class LclVarDsc
752762
return m_addrExposed;
753763
}
754764

765+
#ifdef DEBUG
766+
void SetHiddenBufferStructArg(char value)
767+
{
768+
lvHiddenBufferStructArg = value;
769+
}
770+
771+
bool IsHiddenBufferStructArg() const
772+
{
773+
return lvHiddenBufferStructArg;
774+
}
775+
#endif
776+
755777
private:
756778
regNumberSmall _lvRegNum; // Used to store the register this variable is in (or, the low register of a
757779
// register pair). It is set during codegen any time the
@@ -3784,6 +3806,7 @@ class Compiler
37843806
// Getters and setters for address-exposed and do-not-enregister local var properties.
37853807
bool lvaVarAddrExposed(unsigned varNum) const;
37863808
void lvaSetVarAddrExposed(unsigned varNum DEBUGARG(AddressExposedReason reason));
3809+
void lvaSetHiddenBufferStructArg(unsigned varNum);
37873810
void lvaSetVarLiveInOutOfHandler(unsigned varNum);
37883811
bool lvaVarDoNotEnregister(unsigned varNum);
37893812

@@ -7494,7 +7517,7 @@ class Compiler
74947517
void optCopyProp(Statement* stmt, GenTreeLclVarCommon* tree, unsigned lclNum, LclNumToLiveDefsMap* curSsaName);
74957518
void optBlockCopyPropPopStacks(BasicBlock* block, LclNumToLiveDefsMap* curSsaName);
74967519
void optBlockCopyProp(BasicBlock* block, LclNumToLiveDefsMap* curSsaName);
7497-
void optCopyPropPushDef(GenTreeOp* asg,
7520+
void optCopyPropPushDef(GenTree* defNode,
74987521
GenTreeLclVarCommon* lclNode,
74997522
unsigned lclNum,
75007523
LclNumToLiveDefsMap* curSsaName);
@@ -9921,6 +9944,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
99219944
// If set, tries to hide alignment instructions behind unconditional jumps.
99229945
bool compJitHideAlignBehindJmp;
99239946

9947+
// If set, tracks the hidden return buffer for struct arg.
9948+
bool compJitOptimizeStructHiddenBuffer;
9949+
99249950
#ifdef LATE_DISASM
99259951
bool doLateDisasm; // Run the late disassembler
99269952
#endif // LATE_DISASM
@@ -10570,9 +10596,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1057010596
// size of the type these describe.
1057110597
unsigned compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd);
1057210598

10573-
// Returns true if the method being compiled has a return buffer.
10574-
bool compHasRetBuffArg();
10575-
1057610599
#ifdef DEBUG
1057710600
// Components used by the compiler may write unit test suites, and
1057810601
// have them run within this method. They will be run only once per process, and only
@@ -10628,6 +10651,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1062810651
unsigned m_totalNumberOfStructEnregVars;
1062910652

1063010653
unsigned m_addrExposed;
10654+
unsigned m_hiddenStructArg;
1063110655
unsigned m_VMNeedsStackAddr;
1063210656
unsigned m_localField;
1063310657
unsigned m_blockOp;

src/coreclr/jit/copyprop.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ void Compiler::optBlockCopyPropPopStacks(BasicBlock* block, LclNumToLiveDefsMap*
3333
for (GenTree* const tree : stmt->TreeList())
3434
{
3535
GenTreeLclVarCommon* lclDefNode = nullptr;
36-
if (tree->OperIs(GT_ASG) && tree->DefinesLocal(this, &lclDefNode))
36+
if (tree->OperIsSsaDef() && tree->DefinesLocal(this, &lclDefNode))
3737
{
3838
const unsigned lclNum = optIsSsaLocal(lclDefNode);
3939

@@ -279,12 +279,12 @@ unsigned Compiler::optIsSsaLocal(GenTreeLclVarCommon* lclNode)
279279
// optCopyPropPushDef: Push the new live SSA def on the stack for "lclNode".
280280
//
281281
// Arguments:
282-
// asg - The assignment node for this def (will be "nullptr" for "use" defs)
282+
// defNode - The definition node for this def (GT_ASG/GT_CALL) (will be "nullptr" for "use" defs)
283283
// lclNode - The local tree representing "the def" (that can actually be a use)
284284
// lclNum - The local's number (see "optIsSsaLocal")
285285
// curSsaName - The map of local numbers to stacks of their defs
286286
//
287-
void Compiler::optCopyPropPushDef(GenTreeOp* asg,
287+
void Compiler::optCopyPropPushDef(GenTree* defNode,
288288
GenTreeLclVarCommon* lclNode,
289289
unsigned lclNum,
290290
LclNumToLiveDefsMap* curSsaName)
@@ -301,7 +301,7 @@ void Compiler::optCopyPropPushDef(GenTreeOp* asg,
301301
}
302302

303303
unsigned ssaDefNum = SsaConfig::RESERVED_SSA_NUM;
304-
if (asg == nullptr)
304+
if (defNode == nullptr)
305305
{
306306
// Parameters, this pointer etc.
307307
assert((lclNode->gtFlags & GTF_VAR_DEF) == 0);
@@ -313,7 +313,7 @@ void Compiler::optCopyPropPushDef(GenTreeOp* asg,
313313
assert((lclNode->gtFlags & GTF_VAR_DEF) != 0);
314314

315315
// TODO-CQ: design better heuristics for propagation and remove this condition.
316-
if (!asg->IsPhiDefn())
316+
if (!defNode->IsPhiDefn())
317317
{
318318
ssaDefNum = GetSsaNumForLocalVarDef(lclNode);
319319

@@ -377,7 +377,7 @@ void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToLiveDefsMap* curSsaNa
377377
treeLifeUpdater.UpdateLife(tree);
378378

379379
GenTreeLclVarCommon* lclDefNode = nullptr;
380-
if (tree->OperIs(GT_ASG) && tree->DefinesLocal(this, &lclDefNode))
380+
if (tree->OperIsSsaDef() && tree->DefinesLocal(this, &lclDefNode))
381381
{
382382
const unsigned lclNum = optIsSsaLocal(lclDefNode);
383383

@@ -386,7 +386,7 @@ void Compiler::optBlockCopyProp(BasicBlock* block, LclNumToLiveDefsMap* curSsaNa
386386
continue;
387387
}
388388

389-
optCopyPropPushDef(tree->AsOp(), lclDefNode, lclNum, curSsaName);
389+
optCopyPropPushDef(tree, lclDefNode, lclNum, curSsaName);
390390
}
391391
// TODO-CQ: propagate on LCL_FLDs too.
392392
else if (tree->OperIs(GT_LCL_VAR) && ((tree->gtFlags & GTF_VAR_DEF) == 0))

src/coreclr/jit/gentree.cpp

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,6 +1266,16 @@ bool GenTreeCall::AreArgsComplete() const
12661266
return false;
12671267
}
12681268

1269+
//-------------------------------------------------------------------------
1270+
// SetRetBufArg: Sets the "return buffer" argument use.
1271+
//
1272+
void GenTreeCall::SetLclRetBufArg(Use* retBufArg)
1273+
{
1274+
assert(retBufArg->GetNode()->TypeIs(TYP_I_IMPL, TYP_BYREF) && retBufArg->GetNode()->OperIs(GT_ADDR, GT_ASG));
1275+
assert(HasRetBufArg());
1276+
gtRetBufArg = retBufArg;
1277+
}
1278+
12691279
//--------------------------------------------------------------------------
12701280
// Equals: Check if 2 CALL nodes are equal.
12711281
//
@@ -5376,6 +5386,12 @@ bool GenTree::OperRequiresAsgFlag()
53765386
}
53775387
}
53785388
#endif // FEATURE_HW_INTRINSICS
5389+
if (gtOper == GT_CALL)
5390+
{
5391+
// If the call has return buffer argument, it produced a definition and hence
5392+
// should be marked with assignment.
5393+
return AsCall()->GetLclRetBufArgNode() != nullptr;
5394+
}
53795395
return false;
53805396
}
53815397

@@ -7942,21 +7958,46 @@ GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree,
79427958
copy->gtCallMoreFlags = tree->gtCallMoreFlags;
79437959
copy->gtCallArgs = nullptr;
79447960
copy->gtCallLateArgs = nullptr;
7961+
copy->gtRetBufArg = nullptr;
79457962

79467963
GenTreeCall::Use** argsTail = &copy->gtCallArgs;
79477964
for (GenTreeCall::Use& use : tree->Args())
79487965
{
7949-
*argsTail = gtNewCallArgs(gtCloneExpr(use.GetNode(), addFlags, deepVarNum, deepVarVal));
7950-
argsTail = &((*argsTail)->NextRef());
7966+
GenTree* argNode = use.GetNode();
7967+
GenTree* copyArgNode = gtCloneExpr(argNode, addFlags, deepVarNum, deepVarVal);
7968+
*argsTail = gtNewCallArgs(copyArgNode);
7969+
7970+
if (tree->gtRetBufArg == &use)
7971+
{
7972+
// Set the return buffer arg, if any.
7973+
assert(copy->gtRetBufArg == nullptr);
7974+
copy->gtRetBufArg = *argsTail;
7975+
}
7976+
7977+
argsTail = &((*argsTail)->NextRef());
79517978
}
79527979

79537980
argsTail = &copy->gtCallLateArgs;
79547981
for (GenTreeCall::Use& use : tree->LateArgs())
79557982
{
7956-
*argsTail = gtNewCallArgs(gtCloneExpr(use.GetNode(), addFlags, deepVarNum, deepVarVal));
7957-
argsTail = &((*argsTail)->NextRef());
7983+
GenTree* argNode = use.GetNode();
7984+
GenTree* copyArgNode = gtCloneExpr(argNode, addFlags, deepVarNum, deepVarVal);
7985+
*argsTail = gtNewCallArgs(copyArgNode);
7986+
7987+
if (tree->gtRetBufArg == &use)
7988+
{
7989+
// Set the return buffer arg, if any.
7990+
assert(copy->gtRetBufArg == nullptr);
7991+
copy->gtRetBufArg = *argsTail;
7992+
}
7993+
7994+
argsTail = &((*argsTail)->NextRef());
79587995
}
79597996

7997+
// Either there was not return buffer for the "tree" or else we successfully set the
7998+
// return buffer in the copy.
7999+
assert((tree->gtRetBufArg == nullptr) || (copy->gtRetBufArg != nullptr));
8000+
79608001
// The call sig comes from the EE and doesn't change throughout the compilation process, meaning
79618002
// we only really need one physical copy of it. Therefore a shallow pointer copy will suffice.
79628003
// (Note that this still holds even if the tree we are cloning was created by an inlinee compiler,
@@ -9781,7 +9822,10 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, _In_ _In_opt_
97819822
{
97829823
printf("(AX)"); // Variable has address exposed.
97839824
}
9784-
9825+
if (varDsc->IsHiddenBufferStructArg())
9826+
{
9827+
printf("(RB)"); // Variable is hidden return buffer
9828+
}
97859829
if (varDsc->lvUnusedStruct)
97869830
{
97879831
assert(varDsc->lvPromoted);
@@ -15461,6 +15505,17 @@ bool GenTree::DefinesLocal(Compiler* comp, GenTreeLclVarCommon** pLclVarTree, bo
1546115505
blkNode = AsOp()->gtOp1->AsBlk();
1546215506
}
1546315507
}
15508+
else if (OperIs(GT_CALL))
15509+
{
15510+
GenTree* retBufArg = AsCall()->GetLclRetBufArgNode();
15511+
if (retBufArg == nullptr)
15512+
{
15513+
return false;
15514+
}
15515+
15516+
unsigned size = comp->typGetObjLayout(AsCall()->gtRetClsHnd)->GetSize();
15517+
return retBufArg->DefinesLocalAddr(comp, size, pLclVarTree, pIsEntire);
15518+
}
1546415519
else if (OperIsBlk())
1546515520
{
1546615521
blkNode = this->AsBlk();

0 commit comments

Comments
 (0)