diff --git a/llvm/include/llvm/MC/MCAsmLayout.h b/llvm/include/llvm/MC/MCAsmLayout.h --- a/llvm/include/llvm/MC/MCAsmLayout.h +++ b/llvm/include/llvm/MC/MCAsmLayout.h @@ -49,6 +49,10 @@ /// 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. diff --git a/llvm/include/llvm/MC/MCFragment.h b/llvm/include/llvm/MC/MCFragment.h --- a/llvm/include/llvm/MC/MCFragment.h +++ b/llvm/include/llvm/MC/MCFragment.h @@ -65,6 +65,9 @@ FragmentType Kind; + /// Whether fragment is being laid out. + bool IsBeingLaidOut; + protected: bool HasInstructions; diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp --- a/llvm/lib/MC/MCAssembler.cpp +++ b/llvm/lib/MC/MCAssembler.cpp @@ -397,6 +397,9 @@ 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 @@ 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 diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -547,8 +547,10 @@ 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 @@ 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()); diff --git a/llvm/lib/MC/MCFragment.cpp b/llvm/lib/MC/MCFragment.cpp --- a/llvm/lib/MC/MCFragment.cpp +++ b/llvm/lib/MC/MCFragment.cpp @@ -48,6 +48,25 @@ 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 @@ 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(*this)) Parent->getFragmentList().push_back(this); } diff --git a/llvm/test/MC/AsmParser/layout-interdependency.s b/llvm/test/MC/AsmParser/layout-interdependency.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/AsmParser/layout-interdependency.s @@ -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: