Skip to content

Commit 89fe083

Browse files
committed
[WebAssembly] Check features before making SjLj vars thread-local
1c5a3c4 updated the variables inserted by Emscripten SjLj lowering to be thread-local, depending on the CoalesceFeaturesAndStripAtomics pass to downgrade them to normal globals if the target features did not support TLS. However, this had the unintended side effect of preventing all non-TLS-supporting objects from being linked into modules with shared memory, because stripping TLS marks an object as thread-unsafe. This patch fixes the problem by only making the SjLj lowering variables thread-local if the target machine supports TLS so that it never introduces new usage of TLS that will be stripped. Since SjLj lowering works on Modules instead of Functions, this required that the WebAssemblyTargetMachine have its feature string updated to reflect the coalesced features collected from all the functions so that a WebAssemblySubtarget can be created without using any particular function. Differential Revision: https://reviews.llvm.org/D88323
1 parent d216607 commit 89fe083

File tree

6 files changed

+46
-19
lines changed

6 files changed

+46
-19
lines changed

llvm/include/llvm/Target/TargetMachine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class TargetMachine {
111111
const Triple &getTargetTriple() const { return TargetTriple; }
112112
StringRef getTargetCPU() const { return TargetCPU; }
113113
StringRef getTargetFeatureString() const { return TargetFS; }
114+
void setTargetFeatureString(StringRef FS) { TargetFS = std::string(FS); }
114115

115116
/// Virtual method implemented by subclasses that returns a reference to that
116117
/// target's TargetSubtargetInfo-derived member variable.

llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,9 @@
208208
///===----------------------------------------------------------------------===//
209209

210210
#include "WebAssembly.h"
211+
#include "WebAssemblyTargetMachine.h"
211212
#include "llvm/ADT/StringExtras.h"
213+
#include "llvm/CodeGen/TargetPassConfig.h"
212214
#include "llvm/IR/DebugInfoMetadata.h"
213215
#include "llvm/IR/Dominators.h"
214216
#include "llvm/IR/IRBuilder.h"
@@ -314,16 +316,23 @@ static bool canThrow(const Value *V) {
314316
// Get a global variable with the given name. If it doesn't exist declare it,
315317
// which will generate an import and asssumes that it will exist at link time.
316318
static GlobalVariable *getGlobalVariableI32(Module &M, IRBuilder<> &IRB,
319+
WebAssemblyTargetMachine &TM,
317320
const char *Name) {
318321
auto Int32Ty = IRB.getInt32Ty();
319-
auto *GV = dyn_cast<GlobalVariable>(M.getOrInsertGlobal(Name, Int32Ty, [&]() {
320-
return new GlobalVariable(M, Int32Ty, false,
321-
GlobalVariable::ExternalLinkage, nullptr, Name,
322-
nullptr, GlobalValue::LocalExecTLSModel);
323-
}));
322+
auto *GV = dyn_cast<GlobalVariable>(M.getOrInsertGlobal(Name, Int32Ty));
324323
if (!GV)
325324
report_fatal_error(Twine("unable to create global: ") + Name);
326325

326+
// If the target supports TLS, make this variable thread-local. We can't just
327+
// unconditionally make it thread-local and depend on
328+
// CoalesceFeaturesAndStripAtomics to downgrade it, because stripping TLS has
329+
// the side effect of disallowing the object from being linked into a
330+
// shared-memory module, which we don't want to be responsible for.
331+
auto *Subtarget = TM.getSubtargetImpl();
332+
auto TLS = Subtarget->hasAtomics() && Subtarget->hasBulkMemory()
333+
? GlobalValue::LocalExecTLSModel
334+
: GlobalValue::NotThreadLocal;
335+
GV->setThreadLocalMode(TLS);
327336
return GV;
328337
}
329338

@@ -645,11 +654,15 @@ bool WebAssemblyLowerEmscriptenEHSjLj::runOnModule(Module &M) {
645654
bool LongjmpUsed = LongjmpF && !LongjmpF->use_empty();
646655
bool DoSjLj = EnableSjLj && (SetjmpUsed || LongjmpUsed);
647656

657+
auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
658+
assert(TPC && "Expected a TargetPassConfig");
659+
auto &TM = TPC->getTM<WebAssemblyTargetMachine>();
660+
648661
// Declare (or get) global variables __THREW__, __threwValue, and
649662
// getTempRet0/setTempRet0 function which are used in common for both
650663
// exception handling and setjmp/longjmp handling
651-
ThrewGV = getGlobalVariableI32(M, IRB, "__THREW__");
652-
ThrewValueGV = getGlobalVariableI32(M, IRB, "__threwValue");
664+
ThrewGV = getGlobalVariableI32(M, IRB, TM, "__THREW__");
665+
ThrewValueGV = getGlobalVariableI32(M, IRB, TM, "__threwValue");
653666
GetTempRet0Func = getEmscriptenFunction(
654667
FunctionType::get(IRB.getInt32Ty(), false), "getTempRet0", &M);
655668
SetTempRet0Func = getEmscriptenFunction(

llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine(
145145

146146
WebAssemblyTargetMachine::~WebAssemblyTargetMachine() = default; // anchor.
147147

148+
const WebAssemblySubtarget *WebAssemblyTargetMachine::getSubtargetImpl() const {
149+
return getSubtargetImpl(std::string(getTargetCPU()),
150+
std::string(getTargetFeatureString()));
151+
}
152+
148153
const WebAssemblySubtarget *
149154
WebAssemblyTargetMachine::getSubtargetImpl(std::string CPU,
150155
std::string FS) const {
@@ -191,6 +196,7 @@ class CoalesceFeaturesAndStripAtomics final : public ModulePass {
191196
FeatureBitset Features = coalesceFeatures(M);
192197

193198
std::string FeatureStr = getFeatureString(Features);
199+
WasmTM->setTargetFeatureString(FeatureStr);
194200
for (auto &F : M)
195201
replaceFeatures(F, FeatureStr);
196202

@@ -348,6 +354,12 @@ FunctionPass *WebAssemblyPassConfig::createTargetRegisterAllocator(bool) {
348354
//===----------------------------------------------------------------------===//
349355

350356
void WebAssemblyPassConfig::addIRPasses() {
357+
// Lower atomics and TLS if necessary
358+
addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine()));
359+
360+
// This is a no-op if atomics are not used in the module
361+
addPass(createAtomicExpandPass());
362+
351363
// Add signatures to prototype-less function declarations
352364
addPass(createWebAssemblyAddMissingPrototypes());
353365

@@ -383,12 +395,6 @@ void WebAssemblyPassConfig::addIRPasses() {
383395
// Expand indirectbr instructions to switches.
384396
addPass(createIndirectBrExpandPass());
385397

386-
// Lower atomics and TLS if necessary
387-
addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine()));
388-
389-
// This is a no-op if atomics are not used in the module
390-
addPass(createAtomicExpandPass());
391-
392398
TargetPassConfig::addIRPasses();
393399
}
394400

llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class WebAssemblyTargetMachine final : public LLVMTargetMachine {
3333

3434
~WebAssemblyTargetMachine() override;
3535

36+
const WebAssemblySubtarget *getSubtargetImpl() const;
3637
const WebAssemblySubtarget *getSubtargetImpl(std::string CPU,
3738
std::string FS) const;
3839
const WebAssemblySubtarget *

llvm/test/CodeGen/WebAssembly/lower-em-exceptions.ll

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s
1+
; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s --check-prefixes=CHECK,NO-TLS
2+
; RUN: opt < %s -wasm-lower-em-ehsjlj -S --mattr=+atomics,+bulk-memory | FileCheck %s --check-prefixes=CHECK,TLS
23

34
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
45
target triple = "wasm32-unknown-unknown"
56

67
@_ZTIi = external constant i8*
78
@_ZTIc = external constant i8*
8-
; CHECK-DAG: __THREW__ = external thread_local(localexec) global i32
9-
; CHECK-DAG: __threwValue = external thread_local(localexec) global i32
9+
; NO-TLS-DAG: __THREW__ = external global i32
10+
; NO-TLS-DAG: __threwValue = external global i32
11+
; TLS-DAG: __THREW__ = external thread_local(localexec) global i32
12+
; TLS-DAG: __threwValue = external thread_local(localexec) global i32
1013

1114
; Test invoke instruction with clauses (try-catch block)
1215
define void @clause() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {

llvm/test/CodeGen/WebAssembly/lower-em-sjlj.ll

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s
1+
; RUN: opt < %s -wasm-lower-em-ehsjlj -S | FileCheck %s --check-prefixes=CHECK,NO-TLS
2+
; RUN: opt < %s -wasm-lower-em-ehsjlj -S --mattr=+atomics,+bulk-memory | FileCheck %s --check-prefixes=CHECK,TLS
23

34
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
45
target triple = "wasm32-unknown-unknown"
56

67
%struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] }
78

89
@global_var = global i32 0, align 4
9-
; CHECK-DAG: __THREW__ = external thread_local(localexec) global i32
10-
; CHECK-DAG: __threwValue = external thread_local(localexec) global i32
10+
; NO-TLS-DAG: __THREW__ = external global i32
11+
; NO-TLS-DAG: __threwValue = external global i32
12+
; TLS-DAG: __THREW__ = external thread_local(localexec) global i32
13+
; TLS-DAG: __threwValue = external thread_local(localexec) global i32
1114

1215
; Test a simple setjmp - longjmp sequence
1316
define void @setjmp_longjmp() {

0 commit comments

Comments
 (0)