[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