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 @@ -490,16 +490,37 @@ bool MCExpr::evaluateAsAbsolute(int64_t &Res, const MCAssembler *Asm, const MCAsmLayout *Layout, const SectionAddrMap *Addrs, bool InSet) const { + // Fast path constants. + if (const MCConstantExpr *CE = dyn_cast(this)) { + Res = CE->getValue(); + return true; + } + MCValue Value; + bool IsRelocatable = evaluateAsRelocatableImpl( + Value, Asm, Layout, nullptr, Addrs, InSet, /* IsCond */ false); + // Record the current value. + Res = Value.getConstant(); + + return IsRelocatable && Value.isAbsolute(); +} + +bool MCExpr::evaluateIfCondAsAbsolute(int64_t &Res, + const MCAssembler *Asm) const { // Fast path constants. if (const MCConstantExpr *CE = dyn_cast(this)) { Res = CE->getValue(); return true; } + MCValue Value; + // 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, Layout, nullptr, Addrs, InSet); + evaluateAsRelocatableImpl(Value, Asm, nullptr, nullptr, nullptr, + /* InSet */ false, /* IsCond */ true); // Record the current value. Res = Value.getConstant(); @@ -511,7 +532,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 +545,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 +559,56 @@ // 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) { + if (IsCond) { + MCFragment::FragmentType FragATy = FragA->getKind(); + MCFragment::FragmentType FragBTy = FragB->getKind(); + // 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. + if (FragATy == MCFragment::FT_Data && + FragBTy == MCFragment::FT_Data && + FragA->getPrevNode() == 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 +652,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 +681,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 +708,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 +733,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 +755,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 +785,7 @@ MCValue Value; if (!AUE->getSubExpr()->evaluateAsRelocatableImpl(Value, Asm, Layout, Fixup, - Addrs, InSet)) + Addrs, InSet, IsCond)) return false; switch (AUE->getOpcode()) { @@ -784,9 +821,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 +849,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,13 @@ +@ 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:nop +.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