[IR] Don't allow values of opaque type (#137625)
Consider opaque types as non-first-class types, i.e. do not allow SSA values to have opaque type.
This commit is contained in:
@@ -4122,6 +4122,30 @@ except :ref:`label <t_label>` and :ref:`metadata <t_metadata>`.
|
||||
| ``{i32, i32} (i32)`` | A function taking an ``i32``, returning a :ref:`structure <t_struct>` containing two ``i32`` values |
|
||||
+---------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
.. _t_opaque:
|
||||
|
||||
Opaque Structure Types
|
||||
----------------------
|
||||
|
||||
:Overview:
|
||||
|
||||
Opaque structure types are used to represent structure types that
|
||||
do not have a body specified. This corresponds (for example) to the C
|
||||
notion of a forward declared structure. They can be named (``%X``) or
|
||||
unnamed (``%52``).
|
||||
|
||||
It is not possible to create SSA values with an opaque structure type. In
|
||||
practice, this largely limits their use to the value type of external globals.
|
||||
|
||||
:Syntax:
|
||||
|
||||
::
|
||||
|
||||
%X = type opaque
|
||||
%52 = type opaque
|
||||
|
||||
@g = external global %X
|
||||
|
||||
.. _t_firstclass:
|
||||
|
||||
First Class Types
|
||||
@@ -4562,31 +4586,6 @@ opaqued and are never uniqued. Identified types must not be recursive.
|
||||
| ``<{ i8, i32 }>`` | A packed struct known to be 5 bytes in size. |
|
||||
+------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
.. _t_opaque:
|
||||
|
||||
Opaque Structure Types
|
||||
""""""""""""""""""""""
|
||||
|
||||
:Overview:
|
||||
|
||||
Opaque structure types are used to represent structure types that
|
||||
do not have a body specified. This corresponds (for example) to the C
|
||||
notion of a forward declared structure. They can be named (``%X``) or
|
||||
unnamed (``%52``).
|
||||
|
||||
:Syntax:
|
||||
|
||||
::
|
||||
|
||||
%X = type opaque
|
||||
%52 = type opaque
|
||||
|
||||
:Examples:
|
||||
|
||||
+--------------+-------------------+
|
||||
| ``opaque`` | An opaque type. |
|
||||
+--------------+-------------------+
|
||||
|
||||
.. _constants:
|
||||
|
||||
Constants
|
||||
|
||||
@@ -286,9 +286,7 @@ public:
|
||||
|
||||
/// 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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
%struct.A = type { %struct.B }
|
||||
%struct.B = type opaque
|
||||
|
||||
define i32 @foo(%struct.A** %A) {
|
||||
ret i32 0
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
%struct.A = type { %struct.B }
|
||||
%struct.B = type opaque
|
||||
|
||||
define i32 @bar(%struct.A %A) {
|
||||
ret i32 0
|
||||
}
|
||||
@g = external global %struct.A
|
||||
@@ -1,6 +1,4 @@
|
||||
%t = type { i8 }
|
||||
%t2 = type { %t, i16 }
|
||||
|
||||
define %t2 @f() {
|
||||
ret %t2 { %t { i8 0 }, i16 0 }
|
||||
}
|
||||
@g = external global %t2
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@g = external global %struct.B
|
||||
|
||||
define ptr @test() {
|
||||
ret ptr @g
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
@g = external global %u
|
||||
|
||||
define %u @g(%u %a) {
|
||||
ret %u %a
|
||||
define ptr @test() {
|
||||
ret ptr @g
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@g = external global %a
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@g = external global %a
|
||||
|
||||
define i32 @main() {
|
||||
call void @foo(%a undef)
|
||||
ret i32 0
|
||||
define ptr @test() {
|
||||
ret ptr @g
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ define i1 @cmp_gv_weak_alloca() {
|
||||
}
|
||||
|
||||
%opaque = type opaque
|
||||
@gv_unsized = weak global %opaque zeroinitializer, align 16
|
||||
@gv_unsized = external global %opaque, align 16
|
||||
|
||||
define i1 @cmp_gv_unsized_alloca() {
|
||||
; CHECK-LABEL: define i1 @cmp_gv_unsized_alloca() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
"}";
|
||||
|
||||
Reference in New Issue
Block a user