[llvm] [llvm-ml] Recognise `imagerel` operator (PR #209630)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 12 00:23:15 PDT 2026


https://github.com/dingxiangfei2009 updated https://github.com/llvm/llvm-project/pull/209630

>From b17fab5da07d8904d11441a76fae728ae2ef1639 Mon Sep 17 00:00:00 2001
From: Xiangfei Ding <dingxiangfei2009 at protonmail.ch>
Date: Tue, 7 Jul 2026 20:58:17 +0000
Subject: [PATCH] llvm-ml: Recognise imagerel operator

Signed-off-by: Xiangfei Ding <dingxiangfei2009 at protonmail.ch>
---
 llvm/lib/MC/MCParser/MasmParser.cpp           | 11 ++++
 .../lib/Target/X86/AsmParser/X86AsmParser.cpp | 66 ++++++++++++++++++-
 llvm/test/tools/llvm-ml/imagerel-obj.asm      | 21 ++++++
 llvm/test/tools/llvm-ml/imagerel.asm          | 62 +++++++++++++++++
 4 files changed, 159 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/tools/llvm-ml/imagerel-obj.asm
 create mode 100644 llvm/test/tools/llvm-ml/imagerel.asm

diff --git a/llvm/lib/MC/MCParser/MasmParser.cpp b/llvm/lib/MC/MCParser/MasmParser.cpp
index 4125c492db940..75b2888940cbe 100644
--- a/llvm/lib/MC/MCParser/MasmParser.cpp
+++ b/llvm/lib/MC/MCParser/MasmParser.cpp
@@ -1426,6 +1426,17 @@ bool MasmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc,
       Res = MCUnaryExpr::createNot(Res, getContext(), FirstTokenLoc);
       return false;
     }
+    // Parse IMAGEREL operator.
+    if (Identifier.equals_insensitive("imagerel")) {
+      if (parsePrimaryExpr(Res, EndLoc, nullptr))
+        return true;
+      if (const MCExpr *ModifiedRes =
+              applySpecifier(Res, MCSymbolRefExpr::VK_COFF_IMGREL32)) {
+        Res = ModifiedRes;
+        return false;
+      }
+      return Error(FirstTokenLoc, "cannot apply 'imagerel' to this expression");
+    }
     // Parse directional local label references.
     if (Identifier.equals_insensitive("@b") ||
         Identifier.equals_insensitive("@f")) {
diff --git a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
index f03b7408c8f30..05a687a2de228 100644
--- a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
+++ b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
@@ -1187,6 +1187,25 @@ class X86AsmParser : public MCTargetAsmParser {
       }
       return false;
     }
