Skip to content

Commit

Permalink
Do not contain out-of-bounds local addresses (#99195)
Browse files Browse the repository at this point in the history
* Do not contain out-of-bounds local access

* Fix HWIs

* Codegen fixups
  • Loading branch information
SingleAccretion committed Mar 5, 2024
1 parent 825bc44 commit 0df43f2
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 96 deletions.
25 changes: 2 additions & 23 deletions src/coreclr/jit/codegenloongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5290,7 +5290,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)

// addrNode can either be a GT_LCL_ADDR<0> or an address expression
//
if (addrNode->IsLclVarAddr())
if (addrNode->isContained() && addrNode->IsLclVarAddr())
{
// We have a GT_BLK(GT_LCL_ADDR<0>)
//
Expand Down Expand Up @@ -5563,7 +5563,7 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)

// addrNode can either be a GT_LCL_ADDR<0> or an address expression
//
if (addrNode->IsLclVarAddr())
if (addrNode->isContained() && addrNode->IsLclVarAddr())
{
// We have a GT_BLK(GT_LCL_ADDR<0>)
//
Expand Down Expand Up @@ -6334,27 +6334,6 @@ void CodeGen::genCodeForInitBlkLoop(GenTreeBlk* initBlkNode)
}
}

// Generate code for a load from some address + offset
// base: tree node which can be either a local address or arbitrary node
// offset: distance from the base from which to load
void CodeGen::genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* base, unsigned offset)
{
emitter* emit = GetEmitter();

if (base->OperIs(GT_LCL_ADDR))
{
if (base->gtOper == GT_LCL_ADDR)
{
offset += base->AsLclFld()->GetLclOffs();
}
emit->emitIns_R_S(ins, size, dst, base->AsLclVarCommon()->GetLclNum(), offset);
}
else
{
emit->emitIns_R_R_I(ins, size, dst, base->GetRegNum(), offset);
}
}

//------------------------------------------------------------------------
// genCall: Produce code for a GT_CALL node
//
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -6675,6 +6675,8 @@ class Compiler
public:
bool fgIsBigOffset(size_t offset);

bool IsValidLclAddr(unsigned lclNum, unsigned offset);

private:
bool fgNeedReturnSpillTemp();

Expand Down
18 changes: 18 additions & 0 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3165,6 +3165,24 @@ inline bool Compiler::fgIsBigOffset(size_t offset)
return (offset > compMaxUncheckedOffsetForNullObject);
}

//------------------------------------------------------------------------
// IsValidLclAddr: Can the given local address be represented as "LCL_FLD_ADDR"?
//
// Local address nodes cannot point beyond the local and can only store
// 16 bits worth of offset.
//
// Arguments:
// lclNum - The local's number
// offset - The address' offset
//
// Return Value:
// Whether "LCL_FLD_ADDR<lclNum> [+offset]" would be valid IR.
//
inline bool Compiler::IsValidLclAddr(unsigned lclNum, unsigned offset)
{
return (offset < UINT16_MAX) && (offset < lvaLclExactSize(lclNum));
}

