https://github.com/FireBurn updated 
https://github.com/llvm/llvm-project/pull/237119

>From e03f96b181f42496b2500b8a13b0d50033ceb6b1 Mon Sep 17 00:00:00 2001
From: Mike Lothian <[email protected]>
Date: Fri, 30 Oct 2026 07:07:51 +0000
Subject: [PATCH] [clang] Add __builtin_imgrel32

The builtin returns the offset of a global variable or function from the
image base as an unsigned 32-bit value. It is accepted on COFF targets
for the address of a global variable, an element of one, or a function.
Local and thread-local variables and dllimport declarations are
rejected, as they have no offset from this image's base.

Both constant evaluators represent the result as an lvalue based on the
call, the way __builtin_function_start is represented, so it works in
constant initializers and anywhere else the evaluator is used. The
bytecode interpreter has an Imgrel32 opcode that checks the pointer and
produces the same value. CodeGen emits it as a difference from
__ImageBase, which the backend lowers to an image relative relocation in
data. In a function body the same expression is emitted without needing
a constant context.

C has no other way to write this. MSVC style run-time type information
and exception tables hold 32-bit image relative references to other
objects, and without a way to express them in C they have to be written
in assembly. Assembly in a function body is invisible to ThinLTO, so a
symbol defined there is undefined at link time.

Assisted-by: Claude
---
 clang/docs/LanguageExtensions.md              | 25 +++++++++++
 clang/docs/ReleaseNotes.md                    |  4 ++
 clang/include/clang/Basic/Builtins.td         |  6 +++
 .../clang/Basic/DiagnosticSemaKinds.td        |  6 +++
 clang/lib/AST/ByteCode/Compiler.cpp           | 12 ++++++
 clang/lib/AST/ByteCode/Interp.cpp             | 18 ++++++++
 clang/lib/AST/ByteCode/Interp.h               |  4 +-
 clang/lib/AST/ByteCode/Opcodes.td             |  4 ++
 clang/lib/AST/ExprConstShared.h               |  3 +-
 clang/lib/AST/ExprConstant.cpp                | 15 +++++++
 clang/lib/CodeGen/CGBuiltin.cpp               |  8 ++++
 clang/lib/CodeGen/CGExprConstant.cpp          | 10 +++++
 clang/lib/CodeGen/CodeGenModule.cpp           | 19 ++++++++
 clang/lib/CodeGen/CodeGenModule.h             |  4 ++
 clang/lib/Sema/SemaChecking.cpp               | 43 +++++++++++++++++++
 clang/test/CodeGen/builtin-imgrel32.c         | 37 ++++++++++++++++
 clang/test/Sema/builtin-imgrel32.c            | 30 +++++++++++++
 17 files changed, 246 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGen/builtin-imgrel32.c
 create mode 100644 clang/test/Sema/builtin-imgrel32.c

