diff --git a/llvm/lib/Linker/LinkModules.cpp b/llvm/lib/Linker/LinkModules.cpp --- a/llvm/lib/Linker/LinkModules.cpp +++ b/llvm/lib/Linker/LinkModules.cpp @@ -15,6 +15,8 @@ #include "llvm/ADT/SetVector.h" #include "llvm/IR/Comdat.h" #include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Linker/Linker.h" @@ -379,6 +381,24 @@ GV.hasAvailableExternallyLinkage())) return false; + // Drop 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. + if (auto *F = dyn_cast(&GV)) { + if (!F->isIntrinsic()) + F->removeFnAttr(llvm::Attribute::NoCallback); + + // Drop 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); + } + if (GV.isDeclaration()) return false; 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()