[llvm] WholeProgramDevirt: Always import/export constants directly in the summary (PR #188979)

via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 27 05:17:56 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lto

Author: Hans Wennborg (zmodem)

<details>
<summary>Changes</summary>

rather than using absolute symbol constants on ELF/x86.

This leads to better codegen as the absolute symbol constants were not resolved until link time (see bug for example).

It also simplifies the code by having a single code path for all targets.

Fixes #<!-- -->188470

---
Full diff: https://github.com/llvm/llvm-project/pull/188979.diff


3 Files Affected:

- (modified) llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp (+4-64) 
- (modified) llvm/test/Transforms/WholeProgramDevirt/export-vcp.ll (+9-17) 
- (modified) llvm/test/Transforms/WholeProgramDevirt/import.ll (+7-21) 


``````````diff
diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
index 019b5ee6ac2f9..c19856f0b2daa 100644
--- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
+++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
@@ -696,23 +696,16 @@ struct DevirtModule {
   std::string getGlobalName(VTableSlot Slot, ArrayRef<uint64_t> Args,
                             StringRef Name);
 
-  bool shouldExportConstantsAsAbsoluteSymbols();
-
   // This function is called during the export phase to create a symbol
   // definition containing information about the given vtable slot and list of
   // arguments.
   void exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args, StringRef Name,
                     Constant *C);
-  void exportConstant(VTableSlot Slot, ArrayRef<uint64_t> Args, StringRef Name,
-                      uint32_t Const, uint32_t &Storage);
 
   // This function is called during the import phase to create a reference to
   // the symbol definition created during the export phase.
   Constant *importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
                          StringRef Name);
-  Constant *importConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
-                           StringRef Name, IntegerType *IntTy,
-                           uint32_t Storage);
 
   Constant *getMemberAddr(const TypeMemberInfo *M);
 
@@ -1735,11 +1728,6 @@ std::string DevirtModule::getGlobalName(VTableSlot Slot,
   return FullName;
 }
 
-bool DevirtModule::shouldExportConstantsAsAbsoluteSymbols() {
-  Triple T(M.getTargetTriple());
-  return T.isX86() && T.getObjectFormat() == Triple::ELF;
-}
-
 void DevirtModule::exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
                                 StringRef Name, Constant *C) {
   GlobalAlias *GA = GlobalAlias::create(Int8Ty, 0, GlobalValue::ExternalLinkage,
@@ -1747,19 +1735,6 @@ void DevirtModule::exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
   GA->setVisibility(GlobalValue::HiddenVisibility);
 }
 
-void DevirtModule::exportConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
-                                  StringRef Name, uint32_t Const,
-                                  uint32_t &Storage) {
-  if (shouldExportConstantsAsAbsoluteSymbols()) {
-    exportGlobal(
-        Slot, Args, Name,
-        ConstantExpr::getIntToPtr(ConstantInt::get(Int32Ty, Const), Int8PtrTy));
-    return;
-  }
-
-  Storage = Const;
-}
-
 Constant *DevirtModule::importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
                                      StringRef Name) {
   GlobalVariable *GV =
@@ -1768,37 +1743,6 @@ Constant *DevirtModule::importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
   return GV;
 }
 
