[llvm] [llubi] Reject invalid types (PR #230495)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 11 09:30:09 PDT 2026


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/230495

>From a6ae7b2f9ad96c078dcdf9b9795a445dfe7f7a42 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 9 Oct 2026 20:58:28 +0800
Subject: [PATCH 1/7] [llubi] Reject invalid types

---
 llvm/test/tools/llubi/load_target_ext.ll      | 10 +++
 .../tools/llubi/shufflevector_large_size.ll   |  9 +++
 .../tools/llubi/target_ext_extractvalue.ll    |  9 +++
 .../tools/llubi/target_ext_freeze_poison.ll   |  9 +++
 llvm/test/tools/llubi/target_ext_global.ll    | 10 +++
 .../tools/llubi/target_ext_indirect_call.ll   |  9 +++
 llvm/test/tools/llubi/target_ext_main_arg.ll  |  8 ++
 llvm/test/tools/llubi/target_ext_return.ll    |  8 ++
 llvm/test/tools/llubi/target_ext_store.ll     | 10 +++
 .../tools/llubi/target_ext_store_array.ll     |  9 +++
 .../target_ext_store_nested_aggregate.ll      |  9 +++
 .../tools/llubi/target_ext_store_struct.ll    |  9 +++
 .../llubi/target_ext_store_vector_element.ll  |  9 +++
 llvm/test/tools/llubi/target_ext_vararg.ll    | 13 ++++
 .../tools/llubi/vector_reduce_fmax_empty.ll   |  9 +++
 .../llubi/vector_reduce_fmaximum_empty.ll     |  9 +++
 llvm/test/tools/llubi/x86_amx_bitcast.ll      |  9 +++
 llvm/test/tools/llubi/x86_amx_store_struct.ll |  9 +++
 llvm/tools/llubi/lib/Context.cpp              | 77 +++++++++++++++++++
 llvm/tools/llubi/lib/Context.h                |  5 ++
 llvm/tools/llubi/llubi.cpp                    |  5 ++
 21 files changed, 254 insertions(+)
 create mode 100644 llvm/test/tools/llubi/load_target_ext.ll
 create mode 100644 llvm/test/tools/llubi/shufflevector_large_size.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_extractvalue.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_freeze_poison.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_global.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_indirect_call.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_main_arg.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_return.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_store.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_store_array.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_store_nested_aggregate.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_store_struct.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_store_vector_element.ll
 create mode 100644 llvm/test/tools/llubi/target_ext_vararg.ll
 create mode 100644 llvm/test/tools/llubi/vector_reduce_fmax_empty.ll
 create mode 100644 llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll
 create mode 100644 llvm/test/tools/llubi/x86_amx_bitcast.ll
 create mode 100644 llvm/test/tools/llubi/x86_amx_store_struct.ll

diff --git a/llvm/test/tools/llubi/load_target_ext.ll b/llvm/test/tools/llubi/load_target_ext.ll
new file mode 100644
index 0000000000000..d89e891cecfe4
--- /dev/null
+++ b/llvm/test/tools/llubi/load_target_ext.ll
@@ -0,0 +1,10 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %a = alloca i64
+  %v = load target("spirv.Event"), ptr %a
+  ret void
+}
+; CHECK: Unsupported type target("spirv.Event")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/shufflevector_large_size.ll b/llvm/test/tools/llubi/shufflevector_large_size.ll
new file mode 100644
index 0000000000000..cf106a4d1e337
--- /dev/null
+++ b/llvm/test/tools/llubi/shufflevector_large_size.ll
@@ -0,0 +1,9 @@
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %x = shufflevector <vscale x 1073741823 x i32> poison, <vscale x 1073741823 x i32> poison, <vscale x 1 x i32> zeroinitializer
+  ret void
+}
+
+; CHECK: The number of elements of <vscale x 1073741823 x i32> is too large!
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_extractvalue.ll b/llvm/test/tools/llubi/target_ext_extractvalue.ll
new file mode 100644
index 0000000000000..7bfd61bbf51b8
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_extractvalue.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %x = extractvalue {i32, target("wasm.externref")} undef, 0
+  ret void
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_freeze_poison.ll b/llvm/test/tools/llubi/target_ext_freeze_poison.ll
new file mode 100644
index 0000000000000..366aa5340d14d
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_freeze_poison.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %x = freeze target("wasm.externref") poison
+  ret void
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_global.ll b/llvm/test/tools/llubi/target_ext_global.ll
new file mode 100644
index 0000000000000..052a6042f4d60
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_global.ll
@@ -0,0 +1,10 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+ at g = global target("wasm.externref") poison
+
+define i32 @main() {
+  ret i32 0
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_indirect_call.ll b/llvm/test/tools/llubi/target_ext_indirect_call.ll
new file mode 100644
index 0000000000000..b3fef6e388c30
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_indirect_call.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  call void null(target("wasm.externref") poison)
+  ret void
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_main_arg.ll b/llvm/test/tools/llubi/target_ext_main_arg.ll
new file mode 100644
index 0000000000000..2981a324d28b7
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_main_arg.ll
@@ -0,0 +1,8 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define i32 @main(target("wasm.externref") %a) {
+  ret i32 0
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_return.ll b/llvm/test/tools/llubi/target_ext_return.ll
new file mode 100644
index 0000000000000..60e3d3564f607
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_return.ll
@@ -0,0 +1,8 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define target("wasm.externref") @main() {
+  ret target("wasm.externref") poison
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store.ll b/llvm/test/tools/llubi/target_ext_store.ll
new file mode 100644
index 0000000000000..883a83436dadc
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_store.ll
@@ -0,0 +1,10 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %a = alloca i8
+  store target("wasm.externref") poison, ptr %a
+  ret void
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store_array.ll b/llvm/test/tools/llubi/target_ext_store_array.ll
new file mode 100644
index 0000000000000..e09730d7bf486
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_store_array.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  store [2 x target("wasm.externref")] poison, ptr null
+  ret void
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store_nested_aggregate.ll b/llvm/test/tools/llubi/target_ext_store_nested_aggregate.ll
new file mode 100644
index 0000000000000..01c6585849983
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_store_nested_aggregate.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  store {i32, [2 x target("wasm.externref")]} poison, ptr null
+  ret void
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store_struct.ll b/llvm/test/tools/llubi/target_ext_store_struct.ll
new file mode 100644
index 0000000000000..02c281bc44329
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_store_struct.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  store {i32, target("wasm.externref")} poison, ptr null
+  ret void
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store_vector_element.ll b/llvm/test/tools/llubi/target_ext_store_vector_element.ll
new file mode 100644
index 0000000000000..9d001b0926840
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_store_vector_element.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  store <2 x target("llvm.test.vectorelement")> poison, ptr null
+  ret void
+}
+; CHECK: Unsupported type <2 x target("llvm.test.vectorelement")>
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_vararg.ll b/llvm/test/tools/llubi/target_ext_vararg.ll
new file mode 100644
index 0000000000000..1d6f6079b2d66
--- /dev/null
+++ b/llvm/test/tools/llubi/target_ext_vararg.ll
@@ -0,0 +1,13 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+ at .str = private constant [3 x i8] c"%d\00"
+
+declare i32 @printf(ptr, ...)
+
+define void @main() {
+  %r = call i32 (ptr, ...) @printf(ptr @.str, target("wasm.externref") poison)
+  ret void
+}
+; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll b/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll
new file mode 100644
index 0000000000000..c481bf8890b5d
--- /dev/null
+++ b/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %r = call float @llvm.vector.reduce.fmax.nxv1073741824f32(<vscale x 1073741824 x float> zeroinitializer)
+  ret void
+}
+; CHECK: The number of elements of <vscale x 1073741824 x float> is too large!
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll b/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll
new file mode 100644
index 0000000000000..b383f90f3ef27
--- /dev/null
+++ b/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %r = call float @llvm.vector.reduce.fmaximum.nxv1073741824f32(<vscale x 1073741824 x float> zeroinitializer)
+  ret void
+}
+; CHECK: The number of elements of <vscale x 1073741824 x float> is too large!
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/x86_amx_bitcast.ll b/llvm/test/tools/llubi/x86_amx_bitcast.ll
new file mode 100644
index 0000000000000..0263648936776
--- /dev/null
+++ b/llvm/test/tools/llubi/x86_amx_bitcast.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %x = bitcast x86_amx poison to i8192
+  ret void
+}
+; CHECK: Unsupported type x86_amx
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/x86_amx_store_struct.ll b/llvm/test/tools/llubi/x86_amx_store_struct.ll
new file mode 100644
index 0000000000000..cd8065d8534b1
--- /dev/null
+++ b/llvm/test/tools/llubi/x86_amx_store_struct.ll
@@ -0,0 +1,9 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  store {i32, x86_amx} poison, ptr null
+  ret void
+}
+; CHECK: Unsupported type x86_amx
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index e78263a2bab76..d70d054b4887f 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -1270,6 +1270,83 @@ uint64_t Context::getEffectiveTypeStoreSize(Type *Ty) {
   return getEffectiveTypeSize(DL.getTypeStoreSize(Ty));
 }
 
