mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 20:51:52 +01:00
Remangle intrinsics names when types are renamed
This is a resubmittion of previously reverted rL273568. This is a fix for the problem mentioned in "LTO and intrinsics mangling" llvm-dev mail thread: http://lists.llvm.org/pipermail/llvm-dev/2016-April/098387.html Reviewers: mehdi_amini, reames Differential Revision: http://reviews.llvm.org/D19373 llvm-svn: 273686
This commit is contained in:
parent
93a6698cb0
commit
4c8cb14fd4
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "llvm/ADT/ArrayRef.h"
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
#include "llvm/ADT/None.h"
|
#include "llvm/ADT/None.h"
|
||||||
|
#include "llvm/ADT/Optional.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
@ -149,6 +150,10 @@ namespace Intrinsic {
|
|||||||
/// This method returns true on error.
|
/// This method returns true on error.
|
||||||
bool matchIntrinsicVarArg(bool isVarArg, ArrayRef<IITDescriptor> &Infos);
|
bool matchIntrinsicVarArg(bool isVarArg, ArrayRef<IITDescriptor> &Infos);
|
||||||
|
|
||||||
|
// Checks if the intrinsic name matches with its signature and if not
|
||||||
|
// returns the declaration with the same signature and remangled name.
|
||||||
|
llvm::Optional<Function*> remangleIntrinsicFunction(Function *F);
|
||||||
|
|
||||||
} // End Intrinsic namespace
|
} // End Intrinsic namespace
|
||||||
|
|
||||||
} // End llvm namespace
|
} // End llvm namespace
|
||||||
|
@ -18,12 +18,14 @@
|
|||||||
#include "llvm/AsmParser/SlotMapping.h"
|
#include "llvm/AsmParser/SlotMapping.h"
|
||||||
#include "llvm/IR/AutoUpgrade.h"
|
#include "llvm/IR/AutoUpgrade.h"
|
||||||
#include "llvm/IR/CallingConv.h"
|
#include "llvm/IR/CallingConv.h"
|
||||||
|
#include "llvm/IR/CallSite.h"
|
||||||
#include "llvm/IR/Constants.h"
|
#include "llvm/IR/Constants.h"
|
||||||
#include "llvm/IR/DebugInfo.h"
|
#include "llvm/IR/DebugInfo.h"
|
||||||
#include "llvm/IR/DebugInfoMetadata.h"
|
#include "llvm/IR/DebugInfoMetadata.h"
|
||||||
#include "llvm/IR/DerivedTypes.h"
|
#include "llvm/IR/DerivedTypes.h"
|
||||||
#include "llvm/IR/InlineAsm.h"
|
#include "llvm/IR/InlineAsm.h"
|
||||||
#include "llvm/IR/Instructions.h"
|
#include "llvm/IR/Instructions.h"
|
||||||
|
#include "llvm/IR/Intrinsics.h"
|
||||||
#include "llvm/IR/LLVMContext.h"
|
#include "llvm/IR/LLVMContext.h"
|
||||||
#include "llvm/IR/Module.h"
|
#include "llvm/IR/Module.h"
|
||||||
#include "llvm/IR/Operator.h"
|
#include "llvm/IR/Operator.h"
|
||||||
@ -210,6 +212,17 @@ bool LLParser::ValidateEndOfModule() {
|
|||||||
for (Module::iterator FI = M->begin(), FE = M->end(); FI != FE; )
|
for (Module::iterator FI = M->begin(), FE = M->end(); FI != FE; )
|
||||||
UpgradeCallsToIntrinsic(&*FI++); // must be post-increment, as we remove
|
UpgradeCallsToIntrinsic(&*FI++); // must be post-increment, as we remove
|
||||||
|
|
||||||
|
// Some types could be renamed during loading if several modules are
|
||||||
|
// loaded in the same LLVMContext (LTO scenario). In this case we should
|
||||||
|
// remangle intrinsics names as well.
|
||||||
|
for (Module::iterator FI = M->begin(), FE = M->end(); FI != FE; ) {
|
||||||
|
Function *F = &*FI++;
|
||||||
|
if (auto Remangled = Intrinsic::remangleIntrinsicFunction(F)) {
|
||||||
|
F->replaceAllUsesWith(Remangled.getValue());
|
||||||
|
F->eraseFromParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UpgradeDebugInfo(*M);
|
UpgradeDebugInfo(*M);
|
||||||
|
|
||||||
UpgradeModuleFlags(*M);
|
UpgradeModuleFlags(*M);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "llvm/Bitcode/LLVMBitCodes.h"
|
#include "llvm/Bitcode/LLVMBitCodes.h"
|
||||||
#include "llvm/Bitcode/ReaderWriter.h"
|
#include "llvm/Bitcode/ReaderWriter.h"
|
||||||
#include "llvm/IR/AutoUpgrade.h"
|
#include "llvm/IR/AutoUpgrade.h"
|
||||||
|
#include "llvm/IR/CallSite.h"
|
||||||
#include "llvm/IR/Constants.h"
|
#include "llvm/IR/Constants.h"
|
||||||
#include "llvm/IR/DebugInfo.h"
|
#include "llvm/IR/DebugInfo.h"
|
||||||
#include "llvm/IR/DebugInfoMetadata.h"
|
#include "llvm/IR/DebugInfoMetadata.h"
|
||||||
@ -230,8 +231,10 @@ class BitcodeReader : public GVMaterializer {
|
|||||||
|
|
||||||
// When intrinsic functions are encountered which require upgrading they are
|
// When intrinsic functions are encountered which require upgrading they are
|
||||||
// stored here with their replacement function.
|
// stored here with their replacement function.
|
||||||
typedef DenseMap<Function*, Function*> UpgradedIntrinsicMap;
|
typedef DenseMap<Function*, Function*> UpdatedIntrinsicMap;
|
||||||
UpgradedIntrinsicMap UpgradedIntrinsics;
|
UpdatedIntrinsicMap UpgradedIntrinsics;
|
||||||
|
// Intrinsics which were remangled because of types rename
|
||||||
|
UpdatedIntrinsicMap RemangledIntrinsics;
|
||||||
|
|
||||||
// Map the bitcode's custom MDKind ID to the Module's MDKind ID.
|
// Map the bitcode's custom MDKind ID to the Module's MDKind ID.
|
||||||
DenseMap<unsigned, unsigned> MDKindMap;
|
DenseMap<unsigned, unsigned> MDKindMap;
|
||||||
@ -3425,6 +3428,11 @@ std::error_code BitcodeReader::globalCleanup() {
|
|||||||
Function *NewFn;
|
Function *NewFn;
|
||||||
if (UpgradeIntrinsicFunction(&F, NewFn))
|
if (UpgradeIntrinsicFunction(&F, NewFn))
|
||||||
UpgradedIntrinsics[&F] = NewFn;
|
UpgradedIntrinsics[&F] = NewFn;
|
||||||
|
else if (auto Remangled = Intrinsic::remangleIntrinsicFunction(&F))
|
||||||
|
// Some types could be renamed during loading if several modules are
|
||||||
|
// loaded in the same LLVMContext (LTO scenario). In this case we should
|
||||||
|
// remangle intrinsics names as well.
|
||||||
|
RemangledIntrinsics[&F] = Remangled.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for global variables which need to be renamed.
|
// Look for global variables which need to be renamed.
|
||||||
@ -5601,6 +5609,13 @@ std::error_code BitcodeReader::materialize(GlobalValue *GV) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update calls to the remangled intrinsics
|
||||||
|
for (auto &I : RemangledIntrinsics)
|
||||||
|
for (auto UI = I.first->materialized_user_begin(), UE = I.first->user_end();
|
||||||
|
UI != UE;)
|
||||||
|
// Don't expect any other users than call sites
|
||||||
|
CallSite(*UI++).setCalledFunction(I.second);
|
||||||
|
|
||||||
// Finish fn->subprogram upgrade for materialized functions.
|
// Finish fn->subprogram upgrade for materialized functions.
|
||||||
if (DISubprogram *SP = FunctionsWithSPs.lookup(F))
|
if (DISubprogram *SP = FunctionsWithSPs.lookup(F))
|
||||||
F->setSubprogram(SP);
|
F->setSubprogram(SP);
|
||||||
@ -5654,6 +5669,12 @@ std::error_code BitcodeReader::materializeModule() {
|
|||||||
I.first->eraseFromParent();
|
I.first->eraseFromParent();
|
||||||
}
|
}
|
||||||
UpgradedIntrinsics.clear();
|
UpgradedIntrinsics.clear();
|
||||||
|
// Do the same for remangled intrinsics
|
||||||
|
for (auto &I : RemangledIntrinsics) {
|
||||||
|
I.first->replaceAllUsesWith(I.second);
|
||||||
|
I.first->eraseFromParent();
|
||||||
|
}
|
||||||
|
RemangledIntrinsics.clear();
|
||||||
|
|
||||||
UpgradeDebugInfo(*TheModule);
|
UpgradeDebugInfo(*TheModule);
|
||||||
|
|
||||||
|
@ -1080,6 +1080,40 @@ Intrinsic::matchIntrinsicVarArg(bool isVarArg,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<Function*> Intrinsic::remangleIntrinsicFunction(Function *F) {
|
||||||
|
Intrinsic::ID ID = F->getIntrinsicID();
|
||||||
|
if (!ID)
|
||||||
|
return None;
|
||||||
|
|
||||||
|
FunctionType *FTy = F->getFunctionType();
|
||||||
|
// Accumulate an array of overloaded types for the given intrinsic
|
||||||
|
SmallVector<Type *, 4> ArgTys;
|
||||||
|
{
|
||||||
|
SmallVector<Intrinsic::IITDescriptor, 8> Table;
|
||||||
|
getIntrinsicInfoTableEntries(ID, Table);
|
||||||
|
ArrayRef<Intrinsic::IITDescriptor> TableRef = Table;
|
||||||
|
|
||||||
|
// If we encounter any problems matching the signature with the descriptor
|
||||||
|
// just give up remangling. It's up to verifier to report the discrepancy.
|
||||||
|
if (Intrinsic::matchIntrinsicType(FTy->getReturnType(), TableRef, ArgTys))
|
||||||
|
return None;
|
||||||
|
for (auto Ty : FTy->params())
|
||||||
|
if (Intrinsic::matchIntrinsicType(Ty, TableRef, ArgTys))
|
||||||
|
return None;
|
||||||
|
if (Intrinsic::matchIntrinsicVarArg(FTy->isVarArg(), TableRef))
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringRef Name = F->getName();
|
||||||
|
if (Name == Intrinsic::getName(ID, ArgTys))
|
||||||
|
return None;
|
||||||
|
|
||||||
|
auto NewDecl = Intrinsic::getDeclaration(F->getParent(), ID, ArgTys);
|
||||||
|
NewDecl->setCallingConv(F->getCallingConv());
|
||||||
|
assert(NewDecl->getFunctionType() == FTy && "Shouldn't change the signature");
|
||||||
|
return NewDecl;
|
||||||
|
}
|
||||||
|
|
||||||
/// hasAddressTaken - returns true if there are any uses of this function
|
/// hasAddressTaken - returns true if there are any uses of this function
|
||||||
/// other than direct calls or invokes to it.
|
/// other than direct calls or invokes to it.
|
||||||
bool Function::hasAddressTaken(const User* *PutOffender) const {
|
bool Function::hasAddressTaken(const User* *PutOffender) const {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "llvm/IR/DebugInfo.h"
|
#include "llvm/IR/DebugInfo.h"
|
||||||
#include "llvm/IR/DiagnosticPrinter.h"
|
#include "llvm/IR/DiagnosticPrinter.h"
|
||||||
#include "llvm/IR/GVMaterializer.h"
|
#include "llvm/IR/GVMaterializer.h"
|
||||||
|
#include "llvm/IR/Intrinsics.h"
|
||||||
#include "llvm/IR/TypeFinder.h"
|
#include "llvm/IR/TypeFinder.h"
|
||||||
#include "llvm/Support/Error.h"
|
#include "llvm/Support/Error.h"
|
||||||
#include "llvm/Transforms/Utils/Cloning.h"
|
#include "llvm/Transforms/Utils/Cloning.h"
|
||||||
@ -901,6 +902,14 @@ Expected<Constant *> IRLinker::linkGlobalValueProto(GlobalValue *SGV,
|
|||||||
if (ShouldLink || !ForAlias)
|
if (ShouldLink || !ForAlias)
|
||||||
forceRenaming(NewGV, SGV->getName());
|
forceRenaming(NewGV, SGV->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Overloaded intrinsics have overloaded types names as part of their
|
||||||
|
// names. If we renamed overloaded types we should rename the intrinsic
|
||||||
|
// as well.
|
||||||
|
if (Function *F = dyn_cast<Function>(NewGV))
|
||||||
|
if (auto Remangled = Intrinsic::remangleIntrinsicFunction(F))
|
||||||
|
NewGV = Remangled.getValue();
|
||||||
|
|
||||||
if (ShouldLink || ForAlias) {
|
if (ShouldLink || ForAlias) {
|
||||||
if (const Comdat *SC = SGV->getComdat()) {
|
if (const Comdat *SC = SGV->getComdat()) {
|
||||||
if (auto *GO = dyn_cast<GlobalObject>(NewGV)) {
|
if (auto *GO = dyn_cast<GlobalObject>(NewGV)) {
|
||||||
|
8
test/LTO/X86/Inputs/remangle_intrinsics.ll
Normal file
8
test/LTO/X86/Inputs/remangle_intrinsics.ll
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
%struct.rtx_def = type { i16, i16 }
|
||||||
|
|
||||||
|
define void @bar(%struct.rtx_def* %a, i8 %b, i32 %c) {
|
||||||
|
call void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def*, i8, i32, i32, i1)
|
23
test/LTO/X86/remangle_intrinsics.ll
Normal file
23
test/LTO/X86/remangle_intrinsics.ll
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
; RUN: llvm-as < %s > %t1
|
||||||
|
; RUN: llvm-as < %p/Inputs/remangle_intrinsics.ll > %t2
|
||||||
|
; RUN: llvm-lto %t1 %t2 | FileCheck %s
|
||||||
|
|
||||||
|
; We have "struct.rtx_def" type in both modules being LTOed. Both modules use
|
||||||
|
; an overloaded intrinsic which has this type in its signature/name. When
|
||||||
|
; modules are loaded one of the types is renamed to "struct.rtx_def.0".
|
||||||
|
; The intrinsic which uses this type should be remangled/renamed as well.
|
||||||
|
; If we didn't do that verifier would complain.
|
||||||
|
|
||||||
|
; CHECK: Wrote native object file
|
||||||
|
|
||||||
|
%struct.rtx_def = type { i16 }
|
||||||
|
|
||||||
|
define void @foo(%struct.rtx_def* %a, i8 %b, i32 %c) {
|
||||||
|
call void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def* %a, i8 %b, i32 %c, i32 4, i1 true)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def*, i8, i32, i32, i1)
|
||||||
|
|
||||||
|
; Check that remangling code doesn't fail on an intrinsic with wrong signature
|
||||||
|
declare void @llvm.memset.i64(i8* nocapture, i8, i64, i32) nounwind
|
@ -306,4 +306,58 @@ TEST_F(LinkModuleTest, MoveDistinctMDs) {
|
|||||||
EXPECT_EQ(M3, M4->getOperand(0));
|
EXPECT_EQ(M3, M4->getOperand(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(LinkModuleTest, RemangleIntrinsics) {
|
||||||
|
LLVMContext C;
|
||||||
|
SMDiagnostic Err;
|
||||||
|
|
||||||
|
// We load two modules inside the same context C. In both modules there is a
|
||||||
|
// "struct.rtx_def" type. In the module loaded the second (Bar) this type will
|
||||||
|
// be renamed to "struct.rtx_def.0". Check that the intrinsics which have this
|
||||||
|
// type in the signature are properly remangled.
|
||||||
|
const char *FooStr =
|
||||||
|
"%struct.rtx_def = type { i16 }\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"
|
||||||
|
" ret void\n"
|
||||||
|
"}\n"
|
||||||
|
"declare void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def*, i8, i32, i32, i1)\n";
|
||||||
|
|
||||||
|
const char *BarStr =
|
||||||
|
"%struct.rtx_def = type { i16 }\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"
|
||||||
|
" ret void\n"
|
||||||
|
"}\n"
|
||||||
|
"declare void @llvm.memset.p0struct.rtx_def.i32(%struct.rtx_def*, i8, i32, i32, i1)\n";
|
||||||
|
|
||||||
|
std::unique_ptr<Module> Foo = parseAssemblyString(FooStr, Err, C);
|
||||||
|
assert(Foo);
|
||||||
|
ASSERT_TRUE(Foo.get());
|
||||||
|
// Foo is loaded first, so the type and the intrinsic have theis original
|
||||||
|
// names.
|
||||||
|
ASSERT_TRUE(Foo->getFunction("llvm.memset.p0struct.rtx_def.i32"));
|
||||||
|
ASSERT_FALSE(Foo->getFunction("llvm.memset.p0struct.rtx_def.0.i32"));
|
||||||
|
|
||||||
|
std::unique_ptr<Module> Bar = parseAssemblyString(BarStr, Err, C);
|
||||||
|
assert(Bar);
|
||||||
|
ASSERT_TRUE(Bar.get());
|
||||||
|
// Bar is loaded after Foo, so the type is renamed to struct.rtx_def.0. Check
|
||||||
|
// that the intrinsic is also renamed.
|
||||||
|
ASSERT_FALSE(Bar->getFunction("llvm.memset.p0struct.rtx_def.i32"));
|
||||||
|
ASSERT_TRUE(Bar->getFunction("llvm.memset.p0struct.rtx_def.0.i32"));
|
||||||
|
|
||||||
|
// Link two modules together.
|
||||||
|
auto Dst = llvm::make_unique<Module>("Linked", C);
|
||||||
|
ASSERT_TRUE(Dst.get());
|
||||||
|
Ctx.setDiagnosticHandler(expectNoDiags);
|
||||||
|
bool Failed = Linker::linkModules(*Foo, std::move(Bar));
|
||||||
|
ASSERT_FALSE(Failed);
|
||||||
|
|
||||||
|
// "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
|
||||||
|
// intrinsic definition.
|
||||||
|
Function *F = Foo->getFunction("llvm.memset.p0struct.rtx_def.i32");
|
||||||
|
ASSERT_EQ(F->getNumUses(), (unsigned)2);
|
||||||
|
}
|
||||||
|
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
Loading…
x
Reference in New Issue
Block a user