Skip to content

Commit 3f34e1b

Browse files
committedMar 29, 2019
[WebAssembly] Merge used feature sets, update atomics linkage policy
Summary: It does not currently make sense to use WebAssembly features in some functions but not others, so this CL adds an IR pass that takes the union of all used feature sets and applies it to each function in the module. This allows us to prevent atomics from being lowered away if some function has opted in to using them. When atomics is not enabled anywhere, we detect whether there exists any atomic operations or thread local storage that would be stripped and disallow linking with objects that contain atomics if and only if atomics or tls are stripped. When atomics is enabled, mark it as used but do not require it of other objects in the link. These changes allow libraries that do not use atomics to be built once and linked into both single-threaded and multithreaded binaries. Reviewers: aheejin, sbc100, dschuff Subscribers: jgravelle-google, hiraditya, sunfish, jfb, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59625 llvm-svn: 357226
1 parent 0c9ea10 commit 3f34e1b

20 files changed

+230
-133
lines changed
 

‎llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYMCTARGETDESC_H
1515
#define LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYMCTARGETDESC_H
1616

17+
#include "../WebAssemblySubtarget.h"
1718
#include "llvm/BinaryFormat/Wasm.h"
1819
#include "llvm/MC/MCInstrDesc.h"
1920
#include "llvm/Support/DataTypes.h"
@@ -115,9 +116,6 @@ enum TOF {
115116
#define GET_INSTRINFO_ENUM
116117
#include "WebAssemblyGenInstrInfo.inc"
117118

118-
#define GET_SUBTARGETINFO_ENUM
119-
#include "WebAssemblyGenSubtargetInfo.inc"
120-
121119
namespace llvm {
122120
namespace WebAssembly {
123121

‎llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp

+27-32
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "llvm/IR/DataLayout.h"
3434
#include "llvm/IR/DebugInfoMetadata.h"
3535
#include "llvm/IR/GlobalVariable.h"
36+
#include "llvm/IR/Metadata.h"
3637
#include "llvm/MC/MCContext.h"
3738
#include "llvm/MC/MCSectionWasm.h"
3839
#include "llvm/MC/MCStreamer.h"
@@ -161,7 +162,7 @@ void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
161162
}
162163

163164
EmitProducerInfo(M);
164-
EmitTargetFeatures();
165+
EmitTargetFeatures(M);
165166
}
166167

167168
void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) {
@@ -215,46 +216,40 @@ void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) {
215216
}
216217
}
217218