diff --git a/clang/docs/LanguageExtensions.md b/clang/docs/LanguageExtensions.md
index 6c1eddb4bc863b..ecaff6fc08988d 100644
--- a/clang/docs/LanguageExtensions.md
+++ b/clang/docs/LanguageExtensions.md
@@ -4056,6 +4056,31 @@ template<typename T> constexpr T *addressof(T &value) {
 }
 ```
 
+### `__builtin_imgrel32`
+
+`__builtin_imgrel32` returns the offset of a global variable or function from
+the image base, as a 32-bit unsigned integer. It is only available on COFF
+targets, and the argument must be the address of a global variable, an element
+of one, or a function. Thread-local variables and `dllimport` declarations are
+rejected. The result is an address constant that the linker resolves, so it
+can initialize static data. In a function body it is evaluated at run time.
+It lowers to an image relative relocation (`IMAGE_REL_AMD64_ADDR32NB` on
+x86-64), the same relocation MSVC uses for C++ run-time type information.
+
+**Syntax**:
+
+```c++
+unsigned __builtin_imgrel32(const void *address)
+```
+
+**Example of use**:
+
+```c++
+extern int data;
+struct locator { unsigned signature, data_rva; };
+const struct locator locator = { 1, __builtin_imgrel32(&data) };
+```
+
 ### `__builtin_function_start`
 
 `__builtin_function_start` returns the address of a function body.
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index f2cc86bf9ccc23..f294a67d044e53 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -328,6 +328,10 @@ documentation](CIR/index.md) for more information 
regarding ClangIR's status and
 
 - Added support for the `__builtin_strlcat` and `__builtin_strlcpy` builtins.
 
+- Added `__builtin_imgrel32`, which returns the offset of a global from the 
image
+  base as a constant, for static data that needs image relative references on
+  COFF targets.
+
 - Added `__builtin_sort_pack` to sort a pack of types using the same
   order as `__builtin_type_order`.
 
diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index d04fd899eb63a6..373d34fb10da23 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4963,6 +4963,12 @@ def BuiltinFunctionStart : Builtin {
   let Prototype = "void*(void&)";
 }
 
+def BuiltinImgrel32 : Builtin {
+  let Spellings = ["__builtin_imgrel32"];
+  let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
+  let Prototype = "unsigned int(void const*)";
+}
+
 def BuiltinOperatorNew : Builtin {
   let Spellings = ["__builtin_operator_new"];
   let Attributes = [Const, CustomTypeChecking, Constexpr];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 56cc9721781063..2ba37ecb8e4ff9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1062,6 +1062,12 @@ def warn_fortify_source_overread : Warning<
 def err_function_start_invalid_type: Error<
   "argument must be a function">;
 
+def err_imgrel32_requires_coff : Error<
+  "'__builtin_imgrel32' is only supported on COFF targets">;
+def err_imgrel32_not_address_constant : Error<
+  "argument to '__builtin_imgrel32' must be the address of a global variable "
+  "or function">;
+
 def err_ptrauth_disabled :
   Error<"this target does not support pointer authentication">;
 def err_ptrauth_invalid_key :
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 71f761f58aed28..a84957350b05a5 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -6331,6 +6331,18 @@ bool Compiler<Emitter>::VisitBuiltinCallExpr(const 
CallExpr *E,
     return true;
   }
 
+  // The offset from the image base is not known until link time. The result is
+  // an integral based on the CallExpr, which codegen turns into a relocation.
+  if (BuiltinID == Builtin::BI__builtin_imgrel32) {
+    if (!this->visit(E->getArg(0)))
+      return false;
+    if (!this->emitImgrel32(E, E))
+      return false;
+    if (DiscardResult)
+      return this->emitPopUint32(E);
+    return true;
+  }
+
   // For these, we're expected to ultimately return an APValue pointing
   // to the CallExpr. This is needed to get the correct codegen.
   if (BuiltinID == Builtin::BI__builtin___CFStringMakeConstantString ||
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 4d3ee95df216c0..461b41a3b56857 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -3022,6 +3022,24 @@ bool CheckIntegralAddressCast(InterpState &S, CodePtr 
OpPC, unsigned BitWidth) {
           BitWidth);
 }
 
+/// The offset of a global from the image base is only known to the linker, so
+/// the result is an integral based on the call, as in the other evaluator.
+bool Imgrel32(InterpState &S, CodePtr OpPC, const Expr *E) {
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+  if (!Ptr.isFunctionPointer()) {
+    const auto *VD = Ptr.getRootValueDecl();
+    if (!VD)
+      return Invalid(S, OpPC);
+    if (const auto *Var = dyn_cast<VarDecl>(VD);
+        Var && !Var->hasGlobalStorage())
+      return Invalid(S, OpPC);
+  }
+
+  S.Stk.push<Integral<32, false>>(IntegralKind::ExprAddress, E, 0);
+  return true;
+}
+
 bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
 
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 98970cdf2cfc06..159c6a16874c7f 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1290,7 +1290,8 @@ static inline bool IsOpaqueConstantCall(const CallExpr 
*E) {
   return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
           Builtin == Builtin::BI__builtin___NSStringMakeConstantString ||
           Builtin == Builtin::BI__builtin_ptrauth_sign_constant ||
-          Builtin == Builtin::BI__builtin_function_start);
+          Builtin == Builtin::BI__builtin_function_start ||
+          Builtin == Builtin::BI__builtin_imgrel32);
 }
 
 bool arePotentiallyOverlappingStringLiterals(const Pointer &LHS,
@@ -3050,6 +3051,7 @@ bool CheckPointerToIntegralCast(InterpState &S, CodePtr 
OpPC,
 bool CheckIntegralAddressCast(InterpState &S, CodePtr OpPC, unsigned BitWidth);
 bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth);
 bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth);
+bool Imgrel32(InterpState &S, CodePtr OpPC, const Expr *E);
 
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
diff --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index 23aa5abeb0a881..8fa35750f324fc 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -857,6 +857,10 @@ def CastPointerIntegralAP : Opcode {
 def CastPointerIntegralAPS : Opcode {
   let Args = [ArgUint32];
 }
+// [Pointer] -> [Uint32]
+def Imgrel32 : Opcode {
+  let Args = [ArgExpr];
+}
 def CastIntegralFixedPoint : Opcode {
   let Types = [IntegralTypeClass];
   let Args = [ArgUint32];
diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index a306af5bcf61db..bc41579a69f90b 100644
--- a/clang/lib/AST/ExprConstShared.h
+++ b/clang/lib/AST/ExprConstShared.h
@@ -159,7 +159,8 @@ inline bool isOpaqueConstantCall(const CallExpr *E) {
   return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
           Builtin == Builtin::BI__builtin___NSStringMakeConstantString ||
           Builtin == Builtin::BI__builtin_ptrauth_sign_constant ||
-          Builtin == Builtin::BI__builtin_function_start);
+          Builtin == Builtin::BI__builtin_function_start ||
+          Builtin == Builtin::BI__builtin_imgrel32);
 }
 
 bool isGlobalLValue(const ValueDecl *D, const Expr *E);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f1da1dca7b87b1..2f8282ec8da3eb 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17234,6 +17234,21 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const 
CallExpr *E,
     return Success(Result, E);
   }
 
+  if (BuiltinOp == Builtin::BI__builtin_imgrel32) {
+    // The offset from the image base is only known to the linker. The result
+    // is an lvalue based on the call, which CodeGen turns into a relocation.
+    LValue LV;
+    if (!EvaluatePointer(E->getArg(0), LV, Info))
+      return false;
+    if (!LV.getLValueBase().is<const ValueDecl *>())
+      return Error(E);
+    LV.Designator.setInvalid();
+    LV.Base = E;
+    LV.Offset = CharUnits::Zero();
+    LV.moveInto(Result);
+    return true;
+  }
+
   auto EvalTestOp = [&](llvm::function_ref<bool(const APInt &, const APInt &)>
                             Fn) {
     APValue SourceLHS, SourceRHS;
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index e154b9e752fd5c..db1bcdc6594665 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -6021,6 +6021,14 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl 
GD, unsigned BuiltinID,
   case Builtin::BI__builtin_function_start:
     return RValue::get(CGM.GetFunctionStart(
         E->getArg(0)->getAsBuiltinConstantDeclRef(CGM.getContext())));
+  case Builtin::BI__builtin_imgrel32: {
+    const Expr *Arg = E->getArg(0);
+    llvm::Constant *Ptr =
+        ConstantEmitter(*this).tryEmitAbstract(Arg, Arg->getType());
+    assert(Ptr && "Sema checked that the argument is a constant address");
+    return RValue::get(
+        CGM.GetImageRelativeOffset(Ptr, ConvertType(E->getType())));
+  }
   case Builtin::BI__builtin_operator_new:
     return EmitBuiltinNewDeleteCall(
         E->getCallee()->getType()->castAs<FunctionProtoType>(), E, false);
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index e2ec07bbae711a..d4d45687e9d4ba 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2266,6 +2266,12 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() {
       return nullptr;
   }
 
+  // The base is the address the offset is taken from; subtract the image base.
+  if (const auto *CE =
+          dyn_cast_if_present<CallExpr>(base.dyn_cast<const Expr *>()))
+    if (CE->getBuiltinCallee() == Builtin::BI__builtin_imgrel32)
+      return CGM.GetImageRelativeOffset(value, destTy);
+
   // Convert to the appropriate type; this could be an lvalue for
   // an integer.  FIXME: performAddrSpaceCast
   if (isa<llvm::PointerType>(destTy))
@@ -2514,6 +2520,10 @@ ConstantLValueEmitter::VisitAddrLabelExpr(const 
AddrLabelExpr *E) {
 ConstantLValue
 ConstantLValueEmitter::VisitCallExpr(const CallExpr *E) {
   unsigned builtin = E->getBuiltinCallee();
+  if (builtin == Builtin::BI__builtin_imgrel32)
+    return Emitter.tryEmitPrivateForMemory(E->getArg(0),
+                                           E->getArg(0)->getType());
+
   if (builtin == Builtin::BI__builtin_function_start)
     return CGM.GetFunctionStart(
         E->getArg(0)->getAsBuiltinConstantDeclRef(CGM.getContext()));
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index ce084487a69820..45f3a5ea0fc2fd 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5924,6 +5924,25 @@ CodeGenModule::GetAddrOfFunction(GlobalDecl GD, 
llvm::Type *Ty, bool ForVTable,
   return F;
 }
 
+llvm::Constant *CodeGenModule::GetImageRelativeOffset(llvm::Constant *Ptr,
+                                                      llvm::Type *Ty) {
+  llvm::GlobalVariable *ImageBase = TheModule.getNamedGlobal("__ImageBase");
+  if (!ImageBase) {
+    ImageBase =
+        new llvm::GlobalVariable(TheModule, Int8Ty, /*isConstant=*/true,
+                                 llvm::GlobalValue::ExternalLinkage,
+                                 /*Initializer=*/nullptr, "__ImageBase");
+    setDSOLocal(ImageBase);
+  }
+
+  // trunc(ptrtoint(P) - ptrtoint(__ImageBase)), which the backend lowers to an
+  // image relative relocation.
+  llvm::Constant *Diff = llvm::ConstantExpr::getSub(
+      llvm::ConstantExpr::getPtrToInt(Ptr, IntPtrTy),
+      llvm::ConstantExpr::getPtrToInt(ImageBase, IntPtrTy));
+  return llvm::ConstantExpr::getTrunc(Diff, Ty);
+}
+
 llvm::Constant *CodeGenModule::GetFunctionStart(const ValueDecl *Decl) {
   llvm::GlobalValue *F =
       cast<llvm::GlobalValue>(GetAddrOfFunction(Decl)->stripPointerCasts());
diff --git a/clang/lib/CodeGen/CodeGenModule.h 
b/clang/lib/CodeGen/CodeGenModule.h
index e2bd6bc4701fda..0d35b251211c39 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1111,6 +1111,10 @@ class CodeGenModule : public CodeGenTypeCache {
   // Return the function body address of the given function.
   llvm::Constant *GetFunctionStart(const ValueDecl *Decl);
 
+  /// Returns the offset of the global Ptr points to from the image base,
+  /// as a constant of integer type Ty, for COFF targets.
+  llvm::Constant *GetImageRelativeOffset(llvm::Constant *Ptr, llvm::Type *Ty);
+
   /// Return a function pointer for a reference to the given function.
   /// This correctly handles weak references, but does not apply a
   /// pointer signature.
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 406f03a17f7ebe..9f89ce191a6da6 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -307,6 +307,45 @@ static bool BuiltinFunctionStart(Sema &S, CallExpr 
*TheCall) {
                                               TheCall->getBeginLoc());
 }
 
+/// Check the argument to __builtin_imgrel32, the 32-bit offset of a global
+/// from the image base. It is only meaningful as a constant.
+static bool BuiltinImgrel32(Sema &S, CallExpr *TheCall) {
+  if (S.checkArgCount(TheCall, 1))
+    return true;
+
+  if (TheCall->getArg(0)->containsErrors())
+    return true;
+
+  if (!S.Context.getTargetInfo().getTriple().isOSBinFormatCOFF()) {
+    S.Diag(TheCall->getBeginLoc(), diag::err_imgrel32_requires_coff)
+        << TheCall->getSourceRange();
+    return true;
+  }
+
+  ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
+  if (Arg.isInvalid())
+    return true;
+  TheCall->setArg(0, Arg.get());
+
+  // A dllimport declaration lives in another image and a thread-local variable
+  // has no fixed address, so neither has an offset from this image's base.
+  Expr::EvalResult Result;
+  const auto *D =
+      Arg.get()->EvaluateAsRValue(Result, S.Context) && Result.Val.isLValue()
+          ? Result.Val.getLValueBase().dyn_cast<const ValueDecl *>()
+          : nullptr;
+  if (!D || D->hasAttr<DLLImportAttr>() ||
+      (isa<VarDecl>(D) &&
+       cast<VarDecl>(D)->getTLSKind() != VarDecl::TLS_None)) {
+    S.Diag(TheCall->getBeginLoc(), diag::err_imgrel32_not_address_constant)
+        << TheCall->getSourceRange();
+    return true;
+  }
+
+  TheCall->setType(S.Context.UnsignedIntTy);
+  return false;
+}
+
 /// Check the number of arguments and set the result type to
 /// the argument type.
 static bool BuiltinPreserveAI(Sema &S, CallExpr *TheCall) {
@@ -3640,6 +3679,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, 
unsigned BuiltinID,
     if (BuiltinFunctionStart(*this, TheCall))
       return ExprError();
     break;
+  case Builtin::BI__builtin_imgrel32:
+    if (BuiltinImgrel32(*this, TheCall))
+      return ExprError();
+    break;
   case Builtin::BI__builtin_is_aligned:
   case Builtin::BI__builtin_align_up:
   case Builtin::BI__builtin_align_down:
diff --git a/clang/test/CodeGen/builtin-imgrel32.c 
b/clang/test/CodeGen/builtin-imgrel32.c
new file mode 100644
index 00000000000000..c8ed9ec3c94cd7
--- /dev/null
+++ b/clang/test/CodeGen/builtin-imgrel32.c
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple x86_64-w64-mingw32 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-w64-mingw32 
-fexperimental-new-constant-interpreter -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -emit-llvm -o - %s | 
FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-w64-mingw32 -emit-llvm -o - %s | FileCheck 
%s
+
+// __builtin_imgrel32 is the offset of a global from the image base, which the
+// linker resolves. It is emitted as a difference from __ImageBase.
+
+struct locator {
+  unsigned signature;
+  unsigned self;
+  unsigned data;
+  unsigned element;
+};
+
+int data;
+int array[4];
+void func(void) {}
+
+// CHECK: @locator = {{.*}}constant %struct.locator { i32 1, i32 trunc (i64 
sub (i64 ptrtoint (ptr @locator to i64), i64 ptrtoint (ptr @__ImageBase to 
i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (ptr @data to i64), i64 
ptrtoint (ptr @__ImageBase to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint 
(ptr getelementptr {{.*}}(i8, ptr @array, i64 8) to i64), i64 ptrtoint (ptr 
@__ImageBase to i64)) to i32) }
+// CHECK: @__ImageBase = external {{(dso_local )?}}constant i8
+const struct locator locator = {
+  1, __builtin_imgrel32(&locator), __builtin_imgrel32(&data),
+  __builtin_imgrel32(&array[2]),
+};
+
+// CHECK: @func_rva = {{.*}}constant i32 trunc (i64 sub (i64 ptrtoint (ptr 
@func to i64), i64 ptrtoint (ptr @__ImageBase to i64)) to i32)
+const unsigned func_rva = __builtin_imgrel32(func);
+
+// It also works outside a constant initializer.
+// CHECK-LABEL: define {{.*}}i32 @func_offset(
+// CHECK: ret i32 trunc (i64 sub (i64 ptrtoint (ptr @func_offset to i64), i64 
ptrtoint (ptr @__ImageBase to i64)) to i32)
+unsigned func_offset(void) { return __builtin_imgrel32(func_offset); }
+
+// CHECK-LABEL: define {{.*}}i32 @data_offset(
+// CHECK: ret i32 trunc (i64 sub (i64 ptrtoint (ptr getelementptr {{.*}}(i8, 
ptr @array, i64 4) to i64), i64 ptrtoint (ptr @__ImageBase to i64)) to i32)
+unsigned data_offset(void) { return __builtin_imgrel32(&array[1]); }
diff --git a/clang/test/Sema/builtin-imgrel32.c 
b/clang/test/Sema/builtin-imgrel32.c
new file mode 100644
index 00000000000000..819ea6095d9b17
--- /dev/null
+++ b/clang/test/Sema/builtin-imgrel32.c
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple x86_64-w64-mingw32 -fdeclspec -fsyntax-only 
-verify=coff %s
+// RUN: %clang_cc1 -triple x86_64-w64-mingw32 -fdeclspec 
-fexperimental-new-constant-interpreter -fsyntax-only -verify=coff %s
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fdeclspec -fsyntax-only 
-verify=coff %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fdeclspec -fsyntax-only 
-verify=elf %s
+
+int data;
+__declspec(dllimport) int imported;
+// elf-warning@-1 {{__declspec attribute 'dllimport' is not supported}}
+_Thread_local int tls;
+
+void test(int *p) {
+  int local;
+  unsigned a = __builtin_imgrel32(&data);
+  // elf-error@-1 {{'__builtin_imgrel32' is only supported on COFF targets}}
+  unsigned b = __builtin_imgrel32(p);
+  // coff-error@-1 {{argument to '__builtin_imgrel32' must be the address of a 
global variable or function}}
+  // elf-error@-2 {{'__builtin_imgrel32' is only supported on COFF targets}}
+  unsigned d = __builtin_imgrel32(&local);
+  // coff-error@-1 {{argument to '__builtin_imgrel32' must be the address of a 
global variable or function}}
+  // elf-error@-2 {{'__builtin_imgrel32' is only supported on COFF targets}}
+  unsigned e = __builtin_imgrel32(&imported);
+  // coff-error@-1 {{argument to '__builtin_imgrel32' must be the address of a 
global variable or function}}
+  // elf-error@-2 {{'__builtin_imgrel32' is only supported on COFF targets}}
+  unsigned f = __builtin_imgrel32(&tls);
+  // coff-error@-1 {{argument to '__builtin_imgrel32' must be the address of a 
global variable or function}}
+  // elf-error@-2 {{'__builtin_imgrel32' is only supported on COFF targets}}
+  unsigned c = __builtin_imgrel32();
+  // coff-error@-1 {{too few arguments to function call, expected 1, have 0}}
+  // elf-error@-2 {{too few arguments to function call, expected 1, have 0}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to