forked from OSchip/llvm-project
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:
parent
793cc518b9
commit
9d55e4ee13
|
@ -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,
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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}
|
Loading…
Reference in New Issue