Skip to content

Commit 0df43f2

Browse files
authoredMar 5, 2024
Do not contain out-of-bounds local addresses (#99195)
* Do not contain out-of-bounds local access * Fix HWIs * Codegen fixups
1 parent 825bc44 commit 0df43f2

12 files changed

+110
-96
lines changed
 

‎src/coreclr/jit/codegenloongarch64.cpp

+2-23
Original file line numberDiff line numberDiff line change
@@ -5290,7 +5290,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
52905290

52915291
// addrNode can either be a GT_LCL_ADDR<0> or an address expression
52925292
//
5293-
if (addrNode->IsLclVarAddr())
5293+
if (addrNode->isContained() && addrNode->IsLclVarAddr())
52945294
{
52955295
// We have a GT_BLK(GT_LCL_ADDR<0>)
52965296
//
@@ -5563,7 +5563,7 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
55635563

55645564
// addrNode can either be a GT_LCL_ADDR<0> or an address expression
55655565
//
5566-
if (addrNode->IsLclVarAddr())
5566+
if (addrNode->isContained() && addrNode->IsLclVarAddr())
55675567
{
55685568
// We have a GT_BLK(GT_LCL_ADDR<0>)
55695569
//
@@ -6334,27 +6334,6 @@ void CodeGen::genCodeForInitBlkLoop(GenTreeBlk* initBlkNode)
63346334
}
63356335
}
63366336

6337-
// Generate code for a load from some address + offset
6338-
// base: tree node which can be either a local address or arbitrary node
6339-
// offset: distance from the base from which to load
6340-
void CodeGen::genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* base, unsigned offset)
6341-
{
6342-
emitter* emit = GetEmitter();
6343-
6344-
if (base->OperIs(GT_LCL_ADDR))
6345-
{
6346-
if (base->gtOper == GT_LCL_ADDR)
6347-
{
6348-
offset += base->AsLclFld()->GetLclOffs();
6349-
}
6350-
emit->emitIns_R_S(ins, size, dst, base->AsLclVarCommon()->GetLclNum(), offset);
6351-
}
6352-
else
6353-
{
6354-
emit->emitIns_R_R_I(ins, size, dst, base->GetRegNum(), offset);
6355-
}
6356-
}
6357-
63586337
//------------------------------------------------------------------------
63596338
// genCall: Produce code for a GT_CALL node
63606339
//

‎src/coreclr/jit/compiler.h

+2
Original file line numberDiff line numberDiff line change
@@ -6675,6 +6675,8 @@ class Compiler
66756675
public:
66766676
bool fgIsBigOffset(size_t offset);
66776677

6678+
bool IsValidLclAddr(unsigned lclNum, unsigned offset);
6679+
66786680
private:
66796681
bool fgNeedReturnSpillTemp();
66806682

‎src/coreclr/jit/compiler.hpp

+18
Original file line numberDiff line numberDiff line change
@@ -3165,6 +3165,24 @@ inline bool Compiler::fgIsBigOffset(size_t offset)
31653165
return (offset > compMaxUncheckedOffsetForNullObject);
31663166
}
31673167

