| ; This testcase is for testing the positive return values of the |
| ; TOCRestoreNeededForCallToImplementation query, specifically the type of |
| ; functions that are considered DSO local on AIX. |
| |
| ; REQUIRES: asserts |
| ; RUN: llc < %s -mtriple=powerpc64-ibm-aix-xcoff --function-sections -filetype=obj -o /dev/null -debug-only=asmprinter 2>&1 | FileCheck %s |
| ; RUN: llc < %s -mtriple=powerpc-ibm-aix-xcoff --function-sections -filetype=obj -o /dev/null -debug-only=asmprinter 2>&1 | FileCheck %s |
| ; RUN: llc < %s -mtriple=powerpc64-ibm-aix-xcoff -ifunc-local-if-proven=1 -o /dev/null -debug-only=asmprinter 2>&1 | FileCheck %s |
| |
| ; CHECK: foo_ext_hidden_decl is dso_local |
| ; CHECK: foo_ext_hidden_def is dso_local |
| ; CHECK: foo_ext_hidden_weak_def is dso_local |
| ; CHECK: foo_ext_protected_decl is dso_local |
| ; CHECK: foo_ext_protected_def is dso_local |
| ; CHECK: foo_ext_protected_weak_def is dso_local |
| ; CHECK: foo_static is dso_local |
| |
| ; The following decls/defs are dso_local in the IR, and it matches the behaviour on AIX in practice |
| ; (1) a hidden/protected declaration should have a matching definition in the same shared object, |
| ; with the matching visibility. So the definition is not interposable due to hidden/protected. |
| ; (2) a hidden/protected definition is not interposable. |
| ; (3) attribute weak has no effect on interposition, and if a strong definition in the same shared object |
| ; exists, then it's a user error to have that definition have conflicting visibility. |
| ; In practice, on AIX the linker will silently pick the strong definition and keep it's visibility |
| ; ignoring what's on the weak definition. |
| ; On Linux, both ld and lld pick the strong definition but give it the most restrictive visibility based |
| ; on the candidates available (so a weak hidden and a strong default will yield a hidden strong) |
| ; |
| declare hidden void @foo_ext_hidden_decl(...) ; (1) |
| |
| declare protected void @foo_ext_protected_decl(...) ; (1) |
| |
| define hidden void @foo_ext_hidden_def() { ; (2) |
| entry: |
| ret void |
| } |
| |
| define protected void @foo_ext_protected_def() { ; (2) |
| entry: |
| ret void |
| } |
| |
| define weak hidden void @foo_ext_hidden_weak_def() { ; (3) |
| entry: |
| ret void |
| } |
| |
| define weak protected void @foo_ext_protected_weak_def() { ; (3) |
| entry: |
| ret void |
| } |
| |
| define internal void @foo_static() { |
| entry: |
| ret void |
| } |
| |
| @foo = ifunc void (...), ptr @resolve_foo |
| |
| @x = global i32 0, align 4 |
| |
| @switch.table.bar = private unnamed_addr constant [6 x ptr] [ptr @foo_ext_hidden_decl, ptr @foo_ext_hidden_def, ptr @foo_ext_hidden_weak_def, ptr @foo_ext_protected_decl, ptr @foo_ext_protected_def, ptr @foo_ext_protected_weak_def], align 4 |
| |
| define internal nonnull ptr @resolve_foo() { |
| entry: |
| %x = load i32, ptr @x, align 4 |
| %0 = icmp ult i32 %x, 6 |
| br i1 %0, label %switch.lookup, label %return |
| |
| switch.lookup: ; preds = %entry |
| %switch.gep = getelementptr inbounds nuw ptr, ptr @switch.table.bar, i32 %x |
| %switch.load = load ptr, ptr %switch.gep, align 4 |
| br label %return |
| |
| return: ; preds = %entry, %switch.lookup |
| %retval.0 = phi ptr [ %switch.load, %switch.lookup ], [ @foo_static, %entry ] |
| ret ptr %retval.0 |
| } |