diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -3753,6 +3753,25 @@ 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: + +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. + +``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. + .. _constantexprs: Constant Expressions 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,6 +263,7 @@ LLVMGlobalIFuncValueKind, LLVMGlobalVariableValueKind, LLVMBlockAddressValueKind, + LLVMUnnamedFuncValueKind, LLVMConstantExprValueKind, LLVMConstantArrayValueKind, LLVMConstantStructValueKind, @@ -1549,6 +1550,7 @@ macro(GlobalObject) \ macro(Function) \ macro(GlobalVariable) \ + macro(UnnamedFunc) \ macro(UndefValue) \ macro(Instruction) \ macro(UnaryOperator) \ @@ -2154,6 +2156,7 @@ LLVMValueRef ElementValueConstant, unsigned *IdxList, unsigned NumIdx); LLVMValueRef LLVMBlockAddress(LLVMValueRef F, LLVMBasicBlockRef BB); +LLVMValueRef LLVMUnnamedFunc(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 @@ -95,6 +95,9 @@ const GlobalValue *RHS, const TargetMachine &TM) const override; + const MCExpr *lowerUnnamedFunc(const UnnamedFunc *UF, + 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,6 +889,39 @@ 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 { + friend class Constant; + + UnnamedFunc(Function *F); + + void *operator new(size_t s) { return User::operator new(s, 1); } + + void destroyConstantImpl(); + Value *handleOperandChangeImpl(Value *From, Value *To); + +public: + /// Return a UnnamedFunc for the specified function. + static UnnamedFunc *get(Function *F); + + /// Transparently provide more efficient getOperand methods. + DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Value); + + Function *getFunction() 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; + } +}; + +template <> +struct OperandTraits + : public FixedNumOperandTraits {}; + +DEFINE_TRANSPARENT_OPERAND_ACCESSORS(UnnamedFunc, Value) + //===----------------------------------------------------------------------===// /// A constant value that is initialized with an expression using /// other constant values. 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,6 +65,7 @@ HANDLE_GLOBAL_VALUE(GlobalVariable) HANDLE_CONSTANT(BlockAddress) HANDLE_CONSTANT(ConstantExpr) +HANDLE_CONSTANT(UnnamedFunc) // 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,6 +38,7 @@ class SectionKind; class StringRef; class TargetMachine; +class UnnamedFunc; class TargetLoweringObjectFile : public MCObjectFileInfo { /// Name-mangler for global names. @@ -176,6 +177,11 @@ return nullptr; } + virtual const MCExpr *lowerUnnamedFunc(const UnnamedFunc *UF, + const TargetMachine &TM) const { + return nullptr; + } + /// Target supports replacing a data "PC"-relative access to a symbol /// through another symbol, by accessing the later via a GOT entry instead? bool supportIndirectSymViaGOTPCRel() const { 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,6 +303,9 @@ return true; } + if (auto *UF = dyn_cast(C)) + return IsConstantOffsetFromGlobal(UF->getFunction(), GV, Offset, DL); + // Otherwise, if this isn't a constant expr, bail out. auto *CE = dyn_cast(C); if (!CE) return false; 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,6 +722,7 @@ KEYWORD(vscale); KEYWORD(x); KEYWORD(blockaddress); + KEYWORD(unnamed_func); // 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,6 +3420,39 @@ return false; } + case lltok::kw_unnamed_func: { + // ValID ::= 'unnamed_func' '(' @foo ')' + Lex.Lex(); + + ValID Fn; + + if (ParseToken(lltok::lparen, "expected '(' in unnamed_func expression") || + ParseValID(Fn) || + ParseToken(lltok::rparen, "expected ')' in unnamed_func expression")) + return true; + + if (Fn.Kind != ValID::t_GlobalID && Fn.Kind != ValID::t_GlobalName) + return Error(Fn.Loc, "expected function name in unnamed_func"); + + // Try to find the function (but skip it if it's forward-referenced). + GlobalValue *GV = nullptr; + if (Fn.Kind == ValID::t_GlobalID) { + if (Fn.UIntVal < NumberedVals.size()) + GV = NumberedVals[Fn.UIntVal]; + } else if (!ForwardRefVals.count(Fn.StrVal)) { + GV = M->getNamedValue(Fn.StrVal); + } + + assert(GV && "Could not find a corresponding global variable"); + if (!isa(GV)) + return Error(Fn.Loc, "expected function name in unnamed_func"); + Function *F = cast(GV); + + ID.ConstantVal = UnnamedFunc::get(F); + ID.Kind = ValID::t_Constant; + return false; + } + case lltok::kw_trunc: case lltok::kw_zext: case lltok::kw_sext: 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,6 +358,7 @@ kw_extractvalue, kw_insertvalue, kw_blockaddress, + kw_unnamed_func, 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 @@ -2288,6 +2288,9 @@ 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); + const ConstantExpr *CE = dyn_cast(CV); if (!CE) { llvm_unreachable("Unknown constant value to lower!"); 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 @@ -1557,6 +1557,9 @@ if (const BlockAddress *BA = dyn_cast(C)) return DAG.getBlockAddress(BA, VT); + if (const auto *UF = dyn_cast(C)) + return getValue(UF->getFunction()); + VectorType *VecTy = cast(V->getType()); // Now that we know the number and type of the elements, get that number of 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 @@ -969,6 +969,13 @@ 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()), + PLTRelativeVariantKind, getContext()); +} + MCSection *TargetLoweringObjectFileELF::getSectionForCommandLines() const { // Use ".GCC.command.line" since this feature is to support clang's // -frecord-gcc-switches which in turn attempts to mimic GCC's switch of the 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,6 +1433,14 @@ return; } + if (const auto *UF = dyn_cast(CV)) { + Out << "unnamed_func("; + WriteAsOperandInternal(Out, UF->getFunction(), &TypePrinter, Machine, + Context); + Out << ")"; + return; + } + if (const ConstantArray *CA = dyn_cast(CV)) { Type *ETy = CA->getType()->getElementType(); Out << '['; 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 @@ -501,6 +501,9 @@ case Constant::BlockAddressVal: delete static_cast(C); break; + case Constant::UnnamedFuncVal: + delete static_cast(C); + break; case Constant::UndefValueVal: delete static_cast(C); break; @@ -646,10 +649,17 @@ return false; // Relative pointers do not need to be dynamically relocated. - if (auto *LHSGV = dyn_cast(LHSOp0->stripPointerCasts())) - if (auto *RHSGV = dyn_cast(RHSOp0->stripPointerCasts())) + if (auto *RHSGV = + dyn_cast(RHSOp0->stripInBoundsConstantOffsets())) { + auto *LHS = LHSOp0->stripInBoundsConstantOffsets(); + if (auto *LHSGV = dyn_cast(LHS)) { if (LHSGV->isDSOLocal() && RHSGV->isDSOLocal()) return false; + } else if (isa(LHS)) { + if (RHSGV->isDSOLocal()) + return false; + } + } } } } @@ -1756,6 +1766,53 @@ return nullptr; } +UnnamedFunc *UnnamedFunc::get(Function *F) { + UnnamedFunc *&UF = F->getContext().pImpl->UnnamedFuncs[F]; + if (!UF) + UF = new UnnamedFunc(F); + + assert(UF->getFunction() == F && + "Unnamed function does not match the expected function"); + return UF; +} + +UnnamedFunc::UnnamedFunc(Function *F) + : Constant(F->getType(), Value::UnnamedFuncVal, &Op<0>(), 1) { + setOperand(0, F); +} + +/// Remove the constant from the constant table. +void UnnamedFunc::destroyConstantImpl() { + if (const auto *F = dyn_cast(getFunction())) + getFunction()->getType()->getContext().pImpl->UnnamedFuncs.erase(F); +} + +Value *UnnamedFunc::handleOperandChangeImpl(Value *From, Value *To) { + assert(From == getFunction() && "Changing value does not match operand."); + assert(To->getType() == getType()); + assert(isa(To)); + + // 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 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); + return nullptr; +} + //---- ConstantExpr::get() implementations. // 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 @@ -1364,6 +1364,9 @@ DenseMap, BlockAddress *> BlockAddresses; + + DenseMap UnnamedFuncs; + ConstantUniqueMap ExprConstants; ConstantUniqueMap InlineAsms; 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 @@ -19,3 +19,27 @@ ; CHECK: relro2: ; CHECK: .long hidden-relro2 @relro2 = constant i32 trunc (i64 sub (i64 ptrtoint (i8* @hidden to i64), i64 ptrtoint (i32* @relro2 to i64)) to i32) + +; CHECK: .section .rodata.cst8 +; CHECK-NEXT: .globl obj +; CHECK: obj: +; CHECK: .long 0 +; CHECK: .long (hidden_func-obj)-4 + +declare hidden void @hidden_func() + +; Ensure that inbound GEPs with constant offsets are also resolved. +@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) + } }, align 4 + +; CHECK: .section .rodata.rodata2 +; CHECK-NEXT: .globl rodata2 +; CHECK: rodata2: +; CHECK: .long extern_func@PLT-rodata2 + +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) diff --git a/llvm/test/CodeGen/X86/unnamed_func.ll b/llvm/test/CodeGen/X86/unnamed_func.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/unnamed_func.ll @@ -0,0 +1,11 @@ +; RUN: llc -relocation-model=pic -data-sections -o - %s | FileCheck %s + +target triple = "x86_64-unknown-linux-gnu" + +declare void @extern_func() + +; CHECK: data: +; CHECK-NEXT: .quad extern_func@PLT +@data = constant { void ()* } { + void ()* unnamed_func(@extern_func) +}, align 4