diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h --- a/llvm/include/llvm/MC/MCExpr.h +++ b/llvm/include/llvm/MC/MCExpr.h @@ -56,7 +56,8 @@ bool evaluateAsRelocatableImpl(MCValue &Res, const MCAssembler *Asm, const MCAsmLayout *Layout, const MCFixup *Fixup, - const SectionAddrMap *Addrs, bool InSet) const; + const SectionAddrMap *Addrs, bool InSet, + bool IsCond) const; public: MCExpr(const MCExpr &) = delete; @@ -93,6 +94,7 @@ bool evaluateAsAbsolute(int64_t &Res, const MCAssembler &Asm) const; bool evaluateAsAbsolute(int64_t &Res, const MCAssembler *Asm) const; bool evaluateAsAbsolute(int64_t &Res, const MCAsmLayout &Layout) const; + bool evaluateIfCondAsAbsolute(int64_t &Res, const MCAssembler *Asm) const; bool evaluateKnownAbsolute(int64_t &Res, const MCAsmLayout &Layout) const; 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 @@ -481,6 +481,28 @@ return evaluateAsAbsolute(Res, Asm, nullptr, nullptr, false); } +bool MCExpr::evaluateIfCondAsAbsolute(int64_t &Res, + const MCAssembler *Asm) const { + MCValue Value; + + // Fast path constants. + if (const MCConstantExpr *CE = dyn_cast(this)) { + Res = CE->getValue(); + return true; + } + + // Setting IsCond causes us to absolutize differences of symbols in consective + // MCDataFragments, e.g. dot symbol and foo in the following example: + // foo:instr; .if . - foo; instr; .endif + bool IsRelocatable = evaluateAsRelocatableImpl(Value, Asm, nullptr, nullptr, + nullptr, /* InSet */ false, /* IsCond */ true); + + // Record the current value. + Res = Value.getConstant(); + + return IsRelocatable && Value.isAbsolute(); +} + bool MCExpr::evaluateKnownAbsolute(int64_t &Res, const MCAsmLayout &Layout) const { return evaluateAsAbsolute(Res, &Layout.getAssembler(), &Layout, nullptr, @@ -498,8 +520,8 @@ return true; } - bool IsRelocatable = - evaluateAsRelocatableImpl(Value, Asm, Layout, nullptr, Addrs, InSet); + bool IsRelocatable = evaluateAsRelocatableImpl(Value, Asm, Layout, nullptr, + Addrs, InSet, /* IsCond */ false); // Record the current value. Res = Value.getConstant(); @@ -511,7 +533,7 @@ static void AttemptToFoldSymbolOffsetDifference( const MCAssembler *Asm, const MCAsmLayout *Layout, const SectionAddrMap *Addrs, bool InSet, const MCSymbolRefExpr *&A, - const MCSymbolRefExpr *&B, int64_t &Addend) { + const MCSymbolRefExpr *&B, int64_t &Addend, bool IsCond) { if (!A || !B) return; @@ -524,10 +546,7 @@ if (!Asm->getWriter().isSymbolRefDifferenceFullyResolved(*Asm, A, B, InSet)) return; - if (SA.getFragment() == SB.getFragment() && !SA.isVariable() && - !SA.isUnset() && !SB.isVariable() && !SB.isUnset()) { - Addend += (SA.getOffset() - SB.getOffset()); - + auto FinalizeFolding = [&]() { // Pointers to Thumb symbols need to have their low-bit set to allow // for interworking. if (Asm->isThumbFunc(&SA)) @@ -541,37 +560,53 @@ // Clear the symbol expr pointers to indicate we have folded these // operands. A = B = nullptr; - return; - } + }; - if (!Layout) + const MCFragment *FragA = SA.getFragment(); + const MCFragment *FragB = SB.getFragment(); + // If both symbols are in the same fragment, return the difference of their + // offsets + if (FragA == FragB && !SA.isVariable() && !SA.isUnset() && !SB.isVariable() && + !SB.isUnset()) { + + Addend += (SA.getOffset() - SB.getOffset()); + + FinalizeFolding(); return; + } - const MCSection &SecA = *SA.getFragment()->getParent(); - const MCSection &SecB = *SB.getFragment()->getParent(); + const MCSection &SecA = *FragA->getParent(); + const MCSection &SecB = *FragB->getParent(); if ((&SecA != &SecB) && !Addrs) return; + if (!Layout) { + // When there is no layout our ability to resolve differences between symbols is + // limited. In specific cases where the symbols are both defined in consecutive + // MCDataFragments the difference can be calculated. This is important for an + // idiom like foo:instr; .if . - foo; instr; .endif + // We cannot handle any kind of case where the difference may change due to + // layout. + // FIXME: can we resolve .if conditions while finalizing layout? + if (IsCond && SecB.getFragmentList().getNextNode(*FragB) == FragA && + isa(FragA) && isa(FragB)) { + Addend += (SA.getOffset() + + (cast(FragB))->getContents().size() - + SB.getOffset()); + + FinalizeFolding(); + } + return; + } + // Eagerly evaluate. Addend += Layout->getSymbolOffset(A->getSymbol()) - Layout->getSymbolOffset(B->getSymbol()); if (Addrs && (&SecA != &SecB)) Addend += (Addrs->lookup(&SecA) - Addrs->lookup(&SecB)); - // Pointers to Thumb symbols need to have their low-bit set to allow - // for interworking. - if (Asm->isThumbFunc(&SA)) - Addend |= 1; - - // If symbol is labeled as micromips, we set low-bit to ensure - // correct offset in .gcc_except_table - if (Asm->getBackend().isMicroMips(&SA)) - Addend |= 1; - - // Clear the symbol expr pointers to indicate we have folded these - // operands. - A = B = nullptr; + FinalizeFolding(); } static bool canFold(const MCAssembler *Asm, const MCSymbolRefExpr *A, @@ -615,7 +650,7 @@ EvaluateSymbolicAdd(const MCAssembler *Asm, const MCAsmLayout *Layout, const SectionAddrMap *Addrs, bool InSet, const MCValue &LHS, const MCSymbolRefExpr *RHS_A, const MCSymbolRefExpr *RHS_B, - int64_t RHS_Cst, MCValue &Res) { + int64_t RHS_Cst, MCValue &Res, bool IsCond) { // FIXME: This routine (and other evaluation parts) are *incredibly* sloppy // about dealing with modifiers. This will ultimately bite us, one day. const MCSymbolRefExpr *LHS_A = LHS.getSymA(); @@ -644,13 +679,13 @@ // Since we are attempting to be as aggressive as possible about folding, we // attempt to evaluate each possible alternative. AttemptToFoldSymbolOffsetDifference(Asm, Layout, Addrs, InSet, LHS_A, LHS_B, - Result_Cst); + Result_Cst, IsCond); AttemptToFoldSymbolOffsetDifference(Asm, Layout, Addrs, InSet, LHS_A, RHS_B, - Result_Cst); + Result_Cst, IsCond); AttemptToFoldSymbolOffsetDifference(Asm, Layout, Addrs, InSet, RHS_A, LHS_B, - Result_Cst); + Result_Cst, IsCond); AttemptToFoldSymbolOffsetDifference(Asm, Layout, Addrs, InSet, RHS_A, RHS_B, - Result_Cst); + Result_Cst, IsCond); } // We can't represent the addition or subtraction of two symbols. @@ -671,13 +706,13 @@ const MCFixup *Fixup) const { MCAssembler *Assembler = Layout ? &Layout->getAssembler() : nullptr; return evaluateAsRelocatableImpl(Res, Assembler, Layout, Fixup, nullptr, - false); + /* InSet */ false, /* IsCond */ false); } bool MCExpr::evaluateAsValue(MCValue &Res, const MCAsmLayout &Layout) const { MCAssembler *Assembler = &Layout.getAssembler(); return evaluateAsRelocatableImpl(Res, Assembler, &Layout, nullptr, nullptr, - true); + /* InSet */ true, /* IsCond */ false); } static bool canExpand(const MCSymbol &Sym, bool InSet) { @@ -696,8 +731,8 @@ bool MCExpr::evaluateAsRelocatableImpl(MCValue &Res, const MCAssembler *Asm, const MCAsmLayout *Layout, const MCFixup *Fixup, - const SectionAddrMap *Addrs, - bool InSet) const { + const SectionAddrMap *Addrs, bool InSet, + bool IsCond) const { ++stats::MCExprEvaluate; switch (getKind()) { @@ -718,7 +753,7 @@ canExpand(Sym, InSet)) { bool IsMachO = SRE->hasSubsectionsViaSymbols(); if (Sym.getVariableValue()->evaluateAsRelocatableImpl( - Res, Asm, Layout, Fixup, Addrs, InSet || IsMachO)) { + Res, Asm, Layout, Fixup, Addrs, InSet || IsMachO, IsCond)) { if (!IsMachO) return true; @@ -748,7 +783,7 @@ MCValue Value; if (!AUE->getSubExpr()->evaluateAsRelocatableImpl(Value, Asm, Layout, Fixup, - Addrs, InSet)) + Addrs, InSet, IsCond)) return false; switch (AUE->getOpcode()) { @@ -784,9 +819,9 @@ MCValue LHSValue, RHSValue; if (!ABE->getLHS()->evaluateAsRelocatableImpl(LHSValue, Asm, Layout, Fixup, - Addrs, InSet) || + Addrs, InSet, IsCond) || !ABE->getRHS()->evaluateAsRelocatableImpl(RHSValue, Asm, Layout, Fixup, - Addrs, InSet)) { + Addrs, InSet, IsCond)) { // Check if both are Target Expressions, see if we can compare them. if (const MCTargetExpr *L = dyn_cast(ABE->getLHS())) if (const MCTargetExpr *R = cast(ABE->getRHS())) { @@ -812,14 +847,14 @@ case MCBinaryExpr::Sub: // Negate RHS and add. // The cast avoids undefined behavior if the constant is INT64_MIN. - return EvaluateSymbolicAdd(Asm, Layout, Addrs, InSet, LHSValue, - RHSValue.getSymB(), RHSValue.getSymA(), - -(uint64_t)RHSValue.getConstant(), Res); + return EvaluateSymbolicAdd( + Asm, Layout, Addrs, InSet, LHSValue, RHSValue.getSymB(), + RHSValue.getSymA(), -(uint64_t)RHSValue.getConstant(), Res, IsCond); case MCBinaryExpr::Add: return EvaluateSymbolicAdd(Asm, Layout, Addrs, InSet, LHSValue, RHSValue.getSymA(), RHSValue.getSymB(), - RHSValue.getConstant(), Res); + RHSValue.getConstant(), Res, IsCond); } } diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -254,6 +254,7 @@ bool parseParenExprOfDepth(unsigned ParenDepth, const MCExpr *&Res, SMLoc &EndLoc) override; bool parseAbsoluteExpression(int64_t &Res) override; + bool parseAbsoluteIfCond(int64_t &Res); /// Parse a floating point expression using the float \p Semantics /// and set \p Res to the value. @@ -1485,6 +1486,19 @@ return false; } +bool AsmParser::parseAbsoluteIfCond(int64_t &Res) { + const MCExpr *Expr; + + SMLoc StartLoc = Lexer.getLoc(); + if (parseExpression(Expr)) + return true; + + if (!Expr->evaluateIfCondAsAbsolute(Res, getStreamer().getAssemblerPtr())) + return Error(StartLoc, "expected absolute expression"); + + return false; +} + static unsigned getDarwinBinOpPrecedence(AsmToken::TokenKind K, MCBinaryExpr::Opcode &Kind, bool ShouldUseLogicalShr) { @@ -5040,7 +5054,7 @@ eatToEndOfStatement(); } else { int64_t ExprValue; - if (parseAbsoluteExpression(ExprValue) || + if (parseAbsoluteIfCond(ExprValue) || parseToken(AsmToken::EndOfStatement, "unexpected token in '.if' directive")) return true; diff --git a/llvm/test/MC/ARM/directive_if_offset.s b/llvm/test/MC/ARM/directive_if_offset.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/directive_if_offset.s @@ -0,0 +1,12 @@ +@ RUN: llvm-mc -triple armv7a-linux-gnueabihf %s -filetype=obj -o /dev/null 2>&1 | FileCheck --allow-empty %s +@ RUN: llvm-mc -triple armv7a-linux-gnueabihf %s -filetype=obj -o %t | llvm-objdump -d %t | FileCheck --check-prefix=CHECK-ASM %s + +nop +.arch_extension sec +9997: +.if . - 9997b == 0 ; +// CHECK-NOT: error: expected absolute expression +orr r1, r1, #1 ; +.else ; orr r1, r1, #2; +.endif; +// CHECK-ASM: orr r1, r1, #1 diff --git a/llvm/test/MC/ARM/directive_if_offset_error.s b/llvm/test/MC/ARM/directive_if_offset_error.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/directive_if_offset_error.s @@ -0,0 +1,23 @@ +@ RUN: not llvm-mc -filetype=obj -triple arm-linux-gnueabihf %s -o /dev/null 2>&1 | FileCheck %s + +9997: nop ; + .align 4 + nop +.if . - 9997b == 4 ; +// CHECK: error: expected absolute expression +.endif + +9997: nop ; + .space 4 + nop +.if . - 9997b == 4 ; +// CHECK: error: expected absolute expression +.endif + +9997: + ldr r0,=0x12345678 ; + .ltorg ; + nop ; +.if . - 9997b == 4 ; +// CHECK: error: expected absolute expression +.endif diff --git a/llvm/test/MC/ARM/thumb2_directive_if_offset_error.s b/llvm/test/MC/ARM/thumb2_directive_if_offset_error.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/thumb2_directive_if_offset_error.s @@ -0,0 +1,8 @@ +@ RUN: not llvm-mc -filetype=obj -triple thumbv7a-linux-gnueabihf %s -o /dev/null 2>&1 | FileCheck %s + +9997: nop ; + b external + nop +.if . - 9997b == 4 ; +// CHECK: error: expected absolute expression +.endif