Make explicit -fno-semantic-interposition (in -fpic mode) infer dso_local

-fno-semantic-interposition is currently the CC1 default. (The opposite
disables some interprocedural optimizations.) However, it does not infer
dso_local: on most targets accesses to ExternalLinkage functions/variables
defined in the current module still need PLT/GOT.

This patch makes explicit -fno-semantic-interposition infer dso_local,
so that PLT/GOT can be eliminated if targets implement local aliases
for AsmPrinter::getSymbolPreferLocal (currently only x86).

Currently we check whether the module flag "SemanticInterposition" is 0.
If yes, infer dso_local. In the future, we can infer dso_local unless
"SemanticInterposition" is 1: frontends other than clang will also
benefit from the optimization if they don't bother setting the flag.
(There will be risks if they do want ELF interposition: they need to set
"SemanticInterposition" to 1.)
This commit is contained in:
Fangrui Song 2020-05-25 15:05:35 -07:00
parent 793cc518b9
commit 9d55e4ee13
14 changed files with 96 additions and 10 deletions

View File

@ -301,6 +301,7 @@ ENUM_LANGOPT(TypeVisibilityMode, Visibility, 3, DefaultVisibility,
LANGOPT(SetVisibilityForExternDecls, 1, 0,
"apply global symbol visibility to external declarations without an explicit visibility")
BENIGN_LANGOPT(SemanticInterposition , 1, 0, "semantic interposition")
BENIGN_LANGOPT(ExplicitNoSemanticInterposition, 1, 0, "explicitly no semantic interposition")
ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff,
"stack protector mode")
ENUM_LANGOPT(TrivialAutoVarInit, TrivialAutoVarInitKind, 2, TrivialAutoVarInitKind::Uninitialized,

View File

@ -3371,7 +3371,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>;
def fno_semantic_interposition: Flag<["-"], "fno-semantic-interposition">, Group<f_Group>, Flags<[CC1Option]>;
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>;

View File

@ -493,6 +493,9 @@ 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.

View File

@ -4471,10 +4471,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(A->getValue());
}
if (Args.hasFlag(options::OPT_fsemantic_interposition,
options::OPT_fno_semantic_interposition, false) &&
RelocationModel != llvm::Reloc::Static && !IsPIE)
CmdArgs.push_back("-fsemantic-interposition");
// 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);
CmdArgs.push_back("-mthread-model");
if (Arg *A = Args.getLastArg(options::OPT_mthread_model)) {

View File

@ -3049,6 +3049,9 @@ 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);
// -mrtd option
if (Arg *A = Args.getLastArg(OPT_mrtd)) {

View File

@ -1,5 +1,9 @@
// 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}

View File

@ -2,8 +2,12 @@
// RUN: %clang -target x86_64 %s -Werror -fPIC -fsemantic-interposition -c -### 2>&1 | FileCheck %s
// CHECK: "-fsemantic-interposition"
// RUN: %clang -target x86_64 %s -Werror -fPIC -fsemantic-interposition -fno-semantic-interposition -c -### 2>&1 | FileCheck --check-prefix=NO %s
/// Require explicit -fno-semantic-interposition to infer dso_local.
// RUN: %clang -target x86_64 %s -Werror -fPIC -fsemantic-interposition -fno-semantic-interposition -c -### 2>&1 | FileCheck --check-prefix=EXPLICIT_NO %s
// EXPLICIT_NO: "-fno-semantic-interposition"
// RUN: %clang -target x86_64 %s -Werror -fsemantic-interposition -c -### 2>&1 | FileCheck --check-prefix=NO %s
// RUN: %clang -target x86_64 %s -Werror -fPIC -c -### 2>&1 | FileCheck --check-prefix=NO %s
// RUN: %clang -target x86_64 %s -Werror -fPIE -fsemantic-interposition -c -### 2>&1 | FileCheck --check-prefix=NO %s
// NO-NOT: "-fsemantic-interposition"
// NO-NOT: "-fno-semantic-interposition"

View File

@ -427,6 +427,7 @@ public:
/// inlining across interposable call edges, since the callee can be
/// replaced with something arbitrary.
bool isInterposable() const;
bool canBenefitFromLocalAlias() const;
bool hasExternalLinkage() const { return isExternalLinkage(getLinkage()); }
bool hasAvailableExternallyLinkage() const {

View File

@ -857,6 +857,7 @@ public:
/// Returns whether semantic interposition is to be respected.
bool getSemanticInterposition() const;
bool noSemanticInterposition() const;
/// Set whether semantic interposition is to be respected.
void setSemanticInterposition(bool);

View File

@ -462,10 +462,10 @@ MCSymbol *AsmPrinter::getSymbolPreferLocal(const GlobalValue &GV) const {
// assembler would otherwise be conservative and assume a global default
// visibility symbol can be interposable, even if the code generator already
// assumed it.
if (TM.getTargetTriple().isOSBinFormatELF() &&
GlobalObject::isExternalLinkage(GV.getLinkage()) && GV.isDSOLocal() &&
!GV.isDeclaration() && !isa<GlobalIFunc>(GV) && !GV.hasComdat())
return getSymbolWithGlobalValueBase(&GV, "$local");
if (TM.getTargetTriple().isOSBinFormatELF() && GV.canBenefitFromLocalAlias())
if (GV.isDSOLocal() || (TM.getTargetTriple().isX86() &&
GV.getParent()->noSemanticInterposition()))
return getSymbolWithGlobalValueBase(&GV, "$local");
return TM.getSymbol(&GV);
}

View File

@ -101,6 +101,12 @@ bool GlobalValue::isInterposable() const {
!isDSOLocal();
}
bool GlobalValue::canBenefitFromLocalAlias() const {
// See AsmPrinter::getSymbolPreferLocal().
return GlobalObject::isExternalLinkage(getLinkage()) && !isDeclaration() &&
!isa<GlobalIFunc>(this) && !hasComdat();
}
unsigned GlobalValue::getAlignment() const {
if (auto *GA = dyn_cast<GlobalAlias>(this)) {
// In general we cannot compute this at the IR level, but we try.

View File

@ -600,6 +600,13 @@ void Module::setSemanticInterposition(bool SI) {
addModuleFlag(ModFlagBehavior::Error, "SemanticInterposition", SI);
}
bool Module::noSemanticInterposition() const {
// Conservatively require an explicit zero value for now.
Metadata *MF = getModuleFlag("SemanticInterposition");
auto *Val = cast_or_null<ConstantAsMetadata>(MF);
return Val && cast<ConstantInt>(Val->getValue())->getZExtValue() == 0;
}
void Module::setOwnedMemoryBuffer(std::unique_ptr<MemoryBuffer> MB) {
OwnedMemoryBuffer = std::move(MB);
}

View File

@ -193,6 +193,14 @@ bool TargetMachine::shouldAssumeDSOLocal(const Module &M,
// Check if we can use copy relocations.
if (!(GV && GV->isThreadLocal()) && RM == Reloc::Static)
return true;
} else if (TT.isOSBinFormatELF()) {
// If dso_local allows AsmPrinter::getSymbolPreferLocal to use a local
// alias, set the flag. We cannot set dso_local for other global values,
// because otherwise direct accesses to a probably interposable symbol (even
// if the codegen assumes not) will be rejected by the linker.
if (!GV || !GV->canBenefitFromLocalAlias())
return false;
return TT.isX86() && M.noSemanticInterposition();
}
// ELF & wasm support preemption of other symbols.

View File

@ -0,0 +1,46 @@
; RUN: llc -mtriple=x86_64 -relocation-model=pic < %s | FileCheck %s
;; With a module flag SemanticInterposition=0, infer dso_local flags even if PIC.
;; Local aliases will be generated for applicable variables and functions.
@var = global i32 0, align 4
@ifunc = ifunc i32 (), bitcast (i32 ()* ()* @ifunc_resolver to i32 ()*)
define i32 @ifunc_impl() {
entry:
ret i32 0
}
define i32 ()* @ifunc_resolver() {
entry:
ret i32 ()* @ifunc_impl
}
declare i32 @external()
define i32 @func() {
ret i32 0
}
;; Don't set dso_local on declarations or ifuncs.
define i32 @foo() {
; CHECK: movl .Lvar$local(%rip), %ebp
; CHECK: callq external@PLT
; CHECK: callq ifunc@PLT
; CHECK: callq .Lfunc$local{{$}}
entry:
%0 = load i32, i32* @var, align 4
%call = tail call i32 @external()
%add = add nsw i32 %call, %0
%call1 = tail call i32 @ifunc()
%add2 = add nsw i32 %add, %call1
%call2 = tail call i32 @func()
%add3 = add nsw i32 %add, %call2
ret i32 %add3
}
!llvm.module.flags = !{!0, !1}
!0 = !{i32 1, !"SemanticInterposition", i32 0}
!1 = !{i32 7, !"PIC Level", i32 2}