+    // Unlike onOffset, we do not set OffsetOperator here. The IMAGEREL
+    // specifier is already encoded in the MCExpr with VK_COFF_IMGREL32,
+    // so no additional rewriting is needed for inline asm.
+    bool onImagerel(const MCExpr *Val, StringRef ID, StringRef &ErrMsg) {
+      PrevState = State;
+      switch (State) {
+      case IES_PLUS:
+      case IES_INIT:
+      case IES_LBRAC:
+        if (setSymRef(Val, ID, ErrMsg))
+          return true;
+        State = IES_OFFSET;
+        IC.pushOperand(IC_IMM);
+        return false;
+      default:
+        ErrMsg = "unexpected imagerel operator expression";
+        return true;
+      }
+    }
     void onCast(AsmTypeInfo Info) {
       PrevState = State;
       switch (State) {
@@ -1231,6 +1250,8 @@ class X86AsmParser : public MCTargetAsmParser {
   bool parseIntelOperand(OperandVector &Operands, StringRef Name);
   bool ParseIntelOffsetOperator(const MCExpr *&Val, StringRef &ID,
                                 InlineAsmIdentifierInfo &Info, SMLoc &End);
+  bool ParseIntelImagerelOperator(const MCExpr *&Val, StringRef &ID,
+                                  InlineAsmIdentifierInfo &Info, SMLoc &End);
   bool ParseIntelDotOperator(IntelExprStateMachine &SM, SMLoc &End);
   unsigned IdentifyIntelInlineAsmOperator(StringRef Name);
   unsigned ParseIntelInlineAsmOperator(unsigned OpKind);
@@ -1924,6 +1945,9 @@ bool X86AsmParser::ParseIntelNamedOperator(StringRef Name,
   if (Name != Name.lower() && Name != Name.upper() &&
       !getParser().isParsingMasm())
     return false;
+  // Operators like 'offset' and 'imagerel' consume their operand tokens
+  // internally; other named operators need a trailing consumeToken().
+  bool AlreadyConsumed = false;
   if (Name.equals_insensitive("not")) {
     SM.onNot();
   } else if (Name.equals_insensitive("or")) {
@@ -1951,10 +1975,23 @@ bool X86AsmParser::ParseIntelNamedOperator(StringRef Name,
         SM.onOffset(Val, OffsetLoc, ID, Info, isParsingMSInlineAsm(), ErrMsg);
     if (ParseError)
       return Error(SMLoc::getFromPointer(Name.data()), ErrMsg);
+    AlreadyConsumed = true;
+  } else if (Name.equals_insensitive("imagerel")) {
+    const MCExpr *Val;
+    StringRef ID;
+    InlineAsmIdentifierInfo Info;
+    ParseError = ParseIntelImagerelOperator(Val, ID, Info, End);
+    if (ParseError)
+      return true;
+    StringRef ErrMsg;
+    ParseError = SM.onImagerel(Val, ID, ErrMsg);
+    if (ParseError)
+      return Error(SMLoc::getFromPointer(Name.data()), ErrMsg);
+    AlreadyConsumed = true;
   } else {
     return false;
   }
-  if (!Name.equals_insensitive("offset"))
+  if (!AlreadyConsumed)
     End = consumeToken();
   return true;
 }
@@ -2557,6 +2594,33 @@ bool X86AsmParser::ParseIntelOffsetOperator(const MCExpr *&Val, StringRef &ID,
   return false;
 }
 
+/// Parse the 'imagerel' operator.
+/// This operator is used to specify an image-relative reference to a symbol.
+bool X86AsmParser::ParseIntelImagerelOperator(const MCExpr *&Val, StringRef &ID,
+                                              InlineAsmIdentifierInfo &Info,
+                                              SMLoc &End) {
+  // Eat imagerel, mark start of identifier.
+  SMLoc Start = Lex().getLoc();
+  ID = getTok().getString();
+  if (!isParsingMSInlineAsm()) {
+    if ((getTok().isNot(AsmToken::Identifier) &&
+         getTok().isNot(AsmToken::String)) ||
+        getParser().parsePrimaryExpr(Val, End, nullptr))
+      return Error(Start, "unexpected token!");
+  } else if (ParseIntelInlineAsmIdentifier(Val, ID, Info, false, End, true)) {
+    return Error(Start, "unable to lookup expression");
+  } else if (Info.isKind(InlineAsmIdentifierInfo::IK_EnumVal)) {
+    return Error(Start, "imagerel operator cannot yet handle constants");
+  }
+
+  const MCExpr *ModifiedVal =
+      getParser().applySpecifier(Val, MCSymbolRefExpr::VK_COFF_IMGREL32);
+  if (!ModifiedVal)
+    return Error(Start, "cannot apply 'imagerel' to this expression");
+  Val = ModifiedVal;
+  return false;
+}
+
 // Query a candidate string for being an Intel assembly operator
 // Report back its kind, or IOK_INVALID if does not evaluated as a known one
 unsigned X86AsmParser::IdentifyIntelInlineAsmOperator(StringRef Name) {
diff --git a/llvm/test/tools/llvm-ml/imagerel-obj.asm b/llvm/test/tools/llvm-ml/imagerel-obj.asm
new file mode 100644
index 0000000000000..611b24561b14a
--- /dev/null
+++ b/llvm/test/tools/llvm-ml/imagerel-obj.asm
@@ -0,0 +1,21 @@
+; RUN: llvm-ml -filetype=obj -m32 %s /Fo %t.32.obj
+; RUN: llvm-readobj --relocations %t.32.obj | FileCheck %s --check-prefix=CHECK-I386
+
+; RUN: llvm-ml -filetype=obj -m64 %s /Fo %t.64.obj
+; RUN: llvm-readobj --relocations %t.64.obj | FileCheck %s --check-prefix=CHECK-AMD64
+
+.data
+sym1:
+dd 42
+
+; CHECK-I386:      Relocations [
+; CHECK-I386:        IMAGE_REL_I386_DIR32NB sym1
+; CHECK-I386:      ]
+
+; CHECK-AMD64:      Relocations [
+; CHECK-AMD64:        IMAGE_REL_AMD64_ADDR32NB sym1
+; CHECK-AMD64:      ]
+
+rva_data dd IMAGEREL sym1
+
+END
diff --git a/llvm/test/tools/llvm-ml/imagerel.asm b/llvm/test/tools/llvm-ml/imagerel.asm
new file mode 100644
index 0000000000000..6e88f1cf349a8
--- /dev/null
+++ b/llvm/test/tools/llvm-ml/imagerel.asm
@@ -0,0 +1,62 @@
+; RUN: llvm-ml -filetype=s %s /Fo - | FileCheck %s
+
+.data
+sym1:
+dd 42
+
+sym2:
+dd 43
+
+; CHECK-LABEL: rva_data:
+; CHECK: .long sym1 at IMGREL
+rva_data dd IMAGEREL sym1
+
+; CHECK-LABEL: rva_data_offset:
+; CHECK: .long sym1 at IMGREL+4
+rva_data_offset dd IMAGEREL sym1 + 4
+
+; CHECK-LABEL: rva_data_paren:
+; CHECK: .long sym1 at IMGREL+4
+rva_data_paren dd (IMAGEREL sym1) + 4
+
+MY_STRUCT STRUCT
+  field_default dd IMAGEREL sym1
+MY_STRUCT ENDS
+
+; CHECK-LABEL: struct_inst_default:
+; CHECK: .long sym1 at IMGREL
+struct_inst_default MY_STRUCT <>
+
+; CHECK-LABEL: struct_inst_override:
+; CHECK: .long sym2 at IMGREL
+struct_inst_override MY_STRUCT <IMAGEREL sym2>
+
+.code
+sym3:
+; CHECK-LABEL: t1:
+; CHECK: mov eax, offset sym1 at IMGREL
+t1:
+mov eax, IMAGEREL sym1
+
+; CHECK-LABEL: t2:
+; CHECK: mov eax, offset sym1 at IMGREL+4
+t2:
+mov eax, IMAGEREL sym1 + 4
+
+; CHECK-LABEL: t3:
+; CHECK: mov ebx, dword ptr [eax + sym1 at IMGREL]
+t3:
+mov ebx, [eax + IMAGEREL sym1]
+
+; CHECK-LABEL: t4:
+; CHECK: mov eax, dword ptr fs:[sym1 at IMGREL]
+t4:
+mov eax, fs:[IMAGEREL sym1]
+
+; Test negative offset.
+; CHECK-LABEL: t5:
+; CHECK: mov eax, offset sym1 at IMGREL-4
+t5:
+mov eax, IMAGEREL sym1 - 4
+
+END



More information about the llvm-commits mailing list