3168+
//------------------------------------------------------------------------
3169+
// IsValidLclAddr: Can the given local address be represented as "LCL_FLD_ADDR"?
3170+
//
3171+
// Local address nodes cannot point beyond the local and can only store
3172+
// 16 bits worth of offset.
3173+
//
3174+
// Arguments:
3175+
// lclNum - The local's number
3176+
// offset - The address' offset
3177+
//
3178+
// Return Value:
3179+
// Whether "LCL_FLD_ADDR<lclNum> [+offset]" would be valid IR.
3180+
//
3181+
inline bool Compiler::IsValidLclAddr(unsigned lclNum, unsigned offset)
3182+
{
3183+
return (offset < UINT16_MAX) && (offset < lvaLclExactSize(lclNum));
3184+
}
3185+
31683186
/*
31693187
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
31703188
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

‎src/coreclr/jit/emitxarch.cpp

+13-13
Original file line numberDiff line numberDiff line change
@@ -4849,7 +4849,7 @@ void emitter::emitInsLoadInd(instruction ins, emitAttr attr, regNumber dstReg, G
48494849

48504850
GenTree* addr = mem->Addr();
48514851

4852-
if (addr->OperIs(GT_LCL_ADDR))
4852+
if (addr->isContained() && addr->OperIs(GT_LCL_ADDR))
48534853
{
48544854
GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
48554855
unsigned offset = varNode->GetLclOffs();
@@ -4899,7 +4899,7 @@ void emitter::emitInsStoreInd(instruction ins, emitAttr attr, GenTreeStoreInd* m
48994899
data = data->gtGetOp1();
49004900
}
49014901

4902-
if (addr->OperIs(GT_LCL_ADDR))
4902+
if (addr->isContained() && addr->OperIs(GT_LCL_ADDR))
49034903
{
49044904
GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
49054905
unsigned offset = varNode->GetLclOffs();
@@ -5140,18 +5140,18 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
51405140
switch (memBase->OperGet())
51415141
{
51425142
case GT_LCL_ADDR:
5143-
{
5144-
assert(memBase->isContained());
5145-
varNum = memBase->AsLclFld()->GetLclNum();
5146-
offset = memBase->AsLclFld()->GetLclOffs();
5147-
5148-
// Ensure that all the GenTreeIndir values are set to their defaults.
5149-
assert(!memIndir->HasIndex());
5150-
assert(memIndir->Scale() == 1);
5151-
assert(memIndir->Offset() == 0);
5143+
if (memBase->isContained())
5144+
{
5145+
varNum = memBase->AsLclFld()->GetLclNum();
5146+
offset = memBase->AsLclFld()->GetLclOffs();
51525147

5153-
break;
5154-
}
5148+
// Ensure that all the GenTreeIndir values are set to their defaults.
5149+
assert(!memIndir->HasIndex());
5150+
assert(memIndir->Scale() == 1);
5151+
assert(memIndir->Offset() == 0);
5152+
break;
5153+
}
5154+
FALLTHROUGH;
51555155

51565156
default: // Addressing mode [base + index * scale + offset]
51575157
{

‎src/coreclr/jit/instr.cpp

+7-11
Original file line numberDiff line numberDiff line change
@@ -916,18 +916,14 @@ CodeGen::OperandDesc CodeGen::genOperandDesc(GenTree* op)
916916
#endif // FEATURE_HW_INTRINSICS
917917
}
918918

919-
switch (addr->OperGet())
919+
if (addr->isContained() && addr->OperIs(GT_LCL_ADDR))
920920
{
921-
case GT_LCL_ADDR:
922-
{
923-
assert(addr->isContained());
924-
varNum = addr->AsLclFld()->GetLclNum();
925-
offset = addr->AsLclFld()->GetLclOffs();
926-
break;
927-
}
928-
929-
default:
930-
return (memIndir != nullptr) ? OperandDesc(memIndir) : OperandDesc(op->TypeGet(), addr);
921+
varNum = addr->AsLclFld()->GetLclNum();
922+
offset = addr->AsLclFld()->GetLclOffs();
923+
}
924+
else
925+
{
926+
return (memIndir != nullptr) ? OperandDesc(memIndir) : OperandDesc(op->TypeGet(), addr);
931927
}
932928
}
933929
else

‎src/coreclr/jit/lclmorph.cpp

+3-20
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
719719
GenTreeCall* callUser = user->IsCall() ? user->AsCall() : nullptr;
720720
bool hasHiddenStructArg = false;
721721
if (m_compiler->opts.compJitOptimizeStructHiddenBuffer && (callUser != nullptr) &&
722-
IsValidLclAddr(lclNum, val.Offset()))
722+
m_compiler->IsValidLclAddr(lclNum, val.Offset()))
723723
{
724724
// We will only attempt this optimization for locals that are:
725725
// a) Not susceptible to liveness bugs (see "lvaSetHiddenBufferStructArg").
@@ -805,6 +805,7 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
805805
unsigned indirSize = node->AsIndir()->Size();
806806
bool isWide;
807807

808+
// TODO-Cleanup: delete "indirSize == 0", use "Compiler::IsValidLclAddr".
808809
if ((indirSize == 0) || ((offset + indirSize) > UINT16_MAX))
809810
{
810811
// If we can't figure out the indirection size then treat it as a wide indirection.
@@ -856,7 +857,7 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
856857
assert(addr->TypeIs(TYP_BYREF, TYP_I_IMPL));
857858
assert(m_compiler->lvaVarAddrExposed(lclNum) || m_compiler->lvaGetDesc(lclNum)->IsHiddenBufferStructArg());
858859

859-
if (IsValidLclAddr(lclNum, offset))
860+
if (m_compiler->IsValidLclAddr(lclNum, offset))
860861
{
861862
addr->ChangeOper(GT_LCL_ADDR);
862863
addr->AsLclFld()->SetLclNum(lclNum);
@@ -1449,24 +1450,6 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
14491450
}
14501451

14511452
private:
1452-
//------------------------------------------------------------------------
1453-
// IsValidLclAddr: Can the given local address be represented as "LCL_FLD_ADDR"?
1454-
//
1455-
// Local address nodes cannot point beyond the local and can only store
1456-
// 16 bits worth of offset.
1457-
//
1458-
// Arguments:
1459-
// lclNum - The local's number
1460-
// offset - The address' offset
1461-
//
1462-
// Return Value:
1463-
// Whether "LCL_FLD_ADDR<lclNum> [+offset]" would be valid IR.
1464-
//
1465-
bool IsValidLclAddr(unsigned lclNum, unsigned offset) const
1466-
{
1467-
return (offset < UINT16_MAX) && (offset < m_compiler->lvaLclExactSize(lclNum));
1468-
}
1469-
14701453
//------------------------------------------------------------------------
14711454
// IsUnused: is the given node unused?
14721455
//

‎src/coreclr/jit/lower.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -9097,6 +9097,36 @@ void Lowering::UnmarkTree(GenTree* node)
90979097

90989098
#endif // TARGET_ARM64
90999099

9100+
//------------------------------------------------------------------------
9101+
// IsContainableLclAddr: Can a given local address be contained?
9102+
//
9103+
// Most local addresses can be contained, however, there are two edge cases
9104+
// where this is not true:
9105+
// 1. When the resulting memory access will go beyond the local's location.
9106+
// 2. When the resulting access may go past a UINT16_MAX.
9107+
// Both of these requirements are imposed by the emitter.
9108+
//
9109+
// Arguments:
9110+
// lclAddr - The local address node
9111+
// accessSize - The access size (of an indirection)
9112+
//
9113+
// Return Value:
9114+
// Whether an indirection of "accessSize" may contain "lclAddr".
9115+
//
9116+
bool Lowering::IsContainableLclAddr(GenTreeLclFld* lclAddr, unsigned accessSize) const
9117+
{
9118+
if (CheckedOps::AddOverflows<int32_t>(lclAddr->GetLclOffs(), accessSize, CheckedOps::Unsigned) ||
9119+
!comp->IsValidLclAddr(lclAddr->GetLclNum(), lclAddr->GetLclOffs() + accessSize - 1))
9120+
{
9121+
// We depend on containment for correctness of liveness updates in codegen. Therefore, all
9122+
// locals that may "return false" here MUST be address-exposed. Local morph ensures this.
9123+
assert(comp->lvaGetDesc(lclAddr)->IsAddressExposed());
9124+
return false;
9125+
}
9126+
9127+
return true;
9128+
}
9129+
91009130
//------------------------------------------------------------------------
91019131
// TransformUnusedIndirection: change the opcode and the type of the unused indirection.
91029132
//

‎src/coreclr/jit/lower.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class Lowering final : public Phase
114114
void ContainCheckIntrinsic(GenTreeOp* node);
115115
#endif // TARGET_XARCH
116116
#ifdef FEATURE_HW_INTRINSICS
117-
void ContainCheckHWIntrinsicAddr(GenTreeHWIntrinsic* node, GenTree* addr);
117+
void ContainCheckHWIntrinsicAddr(GenTreeHWIntrinsic* node, GenTree* addr, unsigned size);
118118
void ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node);
119119
#ifdef TARGET_XARCH
120120
void TryFoldCnsVecForEmbeddedBroadcast(GenTreeHWIntrinsic* parentNode, GenTreeVecCon* childNode);
@@ -492,6 +492,8 @@ class Lowering final : public Phase
492492
return false;
493493
}
494494

495+
bool IsContainableLclAddr(GenTreeLclFld* lclAddr, unsigned accessSize) const;
496+
495497
#ifdef TARGET_ARM64
496498
bool IsContainableUnaryOrBinaryOp(GenTree* parentNode, GenTree* childNode) const;
497499
#endif // TARGET_ARM64

‎src/coreclr/jit/lowerarmarch.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
705705
assert(blkNode->OperIs(GT_STORE_BLK) && (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll));
706706
assert(size < INT32_MAX);
707707

708-
if (addr->OperIs(GT_LCL_ADDR))
708+
if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), size))
709709
{
710710
addr->SetContained();
711711
return;
@@ -2060,7 +2060,7 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
20602060
MakeSrcContained(indirNode, addr);
20612061
}
20622062
}
2063-
else if (addr->OperIs(GT_LCL_ADDR))
2063+
else if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), indirNode->Size()))
20642064
{
20652065
// These nodes go into an addr mode:
20662066
// - GT_LCL_ADDR is a stack addr mode.

‎src/coreclr/jit/lowerloongarch64.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
407407
assert(blkNode->OperIs(GT_STORE_BLK) && (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll));
408408
assert(size < INT32_MAX);
409409

410-
if (addr->OperIs(GT_LCL_ADDR))
410+
if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), size))
411411
{
412412
addr->SetContained();
413413
return;
@@ -485,7 +485,8 @@ void Lowering::LowerPutArgStkOrSplit(GenTreePutArgStk* putArgNode)
485485
}
486486

487487
// Codegen supports containment of local addresses under BLKs.
488-
if (src->OperIs(GT_BLK) && src->AsBlk()->Addr()->IsLclVarAddr())
488+
if (src->OperIs(GT_BLK) && src->AsBlk()->Addr()->IsLclVarAddr() &&
489+
IsContainableLclAddr(src->AsBlk()->Addr()->AsLclFld(), src->AsBlk()->Size()))
489490
{
490491
// TODO-LOONGARCH64-CQ: support containment of LCL_ADDR with non-zero offset too.
491492
MakeSrcContained(src, src->AsBlk()->Addr());
@@ -704,7 +705,7 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
704705
{
705706
MakeSrcContained(indirNode, addr);
706707
}
707-
else if (addr->OperIs(GT_LCL_ADDR))
708+
else if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), indirNode->Size()))
708709
{
709710
// These nodes go into an addr mode:
710711
// - GT_LCL_ADDR is a stack addr mode.

‎src/coreclr/jit/lowerriscv64.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
354354
assert(blkNode->OperIs(GT_STORE_BLK) && (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll));
355355
assert(size < INT32_MAX);
356356

357-
if (addr->OperIs(GT_LCL_ADDR))
357+
if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), size))
358358
{
359359
addr->SetContained();
360360
return;
@@ -615,7 +615,7 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
615615
{
616616
MakeSrcContained(indirNode, addr);
617617
}
618-
else if (addr->OperIs(GT_LCL_ADDR))
618+
else if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), indirNode->Size()))
619619
{
620620
// These nodes go into an addr mode:
621621
// - GT_LCL_ADDR is a stack addr mode.

0 commit comments

Comments
 (0)
Please sign in to comment.