Skip to content

Commit

Permalink
Refactor how -fno-semantic-interposition sets dso_local on default vi…
Browse files Browse the repository at this point in the history
…sibility external linkage definitions

The idea is that the CC1 default for ELF should set dso_local on default
visibility external linkage definitions in the default -mrelocation-model pic
mode (-fpic/-fPIC) to match COFF/Mach-O and make output IR similar.

The refactoring is made available by 2820a2c.

Currently only x86 supports local aliases. We move the decision to the driver.
There are three CC1 states:

* -fsemantic-interposition: make some linkages interposable and make default visibility external linkage definitions dso_preemptable.
* (default): selected if the target supports .Lfoo$local: make default visibility external linkage definitions dso_local
* -fhalf-no-semantic-interposition: if neither option is set or the target does not support .Lfoo$local: like -fno-semantic-interposition but local aliases are not used. So references can be interposed if not optimized out.

Add -fhalf-no-semantic-interposition to a few tests using the half-based semantic interposition behavior.
  • Loading branch information
MaskRay committed Dec 31, 2020
1 parent 219d00e commit d1fd723
Show file tree
Hide file tree
Showing 25 changed files with 118 additions and 89 deletions.
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,8 @@ ENUM_LANGOPT(ExternDeclDLLImportVisibility, Visibility, 3, DefaultVisibility,
ENUM_LANGOPT(ExternDeclNoDLLStorageClassVisibility, Visibility, 3, HiddenVisibility,
"visibility for external declarations without an explicit DLL storage class [-fvisibility-from-dllstorageclass]")
BENIGN_LANGOPT(SemanticInterposition , 1, 0, "semantic interposition")
BENIGN_LANGOPT(ExplicitNoSemanticInterposition, 1, 0, "explicitly no semantic interposition")
BENIGN_LANGOPT(HalfNoSemanticInterposition, 1, 0,
"Like -fno-semantic-interposition but don't use local aliases")
ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff,
"stack protector mode")
ENUM_LANGOPT(TrivialAutoVarInit, TrivialAutoVarInitKind, 2, TrivialAutoVarInitKind::Uninitialized,
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -3793,7 +3793,7 @@ defm ipa_cp : BooleanFFlag<"ipa-cp">,
Group<clang_ignored_gcc_optimization_f_Group>;
defm ivopts : BooleanFFlag<"ivopts">, Group<clang_ignored_gcc_optimization_f_Group>;
def fsemantic_interposition : Flag<["-"], "fsemantic-interposition">, Group<f_Group>, Flags<[CC1Option]>;
def fno_semantic_interposition: Flag<["-"], "fno-semantic-interposition">, Group<f_Group>, Flags<[CC1Option]>;
def fno_semantic_interposition: Flag<["-"], "fno-semantic-interposition">, Group<f_Group>;
defm non_call_exceptions : BooleanFFlag<"non-call-exceptions">, Group<clang_ignored_f_Group>;
defm peel_loops : BooleanFFlag<"peel-loops">, Group<clang_ignored_gcc_optimization_f_Group>;
defm permissive : BooleanFFlag<"permissive">, Group<clang_ignored_f_Group>;
Expand Down Expand Up @@ -4722,6 +4722,8 @@ def pic_level : Separate<["-"], "pic-level">,
HelpText<"Value for __PIC__">;
def pic_is_pie : Flag<["-"], "pic-is-pie">,
HelpText<"File is for a position independent executable">;
def fhalf_no_semantic_interposition : Flag<["-"], "fhalf-no-semantic-interposition">,
HelpText<"Like -fno-semantic-interposition but don't use local aliases">;
def fno_validate_pch : Flag<["-"], "fno-validate-pch">,
HelpText<"Disable validation of precompiled headers">,
MarshallingInfoFlag<"PreprocessorOpts->DisablePCHValidation">;
Expand Down
19 changes: 8 additions & 11 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,6 @@ void CodeGenModule::Release() {
if (Context.getLangOpts().SemanticInterposition)
// Require various optimization to respect semantic interposition.
getModule().setSemanticInterposition(1);
else if (Context.getLangOpts().ExplicitNoSemanticInterposition)
// Allow dso_local on applicable targets.
getModule().setSemanticInterposition(0);

if (CodeGenOpts.EmitCodeView) {
// Indicate that we want CodeView in the metadata.
Expand Down Expand Up @@ -961,15 +958,15 @@ static bool shouldAssumeDSOLocal(const CodeGenModule &CGM,
return false;

if (RM != llvm::Reloc::Static && !LOpts.PIE) {
// On ELF, if -fno-semantic-interposition is specified, we can set dso_local
// if using a local alias is preferable (can avoid GOT indirection).
// Currently only x86 supports local alias.
if (!TT.isOSBinFormatELF() ||
!CGM.getLangOpts().ExplicitNoSemanticInterposition ||
!GV->canBenefitFromLocalAlias())
// On ELF, if -fno-semantic-interposition is specified and the target
// supports local aliases, there will be neither CC1
// -fsemantic-interposition nor -fhalf-no-semantic-interposition. Set
// dso_local if using a local alias is preferable (can avoid GOT
// indirection).
if (!GV->canBenefitFromLocalAlias())
return false;
// The allowed targets need to match AsmPrinter::getSymbolPreferLocal.
return TT.isX86();
return !(CGM.getLangOpts().SemanticInterposition ||
CGM.getLangOpts().HalfNoSemanticInterposition);
}

// A definition cannot be preempted from an executable.
Expand Down
31 changes: 25 additions & 6 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4561,12 +4561,31 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(A->getValue());
}

// The default is -fno-semantic-interposition. We render it just because we
// require explicit -fno-semantic-interposition to infer dso_local.
if (Arg *A = Args.getLastArg(options::OPT_fsemantic_interposition,
options::OPT_fno_semantic_interposition))
if (RelocationModel != llvm::Reloc::Static && !IsPIE)
A->render(Args, CmdArgs);
// -fsemantic-interposition is forwarded to CC1: set the
// "SemanticInterposition" metadata to 1 (make some linkages interposable) and
// make default visibility external linkage definitions dso_preemptable.
//
// -fno-semantic-interposition: if the target supports .Lfoo$local local
// aliases (make default visibility external linkage definitions dso_local).
// This is the CC1 default for ELF to match COFF/Mach-O.
//
// Otherwise use Clang's traditional behavior: like
// -fno-semantic-interposition but local aliases are not used. So references
// can be interposed if not optimized out.
if (Triple.isOSBinFormatELF()) {
Arg *A = Args.getLastArg(options::OPT_fsemantic_interposition,
options::OPT_fno_semantic_interposition);
if (RelocationModel != llvm::Reloc::Static && !IsPIE) {
// The supported targets need to call AsmPrinter::getSymbolPreferLocal.
bool SupportsLocalAlias = Triple.isX86();
if (!A)
CmdArgs.push_back("-fhalf-no-semantic-interposition");
else if (A->getOption().matches(options::OPT_fsemantic_interposition))
A->render(Args, CmdArgs);
else if (!SupportsLocalAlias)
CmdArgs.push_back("-fhalf-no-semantic-interposition");
}
}

{
std::string Model;
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3197,9 +3197,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
}

Opts.SemanticInterposition = Args.hasArg(OPT_fsemantic_interposition);
// An explicit -fno-semantic-interposition infers dso_local.
Opts.ExplicitNoSemanticInterposition =
Args.hasArg(OPT_fno_semantic_interposition);
Opts.HalfNoSemanticInterposition =
Args.hasArg(OPT_fhalf_no_semantic_interposition);

// -mrtd option
if (Arg *A = Args.getLastArg(OPT_mrtd)) {
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGen/attr-arm-sve-vector-bits-globals.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// REQUIRES: aarch64-registered-target
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -target-feature +bf16 -msve-vector-bits=128 -fallow-half-arguments-and-returns -S -O1 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-128
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -target-feature +bf16 -msve-vector-bits=512 -fallow-half-arguments-and-returns -S -O1 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-512
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -target-feature +bf16 -msve-vector-bits=128 -fallow-half-arguments-and-returns -S -O1 -emit-llvm -o - %s -fhalf-no-semantic-interposition | FileCheck %s --check-prefix=CHECK-128
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -target-feature +bf16 -msve-vector-bits=512 -fallow-half-arguments-and-returns -S -O1 -emit-llvm -o - %s -fhalf-no-semantic-interposition | FileCheck %s --check-prefix=CHECK-512

#include <arm_sve.h>

Expand Down
23 changes: 0 additions & 23 deletions clang/test/CodeGen/semantic-interposition-no.c

This file was deleted.

47 changes: 38 additions & 9 deletions clang/test/CodeGen/semantic-interposition.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
// RUN: %clang_cc1 -emit-llvm -fsemantic-interposition %s -o - | FileCheck --check-prefix=INTERPOSITION %s
// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck --check-prefix=NO %s
/// With explicit -fno-semantic-interposition, add a module flag to inform the
/// backend that dso_local can be inferred.
// RUN: %clang_cc1 -emit-llvm -fno-semantic-interposition %s -o - | FileCheck --check-prefix=EXPLICIT_NO %s

// INTERPOSITION: !{{[0-9]+}} = !{i32 1, !"SemanticInterposition", i32 1}
// NO-NOT: "SemanticInterposition"
// EXPLICIT_NO: !{{[0-9]+}} = !{i32 1, !"SemanticInterposition", i32 0}
/// -fno-semantic-interposition is the default and local aliases (via dso_local) are allowed.
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -mrelocation-model pic -pic-level 1 %s -o - | FileCheck %s --check-prefixes=CHECK,NOMETADATA

/// -fsemantic-interposition sets a module metadata.
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -mrelocation-model pic -pic-level 1 -fsemantic-interposition %s -o - | FileCheck %s --check-prefixes=PREEMPT,METADATA

/// Traditional half-baked behavior: interprocedural optimizations are allowed
/// but local aliases are not used.
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -mrelocation-model pic -pic-level 1 -fhalf-no-semantic-interposition %s -o - | FileCheck %s --check-prefixes=PREEMPT,NOMETADATA

// CHECK: @var = dso_local global i32 0, align 4
// CHECK: @ext_var = external global i32, align 4
// CHECK: @ifunc = ifunc i32 (), bitcast (i8* ()* @ifunc_resolver to i32 ()*)
// CHECK: define dso_local i32 @func()
// CHECK: declare i32 @ext()

// PREEMPT: @var = global i32 0, align 4
// PREEMPT: @ext_var = external global i32, align 4
// PREEMPT: @ifunc = ifunc i32 (), bitcast (i8* ()* @ifunc_resolver to i32 ()*)
// PREEMPT: define i32 @func()
// PREEMPT: declare i32 @ext()

// METADATA: !{{[0-9]+}} = !{i32 1, !"SemanticInterposition", i32 1}
// NOMETADATA-NOT: "SemanticInterposition"

int var;
extern int ext_var;

int ifunc(void) __attribute__((ifunc("ifunc_resolver")));

int func(void) { return 0; }
int ext(void);

static void *ifunc_resolver() { return func; }

int foo() {
return var + ext_var + ifunc() + func() + ext();
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Cross comdat example
// Parent VTable is in a comdat section.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

// The inline function is emitted in each module with the same comdat
// CHECK: $_ZTS1A = comdat any
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Check the vtable layout for classes with key functions defined in different
// translation units. This TU only manifests the vtable for A.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

#include "cross-tu-header.h"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Check the vtable layout for classes with key functions defined in different
// translation units. This TU manifests the vtable for B.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

#include "cross-tu-header.h"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Diamond inheritance.
// A more complicated multiple inheritance example that includes longer chain of inheritance and a common ancestor.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

// CHECK-DAG: %class.B = type { %class.A }
// CHECK-DAG: %class.A = type { i32 (...)** }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Diamond virtual inheritance.
// This should cover virtual inheritance, construction vtables, and VTTs.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

// Class A contains a vtable ptr, then int, then padding
// CHECK-DAG: %class.B = type { i32 (...)**, %class.A.base }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Check the layout of the vtable for a child class that inherits a virtual
// function but does not override it.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

class A {
public:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// The VTable is not in a comdat but the inline methods are.
// This doesn’t affect the vtable or the stubs we emit.

// RUN: %clang_cc1 %s -triple=aarch64 -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=x86_64 -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64 -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s
// RUN: %clang_cc1 %s -triple=x86_64 -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

// CHECK: $_ZTI1A.rtti_proxy = comdat any

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Multiple inheritance.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

// CHECK: %class.C = type { %class.A, %class.B }
// CHECK: %class.A = type { i32 (...)** }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Check that no alias is emitted when the vtable is already dso_local. This can
// happen if the class is hidden.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s --check-prefix=DEFAULT-VIS
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s --check-prefix=DEFAULT-VIS
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fvisibility hidden | FileCheck %s --check-prefix=HIDDEN-VIS

// DEFAULT-VIS: @_ZTV1A.local = private unnamed_addr constant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// We instead emit zero for the pure virtual function component. See PR43094 for
// details.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

// CHECK: @_ZTV1A.local = private unnamed_addr constant { [4 x i32] } { [4 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8* }** @_ZTI1A.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32] }, { [4 x i32] }* @_ZTV1A.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 0, i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* dso_local_equivalent @_ZN1A3barEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32] }, { [4 x i32] }* @_ZTV1A.local, i32 0, i32 0, i32 2) to i64)) to i32)] }, align 4

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Check the layout of the vtable for a child class that inherits a virtual
// function but does override it.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

// CHECK: @_ZTV1B.local = private unnamed_addr constant { [4 x i32] } { [4 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i8* }** @_ZTI1B.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32] }, { [4 x i32] }* @_ZTV1B.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* dso_local_equivalent @_ZN1B3fooEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32] }, { [4 x i32] }* @_ZTV1B.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* dso_local_equivalent @_ZN1B3barEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32] }, { [4 x i32] }* @_ZTV1B.local, i32 0, i32 0, i32 2) to i64)) to i32)] }, align 4

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// of a soft incremental rollout. This ABI should only be used if the flag for
// it is passed on Fuchsia.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck --check-prefix=RELATIVE-ABI %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck --check-prefix=RELATIVE-ABI %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -S -o - -emit-llvm -fno-experimental-relative-c++-abi-vtables | FileCheck --check-prefix=DEFAULT-ABI %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -S -o - -emit-llvm | FileCheck --check-prefix=DEFAULT-ABI %s

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Check the layout of the vtable for a normal class.

// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables | FileCheck %s
// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -O1 -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fhalf-no-semantic-interposition | FileCheck %s

// We should be emitting comdats for each of the virtual function RTTI proxies
// CHECK: $_ZTI1A.rtti_proxy = comdat any
Expand Down
Loading

0 comments on commit d1fd723

Please sign in to comment.