[llvm] [IR] Don't allow values of opaque type (PR #137625)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 28 05:46:04 PDT 2025
https://github.com/nikic created https://github.com/llvm/llvm-project/pull/137625
Consider opaque types as non-first-class types, i.e. do not allow SSA values to have opaque type.
>From af9d62999fc6e34d9465610b57b4372e591372e5 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Mon, 28 Apr 2025 12:22:00 +0200
Subject: [PATCH] [IR] Don't allow values of opaque type
Consider opaque types as non-first-class types, i.e. do not allow
SSA values to have opaque type.
---
llvm/include/llvm/IR/Type.h | 4 +---
llvm/lib/IR/Type.cpp | 14 ++++++++++++++
llvm/lib/Linker/IRMover.cpp | 2 ++
.../test/Assembler/2004-11-28-InvalidTypeCrash.ll | 2 +-
.../Assembler/2005-05-05-OpaqueUndefValues.ll | 5 +++--
.../Assembler/2007-01-02-Undefined-Arg-Type.ll | 2 +-
.../CodeGen/WebAssembly/externref-unsized-load.ll | 2 +-
.../WebAssembly/externref-unsized-store.ll | 2 +-
.../test/CodeGen/X86/cfguard-x86-64-vectorcall.ll | 3 +--
llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll | 3 +--
llvm/test/Linker/Inputs/pr22807-1.ll | 6 ------
.../Linker/Inputs/{pr22807-2.ll => pr22807.ll} | 4 +---
llvm/test/Linker/Inputs/type-unique-opaque.ll | 4 +---
llvm/test/Linker/intrinsics-with-unnamed-types.ll | 8 ++++----
llvm/test/Linker/pr22807.ll | 8 +++++---
llvm/test/Linker/type-unique-opaque.ll | 8 ++++----
.../test/ThinLTO/X86/Inputs/import_opaque_type.ll | 9 +--------
llvm/test/ThinLTO/X86/import_opaque_type.ll | 15 +++++----------
.../test/Transforms/InstSimplify/gv-alloca-cmp.ll | 2 +-
llvm/test/Transforms/Reg2Mem/non-token-test.ll | 14 ++++++--------
llvm/test/Verifier/memset-pattern-unsized.ll | 6 ++----
llvm/test/Verifier/nofpclass.ll | 10 ----------
llvm/test/Verifier/unsized-types-load.ll | 6 ++----
llvm/test/Verifier/unsized-types-store.ll | 8 +++-----
llvm/unittests/FuzzMutate/OperationsTest.cpp | 5 ++---
25 files changed, 63 insertions(+), 89 deletions(-)
delete mode 100644 llvm/test/Linker/Inputs/pr22807-1.ll
rename llvm/test/Linker/Inputs/{pr22807-2.ll => pr22807.ll} (54%)
diff --git a/llvm/include/llvm/IR/Type.h b/llvm/include/llvm/IR/Type.h
index a4c0648a9197e..37f6b2ed8ee68 100644
--- a/llvm/include/llvm/IR/Type.h
+++ b/llvm/include/llvm/IR/Type.h
@@ -286,9 +286,7 @@ class Type {
/// Return true if the type is "first class", meaning it is a valid type for a
/// Value.
- bool isFirstClassType() const {
- return getTypeID() != FunctionTyID && getTypeID() != VoidTyID;
- }
+ bool isFirstClassType() const;
/// Return true if the type is a valid type for a register in codegen. This
/// includes all first-class types except struct and array types.
diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp
index 884609e734298..3763980e9db82 100644
--- a/llvm/lib/IR/Type.cpp
+++ b/llvm/lib/IR/Type.cpp
@@ -247,6 +247,20 @@ int Type::getFPMantissaWidth() const {
return -1;
}
+bool Type::isFirstClassType() const {
+ switch (getTypeID()) {
+ default:
+ return true;
+ case FunctionTyID:
+ case VoidTyID:
+ return false;
+ case StructTyID: {
+ auto *ST = cast<StructType>(this);
+ return !ST->isOpaque();
+ }
+ }
+}
+
bool Type::isSizedDerivedType(SmallPtrSetImpl<Type*> *Visited) const {
if (auto *ATy = dyn_cast<ArrayType>(this))
return ATy->getElementType()->isSized(Visited);
diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp
index 98d69051c935a..086718a188845 100644
--- a/llvm/lib/Linker/IRMover.cpp
+++ b/llvm/lib/Linker/IRMover.cpp
@@ -1640,6 +1640,8 @@ Error IRLinker::run() {
if (GV.hasAppendingLinkage())
continue;
Value *NewValue = Mapper.mapValue(GV);
+ if (FoundError)
+ return std::move(*FoundError);
if (NewValue) {
auto *NewGV = dyn_cast<GlobalVariable>(NewValue->stripPointerCasts());
if (NewGV) {
diff --git a/llvm/test/Assembler/2004-11-28-InvalidTypeCrash.ll b/llvm/test/Assembler/2004-11-28-InvalidTypeCrash.ll
index 7260f19bfd9a5..98ad9eefdcd1e 100644
--- a/llvm/test/Assembler/2004-11-28-InvalidTypeCrash.ll
+++ b/llvm/test/Assembler/2004-11-28-InvalidTypeCrash.ll
@@ -1,5 +1,5 @@
; Test for PR463. This program is erroneous, but should not crash llvm-as.
; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
-; CHECK: use of undefined type named 'struct.none'
+; CHECK: invalid type for null constant
@.FOO = internal global %struct.none zeroinitializer
diff --git a/llvm/test/Assembler/2005-05-05-OpaqueUndefValues.ll b/llvm/test/Assembler/2005-05-05-OpaqueUndefValues.ll
index 01456f10c9dc1..b14f4c16ab2d0 100644
--- a/llvm/test/Assembler/2005-05-05-OpaqueUndefValues.ll
+++ b/llvm/test/Assembler/2005-05-05-OpaqueUndefValues.ll
@@ -1,5 +1,6 @@
-; RUN: llvm-as < %s | llvm-dis | llvm-as > /dev/null
-; RUN: verify-uselistorder %s
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: error: invalid type for undef constant
%t = type opaque
@x = global %t undef
diff --git a/llvm/test/Assembler/2007-01-02-Undefined-Arg-Type.ll b/llvm/test/Assembler/2007-01-02-Undefined-Arg-Type.ll
index 5f4db4ee06ba4..1d40c76bbad20 100644
--- a/llvm/test/Assembler/2007-01-02-Undefined-Arg-Type.ll
+++ b/llvm/test/Assembler/2007-01-02-Undefined-Arg-Type.ll
@@ -1,7 +1,7 @@
; The assembler should catch an undefined argument type .
; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
-; CHECK: use of undefined type named 'typedef.bc_struct'
+; CHECK: invalid type for function argument
; %typedef.bc_struct = type opaque
diff --git a/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll b/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll
index 945045d902ef5..1f8f4d5140c51 100644
--- a/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll
+++ b/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll
@@ -7,4 +7,4 @@ define void @load_extern(%externref %ref) {
ret void
}
-; CHECK-ERROR: error: loading unsized types is not allowed
+; CHECK-ERROR: error: load operand must be a pointer to a first class type
diff --git a/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll b/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll
index ca01f69a08ad2..c7e062d1b0526 100644
--- a/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll
+++ b/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll
@@ -7,4 +7,4 @@ define void @store_extern(%externref %ref) {
ret void
}
-; CHECK-ERROR: error: storing unsized types is not allowed
+; CHECK-ERROR: error: invalid type for undef constant
diff --git a/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll b/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll
index 8dc33fb98bf0c..7748f8007c619 100644
--- a/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll
+++ b/llvm/test/CodeGen/X86/cfguard-x86-64-vectorcall.ll
@@ -3,6 +3,7 @@
; RUN: llc < %s -mtriple=x86_64-w64-windows-gnu | FileCheck %s -check-prefix=X64
; Control Flow Guard is currently only available on Windows
+%struct.HVA = type { double, double, double, double }
; Test that Control Flow Guard checks are correctly added for x86_64 vector calls.
define void @func_cf_vector_x64(ptr %0, ptr %1) #0 {
@@ -37,8 +38,6 @@ entry:
}
attributes #0 = { "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" }
-%struct.HVA = type { double, double, double, double }
-
declare void @llvm.memcpy.p0.p0.i64(ptr nocapture writeonly, ptr nocapture readonly, i64, i1 immarg) #1
attributes #1 = { argmemonly nounwind willreturn }
diff --git a/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll b/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll
index a75973310d15c..fd95e923df7ce 100644
--- a/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll
+++ b/llvm/test/CodeGen/X86/cfguard-x86-vectorcall.ll
@@ -2,6 +2,7 @@
; RUN: llc < %s -mtriple=i686-w64-windows-gnu | FileCheck %s -check-prefix=X86
; Control Flow Guard is currently only available on Windows
+%struct.HVA = type { double, double, double, double }
; Test that Control Flow Guard checks are correctly added for x86 vector calls.
define void @func_cf_vector_x86(ptr %0, ptr %1) #0 {
@@ -32,8 +33,6 @@ entry:
}
attributes #0 = { "target-cpu"="pentium4" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" }
-%struct.HVA = type { double, double, double, double }
-
declare void @llvm.memcpy.p0.p0.i32(ptr nocapture writeonly, ptr nocapture readonly, i32, i1 immarg) #1
attributes #1 = { argmemonly nounwind willreturn }
diff --git a/llvm/test/Linker/Inputs/pr22807-1.ll b/llvm/test/Linker/Inputs/pr22807-1.ll
deleted file mode 100644
index a1006bf409a13..0000000000000
--- a/llvm/test/Linker/Inputs/pr22807-1.ll
+++ /dev/null
@@ -1,6 +0,0 @@
-%struct.A = type { %struct.B }
-%struct.B = type opaque
-
-define i32 @foo(%struct.A** %A) {
- ret i32 0
-}
diff --git a/llvm/test/Linker/Inputs/pr22807-2.ll b/llvm/test/Linker/Inputs/pr22807.ll
similarity index 54%
rename from llvm/test/Linker/Inputs/pr22807-2.ll
rename to llvm/test/Linker/Inputs/pr22807.ll
index 137638e2987a2..d5ebcccb4756f 100644
--- a/llvm/test/Linker/Inputs/pr22807-2.ll
+++ b/llvm/test/Linker/Inputs/pr22807.ll
@@ -1,6 +1,4 @@
%struct.A = type { %struct.B }
%struct.B = type opaque
-define i32 @bar(%struct.A %A) {
- ret i32 0
-}
+ at g = external global %struct.A
diff --git a/llvm/test/Linker/Inputs/type-unique-opaque.ll b/llvm/test/Linker/Inputs/type-unique-opaque.ll
index 2e6f45ec84fe1..9d12d3b19f3a7 100644
--- a/llvm/test/Linker/Inputs/type-unique-opaque.ll
+++ b/llvm/test/Linker/Inputs/type-unique-opaque.ll
@@ -1,6 +1,4 @@
%t = type { i8 }
%t2 = type { %t, i16 }
-define %t2 @f() {
- ret %t2 { %t { i8 0 }, i16 0 }
-}
+ at g = external global %t2
diff --git a/llvm/test/Linker/intrinsics-with-unnamed-types.ll b/llvm/test/Linker/intrinsics-with-unnamed-types.ll
index 0772a764e10fa..d870e7f100e05 100644
--- a/llvm/test/Linker/intrinsics-with-unnamed-types.ll
+++ b/llvm/test/Linker/intrinsics-with-unnamed-types.ll
@@ -7,8 +7,8 @@
; Make sure we can link files with clashing intrinsic names using unnamed types.
;--- f01.ll
-%1 = type opaque
-%0 = type opaque
+%1 = type { i32 }
+%0 = type { i64 }
; CHECK-LABEL: @test01(
; CHECK: %c1 = call %0 @llvm.ssa.copy.s_s.0(%0 %arg)
@@ -38,8 +38,8 @@ bb:
}
;--- f02.ll
-%1 = type opaque
-%2 = type opaque
+%1 = type { i8 }
+%2 = type { i16 }
; CHECK-LABEL: @test03(
; CHECK: %c1 = call %3 @llvm.ssa.copy.s_s.2(%3 %arg)
diff --git a/llvm/test/Linker/pr22807.ll b/llvm/test/Linker/pr22807.ll
index a1fe38480c6ee..24bcf1732112b 100644
--- a/llvm/test/Linker/pr22807.ll
+++ b/llvm/test/Linker/pr22807.ll
@@ -1,10 +1,12 @@
-; RUN: not llvm-link -S -o - %p/pr22807.ll %p/Inputs/pr22807-1.ll %p/Inputs/pr22807-2.ll 2>&1 | FileCheck %s
+; RUN: not llvm-link -S -o - %p/pr22807.ll %p/Inputs/pr22807.ll 2>&1 | FileCheck %s
; CHECK: error: identified structure type 'struct.A' is recursive
%struct.B = type { %struct.A }
%struct.A = type opaque
-define i32 @baz(%struct.B %BB) {
- ret i32 0
+ at g = external global %struct.B
+
+define ptr @test() {
+ ret ptr @g
}
diff --git a/llvm/test/Linker/type-unique-opaque.ll b/llvm/test/Linker/type-unique-opaque.ll
index 72466f2e134a2..3fc49b50102f0 100644
--- a/llvm/test/Linker/type-unique-opaque.ll
+++ b/llvm/test/Linker/type-unique-opaque.ll
@@ -4,13 +4,13 @@
; not cause %u and %t to get merged.
; CHECK: %u = type opaque
-; CHECK: define %u @g(%u %a) {
+; CHECK: external global %u
%u = type opaque
%u2 = type { %u, i8 }
-declare %u2 @f()
+ at g = external global %u
-define %u @g(%u %a) {
- ret %u %a
+define ptr @test() {
+ ret ptr @g
}
diff --git a/llvm/test/ThinLTO/X86/Inputs/import_opaque_type.ll b/llvm/test/ThinLTO/X86/Inputs/import_opaque_type.ll
index ecdaa727fb0df..e72f2d9fe57f6 100644
--- a/llvm/test/ThinLTO/X86/Inputs/import_opaque_type.ll
+++ b/llvm/test/ThinLTO/X86/Inputs/import_opaque_type.ll
@@ -3,11 +3,4 @@ target triple = "x86_64-apple-macosx10.11.0"
%a = type { i8 }
-define void @bar(%a) {
- ret void
-}
-
-define void @baz() {
- call void @bar(%a undef)
- ret void
-}
+ at g = external global %a
diff --git a/llvm/test/ThinLTO/X86/import_opaque_type.ll b/llvm/test/ThinLTO/X86/import_opaque_type.ll
index ecb7b927bd8b6..1e8aade3eccd3 100644
--- a/llvm/test/ThinLTO/X86/import_opaque_type.ll
+++ b/llvm/test/ThinLTO/X86/import_opaque_type.ll
@@ -3,23 +3,18 @@
; RUN: opt -module-summary %p/Inputs/import_opaque_type.ll -o %t2.bc
; RUN: llvm-lto -thinlto-action=thinlink -o %t3.bc %t.bc %t2.bc
-; Check that we import correctly the imported type to replace the opaque one here
; RUN: llvm-lto -thinlto-action=import %t.bc -thinlto-index=%t3.bc -o - | llvm-dis -o - | FileCheck %s
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.11.0"
-; CHECK: %a = type { i8 }
+; FIXME: It would be better to produce %a = type { i8 } here
+; CHECK: %a = type opaque
%a = type opaque
-declare void @baz()
-define void @foo(%a) {
- call void @baz()
- ret void
-}
+ at g = external global %a
-define i32 @main() {
- call void @foo(%a undef)
- ret i32 0
+define ptr @test() {
+ ret ptr @g
}
diff --git a/llvm/test/Transforms/InstSimplify/gv-alloca-cmp.ll b/llvm/test/Transforms/InstSimplify/gv-alloca-cmp.ll
index 4f2544052b215..f30c6379e47e1 100644
--- a/llvm/test/Transforms/InstSimplify/gv-alloca-cmp.ll
+++ b/llvm/test/Transforms/InstSimplify/gv-alloca-cmp.ll
@@ -46,7 +46,7 @@ define i1 @cmp_gv_weak_alloca() {
}
%opaque = type opaque
- at gv_unsized = weak global %opaque zeroinitializer, align 16
+ at gv_unsized = external global %opaque, align 16
define i1 @cmp_gv_unsized_alloca() {
; CHECK-LABEL: define i1 @cmp_gv_unsized_alloca() {
diff --git a/llvm/test/Transforms/Reg2Mem/non-token-test.ll b/llvm/test/Transforms/Reg2Mem/non-token-test.ll
index e30f37abaa443..cdf09bb6de9b4 100644
--- a/llvm/test/Transforms/Reg2Mem/non-token-test.ll
+++ b/llvm/test/Transforms/Reg2Mem/non-token-test.ll
@@ -1,24 +1,22 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -passes=reg2mem -S < %s | FileCheck %s
-%opaque = type opaque
-
-declare %opaque @ret_opaque()
-declare void @pass_opaque(%opaque)
+declare target("opaque") @ret_opaque()
+declare void @pass_opaque(target("opaque"))
define void @test() {
; CHECK-LABEL: @test(
; CHECK-NEXT: %"reg2mem alloca point" = bitcast i32 0 to i32
-; CHECK-NEXT: [[X:%.*]] = call [[OPAQUE:%.*]] @ret_opaque()
+; CHECK-NEXT: [[X:%.*]] = call target("opaque") @ret_opaque()
; CHECK-NEXT: br label [[NEXT:%.*]]
; CHECK: next:
-; CHECK-NEXT: call void @pass_opaque([[OPAQUE]] [[X]])
+; CHECK-NEXT: call void @pass_opaque(target("opaque") [[X]])
; CHECK-NEXT: ret void
;
- %x = call %opaque @ret_opaque()
+ %x = call target("opaque") @ret_opaque()
br label %next
next:
- call void @pass_opaque(%opaque %x)
+ call void @pass_opaque(target("opaque") %x)
ret void
}
diff --git a/llvm/test/Verifier/memset-pattern-unsized.ll b/llvm/test/Verifier/memset-pattern-unsized.ll
index 71b7dca9a5a19..784fe1c7484ee 100644
--- a/llvm/test/Verifier/memset-pattern-unsized.ll
+++ b/llvm/test/Verifier/memset-pattern-unsized.ll
@@ -2,9 +2,7 @@
; CHECK: unsized types cannot be used as memset patterns
-%X = type opaque
-define void @bar(ptr %P, %X %value) {
- call void @llvm.experimental.memset.pattern.p0.s_s.i32.0(ptr %P, %X %value, i32 4, i1 false)
+define void @bar(ptr %P, target("foo") %value) {
+ call void @llvm.experimental.memset.pattern.p0.s_s.i32.0(ptr %P, target("foo") %value, i32 4, i1 false)
ret void
}
-declare void @llvm.experimental.memset.pattern.p0.s_s.i32.0(ptr nocapture, %X, i32, i1) nounwind
diff --git a/llvm/test/Verifier/nofpclass.ll b/llvm/test/Verifier/nofpclass.ll
index ea140ac6d97ef..3569598e91fb8 100644
--- a/llvm/test/Verifier/nofpclass.ll
+++ b/llvm/test/Verifier/nofpclass.ll
@@ -45,16 +45,6 @@ define nofpclass(nan) [4 x <8 x i32>] @nofpclass_vector_array_int([4 x <8 x i32>
ret [4 x <8 x i32>] %arg
}
-%opaque = type opaque
-
-; CHECK: 'nofpclass(nan)' applied to incompatible type!
-; CHECK-NEXT: ptr @nofpclass_opaque_type
-; CHECK-NEXT: 'nofpclass(zero)' applied to incompatible type!
-; CHECK-NEXT: ptr @nofpclass_opaque_type
-define nofpclass(nan) %opaque @nofpclass_opaque_type(%opaque nofpclass(zero) %arg) {
- ret %opaque %arg
-}
-
%struct = type { i32, float }
; CHECK: 'nofpclass(nan)' applied to incompatible type!
diff --git a/llvm/test/Verifier/unsized-types-load.ll b/llvm/test/Verifier/unsized-types-load.ll
index fed6540f2c8cb..5959ddc8fde0e 100644
--- a/llvm/test/Verifier/unsized-types-load.ll
+++ b/llvm/test/Verifier/unsized-types-load.ll
@@ -1,10 +1,8 @@
; RUN: not opt -passes=verify < %s 2>&1 | FileCheck %s
-%X = type opaque
-
define void @f_0(ptr %ptr) {
- %t = load %X, ptr %ptr
+ %t = load target("foo"), ptr %ptr
ret void
; CHECK: loading unsized types is not allowed
-; CHECK-NEXT: %t = load %X, ptr %ptr
+; CHECK-NEXT: %t = load target("foo"), ptr %ptr
}
diff --git a/llvm/test/Verifier/unsized-types-store.ll b/llvm/test/Verifier/unsized-types-store.ll
index 3748fde8470ef..7b7e0e8c8a6ed 100644
--- a/llvm/test/Verifier/unsized-types-store.ll
+++ b/llvm/test/Verifier/unsized-types-store.ll
@@ -1,10 +1,8 @@
; RUN: not opt -passes=verify < %s 2>&1 | FileCheck %s
-%X = type opaque
-
-define void @f_1(%X %val, ptr %ptr) {
- store %X %val, ptr %ptr
+define void @f_1(target("foo") %val, ptr %ptr) {
+ store target("foo") %val, ptr %ptr
ret void
; CHECK: storing unsized types is not allowed
-; CHECK-NEXT: store %X %val, ptr %ptr
+; CHECK-NEXT: store target("foo") %val, ptr %ptr
}
diff --git a/llvm/unittests/FuzzMutate/OperationsTest.cpp b/llvm/unittests/FuzzMutate/OperationsTest.cpp
index e4ee6d96ba187..666931c9f40f4 100644
--- a/llvm/unittests/FuzzMutate/OperationsTest.cpp
+++ b/llvm/unittests/FuzzMutate/OperationsTest.cpp
@@ -380,9 +380,8 @@ TEST(OperationsTest, GEPPointerOperand) {
// Check that we only pick sized pointers for the GEP instructions
LLVMContext Ctx;
- const char *SourceCode = "%opaque = type opaque\n"
- "declare void @f()\n"
- "define void @test(%opaque %o) {\n"
+ const char *SourceCode = "declare void @f()\n"
+ "define void @test(target(\"foo\") %o) {\n"
" %a = alloca i64, i32 10\n"
" ret void\n"
"}";
More information about the llvm-commits
mailing list