diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -17,6 +17,7 @@ #include "llvm/BinaryFormat/Wasm.h" #include "llvm/MC/MCInstrDesc.h" #include "llvm/Support/DataTypes.h" +#include "../WebAssemblySubtarget.h" #include namespace llvm { @@ -110,9 +111,6 @@ #define GET_INSTRINFO_ENUM #include "WebAssemblyGenInstrInfo.inc" -#define GET_SUBTARGETINFO_ENUM -#include "WebAssemblyGenSubtargetInfo.inc" - namespace llvm { namespace WebAssembly { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -216,42 +216,30 @@ } void WebAssemblyAsmPrinter::EmitTargetFeatures() { - static const std::pair FeaturePairs[] = { - {WebAssembly::FeatureAtomics, "atomics"}, - {WebAssembly::FeatureBulkMemory, "bulk-memory"}, - {WebAssembly::FeatureExceptionHandling, "exception-handling"}, - {WebAssembly::FeatureNontrappingFPToInt, "nontrapping-fptoint"}, - {WebAssembly::FeatureSignExt, "sign-ext"}, - {WebAssembly::FeatureSIMD128, "simd128"}, - }; - struct FeatureEntry { uint8_t Prefix; StringRef Name; }; - FeatureBitset UsedFeatures = - static_cast(TM).getUsedFeatures(); + auto &WasmTM = static_cast(TM); + FeatureBitset UsedFeatures = WasmTM.getUsedFeatures(); // Calculate the features and linkage policies to emit SmallVector EmittedFeatures; - for (auto &F : FeaturePairs) { + for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) { FeatureEntry Entry; - Entry.Name = F.second; - if (F.first == WebAssembly::FeatureAtomics) { + Entry.Name = KV.Key; + if (KV.Value == WebAssembly::FeatureAtomics && WasmTM.getAtomicsStripped()) { // "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; + // atomics lowered to nonatomic operations. In that case, atomics is + // disallowed to prevent unsafe linking with atomics-enabled objects. + assert(!UsedFeatures[WebAssembly::FeatureAtomics]); + Entry.Prefix = wasm::WASM_FEATURE_PREFIX_DISALLOWED; + EmittedFeatures.push_back(Entry); + } else if (UsedFeatures[KV.Value]) { + // Otherwise features are marked Used or not mentioned + Entry.Prefix = wasm::WASM_FEATURE_PREFIX_USED; 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); - } } } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h --- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h @@ -22,11 +22,15 @@ #include "llvm/CodeGen/TargetSubtargetInfo.h" #include +#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, @@ -77,6 +81,7 @@ return &getInstrInfo()->getRegisterInfo(); } const Triple &getTargetTriple() const { return TargetTriple; } + bool enableAtomicExpand() const override; bool enableMachineScheduler() const override; bool useAA() const override; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.cpp @@ -44,6 +44,11 @@ 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 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 @@ -23,7 +23,8 @@ class WebAssemblyTargetMachine final : public LLVMTargetMachine { std::unique_ptr TLOF; mutable StringMap> SubtargetMap; - mutable FeatureBitset UsedFeatures; + FeatureBitset UsedFeatures = FeatureBitset(); + bool AtomicsStripped = false; public: WebAssemblyTargetMachine(const Target &T, const Triple &TT, StringRef CPU, @@ -46,7 +47,10 @@ return TLOF.get(); } + void setUsedFeatures(FeatureBitset Features) { UsedFeatures = Features; } FeatureBitset getUsedFeatures() const { return UsedFeatures; } + void setAtomicsStripped() { AtomicsStripped = true; } + bool getAtomicsStripped() const { return AtomicsStripped; } TargetTransformInfo getTargetTransformInfo(const Function &F) override; 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 @@ -24,6 +24,7 @@ #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; @@ -115,10 +116,6 @@ 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. @@ -132,7 +129,6 @@ auto &I = SubtargetMap[CPU + FS]; if (!I) { I = llvm::make_unique(TargetTriple, CPU, FS, *this); - UsedFeatures |= I->getFeatureBits(); } return I.get(); } @@ -158,21 +154,93 @@ } 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); + WasmTM->setUsedFeatures(Features); + + std::string FeatureStr = getFeatureString(Features); + for (auto &F : M) + replaceFeatures(F, FeatureStr); + + if (!Features[WebAssembly::FeatureAtomics]) { + stripAtomics(M); + stripThreadLocals(M); + } + + // 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(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& Feature) { + F.removeFnAttr("target-features"); + F.removeFnAttr("target-cpu"); + F.addFnAttr("target-features", Feature); + } + + void 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. + for (auto &F : M) + for (auto &B : F) + for (auto &I : B) + if (I.isAtomic()) { + WasmTM->setAtomicsStripped(); + goto done; + } + done: + if (!WasmTM->getAtomicsStripped()) + return; + + LowerAtomicPass Lowerer; + FunctionAnalysisManager FAM; + for (auto &F : M) + Lowerer.run(F, FAM); + } + + void stripThreadLocals(Module &M) { + for (auto &GV : M.globals()) { + if (GV.getThreadLocalMode() != + GlobalValue::ThreadLocalMode::NotThreadLocal) { + WasmTM->setAtomicsStripped(); + GV.setThreadLocalMode(GlobalValue::ThreadLocalMode::NotThreadLocal); + } + } + } }; -char StripThreadLocal::ID = 0; +char CoalesceFeaturesAndStripAtomics::ID = 0; /// WebAssembly Code Generator Pass Configuration Options. class WebAssemblyPassConfig final : public TargetPassConfig { @@ -220,16 +288,11 @@ //===----------------------------------------------------------------------===// void WebAssemblyPassConfig::addIRPasses() { - if (static_cast(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()); diff --git a/llvm/test/CodeGen/WebAssembly/target-features-tls.ll b/llvm/test/CodeGen/WebAssembly/target-features-tls.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/target-features-tls.ll @@ -0,0 +1,26 @@ +; 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,"",@ diff --git a/llvm/test/CodeGen/WebAssembly/target-features.ll b/llvm/test/CodeGen/WebAssembly/target-features.ll --- a/llvm/test/CodeGen/WebAssembly/target-features.ll +++ b/llvm/test/CodeGen/WebAssembly/target-features.ll @@ -1,38 +1,69 @@ ; 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 @@ -43,15 +74,9 @@ ; 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 diff --git a/llvm/test/MC/WebAssembly/array-fill.ll b/llvm/test/MC/WebAssembly/array-fill.ll --- a/llvm/test/MC/WebAssembly/array-fill.ll +++ b/llvm/test/MC/WebAssembly/array-fill.ll @@ -26,7 +26,5 @@ ; 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: Features: [] ; CHECK-NEXT: ... diff --git a/llvm/test/MC/WebAssembly/assembler-binary.ll b/llvm/test/MC/WebAssembly/assembler-binary.ll --- a/llvm/test/MC/WebAssembly/assembler-binary.ll +++ b/llvm/test/MC/WebAssembly/assembler-binary.ll @@ -89,7 +89,5 @@ ; 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: Features: [] ; CHECK-NEXT: ... diff --git a/llvm/test/MC/WebAssembly/bss.ll b/llvm/test/MC/WebAssembly/bss.ll --- a/llvm/test/MC/WebAssembly/bss.ll +++ b/llvm/test/MC/WebAssembly/bss.ll @@ -80,7 +80,5 @@ ; 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: Features: [] ; CHECK-NEXT: ... diff --git a/llvm/test/MC/WebAssembly/comdat.ll b/llvm/test/MC/WebAssembly/comdat.ll --- a/llvm/test/MC/WebAssembly/comdat.ll +++ b/llvm/test/MC/WebAssembly/comdat.ll @@ -121,7 +121,5 @@ ; 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: Features: [] ; CHECK-NEXT: ... diff --git a/llvm/test/MC/WebAssembly/debug-info.ll b/llvm/test/MC/WebAssembly/debug-info.ll --- a/llvm/test/MC/WebAssembly/debug-info.ll +++ b/llvm/test/MC/WebAssembly/debug-info.ll @@ -132,7 +132,7 @@ ; CHECK-NEXT: } ; CHECK-NEXT: Section { ; CHECK-NEXT: Type: CUSTOM (0x0) -; CHECK-NEXT: Size: 10 +; CHECK-NEXT: Size: 1 ; CHECK-NEXT: Offset: 1114 ; CHECK-NEXT: Name: target_features ; CHECK-NEXT: } diff --git a/llvm/test/MC/WebAssembly/explicit-sections.ll b/llvm/test/MC/WebAssembly/explicit-sections.ll --- a/llvm/test/MC/WebAssembly/explicit-sections.ll +++ b/llvm/test/MC/WebAssembly/explicit-sections.ll @@ -72,7 +72,5 @@ ; 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: Features: [] ; CHECK-NEXT: ... diff --git a/llvm/test/MC/WebAssembly/global-ctor-dtor.ll b/llvm/test/MC/WebAssembly/global-ctor-dtor.ll --- a/llvm/test/MC/WebAssembly/global-ctor-dtor.ll +++ b/llvm/test/MC/WebAssembly/global-ctor-dtor.ll @@ -183,7 +183,5 @@ ; 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: Features: [] ; CHECK-NEXT: ... diff --git a/llvm/test/MC/WebAssembly/visibility.ll b/llvm/test/MC/WebAssembly/visibility.ll --- a/llvm/test/MC/WebAssembly/visibility.ll +++ b/llvm/test/MC/WebAssembly/visibility.ll @@ -27,7 +27,5 @@ ; 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: Features: [] ; CHECK-NEXT: ... diff --git a/llvm/test/MC/WebAssembly/weak-alias.ll b/llvm/test/MC/WebAssembly/weak-alias.ll --- a/llvm/test/MC/WebAssembly/weak-alias.ll +++ b/llvm/test/MC/WebAssembly/weak-alias.ll @@ -209,9 +209,7 @@ ; 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: Features: [] ; CHECK-NEXT: ... ; CHECK-SYMS: SYMBOL TABLE: diff --git a/llvm/test/MC/WebAssembly/weak.ll b/llvm/test/MC/WebAssembly/weak.ll --- a/llvm/test/MC/WebAssembly/weak.ll +++ b/llvm/test/MC/WebAssembly/weak.ll @@ -32,7 +32,5 @@ ; 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: Features: [] ; CHECK-NEXT: ... diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp --- a/llvm/utils/TableGen/SubtargetEmitter.cpp +++ b/llvm/utils/TableGen/SubtargetEmitter.cpp @@ -149,7 +149,7 @@ 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"; @@ -169,6 +169,8 @@ FeatureMap[Def] = i; } + OS << " " << "NumSubtargetFeatures = " << N << "\n"; + // Close enumeration and namespace OS << "};\n"; OS << "} // end namespace " << Target << "\n";