mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-29 23:36:05 +00:00
Implement intrinsic mangling for literal struct types.
Fixes PR 31921 Summary: Predicateinfo requires an ugly workaround to try to avoid literal struct types due to the intrinsic mangling not being implemented. This workaround actually does not work in all cases (you can hit the assert by bootstrapping with -print-predicateinfo), and can't be made to work without DFS'ing the type (IE copying getMangledStr and using a version that detects if it would crash). Rather than do that, i just implemented the mangling. It seems simple, since they are unified structurally. Looking at the overloaded-mangling testcase we have, it actually turns out the gc intrinsics will *also* crash if you try to use a literal struct. Thus, the testcase added fails before this patch, and works after, without needing to resort to predicateinfo. Reviewers: chandlerc, davide Subscribers: llvm-commits, sanjoy Differential Revision: https://reviews.llvm.org/D29925 llvm-svn: 295253
This commit is contained in:
parent
9584508d5c
commit
3c1432fecf
@ -489,6 +489,12 @@ static bool UpgradeIntrinsicFunction1(Function *F, Function *&NewFn) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Remangle our intrinsic since we upgrade the mangling
|
||||||
|
auto Result = llvm::Intrinsic::remangleIntrinsicFunction(F);
|
||||||
|
if (Result != None) {
|
||||||
|
NewFn = Result.getValue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// This may not belong here. This function is effectively being overloaded
|
// This may not belong here. This function is effectively being overloaded
|
||||||
// to both detect an intrinsic which needs upgrading, and to provide the
|
// to both detect an intrinsic which needs upgrading, and to provide the
|
||||||
@ -1821,8 +1827,17 @@ void llvm::UpgradeIntrinsicCall(CallInst *CI, Function *NewFn) {
|
|||||||
CI->setName(Name + ".old");
|
CI->setName(Name + ".old");
|
||||||
|
|
||||||
switch (NewFn->getIntrinsicID()) {
|
switch (NewFn->getIntrinsicID()) {
|
||||||
default:
|
default: {
|
||||||
llvm_unreachable("Unknown function for CallInst upgrade.");
|
// Handle generic mangling change, but nothing else
|
||||||
|
assert(
|
||||||
|
(CI->getCalledFunction()->getName() != NewFn->getName()) &&
|
||||||
|
"Unknown function for CallInst upgrade and isn't just a name change");
|
||||||
|
SmallVector<Value *, 4> Args(CI->arg_operands().begin(),
|
||||||
|
CI->arg_operands().end());
|
||||||
|
CI->replaceAllUsesWith(Builder.CreateCall(NewFn, Args));
|
||||||
|
CI->eraseFromParent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case Intrinsic::arm_neon_vld1:
|
case Intrinsic::arm_neon_vld1:
|
||||||
case Intrinsic::arm_neon_vld2:
|
case Intrinsic::arm_neon_vld2:
|
||||||
|
@ -505,10 +505,18 @@ static std::string getMangledTypeStr(Type* Ty) {
|
|||||||
} else if (ArrayType* ATyp = dyn_cast<ArrayType>(Ty)) {
|
} else if (ArrayType* ATyp = dyn_cast<ArrayType>(Ty)) {
|
||||||
Result += "a" + llvm::utostr(ATyp->getNumElements()) +
|
Result += "a" + llvm::utostr(ATyp->getNumElements()) +
|
||||||
getMangledTypeStr(ATyp->getElementType());
|
getMangledTypeStr(ATyp->getElementType());
|
||||||
} else if (StructType* STyp = dyn_cast<StructType>(Ty)) {
|
} else if (StructType *STyp = dyn_cast<StructType>(Ty)) {
|
||||||
assert(!STyp->isLiteral() && "TODO: implement literal types");
|
if (!STyp->isLiteral()) {
|
||||||
Result += STyp->getName();
|
Result += "s_";
|
||||||
} else if (FunctionType* FT = dyn_cast<FunctionType>(Ty)) {
|
Result += STyp->getName();
|
||||||
|
} else {
|
||||||
|
Result += "sl_";
|
||||||
|
for (auto Elem : STyp->elements())
|
||||||
|
Result += getMangledTypeStr(Elem);
|
||||||
|
}
|
||||||
|
// Ensure nested structs are distinguishable.
|
||||||
|
Result += "s";
|
||||||
|
} else if (FunctionType *FT = dyn_cast<FunctionType>(Ty)) {
|
||||||
Result += "f_" + getMangledTypeStr(FT->getReturnType());
|
Result += "f_" + getMangledTypeStr(FT->getReturnType());
|
||||||
for (size_t i = 0; i < FT->getNumParams(); i++)
|
for (size_t i = 0; i < FT->getNumParams(); i++)
|
||||||
Result += getMangledTypeStr(FT->getParamType(i));
|
Result += getMangledTypeStr(FT->getParamType(i));
|
||||||
|
@ -44,14 +44,41 @@ define <3 x i32>* @test_vAny(<3 x i32>* %v) gc "statepoint-example" {
|
|||||||
; struct
|
; struct
|
||||||
define %struct.test* @test_struct(%struct.test* %v) gc "statepoint-example" {
|
define %struct.test* @test_struct(%struct.test* %v) gc "statepoint-example" {
|
||||||
%tok = call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0, %struct.test* %v)
|
%tok = call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0, %struct.test* %v)
|
||||||
%v-new = call %struct.test* @llvm.experimental.gc.relocate.p0struct.test(token %tok, i32 7, i32 7)
|
%v-new = call %struct.test* @llvm.experimental.gc.relocate.p0s_struct.tests(token %tok, i32 7, i32 7)
|
||||||
ret %struct.test* %v-new
|
ret %struct.test* %v-new
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; literal struct with nested literal struct
|
||||||
|
define {i64, i64, {i64} }* @test_literal_struct({i64, i64, {i64}}* %v) gc "statepoint-example" {
|
||||||
|
%tok = call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0, {i64, i64, {i64}} *%v)
|
||||||
|
%v-new = call {i64, i64, {i64}}* @llvm.experimental.gc.relocate.p0sl_i64i64sl_i64ss.test(token %tok, i32 7, i32 7)
|
||||||
|
ret {i64, i64, {i64}}* %v-new
|
||||||
|
}
|
||||||
|
; struct with a horrible name, broken when structs were unprefixed
|
||||||
|
%i32 = type { i32 }
|
||||||
|
|
||||||
|
define %i32* @test_i32_struct(%i32* %v) gc "statepoint-example" {
|
||||||
|
entry:
|
||||||
|
%tok = call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0, %i32* %v)
|
||||||
|
%v-new = call %i32* @llvm.experimental.gc.relocate.p0s_i32s(token %tok, i32 7, i32 7)
|
||||||
|
ret %i32* %v-new
|
||||||
|
}
|
||||||
|
; completely broken intrinsic naming due to needing remangling. Just use random naming to test
|
||||||
|
|
||||||
|
define %i32* @test_broken_names(%i32* %v) gc "statepoint-example" {
|
||||||
|
entry:
|
||||||
|
%tok = call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.deadbeef(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0, %i32* %v)
|
||||||
|
%v-new = call %i32* @llvm.experimental.gc.relocate.beefdead(token %tok, i32 7, i32 7)
|
||||||
|
ret %i32* %v-new
|
||||||
|
}
|
||||||
declare zeroext i1 @return_i1()
|
declare zeroext i1 @return_i1()
|
||||||
declare token @llvm.experimental.gc.statepoint.p0f_i1f(i64, i32, i1 ()*, i32, i32, ...)
|
declare token @llvm.experimental.gc.statepoint.p0f_i1f(i64, i32, i1 ()*, i32, i32, ...)
|
||||||
declare i32* @llvm.experimental.gc.relocate.p0i32(token, i32, i32)
|
declare i32* @llvm.experimental.gc.relocate.p0i32(token, i32, i32)
|
||||||
declare float* @llvm.experimental.gc.relocate.p0f32(token, i32, i32)
|
declare float* @llvm.experimental.gc.relocate.p0f32(token, i32, i32)
|
||||||
declare [3 x i32]* @llvm.experimental.gc.relocate.p0a3i32(token, i32, i32)
|
declare [3 x i32]* @llvm.experimental.gc.relocate.p0a3i32(token, i32, i32)
|
||||||
declare <3 x i32>* @llvm.experimental.gc.relocate.p0v3i32(token, i32, i32)
|
declare <3 x i32>* @llvm.experimental.gc.relocate.p0v3i32(token, i32, i32)
|
||||||
declare %struct.test* @llvm.experimental.gc.relocate.p0struct.test(token, i32, i32)
|
declare %struct.test* @llvm.experimental.gc.relocate.p0s_struct.tests(token, i32, i32)
|
||||||
|
declare {i64, i64, {i64}}* @llvm.experimental.gc.relocate.p0sl_i64i64sl_i64ss.test(token, i32, i32)
|
||||||
|
declare %i32* @llvm.experimental.gc.relocate.p0s_i32s(token, i32, i32)
|
||||||
|
declare %i32* @llvm.experimental.gc.relocate.beefdead(token, i32, i32)
|
||||||
|
declare token @llvm.experimental.gc.statepoint.deadbeef(i64, i32, i1 ()*, i32, i32, ...)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
; RUN: llvm-link -disable-lazy-loading %t2.bc %t1.bc -S | FileCheck %s
|
; RUN: llvm-link -disable-lazy-loading %t2.bc %t1.bc -S | FileCheck %s
|
||||||
|
|
||||||
; Verify that we correctly rename the intrinsic and don't crash
|
; Verify that we correctly rename the intrinsic and don't crash
|
||||||
; CHECK: @llvm.masked.store.v4p0some_named_struct.0.p0v4p0some_named_struct.0
|
; CHECK: @llvm.masked.store.v4p0s_some_named_struct.0s.p0v4p0s_some_named_struct.0s
|
||||||
|
|
||||||
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
target triple = "x86_64-apple-macosx10.11.0"
|
target triple = "x86_64-apple-macosx10.11.0"
|
||||||
|
@ -317,34 +317,34 @@ TEST_F(LinkModuleTest, RemangleIntrinsics) {
|
|||||||
const char *FooStr =
|
const char *FooStr =
|
||||||
"%struct.rtx_def = type { i16 }\n"
|
"%struct.rtx_def = type { i16 }\n"
|
||||||
"define void @foo(%struct.rtx_def* %a, i8 %b, i32 %c) {\n"
|
"define void @foo(%struct.rtx_def* %a, i8 %b, i32 %c) {\n"
|
||||||
" call void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n"
|
" call void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n"
|
||||||
" ret void\n"
|
" ret void\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"declare void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def*, i8, i32, i32, i1)\n";
|
"declare void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def*, i8, i32, i32, i1)\n";
|
||||||
|
|
||||||
const char *BarStr =
|
const char *BarStr =
|
||||||
"%struct.rtx_def = type { i16 }\n"
|
"%struct.rtx_def = type { i16 }\n"
|
||||||
"define void @bar(%struct.rtx_def* %a, i8 %b, i32 %c) {\n"
|
"define void @bar(%struct.rtx_def* %a, i8 %b, i32 %c) {\n"
|
||||||
" call void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n"
|
" call void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)\n"
|
||||||
" ret void\n"
|
" ret void\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"declare void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def*, i8, i32, i32, i1)\n";
|
"declare void @llvm.memset.p0s_struct.rtx_defs.i32(%struct.rtx_def*, i8, i32, i32, i1)\n";
|
||||||
|
|
||||||
std::unique_ptr<Module> Foo = parseAssemblyString(FooStr, Err, C);
|
std::unique_ptr<Module> Foo = parseAssemblyString(FooStr, Err, C);
|
||||||
assert(Foo);
|
assert(Foo);
|
||||||
ASSERT_TRUE(Foo.get());
|
ASSERT_TRUE(Foo.get());
|
||||||
// Foo is loaded first, so the type and the intrinsic have theis original
|
// Foo is loaded first, so the type and the intrinsic have theis original
|
||||||
// names.
|
// names.
|
||||||
ASSERT_TRUE(Foo->getFunction("llvm.memset.p0struct.rtx_def.i32"));
|
ASSERT_TRUE(Foo->getFunction("llvm.memset.p0s_struct.rtx_defs.i32"));
|
||||||
ASSERT_FALSE(Foo->getFunction("llvm.memset.p0struct.rtx_def.0.i32"));
|
ASSERT_FALSE(Foo->getFunction("llvm.memset.p0s_struct.rtx_defs.0.i32"));
|
||||||
|
|
||||||
std::unique_ptr<Module> Bar = parseAssemblyString(BarStr, Err, C);
|
std::unique_ptr<Module> Bar = parseAssemblyString(BarStr, Err, C);
|
||||||
assert(Bar);
|
assert(Bar);
|
||||||
ASSERT_TRUE(Bar.get());
|
ASSERT_TRUE(Bar.get());
|
||||||
// Bar is loaded after Foo, so the type is renamed to struct.rtx_def.0. Check
|
// Bar is loaded after Foo, so the type is renamed to struct.rtx_def.0. Check
|
||||||
// that the intrinsic is also renamed.
|
// that the intrinsic is also renamed.
|
||||||
ASSERT_FALSE(Bar->getFunction("llvm.memset.p0struct.rtx_def.i32"));
|
ASSERT_FALSE(Bar->getFunction("llvm.memset.p0s_struct.rtx_defs.i32"));
|
||||||
ASSERT_TRUE(Bar->getFunction("llvm.memset.p0struct.rtx_def.0.i32"));
|
ASSERT_TRUE(Bar->getFunction("llvm.memset.p0s_struct.rtx_def.0s.i32"));
|
||||||
|
|
||||||
// Link two modules together.
|
// Link two modules together.
|
||||||
auto Dst = llvm::make_unique<Module>("Linked", C);
|
auto Dst = llvm::make_unique<Module>("Linked", C);
|
||||||
@ -356,7 +356,7 @@ TEST_F(LinkModuleTest, RemangleIntrinsics) {
|
|||||||
// "struct.rtx_def" from Foo and "struct.rtx_def.0" from Bar are isomorphic
|
// "struct.rtx_def" from Foo and "struct.rtx_def.0" from Bar are isomorphic
|
||||||
// types, so they must be uniquified by linker. Check that they use the same
|
// types, so they must be uniquified by linker. Check that they use the same
|
||||||
// intrinsic definition.
|
// intrinsic definition.
|
||||||
Function *F = Foo->getFunction("llvm.memset.p0struct.rtx_def.i32");
|
Function *F = Foo->getFunction("llvm.memset.p0s_struct.rtx_defs.i32");
|
||||||
ASSERT_EQ(F->getNumUses(), (unsigned)2);
|
ASSERT_EQ(F->getNumUses(), (unsigned)2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user