https://github.com/yxsamliu updated https://github.com/llvm/llvm-project/pull/215390
>From 93f48f6a3ea32b535c1453868f4a5b75eaa42dfd Mon Sep 17 00:00:00 2001 From: "Yaxun (Sam) Liu" <[email protected]> Date: Mon, 10 Aug 2026 14:59:39 -0400 Subject: [PATCH] [Offload] Keep empty COFF offload entry ranges alive A Windows offload link can have no real offload entries. One example is a HIP program, or a HIP-related host-only object, built with RDC but with no kernels or registered device globals. The wrapper still emits registration code that refers to the offload entry range. On COFF this range is built from ordered sections. Empty start and stop sections let `lld-link /opt:ref` discard them. The registration code then has relocations against discarded `__start_llvm_offload_entries` and `__stop_llvm_offload_entries` symbols, and the link fails. Linux avoids this through the ELF section-retention path. The wrapper emits a dummy `llvm_offload_entries` section entry and places it in `llvm.used`, which gives the section a retain flag in the ELF object. That keeps the section alive under `--gc-sections`, so the linker can still synthesize the `__start` and `__stop` symbols. The same fix does not map to COFF. COFF does not use ELF-style linker-synthesized section boundary symbols for this range. The wrapper instead creates explicit start and stop symbols in ordered sections, and `/opt:ref` can still discard those symbols if their sections are empty. Putting a zero-sized COFF boundary global in a used list is not enough to keep the relocation targets alive. Fix the COFF case by emitting one zero-initialized sentinel entry in each boundary section. The start sentinel is not a real offload entry, so use `start + 1` as the begin pointer passed to the registration code. This follows the Windows profile runtime pattern for `$A` and `$Z` section sentinels, where the begin accessor returns `Start + 1` and the sentinels are aligned to match real records. The COFF wrapper does this unconditionally. Real offload entries may live in other input objects, so this helper cannot reliably tell whether the final linked range is empty. Always emitting boundary sentinels keeps the fix local and avoids duplicating linker input selection logic. The cost is two hidden boundary records in the COFF wrapper, and neither is included in the runtime entry range. ELF and Mach-O keep their existing behavior. --- .../linker-wrapper-image.c | 24 ++--- .../llvm/Frontend/Offloading/OffloadWrapper.h | 14 +-- .../llvm/Frontend/Offloading/Utility.h | 7 +- llvm/lib/Frontend/Offloading/Utility.cpp | 31 ++++--- .../llvm-offload-wrapper/coff-opt-ref.ll | 89 +++++++++++++++++++ .../tools/llvm-offload-wrapper/lit.local.cfg | 4 + .../llvm-offload-wrapper/offload-wrapper.ll | 8 ++ 7 files changed, 144 insertions(+), 33 deletions(-) create mode 100644 llvm/test/tools/llvm-offload-wrapper/coff-opt-ref.ll create mode 100644 llvm/test/tools/llvm-offload-wrapper/lit.local.cfg diff --git a/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper-image.c b/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper-image.c index d05e9d54a108a..1f6cc9cc32e8e 100644 --- a/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper-image.c +++ b/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper-image.c @@ -19,14 +19,14 @@ // OPENMP-ELF-NEXT: @__stop_llvm_offload_entries = external hidden constant [0 x %struct.__tgt_offload_entry] // OPENMP-ELF-NEXT: @__dummy.llvm_offload_entries = internal constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries" -// OPENMP-COFF: @__start_llvm_offload_entries = weak_odr hidden constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OA" -// OPENMP-COFF-NEXT: @__stop_llvm_offload_entries = weak_odr hidden constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OZ" +// OPENMP-COFF: @__start_llvm_offload_entries = weak_odr hidden constant [1 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OA" +// OPENMP-COFF-NEXT: @__stop_llvm_offload_entries = weak_odr hidden constant [1 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OZ" // OPENMP-REL: @.omp_offloading.device_image = internal unnamed_addr constant [[[SIZE:[0-9]+]] x i8] c"\10\FF\10\AD{{.*}}", section ".llvm.offloading.relocatable", align 8 // OPENMP: @.omp_offloading.device_image = internal unnamed_addr constant [[[SIZE:[0-9]+]] x i8] c"\10\FF\10\AD{{.*}}", section ".llvm.offloading", align 8 -// OPENMP-NEXT: @.omp_offloading.device_images = internal unnamed_addr constant [1 x %__tgt_device_image] [%__tgt_device_image { ptr getelementptr ([[[IMG_OFF:[0-9]+]] x i8], ptr @.omp_offloading.device_image, i64 0, i64 [[IMG_OFF]]), ptr getelementptr ([[[IMG_OFF]] x i8], ptr @.omp_offloading.device_image, i64 0, i64 [[IMG_OFF]]), ptr @__start_llvm_offload_entries, ptr @__stop_llvm_offload_entries }] -// OPENMP-NEXT: @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 1, ptr @.omp_offloading.device_images, ptr @__start_llvm_offload_entries, ptr @__stop_llvm_offload_entries } +// OPENMP-NEXT: @.omp_offloading.device_images = internal unnamed_addr constant [1 x %__tgt_device_image] [%__tgt_device_image { ptr getelementptr ([[[IMG_OFF:[0-9]+]] x i8], ptr @.omp_offloading.device_image, i64 0, i64 [[IMG_OFF]]), ptr getelementptr ([[[IMG_OFF]] x i8], ptr @.omp_offloading.device_image, i64 0, i64 [[IMG_OFF]]), ptr {{(@__start_llvm_offload_entries|getelementptr inbounds \(\[1 x %struct.__tgt_offload_entry\], ptr @__start_llvm_offload_entries, i32 0, i32 1\))}}, ptr @__stop_llvm_offload_entries }] +// OPENMP-NEXT: @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 1, ptr @.omp_offloading.device_images, ptr {{(@__start_llvm_offload_entries|getelementptr inbounds \(\[1 x %struct.__tgt_offload_entry\], ptr @__start_llvm_offload_entries, i32 0, i32 1\))}}, ptr @__stop_llvm_offload_entries } // OPENMP-NEXT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @.omp_offloading.descriptor_reg, ptr null }] // OPENMP: define internal void @.omp_offloading.descriptor_reg() section ".text.startup" { @@ -56,8 +56,8 @@ // CUDA-ELF-NEXT: @__stop_llvm_offload_entries = external hidden constant [0 x %struct.__tgt_offload_entry] // CUDA-ELF-NEXT: @__dummy.llvm_offload_entries = internal constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries" -// CUDA-COFF: @__start_llvm_offload_entries = weak_odr hidden constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OA" -// CUDA-COFF-NEXT: @__stop_llvm_offload_entries = weak_odr hidden constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OZ" +// CUDA-COFF: @__start_llvm_offload_entries = weak_odr hidden constant [1 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OA" +// CUDA-COFF-NEXT: @__stop_llvm_offload_entries = weak_odr hidden constant [1 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OZ" // CUDA: @.fatbin_image = internal constant // CUDA-SAME: section ".nv_fatbin" @@ -90,11 +90,11 @@ // // CUDA: define internal void @.cuda.globals_reg(ptr %0) section ".text.startup" { // CUDA-NEXT: entry: -// CUDA-NEXT: %1 = icmp ne ptr @__start_llvm_offload_entries, @__stop_llvm_offload_entries +// CUDA-NEXT: %1 = icmp ne ptr {{(@__start_llvm_offload_entries|getelementptr inbounds \(\[1 x %struct.__tgt_offload_entry\], ptr @__start_llvm_offload_entries, i32 0, i32 1\))}}, @__stop_llvm_offload_entries // CUDA-NEXT: br i1 %1, label %while.entry, label %while.end // // CUDA: while.entry: -// CUDA-NEXT: %entry1 = phi ptr [ @__start_llvm_offload_entries, %entry ], [ %16, %if.end ] +// CUDA-NEXT: %entry1 = phi ptr [ {{(@__start_llvm_offload_entries|getelementptr inbounds \(\[1 x %struct.__tgt_offload_entry\], ptr @__start_llvm_offload_entries, i32 0, i32 1\))}}, %entry ], [ %16, %if.end ] // CUDA-NEXT: %2 = getelementptr inbounds %struct.__tgt_offload_entry, ptr %entry1, i32 0, i32 4 // CUDA-NEXT: %addr = load ptr, ptr %2, align 8 // CUDA-NEXT: %3 = getelementptr inbounds %struct.__tgt_offload_entry, ptr %entry1, i32 0, i32 8 @@ -175,8 +175,8 @@ // HIP-ELF-NEXT: @__stop_llvm_offload_entries = external hidden constant [0 x %struct.__tgt_offload_entry] // HIP-ELF-NEXT: @__dummy.llvm_offload_entries = internal constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries" -// HIP-COFF: @__start_llvm_offload_entries = weak_odr hidden constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OA" -// HIP-COFF-NEXT: @__stop_llvm_offload_entries = weak_odr hidden constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OZ" +// HIP-COFF: @__start_llvm_offload_entries = weak_odr hidden constant [1 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OA" +// HIP-COFF-NEXT: @__stop_llvm_offload_entries = weak_odr hidden constant [1 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OZ" // HIP-MACHO: @"\01section$start$__LLVM$offload_entries" = external hidden constant [0 x %struct.__tgt_offload_entry] // HIP-MACHO-NEXT: @"\01section$end$__LLVM$offload_entries" = external hidden constant [0 x %struct.__tgt_offload_entry] @@ -232,11 +232,11 @@ // // HIP: define internal void @.hip.globals_reg(ptr %0) section "{{\.text\.startup|__TEXT,__StaticInit}}" { // HIP-NEXT: entry: -// HIP-NEXT: %1 = icmp ne ptr @{{.*offload_entries.*}}, @{{.*offload_entries.*}} +// HIP-NEXT: %1 = icmp ne ptr {{(@.*offload_entries.*|getelementptr inbounds \(\[1 x %struct.__tgt_offload_entry\], ptr @__start_llvm_offload_entries, i32 0, i32 1\))}}, @{{.*offload_entries.*}} // HIP-NEXT: br i1 %1, label %while.entry, label %while.end // // HIP: while.entry: -// HIP-NEXT: %entry1 = phi ptr [ @{{.*offload_entries.*}}, %entry ], [ %16, %if.end ] +// HIP-NEXT: %entry1 = phi ptr [ {{(@.*offload_entries.*|getelementptr inbounds \(\[1 x %struct.__tgt_offload_entry\], ptr @__start_llvm_offload_entries, i32 0, i32 1\))}}, %entry ], [ %16, %if.end ] // HIP-NEXT: %2 = getelementptr inbounds %struct.__tgt_offload_entry, ptr %entry1, i32 0, i32 4 // HIP-NEXT: %addr = load ptr, ptr %2, align 8 // HIP-NEXT: %3 = getelementptr inbounds %struct.__tgt_offload_entry, ptr %entry1, i32 0, i32 8 diff --git a/llvm/include/llvm/Frontend/Offloading/OffloadWrapper.h b/llvm/include/llvm/Frontend/Offloading/OffloadWrapper.h index dc99e29964afc..cb2e793eb2c67 100644 --- a/llvm/include/llvm/Frontend/Offloading/OffloadWrapper.h +++ b/llvm/include/llvm/Frontend/Offloading/OffloadWrapper.h @@ -17,11 +17,11 @@ namespace llvm { namespace offloading { -using EntryArrayTy = std::pair<GlobalVariable *, GlobalVariable *>; +using EntryArrayTy = std::pair<Constant *, Constant *>; /// Wraps the input device images into the module \p M as global symbols and /// registers the images with the OpenMP Offloading runtime libomptarget. -/// \param EntryArray Optional pair pointing to the `__start` and `__stop` -/// symbols holding the `__tgt_offload_entry` array. +/// \param EntryArray Optional pair pointing to the begin and end of the +/// `__tgt_offload_entry` array. /// \param Suffix An optional suffix appended to the emitted symbols. /// \param Relocatable Indicate if we need to change the offloading section to /// create a relocatable object. @@ -32,8 +32,8 @@ wrapOpenMPBinaries(llvm::Module &M, llvm::ArrayRef<llvm::ArrayRef<char>> Images, /// Wraps the input fatbinary image into the module \p M as global symbols and /// registers the images with the CUDA runtime. -/// \param EntryArray Optional pair pointing to the `__start` and `__stop` -/// symbols holding the `__tgt_offload_entry` array. +/// \param EntryArray Optional pair pointing to the begin and end of the +/// `__tgt_offload_entry` array. /// \param Suffix An optional suffix appended to the emitted symbols. /// \param EmitSurfacesAndTextures Whether to emit surface and textures /// registration code. It defaults to false. @@ -45,8 +45,8 @@ LLVM_ABI llvm::Error wrapCudaBinary(llvm::Module &M, /// Wraps the input bundled image into the module \p M as global symbols and /// registers the images with the HIP runtime. -/// \param EntryArray Optional pair pointing to the `__start` and `__stop` -/// symbols holding the `__tgt_offload_entry` array. +/// \param EntryArray Optional pair pointing to the begin and end of the +/// `__tgt_offload_entry` array. /// \param Suffix An optional suffix appended to the emitted symbols. /// \param EmitSurfacesAndTextures Whether to emit surface and textures /// registration code. It defaults to false. diff --git a/llvm/include/llvm/Frontend/Offloading/Utility.h b/llvm/include/llvm/Frontend/Offloading/Utility.h index 4c0bc87786dfb..4ff7eaaed0b30 100644 --- a/llvm/include/llvm/Frontend/Offloading/Utility.h +++ b/llvm/include/llvm/Frontend/Offloading/Utility.h @@ -104,10 +104,9 @@ getOffloadingEntryInitializer(Module &M, object::OffloadKind Kind, Constant *Addr, StringRef Name, uint64_t Size, uint32_t Flags, uint64_t Data, Constant *AuxAddr); -/// Creates a pair of globals used to iterate the array of offloading entries by -/// accessing the section variables provided by the linker. -LLVM_ABI std::pair<GlobalVariable *, GlobalVariable *> -getOffloadEntryArray(Module &M); +/// Creates a pair of constants used to iterate the array of offloading entries +/// by accessing the section variables provided by the linker. +LLVM_ABI std::pair<Constant *, Constant *> getOffloadEntryArray(Module &M); namespace amdgpu { /// Check if an image is compatible with current system's environment. The diff --git a/llvm/lib/Frontend/Offloading/Utility.cpp b/llvm/lib/Frontend/Offloading/Utility.cpp index 511fd39a956be..3dd8800b8209e 100644 --- a/llvm/lib/Frontend/Offloading/Utility.cpp +++ b/llvm/lib/Frontend/Offloading/Utility.cpp @@ -128,15 +128,14 @@ GlobalVariable *offloading::emitOffloadingEntry( return Entry; } -std::pair<GlobalVariable *, GlobalVariable *> -offloading::getOffloadEntryArray(Module &M) { +std::pair<Constant *, Constant *> offloading::getOffloadEntryArray(Module &M) { const llvm::Triple &Triple = M.getTargetTriple(); StringRef SectionName = getOffloadEntrySection(M); - auto *ZeroInitilaizer = - ConstantAggregateZero::get(ArrayType::get(getEntryTy(M), 0u)); - auto *EntryInit = Triple.isOSBinFormatCOFF() ? ZeroInitilaizer : nullptr; - auto *EntryType = ArrayType::get(getEntryTy(M), 0); + auto *ZeroInitializer = ConstantAggregateZero::get( + ArrayType::get(getEntryTy(M), Triple.isOSBinFormatCOFF() ? 1u : 0u)); + auto *EntryInit = Triple.isOSBinFormatCOFF() ? ZeroInitializer : nullptr; + auto *EntryType = ZeroInitializer->getType(); auto Linkage = Triple.isOSBinFormatCOFF() ? GlobalValue::WeakODRLinkage : GlobalValue::ExternalLinkage; @@ -156,8 +155,8 @@ offloading::getOffloadEntryArray(Module &M) { // valid C-identifier is present. We define a dummy variable here to force // the linker to always provide these symbols. auto *DummyEntry = new GlobalVariable( - M, ZeroInitilaizer->getType(), true, GlobalVariable::InternalLinkage, - ZeroInitilaizer, "__dummy." + SectionName); + M, ZeroInitializer->getType(), true, GlobalVariable::InternalLinkage, + ZeroInitializer, "__dummy." + SectionName); DummyEntry->setSection(SectionName); DummyEntry->setAlignment(Align(object::OffloadBinary::getAlignment())); appendToUsed(M, DummyEntry); @@ -166,8 +165,8 @@ offloading::getOffloadEntryArray(Module &M) { // linker provides the section boundary symbols. Mark it used so the // section survives dead-stripping. auto *DummyEntry = new GlobalVariable( - M, ZeroInitilaizer->getType(), true, GlobalVariable::InternalLinkage, - ZeroInitilaizer, "__dummy." + SectionName); + M, ZeroInitializer->getType(), true, GlobalVariable::InternalLinkage, + ZeroInitializer, "__dummy." + SectionName); DummyEntry->setSection(SectionName); DummyEntry->setAlignment(Align(object::OffloadBinary::getAlignment())); appendToUsed(M, DummyEntry); @@ -178,6 +177,18 @@ offloading::getOffloadEntryArray(Module &M) { // sections here to ensure that the beginning and end symbols are sorted. EntriesB->setSection((SectionName + "$OA").str()); EntriesE->setSection((SectionName + "$OZ").str()); + EntriesB->setAlignment(Align(object::OffloadBinary::getAlignment())); + EntriesE->setAlignment(Align(object::OffloadBinary::getAlignment())); + + // Keep COFF boundary sections non-empty so lld-link does not discard them + // under /opt:ref. The start entry is synthetic, so skip it for runtime + // users. + Type *Int32Ty = Type::getInt32Ty(M.getContext()); + Constant *Indices[] = {ConstantInt::get(Int32Ty, 0), + ConstantInt::get(Int32Ty, 1)}; + Constant *BeginAfterSentinel = ConstantExpr::getInBoundsGetElementPtr( + EntriesB->getValueType(), EntriesB, Indices); + return std::make_pair(BeginAfterSentinel, EntriesE); } return std::make_pair(EntriesB, EntriesE); diff --git a/llvm/test/tools/llvm-offload-wrapper/coff-opt-ref.ll b/llvm/test/tools/llvm-offload-wrapper/coff-opt-ref.ll new file mode 100644 index 0000000000000..4f1413ce452a5 --- /dev/null +++ b/llvm/test/tools/llvm-offload-wrapper/coff-opt-ref.ll @@ -0,0 +1,89 @@ +; REQUIRES: x86-registered-target +; REQUIRES: lld + +; RUN: rm -rf %t && split-file %s %t +; RUN: llvm-offload-wrapper --triple=x86_64-pc-windows-msvc -kind=hip \ +; RUN: %t/image.bin -o %t/wrapper.bc +; RUN: llvm-dis %t/wrapper.bc -o - | FileCheck %s +; RUN: llc -filetype=obj %t/wrapper.bc -o %t/wrapper.obj +; RUN: llvm-readobj --sections %t/wrapper.obj | FileCheck %s --check-prefix=OBJ +; RUN: llc -filetype=obj %t/stubs.ll -o %t/stubs.obj +; RUN: lld-link /dll /noentry /nodefaultlib /opt:ref /map:%t/wrapper.map \ +; RUN: /out:%t/wrapper.dll %t/wrapper.obj %t/stubs.obj +; RUN: FileCheck %s --check-prefix=MAP --input-file=%t/wrapper.map + +; CHECK: @__start_llvm_offload_entries = weak_odr hidden constant [1 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OA" +; CHECK-NEXT: @__stop_llvm_offload_entries = weak_odr hidden constant [1 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OZ" +; CHECK: icmp ne ptr getelementptr inbounds ([1 x %struct.__tgt_offload_entry], ptr @__start_llvm_offload_entries, i32 0, i32 1), @__stop_llvm_offload_entries + +; OBJ: Name: llvm_offload_entries{{[$]}}OA +; OBJ: RawDataSize: 56 +; OBJ: IMAGE_SCN_ALIGN_8BYTES +; OBJ: Name: llvm_offload_entries{{[$]}}OZ +; OBJ: RawDataSize: 56 +; OBJ: IMAGE_SCN_ALIGN_8BYTES + +; MAP: {{[0-9A-Fa-f]+}}:00000000 00000038H llvm_offload_entries{{[$]}}OA DATA +; MAP-NEXT: {{[0-9A-Fa-f]+}}:00000038 00000038H llvm_offload_entries{{[$]}}OZ DATA +; MAP: {{[0-9A-Fa-f]+}}:00000000 __start_llvm_offload_entries +; MAP: {{[0-9A-Fa-f]+}}:00000038 __stop_llvm_offload_entries + +;--- image.bin +device image + +;--- stubs.ll +target triple = "x86_64-pc-windows-msvc" + +define ptr @__hipRegisterFatBinary(ptr %x) { +entry: + ret ptr %x +} + +define void @__cudaRegisterFatBinaryEnd(ptr %x) { +entry: + ret void +} + +define void @__hipUnregisterFatBinary(ptr %x) { +entry: + ret void +} + +define i32 @atexit(ptr %f) { +entry: + ret i32 0 +} + +define i32 @__hipRegisterFunction(ptr %handle, ptr %addr, ptr %name, + ptr %name2, i32 %thread_limit, ptr %tid, + ptr %bid, ptr %bDim, ptr %gDim, + ptr %wSize) { +entry: + ret i32 0 +} + +define void @__hipRegisterVar(ptr %handle, ptr %addr, ptr %name, ptr %name2, + i32 %ext, i64 %size, i32 %constant, + i32 %global) { +entry: + ret void +} + +define void @__hipRegisterManagedVar(ptr %handle, ptr %aux, ptr %addr, + ptr %name, i64 %size, i32 %flags) { +entry: + ret void +} + +define void @__hipRegisterSurface(ptr %handle, ptr %addr, ptr %name, + ptr %name2, i32 %dim, i32 %ext) { +entry: + ret void +} + +define void @__hipRegisterTexture(ptr %handle, ptr %addr, ptr %name, + ptr %name2, i32 %dim, i32 %normalized, + i32 %ext) { +entry: + ret void +} diff --git a/llvm/test/tools/llvm-offload-wrapper/lit.local.cfg b/llvm/test/tools/llvm-offload-wrapper/lit.local.cfg new file mode 100644 index 0000000000000..21771693b720e --- /dev/null +++ b/llvm/test/tools/llvm-offload-wrapper/lit.local.cfg @@ -0,0 +1,4 @@ +from lit.llvm import llvm_config + +if llvm_config.use_lld(required=False): + config.available_features.add("lld") diff --git a/llvm/test/tools/llvm-offload-wrapper/offload-wrapper.ll b/llvm/test/tools/llvm-offload-wrapper/offload-wrapper.ll index f58782f8ecd50..cc053642761e1 100644 --- a/llvm/test/tools/llvm-offload-wrapper/offload-wrapper.ll +++ b/llvm/test/tools/llvm-offload-wrapper/offload-wrapper.ll @@ -56,6 +56,14 @@ ; HIP-NEXT: ret void ; HIP-NEXT: } +; RUN: llvm-offload-wrapper --triple=x86_64-pc-windows-msvc -kind=hip %s -o %t.coff.bc +; RUN: llvm-dis %t.coff.bc -o - | FileCheck %s --check-prefix=HIP-COFF + +; HIP-COFF: @__start_llvm_offload_entries = weak_odr hidden constant [1 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OA" +; HIP-COFF-NEXT: @__stop_llvm_offload_entries = weak_odr hidden constant [1 x %struct.__tgt_offload_entry] zeroinitializer, section "llvm_offload_entries$OZ" +; HIP-COFF: icmp ne ptr getelementptr inbounds ([1 x %struct.__tgt_offload_entry], ptr @__start_llvm_offload_entries, i32 0, i32 1), @__stop_llvm_offload_entries +; HIP-COFF: phi ptr [ getelementptr inbounds ([1 x %struct.__tgt_offload_entry], ptr @__start_llvm_offload_entries, i32 0, i32 1), %entry ] + ; RUN: llvm-offload-wrapper --triple=x86_64-apple-macosx10.15.0 -kind=hip %s -o %t.bc ; RUN: llvm-dis %t.bc -o - | FileCheck %s --check-prefix=HIP-MACHO _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
