forked from OSchip/llvm-project
[MC] Fix PR45805: infinite recursion in assembler
Give up folding an expression if the fragment of one of the operands would require laying out a fragment already being laid out. This prevents hitting an infinite recursion when a fill size expression refers to a later fragment since computing the offset of that fragment would require laying out the fill fragment and thus computing its size expression. Reviewed By: echristo Differential Revision: https://reviews.llvm.org/D79570
This commit is contained in:
parent
17326ebbd6
commit
6c67ee0f58
|
@ -49,6 +49,10 @@ public:
|
|||
/// Get the assembler object this is a layout for.
|
||||
MCAssembler &getAssembler() const { return Assembler; }
|
||||
|
||||
/// \returns whether the offset of fragment \p F can be obtained via
|
||||
/// getFragmentOffset.
|
||||
bool canGetFragmentOffset(const MCFragment *F) const;
|
||||
|
||||
/// Invalidate the fragments starting with F because it has been
|
||||
/// resized. The fragment's size should have already been updated, but
|
||||
/// its bundle padding will be recomputed.
|
||||
|
|
|
@ -65,6 +65,9 @@ private:
|
|||
|
||||
FragmentType Kind;
|
||||
|
||||
/// Whether fragment is being laid out.
|
||||
bool IsBeingLaidOut;
|
||||
|
||||
protected:
|
||||
bool HasInstructions;
|
||||
|
||||
|
|
|
@ -397,6 +397,9 @@ void MCAsmLayout::layoutFragment(MCFragment *F) {
|
|||
assert((!Prev || isFragmentValid(Prev)) &&
|
||||
"Attempt to compute fragment before its predecessor!");
|
||||
|
||||
assert(!F->IsBeingLaidOut && "Already being laid out!");
|
||||
F->IsBeingLaidOut = true;
|
||||
|
||||
++stats::FragmentLayouts;
|
||||
|
||||
// Compute fragment offset and size.
|
||||
|
@ -404,6 +407,7 @@ void MCAsmLayout::layoutFragment(MCFragment *F) {
|
|||
F->Offset = Prev->Offset + getAssembler().computeFragmentSize(*this, *Prev);
|
||||
else
|
||||
F->Offset = 0;
|
||||
F->IsBeingLaidOut = false;
|
||||
LastValidFragment[F->getParent()] = F;
|
||||
|
||||
// If bundling is enabled and this fragment has instructions in it, it has to
|
||||
|
|
|
@ -547,8 +547,10 @@ static void AttemptToFoldSymbolOffsetDifference(
|
|||
if (!Asm->getWriter().isSymbolRefDifferenceFullyResolved(*Asm, A, B, InSet))
|
||||
return;
|
||||
|
||||
if (SA.getFragment() == SB.getFragment() && !SA.isVariable() &&
|
||||
!SA.isUnset() && !SB.isVariable() && !SB.isUnset()) {
|
||||
MCFragment *FA = SA.getFragment();
|
||||
MCFragment *FB = SB.getFragment();
|
||||
if (FA == FB && !SA.isVariable() && !SA.isUnset() && !SB.isVariable() &&
|
||||
!SB.isUnset()) {
|
||||
Addend += (SA.getOffset() - SB.getOffset());
|
||||
|
||||
// Pointers to Thumb symbols need to have their low-bit set to allow
|
||||
|
@ -570,12 +572,17 @@ static void AttemptToFoldSymbolOffsetDifference(
|
|||
if (!Layout)
|
||||
return;
|
||||
|
||||
const MCSection &SecA = *SA.getFragment()->getParent();
|
||||
const MCSection &SecB = *SB.getFragment()->getParent();
|
||||
const MCSection &SecA = *FA->getParent();
|
||||
const MCSection &SecB = *FB->getParent();
|
||||
|
||||
if ((&SecA != &SecB) && !Addrs)
|
||||
return;
|
||||
|
||||
// One of the symbol involved is part of a fragment being laid out. Quit now
|
||||
// to avoid a self loop.
|
||||
if (!Layout->canGetFragmentOffset(FA) || !Layout->canGetFragmentOffset(FB))
|
||||
return;
|
||||
|
||||
// Eagerly evaluate.
|
||||
Addend += Layout->getSymbolOffset(A->getSymbol()) -
|
||||
Layout->getSymbolOffset(B->getSymbol());
|
||||
|
|
|
@ -48,6 +48,25 @@ bool MCAsmLayout::isFragmentValid(const MCFragment *F) const {
|
|||
return F->getLayoutOrder() <= LastValid->getLayoutOrder();
|
||||
}
|
||||
|
||||
bool MCAsmLayout::canGetFragmentOffset(const MCFragment *F) const {
|
||||
MCSection *Sec = F->getParent();
|
||||
MCSection::iterator I;
|
||||
if (MCFragment *LastValid = LastValidFragment[Sec]) {
|
||||
// Fragment already valid, offset is available.
|
||||
if (F->getLayoutOrder() <= LastValid->getLayoutOrder())
|
||||
return true;
|
||||
I = ++MCSection::iterator(LastValid);
|
||||
} else
|
||||
I = Sec->begin();
|
||||
|
||||
// A fragment ordered before F is currently being laid out.
|
||||
const MCFragment *FirstInvalidFragment = &*I;
|
||||
if (FirstInvalidFragment->IsBeingLaidOut)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MCAsmLayout::invalidateFragmentsFrom(MCFragment *F) {
|
||||
// If this fragment wasn't already valid, we don't need to do anything.
|
||||
if (!isFragmentValid(F))
|
||||
|
@ -235,7 +254,7 @@ void ilist_alloc_traits<MCFragment>::deleteNode(MCFragment *V) { V->destroy(); }
|
|||
MCFragment::MCFragment(FragmentType Kind, bool HasInstructions,
|
||||
MCSection *Parent)
|
||||
: Parent(Parent), Atom(nullptr), Offset(~UINT64_C(0)), LayoutOrder(0),
|
||||
Kind(Kind), HasInstructions(HasInstructions) {
|
||||
Kind(Kind), IsBeingLaidOut(false), HasInstructions(HasInstructions) {
|
||||
if (Parent && !isa<MCDummyFragment>(*this))
|
||||
Parent->getFragmentList().push_back(this);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# RUN: not llvm-mc --filetype=obj %s -o /dev/null 2>&1 | FileCheck %s
|
||||
|
||||
fct_end:
|
||||
|
||||
# CHECK: layout-interdependency.s:[[#@LINE+1]]:7: error: expected assembly-time absolute expression
|
||||
.fill (data_start - fct_end), 1, 42
|
||||
# CHECK: layout-interdependency.s:[[#@LINE+1]]:7: error: expected assembly-time absolute expression
|
||||
.fill (fct_end - data_start), 1, 42
|
||||
|
||||
data_start:
|
Loading…
Reference in New Issue