/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Expand Down
26 changes: 13 additions & 13 deletions src/coreclr/jit/emitxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4849,7 +4849,7 @@ void emitter::emitInsLoadInd(instruction ins, emitAttr attr, regNumber dstReg, G

GenTree* addr = mem->Addr();

if (addr->OperIs(GT_LCL_ADDR))
if (addr->isContained() && addr->OperIs(GT_LCL_ADDR))
{
GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
unsigned offset = varNode->GetLclOffs();
Expand Down Expand Up @@ -4899,7 +4899,7 @@ void emitter::emitInsStoreInd(instruction ins, emitAttr attr, GenTreeStoreInd* m
data = data->gtGetOp1();
}

if (addr->OperIs(GT_LCL_ADDR))
if (addr->isContained() && addr->OperIs(GT_LCL_ADDR))
{
GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
unsigned offset = varNode->GetLclOffs();
Expand Down Expand Up @@ -5140,18 +5140,18 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
switch (memBase->OperGet())
{
case GT_LCL_ADDR:
{
assert(memBase->isContained());
varNum = memBase->AsLclFld()->GetLclNum();
offset = memBase->AsLclFld()->GetLclOffs();

// Ensure that all the GenTreeIndir values are set to their defaults.
assert(!memIndir->HasIndex());
assert(memIndir->Scale() == 1);
assert(memIndir->Offset() == 0);
if (memBase->isContained())
{
varNum = memBase->AsLclFld()->GetLclNum();
offset = memBase->AsLclFld()->GetLclOffs();

break;
}
// Ensure that all the GenTreeIndir values are set to their defaults.
assert(!memIndir->HasIndex());
assert(memIndir->Scale() == 1);
assert(memIndir->Offset() == 0);
break;
}
FALLTHROUGH;

default: // Addressing mode [base + index * scale + offset]
{
Expand Down
18 changes: 7 additions & 11 deletions src/coreclr/jit/instr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -916,18 +916,14 @@ CodeGen::OperandDesc CodeGen::genOperandDesc(GenTree* op)
#endif // FEATURE_HW_INTRINSICS
}

switch (addr->OperGet())
if (addr->isContained() && addr->OperIs(GT_LCL_ADDR))
{
case GT_LCL_ADDR:
{
assert(addr->isContained());
varNum = addr->AsLclFld()->GetLclNum();
offset = addr->AsLclFld()->GetLclOffs();
break;
}

default:
return (memIndir != nullptr) ? OperandDesc(memIndir) : OperandDesc(op->TypeGet(), addr);
varNum = addr->AsLclFld()->GetLclNum();
offset = addr->AsLclFld()->GetLclOffs();
}
else
{
return (memIndir != nullptr) ? OperandDesc(memIndir) : OperandDesc(op->TypeGet(), addr);
}
}
else
Expand Down
23 changes: 3 additions & 20 deletions src/coreclr/jit/lclmorph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
GenTreeCall* callUser = user->IsCall() ? user->AsCall() : nullptr;
bool hasHiddenStructArg = false;
if (m_compiler->opts.compJitOptimizeStructHiddenBuffer && (callUser != nullptr) &&
IsValidLclAddr(lclNum, val.Offset()))
m_compiler->IsValidLclAddr(lclNum, val.Offset()))
{
// We will only attempt this optimization for locals that are:
// a) Not susceptible to liveness bugs (see "lvaSetHiddenBufferStructArg").
Expand Down Expand Up @@ -805,6 +805,7 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
unsigned indirSize = node->AsIndir()->Size();
bool isWide;

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

if (IsValidLclAddr(lclNum, offset))
if (m_compiler->IsValidLclAddr(lclNum, offset))
{
addr->ChangeOper(GT_LCL_ADDR);
addr->AsLclFld()->SetLclNum(lclNum);
Expand Down Expand Up @@ -1449,24 +1450,6 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
}

private:
//------------------------------------------------------------------------
// IsValidLclAddr: Can the given local address be represented as "LCL_FLD_ADDR"?
//
// Local address nodes cannot point beyond the local and can only store
// 16 bits worth of offset.
//
// Arguments:
// lclNum - The local's number
// offset - The address' offset
//
// Return Value:
// Whether "LCL_FLD_ADDR<lclNum> [+offset]" would be valid IR.
//
bool IsValidLclAddr(unsigned lclNum, unsigned offset) const
{
return (offset < UINT16_MAX) && (offset < m_compiler->lvaLclExactSize(lclNum));
}

//------------------------------------------------------------------------
// IsUnused: is the given node unused?
//
Expand Down
30 changes: 30 additions & 0 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9097,6 +9097,36 @@ void Lowering::UnmarkTree(GenTree* node)

#endif // TARGET_ARM64

//------------------------------------------------------------------------
// IsContainableLclAddr: Can a given local address be contained?
//
// Most local addresses can be contained, however, there are two edge cases
// where this is not true:
// 1. When the resulting memory access will go beyond the local's location.
// 2. When the resulting access may go past a UINT16_MAX.
// Both of these requirements are imposed by the emitter.
//
// Arguments:
// lclAddr - The local address node
// accessSize - The access size (of an indirection)
//
// Return Value:
// Whether an indirection of "accessSize" may contain "lclAddr".
//
bool Lowering::IsContainableLclAddr(GenTreeLclFld* lclAddr, unsigned accessSize) const
{
if (CheckedOps::AddOverflows<int32_t>(lclAddr->GetLclOffs(), accessSize, CheckedOps::Unsigned) ||
!comp->IsValidLclAddr(lclAddr->GetLclNum(), lclAddr->GetLclOffs() + accessSize - 1))
{
// We depend on containment for correctness of liveness updates in codegen. Therefore, all
// locals that may "return false" here MUST be address-exposed. Local morph ensures this.
assert(comp->lvaGetDesc(lclAddr)->IsAddressExposed());
return false;
}

return true;
}

//------------------------------------------------------------------------
// TransformUnusedIndirection: change the opcode and the type of the unused indirection.
//
Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/jit/lower.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class Lowering final : public Phase
void ContainCheckIntrinsic(GenTreeOp* node);
#endif // TARGET_XARCH
#ifdef FEATURE_HW_INTRINSICS
void ContainCheckHWIntrinsicAddr(GenTreeHWIntrinsic* node, GenTree* addr);
void ContainCheckHWIntrinsicAddr(GenTreeHWIntrinsic* node, GenTree* addr, unsigned size);
void ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node);
#ifdef TARGET_XARCH
void TryFoldCnsVecForEmbeddedBroadcast(GenTreeHWIntrinsic* parentNode, GenTreeVecCon* childNode);
Expand Down Expand Up @@ -492,6 +492,8 @@ class Lowering final : public Phase
return false;
}

bool IsContainableLclAddr(GenTreeLclFld* lclAddr, unsigned accessSize) const;

#ifdef TARGET_ARM64
bool IsContainableUnaryOrBinaryOp(GenTree* parentNode, GenTree* childNode) const;
#endif // TARGET_ARM64
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/lowerarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
assert(blkNode->OperIs(GT_STORE_BLK) && (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll));
assert(size < INT32_MAX);

if (addr->OperIs(GT_LCL_ADDR))
if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), size))
{
addr->SetContained();
return;
Expand Down Expand Up @@ -2060,7 +2060,7 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
MakeSrcContained(indirNode, addr);
}
}
else if (addr->OperIs(GT_LCL_ADDR))
else if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), indirNode->Size()))
{
// These nodes go into an addr mode:
// - GT_LCL_ADDR is a stack addr mode.
Expand Down
7 changes: 4 additions & 3 deletions src/coreclr/jit/lowerloongarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
assert(blkNode->OperIs(GT_STORE_BLK) && (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll));
assert(size < INT32_MAX);

if (addr->OperIs(GT_LCL_ADDR))
if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), size))
{
addr->SetContained();
return;
Expand Down Expand Up @@ -485,7 +485,8 @@ void Lowering::LowerPutArgStkOrSplit(GenTreePutArgStk* putArgNode)
}

