[llvm] [X86] Allow symbol arithmetic in Intel syntax operands (PR #225985)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 23 18:00:20 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: Zane Hambly (Zaneham)

<details>
<summary>Changes</summary>

Intel syntax only kept one symbol per operand and pushed it through the expression calculator as 0, so `offset foo - bar` was rejected and a negated or scaled symbol lost its sign or scale. `[ebx - foo]`, `[-foo]` and `[foo * 2]` all assembled as `+foo`. This also meant llvm-mc couldn't reassemble llc's own i386 PIC output in Intel syntax (`add eax, offset _GLOBAL_OFFSET_TABLE_+(.Ltmp0-.L0$pb)`).

The calculator now carries the symbolic part of each operand, so the operand gets the same MCExpr the AT&T parser builds and the object writer accepts or rejects it the same way. MS inline asm still refuses a second symbol since it rewrites around one.

Fixes #<!-- -->161550

---

Patch is 20.10 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/225985.diff


3 Files Affected:

- (modified) llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp (+177-88) 
- (modified) llvm/test/MC/X86/intel-syntax-error.s (-2) 
- (modified) llvm/test/MC/X86/intel-syntax.s (+16-1) 


``````````diff
diff --git a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
index 8383b1fee7c34..c6ea63cf96482 100644
--- a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
+++ b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
@@ -195,7 +195,14 @@ class X86AsmParser : public MCTargetAsmParser {
   };
 
   class InfixCalculator {
-    typedef std::pair< InfixCalculatorTok, int64_t > ICToken;
+  public:
+    struct ICToken {
+      InfixCalculatorTok Kind;
+      int64_t Val = 0;
+      const MCExpr *Sym = nullptr;
+    };
+
+  private:
     SmallVector<InfixCalculatorTok, 4> InfixOperatorStack;
     SmallVector<ICToken, 4> PostfixStack;
 
@@ -207,14 +214,15 @@ class X86AsmParser : public MCTargetAsmParser {
     int64_t popOperand() {
       assert (!PostfixStack.empty() && "Poped an empty stack!");
       ICToken Op = PostfixStack.pop_back_val();
-      if (!(Op.first == IC_IMM || Op.first == IC_REGISTER))
+      if (!(Op.Kind == IC_IMM || Op.Kind == IC_REGISTER))
         return -1; // The invalid Scale value will be caught later by checkScale
-      return Op.second;
+      return Op.Val;
     }
-    void pushOperand(InfixCalculatorTok Op, int64_t Val = 0) {
+    void pushOperand(InfixCalculatorTok Op, int64_t Val = 0,
+                     const MCExpr *Sym = nullptr) {
       assert ((Op == IC_IMM || Op == IC_REGISTER) &&
               "Unexpected operand!");
-      PostfixStack.push_back(std::make_pair(Op, Val));
+      PostfixStack.push_back({Op, Val, Sym});
     }
 
     void popOperator() { InfixOperatorStack.pop_back(); }
@@ -261,42 +269,114 @@ class X86AsmParser : public MCTargetAsmParser {
           InfixOperatorStack.pop_back();
         } else {
           InfixOperatorStack.pop_back();
-          PostfixStack.push_back(std::make_pair(StackOp, 0));
+          PostfixStack.push_back({StackOp});
         }
       }
       // Push the new operator.
       InfixOperatorStack.push_back(Op);
     }
 
-    int64_t execute() {
+    static const MCExpr *toExpr(const ICToken &T, MCContext &Ctx) {
+      const MCExpr *C = MCConstantExpr::create(T.Val, Ctx);
+      if (!T.Sym)
+        return C;
+      if (!T.Val)
+        return T.Sym;
+      return MCBinaryExpr::createAdd(T.Sym, C, Ctx);
+    }
+
+    static MCBinaryExpr::Opcode toBinaryOpcode(InfixCalculatorTok Op) {
+      switch (Op) {
+      default:
+        llvm_unreachable("Unexpected operator!");
+      case IC_OR:
+        return MCBinaryExpr::Or;
+      case IC_XOR:
+        return MCBinaryExpr::Xor;
+      case IC_AND:
+        return MCBinaryExpr::And;
+      case IC_LSHIFT:
+        return MCBinaryExpr::Shl;
+      case IC_RSHIFT:
+        return MCBinaryExpr::AShr;
+      case IC_MULTIPLY:
+        return MCBinaryExpr::Mul;
+      case IC_DIVIDE:
+        return MCBinaryExpr::Div;
+      case IC_MOD:
+        return MCBinaryExpr::Mod;
+      case IC_EQ:
+        return MCBinaryExpr::EQ;
+      case IC_NE:
+        return MCBinaryExpr::NE;
+      case IC_LT:
+        return MCBinaryExpr::LT;
+      case IC_LE:
+        return MCBinaryExpr::LTE;
+      case IC_GT:
+        return MCBinaryExpr::GT;
+      case IC_GE:
+        return MCBinaryExpr::GTE;
+      }
+    }
+
+    static ICToken applySymbolic(InfixCalculatorTok Op, const ICToken &Op1,
+                                 const ICToken &Op2, MCContext &Ctx) {
+      if (Op == IC_PLUS || Op == IC_MINUS) {
+        const MCExpr *Sym = Op1.Sym;
+        if (Op2.Sym && Op == IC_PLUS)
+          Sym = Sym ? MCBinaryExpr::createAdd(Sym, Op2.Sym, Ctx) : Op2.Sym;
+        else if (Op2.Sym && Sym)
+          Sym = MCBinaryExpr::createSub(Sym, Op2.Sym, Ctx);
+        else if (Op2.Sym)
+          Sym = MCUnaryExpr::createMinus(Op2.Sym, Ctx);
+        int64_t Val = Op == IC_PLUS ? Op1.Val + Op2.Val : Op1.Val - Op2.Val;
+        return {IC_IMM, Val, Sym};
+      }
+      return {IC_IMM, 0,
+              MCBinaryExpr::create(toBinaryOpcode(Op), toExpr(Op1, Ctx),
+                                   toExpr(Op2, Ctx), Ctx)};
+    }
+
+    ICToken execute(MCContext &Ctx) {
       // Push any remaining operators onto the postfix stack.
       while (!InfixOperatorStack.empty()) {
         InfixCalculatorTok StackOp = InfixOperatorStack.pop_back_val();
         if (StackOp != IC_LPAREN && StackOp != IC_RPAREN)
-          PostfixStack.push_back(std::make_pair(StackOp, 0));
+          PostfixStack.push_back({StackOp});
       }
 
       if (PostfixStack.empty())
-        return 0;
+        return {IC_IMM};
 
       SmallVector<ICToken, 16> OperandStack;
       for (const ICToken &Op : PostfixStack) {
-        if (Op.first == IC_IMM || Op.first == IC_REGISTER) {
+        if (Op.Kind == IC_IMM || Op.Kind == IC_REGISTER) {
           OperandStack.push_back(Op);
-        } else if (isUnaryOperator(Op.first)) {
+        } else if (isUnaryOperator(Op.Kind)) {
           assert (OperandStack.size() > 0 && "Too few operands.");
           ICToken Operand = OperandStack.pop_back_val();
-          assert (Operand.first == IC_IMM &&
-                  "Unary operation with a register!");
-          switch (Op.first) {
+          assert(Operand.Kind == IC_IMM && "Unary operation with a register!");
+          if (Operand.Sym && Op.Kind == IC_NEG) {
+            OperandStack.push_back(
+                {IC_IMM, -Operand.Val,
+                 MCUnaryExpr::createMinus(Operand.Sym, Ctx)});
+            continue;
+          }
+          if (Operand.Sym) {
+            OperandStack.push_back(
+                {IC_IMM, 0, MCUnaryExpr::createNot(toExpr(Operand, Ctx), Ctx)});
+            continue;
+          }
+          switch (Op.Kind) {
           default:
             report_fatal_error("Unexpected operator!");
             break;
           case IC_NEG:
-            OperandStack.push_back(std::make_pair(IC_IMM, -Operand.second));
+            OperandStack.push_back({IC_IMM, -Operand.Val});
             break;
           case IC_NOT:
-            OperandStack.push_back(std::make_pair(IC_IMM, ~Operand.second));
+            OperandStack.push_back({IC_IMM, ~Operand.Val});
             break;
           }
         } else {
@@ -304,110 +384,114 @@ class X86AsmParser : public MCTargetAsmParser {
           int64_t Val;
           ICToken Op2 = OperandStack.pop_back_val();
           ICToken Op1 = OperandStack.pop_back_val();
-          switch (Op.first) {
+          if (Op1.Sym || Op2.Sym) {
+            OperandStack.push_back(applySymbolic(Op.Kind, Op1, Op2, Ctx));
+            continue;
+          }
+          switch (Op.Kind) {
           default:
             report_fatal_error("Unexpected operator!");
             break;
           case IC_PLUS:
-            Val = Op1.second + Op2.second;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            Val = Op1.Val + Op2.Val;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_MINUS:
-            Val = Op1.second - Op2.second;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            Val = Op1.Val - Op2.Val;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_MULTIPLY:
-            assert (Op1.first == IC_IMM && Op2.first == IC_IMM &&
-                    "Multiply operation with an immediate and a register!");
-            Val = Op1.second * Op2.second;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
+                   "Multiply operation with an immediate and a register!");
+            Val = Op1.Val * Op2.Val;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_DIVIDE:
-            assert (Op1.first == IC_IMM && Op2.first == IC_IMM &&
-                    "Divide operation with an immediate and a register!");
-            assert (Op2.second != 0 && "Division by zero!");
-            Val = Op1.second / Op2.second;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
+                   "Divide operation with an immediate and a register!");
+            assert(Op2.Val != 0 && "Division by zero!");
+            Val = Op1.Val / Op2.Val;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_MOD:
-            assert (Op1.first == IC_IMM && Op2.first == IC_IMM &&
-                    "Modulo operation with an immediate and a register!");
-            Val = Op1.second % Op2.second;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
+                   "Modulo operation with an immediate and a register!");
+            Val = Op1.Val % Op2.Val;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_OR:
-            assert (Op1.first == IC_IMM && Op2.first == IC_IMM &&
-                    "Or operation with an immediate and a register!");
-            Val = Op1.second | Op2.second;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
+                   "Or operation with an immediate and a register!");
+            Val = Op1.Val | Op2.Val;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_XOR:
-            assert(Op1.first == IC_IMM && Op2.first == IC_IMM &&
-              "Xor operation with an immediate and a register!");
-            Val = Op1.second ^ Op2.second;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
+                   "Xor operation with an immediate and a register!");
+            Val = Op1.Val ^ Op2.Val;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_AND:
-            assert (Op1.first == IC_IMM && Op2.first == IC_IMM &&
-                    "And operation with an immediate and a register!");
-            Val = Op1.second & Op2.second;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
+                   "And operation with an immediate and a register!");
+            Val = Op1.Val & Op2.Val;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_LSHIFT:
-            assert (Op1.first == IC_IMM && Op2.first == IC_IMM &&
-                    "Left shift operation with an immediate and a register!");
-            Val = Op1.second << Op2.second;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
+                   "Left shift operation with an immediate and a register!");
+            Val = Op1.Val << Op2.Val;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_RSHIFT:
-            assert (Op1.first == IC_IMM && Op2.first == IC_IMM &&
-                    "Right shift operation with an immediate and a register!");
-            Val = Op1.second >> Op2.second;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
+                   "Right shift operation with an immediate and a register!");
+            Val = Op1.Val >> Op2.Val;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_EQ:
-            assert(Op1.first == IC_IMM && Op2.first == IC_IMM &&
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
                    "Equals operation with an immediate and a register!");
-            Val = (Op1.second == Op2.second) ? -1 : 0;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            Val = (Op1.Val == Op2.Val) ? -1 : 0;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_NE:
-            assert(Op1.first == IC_IMM && Op2.first == IC_IMM &&
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
                    "Not-equals operation with an immediate and a register!");
-            Val = (Op1.second != Op2.second) ? -1 : 0;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            Val = (Op1.Val != Op2.Val) ? -1 : 0;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_LT:
-            assert(Op1.first == IC_IMM && Op2.first == IC_IMM &&
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
                    "Less-than operation with an immediate and a register!");
-            Val = (Op1.second < Op2.second) ? -1 : 0;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            Val = (Op1.Val < Op2.Val) ? -1 : 0;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_LE:
-            assert(Op1.first == IC_IMM && Op2.first == IC_IMM &&
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
                    "Less-than-or-equal operation with an immediate and a "
                    "register!");
-            Val = (Op1.second <= Op2.second) ? -1 : 0;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            Val = (Op1.Val <= Op2.Val) ? -1 : 0;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_GT:
-            assert(Op1.first == IC_IMM && Op2.first == IC_IMM &&
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
                    "Greater-than operation with an immediate and a register!");
-            Val = (Op1.second > Op2.second) ? -1 : 0;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            Val = (Op1.Val > Op2.Val) ? -1 : 0;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           case IC_GE:
-            assert(Op1.first == IC_IMM && Op2.first == IC_IMM &&
+            assert(Op1.Kind == IC_IMM && Op2.Kind == IC_IMM &&
                    "Greater-than-or-equal operation with an immediate and a "
                    "register!");
-            Val = (Op1.second >= Op2.second) ? -1 : 0;
-            OperandStack.push_back(std::make_pair(IC_IMM, Val));
+            Val = (Op1.Val >= Op2.Val) ? -1 : 0;
+            OperandStack.push_back({IC_IMM, Val});
             break;
           }
         }
       }
       assert (OperandStack.size() == 1 && "Expected a single result.");
-      return OperandStack.pop_back_val().second;
+      return OperandStack.pop_back_val();
     }
   };
 
