From 941b1f1426afc67e96430b5cde03b93744efc5d8 Mon Sep 17 00:00:00 2001 From: Bjorn Pettersson Date: Thu, 26 Oct 2017 13:42:55 +0000 Subject: [PATCH 0001/1206] [LSV] Skip all non-byte sizes, not only less than eight bits Summary: The code comments indicate that no effort has been spent on handling load/stores when the size isn't a multiple of the byte size correctly. However, the code only avoided types smaller than 8 bits. So for example a load of an i28 could still be considered as a candidate for vectorization. This patch adjusts the code to behave according to the code comment. The test case used to hit the following assert when trying to use "cast" an i32 to i28 using CreateBitOrPointerCast: opt: ../lib/IR/Instructions.cpp:2565: Assertion `castIsValid(op, S, Ty) && "Invalid cast!"' failed. #0 PrintStackTraceSignalHandler(void*) #1 SignalHandler(int) #2 __restore_rt #3 __GI_raise #4 __GI_abort #5 __GI___assert_fail #6 llvm::CastInst::Create(llvm::Instruction::CastOps, llvm::Value*, llvm::Type*, llvm::Twine const&, llvm::Instruction*) #7 llvm::IRBuilder::CreateBitOrPointerCast(llvm::Value*, llvm::Type*, llvm::Twine const&) #8 (anonymous namespace)::Vectorizer::vectorizeLoadChain(llvm::ArrayRef, llvm::SmallPtrSet*) Reviewers: arsenm Reviewed By: arsenm Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D39295 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316663 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Vectorize/LoadStoreVectorizer.cpp | 6 ++-- .../LoadStoreVectorizer/X86/non-byte-size.ll | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 test/Transforms/LoadStoreVectorizer/X86/non-byte-size.ll diff --git a/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp b/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp index 2ec4f6ca9e7f0..8394f5ec73fcd 100644 --- a/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp +++ b/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp @@ -613,7 +613,7 @@ Vectorizer::collectInstructions(BasicBlock *BB) { // Skip weird non-byte sizes. They probably aren't worth the effort of // handling correctly. unsigned TySize = DL.getTypeSizeInBits(Ty); - if (TySize < 8) + if ((TySize % 8) != 0) continue; Value *Ptr = LI->getPointerOperand(); @@ -649,12 +649,14 @@ Vectorizer::collectInstructions(BasicBlock *BB) { // Skip weird non-byte sizes. They probably aren't worth the effort of // handling correctly. unsigned TySize = DL.getTypeSizeInBits(Ty); - if (TySize < 8) + if ((TySize % 8) != 0) continue; Value *Ptr = SI->getPointerOperand(); unsigned AS = Ptr->getType()->getPointerAddressSpace(); unsigned VecRegSize = TTI.getLoadStoreVecRegBitWidth(AS); + + // No point in looking at these if they're too big to vectorize. if (TySize > VecRegSize / 2) continue; diff --git a/test/Transforms/LoadStoreVectorizer/X86/non-byte-size.ll b/test/Transforms/LoadStoreVectorizer/X86/non-byte-size.ll new file mode 100644 index 0000000000000..1f00f980eac19 --- /dev/null +++ b/test/Transforms/LoadStoreVectorizer/X86/non-byte-size.ll @@ -0,0 +1,30 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -load-store-vectorizer -S -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +%rec = type { i32, i28 } + +; We currently do not optimize this scenario. +; But we verify that we no longer crash when compiling this. +define void @test1(%rec* %out, %rec* %in) { +; CHECK-LABEL: @test1( +; CHECK-NEXT: [[IN1:%.*]] = getelementptr [[REC:%.*]], %rec* [[IN:%.*]], i16 0, i32 0 +; CHECK-NEXT: [[IN2:%.*]] = getelementptr [[REC]], %rec* [[IN]], i16 0, i32 1 +; CHECK-NEXT: [[VAL1:%.*]] = load i32, i32* [[IN1]], align 8 +; CHECK-NEXT: [[VAL2:%.*]] = load i28, i28* [[IN2]] +; CHECK-NEXT: [[OUT1:%.*]] = getelementptr [[REC]], %rec* [[OUT:%.*]], i16 0, i32 0 +; CHECK-NEXT: [[OUT2:%.*]] = getelementptr [[REC]], %rec* [[OUT]], i16 0, i32 1 +; CHECK-NEXT: store i32 [[VAL1]], i32* [[OUT1]], align 8 +; CHECK-NEXT: store i28 [[VAL2]], i28* [[OUT2]] +; CHECK-NEXT: ret void +; + %in1 = getelementptr %rec, %rec* %in, i16 0, i32 0 + %in2 = getelementptr %rec, %rec* %in, i16 0, i32 1 + %val1 = load i32, i32* %in1, align 8 + %val2 = load i28, i28* %in2 + %out1 = getelementptr %rec, %rec* %out, i16 0, i32 0 + %out2 = getelementptr %rec, %rec* %out, i16 0, i32 1 + store i32 %val1, i32* %out1, align 8 + store i28 %val2, i28* %out2 + ret void +} + From 4ff96e5fbb1d79afb90f25698c4bf1696e2d63b9 Mon Sep 17 00:00:00 2001 From: Bjorn Pettersson Date: Thu, 26 Oct 2017 13:59:15 +0000 Subject: [PATCH 0002/1206] [LSV] Avoid adding vectors of pointers as candidates Summary: We no longer add vectors of pointers as candidates for load/store vectorization. It does not seem to work anyway, but without this patch we can end up in asserts when trying to create casts between an integer type and the pointer of vectors type. The test case I've added used to assert like this when trying to cast between i64 and <2 x i16*>: opt: ../lib/IR/Instructions.cpp:2565: Assertion `castIsValid(op, S, Ty) && "Invalid cast!"' failed. #0 PrintStackTraceSignalHandler(void*) #1 SignalHandler(int) #2 __restore_rt #3 __GI_raise #4 __GI_abort #5 __GI___assert_fail #6 llvm::CastInst::Create(llvm::Instruction::CastOps, llvm::Value*, llvm::Type*, llvm::Twine const&, llvm::Instruction*) #7 llvm::IRBuilder::CreateBitOrPointerCast(llvm::Value*, llvm::Type*, llvm::Twine const&) #8 Vectorizer::vectorizeStoreChain(llvm::ArrayRef, llvm::SmallPtrSet*) Reviewers: arsenm Reviewed By: arsenm Subscribers: nhaehnle, llvm-commits Differential Revision: https://reviews.llvm.org/D39296 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316665 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Vectorize/LoadStoreVectorizer.cpp | 20 ++++++++++++++++--- .../AMDGPU/merge-stores.ll | 20 +++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp b/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp index 8394f5ec73fcd..5d355c520210c 100644 --- a/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp +++ b/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp @@ -616,6 +616,13 @@ Vectorizer::collectInstructions(BasicBlock *BB) { if ((TySize % 8) != 0) continue; + // Skip vectors of pointers. The vectorizeLoadChain/vectorizeStoreChain + // functions are currently using an integer type for the vectorized + // load/store, and does not support casting between the integer type and a + // vector of pointers (e.g. i64 to <2 x i16*>) + if (Ty->isVectorTy() && Ty->isPtrOrPtrVectorTy()) + continue; + Value *Ptr = LI->getPointerOperand(); unsigned AS = Ptr->getType()->getPointerAddressSpace(); unsigned VecRegSize = TTI.getLoadStoreVecRegBitWidth(AS); @@ -646,6 +653,13 @@ Vectorizer::collectInstructions(BasicBlock *BB) { if (!VectorType::isValidElementType(Ty->getScalarType())) continue; + // Skip vectors of pointers. The vectorizeLoadChain/vectorizeStoreChain + // functions are currently using an integer type for the vectorized + // load/store, and does not support casting between the integer type and a + // vector of pointers (e.g. i64 to <2 x i16*>) + if (Ty->isVectorTy() && Ty->isPtrOrPtrVectorTy()) + continue; + // Skip weird non-byte sizes. They probably aren't worth the effort of // handling correctly. unsigned TySize = DL.getTypeSizeInBits(Ty); @@ -701,8 +715,8 @@ bool Vectorizer::vectorizeInstructions(ArrayRef Instrs) { SmallVector Heads, Tails; int ConsecutiveChain[64]; - // Do a quadratic search on all of the given stores and find all of the pairs - // of stores that follow each other. + // Do a quadratic search on all of the given loads/stores and find all of the + // pairs of loads/stores that follow each other. for (int i = 0, e = Instrs.size(); i < e; ++i) { ConsecutiveChain[i] = -1; for (int j = e - 1; j >= 0; --j) { @@ -769,7 +783,7 @@ bool Vectorizer::vectorizeStoreChain( SmallPtrSet *InstructionsProcessed) { StoreInst *S0 = cast(Chain[0]); - // If the vector has an int element, default to int for the whole load. + // If the vector has an int element, default to int for the whole store. Type *StoreTy; for (Instruction *I : Chain) { StoreTy = cast(I)->getValueOperand()->getType(); diff --git a/test/Transforms/LoadStoreVectorizer/AMDGPU/merge-stores.ll b/test/Transforms/LoadStoreVectorizer/AMDGPU/merge-stores.ll index dbb7068eeae0d..19fc44bb6c85f 100644 --- a/test/Transforms/LoadStoreVectorizer/AMDGPU/merge-stores.ll +++ b/test/Transforms/LoadStoreVectorizer/AMDGPU/merge-stores.ll @@ -632,6 +632,26 @@ define amdgpu_kernel void @copy_v3f64_align4(<3 x double> addrspace(1)* noalias ret void } +; Verify that we no longer hit asserts for this test case. No change expected. +; CHECK-LABEL: @copy_vec_of_ptrs +; CHECK: %in.gep.1 = getelementptr <2 x i16*>, <2 x i16*> addrspace(1)* %in, i32 1 +; CHECK: %vec1 = load <2 x i16*>, <2 x i16*> addrspace(1)* %in.gep.1 +; CHECK: %vec2 = load <2 x i16*>, <2 x i16*> addrspace(1)* %in, align 4 +; CHECK: %out.gep.1 = getelementptr <2 x i16*>, <2 x i16*> addrspace(1)* %out, i32 1 +; CHECK: store <2 x i16*> %vec1, <2 x i16*> addrspace(1)* %out.gep.1 +; CHECK: store <2 x i16*> %vec2, <2 x i16*> addrspace(1)* %out, align 4 +define amdgpu_kernel void @copy_vec_of_ptrs(<2 x i16*> addrspace(1)* %out, + <2 x i16*> addrspace(1)* %in ) #0 { + %in.gep.1 = getelementptr <2 x i16*>, <2 x i16*> addrspace(1)* %in, i32 1 + %vec1 = load <2 x i16*>, <2 x i16*> addrspace(1)* %in.gep.1 + %vec2 = load <2 x i16*>, <2 x i16*> addrspace(1)* %in, align 4 + + %out.gep.1 = getelementptr <2 x i16*>, <2 x i16*> addrspace(1)* %out, i32 1 + store <2 x i16*> %vec1, <2 x i16*> addrspace(1)* %out.gep.1 + store <2 x i16*> %vec2, <2 x i16*> addrspace(1)* %out, align 4 + ret void +} + declare void @llvm.amdgcn.s.barrier() #1 attributes #0 = { nounwind } From b25352f371cca1ddf19a7b0c1db681148da8ac66 Mon Sep 17 00:00:00 2001 From: Marek Olsak Date: Thu, 26 Oct 2017 14:43:02 +0000 Subject: [PATCH 0003/1206] AMDGPU: Handle s_buffer_load_dword hazard on SI Reviewers: arsenm, nhaehnle Subscribers: kzhuravl, wdng, yaxunl, dstuttard, tpr, llvm-commits, t-tye Differential Revision: https://reviews.llvm.org/D39171 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316666 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/AMDGPU/GCNHazardRecognizer.cpp | 27 +++++++++++++++++++++++ test/CodeGen/AMDGPU/smrd.ll | 17 ++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/lib/Target/AMDGPU/GCNHazardRecognizer.cpp b/lib/Target/AMDGPU/GCNHazardRecognizer.cpp index 76c3ed76dd086..3f54f8b81432b 100644 --- a/lib/Target/AMDGPU/GCNHazardRecognizer.cpp +++ b/lib/Target/AMDGPU/GCNHazardRecognizer.cpp @@ -335,6 +335,18 @@ int GCNHazardRecognizer::checkSMRDHazards(MachineInstr *SMRD) { // SGPR was written by a VALU instruction. int SmrdSgprWaitStates = 4; auto IsHazardDefFn = [this] (MachineInstr *MI) { return TII.isVALU(*MI); }; + auto IsBufferHazardDefFn = [this] (MachineInstr *MI) { return TII.isSALU(*MI); }; + + bool IsBufferSMRD = SMRD->getOpcode() == AMDGPU::S_BUFFER_LOAD_DWORD_IMM || + SMRD->getOpcode() == AMDGPU::S_BUFFER_LOAD_DWORDX2_IMM || + SMRD->getOpcode() == AMDGPU::S_BUFFER_LOAD_DWORDX4_IMM || + SMRD->getOpcode() == AMDGPU::S_BUFFER_LOAD_DWORDX8_IMM || + SMRD->getOpcode() == AMDGPU::S_BUFFER_LOAD_DWORDX16_IMM || + SMRD->getOpcode() == AMDGPU::S_BUFFER_LOAD_DWORD_SGPR || + SMRD->getOpcode() == AMDGPU::S_BUFFER_LOAD_DWORDX2_SGPR || + SMRD->getOpcode() == AMDGPU::S_BUFFER_LOAD_DWORDX4_SGPR || + SMRD->getOpcode() == AMDGPU::S_BUFFER_LOAD_DWORDX8_SGPR || + SMRD->getOpcode() == AMDGPU::S_BUFFER_LOAD_DWORDX16_SGPR; for (const MachineOperand &Use : SMRD->uses()) { if (!Use.isReg()) @@ -342,7 +354,22 @@ int GCNHazardRecognizer::checkSMRDHazards(MachineInstr *SMRD) { int WaitStatesNeededForUse = SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn); WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); + + // This fixes what appears to be undocumented hardware behavior in SI where + // s_mov writing a descriptor and s_buffer_load_dword reading the descriptor + // needs some number of nops in between. We don't know how many we need, but + // let's use 4. This wasn't discovered before probably because the only + // case when this happens is when we expand a 64-bit pointer into a full + // descriptor and use s_buffer_load_dword instead of s_load_dword, which was + // probably never encountered in the closed-source land. + if (IsBufferSMRD) { + int WaitStatesNeededForUse = + SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), + IsBufferHazardDefFn); + WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); + } } + return WaitStatesNeeded; } diff --git a/test/CodeGen/AMDGPU/smrd.ll b/test/CodeGen/AMDGPU/smrd.ll index 3f1e1cacb879d..a19768d72bdae 100644 --- a/test/CodeGen/AMDGPU/smrd.ll +++ b/test/CodeGen/AMDGPU/smrd.ll @@ -84,6 +84,23 @@ entry: ret void } +; GCN-LABEL: {{^}}smrd_hazard: +; GCN-DAG: s_mov_b32 s3, 3 +; GCN-DAG: s_mov_b32 s2, 2 +; GCN-DAG: s_mov_b32 s1, 1 +; GCN-DAG: s_mov_b32 s0, 0 +; SI-NEXT: nop 3 +; GCN-NEXT: s_buffer_load_dword s0, s[0:3], 0x0 +define amdgpu_ps float @smrd_hazard(<4 x i32> inreg %desc) #0 { +main_body: + %d0 = insertelement <4 x i32> undef, i32 0, i32 0 + %d1 = insertelement <4 x i32> %d0, i32 1, i32 1 + %d2 = insertelement <4 x i32> %d1, i32 2, i32 2 + %d3 = insertelement <4 x i32> %d2, i32 3, i32 3 + %r = call float @llvm.SI.load.const.v4i32(<4 x i32> %d3, i32 0) + ret float %r +} + ; SMRD load using the load.const.v4i32 intrinsic with an immediate offset ; GCN-LABEL: {{^}}smrd_load_const0: ; SICI: s_buffer_load_dword s{{[0-9]}}, s[{{[0-9]:[0-9]}}], 0x4 ; encoding: [0x04 From 509132b368efed10bbdad825403f45e9cf1d6e38 Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Thu, 26 Oct 2017 15:00:26 +0000 Subject: [PATCH 0004/1206] Represent runtime preemption in the IR. 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 --- docs/BitCodeFormat.rst | 19 +- docs/LangRef.rst | 30 ++- include/llvm/IR/GlobalValue.h | 17 +- lib/AsmParser/LLLexer.cpp | 3 + lib/AsmParser/LLParser.cpp | 98 +++++-- lib/AsmParser/LLParser.h | 8 +- lib/AsmParser/LLToken.h | 3 + lib/Bitcode/Reader/BitcodeReader.cpp | 29 ++- lib/Bitcode/Writer/BitcodeWriter.cpp | 15 +- lib/IR/AsmWriter.cpp | 8 + lib/IR/Globals.cpp | 1 + lib/IR/Verifier.cpp | 4 + lib/Target/TargetMachine.cpp | 3 +- test/Assembler/dllimport-dsolocal-diag.ll | 4 + test/Assembler/ifunc-dsolocal-daig.ll | 9 + test/Bitcode/dso_location.ll | 47 ++++ test/CodeGen/PowerPC/preemption.ll | 301 ++++++++++++++++++++++ test/CodeGen/X86/darwin-preemption.ll | 251 ++++++++++++++++++ test/CodeGen/X86/linux-preemption.ll | 225 ++++++++++++++++ test/CodeGen/X86/win32-preemption.ll | 236 +++++++++++++++++ 20 files changed, 1262 insertions(+), 49 deletions(-) create mode 100644 test/Assembler/dllimport-dsolocal-diag.ll create mode 100644 test/Assembler/ifunc-dsolocal-daig.ll create mode 100644 test/Bitcode/dso_location.ll create mode 100644 test/CodeGen/PowerPC/preemption.ll create mode 100644 test/CodeGen/X86/darwin-preemption.ll create mode 100644 test/CodeGen/X86/linux-preemption.ll create mode 100644 test/CodeGen/X86/win32-preemption.ll diff --git a/docs/BitCodeFormat.rst b/docs/BitCodeFormat.rst index 6ee3842c8d908..98a3156e08251 100644 --- a/docs/BitCodeFormat.rst +++ b/docs/BitCodeFormat.rst @@ -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` 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` 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 diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 99a2ffa40d60a..9d910568bd5d8 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -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 `, +an optional :ref:`runtime preemption specifier `, an optional :ref:`global attributes ` and an optional list of attached :ref:`metadata `. @@ -658,7 +677,8 @@ Variables and aliases can have a Syntax:: - @ = [Linkage] [Visibility] [DLLStorageClass] [ThreadLocal] + @ = [Linkage] [PreemptionSpecifier] [Visibility] + [DLLStorageClass] [ThreadLocal] [(unnamed_addr|local_unnamed_addr)] [AddrSpace] [ExternallyInitialized] [] @@ -691,7 +711,8 @@ Functions --------- 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 @@ not be significant within the module. 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 @@ 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 `, 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 diff --git a/include/llvm/IR/GlobalValue.h b/include/llvm/IR/GlobalValue.h index d65d43cc5957d..1793de7887fc7 100644 --- a/include/llvm/IR/GlobalValue.h +++ b/include/llvm/IR/GlobalValue.h @@ -80,13 +80,14 @@ class GlobalValue : public Constant { 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 @@ class GlobalValue : public Constant { /// 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 @@ class GlobalValue : public Constant { 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; } diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp index 52c02cc162ecb..50b391fdf73f2 100644 --- a/lib/AsmParser/LLLexer.cpp +++ b/lib/AsmParser/LLLexer.cpp @@ -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); diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp index 565b1a27daf1f..5b661ceb18004 100644 --- a/lib/AsmParser/LLParser.cpp +++ b/lib/AsmParser/LLParser.cpp @@ -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); diff --git a/lib/AsmParser/LLParser.h b/lib/AsmParser/LLParser.h index 5dadf521538cd..dcc3f22e03b91 100644 --- a/lib/AsmParser/LLParser.h +++ b/lib/AsmParser/LLParser.h @@ -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(); diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h index a729ce4c7012d..db0de6c0d5a98 100644 --- a/lib/AsmParser/LLToken.h +++ b/lib/AsmParser/LLToken.h @@ -45,6 +45,9 @@ enum Kind { kw_global, kw_constant, + kw_dso_local, + kw_dso_preemptable, + kw_private, kw_internal, kw_linkonce, diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index 68b36eef5ebe6..c2272260f44c5 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -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 Record) { 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 @@ Error BitcodeReader::parseGlobalVarRecord(ArrayRef 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 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 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 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(); diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp index de154ba6be7d8..1e491aa066ee5 100644 --- a/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -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(); diff --git a/lib/IR/AsmWriter.cpp b/lib/IR/AsmWriter.cpp index f6ed6a2116b83..3f41a1dc06630 100644 --- a/lib/IR/AsmWriter.cpp +++ b/lib/IR/AsmWriter.cpp @@ -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); diff --git a/lib/IR/Globals.cpp b/lib/IR/Globals.cpp index afd4a36270a87..da1b6c5e0c916 100644 --- a/lib/IR/Globals.cpp +++ b/lib/IR/Globals.cpp @@ -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() { diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index 377f26f2565ae..9dbd05543bf47 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -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(V)) { if (!I->getParent() || !I->getParent()->getParent()) diff --git a/lib/Target/TargetMachine.cpp b/lib/Target/TargetMachine.cpp index 3b16dee5a0fa4..dd6c288607cf6 100644 --- a/lib/Target/TargetMachine.cpp +++ b/lib/Target/TargetMachine.cpp @@ -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 index 0000000000000..e68e800fdcaea --- /dev/null +++ b/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 diff --git a/test/Assembler/ifunc-dsolocal-daig.ll b/test/Assembler/ifunc-dsolocal-daig.ll new file mode 100644 index 0000000000000..86e941d6cac14 --- /dev/null +++ b/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 +} diff --git a/test/Bitcode/dso_location.ll b/test/Bitcode/dso_location.ll new file mode 100644 index 0000000000000..4dc9fe24c198c --- /dev/null +++ b/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 +} diff --git a/test/CodeGen/PowerPC/preemption.ll b/test/CodeGen/PowerPC/preemption.ll new file mode 100644 index 0000000000000..5652f6d730a14 --- /dev/null +++ b/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 +} + diff --git a/test/CodeGen/X86/darwin-preemption.ll b/test/CodeGen/X86/darwin-preemption.ll new file mode 100644 index 0000000000000..9df0389d0d262 --- /dev/null +++ b/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 diff --git a/test/CodeGen/X86/linux-preemption.ll b/test/CodeGen/X86/linux-preemption.ll new file mode 100644 index 0000000000000..ab1ac2f27bc81 --- /dev/null +++ b/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 diff --git a/test/CodeGen/X86/win32-preemption.ll b/test/CodeGen/X86/win32-preemption.ll new file mode 100644 index 0000000000000..accabac102a13 --- /dev/null +++ b/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 From e7df36ebba1063cee0ea8ec06d742bd0f8af7384 Mon Sep 17 00:00:00 2001 From: Balaram Makam Date: Thu, 26 Oct 2017 15:04:53 +0000 Subject: [PATCH 0005/1206] Reapply r316582 [Local] Fix a bug in the domtree update logic for MergeBasicBlockIntoOnlyPred. Summary: This reverts r316612 to reapply r316582. The buildbot failure was unrelated to this commit. Reviewers: Subscribers: git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316669 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Transforms/Utils/Local.cpp | 10 +++++-- unittests/Transforms/Utils/Local.cpp | 45 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/lib/Transforms/Utils/Local.cpp b/lib/Transforms/Utils/Local.cpp index fd3367710f347..8c643c93ec4dc 100644 --- a/lib/Transforms/Utils/Local.cpp +++ b/lib/Transforms/Utils/Local.cpp @@ -649,9 +649,13 @@ void llvm::MergeBasicBlockIntoOnlyPred(BasicBlock *DestBB, DominatorTree *DT) { DestBB->moveAfter(PredBB); if (DT) { - BasicBlock *PredBBIDom = DT->getNode(PredBB)->getIDom()->getBlock(); - DT->changeImmediateDominator(DestBB, PredBBIDom); - DT->eraseNode(PredBB); + // For some irreducible CFG we end up having forward-unreachable blocks + // so check if getNode returns a valid node before updating the domtree. + if (DomTreeNode *DTN = DT->getNode(PredBB)) { + BasicBlock *PredBBIDom = DTN->getIDom()->getBlock(); + DT->changeImmediateDominator(DestBB, PredBBIDom); + DT->eraseNode(PredBB); + } } // Nuke BB. PredBB->eraseFromParent(); diff --git a/unittests/Transforms/Utils/Local.cpp b/unittests/Transforms/Utils/Local.cpp index 1f58dc802fa58..ee864e68fc084 100644 --- a/unittests/Transforms/Utils/Local.cpp +++ b/unittests/Transforms/Utils/Local.cpp @@ -166,3 +166,48 @@ TEST(Local, ReplaceDbgDeclare) { Declares++; EXPECT_EQ(2, Declares); } + +/// Build the dominator tree for the function and run the Test. +static void runWithDomTree( + Module &M, StringRef FuncName, + function_ref Test) { + auto *F = M.getFunction(FuncName); + ASSERT_NE(F, nullptr) << "Could not find " << FuncName; + // Compute the dominator tree for the function. + DominatorTree DT(*F); + Test(*F, &DT); +} + +TEST(Local, MergeBasicBlockIntoOnlyPred) { + LLVMContext C; + + std::unique_ptr M = parseIR( + C, + "define i32 @f(i8* %str) {\n" + "entry:\n" + " br label %bb2.i\n" + "bb2.i: ; preds = %bb4.i, %entry\n" + " br i1 false, label %bb4.i, label %base2flt.exit204\n" + "bb4.i: ; preds = %bb2.i\n" + " br i1 false, label %base2flt.exit204, label %bb2.i\n" + "bb10.i196.bb7.i197_crit_edge: ; No predecessors!\n" + " br label %bb7.i197\n" + "bb7.i197: ; preds = %bb10.i196.bb7.i197_crit_edge\n" + " %.reg2mem.0 = phi i32 [ %.reg2mem.0, %bb10.i196.bb7.i197_crit_edge ]\n" + " br i1 undef, label %base2flt.exit204, label %base2flt.exit204\n" + "base2flt.exit204: ; preds = %bb7.i197, %bb7.i197, %bb2.i, %bb4.i\n" + " ret i32 0\n" + "}\n"); + runWithDomTree( + *M, "f", [&](Function &F, DominatorTree *DT) { + for (Function::iterator I = F.begin(), E = F.end(); I != E;) { + BasicBlock *BB = &*I++; + BasicBlock *SinglePred = BB->getSinglePredecessor(); + if (!SinglePred || SinglePred == BB || BB->hasAddressTaken()) continue; + BranchInst *Term = dyn_cast(SinglePred->getTerminator()); + if (Term && !Term->isConditional()) + MergeBasicBlockIntoOnlyPred(BB, DT); + } + EXPECT_TRUE(DT->verify()); + }); +} From 228f43d82feef56c1b9bb759683a583adc0a1b0d Mon Sep 17 00:00:00 2001 From: Mandeep Singh Grang Date: Thu, 26 Oct 2017 16:07:20 +0000 Subject: [PATCH 0006/1206] [MachineModuleInfoImpls] Replace qsort with array_pod_sort Summary: This seems to be the only place in llvm we directly call qsort. We can replace this with a call to array_pod_sort. Also minor cleanup of the sorting function. Reviewers: bkramer, Eugene.Zelenko, rafael Reviewed By: bkramer Subscribers: efriedma, llvm-commits Differential Revision: https://reviews.llvm.org/D39214 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316671 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/MachineModuleInfoImpls.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/CodeGen/MachineModuleInfoImpls.cpp b/lib/CodeGen/MachineModuleInfoImpls.cpp index a29fbc2852860..07b173bc94f82 100644 --- a/lib/CodeGen/MachineModuleInfoImpls.cpp +++ b/lib/CodeGen/MachineModuleInfoImpls.cpp @@ -15,8 +15,6 @@ #include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/ADT/DenseMap.h" #include "llvm/MC/MCSymbol.h" -#include -#include using namespace llvm; @@ -28,20 +26,16 @@ using namespace llvm; void MachineModuleInfoMachO::anchor() {} void MachineModuleInfoELF::anchor() {} -static int SortSymbolPair(const void *LHS, const void *RHS) { - using PairTy = std::pair; - - const MCSymbol *LHSS = ((const PairTy *)LHS)->first; - const MCSymbol *RHSS = ((const PairTy *)RHS)->first; - return LHSS->getName().compare(RHSS->getName()); +using PairTy = std::pair; +static int SortSymbolPair(const PairTy *LHS, const PairTy *RHS) { + return LHS->first->getName().compare(RHS->first->getName()); } MachineModuleInfoImpl::SymbolListTy MachineModuleInfoImpl::getSortedStubs( DenseMap &Map) { MachineModuleInfoImpl::SymbolListTy List(Map.begin(), Map.end()); - if (!List.empty()) - qsort(&List[0], List.size(), sizeof(List[0]), SortSymbolPair); + array_pod_sort(List.begin(), List.end(), SortSymbolPair); Map.clear(); return List; From ae8900a8833835309aecb0a3d947c2ae46fd86c3 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 26 Oct 2017 16:44:13 +0000 Subject: [PATCH 0007/1206] [DynamicLibrary] Fix build on musl libc Summary: On musl libc, stdin/out/err are defined as `FILE* const` globals, and their address is not implicitly convertible to void *, or at least gcc 6 doesn't allow it, giving errors like: ``` error: cannot initialize return object of type 'void *' with an rvalue of type 'FILE *const *' (aka '_IO_FILE *const *') EXPLICIT_SYMBOL(stderr); ^~~~~~~~~~~~~~~~~~~~~~~ ``` Add an explicit cast to fix that problem. Reviewers: marsupial, krytarowski, dim Reviewed By: dim Differential Revision: https://reviews.llvm.org/D39297 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316672 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Support/Unix/DynamicLibrary.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Support/Unix/DynamicLibrary.inc b/lib/Support/Unix/DynamicLibrary.inc index f05103ccd1ebe..029451f347e8c 100644 --- a/lib/Support/Unix/DynamicLibrary.inc +++ b/lib/Support/Unix/DynamicLibrary.inc @@ -71,7 +71,7 @@ void *DynamicLibrary::HandleSet::DLSym(void *Handle, const char *Symbol) { // Must declare the symbols in the global namespace. static void *DoSearch(const char* SymbolName) { #define EXPLICIT_SYMBOL(SYM) \ - extern void *SYM; if (!strcmp(SymbolName, #SYM)) return &SYM + extern void *SYM; if (!strcmp(SymbolName, #SYM)) return (void*)&SYM // If this is darwin, it has some funky issues, try to solve them here. Some // important symbols are marked 'private external' which doesn't allow From ea6f05e017653ff7851ffb1e263847cc9e6d7cec Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Thu, 26 Oct 2017 17:31:33 +0000 Subject: [PATCH 0008/1206] [dsymutil] Check AttrInfo.Name validity before using it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: This upstreams a patch from the osxcross [1] toolchain. It appears that llvm-dsymutil crashes at this place during GCC bootstrap. Adding the check here seems reasonable, since it operates on arbitrary input DWARF, not necessarily generated by the LLVM toolchain, and it seems the un-mangled name need not necessarily exist. Patch by Thomas Pöchtrager [1] https://github.com/tpoechtrager/osxcross Reviewed By: aprantl Differential Revision: https://reviews.llvm.org/D39336 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316678 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/dsymutil/DwarfLinker.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index e422bd035cbe5..b22b7a9dd1ad5 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -2878,7 +2878,8 @@ DIE *DwarfLinker::DIECloner::cloneDIE( Tag == dwarf::DW_TAG_inlined_subroutine); } else if (isTypeTag(Tag) && !AttrInfo.IsDeclaration && getDIENames(InputDIE, AttrInfo)) { - Unit.addTypeAccelerator(Die, AttrInfo.Name, AttrInfo.NameOffset); + if (AttrInfo.Name) + Unit.addTypeAccelerator(Die, AttrInfo.Name, AttrInfo.NameOffset); } // Determine whether there are any children that we want to keep. From 675c21a18656a05203cf26fe7e02acdb71d94849 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Thu, 26 Oct 2017 17:36:43 +0000 Subject: [PATCH 0009/1206] Clear LastMappingSymbols and LastEMS(Info) when resetting the ARM(AArch64)ELFStreamer Summary: This causes a segfault on ARM when (I think) the pass manager is used multiple times. Reset set the (last) current section to NULL without saving the corresponding LastEMSInfo back into the map. The next use of the streamer then save the LastEMSInfo for the NULL section leaving the LastEMSInfo mapping for the last current section (the one that was there before the reset) NULL which cause the LastEMSInfo to be set to NULL when the section is being used again. The reuse of the section (pointer) might mean that the map was holding dangling pointers previously which is why I went for clearing the map and resetting the info, making it as similar to the state right after the constructor run as possible. The AArch64 one doesn't have segfault (since LastEMS isn't a pointer) but it seems to have the same issue. The segfault is likely caused by https://reviews.llvm.org/D30724 which turns LastEMSInfo into a pointer. As mentioned above, it seems that the actual issue was older though. No test is included since the test is believed to be too complicated for such an obvious fix and not worth doing. Reviewers: llvm-commits, shankare, t.p.northover, peter.smith, rengolin Reviewed By: rengolin Subscribers: mgorny, aemerson, rengolin, javed.absar, kristof.beyls Differential Revision: https://reviews.llvm.org/D38588 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316679 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp | 8 ++++++++ lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp | 2 ++ 2 files changed, 10 insertions(+) diff --git a/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp b/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp index 1e8a07aea8ac9..8ee627d50df26 100644 --- a/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp +++ b/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp @@ -102,6 +102,14 @@ class AArch64ELFStreamer : public MCELFStreamer { MCELFStreamer::ChangeSection(Section, Subsection); } + // Reset state between object emissions + void reset() override { + MappingSymbolCounter = 0; + MCELFStreamer::reset(); + LastMappingSymbols.clear(); + LastEMS = EMS_None; + } + /// This function is the one used to emit instruction data into the ELF /// streamer. We override it to add the appropriate mapping symbol if /// necessary. diff --git a/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp b/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp index 1f995ddba7d71..8cfa18f58b61e 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp @@ -1171,6 +1171,8 @@ void ARMELFStreamer::reset() { ATS.reset(); MappingSymbolCounter = 0; MCELFStreamer::reset(); + LastMappingSymbols.clear(); + LastEMSInfo.reset(); // MCELFStreamer clear's the assembler's e_flags. However, for // arm we manually set the ABI version on streamer creation, so // do the same here From fdd275fc256e144fa0e39a75d7a0229cf65774b9 Mon Sep 17 00:00:00 2001 From: Konstantin Zhuravlyov Date: Thu, 26 Oct 2017 17:54:09 +0000 Subject: [PATCH 0010/1206] AMDGPU: Commit missing fence-barrier test This should have been committed with memory model implementation git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316680 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/CodeGen/AMDGPU/fence-barrier.ll | 197 +++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 test/CodeGen/AMDGPU/fence-barrier.ll diff --git a/test/CodeGen/AMDGPU/fence-barrier.ll b/test/CodeGen/AMDGPU/fence-barrier.ll new file mode 100644 index 0000000000000..4748de570a96d --- /dev/null +++ b/test/CodeGen/AMDGPU/fence-barrier.ll @@ -0,0 +1,197 @@ +; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx803 -enable-si-insert-waitcnts=1 -verify-machineinstrs < %s | FileCheck --check-prefix=GCN %s + +declare i8 addrspace(2)* @llvm.amdgcn.dispatch.ptr() +declare i8 addrspace(2)* @llvm.amdgcn.implicitarg.ptr() +declare i32 @llvm.amdgcn.workitem.id.x() +declare i32 @llvm.amdgcn.workgroup.id.x() +declare void @llvm.amdgcn.s.barrier() + +@test_local.temp = internal addrspace(3) global [1 x i32] undef, align 4 +@test_global_local.temp = internal addrspace(3) global [1 x i32] undef, align 4 + +; GCN-LABEL: {{^}}test_local +; GCN: v_mov_b32_e32 v[[VAL:[0-9]+]], 0x777 +; GCN: ds_write_b32 v{{[0-9]+}}, v[[VAL]] +; GCN: s_waitcnt lgkmcnt(0){{$}} +; GCN-NEXT: s_barrier +; GCN: flat_store_dword +define amdgpu_kernel void @test_local(i32 addrspace(1)*) { + %2 = alloca i32 addrspace(1)*, align 4 + store i32 addrspace(1)* %0, i32 addrspace(1)** %2, align 4 + %3 = call i32 @llvm.amdgcn.workitem.id.x() + %4 = zext i32 %3 to i64 + %5 = icmp eq i64 %4, 0 + br i1 %5, label %6, label %7 + +;