// Codegen supports containment of local addresses under BLKs.
if (src->OperIs(GT_BLK) && src->AsBlk()->Addr()->IsLclVarAddr())
if (src->OperIs(GT_BLK) && src->AsBlk()->Addr()->IsLclVarAddr() &&
IsContainableLclAddr(src->AsBlk()->Addr()->AsLclFld(), src->AsBlk()->Size()))
{
// TODO-LOONGARCH64-CQ: support containment of LCL_ADDR with non-zero offset too.
MakeSrcContained(src, src->AsBlk()->Addr());
Expand Down Expand Up @@ -704,7 +705,7 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
{
MakeSrcContained(indirNode, addr);
}
else if (addr->OperIs(GT_LCL_ADDR))
else if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), indirNode->Size()))
{
// These nodes go into an addr mode:
// - GT_LCL_ADDR is a stack addr mode.
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/lowerriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ void Lowering::ContainBlockStoreAddress(GenTreeBlk* blkNode, unsigned size, GenT
assert(blkNode->OperIs(GT_STORE_BLK) && (blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindUnroll));
assert(size < INT32_MAX);

if (addr->OperIs(GT_LCL_ADDR))
if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), size))
{
addr->SetContained();
return;
Expand Down Expand Up @@ -615,7 +615,7 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
{
MakeSrcContained(indirNode, addr);
}
else if (addr->OperIs(GT_LCL_ADDR))
else if (addr->OperIs(GT_LCL_ADDR) && IsContainableLclAddr(addr->AsLclFld(), indirNode->Size()))
{
// These nodes go into an addr mode:
// - GT_LCL_ADDR is a stack addr mode.
Expand Down
Loading

0 comments on commit 0df43f2

Please sign in to comment.