diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -3753,24 +3753,40 @@ Finally, some targets may provide defined semantics when using the value as the operand to an inline assembly, but that is target specific. -.. _unnamed_func: +.. _dso_local_equivalent: -Unnamed Function ----------------- - -``unnamed_func(@function)`` - -The '``unnamed_func``' constant is a wrapper for functions that represents a -value which may or may not be lowered to a direct reference to the function. -The name is leveraged from ``unnamed_addr`` where the address of a ``GlobalValue`` -marked with this is insignificant. This is equivalent to the ``@plt`` modifier -on X86, where the address of functions marked with this is insignificant. The -type of this constant is always the same type as that of the function. +DSO Local Equivalent +-------------------- -``unnamedfunc(@func)`` can be lowered to something that "functionally" -represents @func. This could be a direct reference (``func``), the ``@plt`` -modifier on X86/AArch64 (``func@plt``), a thunk, or anything that's equivalent -to the resolved function as a call target. +``dso_local_equivalent(@global)`` + +A '``dso_local_equivalent``' constant represents a global value which is +functionally equivalent to a given global value, but is always defined in the +current linkage unit. The resulting pointer has the same type as the underlying +global. The resulting pointer is permitted, but not required, to be different +from a pointer to the global, and it may have different values in different +translation units. + +The target global may not have ``extern_weak`` linkage. + +``dso_local_equivalent`` can be implemented in several ways: + +- If the global has private or internal linkage, has hidden visibility, or is + ``dso_local``, ``dso_local_equivalent`` can be implemented as simply a pointer + to the global. +- If the global is a function, ifunc, or alias to a function or ifunc, + ``dso_local_equivalent`` can be implemented with + a stub that tail-calls the function. Many targets support relocations that + resolve at link time to either a function or a stub for it, depending on if + the function is defined within the linkage unit; LLVM will use this when + available. (This is commonly called a "PLT stub".) On other targets, the stub + may need to be emitted explicitly. +- If the global is a variable or alias to a variable, this can be implemented + by copying the contents + of the variable into the current linkage unit (as if by `memcpy`). This is + not supported on all targets, and target-specific restrictions may apply. For + example, on ELF this can be implemented as a copy relocation, which requires + that the global has the same size at load time as it did at at link time. .. _constantexprs: diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h --- a/llvm/include/llvm-c/Core.h +++ b/llvm/include/llvm-c/Core.h @@ -263,7 +263,7 @@ LLVMGlobalIFuncValueKind, LLVMGlobalVariableValueKind, LLVMBlockAddressValueKind, - LLVMUnnamedFuncValueKind, + LLVMDSOLocalEquivalentValueKind, LLVMConstantExprValueKind, LLVMConstantArrayValueKind, LLVMConstantStructValueKind, @@ -1550,7 +1550,7 @@ macro(GlobalObject) \ macro(Function) \ macro(GlobalVariable) \ - macro(UnnamedFunc) \ + macro(DSOLocalEquivalent) \ macro(UndefValue) \ macro(Instruction) \ macro(UnaryOperator) \ @@ -2156,7 +2156,7 @@ LLVMValueRef ElementValueConstant, unsigned *IdxList, unsigned NumIdx); LLVMValueRef LLVMBlockAddress(LLVMValueRef F, LLVMBasicBlockRef BB); -LLVMValueRef LLVMUnnamedFunc(LLVMValueRef F); +LLVMValueRef LLVMDSOLocalEquivalent(LLVMValueRef F); /** Deprecated: Use LLVMGetInlineAsm instead. */ LLVMValueRef LLVMConstInlineAsm(LLVMTypeRef Ty, diff --git a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h --- a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h +++ b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h @@ -94,8 +94,8 @@ const GlobalValue *RHS, const TargetMachine &TM) const override; - const MCExpr *lowerUnnamedFunc(const UnnamedFunc *UF, - const TargetMachine &TM) const override; + const MCExpr *lowerDSOLocalEquivalent(const DSOLocalEquivalent *Equiv, + const TargetMachine &TM) const override; MCSection *getSectionForCommandLines() const override; }; diff --git a/llvm/include/llvm/IR/Constants.h b/llvm/include/llvm/IR/Constants.h --- a/llvm/include/llvm/IR/Constants.h +++ b/llvm/include/llvm/IR/Constants.h @@ -889,12 +889,13 @@ DEFINE_TRANSPARENT_OPERAND_ACCESSORS(BlockAddress, Value) -/// Wrapper for a function which indicates the value used may not be lowered to -/// a direct reference to the original function. -class UnnamedFunc final : public Constant { +/// Wrapper for a global constant variable that represents a value that +/// functionally represents the original variable. This can be a function, or +/// any constant global variable. +class DSOLocalEquivalent final : public Constant { friend class Constant; - UnnamedFunc(Function *F); + DSOLocalEquivalent(GlobalObject *GO); void *operator new(size_t s) { return User::operator new(s, 1); } @@ -902,25 +903,33 @@ Value *handleOperandChangeImpl(Value *From, Value *To); public: - /// Return a UnnamedFunc for the specified function. - static UnnamedFunc *get(Function *F); + /// Return a DSOLocalEquivalent for the specified function. + static DSOLocalEquivalent *get(Function *F); + + /// Return a DSOLocalEquivalent for the specified global variables. This + /// global variable must be constant. + static DSOLocalEquivalent *get(GlobalVariable *GV); /// Transparently provide more efficient getOperand methods. DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value); + bool isFunction() const { return isa(Op<0>().get()); } Function *getFunction() const { return cast(Op<0>().get()); } + GlobalVariable *getGlobalVariable() const { + return cast(Op<0>().get()); + } /// Methods for support type inquiry through isa, cast, and dyn_cast: static bool classof(const Value *V) { - return V->getValueID() == UnnamedFuncVal; + return V->getValueID() == DSOLocalEquivalentVal; } }; template <> -struct OperandTraits - : public FixedNumOperandTraits {}; +struct OperandTraits + : public FixedNumOperandTraits {}; -DEFINE_TRANSPARENT_OPERAND_ACCESSORS(UnnamedFunc, Value) +DEFINE_TRANSPARENT_OPERAND_ACCESSORS(DSOLocalEquivalent, Value) //===----------------------------------------------------------------------===// /// A constant value that is initialized with an expression using diff --git a/llvm/include/llvm/IR/Value.def b/llvm/include/llvm/IR/Value.def --- a/llvm/include/llvm/IR/Value.def +++ b/llvm/include/llvm/IR/Value.def @@ -65,7 +65,7 @@ HANDLE_GLOBAL_VALUE(GlobalVariable) HANDLE_CONSTANT(BlockAddress) HANDLE_CONSTANT(ConstantExpr) -HANDLE_CONSTANT(UnnamedFunc) +HANDLE_CONSTANT(DSOLocalEquivalent) // ConstantAggregate. HANDLE_CONSTANT(ConstantArray) diff --git a/llvm/include/llvm/Target/TargetLoweringObjectFile.h b/llvm/include/llvm/Target/TargetLoweringObjectFile.h --- a/llvm/include/llvm/Target/TargetLoweringObjectFile.h +++ b/llvm/include/llvm/Target/TargetLoweringObjectFile.h @@ -38,7 +38,7 @@ class SectionKind; class StringRef; class TargetMachine; -class UnnamedFunc; +class DSOLocalEquivalent; class TargetLoweringObjectFile : public MCObjectFileInfo { /// Name-mangler for global names. @@ -182,8 +182,8 @@ return nullptr; } - virtual const MCExpr *lowerUnnamedFunc(const UnnamedFunc *UF, - const TargetMachine &TM) const { + virtual const MCExpr *lowerDSOLocalEquivalent(const DSOLocalEquivalent *Equiv, + const TargetMachine &TM) const { return nullptr; } diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -303,8 +303,11 @@ return true; } - if (auto *UF = dyn_cast(C)) - return IsConstantOffsetFromGlobal(UF->getFunction(), GV, Offset, DL); + if (auto *Equiv = dyn_cast(C)) { + assert(Equiv->isFunction() && "Lowering of dso_local_equivalent of global " + "variables is not yet supported"); + return IsConstantOffsetFromGlobal(Equiv->getFunction(), GV, Offset, DL); + } // Otherwise, if this isn't a constant expr, bail out. auto *CE = dyn_cast(C); diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -722,7 +722,7 @@ KEYWORD(vscale); KEYWORD(x); KEYWORD(blockaddress); - KEYWORD(unnamed_func); + KEYWORD(dso_local_equivalent); // Metadata types. KEYWORD(distinct); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -3420,19 +3420,21 @@ return false; } - case lltok::kw_unnamed_func: { - // ValID ::= 'unnamed_func' '(' @foo ')' + case lltok::kw_dso_local_equivalent: { + // ValID ::= 'dso_local_equivalent' '(' @foo ')' Lex.Lex(); ValID Fn; - if (ParseToken(lltok::lparen, "expected '(' in unnamed_func expression") || + if (ParseToken(lltok::lparen, + "expected '(' in dso_local_equivalent expression") || ParseValID(Fn) || - ParseToken(lltok::rparen, "expected ')' in unnamed_func expression")) + ParseToken(lltok::rparen, + "expected ')' in dso_local_equivalent expression")) return true; if (Fn.Kind != ValID::t_GlobalID && Fn.Kind != ValID::t_GlobalName) - return Error(Fn.Loc, "expected function name in unnamed_func"); + return Error(Fn.Loc, "expected function name in dso_local_equivalent"); // Try to find the function (but skip it if it's forward-referenced). GlobalValue *GV = nullptr; @@ -3445,10 +3447,10 @@ assert(GV && "Could not find a corresponding global variable"); if (!isa(GV)) - return Error(Fn.Loc, "expected function name in unnamed_func"); + return Error(Fn.Loc, "expected function name in dso_local_equivalent"); Function *F = cast(GV); - ID.ConstantVal = UnnamedFunc::get(F); + ID.ConstantVal = DSOLocalEquivalent::get(F); ID.Kind = ValID::t_Constant; return false; } diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -358,7 +358,7 @@ kw_extractvalue, kw_insertvalue, kw_blockaddress, - kw_unnamed_func, + kw_dso_local_equivalent, kw_freeze, diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -2285,8 +2285,8 @@ if (const BlockAddress *BA = dyn_cast(CV)) return MCSymbolRefExpr::create(GetBlockAddressSymbol(BA), Ctx); - if (const auto *UF = dyn_cast(CV)) - return getObjFileLowering().lowerUnnamedFunc(UF, TM); + if (const auto *Equiv = dyn_cast(CV)) + return getObjFileLowering().lowerDSOLocalEquivalent(Equiv, TM); const ConstantExpr *CE = dyn_cast(CV); if (!CE) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -1514,8 +1514,11 @@ if (const BlockAddress *BA = dyn_cast(C)) return DAG.getBlockAddress(BA, VT); - if (const auto *UF = dyn_cast(C)) - return getValue(UF->getFunction()); + if (const auto *Equiv = dyn_cast(C)) { + if (Equiv->isFunction()) + return getValue(Equiv->getFunction()); + return getValue(Equiv->getGlobalVariable()); + } VectorType *VecTy = cast(V->getType()); diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -929,10 +929,11 @@ MCSymbolRefExpr::create(TM.getSymbol(RHS), getContext()), getContext()); } -const MCExpr * -TargetLoweringObjectFileELF::lowerUnnamedFunc(const UnnamedFunc *UF, - const TargetMachine &TM) const { - return MCSymbolRefExpr::create(TM.getSymbol(UF->getFunction()), +const MCExpr *TargetLoweringObjectFileELF::lowerDSOLocalEquivalent( + const DSOLocalEquivalent *Equiv, const TargetMachine &TM) const { + assert(Equiv->isFunction() && "Lowering of dso_local_equivalent of global " + "variables is not yet supported"); + return MCSymbolRefExpr::create(TM.getSymbol(Equiv->getFunction()), PLTRelativeVariantKind, getContext()); } diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -1433,10 +1433,14 @@ return; } - if (const auto *UF = dyn_cast(CV)) { - Out << "unnamed_func("; - WriteAsOperandInternal(Out, UF->getFunction(), &TypePrinter, Machine, - Context); + if (const auto *Equiv = dyn_cast(CV)) { + Out << "dso_local_equivalent("; + if (Equiv->isFunction()) + WriteAsOperandInternal(Out, Equiv->getFunction(), &TypePrinter, Machine, + Context); + else + WriteAsOperandInternal(Out, Equiv->getGlobalVariable(), &TypePrinter, + Machine, Context); Out << ")"; return; } diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -509,8 +509,8 @@ case Constant::BlockAddressVal: delete static_cast(C); break; - case Constant::UnnamedFuncVal: - delete static_cast(C); + case Constant::DSOLocalEquivalentVal: + delete static_cast(C); break; case Constant::UndefValueVal: delete static_cast(C); @@ -663,7 +663,7 @@ if (auto *LHSGV = dyn_cast(LHS)) { if (LHSGV->isDSOLocal() && RHSGV->isDSOLocal()) return false; - } else if (isa(LHS)) { + } else if (isa(LHS)) { if (RHSGV->isDSOLocal()) return false; } @@ -1774,50 +1774,76 @@ return nullptr; } -UnnamedFunc *UnnamedFunc::get(Function *F) { - UnnamedFunc *&UF = F->getContext().pImpl->UnnamedFuncs[F]; - if (!UF) - UF = new UnnamedFunc(F); +DSOLocalEquivalent *DSOLocalEquivalent::get(Function *F) { + DSOLocalEquivalent *&EquivF = F->getContext().pImpl->DSOLocalEquivalents[F]; + if (!EquivF) + EquivF = new DSOLocalEquivalent(F); - assert(UF->getFunction() == F && - "Unnamed function does not match the expected function"); - return UF; + assert(EquivF->getFunction() == F && + "DSOLocalFunction does not match the expected function"); + return EquivF; } -UnnamedFunc::UnnamedFunc(Function *F) - : Constant(F->getType(), Value::UnnamedFuncVal, &Op<0>(), 1) { - setOperand(0, F); +DSOLocalEquivalent *DSOLocalEquivalent::get(GlobalVariable *GV) { + assert(GV->isConstant() && + "The global variable passed to DSOLocalEquivalent must be constant"); + DSOLocalEquivalent *&EquivGV = + GV->getContext().pImpl->DSOLocalEquivalents[GV]; + if (!EquivGV) + EquivGV = new DSOLocalEquivalent(GV); + + assert(EquivGV->getGlobalVariable() == F && + "DSOLocalFunction does not match the expected global variable"); + return EquivGV; +} + +DSOLocalEquivalent::DSOLocalEquivalent(GlobalObject *GO) + : Constant(GO->getType(), Value::DSOLocalEquivalentVal, &Op<0>(), 1) { + setOperand(0, GO); } /// Remove the constant from the constant table. -void UnnamedFunc::destroyConstantImpl() { +void DSOLocalEquivalent::destroyConstantImpl() { if (const auto *F = dyn_cast(getFunction())) - getFunction()->getType()->getContext().pImpl->UnnamedFuncs.erase(F); + F->getContext().pImpl->DSOLocalEquivalents.erase(F); + else if (const auto *GV = dyn_cast(getGlobalVariable())) + GV->getContext().pImpl->DSOLocalEquivalents.erase(GV); } -Value *UnnamedFunc::handleOperandChangeImpl(Value *From, Value *To) { - assert(From == getFunction() && "Changing value does not match operand."); - assert(To->getType() == getType()); - assert(isa(To)); +Value *DSOLocalEquivalent::handleOperandChangeImpl(Value *From, Value *To) { + assert((From == getFunction() || From == getGlobalVariable()) && + "Changing value does not match operand."); + assert(To->getType() == getType() && "Mismatched types"); + assert(isa(To) && "Can only replace the operands with a constant"); - // The replacement is with another function. - if (const auto *ToFunc = dyn_cast(To)) { - UnnamedFunc *&NewUF = getContext().pImpl->UnnamedFuncs[ToFunc]; - if (NewUF) - return NewUF; + // The replacement is with another function or constant global variable. + if (const auto *ToObj = dyn_cast(To)) { + DSOLocalEquivalent *&NewEquiv = + getContext().pImpl->DSOLocalEquivalents[ToObj]; + if (NewEquiv) + return NewEquiv; } // The replacement could be a bitcast or an alias to another function. We can - // replace it with a bitcast to the unnamed_func of that function. - auto *Func = cast(To->stripPointerCastsAndAliases()); - UnnamedFunc *&NewUF = getContext().pImpl->UnnamedFuncs[Func]; - if (NewUF) - return llvm::ConstantExpr::getBitCast(NewUF, getType()); - - // Replace this with the new one. - getContext().pImpl->UnnamedFuncs.erase(getFunction()); - NewUF = this; - setOperand(0, Func); + // replace it with a bitcast to the dso_local_equivalent of that function. + if (auto *Func = dyn_cast(To->stripPointerCastsAndAliases())) { + DSOLocalEquivalent *&NewEquiv = + getContext().pImpl->DSOLocalEquivalents[Func]; + if (NewEquiv) + return llvm::ConstantExpr::getBitCast(NewEquiv, getType()); + + // Replace this with the new one. + getContext().pImpl->DSOLocalEquivalents.erase(getFunction()); + NewEquiv = this; + setOperand(0, Func); + return nullptr; + } + + // TODO: Add the remaining handling for global variables. + auto *GV = dyn_cast(To); + (void)GV; + + llvm_unreachable("Unimplemented handling for global variables."); return nullptr; } diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -1365,7 +1365,7 @@ DenseMap, BlockAddress *> BlockAddresses; - DenseMap UnnamedFuncs; + DenseMap DSOLocalEquivalents; ConstantUniqueMap ExprConstants; diff --git a/llvm/test/CodeGen/X86/unnamed_func.ll b/llvm/test/CodeGen/X86/dso_local_equivalent.ll rename from llvm/test/CodeGen/X86/unnamed_func.ll rename to llvm/test/CodeGen/X86/dso_local_equivalent.ll --- a/llvm/test/CodeGen/X86/unnamed_func.ll +++ b/llvm/test/CodeGen/X86/dso_local_equivalent.ll @@ -1,4 +1,5 @@ ; RUN: llc -relocation-model=pic -data-sections -o - %s | FileCheck %s +; TODO: Add a test for lowering other constant global variables that aren't functions. target triple = "x86_64-unknown-linux-gnu" @@ -7,5 +8,5 @@ ; CHECK: data: ; CHECK-NEXT: .quad extern_func@PLT @data = constant { void ()* } { - void ()* unnamed_func(@extern_func) + void ()* dso_local_equivalent(@extern_func) }, align 4 diff --git a/llvm/test/CodeGen/X86/relptr-rodata.ll b/llvm/test/CodeGen/X86/relptr-rodata.ll --- a/llvm/test/CodeGen/X86/relptr-rodata.ll +++ b/llvm/test/CodeGen/X86/relptr-rodata.ll @@ -32,7 +32,7 @@ @obj = dso_local unnamed_addr constant { { i32, i32 } } { { i32, i32 } { i32 0, - i32 trunc (i64 sub (i64 ptrtoint (void ()* unnamed_func(@hidden_func) to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i32, i32 } }, { { i32, i32 } }* @obj, i32 0, i32 0, i32 1) to i64)) to i32) + i32 trunc (i64 sub (i64 ptrtoint (void ()* dso_local_equivalent(@hidden_func) to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i32, i32 } }, { { i32, i32 } }* @obj, i32 0, i32 0, i32 1) to i64)) to i32) } }, align 4 ; CHECK: .section .rodata.rodata2 @@ -42,4 +42,4 @@ declare void @extern_func() unnamed_addr -@rodata2 = hidden constant i32 trunc (i64 sub (i64 ptrtoint (void ()* unnamed_func(@extern_func) to i64), i64 ptrtoint (i32* @rodata2 to i64)) to i32) +@rodata2 = hidden constant i32 trunc (i64 sub (i64 ptrtoint (void ()* dso_local_equivalent(@extern_func) to i64), i64 ptrtoint (i32* @rodata2 to i64)) to i32)