diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp --- a/llvm/lib/Linker/IRMover.cpp +++ b/llvm/lib/Linker/IRMover.cpp @@ -19,6 +19,8 @@ #include "llvm/IR/Function.h" #include "llvm/IR/GVMaterializer.h" #include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Module.h" #include "llvm/IR/PseudoProbe.h" @@ -525,6 +527,8 @@ /// module. void prepareCompileUnitsForImport(); void linkNamedMDNodes(); + /// Remove nocallback attribute while linking. + void removeAttributes(GlobalValue &GV); public: IRLinker(Module &DstM, MDMapT &SharedMDs, @@ -636,6 +640,7 @@ if (ForIndirectSymbol || shouldLink(New, *SGV)) setError(linkGlobalValueBody(*New, *SGV)); + removeAttributes(*New); return New; } @@ -1523,6 +1528,26 @@ return InlineAsm; } +/// Remove nocallback attribute while linking, because nocallback attribute +/// indicates that the function is only allowed to jump back into caller's +/// module only by a return or an exception. When modules are linked, this +/// property cannot be guaranteed anymore. For example, the function with +/// nocallback attribute might call an imported function. When it's caller +/// module and important function's module are linked together, the function +/// with nocallback attribute now jumps back into its caller's module. +void IRLinker::removeAttributes(GlobalValue &GV) { + if (auto *F = dyn_cast(&GV)) { + if (!F->isIntrinsic()) + F->removeFnAttr(llvm::Attribute::NoCallback); + + // Remove nocallback attribute when it is on a call-site. + for (BasicBlock &BB : *F) + for (Instruction &I : BB) + if (CallInst *CI = dyn_cast(&I)) + CI->removeFnAttr(Attribute::NoCallback); + } +} + Error IRLinker::run() { // Ensure metadata materialized before value mapping. if (SrcM->getMaterializer()) diff --git a/llvm/test/Linker/Inputs/drop-attribute.ll b/llvm/test/Linker/Inputs/drop-attribute.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Linker/Inputs/drop-attribute.ll @@ -0,0 +1,4 @@ +define void @test_nocallback_declaration_definition_linked_in() { +entry: + ret void +} diff --git a/llvm/test/Linker/drop-attribute.ll b/llvm/test/Linker/drop-attribute.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Linker/drop-attribute.ll @@ -0,0 +1,45 @@ +; RUN: llvm-link %s %p/Inputs/drop-attribute.ll -S -o - | FileCheck %s + +; Test case that checks that nocallback attribute is dropped during linking. + +; CHECK: define i32 @main() +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @test_nocallback_definition() +; Test that checks that nocallback attribute on a call-site is dropped. +; CHECK-NEXT: call void @test_nocallback_call_site(){{$}} +; CHECK-NEXT: %0 = call float @llvm.sqrt.f32(float undef) +; CHECK-NEXT: call void @test_nocallback_declaration_definition_not_linked_in() +; CHECK-NEXT: call void @test_nocallback_declaration_definition_linked_in() +define i32 @main() { +entry: + call void @test_nocallback_definition() + call void @test_nocallback_call_site() nocallback + call float @llvm.sqrt.f32(float undef) + call void @test_nocallback_declaration_definition_not_linked_in() + call void @test_nocallback_declaration_definition_linked_in() + ret i32 0 +} + +; Test that checks that nocallback attribute on a definition is dropped. +; CHECK: define void @test_nocallback_definition() +define void @test_nocallback_definition() nocallback { + ret void +} + +; Test that checks that nocallback attribute on a declaration when a definition is linked in is dropped. +; CHECK: declare void @test_nocallback_call_site(){{$}} +declare void @test_nocallback_call_site() + +; Test that checks that nocallback attribute on an intrinsic is NOT dropped. +; CHECK: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn +; CHECK-NEXT: declare float @llvm.sqrt.f32(float) #0 +declare float @llvm.sqrt.f32(float) nocallback + +; Test that checks that nocallback attribute on a declaration when a definition is not linked in is dropped. +; CHECK: declare void @test_nocallback_declaration_definition_not_linked_in(){{$}} +declare void @test_nocallback_declaration_definition_not_linked_in() nocallback + +; Test that checks that nocallback attribute on a declaration when a definition is linked in is dropped. +declare void @test_nocallback_declaration_definition_linked_in() nocallback + +; CHECK: define void @test_nocallback_declaration_definition_linked_in()