diff --git a/llvm/include/llvm/Target/TargetMachine.h b/llvm/include/llvm/Target/TargetMachine.h --- a/llvm/include/llvm/Target/TargetMachine.h +++ b/llvm/include/llvm/Target/TargetMachine.h @@ -111,6 +111,7 @@ const Triple &getTargetTriple() const { return TargetTriple; } StringRef getTargetCPU() const { return TargetCPU; } StringRef getTargetFeatureString() const { return TargetFS; } + void setTargetFeatureString(StringRef FS) { TargetFS = std::string(FS); } /// Virtual method implemented by subclasses that returns a reference to that /// target's TargetSubtargetInfo-derived member variable. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp @@ -208,7 +208,9 @@ ///===----------------------------------------------------------------------===// #include "WebAssembly.h" +#include "WebAssemblyTargetMachine.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/IRBuilder.h" @@ -314,16 +316,23 @@ // Get a global variable with the given name. If it doesn't exist declare it, // which will generate an import and asssumes that it will exist at link time. static GlobalVariable *getGlobalVariableI32(Module &M, IRBuilder<> &IRB, + WebAssemblyTargetMachine &TM, const char *Name) { auto Int32Ty = IRB.getInt32Ty(); - auto *GV = dyn_cast(M.getOrInsertGlobal(Name, Int32Ty, [&]() { - return new GlobalVariable(M, Int32Ty, false, - GlobalVariable::ExternalLinkage, nullptr, Name, - nullptr, GlobalValue::LocalExecTLSModel); - })); + auto *GV = dyn_cast(M.getOrInsertGlobal(Name, Int32Ty)); if (!GV) report_fatal_error(Twine("unable to create global: ") + Name); + // If the target supports TLS, make this variable thread-local. We can't just + // unconditionally make it thread-local and depend on + // CoalesceFeaturesAndStripAtomics to downgrade it, because stripping TLS has + // the side effect of disallowing the object from being linked into a + // shared-memory module, which we don't want to be responsible for. + auto *Subtarget = TM.getSubtargetImpl(); + auto TLS = Subtarget->hasAtomics() && Subtarget->hasBulkMemory() + ? GlobalValue::LocalExecTLSModel + : GlobalValue::NotThreadLocal; + GV->setThreadLocalMode(TLS); return GV; } @@ -645,11 +654,15 @@ bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty(); bool DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed); + auto *TPC = getAnalysisIfAvailable(); + assert(TPC && "Expected a TargetPassConfig"); + auto &TM = TPC->getTM(); + // Declare (or get) global variables __THREW__, __threwValue, and // getTempRet0/setTempRet0 function which are used in common for both // exception handling and setjmp/longjmp handling - ThrewGV = getGlobalVariableI32(M, IRB, "__THREW__"); - ThrewValueGV = getGlobalVariableI32(M, IRB, "__threwValue"); + ThrewGV = getGlobalVariableI32(M, IRB, TM, "__THREW__"); + ThrewValueGV = getGlobalVariableI32(M, IRB, TM, "__threwValue"); GetTempRet0Func = getEmscriptenFunction( FunctionType::get(IRB.getInt32Ty(), false), "getTempRet0", &M); SetTempRet0Func = getEmscriptenFunction( diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h @@ -33,6 +33,7 @@ ~WebAssemblyTargetMachine() override; + const WebAssemblySubtarget *getSubtargetImpl() const; const WebAssemblySubtarget *getSubtargetImpl(std::string CPU, std::string FS) const; const WebAssemblySubtarget * diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -145,6 +145,11 @@ WebAssemblyTargetMachine::~WebAssemblyTargetMachine() = default; // anchor. +const WebAssemblySubtarget *WebAssemblyTargetMachine::getSubtargetImpl() const { + return getSubtargetImpl(std::string(getTargetCPU()), + std::string(getTargetFeatureString())); +} + const WebAssemblySubtarget * WebAssemblyTargetMachine::getSubtargetImpl(std::string CPU, std::string FS) const { @@ -191,6 +196,7 @@ FeatureBitset Features = coalesceFeatures(M); std::string FeatureStr = getFeatureString(Features); + WasmTM->setTargetFeatureString(FeatureStr); for (auto &F : M) replaceFeatures(F, FeatureStr); @@ -348,6 +354,12 @@ //===----------------------------------------------------------------------===// void WebAssemblyPassConfig::addIRPasses() { + // Lower atomics and TLS if necessary + addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine())); + + // This is a no-op if atomics are not used in the module + addPass(createAtomicExpandPass()); + // Add signatures to prototype-less function declarations addPass(createWebAssemblyAddMissingPrototypes()); @@ -383,12 +395,6 @@ // Expand indirectbr instructions to switches. addPass(createIndirectBrExpandPass()); - // Lower atomics and TLS if necessary - addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine())); - - // This is a no-op if atomics are not used in the module - addPass(createAtomicExpandPass()); - TargetPassConfig::addIRPasses(); } diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll --- a/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll @@ -1,12 +1,15 @@ -; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s +; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s --check-prefixes=CHECK,NO-TLS +; RUN: opt < %s -wasm-lower-em-ehsjlj -S --mattr=+atomics,+bulk-memory | FileCheck %s --check-prefixes=CHECK,TLS target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" @_ZTIi = external constant i8* @_ZTIc = external constant i8* -; CHECK-DAG: __THREW__ = external thread_local(localexec) global i32 -; CHECK-DAG: __threwValue = external thread_local(localexec) global i32 +; NO-TLS-DAG: __THREW__ = external global i32 +; NO-TLS-DAG: __threwValue = external global i32 +; TLS-DAG: __THREW__ = external thread_local(localexec) global i32 +; TLS-DAG: __threwValue = external thread_local(localexec) global i32 ; Test invoke instruction with clauses (try-catch block) define void @clause() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll --- a/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll +++ b/llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll @@ -1,4 +1,5 @@ -; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s +; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s --check-prefixes=CHECK,NO-TLS +; RUN: opt < %s -wasm-lower-em-ehsjlj -S --mattr=+atomics,+bulk-memory | FileCheck %s --check-prefixes=CHECK,TLS target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" @@ -6,8 +7,10 @@ %struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] } @global_var = global i32 0, align 4 -; CHECK-DAG: __THREW__ = external thread_local(localexec) global i32 -; CHECK-DAG: __threwValue = external thread_local(localexec) global i32 +; NO-TLS-DAG: __THREW__ = external global i32 +; NO-TLS-DAG: __threwValue = external global i32 +; TLS-DAG: __THREW__ = external thread_local(localexec) global i32 +; TLS-DAG: __threwValue = external thread_local(localexec) global i32 ; Test a simple setjmp - longjmp sequence define void @setjmp_longjmp() {