[llvm] edeffec - [llubi] Global variables with simple initializers and global-address constants (#200547)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 2 02:17:10 PDT 2026


Author: Zhige Chen
Date: 2026-06-02T09:17:05Z
New Revision: edeffec62d462e0d162c6bcb41050c9afe192815

URL: https://github.com/llvm/llvm-project/commit/edeffec62d462e0d162c6bcb41050c9afe192815
DIFF: https://github.com/llvm/llvm-project/commit/edeffec62d462e0d162c6bcb41050c9afe192815.diff

LOG: [llubi] Global variables with simple initializers and global-address constants (#200547)

This PR implements global variables with simple initializers and
global-address constants. Support for constant expressions, alias, etc.,
are left for future PRs.

Added: 
    llvm/test/tools/llubi/global.ll
    llvm/test/tools/llubi/global_constant_store.ll
    llvm/test/tools/llubi/global_constexpr_initializer.ll
    llvm/test/tools/llubi/global_external.ll

Modified: 
    llvm/test/tools/llubi/controlflow.ll
    llvm/tools/llubi/lib/Context.cpp
    llvm/tools/llubi/lib/Context.h
    llvm/tools/llubi/lib/ExecutorBase.cpp
    llvm/tools/llubi/lib/Interpreter.cpp
    llvm/tools/llubi/lib/Value.cpp
    llvm/tools/llubi/lib/Value.h
    llvm/tools/llubi/llubi.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llubi/controlflow.ll b/llvm/test/tools/llubi/controlflow.ll
index 7b14d90b688d9..8a00b30013b16 100644
--- a/llvm/test/tools/llubi/controlflow.ll
+++ b/llvm/test/tools/llubi/controlflow.ll
@@ -243,35 +243,35 @@ exit:
 ; CHECK-NEXT:           to label %next5 unwind label %cleanup jump to %next5
 ; CHECK-NEXT:   indirectbr ptr blockaddress(@main, %exit), [label %exit] jump to %exit
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 5
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => T
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.then
 ; CHECK-NEXT:   %sub1 = sub i32 %n, 1 => i32 4
 ; CHECK-NEXT:   %sub2 = sub i32 %n, 2 => i32 3
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 4
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => T
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.then
 ; CHECK-NEXT:   %sub1 = sub i32 %n, 1 => i32 3
 ; CHECK-NEXT:   %sub2 = sub i32 %n, 2 => i32 2
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 3
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => T
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.then
 ; CHECK-NEXT:   %sub1 = sub i32 %n, 1 => i32 2
 ; CHECK-NEXT:   %sub2 = sub i32 %n, 2 => i32 1
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 2
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => T
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.then
 ; CHECK-NEXT:   %sub1 = sub i32 %n, 1 => i32 1
 ; CHECK-NEXT:   %sub2 = sub i32 %n, 2 => i32 0
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 1
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => F
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.else
@@ -279,7 +279,7 @@ exit:
 ; CHECK-NEXT: Exiting function: fib
 ; CHECK-NEXT:   %res1 = call i32 @fib(ptr %self, i32 %sub1) => i32 1
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 0
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => F
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.else
@@ -291,7 +291,7 @@ exit:
 ; CHECK-NEXT: Exiting function: fib
 ; CHECK-NEXT:   %res1 = call i32 @fib(ptr %self, i32 %sub1) => i32 2
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 1
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => F
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.else
@@ -303,14 +303,14 @@ exit:
 ; CHECK-NEXT: Exiting function: fib
 ; CHECK-NEXT:   %res1 = call i32 @fib(ptr %self, i32 %sub1) => i32 3
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 2
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => T
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.then
 ; CHECK-NEXT:   %sub1 = sub i32 %n, 1 => i32 1
 ; CHECK-NEXT:   %sub2 = sub i32 %n, 2 => i32 0
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 1
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => F
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.else
@@ -318,7 +318,7 @@ exit:
 ; CHECK-NEXT: Exiting function: fib
 ; CHECK-NEXT:   %res1 = call i32 @fib(ptr %self, i32 %sub1) => i32 1
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 0
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => F
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.else
@@ -334,21 +334,21 @@ exit:
 ; CHECK-NEXT: Exiting function: fib
 ; CHECK-NEXT:   %res1 = call i32 @fib(ptr %self, i32 %sub1) => i32 5
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 3
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => T
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.then
 ; CHECK-NEXT:   %sub1 = sub i32 %n, 1 => i32 2
 ; CHECK-NEXT:   %sub2 = sub i32 %n, 2 => i32 1
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 2
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => T
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.then
 ; CHECK-NEXT:   %sub1 = sub i32 %n, 1 => i32 1
 ; CHECK-NEXT:   %sub2 = sub i32 %n, 2 => i32 0
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 1
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => F
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.else
@@ -356,7 +356,7 @@ exit:
 ; CHECK-NEXT: Exiting function: fib
 ; CHECK-NEXT:   %res1 = call i32 @fib(ptr %self, i32 %sub1) => i32 1
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 0
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => F
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.else
@@ -368,7 +368,7 @@ exit:
 ; CHECK-NEXT: Exiting function: fib
 ; CHECK-NEXT:   %res1 = call i32 @fib(ptr %self, i32 %sub1) => i32 2
 ; CHECK-NEXT: Entering function: fib
-; CHECK-NEXT:   ptr %self = ptr 0x8 [fib]
+; CHECK-NEXT:   ptr %self = ptr 0x8 [@fib]
 ; CHECK-NEXT:   i32 %n = i32 1
 ; CHECK-NEXT:   %cond = icmp ugt i32 %n, 1 => F
 ; CHECK-NEXT:   br i1 %cond, label %if.then, label %if.else jump to %if.else

diff  --git a/llvm/test/tools/llubi/global.ll b/llvm/test/tools/llubi/global.ll
new file mode 100644
index 0000000000000..3cff71b21769f
--- /dev/null
+++ b/llvm/test/tools/llubi/global.ll
@@ -0,0 +1,25 @@
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+target datalayout = "e-p:64:64:64-i32:32:32"
+
+ at value = global i32 41
+ at value_ptr = global ptr @value
+ at aggregate = global { i32, [2 x i8] } { i32 7, [2 x i8] c"hi" }
+
+define void @main() {
+  %initial = load i32, ptr @value
+  store i32 42, ptr @value
+  %ptr = load ptr, ptr @value_ptr
+  %updated = load i32, ptr %ptr
+  %aggregate_value = load { i32, [2 x i8] }, ptr @aggregate
+  ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT:   %initial = load i32, ptr @value, align 4 => i32 41
+; CHECK-NEXT:   store i32 42, ptr @value, align 4
+; CHECK-NEXT:   %ptr = load ptr, ptr @value_ptr, align 8 => ptr 0x8 [@value]
+; CHECK-NEXT:   %updated = load i32, ptr %ptr, align 4 => i32 42
+; CHECK-NEXT:   %aggregate_value = load { i32, [2 x i8] }, ptr @aggregate, align 4 => { i32 7, { i8 104, i8 105 } }
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main

diff  --git a/llvm/test/tools/llubi/global_constant_store.ll b/llvm/test/tools/llubi/global_constant_store.ll
new file mode 100644
index 0000000000000..6a8c4e48d0885
--- /dev/null
+++ b/llvm/test/tools/llubi/global_constant_store.ll
@@ -0,0 +1,14 @@
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+ at constant = constant i32 1
+
+define void @main() {
+  store i32 2, ptr @constant
+  ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT: Stacktrace:
+; CHECK-NEXT: #0   store i32 2, ptr @constant, align 4 at @main
+; CHECK-NEXT: Immediate UB detected: Try to write to a constant memory object: ptr 0x8 [@constant].
+; CHECK-NEXT: error: Execution of function 'main' failed.

diff  --git a/llvm/test/tools/llubi/global_constexpr_initializer.ll b/llvm/test/tools/llubi/global_constexpr_initializer.ll
new file mode 100644
index 0000000000000..c4b2ee2e78384
--- /dev/null
+++ b/llvm/test/tools/llubi/global_constexpr_initializer.ll
@@ -0,0 +1,10 @@
+; RUN: not llubi < %s 2>&1 | FileCheck %s
+
+ at value = global i32 0
+ at aggregate = global [1 x ptr] [ptr getelementptr (i32, ptr @value, i64 1)]
+
+define void @main() {
+  ret void
+}
+
+; CHECK: error: Failed to initialize global values

diff  --git a/llvm/test/tools/llubi/global_external.ll b/llvm/test/tools/llubi/global_external.ll
new file mode 100644
index 0000000000000..761f2e82dd691
--- /dev/null
+++ b/llvm/test/tools/llubi/global_external.ll
@@ -0,0 +1,22 @@
+; RUN: llubi --verbose < %s 2>&1 | FileCheck %s
+
+target datalayout = "e-p:64:64:64-i32:32:32"
+
+ at external = external global i32
+ at external_ptr = global ptr @external
+
+define void @main() {
+  %ptr = load ptr, ptr @external_ptr
+  %load1 = load i32, ptr @external
+  store i32 42, ptr @external
+  %load2 = load i32, ptr @external
+  ret void
+}
+
+; CHECK: Entering function: main
+; CHECK-NEXT:   %ptr = load ptr, ptr @external_ptr, align 8 => ptr 0x8 [@external]
+; CHECK-NEXT:   %load1 = load i32, ptr @external, align 4 => i32 -1393641077
+; CHECK-NEXT:   store i32 42, ptr @external, align 4
+; CHECK-NEXT:   %load2 = load i32, ptr @external, align 4 => i32 42
+; CHECK-NEXT:   ret void
+; CHECK-NEXT: Exiting function: main

diff  --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 69cc52545adc6..dc503a788754a 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -29,7 +29,7 @@ bool Context::initGlobalValues() {
       // TODO: Use precise alignment for function pointers if it is necessary.
       auto FuncObj = allocate(0, F.getPointerAlignment(DL).value(), F.getName(),
                               DL.getProgramAddressSpace(), MemInitKind::Zeroed,
-                              MemAllocKind::Global);
+                              MemAllocKind::Global, /*IsIRGlobalValue=*/true);
       if (!FuncObj)
         return false;
       ValidFuncTargets.try_emplace(FuncObj->getAddress(),
@@ -41,7 +41,7 @@ bool Context::initGlobalValues() {
       if (!BB.hasAddressTaken())
         continue;
       auto BlockObj = allocate(0, 1, BB.getName(), DL.getProgramAddressSpace(),
-                               MemInitKind::Zeroed, MemAllocKind::Global);
+                               MemInitKind::Zeroed, MemAllocKind::BlockAddress);
       if (!BlockObj)
         return false;
       ValidBlockTargets.try_emplace(BlockObj->getAddress(),
@@ -49,11 +49,43 @@ bool Context::initGlobalValues() {
       BlockAddrMap.try_emplace(&BB, deriveFromMemoryObject(BlockObj));
     }
   }
-  // TODO: initialize global variables.
+
+  for (GlobalVariable &GV : M.globals()) {
+    Type *ValueTy = GV.getValueType();
+    const uint64_t Size = getEffectiveTypeAllocSize(ValueTy);
+    Align Alignment = GV.getPointerAlignment(DL);
+    auto InitKind =
+        GV.hasInitializer() ? MemInitKind::Zeroed : MemInitKind::Uninitialized;
+    const auto Obj =
+        allocate(Size, Alignment.value(), GV.getName(), GV.getAddressSpace(),
+                 InitKind, MemAllocKind::Global, /*IsIRGlobalValue=*/true);
+
+    if (!Obj)
+      return false;
+
+    Obj->setIsConstant(GV.isConstant());
+    GlobalAddrMap.try_emplace(&GV, deriveFromMemoryObject(Obj));
+  }
+
+  for (GlobalVariable &GV : M.globals()) {
+    if (!GV.hasInitializer())
+      continue;
+
+    MemoryObject *Obj = GlobalAddrMap.at(&GV).getMemoryObject();
+    assert(Obj && "global pointer should have memory object provenance");
+
+    Constant *Init = GV.getInitializer();
+
+    const AnyValue *InitVal = getConstantValue(Init);
+    if (!InitVal)
+      return false;
+
+    store(*Obj, 0, *InitVal, GV.getValueType());
+  }
   return true;
 }
 
-AnyValue Context::getConstantValueImpl(Constant *C) {
+std::optional<AnyValue> Context::getConstantValueImpl(Constant *C) {
   if (isa<PoisonValue>(C))
     return AnyValue::getPoisonValue(*this, C->getType());
 
@@ -80,34 +112,49 @@ AnyValue Context::getConstantValueImpl(Constant *C) {
   if (auto *CDS = dyn_cast<ConstantDataSequential>(C)) {
     std::vector<AnyValue> Elts;
     Elts.reserve(CDS->getNumElements());
-    for (uint32_t I = 0, E = CDS->getNumElements(); I != E; ++I)
-      Elts.push_back(getConstantValue(CDS->getElementAsConstant(I)));
+    for (uint32_t I = 0, E = CDS->getNumElements(); I != E; ++I) {
+      const AnyValue *Elt = getConstantValue(CDS->getElementAsConstant(I));
+      if (!Elt)
+        return std::nullopt;
+      Elts.push_back(*Elt);
+    }
     return std::move(Elts);
   }
 
   if (auto *CA = dyn_cast<ConstantAggregate>(C)) {
     std::vector<AnyValue> Elts;
     Elts.reserve(CA->getNumOperands());
-    for (uint32_t I = 0, E = CA->getNumOperands(); I != E; ++I)
-      Elts.push_back(getConstantValue(CA->getOperand(I)));
+    for (uint32_t I = 0, E = CA->getNumOperands(); I != E; ++I) {
+      const AnyValue *Elt = getConstantValue(CA->getOperand(I));
+      if (!Elt)
+        return std::nullopt;
+      Elts.push_back(*Elt);
+    }
     return std::move(Elts);
   }
 
   if (auto *BA = dyn_cast<BlockAddress>(C))
     return BlockAddrMap.at(BA->getBasicBlock());
 
+  if (auto *GV = dyn_cast<GlobalVariable>(C))
+    return GlobalAddrMap.at(GV);
+
   if (auto *F = dyn_cast<Function>(C))
     return FuncAddrMap.at(F);
 
-  llvm_unreachable("Unrecognized constant");
+  return std::nullopt;
 }
 
-const AnyValue &Context::getConstantValue(Constant *C) {
+const AnyValue *Context::getConstantValue(Constant *C) {
   auto It = ConstCache.find(C);
   if (It != ConstCache.end())
-    return It->second;
+    return &It->second;
+
+  std::optional<AnyValue> Val = getConstantValueImpl(C);
+  if (!Val)
+    return nullptr;
 
-  return ConstCache.emplace(C, getConstantValueImpl(C)).first->second;
+  return &ConstCache.emplace(C, std::move(*Val)).first->second;
 }
 
 APInt Context::getTag(uint32_t BitWidth, Provenance &Prov) {
@@ -503,11 +550,11 @@ void Context::freeze(AnyValue &Val, Type *Ty) {
 MemoryObject::~MemoryObject() = default;
 MemoryObject::MemoryObject(uint64_t Addr, uint64_t Size, StringRef Name,
                            unsigned AS, MemInitKind InitKind,
-                           MemAllocKind AllocKind)
+                           MemAllocKind AllocKind, bool IsIRGlobalValue)
     : Address(Addr), Size(Size), Name(Name), AS(AS),
       State(InitKind != MemInitKind::Poisoned ? MemoryObjectState::Alive
                                               : MemoryObjectState::Dead),
-      AllocKind(AllocKind) {
+      AllocKind(AllocKind), IsIRGlobalValue(IsIRGlobalValue) {
   switch (InitKind) {
   case MemInitKind::Zeroed:
     Bytes.resize(Size, Byte::concrete(0));
@@ -523,15 +570,16 @@ MemoryObject::MemoryObject(uint64_t Addr, uint64_t Size, StringRef Name,
 
 IntrusiveRefCntPtr<MemoryObject>
 Context::allocate(uint64_t Size, uint64_t Align, StringRef Name, unsigned AS,
-                  MemInitKind InitKind, MemAllocKind AllocKind) {
+                  MemInitKind InitKind, MemAllocKind AllocKind,
+                  bool IsIRGlobalValue) {
   // Even if the memory object is zero-sized, it still occupies a byte to obtain
   // a unique address.
   uint64_t AllocateSize = std::max(Size, (uint64_t)1);
   if (MaxMem != 0 && SaturatingAdd(UsedMem, AllocateSize) >= MaxMem)
     return nullptr;
   uint64_t AlignedAddr = alignTo(AllocationBase, Align);
-  auto MemObj = makeIntrusiveRefCnt<MemoryObject>(AlignedAddr, Size, Name, AS,
-                                                  InitKind, AllocKind);
+  auto MemObj = makeIntrusiveRefCnt<MemoryObject>(
+      AlignedAddr, Size, Name, AS, InitKind, AllocKind, IsIRGlobalValue);
   MemoryObjects[AlignedAddr] = MemObj;
   AllocationBase = AlignedAddr + AllocateSize;
   UsedMem += AllocateSize;
@@ -647,6 +695,7 @@ bool MemoryObject::isStackAllocated() const {
 bool MemoryObject::isHeapAllocated() const {
   switch (AllocKind) {
   case MemAllocKind::Global:
+  case MemAllocKind::BlockAddress:
   case MemAllocKind::Stack:
     return false;
   case MemAllocKind::Malloc:

diff  --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index 78d99f2cc5f13..5daa9e816f145 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -16,6 +16,7 @@
 #include "llvm/IR/FPEnv.h"
 #include "llvm/IR/Module.h"
 #include <map>
+#include <optional>
 #include <random>
 
 namespace llvm::ubi {
@@ -28,6 +29,7 @@ enum class MemInitKind {
 
 enum class MemAllocKind {
   Global,
+  BlockAddress,
   Stack,
   Malloc,
   New,
@@ -107,6 +109,7 @@ class MemoryObject : public RefCountedBase<MemoryObject> {
   MemoryObjectState State;
   MemAllocKind AllocKind;
   bool IsConstant = false;
+  bool IsIRGlobalValue = false;
 
   // Tagged provenances related to this memory object.
   // It is used to erasing the tags after the memory object is freed.
@@ -116,7 +119,8 @@ class MemoryObject : public RefCountedBase<MemoryObject> {
 
 public:
   MemoryObject(uint64_t Addr, uint64_t Size, StringRef Name, unsigned AS,
-               MemInitKind InitKind, MemAllocKind AllocKind);
+               MemInitKind InitKind, MemAllocKind AllocKind,
+               bool IsIRGlobalValue = false);
   MemoryObject(const MemoryObject &) = delete;
   MemoryObject(MemoryObject &&) = delete;
   MemoryObject &operator=(const MemoryObject &) = delete;
@@ -130,6 +134,7 @@ class MemoryObject : public RefCountedBase<MemoryObject> {
   MemoryObjectState getState() const { return State; }
   void setState(MemoryObjectState S) { State = S; }
   MemAllocKind getAllocKind() const { return AllocKind; }
+  bool isIRGlobalValue() const { return IsIRGlobalValue; }
   bool isConstant() const { return IsConstant; }
   void setIsConstant(bool C) { IsConstant = C; }
 
@@ -251,7 +256,8 @@ class Context {
       ValidFuncTargets;
   DenseMap<uint64_t, std::pair<BasicBlock *, IntrusiveRefCntPtr<MemoryObject>>>
       ValidBlockTargets;
-  AnyValue getConstantValueImpl(Constant *C);
+  DenseMap<GlobalVariable *, Pointer> GlobalAddrMap;
+  std::optional<AnyValue> getConstantValueImpl(Constant *C);
 
   // Floating-point environment
   RoundingMode CurrentRoundingMode = RoundingMode::NearestTiesToEven;
@@ -313,11 +319,12 @@ class Context {
   uint64_t getEffectiveTypeAllocSize(Type *Ty);
   uint64_t getEffectiveTypeStoreSize(Type *Ty);
 
-  const AnyValue &getConstantValue(Constant *C);
+  const AnyValue *getConstantValue(Constant *C);
   IntrusiveRefCntPtr<MemoryObject> allocate(uint64_t Size, uint64_t Align,
                                             StringRef Name, unsigned AS,
                                             MemInitKind InitKind,
-                                            MemAllocKind AllocKind);
+                                            MemAllocKind AllocKind,
+                                            bool IsIRGlobalValue = false);
   bool free(const MemoryObject &Obj);
   /// Derive a pointer from a memory object with offset 0.
   /// Please use Pointer's interface for further manipulations.

diff  --git a/llvm/tools/llubi/lib/ExecutorBase.cpp b/llvm/tools/llubi/lib/ExecutorBase.cpp
index 3b5a4c6ab0dd5..1433526f18d3a 100644
--- a/llvm/tools/llubi/lib/ExecutorBase.cpp
+++ b/llvm/tools/llubi/lib/ExecutorBase.cpp
@@ -145,6 +145,11 @@ void ExecutorBase::store(const AnyValue &Ptr, Align Alignment,
         << "Invalid memory access via a pointer with nullary provenance.";
     return;
   }
+  if (MO->isConstant()) {
+    reportImmediateUB() << "Try to write to a constant memory object: "
+                        << PtrVal << ".";
+    return;
+  }
   // TODO: pointer capability check
   if (auto Offset =
           verifyMemAccess(*MO, PtrVal.address(),

diff  --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp
index c98622e4bb83e..81cc968535a2f 100644
--- a/llvm/tools/llubi/lib/Interpreter.cpp
+++ b/llvm/tools/llubi/lib/Interpreter.cpp
@@ -150,11 +150,18 @@ class InstExecutor : public InstVisitor<InstExecutor, void>,
   const DataLayout &DL;
   std::list<Frame> CallStack;
   AnyValue None;
+  std::list<AnyValue> UnsupportedConstantValues;
   Library Lib;
 
   const AnyValue &getValue(Value *V) {
-    if (auto *C = dyn_cast<Constant>(V))
-      return Ctx.getConstantValue(C);
+    if (auto *C = dyn_cast<Constant>(V)) {
+      if (const AnyValue *Val = Ctx.getConstantValue(C))
+        return *Val;
+      reportError() << "Unsupported constant: " << *C << ".";
+      UnsupportedConstantValues.push_back(
+          AnyValue::getPoisonValue(Ctx, C->getType()));
+      return UnsupportedConstantValues.back();
+    }
     return CurrentFrame->ValueMap.at(V);
   }
 

diff  --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index fe8e0f3cff330..04a1aa803f66c 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -27,6 +27,8 @@ void Pointer::print(raw_ostream &OS) const {
   Address.toStringUnsigned(AddrStr, 16);
   OS << "ptr 0x" << AddrStr << " [";
   if (MemoryObject *Obj = getMemoryObject()) {
+    if (Obj->isIRGlobalValue())
+      OS << "@";
     OS << Obj->getName();
     if (Address != Obj->getAddress())
       OS << " + " << (Address - Obj->getAddress());

diff  --git a/llvm/tools/llubi/lib/Value.h b/llvm/tools/llubi/lib/Value.h
index 089245ce8e417..71ee212b4299e 100644
--- a/llvm/tools/llubi/lib/Value.h
+++ b/llvm/tools/llubi/lib/Value.h
@@ -273,6 +273,11 @@ inline raw_ostream &operator<<(raw_ostream &OS, const AnyValue &V) {
   return OS;
 }
 
+inline raw_ostream &operator<<(raw_ostream &OS, const Pointer &P) {
+  P.print(OS);
+  return OS;
+}
+
 } // namespace llvm::ubi
 
 #endif

diff  --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index 5f5e451282714..c433e53167743 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -267,8 +267,10 @@ int main(int argc, char **argv) {
   auto *MainFuncTy = FunctionType::get(IntTy, {IntTy, PtrTy}, false);
   SmallVector<ubi::AnyValue> Args;
   if (EntryFn->getFunctionType() == MainFuncTy) {
-    Args.push_back(
-        Ctx.getConstantValue(ConstantInt::get(IntTy, InputArgv.size())));
+    const ubi::AnyValue *Argc =
+        Ctx.getConstantValue(ConstantInt::get(IntTy, InputArgv.size()));
+    assert(Argc && "failed to initialize argc");
+    Args.push_back(*Argc);
 
     uint32_t PtrSize = Ctx.getDataLayout().getPointerSize();
     uint64_t PtrsSize = PtrSize * (InputArgv.size() + 1);


        


More information about the llvm-commits mailing list