@@ -463,8 +547,13 @@ class X86AsmParser : public MCTargetAsmParser {
     bool IsPIC = false;
     AsmTypeInfo CurType;
 
-    bool setSymRef(const MCExpr *Val, StringRef ID, StringRef &ErrMsg) {
+    // MS inline asm rewrites the operand around a single symbol, so only it
+    // still refuses a second one.
+    bool setSymRef(const MCExpr *Val, StringRef ID, bool ParsingMSInlineAsm,
+                   StringRef &ErrMsg) {
       if (Sym) {
+        if (!ParsingMSInlineAsm)
+          return false;
         ErrMsg = "cannot use more than one symbol in memory operand";
         return true;
       }
@@ -490,7 +579,8 @@ class X86AsmParser : public MCTargetAsmParser {
     unsigned getSize() const { return CurType.Size; }
     unsigned getElementSize() const { return CurType.ElementSize; }
     unsigned getLength() const { return CurType.Length; }
-    int64_t getImm() { return Imm + IC.execute(); }
+    int64_t getImm(MCContext &Ctx) { return Imm + IC.execute(Ctx).Val; }
+    const MCExpr *getSymExpr(MCContext &Ctx) { return IC.execute(Ctx).Sym; }
     bool isValidEndState() const {
       return State == IES_RBRAC || State == IES_RPAREN ||
              State == IES_INTEGER || State == IES_REGISTER ||
@@ -902,13 +992,14 @@ class X86AsmParser : public MCTargetAsmParser {
       case IES_INIT:
       case IES_LBRAC:
       case IES_LPAREN:
-        if (setSymRef(SymRef, SymRefName, ErrMsg))
+        if (setSymRef(SymRef, SymRefName, ParsingMSInlineAsm, ErrMsg))
           return true;
         // Mark TmpScale as invalid, in case of multiplying by register
         TmpScale = 0;
-        MemExpr = true;
+        if (!OffsetOperator)
+          MemExpr = true;
         State = IES_INTEGER;
-        IC.pushOperand(IC_IMM);
+        IC.pushOperand(IC_IMM, 0, SymRef);
         if (ParsingMSInlineAsm)
           Info = IDInfo;
         setTypeInfo(Type);
@@ -1169,13 +1260,11 @@ class X86AsmParser : public MCTargetAsmParser {
       case IES_PLUS:
       case IES_INIT:
       case IES_LBRAC:
-        if (setSymRef(Val, ID, ErrMsg))
+        if (setSymRef(Val, ID, ParsingMSInlineAsm, ErrMsg))
           return true;
         OffsetOperator = true;
         State = IES_OFFSET;
-        // As we cannot yet resolve the actual value (offset), we retain
-        // the requested semantics by pushing a '0' to the operands stack
-        IC.pushOperand(IC_IMM);
+        IC.pushOperand(IC_IMM, 0, Val);
         if (ParsingMSInlineAsm) {
           Info = IDInfo;
         }
@@ -1192,10 +1281,10 @@ class X86AsmParser : public MCTargetAsmParser {
       case IES_PLUS:
       case IES_INIT:
       case IES_LBRAC:
-        if (setSymRef(Val, ID, ErrMsg))
+        if (setSymRef(Val, ID, false, ErrMsg))
           return true;
         State = IES_OFFSET;
-        IC.pushOperand(IC_IMM);
+        IC.pushOperand(IC_IMM, 0, Val);
         return false;
       default:
         ErrMsg = "unexpected imagerel operator expression";
@@ -2346,7 +2435,7 @@ void X86AsmParser::RewriteIntelExpression(IntelExprStateMachine &SM,
     ExprLen = End.getPointer() - (SymName.data() + SymName.size());
     // If we have only a symbol than there's no need for complex rewrite,
     // simply skip everything after it
-    if (!(SM.getBaseReg() || SM.getIndexReg() || SM.getImm())) {
+    if (!(SM.getBaseReg() || SM.getIndexReg() || SM.getImm(getContext()))) {
       if (ExprLen)
         InstInfo->AsmRewrites->emplace_back(AOK_Skip, Loc, ExprLen);
       return;
@@ -2364,7 +2453,7 @@ void X86AsmParser::RewriteIntelExpression(IntelExprStateMachine &SM,
     OffsetNameStr = SM.getSymName();
   // Emit it
   IntelExpr Expr(BaseRegStr, IndexRegStr, SM.getScale(), OffsetNameStr,
-                 SM.getImm(), SM.isMemExpr());
+                 SM.getImm(getContext()), SM.isMemExpr());
   InstInfo->AsmRewrites->emplace_back(Loc, ExprLen, Expr);
 }
 
@@ -2835,8 +2924,8 @@ bool X86AsmParser::parseIntelOperand(OperandVector &Operands, StringRef Name) {
   if (isParsingMSInlineAsm())
     RewriteIntelExpression(SM, Start, Tok.getLoc());
 
-  int64_t Imm = SM.getImm();
-  const MCExpr *Disp = SM.getSym();
+  int64_t Imm = SM.getImm(getContext());
+  const MCExpr *Disp = SM.getSymExpr(getContext());
   const MCExpr *ImmDisp = MCConstantExpr::create(Imm, getContext());
   if (Disp && Imm)
     Disp = MCBinaryExpr::createAdd(Disp, ImmDisp, getContext());
diff --git a/llvm/test/MC/X86/intel-syntax-error.s b/llvm/test/MC/X86/intel-syntax-error.s
index 2ccfc49046362..4ab07f3b7e4d3 100644
--- a/llvm/test/MC/X86/intel-syntax-error.s
+++ b/llvm/test/MC/X86/intel-syntax-error.s
@@ -19,8 +19,6 @@ _test2:
 .global i
 .set FOO, 2
 
-//CHECK: error: cannot use more than one symbol in memory operand
-mov eax, DWORD PTR arr[i]
 //CHECK: error: rip can only be used as a base register
 .code64
 mov rax, rip
diff --git a/llvm/test/MC/X86/intel-syntax.s b/llvm/test/MC/X86/intel-syntax.s
index 024af5db6106d..995d1f29e0824 100644
--- a/llvm/test/MC/X86/intel-syntax.s
+++ b/llvm/test/MC/X86/intel-syntax.s
@@ -692,7 +692,7 @@ fxrstor64 [rax]
 // CHECK: movq _g0(,%rsi,4), %rax
 mov rbx, qword ptr [_g0]
 mov rcx, qword ptr [_g0 + 8]
-mov rax, QWORD PTR _g0[rbp + 1 + (2 * 5) - 3 + 1<<1]
+mov rax, QWORD PTR _g0[rbp + (1 + (2 * 5) - 3 + 1<<1)]
 mov rax, QWORD PTR _g0[rsi*4]
 
 "?half@?0??bar@@YAXXZ at 4NA":
@@ -919,3 +919,18 @@ lea eax, [esp+eax]
 vpgatherdq ymm0, [rdi+xmm1], ymm2
 // CHECK: vpgatherdq      %ymm2, (%rdi,%xmm1), %ymm0
 vpgatherdq ymm0, [xmm1+rdi], ymm2
+
+// CHECK: movq $_foo-_bar, %rax
+mov rax, offset _foo - _bar
+// CHECK: addl $_GLOBAL_OFFSET_TABLE_+(.Ltmp0-.L0$pb), %eax
+add eax, offset _GLOBAL_OFFSET_TABLE_+(.Ltmp0-.L0$pb)
+// CHECK: movl _foo-_bar(%rbx), %eax
+mov eax, [rbx + _foo - _bar]
+// CHECK: movl -_foo(%rbx), %eax
+mov eax, [rbx - _foo]
+// CHECK: movl -_foo+4(%rbx), %eax
+mov eax, [rbx + 4 - _foo]
+// CH...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/225985


More information about the llvm-commits mailing list