-Constant *DevirtModule::importConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
-                                       StringRef Name, IntegerType *IntTy,
-                                       uint32_t Storage) {
-  if (!shouldExportConstantsAsAbsoluteSymbols())
-    return ConstantInt::get(IntTy, Storage);
-
-  Constant *C = importGlobal(Slot, Args, Name);
-  auto *GV = cast<GlobalVariable>(C->stripPointerCasts());
-  C = ConstantExpr::getPtrToInt(C, IntTy);
-
-  // We only need to set metadata if the global is newly created, in which
-  // case it would not have hidden visibility.
-  if (GV->hasMetadata(LLVMContext::MD_absolute_symbol))
-    return C;
-
-  auto SetAbsRange = [&](uint64_t Min, uint64_t Max) {
-    auto *MinC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Min));
-    auto *MaxC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Max));
-    GV->setMetadata(LLVMContext::MD_absolute_symbol,
-                    MDNode::get(M.getContext(), {MinC, MaxC}));
-  };
-  unsigned AbsWidth = IntTy->getBitWidth();
-  if (AbsWidth == IntPtrTy->getBitWidth()) {
-    uint64_t AllOnes = IntTy->getBitMask();
-    SetAbsRange(AllOnes, AllOnes); // Full set.
-  } else {
-    SetAbsRange(0, 1ull << AbsWidth);
-  }
-  return C;
-}
-
 void DevirtModule::applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName,
                                         bool IsOne,
                                         Constant *UniqueMemberAddr) {
@@ -2026,10 +1970,8 @@ bool DevirtModule::tryVirtualConstProp(
 
     if (CSByConstantArg.second.isExported()) {
       ResByArg->TheKind = WholeProgramDevirtResolution::ByArg::VirtualConstProp;
-      exportConstant(Slot, CSByConstantArg.first, "byte", OffsetByte,
-                     ResByArg->Byte);
-      exportConstant(Slot, CSByConstantArg.first, "bit", 1ULL << OffsetBit,
-                     ResByArg->Bit);
+      ResByArg->Byte = OffsetByte;
+      ResByArg->Bit = 1ULL << OffsetBit;
     }
 
     // Rewrite each call to a load from OffsetByte/OffsetBit.
@@ -2309,10 +2251,8 @@ void DevirtModule::importResolution(VTableSlot Slot, VTableSlotInfo &SlotInfo) {
       break;
     }
     case WholeProgramDevirtResolution::ByArg::VirtualConstProp: {
-      Constant *Byte = importConstant(Slot, CSByConstantArg.first, "byte",
-                                      Int32Ty, ResByArg.Byte);
-      Constant *Bit = importConstant(Slot, CSByConstantArg.first, "bit", Int8Ty,
-                                     ResByArg.Bit);
+      Constant *Byte = ConstantInt::get(Int32Ty, ResByArg.Byte);
+      Constant *Bit = ConstantInt::get(Int8Ty, ResByArg.Bit);
       applyVirtualConstProp(CSByConstantArg.second, "", Byte, Bit);
       break;
     }
diff --git a/llvm/test/Transforms/WholeProgramDevirt/export-vcp.ll b/llvm/test/Transforms/WholeProgramDevirt/export-vcp.ll
index 24aba548ea9ac..cc02a0668a2f1 100644
--- a/llvm/test/Transforms/WholeProgramDevirt/export-vcp.ll
+++ b/llvm/test/Transforms/WholeProgramDevirt/export-vcp.ll
@@ -1,8 +1,8 @@
-; RUN: opt -mtriple=x86_64-unknown-linux-gnu -passes=wholeprogramdevirt -whole-program-visibility -wholeprogramdevirt-summary-action=export -wholeprogramdevirt-read-summary=%S/Inputs/export.yaml -wholeprogramdevirt-write-summary=%t -S -o - %s | FileCheck --check-prefixes=CHECK,X86 %s
-; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-X86 %s < %t
+; RUN: opt -mtriple=x86_64-unknown-linux-gnu -passes=wholeprogramdevirt -whole-program-visibility -wholeprogramdevirt-summary-action=export -wholeprogramdevirt-read-summary=%S/Inputs/export.yaml -wholeprogramdevirt-write-summary=%t -S -o - %s | FileCheck --check-prefixes=CHECK %s
+; RUN: FileCheck --check-prefixes=SUMMARY %s < %t
 
-; RUN: opt -mtriple=armv7-unknown-linux-gnu -passes=wholeprogramdevirt -whole-program-visibility -wholeprogramdevirt-summary-action=export -wholeprogramdevirt-read-summary=%S/Inputs/export.yaml -wholeprogramdevirt-write-summary=%t -S -o - %s | FileCheck --check-prefixes=CHECK,ARM %s
-; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-ARM %s < %t
+; RUN: opt -mtriple=armv7-unknown-linux-gnu -passes=wholeprogramdevirt -whole-program-visibility -wholeprogramdevirt-summary-action=export -wholeprogramdevirt-read-summary=%S/Inputs/export.yaml -wholeprogramdevirt-write-summary=%t -S -o - %s | FileCheck --check-prefixes=CHECK %s
+; RUN: FileCheck --check-prefixes=SUMMARY %s < %t
 
 target datalayout = "e-p:64:64"
 
@@ -23,10 +23,8 @@ target datalayout = "e-p:64:64"
 ; SUMMARY-NEXT:           12,24:
 ; SUMMARY-NEXT:             Kind:            VirtualConstProp
 ; SUMMARY-NEXT:             Info:            0
-; SUMMARY-X86-NEXT:         Byte:            0
-; SUMMARY-X86-NEXT:         Bit:             0
-; SUMMARY-ARM-NEXT:         Byte:            4294967295
-; SUMMARY-ARM-NEXT:         Bit:             1
+; SUMMARY-NEXT:             Byte:            4294967295
+; SUMMARY-NEXT:             Bit:             1
 ; SUMMARY-NEXT:   typeid4:
 ; SUMMARY-NEXT:     TTRes:
 ; SUMMARY-NEXT:       Kind:            Unknown
@@ -43,10 +41,8 @@ target datalayout = "e-p:64:64"
 ; SUMMARY-NEXT:           24,12:
 ; SUMMARY-NEXT:             Kind:            VirtualConstProp
 ; SUMMARY-NEXT:             Info:            0
-; SUMMARY-X86-NEXT:         Byte:            0
-; SUMMARY-X86-NEXT:         Bit:             0
-; SUMMARY-ARM-NEXT:         Byte:            4294967292
-; SUMMARY-ARM-NEXT:         Bit:             1
+; SUMMARY-NEXT:             Byte:            4294967292
+; SUMMARY-NEXT:             Bit:             1
 
 ; CHECK: [[CVT3A:.*]] = private constant { [8 x i8], ptr, [0 x i8] } { [8 x i8] zeroinitializer, ptr @vf0i1, [0 x i8] zeroinitializer }, !type !0
 @vt3a = constant ptr @vf0i1, !type !0
@@ -66,11 +62,7 @@ target datalayout = "e-p:64:64"
 ; CHECK: [[CVT4B:.*]] = private constant { [8 x i8], ptr, [0 x i8] } { [8 x i8] c"\00\00\00\00\02\00\00\00", ptr @vf2i32, [0 x i8] zeroinitializer }, !type !1
 @vt4b = constant ptr @vf2i32, !type !1
 
-; X86: @__typeid_typeid3_0_12_24_byte = hidden alias i8, inttoptr (i32 -1 to ptr)
-; X86: @__typeid_typeid3_0_12_24_bit = hidden alias i8, inttoptr (i32 1 to ptr)
-; X86: @__typeid_typeid4_0_24_12_byte = hidden alias i8, inttoptr (i32 -4 to ptr)
-; X86: @__typeid_typeid4_0_24_12_bit = hidden alias i8, inttoptr (i32 1 to ptr)
-; ARM-NOT: alias {{.*}} inttoptr
+; CHECK-NOT: alias {{.*}} inttoptr
 
 ; CHECK: @vt3a = alias ptr, getelementptr inbounds ({ [8 x i8], ptr, [0 x i8] }, ptr [[CVT3A]], i32 0, i32 1)
 ; CHECK: @vt3b = alias ptr, getelementptr inbounds ({ [8 x i8], ptr, [0 x i8] }, ptr [[CVT3B]], i32 0, i32 1)
diff --git a/llvm/test/Transforms/WholeProgramDevirt/import.ll b/llvm/test/Transforms/WholeProgramDevirt/import.ll
index 812ffbdf7f3fb..7f843c87e5098 100644
--- a/llvm/test/Transforms/WholeProgramDevirt/import.ll
+++ b/llvm/test/Transforms/WholeProgramDevirt/import.ll
@@ -2,20 +2,15 @@
 ; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-uniform-ret-val.yaml < %s | FileCheck --check-prefixes=CHECK,INDIR,UNIFORM-RET-VAL %s
 ; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-unique-ret-val0.yaml < %s | FileCheck --check-prefixes=CHECK,INDIR,UNIQUE-RET-VAL0 %s
 ; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-unique-ret-val1.yaml < %s | FileCheck --check-prefixes=CHECK,INDIR,UNIQUE-RET-VAL1 %s
-; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-vcp.yaml < %s | FileCheck --check-prefixes=CHECK,VCP,VCP-X86,VCP64,INDIR %s
-; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-vcp.yaml -mtriple=i686-unknown-linux -data-layout=e-p:32:32 < %s | FileCheck --check-prefixes=CHECK,VCP,VCP-X86,VCP32 %s
-; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-vcp.yaml -mtriple=armv7-unknown-linux -data-layout=e-p:32:32 < %s | FileCheck --check-prefixes=CHECK,VCP,VCP-ARM %s
-; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-vcp-branch-funnel.yaml < %s | FileCheck --check-prefixes=CHECK,VCP,VCP-X86,VCP64,BRANCH-FUNNEL %s
+; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-vcp.yaml < %s | FileCheck --check-prefixes=CHECK,VCP,INDIR %s
+; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-vcp.yaml -mtriple=i686-unknown-linux -data-layout=e-p:32:32 < %s | FileCheck --check-prefixes=CHECK,VCP %s
+; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-vcp.yaml -mtriple=armv7-unknown-linux -data-layout=e-p:32:32 < %s | FileCheck --check-prefixes=CHECK,VCP %s
+; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-vcp-branch-funnel.yaml < %s | FileCheck --check-prefixes=CHECK,VCP,BRANCH-FUNNEL %s
 ; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-branch-funnel.yaml < %s | FileCheck --check-prefixes=CHECK,BRANCH-FUNNEL,BRANCH-FUNNEL-NOVCP %s
 
 target datalayout = "e-p:64:64"
 target triple = "x86_64-unknown-linux-gnu"
 
-; VCP-X86: @__typeid_typeid1_0_1_byte = external hidden global [0 x i8], !absolute_symbol !0
-; VCP-X86: @__typeid_typeid1_0_1_bit = external hidden global [0 x i8], !absolute_symbol !1
-; VCP-X86: @__typeid_typeid2_8_3_byte = external hidden global [0 x i8], !absolute_symbol !0
-; VCP-X86: @__typeid_typeid2_8_3_bit = external hidden global [0 x i8], !absolute_symbol !1
-
 ; Test cases where the argument values are known and we can apply virtual
 ; constant propagation.
 
@@ -28,8 +23,7 @@ define i32 @call1(ptr %obj) #0 {
   ; SINGLE-IMPL: call i32 @singleimpl1
   %result = call i32 %fptr(ptr %obj, i32 1)
   ; UNIFORM-RET-VAL: ret i32 42
-  ; VCP-X86: [[GEP1:%.*]] = getelementptr i8, ptr %vtable, i32 ptrtoint (ptr @__typeid_typeid1_0_1_byte to i32)
-  ; VCP-ARM: [[GEP1:%.*]] = getelementptr i8, ptr %vtable, i32 42
+  ; VCP: [[GEP1:%.*]] = getelementptr i8, ptr %vtable, i32 42
   ; VCP: [[LOAD1:%.*]] = load i32, ptr [[GEP1]]
   ; VCP: ret i32 [[LOAD1]]
   ; BRANCH-FUNNEL-NOVCP: call i32 @__typeid_typeid1_0_branch_funnel(ptr nest %vtable, ptr %obj, i32 1)
@@ -72,11 +66,9 @@ cont:
   %result = call i1 %fptr(ptr %obj, i32 3)
   ; UNIQUE-RET-VAL0: icmp ne ptr %vtable, @__typeid_typeid2_8_3_unique_member
   ; UNIQUE-RET-VAL1: icmp eq ptr %vtable, @__typeid_typeid2_8_3_unique_member
-  ; VCP-X86: [[GEP2:%.*]] = getelementptr i8, ptr %vtable, i32 ptrtoint (ptr @__typeid_typeid2_8_3_byte to i32)
-  ; VCP-ARM: [[GEP2:%.*]] = getelementptr i8, ptr %vtable, i32 43
+  ; VCP: [[GEP2:%.*]] = getelementptr i8, ptr %vtable, i32 43
   ; VCP: [[LOAD2:%.*]] = load i8, ptr [[GEP2]]
-  ; VCP-X86: [[AND2:%.*]] = and i8 [[LOAD2]], ptrtoint (ptr @__typeid_typeid2_8_3_bit to i8)
-  ; VCP-ARM: [[AND2:%.*]] = and i8 [[LOAD2]], -128
+  ; VCP: [[AND2:%.*]] = and i8 [[LOAD2]], -128
   ; VCP: [[ICMP2:%.*]] = icmp ne i8 [[AND2]], 0
   ; VCP: ret i1 [[ICMP2]]
   ; BRANCH-FUNNEL-NOVCP: call i1 @__typeid_typeid2_8_branch_funnel(ptr nest %vtable, ptr %obj, i32 3)
@@ -90,12 +82,6 @@ trap:
 ; SINGLE-IMPL-DAG: declare void @singleimpl1()
 ; SINGLE-IMPL-DAG: declare void @singleimpl2()
 
-; VCP32: !0 = !{i32 -1, i32 -1}
-; VCP64: !0 = !{i64 0, i64 4294967296}
-
-; VCP32: !1 = !{i32 0, i32 256}
-; VCP64: !1 = !{i64 0, i64 256}
-
 declare void @llvm.assume(i1)
 declare void @llvm.trap()
 declare {ptr, i1} @llvm.type.checked.load(ptr, i32, metadata)

``````````

</details>


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


More information about the llvm-commits mailing list