diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h --- a/llvm/include/llvm/AsmParser/LLParser.h +++ b/llvm/include/llvm/AsmParser/LLParser.h @@ -70,13 +70,14 @@ Constant *ConstantVal; std::unique_ptr ConstantStructElts; bool NoCFI = false; + bool MakeDSOLocalEquivFwdRef = false; ValID() = default; ValID(const ValID &RHS) : Kind(RHS.Kind), Loc(RHS.Loc), UIntVal(RHS.UIntVal), FTy(RHS.FTy), StrVal(RHS.StrVal), StrVal2(RHS.StrVal2), APSIntVal(RHS.APSIntVal), APFloatVal(RHS.APFloatVal), ConstantVal(RHS.ConstantVal), - NoCFI(RHS.NoCFI) { + NoCFI(RHS.NoCFI), MakeDSOLocalEquivFwdRef(RHS.MakeDSOLocalEquivFwdRef) { assert(!RHS.ConstantStructElts); } @@ -520,6 +521,8 @@ const SmallVector &ArgList, FunctionType *&FuncTy); + GlobalValue *getDSOLocalEquivFwdRef(ValID &Fn, PointerType *ExpectedTy); + // Constant Parsing. bool parseValID(ValID &ID, PerFunctionState *PFS, Type *ExpectedTy = nullptr); 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 @@ -233,6 +233,11 @@ "expected a function, alias to function, or ifunc " "in dso_local_equivalent"); + if (FwdRef->getType() != GV->getType()) + return error(GVRef.Loc, "dso_local_equivalent type does not match global type: got type '" + + getTypeString(GV->getType()) + + "' but expected '" + getTypeString(FwdRef->getType()) + "'"); + auto *Equiv = DSOLocalEquivalent::get(GV); FwdRef->replaceAllUsesWith(Equiv); FwdRef->eraseFromParent(); @@ -1535,6 +1540,32 @@ // GlobalValue Reference/Resolution Routines. //===----------------------------------------------------------------------===// +GlobalValue *LLParser::getDSOLocalEquivFwdRef(ValID &Fn, PointerType *ExpectedTy) { + assert(Fn.Kind == ValID::t_GlobalName || Fn.Kind == ValID::t_GlobalID); + auto &FwdRefMap = (Fn.Kind == ValID::t_GlobalID) + ? ForwardRefDSOLocalEquivalentIDs + : ForwardRefDSOLocalEquivalentNames; + GlobalValue *&FwdRef = FwdRefMap.try_emplace(Fn, nullptr).first->second; + if (!FwdRef) { + Type *Ty; + if (ExpectedTy->isOpaquePointerTy()) { + // This is ok since all opaque pointers are treated the same. + Ty = Type::getInt8Ty(Context); + } else { + Ty = ExpectedTy->getNonOpaquePointerElementType(); + } + + if (auto *FuncTy = dyn_cast(Ty)) { + FwdRef = Function::Create(FuncTy, GlobalValue::InternalLinkage, "", *M); + } else { + FwdRef = new GlobalVariable(*M, Ty, false, + GlobalValue::InternalLinkage, nullptr, "", + nullptr, GlobalValue::NotThreadLocal); + } + } + return FwdRef; +} + static inline GlobalValue *createGlobalFwdRef(Module *M, PointerType *PTy) { // For opaque pointers, the used global type does not matter. We will later // RAUW it with a global/function of the correct type. @@ -3479,20 +3510,31 @@ } if (!GV) { - // Make a placeholder global variable as a placeholder for this reference. - auto &FwdRefMap = (Fn.Kind == ValID::t_GlobalID) - ? ForwardRefDSOLocalEquivalentIDs - : ForwardRefDSOLocalEquivalentNames; - GlobalValue *&FwdRef = FwdRefMap.try_emplace(Fn, nullptr).first->second; - if (!FwdRef) { - FwdRef = new GlobalVariable(*M, Type::getInt8Ty(Context), false, - GlobalValue::InternalLinkage, nullptr, "", - nullptr, GlobalValue::NotThreadLocal); + if (ExpectedTy) { + // We are parsing dso_local_equivalent with a known type given + // beforehand (probably from parsing in a global). + auto *PtrTy = dyn_cast(ExpectedTy); + if (!PtrTy) + return error(Fn.Loc, "expected a pointer to function, alias to function, or ifunc " + "in dso_local_equivalent"); + ID.ConstantVal = getDSOLocalEquivFwdRef(Fn, PtrTy); + ID.Kind = ValID::t_Constant; + return false; + } else { + // We are parsing dso_local_equivalent with an unknown type we do not + // know at this point. This can happen when parsing some call-like + // instruction where the type beforehand is the return type. In this + // case, delay creating the fwd ref until we know the type. + ID.ConstantVal = nullptr; + ID.Kind = Fn.Kind; + if (Fn.Kind == ValID::t_GlobalID) { + ID.UIntVal = Fn.UIntVal; + } else { + ID.StrVal = Fn.StrVal; + } + ID.MakeDSOLocalEquivFwdRef = true; + return false; } - - ID.ConstantVal = FwdRef; - ID.Kind = ValID::t_Constant; - return false; } if (!GV->getValueType()->isFunctionTy()) @@ -5427,6 +5469,18 @@ if (Ty->isFunctionTy()) return error(ID.Loc, "functions are not values, refer to them as pointers"); + if (ID.MakeDSOLocalEquivFwdRef && !ID.ConstantVal) { + // Now that we know the expected type for this function, we can create the + // fwd ref. + auto *PtrTy = dyn_cast(Ty); + if (!PtrTy) + return error(ID.Loc, "expected a pointer to function, alias to function, or ifunc " + "in dso_local_equivalent"); + ID.ConstantVal = getDSOLocalEquivFwdRef(ID, PtrTy); + ID.MakeDSOLocalEquivFwdRef = false; + ID.Kind = ValID::t_Constant; + } + switch (ID.Kind) { case ValID::t_LocalID: if (!PFS) diff --git a/llvm/test/CodeGen/X86/dso_local_equivalent.ll b/llvm/test/CodeGen/X86/dso_local_equivalent.ll --- a/llvm/test/CodeGen/X86/dso_local_equivalent.ll +++ b/llvm/test/CodeGen/X86/dso_local_equivalent.ll @@ -215,3 +215,11 @@ @const = constant i32 trunc (i64 ptrtoint (ptr dso_local_equivalent @forward_extern_func to i64) to i32) declare void @forward_extern_func() + +; CHECK: const2: +; CHECK: .long forward_func_defined@PLT +@const2 = constant i32 trunc (i64 sub (i64 ptrtoint (void ()* dso_local_equivalent @forward_func_defined to i64), i64 ptrtoint (i32* getelementptr inbounds (i32, i32* @const2, i32 0) to i64)) to i32) + +define void @forward_func_defined() { + ret void +} diff --git a/llvm/test/CodeGen/X86/dso_local_equivalent_errors.ll b/llvm/test/CodeGen/X86/dso_local_equivalent_errors.ll --- a/llvm/test/CodeGen/X86/dso_local_equivalent_errors.ll +++ b/llvm/test/CodeGen/X86/dso_local_equivalent_errors.ll @@ -1,6 +1,11 @@ ; RUN: split-file %s %t ; RUN: not llc -mtriple=x86_64-linux-gnu -o - %t/undefined_func.ll 2>&1 | FileCheck %s -check-prefix=UNDEFINED ; RUN: not llc -mtriple=x86_64-linux-gnu -o - %t/invalid_arg.ll 2>&1 | FileCheck %s -check-prefix=INVALID +; RUN: not llc -mtriple=x86_64-linux-gnu -o - %t/invalid_type.ll 2>&1 | FileCheck %s -check-prefix=INVALID-TYPE +; RUN: not llc -mtriple=x86_64-linux-gnu -o - %t/invalid_type2.ll 2>&1 | FileCheck %s -check-prefix=INVALID-TYPE2 +;; TODO(58209): Explicitly disable opaque pointers for this test to ensure it runs correctly. +; RUN: not llc -mtriple=x86_64-linux-gnu -o - -opaque-pointers=0 %t/invalid_type3.ll 2>&1 | FileCheck %s -check-prefix=INVALID-TYPE3 +; RUN: not llc -mtriple=x86_64-linux-gnu -o - %t/invalid_type4.ll 2>&1 | FileCheck %s -check-prefix=INVALID-TYPE4 ;--- undefined_func.ll ; UNDEFINED: error: unknown function 'undefined_func' referenced by dso_local_equivalent @@ -17,3 +22,33 @@ } @glob = constant i32 1 + +;--- invalid_type.ll +; INVALID-TYPE: error: expected a function, alias to function, or ifunc in dso_local_equivalent +@glob = constant i32 trunc (i64 sub (i64 ptrtoint (i32* dso_local_equivalent @const to i64), i64 ptrtoint (i32* @glob to i64)) to i32) +@const = constant i32 1 + +;--- invalid_type2.ll +; INVALID-TYPE2: error: expected a function, alias to function, or ifunc in dso_local_equivalent +@glob = constant i32 trunc (i64 sub (i64 ptrtoint (i32** dso_local_equivalent @const_ptr to i64), i64 ptrtoint (i32* @glob to i64)) to i32) +@const_ptr = constant i32* @const +@const = constant i32 1 + +;--- invalid_type3.ll +; INVALID-TYPE3: error: dso_local_equivalent type does not match global type: got type 'i32 ()*' but expected 'void ()*' +define void @call_global_var() { + call void dso_local_equivalent @glob() + ret void +} + +define i32 @glob() { + ret i32 1 +} + +;--- invalid_type4.ll +; INVALID-TYPE4: error: expected a pointer to function, alias to function, or ifunc in dso_local_equivalent +@glob = constant i32 trunc (i64 sub (i64 ptrtoint (i32 () dso_local_equivalent @func to i64), i64 ptrtoint (i32* @glob to i64)) to i32) + +define i32 @func() { + ret i32 1 +}