Index: llvm/trunk/docs/BitCodeFormat.rst =================================================================== --- llvm/trunk/docs/BitCodeFormat.rst +++ llvm/trunk/docs/BitCodeFormat.rst @@ -681,7 +681,7 @@ MODULE_CODE_GLOBALVAR Record ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``[GLOBALVAR, strtab offset, strtab size, pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal, unnamed_addr, externally_initialized, dllstorageclass, comdat]`` +``[GLOBALVAR, strtab offset, strtab size, pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal, unnamed_addr, externally_initialized, dllstorageclass, comdat, attributes, preemptionspecifier]`` The ``GLOBALVAR`` record (code 7) marks the declaration or definition of a global variable. The operand fields are: @@ -761,12 +761,21 @@ * *comdat*: An encoding of the COMDAT of this function +* *attributes*: If nonzero, the 1-based index into the table of AttributeLists. + +.. _bcpreemptionspecifier: + +* *preemptionspecifier*: If present, an encoding of the runtime preemption specifier of this variable: + + * ``dso_preemptable``: code 0 + * ``dso_local``: code 1 + .. _FUNCTION: MODULE_CODE_FUNCTION Record ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``[FUNCTION, strtab offset, strtab size, type, callingconv, isproto, linkage, paramattr, alignment, section, visibility, gc, prologuedata, dllstorageclass, comdat, prefixdata, personalityfn]`` +``[FUNCTION, strtab offset, strtab size, type, callingconv, isproto, linkage, paramattr, alignment, section, visibility, gc, prologuedata, dllstorageclass, comdat, prefixdata, personalityfn, preemptionspecifier]`` The ``FUNCTION`` record (code 8) marks the declaration or definition of a function. The operand fields are: @@ -828,10 +837,12 @@ * *personalityfn*: If non-zero, the value index of the personality function for this function, plus 1. +* *preemptionspecifier*: If present, an encoding of the :ref:`runtime preemption specifier` of this function. + MODULE_CODE_ALIAS Record ^^^^^^^^^^^^^^^^^^^^^^^^ -``[ALIAS, strtab offset, strtab size, alias type, aliasee val#, linkage, visibility, dllstorageclass, threadlocal, unnamed_addr]`` +``[ALIAS, strtab offset, strtab size, alias type, aliasee val#, linkage, visibility, dllstorageclass, threadlocal, unnamed_addr, preemptionspecifier]`` The ``ALIAS`` record (code 9) marks the definition of an alias. The operand fields are @@ -856,6 +867,8 @@ * *unnamed_addr*: If present, an encoding of the :ref:`unnamed_addr` attribute of this alias +* *preemptionspecifier*: If present, an encoding of the :ref:`runtime preemption specifier` of this alias. + .. _MODULE_CODE_GCNAME: MODULE_CODE_GCNAME Record Index: llvm/trunk/docs/LangRef.rst =================================================================== --- llvm/trunk/docs/LangRef.rst +++ llvm/trunk/docs/LangRef.rst @@ -527,6 +527,24 @@ For platforms without linker support of ELF TLS model, the -femulated-tls flag can be used to generate GCC compatible emulated TLS code. +.. _runtime_preemption_model: + +Runtime Preemption Specifiers +----------------------------- + +Global variables, functions and aliases may have an optional runtime preemption +specifier. If a preemption specifier isn't given explicitly, then a +symbol is assumed to be ``dso_preemptable``. + +``dso_preemptable`` + Indicates that the function or variable may be replaced by a symbol from + outside the linkage unit at runtime. + +``dso_local`` + The compiler may assume that a function or variable marked as ``dso_local`` + will resolve to a symbol within the same linkage unit. Direct access will + be generated even if the definition is not within this compilation unit. + .. _namedtypes: Structure Types @@ -650,6 +668,7 @@ iteration. The maximum alignment is ``1 << 29``. Globals can also have a :ref:`DLL storage class `, +an optional :ref:`runtime preemption specifier `, an optional :ref:`global attributes ` and an optional list of attached :ref:`metadata `. @@ -658,7 +677,8 @@ Syntax:: - @ = [Linkage] [Visibility] [DLLStorageClass] [ThreadLocal] + @ = [Linkage] [PreemptionSpecifier] [Visibility] + [DLLStorageClass] [ThreadLocal] [(unnamed_addr|local_unnamed_addr)] [AddrSpace] [ExternallyInitialized] [] @@ -691,7 +711,8 @@ --------- LLVM function definitions consist of the "``define``" keyword, an -optional :ref:`linkage type `, an optional :ref:`visibility +optional :ref:`linkage type `, an optional :ref:`runtime preemption +specifier `, an optional :ref:`visibility style `, an optional :ref:`DLL storage class `, an optional :ref:`calling convention `, an optional ``unnamed_addr`` attribute, a return type, an optional @@ -750,7 +771,7 @@ Syntax:: - define [linkage] [visibility] [DLLStorageClass] + define [linkage] [PreemptionSpecifier] [visibility] [DLLStorageClass] [cconv] [ret attrs] @ ([argument list]) [(unnamed_addr|local_unnamed_addr)] [fn Attrs] [section "name"] @@ -777,12 +798,13 @@ constant expression. Aliases may have an optional :ref:`linkage type `, an optional +:ref:`runtime preemption specifier `, an optional :ref:`visibility style `, an optional :ref:`DLL storage class ` and an optional :ref:`tls model `. Syntax:: - @ = [Linkage] [Visibility] [DLLStorageClass] [ThreadLocal] [(unnamed_addr|local_unnamed_addr)] alias , * @ + @ = [Linkage] [PreemptionSpecifier] [Visibility] [DLLStorageClass] [ThreadLocal] [(unnamed_addr|local_unnamed_addr)] alias , * @ The linkage must be one of ``private``, ``internal``, ``linkonce``, ``weak``, ``linkonce_odr``, ``weak_odr``, ``external``. Note that some system linkers Index: llvm/trunk/include/llvm/IR/GlobalValue.h =================================================================== --- llvm/trunk/include/llvm/IR/GlobalValue.h +++ llvm/trunk/include/llvm/IR/GlobalValue.h @@ -80,13 +80,14 @@ ValueType(Ty), Linkage(Linkage), Visibility(DefaultVisibility), UnnamedAddrVal(unsigned(UnnamedAddr::None)), DllStorageClass(DefaultStorageClass), ThreadLocal(NotThreadLocal), - HasLLVMReservedName(false), IntID((Intrinsic::ID)0U), Parent(nullptr) { + HasLLVMReservedName(false), IsDSOLocal(false), + IntID((Intrinsic::ID)0U), Parent(nullptr) { setName(Name); } Type *ValueType; - static const unsigned GlobalValueSubClassDataBits = 18; + static const unsigned GlobalValueSubClassDataBits = 17; // All bitfields use unsigned as the underlying type so that MSVC will pack // them. @@ -103,11 +104,15 @@ /// Function::intrinsicID() returns Intrinsic::not_intrinsic. unsigned HasLLVMReservedName : 1; + /// If true then there is a definition within the same linkage unit and that + /// definition cannot be runtime preempted. + unsigned IsDSOLocal : 1; + private: friend class Constant; // Give subclasses access to what otherwise would be wasted padding. - // (18 + 4 + 2 + 2 + 2 + 3 + 1) == 32. + // (17 + 4 + 2 + 2 + 2 + 3 + 1 + 1) == 32. unsigned SubClassData : GlobalValueSubClassDataBits; void destroyConstantImpl(); @@ -261,6 +266,12 @@ Type *getValueType() const { return ValueType; } + void setDSOLocal(bool Local) { IsDSOLocal = Local; } + + bool isDSOLocal() const { + return IsDSOLocal; + } + static LinkageTypes getLinkOnceLinkage(bool ODR) { return ODR ? LinkOnceODRLinkage : LinkOnceAnyLinkage; } Index: llvm/trunk/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLLexer.cpp +++ llvm/trunk/lib/AsmParser/LLLexer.cpp @@ -494,6 +494,9 @@ KEYWORD(declare); KEYWORD(define); KEYWORD(global); KEYWORD(constant); + KEYWORD(dso_local); + KEYWORD(dso_preemptable); + KEYWORD(private); KEYWORD(internal); KEYWORD(available_externally); Index: llvm/trunk/lib/AsmParser/LLParser.h =================================================================== --- llvm/trunk/lib/AsmParser/LLParser.h +++ llvm/trunk/lib/AsmParser/LLParser.h @@ -240,7 +240,9 @@ bool ParseOptionalParamAttrs(AttrBuilder &B); bool ParseOptionalReturnAttrs(AttrBuilder &B); bool ParseOptionalLinkage(unsigned &Linkage, bool &HasLinkage, - unsigned &Visibility, unsigned &DLLStorageClass); + unsigned &Visibility, unsigned &DLLStorageClass, + bool &DSOLocal); + void ParseOptionalDSOLocal(bool &DSOLocal); void ParseOptionalVisibility(unsigned &Visibility); void ParseOptionalDLLStorageClass(unsigned &DLLStorageClass); bool ParseOptionalCallingConv(unsigned &CC); @@ -284,12 +286,12 @@ bool ParseNamedGlobal(); bool ParseGlobal(const std::string &Name, LocTy Loc, unsigned Linkage, bool HasLinkage, unsigned Visibility, - unsigned DLLStorageClass, + unsigned DLLStorageClass, bool DSOLocal, GlobalVariable::ThreadLocalMode TLM, GlobalVariable::UnnamedAddr UnnamedAddr); bool parseIndirectSymbol(const std::string &Name, LocTy Loc, unsigned Linkage, unsigned Visibility, - unsigned DLLStorageClass, + unsigned DLLStorageClass, bool DSOLocal, GlobalVariable::ThreadLocalMode TLM, GlobalVariable::UnnamedAddr UnnamedAddr); bool parseComdat(); Index: llvm/trunk/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLParser.cpp +++ llvm/trunk/lib/AsmParser/LLParser.cpp @@ -483,10 +483,12 @@ /// ParseUnnamedGlobal: /// OptionalVisibility (ALIAS | IFUNC) ... -/// OptionalLinkage OptionalVisibility OptionalDLLStorageClass +/// OptionalLinkage OptionalPreemptionSpecifier OptionalVisibility +/// OptionalDLLStorageClass /// ... -> global variable /// GlobalID '=' OptionalVisibility (ALIAS | IFUNC) ... -/// GlobalID '=' OptionalLinkage OptionalVisibility OptionalDLLStorageClass +/// GlobalID '=' OptionalLinkage OptionalPreemptionSpecifier OptionalVisibility +/// OptionalDLLStorageClass /// ... -> global variable bool LLParser::ParseUnnamedGlobal() { unsigned VarID = NumberedVals.size(); @@ -506,23 +508,26 @@ bool HasLinkage; unsigned Linkage, Visibility, DLLStorageClass; + bool DSOLocal; GlobalVariable::ThreadLocalMode TLM; GlobalVariable::UnnamedAddr UnnamedAddr; - if (ParseOptionalLinkage(Linkage, HasLinkage, Visibility, DLLStorageClass) || + if (ParseOptionalLinkage(Linkage, HasLinkage, Visibility, DLLStorageClass, + DSOLocal) || ParseOptionalThreadLocal(TLM) || ParseOptionalUnnamedAddr(UnnamedAddr)) return true; if (Lex.getKind() != lltok::kw_alias && Lex.getKind() != lltok::kw_ifunc) return ParseGlobal(Name, NameLoc, Linkage, HasLinkage, Visibility, - DLLStorageClass, TLM, UnnamedAddr); + DLLStorageClass, DSOLocal, TLM, UnnamedAddr); return parseIndirectSymbol(Name, NameLoc, Linkage, Visibility, - DLLStorageClass, TLM, UnnamedAddr); + DLLStorageClass, DSOLocal, TLM, UnnamedAddr); } /// ParseNamedGlobal: /// GlobalVar '=' OptionalVisibility (ALIAS | IFUNC) ... -/// GlobalVar '=' OptionalLinkage OptionalVisibility OptionalDLLStorageClass +/// GlobalVar '=' OptionalLinkage OptionalPreemptionSpecifier +/// OptionalVisibility OptionalDLLStorageClass /// ... -> global variable bool LLParser::ParseNamedGlobal() { assert(Lex.getKind() == lltok::GlobalVar); @@ -532,19 +537,21 @@ bool HasLinkage; unsigned Linkage, Visibility, DLLStorageClass; + bool DSOLocal; GlobalVariable::ThreadLocalMode TLM; GlobalVariable::UnnamedAddr UnnamedAddr; if (ParseToken(lltok::equal, "expected '=' in global variable") || - ParseOptionalLinkage(Linkage, HasLinkage, Visibility, DLLStorageClass) || + ParseOptionalLinkage(Linkage, HasLinkage, Visibility, DLLStorageClass, + DSOLocal) || ParseOptionalThreadLocal(TLM) || ParseOptionalUnnamedAddr(UnnamedAddr)) return true; if (Lex.getKind() != lltok::kw_alias && Lex.getKind() != lltok::kw_ifunc) return ParseGlobal(Name, NameLoc, Linkage, HasLinkage, Visibility, - DLLStorageClass, TLM, UnnamedAddr); + DLLStorageClass, DSOLocal, TLM, UnnamedAddr); return parseIndirectSymbol(Name, NameLoc, Linkage, Visibility, - DLLStorageClass, TLM, UnnamedAddr); + DLLStorageClass, DSOLocal, TLM, UnnamedAddr); } bool LLParser::parseComdat() { @@ -709,19 +716,21 @@ } /// parseIndirectSymbol: -/// ::= GlobalVar '=' OptionalLinkage OptionalVisibility -/// OptionalDLLStorageClass OptionalThreadLocal -/// OptionalUnnamedAddr 'alias|ifunc' IndirectSymbol +/// ::= GlobalVar '=' OptionalLinkage OptionalPreemptionSpecifier +/// OptionalVisibility OptionalDLLStorageClass +/// OptionalThreadLocal OptionalUnnamedAddr +// 'alias|ifunc' IndirectSymbol /// /// IndirectSymbol /// ::= TypeAndValue /// /// Everything through OptionalUnnamedAddr has already been parsed. /// -bool LLParser::parseIndirectSymbol( - const std::string &Name, LocTy NameLoc, unsigned L, unsigned Visibility, - unsigned DLLStorageClass, GlobalVariable::ThreadLocalMode TLM, - GlobalVariable::UnnamedAddr UnnamedAddr) { +bool LLParser::parseIndirectSymbol(const std::string &Name, LocTy NameLoc, + unsigned L, unsigned Visibility, + unsigned DLLStorageClass, bool DSOLocal, + GlobalVariable::ThreadLocalMode TLM, + GlobalVariable::UnnamedAddr UnnamedAddr) { bool IsAlias; if (Lex.getKind() == lltok::kw_alias) IsAlias = true; @@ -740,6 +749,11 @@ return Error(NameLoc, "symbol with local linkage must have default visibility"); + if (DSOLocal && !IsAlias) { + return Error(NameLoc, + "dso_local is invalid on ifunc"); + } + Type *Ty; LocTy ExplicitTypeLoc = Lex.getLoc(); if (ParseType(Ty) || @@ -812,6 +826,7 @@ GA->setVisibility((GlobalValue::VisibilityTypes)Visibility); GA->setDLLStorageClass((GlobalValue::DLLStorageClassTypes)DLLStorageClass); GA->setUnnamedAddr(UnnamedAddr); + GA->setDSOLocal(DSOLocal); if (Name.empty()) NumberedVals.push_back(GA.get()); @@ -843,12 +858,14 @@ } /// ParseGlobal -/// ::= GlobalVar '=' OptionalLinkage OptionalVisibility OptionalDLLStorageClass -/// OptionalThreadLocal OptionalUnnamedAddr OptionalAddrSpace -/// OptionalExternallyInitialized GlobalType Type Const OptionalAttrs -/// ::= OptionalLinkage OptionalVisibility OptionalDLLStorageClass +/// ::= GlobalVar '=' OptionalLinkage OptionalPreemptionSpecifier +/// OptionalVisibility OptionalDLLStorageClass /// OptionalThreadLocal OptionalUnnamedAddr OptionalAddrSpace /// OptionalExternallyInitialized GlobalType Type Const OptionalAttrs +/// ::= OptionalLinkage OptionalPreemptionSpecifier OptionalVisibility +/// OptionalDLLStorageClass OptionalThreadLocal OptionalUnnamedAddr +/// OptionalAddrSpace OptionalExternallyInitialized GlobalType Type +/// Const OptionalAttrs /// /// Everything up to and including OptionalUnnamedAddr has been parsed /// already. @@ -856,7 +873,7 @@ bool LLParser::ParseGlobal(const std::string &Name, LocTy NameLoc, unsigned Linkage, bool HasLinkage, unsigned Visibility, unsigned DLLStorageClass, - GlobalVariable::ThreadLocalMode TLM, + bool DSOLocal, GlobalVariable::ThreadLocalMode TLM, GlobalVariable::UnnamedAddr UnnamedAddr) { if (!isValidVisibilityForLinkage(Visibility, Linkage)) return Error(NameLoc, @@ -930,6 +947,7 @@ GV->setInitializer(Init); GV->setConstant(IsConstant); GV->setLinkage((GlobalValue::LinkageTypes)Linkage); + GV->setDSOLocal(DSOLocal); GV->setVisibility((GlobalValue::VisibilityTypes)Visibility); GV->setDLLStorageClass((GlobalValue::DLLStorageClassTypes)DLLStorageClass); GV->setExternallyInitialized(IsExternallyInitialized); @@ -1608,15 +1626,38 @@ /// ::= 'external' bool LLParser::ParseOptionalLinkage(unsigned &Res, bool &HasLinkage, unsigned &Visibility, - unsigned &DLLStorageClass) { + unsigned &DLLStorageClass, + bool &DSOLocal) { Res = parseOptionalLinkageAux(Lex.getKind(), HasLinkage); if (HasLinkage) Lex.Lex(); + ParseOptionalDSOLocal(DSOLocal); ParseOptionalVisibility(Visibility); ParseOptionalDLLStorageClass(DLLStorageClass); + + if (DSOLocal && DLLStorageClass == GlobalValue::DLLImportStorageClass) { + return Error(Lex.getLoc(), "dso_location and DLL-StorageClass mismatch"); + } + return false; } +void LLParser::ParseOptionalDSOLocal(bool &DSOLocal) { + switch (Lex.getKind()) { + default: + DSOLocal = false; + break; + case lltok::kw_dso_local: + DSOLocal = true; + Lex.Lex(); + break; + case lltok::kw_dso_preemptable: + DSOLocal = false; + Lex.Lex(); + break; + } +} + /// ParseOptionalVisibility /// ::= /*empty*/ /// ::= 'default' @@ -4699,22 +4740,24 @@ } /// FunctionHeader -/// ::= OptionalLinkage OptionalVisibility OptionalCallingConv OptRetAttrs -/// OptUnnamedAddr Type GlobalName '(' ArgList ')' OptFuncAttrs OptSection -/// OptionalAlign OptGC OptionalPrefix OptionalPrologue OptPersonalityFn +/// ::= OptionalLinkage OptionalPreemptionSpecifier OptionalVisibility +/// OptionalCallingConv OptRetAttrs OptUnnamedAddr Type GlobalName +/// '(' ArgList ')' OptFuncAttrs OptSection OptionalAlign OptGC +/// OptionalPrefix OptionalPrologue OptPersonalityFn bool LLParser::ParseFunctionHeader(Function *&Fn, bool isDefine) { // Parse the linkage. LocTy LinkageLoc = Lex.getLoc(); unsigned Linkage; - unsigned Visibility; unsigned DLLStorageClass; + bool DSOLocal; AttrBuilder RetAttrs; unsigned CC; bool HasLinkage; Type *RetType = nullptr; LocTy RetTypeLoc = Lex.getLoc(); - if (ParseOptionalLinkage(Linkage, HasLinkage, Visibility, DLLStorageClass) || + if (ParseOptionalLinkage(Linkage, HasLinkage, Visibility, DLLStorageClass, + DSOLocal) || ParseOptionalCallingConv(CC) || ParseOptionalReturnAttrs(RetAttrs) || ParseType(RetType, RetTypeLoc, true /*void allowed*/)) return true; @@ -4876,6 +4919,7 @@ NumberedVals.push_back(Fn); Fn->setLinkage((GlobalValue::LinkageTypes)Linkage); + Fn->setDSOLocal(DSOLocal); Fn->setVisibility((GlobalValue::VisibilityTypes)Visibility); Fn->setDLLStorageClass((GlobalValue::DLLStorageClassTypes)DLLStorageClass); Fn->setCallingConv(CC); Index: llvm/trunk/lib/AsmParser/LLToken.h =================================================================== --- llvm/trunk/lib/AsmParser/LLToken.h +++ llvm/trunk/lib/AsmParser/LLToken.h @@ -45,6 +45,9 @@ kw_global, kw_constant, + kw_dso_local, + kw_dso_preemptable, + kw_private, kw_internal, kw_linkonce, Index: llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp @@ -911,6 +911,14 @@ } } +static bool getDecodedDSOLocal(unsigned Val) { + switch(Val) { + default: // Map unknown values to preemptable. + case 0: return false; + case 1: return true; + } +} + static GlobalVariable::ThreadLocalMode getDecodedThreadLocalMode(unsigned Val) { switch (Val) { case 0: return GlobalVariable::NotThreadLocal; @@ -2803,7 +2811,7 @@ Error BitcodeReader::parseGlobalVarRecord(ArrayRef Record) { // v1: [pointer type, isconst, initid, linkage, alignment, section, // visibility, threadlocal, unnamed_addr, externally_initialized, - // dllstorageclass, comdat, attributes] (name in VST) + // dllstorageclass, comdat, attributes, preemption specifier] (name in VST) // v2: [strtab_offset, strtab_size, v1] StringRef Name; std::tie(Name, Record) = readNameFromStrtab(Record); @@ -2888,13 +2896,18 @@ auto AS = getAttributes(Record[12]).getFnAttributes(); NewGV->setAttributes(AS); } + + if (Record.size() > 13) { + NewGV->setDSOLocal(getDecodedDSOLocal(Record[13])); + } + return Error::success(); } Error BitcodeReader::parseFunctionRecord(ArrayRef Record) { // v1: [type, callingconv, isproto, linkage, paramattr, alignment, section, // visibility, gc, unnamed_addr, prologuedata, dllstorageclass, comdat, - // prefixdata] (name in VST) + // prefixdata, personalityfn, preemption specifier] (name in VST) // v2: [strtab_offset, strtab_size, v1] StringRef Name; std::tie(Name, Record) = readNameFromStrtab(Record); @@ -2968,6 +2981,10 @@ if (Record.size() > 14 && Record[14] != 0) FunctionPersonalityFns.push_back(std::make_pair(Func, Record[14] - 1)); + if (Record.size() > 15) { + Func->setDSOLocal(getDecodedDSOLocal(Record[15])); + } + ValueList.push_back(Func); // If this is a function with a body, remember the prototype we are @@ -2984,9 +3001,11 @@ unsigned BitCode, ArrayRef Record) { // v1 ALIAS_OLD: [alias type, aliasee val#, linkage] (name in VST) // v1 ALIAS: [alias type, addrspace, aliasee val#, linkage, visibility, - // dllstorageclass] (name in VST) + // dllstorageclass, threadlocal, unnamed_addr, + // preemption specifier] (name in VST) // v1 IFUNC: [alias type, addrspace, aliasee val#, linkage, - // visibility, dllstorageclass] (name in VST) + // visibility, dllstorageclass, threadlocal, unnamed_addr, + // preemption specifier] (name in VST) // v2: [strtab_offset, strtab_size, v1] StringRef Name; std::tie(Name, Record) = readNameFromStrtab(Record); @@ -3036,6 +3055,8 @@ NewGA->setThreadLocalMode(getDecodedThreadLocalMode(Record[OpNum++])); if (OpNum != Record.size()) NewGA->setUnnamedAddr(getDecodedUnnamedAddrType(Record[OpNum++])); + if (OpNum != Record.size()) + NewGA->setDSOLocal(getDecodedDSOLocal(Record[OpNum++])); ValueList.push_back(NewGA); IndirectSymbolInits.push_back(std::make_pair(NewGA, Val)); return Error::success(); Index: llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1197,7 +1197,7 @@ // GLOBALVAR: [strtab offset, strtab size, type, isconst, initid, // linkage, alignment, section, visibility, threadlocal, // unnamed_addr, externally_initialized, dllstorageclass, - // comdat, attributes] + // comdat, attributes, DSO_Local] Vals.push_back(addToStrtab(GV.getName())); Vals.push_back(GV.getName().size()); Vals.push_back(VE.getTypeID(GV.getValueType())); @@ -1213,7 +1213,8 @@ GV.isExternallyInitialized() || GV.getDLLStorageClass() != GlobalValue::DefaultStorageClass || GV.hasComdat() || - GV.hasAttributes()) { + GV.hasAttributes() || + GV.isDSOLocal()) { Vals.push_back(getEncodedVisibility(GV)); Vals.push_back(getEncodedThreadLocalMode(GV)); Vals.push_back(getEncodedUnnamedAddr(GV)); @@ -1223,6 +1224,8 @@ auto AL = GV.getAttributesAsList(AttributeList::FunctionIndex); Vals.push_back(VE.getAttributeListID(AL)); + + Vals.push_back(GV.isDSOLocal()); } else { AbbrevToUse = SimpleGVarAbbrev; } @@ -1236,7 +1239,7 @@ // FUNCTION: [strtab offset, strtab size, type, callingconv, isproto, // linkage, paramattrs, alignment, section, visibility, gc, // unnamed_addr, prologuedata, dllstorageclass, comdat, - // prefixdata, personalityfn] + // prefixdata, personalityfn, DSO_Local] Vals.push_back(addToStrtab(F.getName())); Vals.push_back(F.getName().size()); Vals.push_back(VE.getTypeID(F.getFunctionType())); @@ -1258,6 +1261,7 @@ Vals.push_back( F.hasPersonalityFn() ? (VE.getValueID(F.getPersonalityFn()) + 1) : 0); + Vals.push_back(F.isDSOLocal()); unsigned AbbrevToUse = 0; Stream.EmitRecord(bitc::MODULE_CODE_FUNCTION, Vals, AbbrevToUse); Vals.clear(); @@ -1266,7 +1270,8 @@ // Emit the alias information. for (const GlobalAlias &A : M.aliases()) { // ALIAS: [strtab offset, strtab size, alias type, aliasee val#, linkage, - // visibility, dllstorageclass, threadlocal, unnamed_addr] + // visibility, dllstorageclass, threadlocal, unnamed_addr, + // DSO_Local] Vals.push_back(addToStrtab(A.getName())); Vals.push_back(A.getName().size()); Vals.push_back(VE.getTypeID(A.getValueType())); @@ -1277,6 +1282,8 @@ Vals.push_back(getEncodedDLLStorageClass(A)); Vals.push_back(getEncodedThreadLocalMode(A)); Vals.push_back(getEncodedUnnamedAddr(A)); + Vals.push_back(A.isDSOLocal()); + unsigned AbbrevToUse = 0; Stream.EmitRecord(bitc::MODULE_CODE_ALIAS, Vals, AbbrevToUse); Vals.clear(); Index: llvm/trunk/lib/IR/AsmWriter.cpp =================================================================== --- llvm/trunk/lib/IR/AsmWriter.cpp +++ llvm/trunk/lib/IR/AsmWriter.cpp @@ -2493,6 +2493,11 @@ } } +static void PrintDSOLocation(bool IsDSOLocal, formatted_raw_ostream &Out){ + if (IsDSOLocal) + Out << "dso_local "; +} + static void PrintDLLStorageClass(GlobalValue::DLLStorageClassTypes SCT, formatted_raw_ostream &Out) { switch (SCT) { @@ -2563,6 +2568,7 @@ Out << "external "; Out << getLinkagePrintName(GV->getLinkage()); + PrintDSOLocation(GV->isDSOLocal(), Out); PrintVisibility(GV->getVisibility(), Out); PrintDLLStorageClass(GV->getDLLStorageClass(), Out); PrintThreadLocalModel(GV->getThreadLocalMode(), Out); @@ -2609,6 +2615,7 @@ Out << " = "; Out << getLinkagePrintName(GIS->getLinkage()); + PrintDSOLocation(GIS->isDSOLocal(), Out); PrintVisibility(GIS->getVisibility(), Out); PrintDLLStorageClass(GIS->getDLLStorageClass(), Out); PrintThreadLocalModel(GIS->getThreadLocalMode(), Out); @@ -2720,6 +2727,7 @@ Out << "define "; Out << getLinkagePrintName(F->getLinkage()); + PrintDSOLocation(F->isDSOLocal(), Out); PrintVisibility(F->getVisibility(), Out); PrintDLLStorageClass(F->getDLLStorageClass(), Out); Index: llvm/trunk/lib/IR/Globals.cpp =================================================================== --- llvm/trunk/lib/IR/Globals.cpp +++ llvm/trunk/lib/IR/Globals.cpp @@ -67,6 +67,7 @@ setVisibility(Src->getVisibility()); setUnnamedAddr(Src->getUnnamedAddr()); setDLLStorageClass(Src->getDLLStorageClass()); + setDSOLocal(Src->isDSOLocal()); } void GlobalValue::removeFromParent() { Index: llvm/trunk/lib/IR/Verifier.cpp =================================================================== --- llvm/trunk/lib/IR/Verifier.cpp +++ llvm/trunk/lib/IR/Verifier.cpp @@ -568,6 +568,10 @@ if (GV.isDeclarationForLinker()) Assert(!GV.hasComdat(), "Declaration may not be in a Comdat!", &GV); + if (GV.hasDLLImportStorageClass()) + Assert(!GV.isDSOLocal(), + "GlobalValue with DLLImport Storage is dso_local!", &GV); + forEachUser(&GV, GlobalValueVisited, [&](const Value *V) -> bool { if (const Instruction *I = dyn_cast(V)) { if (!I->getParent() || !I->getParent()->getParent()) Index: llvm/trunk/lib/Target/TargetMachine.cpp =================================================================== --- llvm/trunk/lib/Target/TargetMachine.cpp +++ llvm/trunk/lib/Target/TargetMachine.cpp @@ -128,7 +128,8 @@ if (TT.isOSBinFormatCOFF() || (TT.isOSWindows() && TT.isOSBinFormatMachO())) return true; - if (GV && (GV->hasLocalLinkage() || !GV->hasDefaultVisibility())) + if (GV && (GV->hasLocalLinkage() || !GV->hasDefaultVisibility() || + GV->isDSOLocal())) return true; if (TT.isOSBinFormatMachO()) { Index: llvm/trunk/test/Assembler/dllimport-dsolocal-diag.ll =================================================================== --- llvm/trunk/test/Assembler/dllimport-dsolocal-diag.ll +++ llvm/trunk/test/Assembler/dllimport-dsolocal-diag.ll @@ -0,0 +1,4 @@ +; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s + +declare dso_local dllimport void @fun() +; CHECK: error: dso_location and DLL-StorageClass mismatch Index: llvm/trunk/test/Assembler/ifunc-dsolocal-daig.ll =================================================================== --- llvm/trunk/test/Assembler/ifunc-dsolocal-daig.ll +++ llvm/trunk/test/Assembler/ifunc-dsolocal-daig.ll @@ -0,0 +1,9 @@ +; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s + +@foo = dso_local ifunc i32 (i32), i64 ()* @foo_ifunc +; CHECK: error: dso_local is invalid on ifunc + +define internal i64 @foo_ifunc() { +entry: + ret i64 0 +} Index: llvm/trunk/test/Bitcode/dso_location.ll =================================================================== --- llvm/trunk/test/Bitcode/dso_location.ll +++ llvm/trunk/test/Bitcode/dso_location.ll @@ -0,0 +1,47 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s + +; Tests parsing for the dso_local keyword as well as the serialization/ +; deserialization of the dso_local value on GlobalValues. + +@local_global = dso_local global i32 0 +; CHECK: @local_global = dso_local global i32 0 + +@weak_local_global = weak dso_local global i32 0 +; CHECK: @weak_local_global = weak dso_local global i32 0 + +@external_local_global = external dso_local global i32 +; CHECK: @external_local_global = external dso_local global i32 + +@default_local_global = dso_local default global i32 0 +; CHECK: @default_local_global = dso_local global i32 0 + +@hidden_local_global = dso_local hidden global i32 0 +; CHECK: @hidden_local_global = dso_local hidden global i32 0 + +@protected_local_global = dso_local protected global i32 0 +; CHECK: @protected_local_global = dso_local protected global i32 0 + +@local_alias = dso_local alias i32, i32* @local_global +; CHECK-DAG: @local_alias = dso_local alias i32, i32* @local_global + +@preemptable_alias = dso_preemptable alias i32, i32* @hidden_local_global +; CHECK-DAG: @preemptable_alias = alias i32, i32* @hidden_local_global + +@preemptable_ifunc = dso_preemptable ifunc void (), i8* ()* @ifunc_resolver +; CHECK-DAG: @preemptable_ifunc = ifunc void (), i8* ()* @ifunc_resolver +declare dso_local default void @default_local() +; CHECK: declare dso_local void @default_local() + +declare dso_local hidden void @hidden_local() +; CHECK: declare dso_local hidden void @hidden_local() + +define dso_local protected void @protected_local() { +; CHECK: define dso_local protected void @protected_local() +entry: + ret void +} + +define i8* @ifunc_resolver() { +entry: + ret i8* null +} Index: llvm/trunk/test/CodeGen/PowerPC/preemption.ll =================================================================== --- llvm/trunk/test/CodeGen/PowerPC/preemption.ll +++ llvm/trunk/test/CodeGen/PowerPC/preemption.ll @@ -0,0 +1,301 @@ +; RUN: llc -mtriple powerpc64le-unkown-gnu-linux < %s | FileCheck %s +; RUN: llc -mtriple powerpc64le-unkown-gnu-linux -relocation-model=static \ +; RUN: < %s | FileCheck --check-prefix=STATIC %s +; RUN: llc -mtriple powerpc64le-unkown-gnu-linux -relocation-model=pic \ +; RUN: < %s | FileCheck %s + +; globals + +@strong_default = global i32 55 +define i32* @get_strong_default() #0 { + ret i32* @strong_default + +; STATIC-LABEL: @get_strong_default +; STATIC: addis 3, 2, strong_default@toc@ha +; STATIC: addi 3, 3, strong_default@toc@l +; STATIC: blr + +; CHECK-LABEL: @get_strong_default +; CHECK: addis 3, 2, .LC0@toc@ha +; CHECK: ld 3, .LC0@toc@l(3) +; CHECK: blr +} + +@weak_default = weak global i32 55 +define i32* @get_weak_default() #0 { + ret i32* @weak_default + +; STATIC-LABEL: @get_weak_default +; STATIC: addis 3, 2, weak_default@toc@ha +; STATIC: addi 3, 3, weak_default@toc@l +; STATIC: blr + +; CHECK-LABEL: @get_weak_default +; CHECK: addis 3, 2, .LC1@toc@ha +; CHECK: ld 3, .LC1@toc@l(3) +; CHECK: blr +} + +@external_default_global = external global i32 +define i32* @get_external_default_global() { + ret i32* @external_default_global + +; STATIC-LABEL: @get_external_default_global +; STATIC: addis 3, 2, .LC0@toc@ha +; STATIC: ld 3, .LC0@toc@l(3) +; STATIC: blr + +; CHECK-LABEL: @get_external_default_global +; CHECK: addis 3, 2, .LC2@toc@ha +; CHECK: ld 3, .LC2@toc@l(3) +; CHECK: blr +} + + +@strong_local_global = dso_local global i32 55 +define i32* @get_strong_local_global() { + ret i32* @strong_local_global + +; STATIC-LABEL: @get_strong_local_global +; STATIC: addis 3, 2, strong_local_global@toc@ha +; STATIC: addi 3, 3, strong_local_global@toc@l +; STATIC: blr + +; CHECK-LABEL: @get_strong_local_global +; CHECK: addis 3, 2, strong_local_global@toc@ha +; CHECK: addi 3, 3, strong_local_global@toc@l +; CHECK: blr +} + +@weak_local_global = weak dso_local global i32 42 +define i32* @get_weak_local_global() { + ret i32* @weak_local_global + +; STATIC-LABEL: @get_weak_local_global +; STATIC: addis 3, 2, weak_local_global@toc@ha +; STATIC: addi 3, 3, weak_local_global@toc@l +; STATIC: blr + +; CHECK-LABEL: @get_weak_local_global +; CHECK: addis 3, 2, weak_local_global@toc@ha +; CHECK: addi 3, 3, weak_local_global@toc@l +; CHECK: blr +} + +@external_local_global = external dso_local global i32 +define i32* @get_external_local_global() { + ret i32* @external_local_global +; STATIC-LABEL: @get_external_local_global +; STATIC: addis 3, 2, external_local_global@toc@ha +; STATIC: addi 3, 3, external_local_global@toc@l +; STATIC: blr + +; CHECK-LABEL: @get_external_local_global +; CHECK: addis 3, 2, external_local_global@toc@ha +; CHECK: addi 3, 3, external_local_global@toc@l +; CHECK: blr +} + +@strong_preemptable_global = dso_preemptable global i32 42 +define i32* @get_strong_preemptable_global() { + ret i32* @strong_preemptable_global + +; STATIC-LABEL: @get_strong_preemptable_global +; STATIC: addis 3, 2, strong_preemptable_global@toc@ha +; STATIC: addi 3, 3, strong_preemptable_global@toc@l +; STATIC: blr + +; CHECK-LABEL: @get_strong_preemptable_global +; CHECK: addis 3, 2, .LC3@toc@ha +; CHECK: ld 3, .LC3@toc@l(3) +; CHECK: blr +} + +@weak_preemptable_global = weak dso_preemptable global i32 42 +define i32* @get_weak_preemptable_global() { + ret i32* @weak_preemptable_global + +; STATIC-LABEL: @get_weak_preemptable_global +; STATIC: addis 3, 2, weak_preemptable_global@toc@ha +; STATIC: addi 3, 3, weak_preemptable_global@toc@l +; STATIC: blr + +; CHECK-LABEL: @get_weak_preemptable_global +; CHECK: addis 3, 2, .LC4@toc@ha +; CHECK: ld 3, .LC4@toc@l(3) +; CHECK: blr +} + +@external_preemptable_global = external dso_preemptable global i32 +define i32* @get_external_preemptable_global() { + ret i32* @external_preemptable_global + +; STATIC-LABEL: @get_external_preemptable_global +; STATIC: addis 3, 2, .LC1@toc@ha +; STATIC: ld 3, .LC1@toc@l(3) +; STATIC: blr + +; CHECK-LABEL: @get_external_preemptable_global +; CHECK: addis 3, 2, .LC5@toc@ha +; CHECK: ld 3, .LC5@toc@l(3) +; CHECK: blr +} + +; functions +define signext i32 @strong_default_function(i32 %i) { + ret i32 %i +} +define signext i32 @strong_default_function_caller(i32 %i) { + %call = notail call signext i32 @strong_default_function(i32 signext %i) + ret i32 %call + +; STATIC-LABEL: @strong_default_function_caller +; STATIC: bl strong_default_function +; STATIC-NOT: nop +; STATIC: blr + +; CHECK-LABEL: @strong_default_function_caller +; CHECK: bl strong_default_function +; CHECK-NEXT: nop +; CHECK: blr +} + +define weak signext i32 @weak_default_function(i32 %i) { + ret i32 %i +} +define signext i32 @weak_default_function_caller(i32 %i) { + %call = notail call signext i32 @weak_default_function(i32 signext %i) + ret i32 %call + +; STATIC-LABEL: @weak_default_function_caller +; STATIC: bl weak_default_function +; STATIC-NOT: nop +; STATIC: blr + +; CHECK-LABEL: @weak_default_function_caller +; CHECK: bl weak_default_function +; CHECK-NEXT: nop +; CHECK: blr +} + + +declare i32 @external_default_function(i32 %i) +define i32 @external_default_function_caller(i32 %i) { + %call = notail call signext i32 @external_default_function(i32 signext %i) + ret i32 %call + +; STATIC-LABEL: @external_default_function_caller +; STATIC: bl external_default_function +; STATIC-NEXT: nop +; STATIC: blr + +; CHECK-LABEL: @external_default_function_caller +; CHECK: bl external_default_function +; CHECK-NEXT: nop +; CHECK: blr +} + +define dso_local signext i32 @strong_local_function(i32 %i) { + ret i32 %i +} +define signext i32 @strong_local_function_caller(i32 %i) { + %call = notail call signext i32 @strong_local_function(i32 signext %i) + ret i32 %call + +; STATIC-LABEL: @strong_local_function_caller +; STATIC: bl strong_local_function +; STATIC-NOT: nop +; STATIC: blr + +; CHECK-LABEL: @strong_local_function_caller +; CHECK: bl strong_local_function +; CHECK-NOT: nop +; CHECK: blr +} + +define weak dso_local signext i32 @weak_local_function(i32 %i) { + ret i32 %i +} +define signext i32 @weak_local_function_caller(i32 %i) { + %call = notail call signext i32 @weak_local_function(i32 signext %i) + ret i32 %call + +; STATIC-LABEL: @weak_local_function_caller +; STATIC: bl weak_local_function +; STATIC-NOT: nop +; STATIC: blr + +; CHECK-LABEL: @weak_local_function_caller +; CHECK: bl weak_local_function +; CHECK-NOT: nop +; CHECK: blr +} + +declare dso_local i32 @external_local_function(i32 %i) +define i32 @external_local_function_caller(i32 %i) { + %call = notail call signext i32 @external_local_function(i32 signext %i) + ret i32 %call + +; STATIC-LABEL: @external_local_function_caller +; STATIC: bl external_local_function +; STATIC-NOT: nop +; STATIC: blr + +; CHECK-LABEL: @external_local_function_caller +; CHECK: bl external_local_function +; CHECK-NOT: nop +; CHECK: blr +} + +define dso_preemptable signext i32 @strong_preemptable_function(i32 %i) { + ret i32 %i +} +define signext i32 @strong_preemptable_function_caller(i32 %i) { + %call = notail call signext i32 @strong_preemptable_function(i32 signext %i) + ret i32 %call + +; STATIC-LABEL: @strong_preemptable_function_caller +; STATIC: bl strong_preemptable_function +; STATIC-NOT: nop +; STATIC: blr + +; CHECK-LABEL: @strong_preemptable_function_caller +; CHECK: bl strong_preemptable_function +; CHECK-NEXT: nop +; CHECK: blr +} + +define weak dso_preemptable signext i32 @weak_preemptable_function(i32 %i) { + ret i32 %i +} +define signext i32 @weak_preemptable_function_caller(i32 %i) { + %call = notail call signext i32 @weak_preemptable_function(i32 signext %i) + ret i32 %call + +; STATIC-LABEL: @weak_preemptable_function_caller +; STATIC: bl weak_preemptable_function +; STATIC-NOT: nop +; STATIC: blr + +; CHECK-LABEL: @weak_preemptable_function_caller +; CHECK: bl weak_preemptable_function +; CHECK-NEXT: nop +; CHECK: blr +} + +declare dso_preemptable i32 @external_preemptable_function(i32 %i) +define i32 @external_preemptable_function_caller(i32 %i) { + %call = notail call signext i32 @external_preemptable_function(i32 signext %i) + ret i32 %call + +; STATIC-LABEL: @external_preemptable_function_caller +; STATIC: bl external_preemptable_function +; STATIC-NEXT: nop +; STATIC: blr + +; CHECK-LABEL: @external_preemptable_function_caller +; CHECK: bl external_preemptable_function +; CHECK-NEXT: nop +; CHECK: blr +} + Index: llvm/trunk/test/CodeGen/X86/darwin-preemption.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/darwin-preemption.ll +++ llvm/trunk/test/CodeGen/X86/darwin-preemption.ll @@ -0,0 +1,251 @@ +; RUN: llc -mtriple x86_64-apple-darwin \ +; RUN: -relocation-model=static < %s | FileCheck %s +; RUN: llc -mtriple x86_64-apple-darwin \ +; RUN: -relocation-model=pic < %s | FileCheck %s +; RUN: llc -mtriple x86_64-apple-darwin \ +; RUN: -relocation-model=dynamic-no-pic < %s | FileCheck %s + +; 32 bits + +; RUN: llc -mtriple i386-apple-darwin \ +; RUN: -relocation-model=static < %s | FileCheck --check-prefix=DARWIN32_S %s +; RUN: llc -mtriple i386-apple-darwin \ +; RUN: -relocation-model=pic < %s | FileCheck --check-prefix=DARWIN32 %s +; RUN: llc -mtriple i386-apple-darwin \ +; RUN: -relocation-model=dynamic-no-pic < %s | \ +; RUN: FileCheck --check-prefix=DARWIN32_DNP %s + +; globals + +@strong_default_global = global i32 42 +define i32* @get_strong_default_global() { + ret i32* @strong_default_global +} +; CHECK: leaq _strong_default_global(%rip), %rax +; DARWIN32: leal _strong_default_global-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_strong_default_global, %eax +; DARWIN32_DNP: movl $_strong_default_global, %eax + +@weak_default_global = weak global i32 42 +define i32* @get_weak_default_global() { + ret i32* @weak_default_global +} +; CHECK: movq _weak_default_global@GOTPCREL(%rip), %rax +; DARWIN32: movl L_weak_default_global$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_weak_default_global, %eax +; DARWIN32_DNP: movl L_weak_default_global$non_lazy_ptr, %eax + +@external_default_global = external global i32 +define i32* @get_external_default_global() { + ret i32* @external_default_global +} +; CHECK: movq _external_default_global@GOTPCREL(%rip), %rax +; DARWIN32: movl L_external_default_global$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_external_default_global, %eax +; DARWIN32_DNP: movl L_external_default_global$non_lazy_ptr, %eax + +@strong_local_global = dso_local global i32 42 +define i32* @get_strong_local_global() { + ret i32* @strong_local_global +} +; CHECK: leaq _strong_local_global(%rip), %rax +; DARWIN32: leal _strong_local_global-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_strong_local_global, %eax +; DARWIN32_DNP: movl $_strong_local_global, %eax + +@weak_local_global = weak dso_local global i32 42 +define i32* @get_weak_local_global() { + ret i32* @weak_local_global +} +; CHECK: leaq _weak_local_global(%rip), %rax +; DARWIN32: leal _weak_local_global-L{{.}}$pb(%eax), %eax +; DARWIN32_S: movl $_weak_local_global, %eax +; DARWIN32_DNP: movl $_weak_local_global, %eax + +@external_local_global = external dso_local global i32 +define i32* @get_external_local_global() { + ret i32* @external_local_global +} +; CHECK: leaq _external_local_global(%rip), %rax +; DARWIN32: movl L_external_local_global$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_external_local_global, %eax +; DARWIN32_DNP: movl $_external_local_global, %eax + +@strong_preemptable_global = dso_preemptable global i32 42 +define i32* @get_strong_preemptable_global() { + ret i32* @strong_preemptable_global +} +; CHECK: leaq _strong_preemptable_global(%rip), %rax +; DARWIN32: leal _strong_preemptable_global-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_strong_preemptable_global, %eax +; DARWIN32_DNP: movl $_strong_preemptable_global, %eax + +@weak_preemptable_global = weak dso_preemptable global i32 42 +define i32* @get_weak_preemptable_global() { + ret i32* @weak_preemptable_global +} +; CHECK: movq _weak_preemptable_global@GOTPCREL(%rip), %rax +; DARWIN32: movl L_weak_preemptable_global$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_weak_preemptable_global, %eax +; DARWIN32_DNP: movl L_weak_preemptable_global$non_lazy_ptr, %eax + +@external_preemptable_global = external dso_preemptable global i32 +define i32* @get_external_preemptable_global() { + ret i32* @external_preemptable_global +} +; CHECK: movq _external_preemptable_global@GOTPCREL(%rip), %rax +; DARWIN32: movl L_external_preemptable_global$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_external_preemptable_global, %eax +; DARWIN32_DNP: movl L_external_preemptable_global$non_lazy_ptr, %eax + +; aliases +@aliasee = global i32 42 + +@strong_default_alias = alias i32, i32* @aliasee +define i32* @get_strong_default_alias() { + ret i32* @strong_default_alias +} +; CHECK: leaq _strong_default_alias(%rip), %rax +; DARWIN32: leal _strong_default_alias-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_strong_default_alias, %eax +; DARWIN32_DNP: movl $_strong_default_alias, %eax + +@weak_default_alias = weak alias i32, i32* @aliasee +define i32* @get_weak_default_alias() { + ret i32* @weak_default_alias +} +; CHECK: movq _weak_default_alias@GOTPCREL(%rip), %rax +; DARWIN32: movl L_weak_default_alias$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_weak_default_alias, %eax +; DARWIN32_DNP: movl L_weak_default_alias$non_lazy_ptr, %eax + +@strong_local_alias = dso_local alias i32, i32* @aliasee +define i32* @get_strong_local_alias() { + ret i32* @strong_local_alias +} +; CHECK: leaq _strong_local_alias(%rip), %rax +; DARWIN32: leal _strong_local_alias-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_strong_local_alias, %eax +; DARWIN32_DNP: movl $_strong_local_alias, %eax + +@weak_local_alias = weak dso_local alias i32, i32* @aliasee +define i32* @get_weak_local_alias() { + ret i32* @weak_local_alias +} +; CHECK: leaq _weak_local_alias(%rip), %rax +; DARWIN32: leal _weak_local_alias-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_weak_local_alias, %eax +; DARWIN32_DNP: movl $_weak_local_alias, %eax + +@strong_preemptable_alias = dso_preemptable alias i32, i32* @aliasee +define i32* @get_strong_preemptable_alias() { + ret i32* @strong_preemptable_alias +} +; CHECK: leaq _strong_preemptable_alias(%rip), %rax +; DARWIN32: leal _strong_preemptable_alias-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_strong_preemptable_alias, %eax +; DARWIN32_DNP: movl $_strong_preemptable_alias, %eax + +@weak_preemptable_alias = weak dso_preemptable alias i32, i32* @aliasee +define i32* @get_weak_preemptable_alias() { + ret i32* @weak_preemptable_alias +} +; CHECK: movq _weak_preemptable_alias@GOTPCREL(%rip), %rax +; DARWIN32: movl L_weak_preemptable_alias$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_weak_preemptable_alias, %eax +; DARWIN32_DNP: movl L_weak_preemptable_alias$non_lazy_ptr, %eax + +; functions + +define void @strong_default_function() { + ret void +} +define void()* @get_strong_default_function() { + ret void()* @strong_default_function +} +; CHECK: leaq _strong_default_function(%rip), %rax +; DARWIN32: leal _strong_default_function-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_strong_default_function, %eax +; DARWIN32_DNP: movl $_strong_default_function, %eax + +define weak void @weak_default_function() { + ret void +} +define void()* @get_weak_default_function() { + ret void()* @weak_default_function +} +; CHECK: movq _weak_default_function@GOTPCREL(%rip), %rax +; DARWIN32: movl L_weak_default_function$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_weak_default_function, %eax +; DARWIN32_DNP: movl L_weak_default_function$non_lazy_ptr, %eax + +declare void @external_default_function() +define void()* @get_external_default_function() { + ret void()* @external_default_function +} +; CHECK: movq _external_default_function@GOTPCREL(%rip), %rax +; DARWIN32: movl L_external_default_function$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_external_default_function, %eax +; DARWIN32_DNP: movl L_external_default_function$non_lazy_ptr, %eax + +define dso_local void @strong_local_function() { + ret void +} +define void()* @get_strong_local_function() { + ret void()* @strong_local_function +} +; CHECK: leaq _strong_local_function(%rip), %rax +; DARWIN32: leal _strong_local_function-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_strong_local_function, %eax +; DARWIN32_DNP: movl $_strong_local_function, %eax + +define weak dso_local void @weak_local_function() { + ret void +} +define void()* @get_weak_local_function() { + ret void()* @weak_local_function +} +; CHECK: leaq _weak_local_function(%rip), %rax +; DARWIN32: leal _weak_local_function-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_weak_local_function, %eax +; DARWIN32_DNP: movl $_weak_local_function, %eax + +declare dso_local void @external_local_function() +define void()* @get_external_local_function() { + ret void()* @external_local_function +} +; CHECK: leaq _external_local_function(%rip), %rax +; DARWIN32: movl L_external_local_function$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_external_local_function, %eax +; DARWIN32_DNP: movl $_external_local_function, %eax + +define dso_preemptable void @strong_preemptable_function() { + ret void +} +define void()* @get_strong_preemptable_function() { + ret void()* @strong_preemptable_function +} +; CHECK: leaq _strong_preemptable_function(%rip), %rax +; DARWIN32: leal _strong_preemptable_function-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_strong_preemptable_function, %eax +; DARWIN32_DNP: movl $_strong_preemptable_function, %eax + +define weak dso_preemptable void @weak_preemptable_function() { + ret void +} +define void()* @get_weak_preemptable_function() { + ret void()* @weak_preemptable_function +} +; CHECK: movq _weak_preemptable_function@GOTPCREL(%rip), %rax +; DARWIN32: movl L_weak_preemptable_function$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_weak_preemptable_function, %eax +; DARWIN32_DNP: movl L_weak_preemptable_function$non_lazy_ptr, %eax + +declare dso_preemptable void @external_preemptable_function() +define void()* @get_external_preemptable_function() { + ret void()* @external_preemptable_function +} +; CHECK: movq _external_preemptable_function@GOTPCREL(%rip), %rax +; DARWIN32: movl L_external_preemptable_function$non_lazy_ptr-L{{.*}}$pb(%eax), %eax +; DARWIN32_S: movl $_external_preemptable_function, %eax +; DARWIN32_DNP: movl L_external_preemptable_function$non_lazy_ptr, %eax Index: llvm/trunk/test/CodeGen/X86/linux-preemption.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/linux-preemption.ll +++ llvm/trunk/test/CodeGen/X86/linux-preemption.ll @@ -0,0 +1,225 @@ +; RUN: llc -mtriple x86_64-pc-linux \ +; RUN: -relocation-model=static < %s | FileCheck --check-prefix=STATIC %s +; RUN: llc -mtriple x86_64-pc-linux \ +; RUN: -relocation-model=pic < %s | FileCheck %s +; RUN: llc -mtriple x86_64-pc-linux \ +; RUN: -relocation-model=dynamic-no-pic < %s | FileCheck %s + +; 32 bits + +; RUN: llc -mtriple i386-pc-linux \ +; RUN: -relocation-model=pic < %s | FileCheck --check-prefix=CHECK32 %s + +; globals + +@strong_default_global = global i32 42 +define i32* @get_strong_default_global() { + ret i32* @strong_default_global +} +; CHECK: movq strong_default_global@GOTPCREL(%rip), %rax +; STATIC: movl $strong_default_global, %eax +; CHECK32: movl strong_default_global@GOT(%eax), %eax + +@weak_default_global = weak global i32 42 +define i32* @get_weak_default_global() { + ret i32* @weak_default_global +} +; CHECK: movq weak_default_global@GOTPCREL(%rip), %rax +; STATIC: movl $weak_default_global, %eax +; CHECK32: movl weak_default_global@GOT(%eax), %eax + +@external_default_global = external global i32 +define i32* @get_external_default_global() { + ret i32* @external_default_global +} +; CHECK: movq external_default_global@GOTPCREL(%rip), %rax +; STATIC: movl $external_default_global, %eax +; CHECK32: movl external_default_global@GOT(%eax), %eax + +@strong_local_global = dso_local global i32 42 +define i32* @get_strong_local_global() { + ret i32* @strong_local_global +} +; CHECK: leaq strong_local_global(%rip), %rax +; STATIC: movl $strong_local_global, %eax +; CHECK32: leal strong_local_global@GOTOFF(%eax), %eax + +@weak_local_global = weak dso_local global i32 42 +define i32* @get_weak_local_global() { + ret i32* @weak_local_global +} +; CHECK: leaq weak_local_global(%rip), %rax +; STATIC: movl $weak_local_global, %eax +; CHECK32: leal weak_local_global@GOTOFF(%eax), %eax + +@external_local_global = external dso_local global i32 +define i32* @get_external_local_global() { + ret i32* @external_local_global +} +; CHECK: leaq external_local_global(%rip), %rax +; STATIC: movl $external_local_global, %eax +; CHECK32: leal external_local_global@GOTOFF(%eax), %eax + + +@strong_preemptable_global = dso_preemptable global i32 42 +define i32* @get_strong_preemptable_global() { + ret i32* @strong_preemptable_global +} +; CHECK: movq strong_preemptable_global@GOTPCREL(%rip), %rax +; STATIC: movl $strong_preemptable_global, %eax +; CHECK32: movl strong_preemptable_global@GOT(%eax), %eax + +@weak_preemptable_global = weak dso_preemptable global i32 42 +define i32* @get_weak_preemptable_global() { + ret i32* @weak_preemptable_global +} +; CHECK ;ADD_LABEL_BACK; movq weak_preemptable_global@GOTPCREL(%rip), %rax +; STATIC ;ADD_LABEL_BACK; movq weak_preemptable_global@GOTPCREL, %rax +; CHECK32 ;ADD_LABEL_BACK; movl weak_preemptable_global@GOT(%eax), %eax + +@external_preemptable_global = external dso_preemptable global i32 +define i32* @get_external_preemptable_global() { + ret i32* @external_preemptable_global +} +; CHECK: movq external_preemptable_global@GOTPCREL(%rip), %rax +; STATIC: movl $external_preemptable_global, %eax +; CHECK32: movl external_preemptable_global@GOT(%eax), %eax + +; aliases +@aliasee = global i32 42 + +@strong_default_alias = alias i32, i32* @aliasee +define i32* @get_strong_default_alias() { + ret i32* @strong_default_alias +} +; CHECK: movq strong_default_alias@GOTPCREL(%rip), %rax +; STATIC: movl $strong_default_alias, %eax +; CHECK32: movl strong_default_alias@GOT(%eax), %eax + +@weak_default_alias = weak alias i32, i32* @aliasee +define i32* @get_weak_default_alias() { + ret i32* @weak_default_alias +} +; CHECK: movq weak_default_alias@GOTPCREL(%rip), %rax +; STATIC: movl $weak_default_alias, %eax +; CHECK32: movl weak_default_alias@GOT(%eax), %eax + +@strong_local_alias = dso_local alias i32, i32* @aliasee +define i32* @get_strong_local_alias() { + ret i32* @strong_local_alias +} +; CHECK: leaq strong_local_alias(%rip), %rax +; STATIC: movl $strong_local_alias, %eax +; CHECK32: leal strong_local_alias@GOTOFF(%eax), %eax + +@weak_local_alias = weak dso_local alias i32, i32* @aliasee +define i32* @get_weak_local_alias() { + ret i32* @weak_local_alias +} +; CHECK: leaq weak_local_alias(%rip), %rax +; STATIC: movl $weak_local_alias, %eax +; CHECK32: leal weak_local_alias@GOTOFF(%eax), %eax + + +@strong_preemptable_alias = dso_preemptable alias i32, i32* @aliasee +define i32* @get_strong_preemptable_alias() { + ret i32* @strong_preemptable_alias +} +; CHECK: movq strong_preemptable_alias@GOTPCREL(%rip), %rax +; STATIC: movl $strong_preemptable_alias, %eax +; CHECK32: movl strong_preemptable_alias@GOT(%eax), %eax + +@weak_preemptable_alias = weak dso_preemptable alias i32, i32* @aliasee +define i32* @get_weak_preemptable_alias() { + ret i32* @weak_preemptable_alias +} +; CHECK: movq weak_preemptable_alias@GOTPCREL(%rip), %rax +; STATIC: movl $weak_preemptable_alias, %eax +; CHECK32: movl weak_preemptable_alias@GOT(%eax), %eax + +; functions + +define void @strong_default_function() { + ret void +} +define void()* @get_strong_default_function() { + ret void()* @strong_default_function +} +; CHECK: movq strong_default_function@GOTPCREL(%rip), %rax +; STATIC: movl $strong_default_function, %eax +; CHECK32: movl strong_default_function@GOT(%eax), %eax + +define weak void @weak_default_function() { + ret void +} +define void()* @get_weak_default_function() { + ret void()* @weak_default_function +} +; CHECK: movq weak_default_function@GOTPCREL(%rip), %rax +; STATIC: movl $weak_default_function, %eax +; CHECK32: movl weak_default_function@GOT(%eax), %eax + +declare void @external_default_function() +define void()* @get_external_default_function() { + ret void()* @external_default_function +} +; CHECK: movq external_default_function@GOTPCREL(%rip), %rax +; STATIC: movl $external_default_function, %eax +; CHECK32: movl external_default_function@GOT(%eax), %eax + +define dso_local void @strong_local_function() { + ret void +} +define void()* @get_strong_local_function() { + ret void()* @strong_local_function +} +; CHECK: leaq strong_local_function(%rip), %rax +; STATIC: movl $strong_local_function, %eax +; CHECK32: leal strong_local_function@GOTOFF(%eax), %eax + +define weak dso_local void @weak_local_function() { + ret void +} +define void()* @get_weak_local_function() { + ret void()* @weak_local_function +} +; CHECK: leaq weak_local_function(%rip), %rax +; STATIC: movl $weak_local_function, %eax +; CHECK32: leal weak_local_function@GOTOFF(%eax), %eax + +declare dso_local void @external_local_function() +define void()* @get_external_local_function() { + ret void()* @external_local_function +} +; CHECK: leaq external_local_function(%rip), %rax +; STATIC: movl $external_local_function, %eax +; CHECK32: leal external_local_function@GOTOFF(%eax), %eax + + +define dso_preemptable void @strong_preemptable_function() { + ret void +} +define void()* @get_strong_preemptable_function() { + ret void()* @strong_preemptable_function +} +; CHECK: movq strong_preemptable_function@GOTPCREL(%rip), %rax +; STATIC: movl $strong_preemptable_function, %eax +; CHECK32: movl strong_preemptable_function@GOT(%eax), %eax + +define weak dso_preemptable void @weak_preemptable_function() { + ret void +} +define void()* @get_weak_preemptable_function() { + ret void()* @weak_preemptable_function +} +; CHECK: movq weak_preemptable_function@GOTPCREL(%rip), %rax +; STATIC: movl $weak_preemptable_function, %eax +; CHECK32: movl weak_preemptable_function@GOT(%eax), %eax + +declare dso_preemptable void @external_preemptable_function() +define void()* @get_external_preemptable_function() { + ret void()* @external_preemptable_function +} +; CHECK: movq external_preemptable_function@GOTPCREL(%rip), %rax +; STATIC: movl $external_preemptable_function, %eax +; CHECK32: movl external_preemptable_function@GOT(%eax), %eax Index: llvm/trunk/test/CodeGen/X86/win32-preemption.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/win32-preemption.ll +++ llvm/trunk/test/CodeGen/X86/win32-preemption.ll @@ -0,0 +1,236 @@ +; RUN: llc -mtriple x86_64-pc-win32 \ +; RUN: -relocation-model=static < %s | FileCheck --check-prefix=COFF_S %s +; RUN: llc -mtriple x86_64-pc-win32 \ +; RUN: -relocation-model=pic < %s | FileCheck --check-prefix=COFF %s +; RUN: llc -mtriple x86_64-pc-win32 \ +; RUN: -relocation-model=dynamic-no-pic < %s | FileCheck --check-prefix=COFF %s + + +; 32 bits + +; RUN: llc -mtriple i386-pc-win32 \ +; RUN: -relocation-model=static < %s | FileCheck --check-prefix=COFF32 %s +; RUN: llc -mtriple i386-pc-win32 \ +; RUN: -relocation-model=pic < %s | FileCheck --check-prefix=COFF32 %s +; RUN: llc -mtriple i386-pc-win32 \ +; RUN: -relocation-model=dynamic-no-pic < %s | \ +; RUN: FileCheck --check-prefix=COFF32 %s + +; globals + +@strong_default_global = global i32 42 +define i32* @get_strong_default_global() { + ret i32* @strong_default_global +} +; COFF: leaq strong_default_global(%rip), %rax +; COFF_S: movl $strong_default_global, %eax +; COFF32: movl $_strong_default_global, %eax + +@weak_default_global = weak global i32 42 +define i32* @get_weak_default_global() { + ret i32* @weak_default_global +} +; COFF: leaq weak_default_global(%rip), %rax +; COFF_S: movl $weak_default_global, %eax +; COFF32: movl $_weak_default_global, %eax + +@external_default_global = external global i32 +define i32* @get_external_default_global() { + ret i32* @external_default_global +} +; COFF: leaq external_default_global(%rip), %rax +; COFF_S: movl $external_default_global, %eax +; COFF32: movl $_external_default_global, %eax + + +@strong_local_global = dso_local global i32 42 +define i32* @get_strong_local_global() { + ret i32* @strong_local_global +} +; COFF: leaq strong_local_global(%rip), %rax +; COFF_S: movl $strong_local_global, %eax +; COFF32: movl $_strong_local_global, %eax + +@weak_local_global = weak dso_local global i32 42 +define i32* @get_weak_local_global() { + ret i32* @weak_local_global +} +; COFF: leaq weak_local_global(%rip), %rax +; COFF_S: movl $weak_local_global, %eax +; COFF32: movl $_weak_local_global, %eax + +@external_local_global = external dso_local global i32 +define i32* @get_external_local_global() { + ret i32* @external_local_global +} +; COFF: leaq external_local_global(%rip), %rax +; COFF_S: movl $external_local_global, %eax +; COFF32: movl $_external_local_global, %eax + + +@strong_preemptable_global = dso_preemptable global i32 42 +define i32* @get_strong_preemptable_global() { + ret i32* @strong_preemptable_global +} +; COFF: leaq strong_preemptable_global(%rip), %rax +; COFF_S: movl $strong_preemptable_global, %eax +; COFF32: movl $_strong_preemptable_global, %eax + +@weak_preemptable_global = weak dso_preemptable global i32 42 +define i32* @get_weak_preemptable_global() { + ret i32* @weak_preemptable_global +} +; COFF: leaq weak_preemptable_global(%rip), %rax +; COFF_S: movl $weak_preemptable_global, %eax +; COFF32: movl $_weak_preemptable_global, %eax + +@external_preemptable_global = external dso_preemptable global i32 +define i32* @get_external_preemptable_global() { + ret i32* @external_preemptable_global +} +; COFF: leaq external_preemptable_global(%rip), %rax +; COFF_S: movl $external_preemptable_global, %eax +; COFF32: movl $_external_preemptable_global, %eax + + +; aliases +@aliasee = global i32 42 + +@strong_default_alias = alias i32, i32* @aliasee +define i32* @get_strong_default_alias() { + ret i32* @strong_default_alias +} +; COFF: leaq strong_default_alias(%rip), %rax +; COFF_S: movl $strong_default_alias, %eax +; COFF32: movl $_strong_default_alias, %eax + +@weak_default_alias = weak alias i32, i32* @aliasee +define i32* @get_weak_default_alias() { + ret i32* @weak_default_alias +} +; COFF: leaq weak_default_alias(%rip), %rax +; COFF_S: movl $weak_default_alias, %eax +; COFF32: movl $_weak_default_alias, %eax + + +@strong_local_alias = dso_local alias i32, i32* @aliasee +define i32* @get_strong_local_alias() { + ret i32* @strong_local_alias +} +; COFF: leaq strong_local_alias(%rip), %rax +; COFF_S: movl $strong_local_alias, %eax +; COFF32: movl $_strong_local_alias, %eax + +@weak_local_alias = weak dso_local alias i32, i32* @aliasee +define i32* @get_weak_local_alias() { + ret i32* @weak_local_alias +} +; COFF: leaq weak_local_alias(%rip), %rax +; COFF_S: movl $weak_local_alias, %eax +; COFF32: movl $_weak_local_alias, %eax + + +@strong_preemptable_alias = dso_preemptable alias i32, i32* @aliasee +define i32* @get_strong_preemptable_alias() { + ret i32* @strong_preemptable_alias +} +; COFF: leaq strong_preemptable_alias(%rip), %rax +; COFF_S: movl $strong_preemptable_alias, %eax +; COFF32: movl $_strong_preemptable_alias, %eax + +@weak_preemptable_alias = weak dso_preemptable alias i32, i32* @aliasee +define i32* @get_weak_preemptable_alias() { + ret i32* @weak_preemptable_alias +} +; COFF: leaq weak_preemptable_alias(%rip), %rax +; COFF_S: movl $weak_preemptable_alias, %eax +; COFF32: movl $_weak_preemptable_alias, %eax + + +; functions + +define void @strong_default_function() { + ret void +} +define void()* @get_strong_default_function() { + ret void()* @strong_default_function +} +; COFF: leaq strong_default_function(%rip), %rax +; COFF_S: movl $strong_default_function, %eax +; COFF32: movl $_strong_default_function, %eax + +define weak void @weak_default_function() { + ret void +} +define void()* @get_weak_default_function() { + ret void()* @weak_default_function +} +; COFF: leaq weak_default_function(%rip), %rax +; COFF_S: movl $weak_default_function, %eax +; COFF32: movl $_weak_default_function, %eax + +declare void @external_default_function() +define void()* @get_external_default_function() { + ret void()* @external_default_function +} +; COFF: leaq external_default_function(%rip), %rax +; COFF_S: movl $external_default_function, %eax +; COFF32: movl $_external_default_function, %eax + + +define dso_local void @strong_local_function() { + ret void +} +define void()* @get_strong_local_function() { + ret void()* @strong_local_function +} +; COFF: leaq strong_local_function(%rip), %rax +; COFF_S: movl $strong_local_function, %eax +; COFF32: movl $_strong_local_function, %eax + +define weak dso_local void @weak_local_function() { + ret void +} +define void()* @get_weak_local_function() { + ret void()* @weak_local_function +} +; COFF: leaq weak_local_function(%rip), %rax +; COFF_S: movl $weak_local_function, %eax +; COFF32: movl $_weak_local_function, %eax + +declare dso_local void @external_local_function() +define void()* @get_external_local_function() { + ret void()* @external_local_function +} +; COFF: leaq external_local_function(%rip), %rax +; COFF_S: movl $external_local_function, %eax +; COFF32: movl $_external_local_function, %eax + + +define dso_preemptable void @strong_preemptable_function() { + ret void +} +define void()* @get_strong_preemptable_function() { + ret void()* @strong_preemptable_function +} +; COFF: leaq strong_preemptable_function(%rip), %rax +; COFF_S: movl $strong_preemptable_function, %eax +; COFF32: movl $_strong_preemptable_function, %eax + +define weak dso_preemptable void @weak_preemptable_function() { + ret void +} +define void()* @get_weak_preemptable_function() { + ret void()* @weak_preemptable_function +} +; COFF: leaq weak_preemptable_function(%rip), %rax +; COFF_S: movl $weak_preemptable_function, %eax +; COFF32: movl $_weak_preemptable_function, %eax + +declare dso_preemptable void @external_preemptable_function() +define void()* @get_external_preemptable_function() { + ret void()* @external_preemptable_function +} +; COFF: leaq external_preemptable_function(%rip), %rax +; COFF_S: movl $external_preemptable_function, %eax +; COFF32: movl $_external_preemptable_function, %eax