[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:
Thomas Preud'homme 2020-05-05 19:11:04 +01:00
parent 17326ebbd6
commit 6c67ee0f58
6 changed files with 52 additions and 5 deletions

View File

@ -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.

View File

@ -65,6 +65,9 @@ private:
FragmentType Kind;
/// Whether fragment is being laid out.
bool IsBeingLaidOut;
protected:
bool HasInstructions;

View File

@ -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

View File

@ -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());

View File

@ -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);
}

View File

@ -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: