]> granicus.if.org Git - llvm/commitdiff
Represent runtime preemption in the IR.
authorSean Fertile <sfertile@ca.ibm.com>
Thu, 26 Oct 2017 15:00:26 +0000 (15:00 +0000)
committerSean Fertile <sfertile@ca.ibm.com>
Thu, 26 Oct 2017 15:00:26 +0000 (15:00 +0000)
Currently we do not represent runtime preemption in the IR, which has several
drawbacks:

  1) The semantics of GlobalValues differ depending on the object file format
     you are targeting (as well as the relocation-model and -fPIE value).
  2) We have no way of disabling inlining of run time interposable functions,
     since in the IR we only know if a function is link-time interposable.
     Because of this llvm cannot support elf-interposition semantics.
  3) In LTO builds of executables we will have extra knowledge that a symbol
     resolved to a local definition and can't be preemptable, but have no way to
     propagate that knowledge through the compiler.

This patch adds preemptability specifiers to the IR with the following meaning:

dso_local --> means the compiler may assume the symbol will resolve to a
 definition within the current linkage unit and the symbol may be accessed
 directly even if the definition is not within this compilation unit.

dso_preemptable --> means that the compiler must assume the GlobalValue may be
replaced with a definition from outside the current linkage unit at runtime.

To ease transitioning dso_preemptable is treated as a 'default' in that
low-level codegen will still do the same checks it did previously to see if a
symbol should be accessed indirectly. Eventually when IR producers emit the
specifiers on all Globalvalues we can change dso_preemptable to mean 'always
access indirectly', and remove the current logic.

Differential Revision: https://reviews.llvm.org/D20217

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316668 91177308-0d34-0410-b5e6-96231b3b80d8

20 files changed:
docs/BitCodeFormat.rst
docs/LangRef.rst
include/llvm/IR/GlobalValue.h
lib/AsmParser/LLLexer.cpp
lib/AsmParser/LLParser.cpp
lib/AsmParser/LLParser.h
lib/AsmParser/LLToken.h
lib/Bitcode/Reader/BitcodeReader.cpp
lib/Bitcode/Writer/BitcodeWriter.cpp
lib/IR/AsmWriter.cpp
lib/IR/Globals.cpp
lib/IR/Verifier.cpp
lib/Target/TargetMachine.cpp
test/Assembler/dllimport-dsolocal-diag.ll [new file with mode: 0644]
test/Assembler/ifunc-dsolocal-daig.ll [new file with mode: 0644]
test/Bitcode/dso_location.ll [new file with mode: 0644]
test/CodeGen/PowerPC/preemption.ll [new file with mode: 0644]
test/CodeGen/X86/darwin-preemption.ll [new file with mode: 0644]
test/CodeGen/X86/linux-preemption.ll [new file with mode: 0644]
test/CodeGen/X86/win32-preemption.ll [new file with mode: 0644]

index 6ee3842c8d908f13eb9993fc46739afc75414cc2..98a3156e082518ec09304a461a2a8f8e5a647b0f 100644 (file)
@@ -681,7 +681,7 @@ for each library name referenced.
 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 @@ global variable. The operand fields are:
 
 * *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 @@ function. The operand fields are:
 * *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<bcpreemptionspecifier>`  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 @@ fields are
 * *unnamed_addr*: If present, an encoding of the
   :ref:`unnamed_addr<bcunnamedaddr>` attribute of this alias
 
+* *preemptionspecifier*: If present, an encoding of the :ref:`runtime preemption specifier<bcpreemptionspecifier>`  of this alias.
+
 .. _MODULE_CODE_GCNAME:
 
 MODULE_CODE_GCNAME Record
index 99a2ffa40d60a00292fc46123fc063d35e71d3b5..9d910568bd5d8a8f147a6e35f248e6926ab9b125 100644 (file)
@@ -527,6 +527,24 @@ the alias is accessed. It will not have any effect in the aliasee.
 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 @@ iterate over them as an array, alignment padding would break this
 iteration. The maximum alignment is ``1 << 29``.
 
 Globals can also have a :ref:`DLL storage class <dllstorageclass>`,
+an optional :ref:`runtime preemption specifier <runtime_preemption_model>`,
 an optional :ref:`global attributes <glattrs>` and
 an optional list of attached :ref:`metadata <metadata>`.
 
@@ -658,7 +677,8 @@ Variables and aliases can have a
 
 Syntax::
 
-      @<GlobalVarName> = [Linkage] [Visibility] [DLLStorageClass] [ThreadLocal]
+      @<GlobalVarName> = [Linkage] [PreemptionSpecifier] [Visibility]
+                         [DLLStorageClass] [ThreadLocal]
                          [(unnamed_addr|local_unnamed_addr)] [AddrSpace]
                          [ExternallyInitialized]
                          <global | constant> <Type> [<InitializerConstant>]
@@ -691,7 +711,8 @@ Functions
 ---------
 
 LLVM function definitions consist of the "``define``" keyword, an
-optional :ref:`linkage type <linkage>`, an optional :ref:`visibility
+optional :ref:`linkage type <linkage>`, an optional :ref:`runtime preemption
+specifier <runtime_preemption_model>`,  an optional :ref:`visibility
 style <visibility>`, an optional :ref:`DLL storage class <dllstorageclass>`,
 an optional :ref:`calling convention <callingconv>`,
 an optional ``unnamed_addr`` attribute, a return type, an optional
@@ -750,7 +771,7 @@ not be significant within the module.
 
 Syntax::
 
-    define [linkage] [visibility] [DLLStorageClass]
+    define [linkage] [PreemptionSpecifier] [visibility] [DLLStorageClass]
            [cconv] [ret attrs]
            <ResultType> @<FunctionName> ([argument list])
            [(unnamed_addr|local_unnamed_addr)] [fn Attrs] [section "name"]
@@ -777,12 +798,13 @@ Aliases have a name and an aliasee that is either a global value or a
 constant expression.
 
 Aliases may have an optional :ref:`linkage type <linkage>`, an optional
+:ref:`runtime preemption specifier <runtime_preemption_model>`, an optional
 :ref:`visibility style <visibility>`, an optional :ref:`DLL storage class
 <dllstorageclass>` and an optional :ref:`tls model <tls_model>`.
 
 Syntax::
 
-    @<Name> = [Linkage] [Visibility] [DLLStorageClass] [ThreadLocal] [(unnamed_addr|local_unnamed_addr)] alias <AliaseeTy>, <AliaseeTy>* @<Aliasee>
+    @<Name> = [Linkage] [PreemptionSpecifier] [Visibility] [DLLStorageClass] [ThreadLocal] [(unnamed_addr|local_unnamed_addr)] alias <AliaseeTy>, <AliaseeTy>* @<Aliasee>
 
 The linkage must be one of ``private``, ``internal``, ``linkonce``, ``weak``,
 ``linkonce_odr``, ``weak_odr``, ``external``. Note that some system linkers
index d65d43cc5957dda76a73627821b413d63fffd445..1793de7887fc74d58e617ba85d0c6dd4342342f7 100644 (file)
@@ -80,13 +80,14 @@ protected:
         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 @@ protected:
   /// 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 @@ public:
 
   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 52c02cc162ecb34bb779cd3efabf185c8ab78ec1..50b391fdf73f2ca1616e370dc6af4138bdb44764 100644 (file)
@@ -494,6 +494,9 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(declare); KEYWORD(define);
   KEYWORD(global);  KEYWORD(constant);
 
+  KEYWORD(dso_local);
+  KEYWORD(dso_preemptable);
+
   KEYWORD(private);
   KEYWORD(internal);
   KEYWORD(available_externally);
