Skip to content

Commit

Permalink
[Linker] Remove nocallback attribute while linking
Browse files Browse the repository at this point in the history
GCC's leaf attribute is lowered to LLVM IR nocallback attribute.
Clang conservatively treats this function attribute as a hint on the
module level, and removes it while linking modules. More context can
be found in: https://reviews.llvm.org/D131628.

Differential Revision: https://reviews.llvm.org/D137360
  • Loading branch information
gulfemsavrun committed Dec 22, 2022
1 parent f5700e7 commit b956899
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 0 deletions.
33 changes: 33 additions & 0 deletions llvm/lib/Linker/IRMover.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -526,6 +528,9 @@ class IRLinker {
void prepareCompileUnitsForImport();
void linkNamedMDNodes();

/// Update attributes while linking.
void updateAttributes(GlobalValue &GV);

public:
IRLinker(Module &DstM, MDMapT &SharedMDs,
IRMover::IdentifiedStructTypeSet &Set, std::unique_ptr<Module> SrcM,
Expand Down Expand Up @@ -636,6 +641,7 @@ Value *IRLinker::materialize(Value *V, bool ForIndirectSymbol) {
if (ForIndirectSymbol || shouldLink(New, *SGV))
setError(linkGlobalValueBody(*New, *SGV));

updateAttributes(*New);
return New;
}

Expand Down Expand Up @@ -1527,6 +1533,33 @@ static std::string adjustInlineAsm(const std::string &InlineAsm,
return InlineAsm;
}

void IRLinker::updateAttributes(GlobalValue &GV) {
/// 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 nocallback
/// function may contain a call to another module. But if we merge its caller
/// and callee module here, and not the module containing the nocallback
/// function definition itself, the nocallback property will be violated
/// (since the nocallback function will call back into the newly merged module
/// containing both its caller and callee). This could happen if the module
/// containing the nocallback function definition is native code, so it does
/// not participate in the LTO link. Note if the nocallback function does
/// participate in the LTO link, and thus ends up in the merged module
/// containing its caller and callee, removing the attribute doesn't hurt as
/// it has no effect on definitions in the same module.
if (auto *F = dyn_cast<Function>(&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<CallInst>(&I))
CI->removeFnAttr(Attribute::NoCallback);
}
}

Error IRLinker::run() {
// Ensure metadata materialized before value mapping.
if (SrcM->getMaterializer())
Expand Down
4 changes: 4 additions & 0 deletions llvm/test/Linker/Inputs/drop-attribute.ll
@@ -0,0 +1,4 @@
define void @test_nocallback_declaration_definition_linked_in() {
entry:
ret void
}
44 changes: 44 additions & 0 deletions llvm/test/Linker/drop-attribute.ll
@@ -0,0 +1,44 @@
; 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.
; CHECK: define void @test_nocallback_declaration_definition_linked_in() {{{$}}
declare void @test_nocallback_declaration_definition_linked_in() nocallback

0 comments on commit b956899

Please sign in to comment.