218-
void WebAssemblyAsmPrinter::EmitTargetFeatures() {
219-
static const std::pair<unsigned, const char *> FeaturePairs[] = {
220-
{WebAssembly::FeatureAtomics, "atomics"},
221-
{WebAssembly::FeatureBulkMemory, "bulk-memory"},
222-
{WebAssembly::FeatureExceptionHandling, "exception-handling"},
223-
{WebAssembly::FeatureNontrappingFPToInt, "nontrapping-fptoint"},
224-
{WebAssembly::FeatureSignExt, "sign-ext"},
225-
{WebAssembly::FeatureSIMD128, "simd128"},
226-
};
227-
219+
void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) {
228220
struct FeatureEntry {
229221
uint8_t Prefix;
230222
StringRef Name;
231223
};
232224

233-
FeatureBitset UsedFeatures =
234-
static_cast<WebAssemblyTargetMachine &>(TM).getUsedFeatures();
235-
236-
// Calculate the features and linkage policies to emit
225+
// Read target features and linkage policies from module metadata
237226
SmallVector<FeatureEntry, 4> EmittedFeatures;
238-
for (auto &F : FeaturePairs) {
227+
for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
228+
std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str();
229+
Metadata *Policy = M.getModuleFlag(MDKey);
230+
if (Policy == nullptr)
231+
continue;
232+
239233
FeatureEntry Entry;
240-
Entry.Name = F.second;
241-
if (F.first == WebAssembly::FeatureAtomics) {
242-
// "atomics" is special: code compiled without atomics may have had its
243-
// atomics lowered to nonatomic operations. Such code would be dangerous
244-
// to mix with proper atomics, so it is always Required or Disallowed.
245-
Entry.Prefix = UsedFeatures[F.first]
246-
? wasm::WASM_FEATURE_PREFIX_REQUIRED
247-
: wasm::WASM_FEATURE_PREFIX_DISALLOWED;
248-
EmittedFeatures.push_back(Entry);
249-
} else {
250-
// Other features are marked Used or not mentioned
251-
if (UsedFeatures[F.first]) {
252-
Entry.Prefix = wasm::WASM_FEATURE_PREFIX_USED;
253-
EmittedFeatures.push_back(Entry);
254-
}
255-
}
234+
Entry.Prefix = 0;
235+
Entry.Name = KV.Key;
236+
237+
if (auto *MD = cast<ConstantAsMetadata>(Policy))
238+
if (auto *I = cast<ConstantInt>(MD->getValue()))
239+
Entry.Prefix = I->getZExtValue();
240+
241+
// Silently ignore invalid metadata
242+
if (Entry.Prefix != wasm::WASM_FEATURE_PREFIX_USED &&
243+
Entry.Prefix != wasm::WASM_FEATURE_PREFIX_REQUIRED &&
244+
Entry.Prefix != wasm::WASM_FEATURE_PREFIX_DISALLOWED)
245+
continue;
246+
247+
EmittedFeatures.push_back(Entry);
256248
}
257249

250+
if (EmittedFeatures.size() == 0)
251+
return;
252+
258253
// Emit features and linkage policies into the "target_features" section
259254
MCSectionWasm *FeaturesSection = OutContext.getWasmSection(
260255
".custom_section.target_features", SectionKind::getMetadata());

‎llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyAsmPrinter final : public AsmPrinter {
5959

6060
void EmitEndOfAsmFile(Module &M) override;
6161
void EmitProducerInfo(Module &M);
62-
void EmitTargetFeatures();
62+
void EmitTargetFeatures(Module &M);
6363
void EmitJumpTableInfo() override;
6464
void EmitConstantPool() override;
6565
void EmitFunctionBodyStart() override;

‎llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ WebAssemblySubtarget::WebAssemblySubtarget(const Triple &TT,
4444
InstrInfo(initializeSubtargetDependencies(FS)), TSInfo(),
4545
TLInfo(TM, *this) {}
4646

47+
bool WebAssemblySubtarget::enableAtomicExpand() const {
48+
// If atomics are disabled, atomic ops are lowered instead of expanded
49+
return hasAtomics();
50+
}
51+
4752
bool WebAssemblySubtarget::enableMachineScheduler() const {
4853
// Disable the MachineScheduler for now. Even with ShouldTrackPressure set and
4954
// enableMachineSchedDefaultSched overridden, it appears to have an overall

‎llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h

+6
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@
2222
#include "llvm/CodeGen/TargetSubtargetInfo.h"
2323
#include <string>
2424

25+
#define GET_SUBTARGETINFO_ENUM
2526
#define GET_SUBTARGETINFO_HEADER
2627
#include "WebAssemblyGenSubtargetInfo.inc"
2728

2829
namespace llvm {
2930

31+
// Defined in WebAssemblyGenSubtargetInfo.inc.
32+
extern const SubtargetFeatureKV
33+
WebAssemblyFeatureKV[WebAssembly::NumSubtargetFeatures];
34+
3035
class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
3136
enum SIMDEnum {
3237
NoSIMD,
@@ -77,6 +82,7 @@ class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
7782
return &getInstrInfo()->getRegisterInfo();
7883
}
7984
const Triple &getTargetTriple() const { return TargetTriple; }
85+
bool enableAtomicExpand() const override;
8086
bool enableMachineScheduler() const override;
8187
bool useAA() const override;
8288

‎llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp

+116-23
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/Support/TargetRegistry.h"
2727
#include "llvm/Target/TargetOptions.h"
2828
#include "llvm/Transforms/Scalar.h"
29+
#include "llvm/Transforms/Scalar/LowerAtomic.h"
2930
#include "llvm/Transforms/Utils.h"
3031
using namespace llvm;
3132

@@ -117,10 +118,6 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine(
117118

118119
initAsmInfo();
119120

120-
// Create a subtarget using the unmodified target machine features to
121-
// initialize the used feature set with explicitly enabled features.
122-
getSubtargetImpl(getTargetCPU(), getTargetFeatureString());
123-
124121
// Note that we don't use setRequiresStructuredCFG(true). It disables
125122
// optimizations than we're ok with, and want, such as critical edge
126123
// splitting and tail merging.
@@ -134,7 +131,6 @@ WebAssemblyTargetMachine::getSubtargetImpl(std::string CPU,
134131
auto &I = SubtargetMap[CPU + FS];
135132
if (!I) {
136133
I = llvm::make_unique<WebAssemblySubtarget>(TargetTriple, CPU, FS, *this);
137-
UsedFeatures |= I->getFeatureBits();
138134
}
139135
return I.get();
140136
}
@@ -160,21 +156,123 @@ WebAssemblyTargetMachine::getSubtargetImpl(const Function &F) const {
160156
}
161157

162158
namespace {
163-
class StripThreadLocal final : public ModulePass {
164-
// The default thread model for wasm is single, where thread-local variables
165-
// are identical to regular globals and should be treated the same. So this
166-
// pass just converts all GlobalVariables to NotThreadLocal
159+
160+
class CoalesceFeaturesAndStripAtomics final : public ModulePass {
161+
// Take the union of all features used in the module and use it for each
162+
// function individually, since having multiple feature sets in one module
163+
// currently does not make sense for WebAssembly. If atomics are not enabled,
164+
// also strip atomic operations and thread local storage.
167165
static char ID;
166+
WebAssemblyTargetMachine *WasmTM;
168167

169168
public:
170-
StripThreadLocal() : ModulePass(ID) {}
169+
CoalesceFeaturesAndStripAtomics(WebAssemblyTargetMachine *WasmTM)
170+
: ModulePass(ID), WasmTM(WasmTM) {}
171+
171172
bool runOnModule(Module &M) override {
172-
for (auto &GV : M.globals())
173-
GV.setThreadLocalMode(GlobalValue::ThreadLocalMode::NotThreadLocal);
173+
FeatureBitset Features = coalesceFeatures(M);
174+
175+
std::string FeatureStr = getFeatureString(Features);
176+
for (auto &F : M)
177+
replaceFeatures(F, FeatureStr);
178+
179+
bool Stripped = false;
180+
if (!Features[WebAssembly::FeatureAtomics]) {
181+
Stripped |= stripAtomics(M);
182+
Stripped |= stripThreadLocals(M);
183+
}
184+
185+
recordFeatures(M, Features, Stripped);
186+
187+
// Conservatively assume we have made some change
174188
return true;
175189
}
190+
191+
private:
192+
FeatureBitset coalesceFeatures(const Module &M) {
193+
FeatureBitset Features =
194+
WasmTM
195+
->getSubtargetImpl(WasmTM->getTargetCPU(),
196+
WasmTM->getTargetFeatureString())
197+
->getFeatureBits();
198+
for (auto &F : M)
199+
Features |= WasmTM->getSubtargetImpl(F)->getFeatureBits();
200+
return Features;
201+
}
202+
203+
std::string getFeatureString(const FeatureBitset &Features) {
204+
std::string Ret;
205+
for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
206+
if (Features[KV.Value])
207+
Ret += (StringRef("+") + KV.Key + ",").str();
208+
}
209+
return Ret;
210+
}
211+
212+
void replaceFeatures(Function &F, const std::string &Features) {
213+
F.removeFnAttr("target-features");
214+
F.removeFnAttr("target-cpu");
215+
F.addFnAttr("target-features", Features);
216+
}
217+
218+
bool stripAtomics(Module &M) {
219+
// Detect whether any atomics will be lowered, since there is no way to tell
220+
// whether the LowerAtomic pass lowers e.g. stores.
221+
bool Stripped = false;
222+
for (auto &F : M) {
223+
for (auto &B : F) {
224+
for (auto &I : B) {
225+
if (I.isAtomic()) {
226+
Stripped = true;
227+
goto done;
228+
}
229+
}
230+
}
231+
}
232+
233+
done:
234+
if (!Stripped)
235+
return false;
236+
237+
LowerAtomicPass Lowerer;
238+
FunctionAnalysisManager FAM;
239+
for (auto &F : M)
240+
Lowerer.run(F, FAM);
241+
242+
return true;
243+
}
244+
245+
bool stripThreadLocals(Module &M) {
246+
bool Stripped = false;
247+
for (auto &GV : M.globals()) {
248+
if (GV.getThreadLocalMode() !=
249+
GlobalValue::ThreadLocalMode::NotThreadLocal) {
250+
Stripped = true;
251+
GV.setThreadLocalMode(GlobalValue::ThreadLocalMode::NotThreadLocal);
252+
}
253+
}
254+
return Stripped;
255+
}
256+
257+
void recordFeatures(Module &M, const FeatureBitset &Features, bool Stripped) {
258+
for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
259+
std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str();
260+
if (KV.Value == WebAssembly::FeatureAtomics && Stripped) {
261+
// "atomics" is special: code compiled without atomics may have had its
262+
// atomics lowered to nonatomic operations. In that case, atomics is
263+
// disallowed to prevent unsafe linking with atomics-enabled objects.
264+
assert(!Features[WebAssembly::FeatureAtomics]);
265+
M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey,
266+
wasm::WASM_FEATURE_PREFIX_DISALLOWED);
267+
} else if (Features[KV.Value]) {
268+
// Otherwise features are marked Used or not mentioned
269+
M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey,
270+
wasm::WASM_FEATURE_PREFIX_USED);
271+
}
272+
}
273+
}
176274
};
177-
char StripThreadLocal::ID = 0;
275+
char CoalesceFeaturesAndStripAtomics::ID = 0;
178276

179277
/// WebAssembly Code Generator Pass Configuration Options.
180278
class WebAssemblyPassConfig final : public TargetPassConfig {
@@ -222,16 +320,11 @@ FunctionPass *WebAssemblyPassConfig::createTargetRegisterAllocator(bool) {
222320
//===----------------------------------------------------------------------===//
223321

224322
void WebAssemblyPassConfig::addIRPasses() {
225-
if (static_cast<WebAssemblyTargetMachine *>(TM)
226-
->getUsedFeatures()[WebAssembly::FeatureAtomics]) {
227-
// Expand some atomic operations. WebAssemblyTargetLowering has hooks which
228-
// control specifically what gets lowered.
229-
addPass(createAtomicExpandPass());
230-
} else {
231-
// If atomics are not enabled, they get lowered to non-atomics.
232-
addPass(createLowerAtomicPass());
233-
addPass(new StripThreadLocal());
234-
}
323+
// Runs LowerAtomicPass if necessary
324+
addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine()));
325+
326+
// This is a no-op if atomics are not used in the module
327+
addPass(createAtomicExpandPass());
235328

236329
// Add signatures to prototype-less function declarations
237330
addPass(createWebAssemblyAddMissingPrototypes());

‎llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h

-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ namespace llvm {
2323
class WebAssemblyTargetMachine final : public LLVMTargetMachine {
2424
std::unique_ptr<TargetLoweringObjectFile> TLOF;
2525
mutable StringMap<std::unique_ptr<WebAssemblySubtarget>> SubtargetMap;
26-
mutable FeatureBitset UsedFeatures;
2726

2827
public:
2928
WebAssemblyTargetMachine(const Target &T, const Triple &TT, StringRef CPU,
@@ -46,8 +45,6 @@ class WebAssemblyTargetMachine final : public LLVMTargetMachine {
4645
return TLOF.get();
4746
}
4847

49-
FeatureBitset getUsedFeatures() const { return UsedFeatures; }
50-
5148
TargetTransformInfo getTargetTransformInfo(const Function &F) override;
5249

5350
bool usesPhysRegsForPEI() const override { return false; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
; RUN: llc < %s -mattr=-atomics | FileCheck %s --check-prefixes CHECK,NO-ATOMICS
2+
; RUN: llc < %s -mattr=+atomics | FileCheck %s --check-prefixes CHECK,ATOMICS
3+
4+
; Test that the target features section contains -atomics or +atomics
5+
; for modules that have thread local storage in their source.
6+
7+
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
8+
target triple = "wasm32-unknown-unknown"
9+
10+
@foo = internal thread_local global i32 0
11+
12+
; CHECK-LABEL: .custom_section.target_features,"",@
13+
14+
; -atomics
15+
; NO-ATOMICS-NEXT: .int8 1
16+
; NO-ATOMICS-NEXT: .int8 45
17+
; NO-ATOMICS-NEXT: .int8 7
18+
; NO-ATOMICS-NEXT: .ascii "atomics"
19+
; NO-ATOMICS-NEXT: .bss.foo,"",@
20+
21+
; +atomics
22+
; ATOMICS-NEXT: .int8 1
23+
; ATOMICS-NEXT: .int8 43
24+
; ATOMICS-NEXT: .int8 7
25+
; ATOMICS-NEXT: .ascii "atomics"
26+
; ATOMICS-NEXT: .tbss.foo,"",@

0 commit comments

Comments
 (0)