index 565b1a27daf1f702bbb9c63d17bc3fa69dd804cb..5b661ceb18004175d43a75f0bf989af3912c4192 100644 (file)
@@ -483,10 +483,12 @@ bool LLParser::ParseOptionalUnnamedAddr(
 
 /// 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 LLParser::ParseUnnamedGlobal() {
 
   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 LLParser::ParseNamedGlobal() {
 
   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 @@ static bool isValidVisibilityForLinkage(unsigned V, unsigned L) {
 }
 
 /// 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 @@ bool LLParser::parseIndirectSymbol(
     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 @@ bool LLParser::parseIndirectSymbol(
   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 @@ bool LLParser::parseIndirectSymbol(
 }
 
 /// 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::parseIndirectSymbol(
 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 @@ bool LLParser::ParseGlobal(const std::string &Name, LocTy NameLoc,
     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 @@ static unsigned parseOptionalLinkageAux(lltok::Kind Kind, bool &HasLinkage) {
 ///   ::= '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 @@ bool LLParser::ParseTypeAndBasicBlock(BasicBlock *&BB, LocTy &Loc,
 }
 
 /// 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 @@ bool LLParser::ParseFunctionHeader(Function *&Fn, bool isDefine) {
     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 5dadf521538cd938a470db8883d8c54b5b93d2eb..dcc3f22e03b9197eac30df72aa695861bc16b653 100644 (file)
@@ -240,7 +240,9 @@ namespace llvm {
     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 @@ namespace llvm {
     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 a729ce4c7012dd1d9c4a76fc11a9f55e6b4d1f89..db0de6c0d5a98378174ec0749c1a3277070405c9 100644 (file)
@@ -45,6 +45,9 @@ enum Kind {
   kw_global,
   kw_constant,
 
+  kw_dso_local,
+  kw_dso_preemptable,
+
   kw_private,
   kw_internal,
   kw_linkonce,
index 68b36eef5ebe675283b78afcae49ca894f15906a..c2272260f44c5b5cf40a5238b701f531b8f24089 100644 (file)
@@ -911,6 +911,14 @@ getDecodedDLLStorageClass(unsigned Val) {
   }
 }
 
+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::parseComdatRecord(ArrayRef<uint64_t> Record) {
 Error BitcodeReader::parseGlobalVarRecord(ArrayRef<uint64_t> 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 @@ Error BitcodeReader::parseGlobalVarRecord(ArrayRef<uint64_t> Record) {
     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<uint64_t> 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 @@ Error BitcodeReader::parseFunctionRecord(ArrayRef<uint64_t> Record) {
   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 @@ Error BitcodeReader::parseGlobalIndirectSymbolRecord(
     unsigned BitCode, ArrayRef<uint64_t> 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 @@ Error BitcodeReader::parseGlobalIndirectSymbolRecord(
     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 de154ba6be7d863efbb0a69df8d6a64c3deb923d..1e491aa066ee5bbe2df02468ffc2212062937b04 100644 (file)
@@ -1197,7 +1197,7 @@ void ModuleBitcodeWriter::writeModuleInfo() {
     // 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 @@ void ModuleBitcodeWriter::writeModuleInfo() {
         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 @@ void ModuleBitcodeWriter::writeModuleInfo() {
 
       auto AL = GV.getAttributesAsList(AttributeList::FunctionIndex);
       Vals.push_back(VE.getAttributeListID(AL));
+
+      Vals.push_back(GV.isDSOLocal());
     } else {
       AbbrevToUse = SimpleGVarAbbrev;
     }
@@ -1236,7 +1239,7 @@ void ModuleBitcodeWriter::writeModuleInfo() {
     // 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 @@ void ModuleBitcodeWriter::writeModuleInfo() {
     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 @@ void ModuleBitcodeWriter::writeModuleInfo() {
   // 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 @@ void ModuleBitcodeWriter::writeModuleInfo() {
     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 f6ed6a2116b8307ad269c78c9df254ec3afafaec..3f41a1dc0663042a110e86da4b3a83a6cf1f5a07 100644 (file)
@@ -2493,6 +2493,11 @@ static void PrintVisibility(GlobalValue::VisibilityTypes Vis,
   }
 }
 
+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 @@ void AssemblyWriter::printGlobal(const GlobalVariable *GV) {
     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 @@ void AssemblyWriter::printIndirectSymbol(const GlobalIndirectSymbol *GIS) {
   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 @@ void AssemblyWriter::printFunction(const Function *F) {
     Out << "define ";
 
   Out << getLinkagePrintName(F->getLinkage());
+  PrintDSOLocation(F->isDSOLocal(), Out);
   PrintVisibility(F->getVisibility(), Out);
   PrintDLLStorageClass(F->getDLLStorageClass(), Out);
 
index afd4a36270a87d3d616b3352adb22a33764c223b..da1b6c5e0c916459c1078af2581d2f19bab82b70 100644 (file)
@@ -67,6 +67,7 @@ void GlobalValue::copyAttributesFrom(const GlobalValue *Src) {
   setVisibility(Src->getVisibility());
   setUnnamedAddr(Src->getUnnamedAddr());
   setDLLStorageClass(Src->getDLLStorageClass());
+  setDSOLocal(Src->isDSOLocal());
 }
 
 void GlobalValue::removeFromParent() {
index 377f26f2565ae5db9b273b7a9c92744684beb050..9dbd05543bf47721889f65fb76762fe3e541836f 100644 (file)
@@ -568,6 +568,10 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
   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<Instruction>(V)) {
       if (!I->getParent() || !I->getParent()->getParent())
index 3b16dee5a0fa4228d5307383c664529dd65e7e08..dd6c288607cf67dd16d3681bb8b100f38eeec46a 100644 (file)
@@ -128,7 +128,8 @@ bool TargetMachine::shouldAssumeDSOLocal(const Module &M,
   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()) {
diff --git a/test/Assembler/dllimport-dsolocal-diag.ll b/test/Assembler/dllimport-dsolocal-diag.ll
new file mode 100644 (file)
index 0000000..e68e800
--- /dev/null
@@ -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
diff --git a/test/Assembler/ifunc-dsolocal-daig.ll b/test/Assembler/ifunc-dsolocal-daig.ll
new file mode 100644 (file)
index 0000000..86e941d
--- /dev/null
@@ -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
+}
diff --git a/test/Bitcode/dso_location.ll b/test/Bitcode/dso_location.ll
new file mode 100644 (file)
index 0000000..4dc9fe2
--- /dev/null
@@ -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
+}
diff --git a/test/CodeGen/PowerPC/preemption.ll b/test/CodeGen/PowerPC/preemption.ll
new file mode 100644 (file)
index 0000000..5652f6d
--- /dev/null
@@ -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
+}
+
diff --git a/test/CodeGen/X86/darwin-preemption.ll b/test/CodeGen/X86/darwin-preemption.ll
new file mode 100644 (file)
index 0000000..9df0389
--- /dev/null
@@ -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
diff --git a/test/CodeGen/X86/linux-preemption.ll b/test/CodeGen/X86/linux-preemption.ll
new file mode 100644 (file)
index 0000000..ab1ac2f
--- /dev/null
@@ -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
diff --git a/test/CodeGen/X86/win32-preemption.ll b/test/CodeGen/X86/win32-preemption.ll
new file mode 100644 (file)
index 0000000..accabac
--- /dev/null
@@ -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