+bool Context::isValid(raw_ostream &OS) const {
+  assert(isPowerOf2_32(VScale) && "VScale must be a power of two.");
+  uint32_t MaxNumElements = UINT32_MAX / VScale;
+
+  SmallPtrSet<Type *, 16> ValidAggTys;
+  auto IsSupportedType = [&](auto &&Self, Type *Ty) {
+    switch (Ty->getTypeID()) {
+    case Type::X86_AMXTyID:
+    case Type::TargetExtTyID:
+      OS << "Unsupported type " << *Ty << '\n';
+      return false;
+    case Type::ScalableVectorTyID:
+      // Avoid overflow when computing EVL.
+      if (cast<VectorType>(Ty)->getElementCount().getKnownMinValue() >=
+          MaxNumElements) {
+        OS << "The number of elements of " << *Ty << " is too large!\n";
+        return false;
+      }
+      [[fallthrough]];
+    case Type::FixedVectorTyID:
+      if (cast<VectorType>(Ty)->getElementType()->isTargetExtTy()) {
+        OS << "Unsupported type " << *Ty << '\n';
+        return false;
+      }
+      return true;
+    case Type::StructTyID:
+    case Type::ArrayTyID:
+      if (ValidAggTys.contains(Ty))
+        return true;
+      for (unsigned I = 0, E = Ty->getNumContainedTypes(); I != E; ++I)
+        if (!Self(Self, Ty->getContainedType(I)))
+          return false;
+      ValidAggTys.insert(Ty);
+      return true;
+    default:
+      return true;
+    }
+  };
+
+#define CHECK(TYPE)                                                            \
+  if (!IsSupportedType(IsSupportedType, TYPE))                                 \
+  return false
+
+  for (auto &F : M) {
+    CHECK(F.getReturnType());
+    for (auto &Arg : F.args())
+      CHECK(Arg.getType());
+    for (auto &BB : F)
+      for (auto &I : BB) {
+        CHECK(I.getType());
+        switch (I.getOpcode()) {
+        case Instruction::Call:
+        case Instruction::Invoke:
+          for (auto &Op : I.operands())
+            CHECK(Op->getType());
+          break;
+        case Instruction::Store:
+        case Instruction::ExtractElement:
+        case Instruction::ExtractValue:
+        case Instruction::BitCast:
+        case Instruction::ShuffleVector:
+          CHECK(I.getOperand(0)->getType());
+          break;
+        default:
+          break;
+        }
+      }
+  }
+
+  for (auto &G : M.globals())
+    CHECK(G.getValueType());
+
+#undef CHECK
+
+  return true;
+}
+
 RoundingMode Context::getCurrentRoundingMode() const {
   return CurrentRoundingMode;
 }
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index 571be140cc83f..10c6effea90ac 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -381,6 +381,11 @@ class Context {
   uint64_t getEffectiveTypeAllocSize(Type *Ty);
   uint64_t getEffectiveTypeStoreSize(Type *Ty);
 
+  /// Check if the input module is compatible with the current setting.
+  /// Otherwise the interpreter might crash on some untrusted inputs, even if it
+  /// passes IRVerifier.
+  bool isValid(raw_ostream &OS) const;
+
   /// Returns a pointer to an evaluated constant \p C. If it cannot be
   /// evaluated, returns nullptr. Note that it returns a pointer to a temporary
   /// buffer when \p C is not context-free. The caller is responsible for
diff --git a/llvm/tools/llubi/llubi.cpp b/llvm/tools/llubi/llubi.cpp
index 00863dd5ecb39..2bc47227610f4 100644
--- a/llvm/tools/llubi/llubi.cpp
+++ b/llvm/tools/llubi/llubi.cpp
@@ -262,6 +262,11 @@ int main(int argc, char **argv) {
   Ctx.setNaNPropagationBehavior(NaNPropagationBehavior);
   Ctx.reseed(Seed);
 
+  if (!NoVerify && !Ctx.isValid(errs())) {
+    WithColor::error() << InputFile << ": input module cannot be executed!\n";
+    return 1;
+  }
+
   if (!Ctx.initGlobalValues()) {
     WithColor::error() << "Failed to initialize global values (e.g., the "
                           "memory limit may be too low).\n";

>From 75ba601f18767162d72418c41d5f75a30d99bdbd Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sat, 10 Oct 2026 02:03:20 +0800
Subject: [PATCH 2/7] [llubi] Reject large arrays

---
 llvm/test/tools/llubi/array_count_truncation.ll | 11 +++++++++++
 llvm/tools/llubi/lib/Context.cpp                | 10 +++++++++-
 2 files changed, 20 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/tools/llubi/array_count_truncation.ll

diff --git a/llvm/test/tools/llubi/array_count_truncation.ll b/llvm/test/tools/llubi/array_count_truncation.ll
new file mode 100644
index 0000000000000..5c646e36cf9b9
--- /dev/null
+++ b/llvm/test/tools/llubi/array_count_truncation.ll
@@ -0,0 +1,11 @@
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+ at g = global i8 0
+define void @main() {
+  %v = load [4294967296 x [0 x i8]], ptr @g
+  %e = extractvalue [4294967296 x [0 x i8]] %v, 0
+  ret void
+}
+
+; CHECK: The number of elements of [4294967296 x [0 x i8]] is too large!
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index d70d054b4887f..726146e9e6cf3 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -1295,8 +1295,16 @@ bool Context::isValid(raw_ostream &OS) const {
         return false;
       }
       return true;
-    case Type::StructTyID:
     case Type::ArrayTyID:
+      // The number of elements is used as uint32_t in the value
+      // representation. Reject arrays with too many elements to avoid
+      // truncation.
+      if (cast<ArrayType>(Ty)->getNumElements() > UINT32_MAX) {
+        OS << "The number of elements of " << *Ty << " is too large!\n";
+        return false;
+      }
+      [[fallthrough]];
+    case Type::StructTyID:
       if (ValidAggTys.contains(Ty))
         return true;
       for (unsigned I = 0, E = Ty->getNumContainedTypes(); I != E; ++I)

>From 082a25eb8c84a6986b11c71d0396953bd2b13c48 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sat, 10 Oct 2026 03:34:48 +0800
Subject: [PATCH 3/7] [llubi][NFC] Use UTC

---
 llvm/test/tools/llubi/array_count_truncation.ll | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/test/tools/llubi/array_count_truncation.ll b/llvm/test/tools/llubi/array_count_truncation.ll
index 5c646e36cf9b9..65044115e9bca 100644
--- a/llvm/test/tools/llubi/array_count_truncation.ll
+++ b/llvm/test/tools/llubi/array_count_truncation.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
 ; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
 
 @g = global i8 0

>From fd6bf2d615bef42f7c7ffacb620ebefddf8724f1 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sat, 10 Oct 2026 04:02:16 +0800
Subject: [PATCH 4/7] [llubi] Dump position

---
 .../tools/llubi/array_count_truncation.ll     |  1 +
 llvm/test/tools/llubi/load_target_ext.ll      |  1 +
 .../tools/llubi/shufflevector_large_size.ll   |  2 +
 .../tools/llubi/target_ext_extractvalue.ll    |  1 +
 .../tools/llubi/target_ext_freeze_poison.ll   |  1 +
 llvm/test/tools/llubi/target_ext_global.ll    |  1 +
 .../tools/llubi/target_ext_indirect_call.ll   |  1 +
 llvm/test/tools/llubi/target_ext_main_arg.ll  |  1 +
 llvm/test/tools/llubi/target_ext_return.ll    |  1 +
 llvm/test/tools/llubi/target_ext_store.ll     |  1 +
 .../tools/llubi/target_ext_store_array.ll     |  1 +
 .../target_ext_store_nested_aggregate.ll      |  1 +
 .../tools/llubi/target_ext_store_struct.ll    |  1 +
 .../llubi/target_ext_store_vector_element.ll  |  1 +
 llvm/test/tools/llubi/target_ext_vararg.ll    |  1 +
 .../tools/llubi/vector_reduce_fmax_empty.ll   |  1 +
 .../llubi/vector_reduce_fmaximum_empty.ll     |  1 +
 llvm/test/tools/llubi/x86_amx_bitcast.ll      |  1 +
 llvm/test/tools/llubi/x86_amx_store_struct.ll |  1 +
 llvm/tools/llubi/lib/Context.cpp              | 62 ++++++++++++++++---
 20 files changed, 74 insertions(+), 8 deletions(-)

diff --git a/llvm/test/tools/llubi/array_count_truncation.ll b/llvm/test/tools/llubi/array_count_truncation.ll
index 65044115e9bca..52362bff98a2d 100644
--- a/llvm/test/tools/llubi/array_count_truncation.ll
+++ b/llvm/test/tools/llubi/array_count_truncation.ll
@@ -9,4 +9,5 @@ define void @main() {
 }
 
 ; CHECK: The number of elements of [4294967296 x [0 x i8]] is too large!
+; CHECK-NEXT:   %v = load [4294967296 x [0 x i8]], ptr @g, align 1 at @main <stdin>:6
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/load_target_ext.ll b/llvm/test/tools/llubi/load_target_ext.ll
index d89e891cecfe4..6a11dd3a2727a 100644
--- a/llvm/test/tools/llubi/load_target_ext.ll
+++ b/llvm/test/tools/llubi/load_target_ext.ll
@@ -7,4 +7,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type target("spirv.Event")
+; CHECK-NEXT:   %v = load target("spirv.Event"), ptr %a, align 8 at @main <stdin>:6
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/shufflevector_large_size.ll b/llvm/test/tools/llubi/shufflevector_large_size.ll
index cf106a4d1e337..1c3fe8227bdaa 100644
--- a/llvm/test/tools/llubi/shufflevector_large_size.ll
+++ b/llvm/test/tools/llubi/shufflevector_large_size.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
 ; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
 
 define void @main() {
@@ -6,4 +7,5 @@ define void @main() {
 }
 
 ; CHECK: The number of elements of <vscale x 1073741823 x i32> is too large!
+; CHECK-NEXT:   %x = shufflevector <vscale x 1073741823 x i32> poison, <vscale x 1073741823 x i32> poison, <vscale x 1 x i32> zeroinitializer at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_extractvalue.ll b/llvm/test/tools/llubi/target_ext_extractvalue.ll
index 7bfd61bbf51b8..22afa4256bbd4 100644
--- a/llvm/test/tools/llubi/target_ext_extractvalue.ll
+++ b/llvm/test/tools/llubi/target_ext_extractvalue.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   %x = extractvalue { i32, target("wasm.externref") } undef, 0 at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_freeze_poison.ll b/llvm/test/tools/llubi/target_ext_freeze_poison.ll
index 366aa5340d14d..af45642f60a05 100644
--- a/llvm/test/tools/llubi/target_ext_freeze_poison.ll
+++ b/llvm/test/tools/llubi/target_ext_freeze_poison.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   %x = freeze target("wasm.externref") poison at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_global.ll b/llvm/test/tools/llubi/target_ext_global.ll
index 052a6042f4d60..99b9dc716362f 100644
--- a/llvm/test/tools/llubi/target_ext_global.ll
+++ b/llvm/test/tools/llubi/target_ext_global.ll
@@ -7,4 +7,5 @@ define i32 @main() {
   ret i32 0
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   at @g
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_indirect_call.ll b/llvm/test/tools/llubi/target_ext_indirect_call.ll
index b3fef6e388c30..fac80beb3d895 100644
--- a/llvm/test/tools/llubi/target_ext_indirect_call.ll
+++ b/llvm/test/tools/llubi/target_ext_indirect_call.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   call void null(target("wasm.externref") poison) at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_main_arg.ll b/llvm/test/tools/llubi/target_ext_main_arg.ll
index 2981a324d28b7..00c179a22b6dd 100644
--- a/llvm/test/tools/llubi/target_ext_main_arg.ll
+++ b/llvm/test/tools/llubi/target_ext_main_arg.ll
@@ -5,4 +5,5 @@ define i32 @main(target("wasm.externref") %a) {
   ret i32 0
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   target("wasm.externref") %a at @main <stdin>:4
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_return.ll b/llvm/test/tools/llubi/target_ext_return.ll
index 60e3d3564f607..c25606229fa77 100644
--- a/llvm/test/tools/llubi/target_ext_return.ll
+++ b/llvm/test/tools/llubi/target_ext_return.ll
@@ -5,4 +5,5 @@ define target("wasm.externref") @main() {
   ret target("wasm.externref") poison
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   at @main <stdin>:4
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store.ll b/llvm/test/tools/llubi/target_ext_store.ll
index 883a83436dadc..2d2694fbcc75b 100644
--- a/llvm/test/tools/llubi/target_ext_store.ll
+++ b/llvm/test/tools/llubi/target_ext_store.ll
@@ -7,4 +7,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   store target("wasm.externref") poison, ptr %a, align 8 at @main <stdin>:6
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store_array.ll b/llvm/test/tools/llubi/target_ext_store_array.ll
index e09730d7bf486..297a600ddda85 100644
--- a/llvm/test/tools/llubi/target_ext_store_array.ll
+++ b/llvm/test/tools/llubi/target_ext_store_array.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   store [2 x target("wasm.externref")] poison, ptr null, align 8 at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store_nested_aggregate.ll b/llvm/test/tools/llubi/target_ext_store_nested_aggregate.ll
index 01c6585849983..b186d93b9739c 100644
--- a/llvm/test/tools/llubi/target_ext_store_nested_aggregate.ll
+++ b/llvm/test/tools/llubi/target_ext_store_nested_aggregate.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   store { i32, [2 x target("wasm.externref")] } poison, ptr null, align 8 at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store_struct.ll b/llvm/test/tools/llubi/target_ext_store_struct.ll
index 02c281bc44329..58042ec612b3d 100644
--- a/llvm/test/tools/llubi/target_ext_store_struct.ll
+++ b/llvm/test/tools/llubi/target_ext_store_struct.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   store { i32, target("wasm.externref") } poison, ptr null, align 8 at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store_vector_element.ll b/llvm/test/tools/llubi/target_ext_store_vector_element.ll
index 9d001b0926840..e9d9894affb8c 100644
--- a/llvm/test/tools/llubi/target_ext_store_vector_element.ll
+++ b/llvm/test/tools/llubi/target_ext_store_vector_element.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type <2 x target("llvm.test.vectorelement")>
+; CHECK-NEXT:   store <2 x target("llvm.test.vectorelement")> poison, ptr null, align 8 at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_vararg.ll b/llvm/test/tools/llubi/target_ext_vararg.ll
index 1d6f6079b2d66..88b2f84edd6cd 100644
--- a/llvm/test/tools/llubi/target_ext_vararg.ll
+++ b/llvm/test/tools/llubi/target_ext_vararg.ll
@@ -10,4 +10,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type target("wasm.externref")
+; CHECK-NEXT:   %r = call i32 (ptr, ...) @printf(ptr @.str, target("wasm.externref") poison) at @main <stdin>:9
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll b/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll
index c481bf8890b5d..934716e76c234 100644
--- a/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll
+++ b/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: The number of elements of <vscale x 1073741824 x float> is too large!
+; CHECK-NEXT:   %r = call float @llvm.vector.reduce.fmax.nxv1073741824f32(<vscale x 1073741824 x float> zeroinitializer) at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll b/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll
index b383f90f3ef27..7c4a404131b2a 100644
--- a/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll
+++ b/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: The number of elements of <vscale x 1073741824 x float> is too large!
+; CHECK-NEXT:   %r = call float @llvm.vector.reduce.fmaximum.nxv1073741824f32(<vscale x 1073741824 x float> zeroinitializer) at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/x86_amx_bitcast.ll b/llvm/test/tools/llubi/x86_amx_bitcast.ll
index 0263648936776..d36267e93d4e3 100644
--- a/llvm/test/tools/llubi/x86_amx_bitcast.ll
+++ b/llvm/test/tools/llubi/x86_amx_bitcast.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type x86_amx
+; CHECK-NEXT:   %x = bitcast x86_amx poison to i8192 at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/x86_amx_store_struct.ll b/llvm/test/tools/llubi/x86_amx_store_struct.ll
index cd8065d8534b1..240172219f65d 100644
--- a/llvm/test/tools/llubi/x86_amx_store_struct.ll
+++ b/llvm/test/tools/llubi/x86_amx_store_struct.ll
@@ -6,4 +6,5 @@ define void @main() {
   ret void
 }
 ; CHECK: Unsupported type x86_amx
+; CHECK-NEXT:   store { i32, x86_amx } poison, ptr null, align 64 at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 726146e9e6cf3..223239a1a0bf3 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -1317,29 +1317,75 @@ bool Context::isValid(raw_ostream &OS) const {
     }
   };
 
-#define CHECK(TYPE)                                                            \
+  StringRef ModuleFileName = M.getModuleIdentifier();
+
+  auto DumpFunction = [&](Function *F) {
+    OS << "  at ";
+    F->printAsOperand(errs(), /*PrintType=*/false);
+    if (ParserContext) {
+      if (auto Loc = ParserContext->getFunctionLocation(F))
+        OS << ' ' << ModuleFileName << ':' << Loc->Start.Line + 1;
+    }
+    OS << '\n';
+
+    return false;
+  };
+
+  auto DumpArg = [&](Argument *Arg) {
+    OS << "  " << *Arg << " at ";
+    Arg->getParent()->printAsOperand(errs(), /*PrintType=*/false);
+    if (ParserContext) {
+      if (auto Loc = ParserContext->getInstructionOrArgumentLocation(Arg))
+        OS << ' ' << ModuleFileName << ':' << Loc->Start.Line + 1;
+    }
+    OS << '\n';
+
+    return false;
+  };
+
+  auto DumpInst = [&](Instruction *Inst) {
+    OS << *Inst << " at ";
+    Inst->getFunction()->printAsOperand(errs(), /*PrintType=*/false);
+    if (ParserContext) {
+      if (auto Loc = ParserContext->getInstructionOrArgumentLocation(Inst))
+        OS << ' ' << ModuleFileName << ':' << Loc->Start.Line + 1;
+    }
+    OS << '\n';
+
+    return false;
+  };
+
+  auto DumpGlobal = [&](GlobalObject *GO) {
+    OS << "  at ";
+    GO->printAsOperand(errs(), /*PrintType=*/false);
+    OS << '\n';
+
+    return false;
+  };
+
+#define CHECK(TYPE, LOC)                                                       \
   if (!IsSupportedType(IsSupportedType, TYPE))                                 \
-  return false
+    return LOC;
 
   for (auto &F : M) {
-    CHECK(F.getReturnType());
+    CHECK(F.getReturnType(), DumpFunction(&F));
     for (auto &Arg : F.args())
-      CHECK(Arg.getType());
+      CHECK(Arg.getType(), DumpArg(&Arg));
     for (auto &BB : F)
       for (auto &I : BB) {
-        CHECK(I.getType());
+        CHECK(I.getType(), DumpInst(&I));
         switch (I.getOpcode()) {
         case Instruction::Call:
         case Instruction::Invoke:
           for (auto &Op : I.operands())
-            CHECK(Op->getType());
+            CHECK(Op->getType(), DumpInst(&I));
           break;
         case Instruction::Store:
         case Instruction::ExtractElement:
         case Instruction::ExtractValue:
         case Instruction::BitCast:
         case Instruction::ShuffleVector:
-          CHECK(I.getOperand(0)->getType());
+          CHECK(I.getOperand(0)->getType(), DumpInst(&I));
           break;
         default:
           break;
@@ -1348,7 +1394,7 @@ bool Context::isValid(raw_ostream &OS) const {
   }
 
   for (auto &G : M.globals())
-    CHECK(G.getValueType());
+    CHECK(G.getValueType(), DumpGlobal(&G));
 
 #undef CHECK
 

>From 8fac4853aac58e0a7e338d46204f92dd6daf6289 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 11 Oct 2026 01:56:14 +0800
Subject: [PATCH 5/7] [llubi] Reject token types

---
 llvm/test/tools/llubi/token_constant.ll | 10 ++++++++++
 llvm/tools/llubi/lib/Context.cpp        |  1 +
 2 files changed, 11 insertions(+)
 create mode 100644 llvm/test/tools/llubi/token_constant.ll

diff --git a/llvm/test/tools/llubi/token_constant.ll b/llvm/test/tools/llubi/token_constant.ll
new file mode 100644
index 0000000000000..83c2cbfb23bd0
--- /dev/null
+++ b/llvm/test/tools/llubi/token_constant.ll
@@ -0,0 +1,10 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  call i8 @llvm.coro.suspend(token none, i1 false)
+  ret void
+}
+; CHECK: Unsupported type token
+; CHECK-NEXT:   %1 = call i8 @llvm.coro.suspend(token none, i1 false) at @main <stdin>:5
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 223239a1a0bf3..d522d48c39a74 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -1279,6 +1279,7 @@ bool Context::isValid(raw_ostream &OS) const {
     switch (Ty->getTypeID()) {
     case Type::X86_AMXTyID:
     case Type::TargetExtTyID:
+    case Type::TokenTyID:
       OS << "Unsupported type " << *Ty << '\n';
       return false;
     case Type::ScalableVectorTyID:

>From 0d58a8bfe773bab1cc3b57b9b53b433d07c3f5e6 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 11 Oct 2026 03:21:52 +0800
Subject: [PATCH 6/7] [llubi] Reject opaque types

---
 llvm/test/tools/llubi/global_unsized.ll | 12 ++++++++++++
 llvm/tools/llubi/lib/Context.cpp        |  4 ++++
 2 files changed, 16 insertions(+)
 create mode 100644 llvm/test/tools/llubi/global_unsized.ll

diff --git a/llvm/test/tools/llubi/global_unsized.ll b/llvm/test/tools/llubi/global_unsized.ll
new file mode 100644
index 0000000000000..69d3ef5930281
--- /dev/null
+++ b/llvm/test/tools/llubi/global_unsized.ll
@@ -0,0 +1,12 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+%opaque = type opaque
+ at unsized = external global %opaque
+
+define void @main() {
+  ret void
+}
+; CHECK: Unsupported opaque struct type %opaque
+; CHECK-NEXT:   at @unsized
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index d522d48c39a74..9dd953e63507e 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -1306,6 +1306,10 @@ bool Context::isValid(raw_ostream &OS) const {
       }
       [[fallthrough]];
     case Type::StructTyID:
+      if (auto *STy = dyn_cast<StructType>(Ty); STy && STy->isOpaque()) {
+        OS << "Unsupported opaque struct type %" << STy->getName() << "\n";
+        return false;
+      }
       if (ValidAggTys.contains(Ty))
         return true;
       for (unsigned I = 0, E = Ty->getNumContainedTypes(); I != E; ++I)

>From 29b323d4058e7f5cfd790ea565edbb70598f0616 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 12 Oct 2026 00:29:15 +0800
Subject: [PATCH 7/7] [llubi] Check total size in bits

---
 llvm/test/tools/llubi/array_count_overflow.ll |  11 ++
 .../tools/llubi/array_count_truncation.ll     |   2 +-
 .../tools/llubi/shufflevector_large_size.ll   |   2 +-
 .../llubi/target_ext_store_vector_element.ll  |   2 +-
 .../tools/llubi/vector_reduce_fmax_empty.ll   |   2 +-
 .../llubi/vector_reduce_fmaximum_empty.ll     |   2 +-
 llvm/tools/llubi/lib/Context.cpp              | 145 ++++++++++++------
 llvm/tools/llubi/lib/Context.h                |   4 +
 8 files changed, 115 insertions(+), 55 deletions(-)
 create mode 100644 llvm/test/tools/llubi/array_count_overflow.ll

diff --git a/llvm/test/tools/llubi/array_count_overflow.ll b/llvm/test/tools/llubi/array_count_overflow.ll
new file mode 100644
index 0000000000000..a6cef78ec5a50
--- /dev/null
+++ b/llvm/test/tools/llubi/array_count_overflow.ll
@@ -0,0 +1,11 @@
+; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6
+; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s
+
+define void @main() {
+  %a = alloca [4 x [2147483648 x [2147483648 x i8]]], align 8
+  %v = load [4 x [2147483648 x [2147483648 x i8]]], ptr %a
+  ret void
+}
+; CHECK: [2147483648 x i8] is too large!
+; CHECK-NEXT:   %v = load [4 x [2147483648 x [2147483648 x i8]]], ptr %a, align 1 at @main <stdin>:6
+; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/array_count_truncation.ll b/llvm/test/tools/llubi/array_count_truncation.ll
index 52362bff98a2d..bc2971e95b1f8 100644
--- a/llvm/test/tools/llubi/array_count_truncation.ll
+++ b/llvm/test/tools/llubi/array_count_truncation.ll
@@ -8,6 +8,6 @@ define void @main() {
   ret void
 }
 
-; CHECK: The number of elements of [4294967296 x [0 x i8]] is too large!
+; CHECK: [4294967296 x [0 x i8]] is too large!
 ; CHECK-NEXT:   %v = load [4294967296 x [0 x i8]], ptr @g, align 1 at @main <stdin>:6
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/shufflevector_large_size.ll b/llvm/test/tools/llubi/shufflevector_large_size.ll
index 1c3fe8227bdaa..b223e6b1e9437 100644
--- a/llvm/test/tools/llubi/shufflevector_large_size.ll
+++ b/llvm/test/tools/llubi/shufflevector_large_size.ll
@@ -6,6 +6,6 @@ define void @main() {
   ret void
 }
 
-; CHECK: The number of elements of <vscale x 1073741823 x i32> is too large!
+; CHECK: <vscale x 1073741823 x i32> is too large!
 ; CHECK-NEXT:   %x = shufflevector <vscale x 1073741823 x i32> poison, <vscale x 1073741823 x i32> poison, <vscale x 1 x i32> zeroinitializer at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/target_ext_store_vector_element.ll b/llvm/test/tools/llubi/target_ext_store_vector_element.ll
index e9d9894affb8c..cc51ed229d554 100644
--- a/llvm/test/tools/llubi/target_ext_store_vector_element.ll
+++ b/llvm/test/tools/llubi/target_ext_store_vector_element.ll
@@ -5,6 +5,6 @@ define void @main() {
   store <2 x target("llvm.test.vectorelement")> poison, ptr null
   ret void
 }
-; CHECK: Unsupported type <2 x target("llvm.test.vectorelement")>
+; CHECK: Unsupported type target("llvm.test.vectorelement")
 ; CHECK-NEXT:   store <2 x target("llvm.test.vectorelement")> poison, ptr null, align 8 at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll b/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll
index 934716e76c234..b2558fad6bed3 100644
--- a/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll
+++ b/llvm/test/tools/llubi/vector_reduce_fmax_empty.ll
@@ -5,6 +5,6 @@ define void @main() {
   %r = call float @llvm.vector.reduce.fmax.nxv1073741824f32(<vscale x 1073741824 x float> zeroinitializer)
   ret void
 }
-; CHECK: The number of elements of <vscale x 1073741824 x float> is too large!
+; CHECK: <vscale x 1073741824 x float> is too large!
 ; CHECK-NEXT:   %r = call float @llvm.vector.reduce.fmax.nxv1073741824f32(<vscale x 1073741824 x float> zeroinitializer) at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll b/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll
index 7c4a404131b2a..60f4f28cb4296 100644
--- a/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll
+++ b/llvm/test/tools/llubi/vector_reduce_fmaximum_empty.ll
@@ -5,6 +5,6 @@ define void @main() {
   %r = call float @llvm.vector.reduce.fmaximum.nxv1073741824f32(<vscale x 1073741824 x float> zeroinitializer)
   ret void
 }
-; CHECK: The number of elements of <vscale x 1073741824 x float> is too large!
+; CHECK: <vscale x 1073741824 x float> is too large!
 ; CHECK-NEXT:   %r = call float @llvm.vector.reduce.fmaximum.nxv1073741824f32(<vscale x 1073741824 x float> zeroinitializer) at @main <stdin>:5
 ; CHECK-NEXT: error: -: input module cannot be executed!
diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp
index 9dd953e63507e..de085fc0b9c9f 100644
--- a/llvm/tools/llubi/lib/Context.cpp
+++ b/llvm/tools/llubi/lib/Context.cpp
@@ -1270,58 +1270,102 @@ uint64_t Context::getEffectiveTypeStoreSize(Type *Ty) {
   return getEffectiveTypeSize(DL.getTypeStoreSize(Ty));
 }
 
-bool Context::isValid(raw_ostream &OS) const {
-  assert(isPowerOf2_32(VScale) && "VScale must be a power of two.");
-  uint32_t MaxNumElements = UINT32_MAX / VScale;
-
-  SmallPtrSet<Type *, 16> ValidAggTys;
-  auto IsSupportedType = [&](auto &&Self, Type *Ty) {
-    switch (Ty->getTypeID()) {
-    case Type::X86_AMXTyID:
-    case Type::TargetExtTyID:
-    case Type::TokenTyID:
-      OS << "Unsupported type " << *Ty << '\n';
-      return false;
-    case Type::ScalableVectorTyID:
-      // Avoid overflow when computing EVL.
-      if (cast<VectorType>(Ty)->getElementCount().getKnownMinValue() >=
-          MaxNumElements) {
-        OS << "The number of elements of " << *Ty << " is too large!\n";
-        return false;
-      }
-      [[fallthrough]];
-    case Type::FixedVectorTyID:
-      if (cast<VectorType>(Ty)->getElementType()->isTargetExtTy()) {
-        OS << "Unsupported type " << *Ty << '\n';
-        return false;
-      }
-      return true;
-    case Type::ArrayTyID:
-      // The number of elements is used as uint32_t in the value
-      // representation. Reject arrays with too many elements to avoid
-      // truncation.
-      if (cast<ArrayType>(Ty)->getNumElements() > UINT32_MAX) {
-        OS << "The number of elements of " << *Ty << " is too large!\n";
-        return false;
-      }
-      [[fallthrough]];
-    case Type::StructTyID:
-      if (auto *STy = dyn_cast<StructType>(Ty); STy && STy->isOpaque()) {
-        OS << "Unsupported opaque struct type %" << STy->getName() << "\n";
-        return false;
+/// Check whether \p Ty is small enough to be emulated. Return the total bits
+/// the type (we use the allocation size as a overestimation) if it is
+/// supported. Otherwise, return std::nullopt.
+/// In fact it duplicates the logic from DL.getTypeAllocSizeInBits, with careful
+/// handling of overflows.
+std::optional<uint64_t> Context::isSupportedType(
+    raw_ostream &OS, DenseMap<Type *, uint64_t> &ValidAggTys, Type *Ty) const {
+  constexpr uint64_t MaxNumBits = UINT32_MAX;
+  uint32_t MaxNumBitsPerElement = MaxNumBits;
+
+  switch (Ty->getTypeID()) {
+  case Type::X86_AMXTyID:
+  case Type::TargetExtTyID:
+  case Type::TokenTyID:
+    OS << "Unsupported type " << *Ty << '\n';
+    return std::nullopt;
+  case Type::ScalableVectorTyID:
+    assert(isPowerOf2_32(VScale) && "VScale must be a power of two.");
+    MaxNumBitsPerElement /= VScale;
+    [[fallthrough]];
+  case Type::FixedVectorTyID: {
+    ElementCount EC = cast<VectorType>(Ty)->getElementCount();
+    MaxNumBitsPerElement /= EC.getKnownMinValue();
+    auto ElementBits = isSupportedType(OS, ValidAggTys,
+                                       cast<VectorType>(Ty)->getElementType());
+    if (!ElementBits)
+      return std::nullopt;
+    if (*ElementBits > MaxNumBitsPerElement) {
+      OS << *Ty << " is too large!\n";
+      return std::nullopt;
+    }
+    // Now the multiplication never overflows.
+    return *ElementBits * getEVL(EC);
+  }
+  case Type::ArrayTyID: {
+    if (uint64_t Size = ValidAggTys.lookup(Ty))
+      return Size;
+    uint64_t Scale =
+        std::max(cast<ArrayType>(Ty)->getNumElements(), (uint64_t)1);
+    MaxNumBitsPerElement /= Scale;
+    auto ElementBits =
+        isSupportedType(OS, ValidAggTys, cast<ArrayType>(Ty)->getElementType());
+    if (!ElementBits)
+      return std::nullopt;
+    if (*ElementBits > MaxNumBitsPerElement) {
+      OS << *Ty << " is too large!\n";
+      return std::nullopt;
+    }
+    // Now the multiplication never overflows.
+    return ValidAggTys.insert({Ty, *ElementBits * Scale}).first->second;
+  }
+  case Type::StructTyID: {
+    auto *STy = cast<StructType>(Ty);
+    if (STy->isOpaque()) {
+      OS << "Unsupported opaque struct type %" << STy->getName() << '\n';
+      return std::nullopt;
+    }
+    if (uint64_t Size = ValidAggTys.lookup(Ty))
+      return Size;
+    uint64_t Size = 0, Alignment = 8;
+    for (unsigned I = 0, E = STy->getNumElements(); I != E; ++I) {
+      Type *ElementType = STy->getElementType(I);
+      auto ElementBits = isSupportedType(OS, ValidAggTys, ElementType);
+      if (!ElementBits)
+        return std::nullopt;
+      if (!STy->isPacked()) {
+        uint64_t ElementAlignment = DL.getABITypeAlign(ElementType).value() * 8;
+        Size = alignTo(Size, ElementAlignment);
+        Alignment = std::max(Alignment, ElementAlignment);
       }
-      if (ValidAggTys.contains(Ty))
-        return true;
-      for (unsigned I = 0, E = Ty->getNumContainedTypes(); I != E; ++I)
-        if (!Self(Self, Ty->getContainedType(I)))
-          return false;
-      ValidAggTys.insert(Ty);
-      return true;
-    default:
-      return true;
+      // The addition never overflows.
+      Size += *ElementBits;
     }
-  };
+    Size = std::max(Size, (uint64_t)1);
+    if (!STy->isPacked()) {
+      // If it is not packed, we need to respect StructABIAlignment specified in
+      // the DL string.
+      Alignment = std::max(Alignment, DL.getABITypeAlign(STy).value() * 8);
+    }
+    Size = alignTo(Size, Alignment);
+    if (Size > MaxNumBits) {
+      OS << *Ty << " is too large!\n";
+      return std::nullopt;
+    }
+    return ValidAggTys.insert({Ty, Size}).first->second;
+  }
+  default:
+    // All scalar single value types are known smaller than MaxNumBits.
+    if (Ty->isSingleValueType())
+      return DL.getTypeAllocSizeInBits(Ty);
+    // void/metadata/label/function types are always supported.
+    return 0;
+  }
+}
 
+bool Context::isValid(raw_ostream &OS) const {
   StringRef ModuleFileName = M.getModuleIdentifier();
 
   auto DumpFunction = [&](Function *F) {
@@ -1368,8 +1412,9 @@ bool Context::isValid(raw_ostream &OS) const {
     return false;
   };
 
+  DenseMap<Type *, uint64_t> ValidAggTys;
 #define CHECK(TYPE, LOC)                                                       \
-  if (!IsSupportedType(IsSupportedType, TYPE))                                 \
+  if (!isSupportedType(OS, ValidAggTys, TYPE))                                 \
     return LOC;
 
   for (auto &F : M) {
diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h
index 10c6effea90ac..646881b7c02eb 100644
--- a/llvm/tools/llubi/lib/Context.h
+++ b/llvm/tools/llubi/lib/Context.h
@@ -228,6 +228,10 @@ class Context {
   const DataLayout &DL;
   const TargetLibraryInfoImpl TLIImpl;
 
+  std::optional<uint64_t>
+  isSupportedType(raw_ostream &OS, DenseMap<Type *, uint64_t> &ValidAggTys,
+                  Type *Ty) const;
+
   // Configuration
   uint64_t MaxMem = 0;
   uint32_t VScale = 4;



More information about the llvm-commits mailing list