#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYMCTARGETDESC_H
#define LLVM_LIB_TARGET_WEBASSEMBLY_MCTARGETDESC_WEBASSEMBLYMCTARGETDESC_H
+#include "../WebAssemblySubtarget.h"
#include "llvm/BinaryFormat/Wasm.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/Support/DataTypes.h"
#define GET_INSTRINFO_ENUM
#include "WebAssemblyGenInstrInfo.inc"
-#define GET_SUBTARGETINFO_ENUM
-#include "WebAssemblyGenSubtargetInfo.inc"
-
namespace llvm {
namespace WebAssembly {
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/Metadata.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCSectionWasm.h"
#include "llvm/MC/MCStreamer.h"
}
EmitProducerInfo(M);
- EmitTargetFeatures();
+ EmitTargetFeatures(M);
}
void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) {
}
}
-void WebAssemblyAsmPrinter::EmitTargetFeatures() {
- static const std::pair<unsigned, const char *> FeaturePairs[] = {
- {WebAssembly::FeatureAtomics, "atomics"},
- {WebAssembly::FeatureBulkMemory, "bulk-memory"},
- {WebAssembly::FeatureExceptionHandling, "exception-handling"},
- {WebAssembly::FeatureNontrappingFPToInt, "nontrapping-fptoint"},
- {WebAssembly::FeatureSignExt, "sign-ext"},
- {WebAssembly::FeatureSIMD128, "simd128"},
- };
-
+void WebAssemblyAsmPrinter::EmitTargetFeatures(Module &M) {
struct FeatureEntry {
uint8_t Prefix;
StringRef Name;
};
- FeatureBitset UsedFeatures =
- static_cast<WebAssemblyTargetMachine &>(TM).getUsedFeatures();
-
- // Calculate the features and linkage policies to emit
+ // Read target features and linkage policies from module metadata
SmallVector<FeatureEntry, 4> EmittedFeatures;
- for (auto &F : FeaturePairs) {
+ for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
+ std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str();
+ Metadata *Policy = M.getModuleFlag(MDKey);
+ if (Policy == nullptr)
+ continue;
+
FeatureEntry Entry;
- Entry.Name = F.second;
- if (F.first == WebAssembly::FeatureAtomics) {
- // "atomics" is special: code compiled without atomics may have had its
- // atomics lowered to nonatomic operations. Such code would be dangerous
- // to mix with proper atomics, so it is always Required or Disallowed.
- Entry.Prefix = UsedFeatures[F.first]
- ? wasm::WASM_FEATURE_PREFIX_REQUIRED
- : wasm::WASM_FEATURE_PREFIX_DISALLOWED;
- EmittedFeatures.push_back(Entry);
- } else {
- // Other features are marked Used or not mentioned
- if (UsedFeatures[F.first]) {
- Entry.Prefix = wasm::WASM_FEATURE_PREFIX_USED;
- EmittedFeatures.push_back(Entry);
- }
- }
+ Entry.Prefix = 0;
+ Entry.Name = KV.Key;
+
+ if (auto *MD = cast<ConstantAsMetadata>(Policy))
+ if (auto *I = cast<ConstantInt>(MD->getValue()))
+ Entry.Prefix = I->getZExtValue();
+
+ // Silently ignore invalid metadata
+ if (Entry.Prefix != wasm::WASM_FEATURE_PREFIX_USED &&
+ Entry.Prefix != wasm::WASM_FEATURE_PREFIX_REQUIRED &&
+ Entry.Prefix != wasm::WASM_FEATURE_PREFIX_DISALLOWED)
+ continue;
+
+ EmittedFeatures.push_back(Entry);
}
+ if (EmittedFeatures.size() == 0)
+ return;
+
// Emit features and linkage policies into the "target_features" section
MCSectionWasm *FeaturesSection = OutContext.getWasmSection(
".custom_section.target_features", SectionKind::getMetadata());
void EmitEndOfAsmFile(Module &M) override;
void EmitProducerInfo(Module &M);
- void EmitTargetFeatures();
+ void EmitTargetFeatures(Module &M);
void EmitJumpTableInfo() override;
void EmitConstantPool() override;
void EmitFunctionBodyStart() override;
InstrInfo(initializeSubtargetDependencies(FS)), TSInfo(),
TLInfo(TM, *this) {}
+bool WebAssemblySubtarget::enableAtomicExpand() const {
+ // If atomics are disabled, atomic ops are lowered instead of expanded
+ return hasAtomics();
+}
+
bool WebAssemblySubtarget::enableMachineScheduler() const {
// Disable the MachineScheduler for now. Even with ShouldTrackPressure set and
// enableMachineSchedDefaultSched overridden, it appears to have an overall
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include <string>
+#define GET_SUBTARGETINFO_ENUM
#define GET_SUBTARGETINFO_HEADER
#include "WebAssemblyGenSubtargetInfo.inc"
namespace llvm {
+// Defined in WebAssemblyGenSubtargetInfo.inc.
+extern const SubtargetFeatureKV
+ WebAssemblyFeatureKV[WebAssembly::NumSubtargetFeatures];
+
class WebAssemblySubtarget final : public WebAssemblyGenSubtargetInfo {
enum SIMDEnum {
NoSIMD,
return &getInstrInfo()->getRegisterInfo();
}
const Triple &getTargetTriple() const { return TargetTriple; }
+ bool enableAtomicExpand() const override;
bool enableMachineScheduler() const override;
bool useAA() const override;
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Scalar/LowerAtomic.h"
#include "llvm/Transforms/Utils.h"
using namespace llvm;
initAsmInfo();
- // Create a subtarget using the unmodified target machine features to
- // initialize the used feature set with explicitly enabled features.
- getSubtargetImpl(getTargetCPU(), getTargetFeatureString());
-
// Note that we don't use setRequiresStructuredCFG(true). It disables
// optimizations than we're ok with, and want, such as critical edge
// splitting and tail merging.
auto &I = SubtargetMap[CPU + FS];
if (!I) {
I = llvm::make_unique<WebAssemblySubtarget>(TargetTriple, CPU, FS, *this);
- UsedFeatures |= I->getFeatureBits();
}
return I.get();
}
}
namespace {
-class StripThreadLocal final : public ModulePass {
- // The default thread model for wasm is single, where thread-local variables
- // are identical to regular globals and should be treated the same. So this
- // pass just converts all GlobalVariables to NotThreadLocal
+
+class CoalesceFeaturesAndStripAtomics final : public ModulePass {
+ // Take the union of all features used in the module and use it for each
+ // function individually, since having multiple feature sets in one module
+ // currently does not make sense for WebAssembly. If atomics are not enabled,
+ // also strip atomic operations and thread local storage.
static char ID;
+ WebAssemblyTargetMachine *WasmTM;
public:
- StripThreadLocal() : ModulePass(ID) {}
+ CoalesceFeaturesAndStripAtomics(WebAssemblyTargetMachine *WasmTM)
+ : ModulePass(ID), WasmTM(WasmTM) {}
+
bool runOnModule(Module &M) override {
- for (auto &GV : M.globals())
- GV.setThreadLocalMode(GlobalValue::ThreadLocalMode::NotThreadLocal);
+ FeatureBitset Features = coalesceFeatures(M);
+
+ std::string FeatureStr = getFeatureString(Features);
+ for (auto &F : M)
+ replaceFeatures(F, FeatureStr);
+
+ bool Stripped = false;
+ if (!Features[WebAssembly::FeatureAtomics]) {
+ Stripped |= stripAtomics(M);
+ Stripped |= stripThreadLocals(M);
+ }
+
+ recordFeatures(M, Features, Stripped);
+
+ // Conservatively assume we have made some change
return true;
}
+
+private:
+ FeatureBitset coalesceFeatures(const Module &M) {
+ FeatureBitset Features =
+ WasmTM
+ ->getSubtargetImpl(WasmTM->getTargetCPU(),
+ WasmTM->getTargetFeatureString())
+ ->getFeatureBits();
+ for (auto &F : M)
+ Features |= WasmTM->getSubtargetImpl(F)->getFeatureBits();
+ return Features;
+ }
+
+ std::string getFeatureString(const FeatureBitset &Features) {
+ std::string Ret;
+ for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
+ if (Features[KV.Value])
+ Ret += (StringRef("+") + KV.Key + ",").str();
+ }
+ return Ret;
+ }
+
+ void replaceFeatures(Function &F, const std::string &Features) {
+ F.removeFnAttr("target-features");
+ F.removeFnAttr("target-cpu");
+ F.addFnAttr("target-features", Features);
+ }
+
+ bool stripAtomics(Module &M) {
+ // Detect whether any atomics will be lowered, since there is no way to tell
+ // whether the LowerAtomic pass lowers e.g. stores.
+ bool Stripped = false;
+ for (auto &F : M) {
+ for (auto &B : F) {
+ for (auto &I : B) {
+ if (I.isAtomic()) {
+ Stripped = true;
+ goto done;
+ }
+ }
+ }
+ }
+
+ done:
+ if (!Stripped)
+ return false;
+
+ LowerAtomicPass Lowerer;
+ FunctionAnalysisManager FAM;
+ for (auto &F : M)
+ Lowerer.run(F, FAM);
+
+ return true;
+ }
+
+ bool stripThreadLocals(Module &M) {
+ bool Stripped = false;
+ for (auto &GV : M.globals()) {
+ if (GV.getThreadLocalMode() !=
+ GlobalValue::ThreadLocalMode::NotThreadLocal) {
+ Stripped = true;
+ GV.setThreadLocalMode(GlobalValue::ThreadLocalMode::NotThreadLocal);
+ }
+ }
+ return Stripped;
+ }
+
+ void recordFeatures(Module &M, const FeatureBitset &Features, bool Stripped) {
+ for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
+ std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str();
+ if (KV.Value == WebAssembly::FeatureAtomics && Stripped) {
+ // "atomics" is special: code compiled without atomics may have had its
+ // atomics lowered to nonatomic operations. In that case, atomics is
+ // disallowed to prevent unsafe linking with atomics-enabled objects.
+ assert(!Features[WebAssembly::FeatureAtomics]);
+ M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey,
+ wasm::WASM_FEATURE_PREFIX_DISALLOWED);
+ } else if (Features[KV.Value]) {
+ // Otherwise features are marked Used or not mentioned
+ M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey,
+ wasm::WASM_FEATURE_PREFIX_USED);
+ }
+ }
+ }
};
-char StripThreadLocal::ID = 0;
+char CoalesceFeaturesAndStripAtomics::ID = 0;
/// WebAssembly Code Generator Pass Configuration Options.
class WebAssemblyPassConfig final : public TargetPassConfig {
//===----------------------------------------------------------------------===//
void WebAssemblyPassConfig::addIRPasses() {
- if (static_cast<WebAssemblyTargetMachine *>(TM)
- ->getUsedFeatures()[WebAssembly::FeatureAtomics]) {
- // Expand some atomic operations. WebAssemblyTargetLowering has hooks which
- // control specifically what gets lowered.
- addPass(createAtomicExpandPass());
- } else {
- // If atomics are not enabled, they get lowered to non-atomics.
- addPass(createLowerAtomicPass());
- addPass(new StripThreadLocal());
- }
+ // Runs LowerAtomicPass 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());
class WebAssemblyTargetMachine final : public LLVMTargetMachine {
std::unique_ptr<TargetLoweringObjectFile> TLOF;
mutable StringMap<std::unique_ptr<WebAssemblySubtarget>> SubtargetMap;
- mutable FeatureBitset UsedFeatures;
public:
WebAssemblyTargetMachine(const Target &T, const Triple &TT, StringRef CPU,
return TLOF.get();
}
- FeatureBitset getUsedFeatures() const { return UsedFeatures; }
-
TargetTransformInfo getTargetTransformInfo(const Function &F) override;
bool usesPhysRegsForPEI() const override { return false; }
--- /dev/null
+; RUN: llc < %s -mattr=-atomics | FileCheck %s --check-prefixes CHECK,NO-ATOMICS
+; RUN: llc < %s -mattr=+atomics | FileCheck %s --check-prefixes CHECK,ATOMICS
+
+; Test that the target features section contains -atomics or +atomics
+; for modules that have thread local storage in their source.
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+@foo = internal thread_local global i32 0
+
+; CHECK-LABEL: .custom_section.target_features,"",@
+
+; -atomics
+; NO-ATOMICS-NEXT: .int8 1
+; NO-ATOMICS-NEXT: .int8 45
+; NO-ATOMICS-NEXT: .int8 7
+; NO-ATOMICS-NEXT: .ascii "atomics"
+; NO-ATOMICS-NEXT: .bss.foo,"",@
+
+; +atomics
+; ATOMICS-NEXT: .int8 1
+; ATOMICS-NEXT: .int8 43
+; ATOMICS-NEXT: .int8 7
+; ATOMICS-NEXT: .ascii "atomics"
+; ATOMICS-NEXT: .tbss.foo,"",@
; RUN: llc < %s | FileCheck %s --check-prefixes CHECK,ATTRS
; RUN: llc < %s -mattr=+simd128 | FileCheck %s --check-prefixes CHECK,SIMD128
-; RUN; llc < %s -mattr=+atomics | FileCheck %s --check-prefixes CHECK,ATOMICS
; RUN: llc < %s -mcpu=bleeding-edge | FileCheck %s --check-prefixes CHECK,BLEEDING-EDGE
; Test that codegen emits target features from the command line or
-; function attributes correctly.
+; function attributes correctly and that features are enabled for the
+; entire module if they are enabled for any function in the module.
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
-define void @foo() #0 {
+define void @foo(i32* %p1) #0 {
+ %a = atomicrmw min i32* undef, i32 42 seq_cst
+ %v = fptoui float undef to i32
+ store i32 %v, i32* %p1
ret void
}
-define void @bar() #1 {
+define void @bar(i32* %p1) #1 {
+ %a = atomicrmw min i32* undef, i32 42 seq_cst
+ %v = fptoui float undef to i32
+ store i32 %v, i32* %p1
ret void
}
-attributes #0 = { "target-features"="+sign-ext" }
+attributes #0 = { "target-features"="+atomics" }
attributes #1 = { "target-features"="+nontrapping-fptoint" }
+
+; CHECK-LABEL: foo:
+
+; Expanded atomicrmw min
+; ATTRS: loop
+; ATTRS: i32.atomic.rmw.cmpxchg
+; SIMD128-NOT: i32.atomic.rmw.cmpxchg
+; ATTRS: end_loop
+
+; nontrapping fptoint
+; ATTRS: i32.trunc_sat_f32_u
+; SIMD128-NOT: i32.trunc_sat_f32_u
+; ATTRS: i32.store
+
+; `bar` should be the same as `foo`
+; CHECK-LABEL: bar:
+
+; Expanded atomicrmw min
+; ATTRS: loop
+; ATTRS: i32.atomic.rmw.cmpxchg
+; SIMD128-NOT: i32.atomic.rmw.cmpxchg
+; ATTRS: end_loop
+
+; nontrapping fptoint
+; ATTRS: i32.trunc_sat_f32_u
+; SIMD128-NOT: i32.trunc_sat_f32_u
+; ATTRS: i32.store
+
; CHECK-LABEL: .custom_section.target_features,"",@
-; -atomics, +sign_ext
-; ATTRS-NEXT: .int8 3
-; ATTRS-NEXT: .int8 45
+; +atomics, +nontrapping-fptoint
+; ATTRS-NEXT: .int8 2
+; ATTRS-NEXT: .int8 43
; ATTRS-NEXT: .int8 7
; ATTRS-NEXT: .ascii "atomics"
; ATTRS-NEXT: .int8 43
; ATTRS-NEXT: .int8 19
; ATTRS-NEXT: .ascii "nontrapping-fptoint"
-; ATTRS-NEXT: .int8 43
-; ATTRS-NEXT: int8 8
-; ATTRS-NEXT: .ascii "sign-ext"
; -atomics, +simd128
; SIMD128-NEXT: .int8 2
; SIMD128-NEXT: .int8 7
; SIMD128-NEXT: .ascii "simd128"
-; =atomics
-; ATOMICS-NEXT: .int8 1
-; ATOMICS-NEXT: .int8 61
-; ATOMICS-NEXT: .int8 7
-; ATOMICS-NEXT: .ascii "atomics"
-
-; =atomics, +nontrapping-fptoint, +sign-ext, +simd128
+; +atomics, +nontrapping-fptoint, +sign-ext, +simd128
; BLEEDING-EDGE-NEXT: .int8 4
-; BLEEDING-EDGE-NEXT: .int8 61
+; BLEEDING-EDGE-NEXT: .int8 43
; BLEEDING-EDGE-NEXT: .int8 7
; BLEEDING-EDGE-NEXT: .ascii "atomics"
; BLEEDING-EDGE-NEXT: .int8 43
; CHECK-NEXT: Name: .data
; CHECK-NEXT: Alignment: 0
; CHECK-NEXT: Flags: [ ]
-; CHECK-NEXT: - Type: CUSTOM
-; CHECK-NEXT: Name: target_features
-; CHECK-NEXT: Features:
-; CHECK-NEXT: - Prefix: DISALLOWED
-; CHECK-NEXT: Name: atomics
; CHECK-NEXT: ...
; CHECK-NEXT: Name: bar
; CHECK-NEXT: Flags: [ UNDEFINED ]
; CHECK-NEXT: Function: 0
-; CHECK-NEXT: - Type: CUSTOM
-; CHECK-NEXT: Name: target_features
-; CHECK-NEXT: Features:
-; CHECK-NEXT: - Prefix: DISALLOWED
-; CHECK-NEXT: Name: atomics
; CHECK-NEXT: ...
; CHECK-NEXT: Name: .bss.bar
; CHECK-NEXT: Alignment: 0
; CHECK-NEXT: Flags: [ ]
-; CHECK-NEXT: - Type: CUSTOM
-; CHECK-NEXT: Name: target_features
-; CHECK-NEXT: Features:
-; CHECK-NEXT: - Prefix: DISALLOWED
-; CHECK-NEXT: Name: atomics
; CHECK-NEXT: ...
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Kind: DATA
; CHECK-NEXT: Index: 0
-; CHECK-NEXT: - Type: CUSTOM
-; CHECK-NEXT: Name: target_features
-; CHECK-NEXT: Features:
-; CHECK-NEXT: - Prefix: DISALLOWED
-; CHECK-NEXT: Name: atomics
; CHECK-NEXT: ...
; CHECK-NEXT: Offset: 1021
; CHECK-NEXT: Name: producers
; CHECK-NEXT: }
-; CHECK-NEXT: Section {
-; CHECK-NEXT: Type: CUSTOM (0x0)
-; CHECK-NEXT: Size: 10
-; CHECK-NEXT: Offset: 1114
-; CHECK-NEXT: Name: target_features
-; CHECK-NEXT: }
; CHECK-NEXT:]
; CHECK-NEXT:Relocations [
; CHECK-NEXT: Section (6) DATA {
; CHECK-NEXT: Name: .sec2
; CHECK-NEXT: Alignment: 3
; CHECK-NEXT: Flags: [ ]
-; CHECK-NEXT: - Type: CUSTOM
-; CHECK-NEXT: Name: target_features
-; CHECK-NEXT: Features:
-; CHECK-NEXT: - Prefix: DISALLOWED
-; CHECK-NEXT: Name: atomics
; CHECK-NEXT: ...
; CHECK-NEXT: Symbol: 10
; CHECK-NEXT: - Priority: 65535
; CHECK-NEXT: Symbol: 7
-; CHECK-NEXT: - Type: CUSTOM
-; CHECK-NEXT: Name: target_features
-; CHECK-NEXT: Features:
-; CHECK-NEXT: - Prefix: DISALLOWED
-; CHECK-NEXT: Name: atomics
; CHECK-NEXT: ...
; CHECK-NEXT: Name: hiddenVis
; CHECK-NEXT: Flags: [ VISIBILITY_HIDDEN ]
; CHECK-NEXT: Function: 1
-; CHECK-NEXT: - Type: CUSTOM
-; CHECK-NEXT: Name: target_features
-; CHECK-NEXT: Features:
-; CHECK-NEXT: - Prefix: DISALLOWED
-; CHECK-NEXT: Name: atomics
; CHECK-NEXT: ...
; CHECK-NEXT: Name: .data.alias_address
; CHECK-NEXT: Alignment: 3
; CHECK-NEXT: Flags: [ ]
-; CHECK-NEXT: - Type: CUSTOM
-; CHECK-NEXT: Name: target_features
-; CHECK-NEXT: Features:
-; CHECK-NEXT: - Prefix: DISALLOWED
-; CHECK-NEXT: Name: atomics
; CHECK-NEXT: ...
; CHECK-SYMS: SYMBOL TABLE:
; CHECK-NEXT: Kind: DATA
; CHECK-NEXT: Name: weak_external_data
; CHECK-NEXT: Flags: [ BINDING_WEAK, UNDEFINED ]
-; CHECK-NEXT: - Type: CUSTOM
-; CHECK-NEXT: Name: target_features
-; CHECK-NEXT: Features:
-; CHECK-NEXT: - Prefix: DISALLOWED
-; CHECK-NEXT: Name: atomics
; CHECK-NEXT: ...
unsigned N = DefList.size();
if (N == 0)
return;
- if (N > MAX_SUBTARGET_FEATURES)
+ if (N + 1 > MAX_SUBTARGET_FEATURES)
PrintFatalError("Too many subtarget features! Bump MAX_SUBTARGET_FEATURES.");
OS << "namespace " << Target << " {\n";
FeatureMap[Def] = i;
}
+ OS << " "
+ << "NumSubtargetFeatures = " << N << "\n";
+
// Close enumeration and namespace
OS << "};\n";
OS << "} // end namespace " << Target << "\n";