diff --git a/lld/test/wasm/lto/thinlto-shared-memory-atomics.ll b/lld/test/wasm/lto/thinlto-shared-memory-atomics.ll new file mode 100644 index 000000000000..0eb54034511d --- /dev/null +++ b/lld/test/wasm/lto/thinlto-shared-memory-atomics.ll @@ -0,0 +1,40 @@ +; RUN: split-file %s %t +; RUN: opt -module-summary %t/main.ll -o %t.main.o +; RUN: opt -module-summary %t/empty.ll -o %t.empty.o +; RUN: wasm-ld --shared-memory %t.main.o %t.empty.o -o %t.wasm +; RUN: llvm-nm %t.wasm | FileCheck %s + +; This test ensures that when an unused function with target-features="+atomics,+bulk-memory" +; is stripped during ThinLTO, the resulting empty module does not fall back to generic TargetMachine +; features and incorrectly omit the atomics/bulk-memory features from the generated custom sections. +; +; Since --shared-memory is passed to wasm-ld, `+atomics` and `+bulk-memory` are propagated +; to the LTO Config via MAttrs, bypassing the functional absence of attributes. + +; CHECK-NOT: error: +; CHECK: _start + +;--- main.ll +target triple = "wasm32-unknown-emscripten" + +define void @_start() #0 { +entry: + ret void +} + +attributes #0 = { "target-features"="+atomics,+bulk-memory,+mutable-globals,+sign-ext" } + +;--- empty.ll +target triple = "wasm32-unknown-emscripten" + +; Thread-local variable that forces generation of TLS layout +@my_tls = thread_local global i32 42, align 4 + +; This function will be removed by dropDeadSymbols because it's unused, +; taking its target-features attribute block along with it. +define void @unused() #0 { +entry: + ret void +} + +attributes #0 = { "target-features"="+atomics,+bulk-memory,+mutable-globals,+sign-ext" } diff --git a/lld/wasm/LTO.cpp b/lld/wasm/LTO.cpp index 668cdf21ea3e..9fcb95b84b54 100644 --- a/lld/wasm/LTO.cpp +++ b/lld/wasm/LTO.cpp @@ -53,6 +53,19 @@ static lto::Config createConfig() { c.OptLevel = ctx.arg.ltoo; c.CPU = getCPUStr(); c.MAttrs = getMAttrs(); + + // If shared memory is enabled, ensure the TargetMachine backend is + // instantiated with atomics and bulk-memory features so that empty + // partitioned ThinLTO modules don't incorrectly strip TLS variables or fall + // back to defaults. This bypasses a bug where deleted functions take their + // target-features away. This is only necessary for atomics (and not other + // features) because Atomics and TLS are the only features we lower away + // during codegen. + if (ctx.arg.sharedMemory) { + c.MAttrs.push_back("+atomics"); + c.MAttrs.push_back("+bulk-memory"); + } + c.CGOptLevel = ctx.arg.ltoCgo; c.DebugPassManager = ctx.arg.ltoDebugPassManager; c.AlwaysEmitRegularLTOObj = !ctx.arg.ltoObjPath.empty();