Bug 1509441: Check coherency of compiler switches when building a CompilerArgs; r=lth
authorBenjamin Bouvier <benj@benj.me>
Wed, 23 Jan 2019 15:59:12 +0100
changeset 515300 cb20dcd8ea7e8e7a20392cfb7bf3759c26cfc240
parent 515299 677eda6bdaf96eab98a43cfe4e4ab47d53f9e29c
child 515301 7df604faea71828fb893e6b5c8f090a35f57d261
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1509441
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1509441: Check coherency of compiler switches when building a CompilerArgs; r=lth
js/src/builtin/TestingFunctions.cpp
js/src/jit-test/tests/debug/wasm-13.js
js/src/jit-test/tests/debug/wasm-jseval.js
js/src/jit-test/tests/wasm/ion-debugger.js
js/src/jit-test/tests/wasm/regress/bug1440512.js
js/src/jit-test/tests/wasm/regress/bug1491322.js
js/src/jit-test/tests/wasm/regress/debug-clone-segment.js
js/src/jit-test/tests/wasm/regress/debug-exception-in-fast-import.js
js/src/jit-test/tests/wasm/regress/debug-osr.js
js/src/shell/js.cpp
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmCraneliftCompile.cpp
js/src/wasm/WasmCraneliftCompile.h
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmJS.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -68,16 +68,17 @@
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/ProxyObject.h"
 #include "vm/SavedStacks.h"
 #include "vm/Stack.h"
 #include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 #include "wasm/AsmJS.h"
+#include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmJS.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmTextToBinary.h"
 #include "wasm/WasmTypes.h"
 
 #include "vm/Compartment-inl.h"
 #include "vm/Debugger-inl.h"
@@ -681,40 +682,58 @@ static bool WasmGcEnabled(JSContext* cx,
 #ifdef ENABLE_WASM_GC
   args.rval().setBoolean(wasm::HasReftypesSupport(cx));
 #else
   args.rval().setBoolean(false);
 #endif
   return true;
 }
 
+static bool WasmDebugSupport(JSContext* cx, unsigned argc, Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+  args.rval().setBoolean(cx->options().wasmBaseline() && wasm::BaselineCanCompile());
+  return true;
+}
+
 static bool WasmGeneralizedTables(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 #ifdef ENABLE_WASM_GENERALIZED_TABLES
   // Generalized tables depend on anyref, though not currently on (ref T)
   // types nor on structures or other GC-proposal features.
   bool isSupported = wasm::HasReftypesSupport(cx);
 #else
   bool isSupported = false;
 #endif
   args.rval().setBoolean(isSupported);
   return true;
 }
 
 static bool WasmCompileMode(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
+  bool baseline = cx->options().wasmBaseline();
+  bool ion = cx->options().wasmIon();
+#ifdef ENABLE_WASM_CRANELIFT
+  bool cranelift = cx->options().wasmCranelift();
+#else
+  bool cranelift = false;
+#endif
+
   // We default to ion if nothing is enabled, as does the Wasm compiler.
   JSString* result;
   if (!wasm::HasSupport(cx)) {
-    result = JS_NewStringCopyZ(cx, "disabled");
-  } else if (cx->options().wasmBaseline() && cx->options().wasmIon()) {
-    result = JS_NewStringCopyZ(cx, "baseline-or-ion");
-  } else if (cx->options().wasmBaseline()) {
+    result = JS_NewStringCopyZ(cx, "none");
+  } else if (baseline && ion) {
+    result = JS_NewStringCopyZ(cx, "baseline+ion");
+  } else if (baseline && cranelift) {
+    result = JS_NewStringCopyZ(cx, "baseline+cranelift");
+  } else if (baseline) {
     result = JS_NewStringCopyZ(cx, "baseline");
+  } else if (cranelift) {
+    result = JS_NewStringCopyZ(cx, "cranelift");
   } else {
     result = JS_NewStringCopyZ(cx, "ion");
   }
 
   if (!result) {
     return false;
   }
 
@@ -5970,16 +5989,20 @@ gc::ZealModeHelpText),
     JS_FN_HELP("wasmReftypesEnabled", WasmReftypesEnabled, 1, 0,
 "wasmReftypesEnabled(bool)",
 "  Returns a boolean indicating whether the WebAssembly reftypes proposal is enabled."),
 
     JS_FN_HELP("wasmGcEnabled", WasmGcEnabled, 1, 0,
 "wasmGcEnabled(bool)",
 "  Returns a boolean indicating whether the WebAssembly GC types proposal is enabled."),
 
+    JS_FN_HELP("wasmDebugSupport", WasmDebugSupport, 1, 0,
+"wasmDebugSupport(bool)",
+"  Returns a boolean indicating whether the WebAssembly compilers support debugging."),
+
     JS_FN_HELP("wasmGeneralizedTables", WasmGeneralizedTables, 1, 0,
 "wasmGeneralizedTables(bool)",
 "  Returns a boolean indicating whether generalized tables are available.\n"
 "  This feature set includes 'anyref' as a table type, and new instructions\n"
 "  including table.get, table.set, table.grow, and table.size."),
 
     JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
 "isLazyFunction(fun)",
--- a/js/src/jit-test/tests/debug/wasm-13.js
+++ b/js/src/jit-test/tests/debug/wasm-13.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-wasm-compiler-ion
+// |jit-test| skip-if: !wasmDebugSupport()
 // Tests that wasm module scripts has inspectable globals and memory.
 
 load(libdir + "wasm.js");
 load(libdir + 'eqArrayHelper.js');
 
 function monitorGlobalValues(wast, lib, expected) {
     function setupFrame(frame) {
         var globals = {};
--- a/js/src/jit-test/tests/debug/wasm-jseval.js
+++ b/js/src/jit-test/tests/debug/wasm-jseval.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-wasm-compiler-ion
+// |jit-test| skip-if: !wasmDebugSupport()
 // Tests that JS can be evaluated on wasm module scripts frames.
 
 load(libdir + "wasm.js");
 
 wasmRunWithDebugger(
     '(module (memory 1 1)\
      (global (mut f64) (f64.const 0.5))\
      (global f32 (f32.const 3.5))\
--- a/js/src/jit-test/tests/wasm/ion-debugger.js
+++ b/js/src/jit-test/tests/wasm/ion-debugger.js
@@ -1,8 +1,9 @@
+// |jit-test| skip-if: !wasmDebugSupport()
 var g = newGlobal({newCompartment: true});
 g.parent = this;
 g.eval("Debugger(parent).onExceptionUnwind = function () {};");
 lfModule = new WebAssembly.Module(wasmTextToBinary(`
 (module
   (export "f" $func0)
   (func $func0 (result i32)
     i32.const -1
--- a/js/src/jit-test/tests/wasm/regress/bug1440512.js
+++ b/js/src/jit-test/tests/wasm/regress/bug1440512.js
@@ -1,8 +1,9 @@
+// |jit-test| skip-if: !wasmDebugSupport()
 var g = newGlobal({newCompartment: true});
 var dbg = new g.Debugger(this);
 var dbg = new Debugger;
 var kWasmH0 = 0;
 var kWasmH1 = 0x61;
 var kWasmH2 = 0x73;
 var kWasmH3 = 0x6d;
 var kWasmV0 = 0x1;
--- a/js/src/jit-test/tests/wasm/regress/bug1491322.js
+++ b/js/src/jit-test/tests/wasm/regress/bug1491322.js
@@ -1,8 +1,9 @@
+// |jit-test| skip-if: !wasmDebugSupport()
 var evalInFrame = (function(global) {
     var dbgGlobal = newGlobal({newCompartment: true});
     var dbg = new dbgGlobal.Debugger();
     dbg.addDebuggee(global);
 })(this);
 const Module = WebAssembly.Module;
 const Instance = WebAssembly.Instance;
 var m = new Module(wasmTextToBinary(`(module
--- a/js/src/jit-test/tests/wasm/regress/debug-clone-segment.js
+++ b/js/src/jit-test/tests/wasm/regress/debug-clone-segment.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !wasmDebugSupport()
+//
 var mod = new WebAssembly.Module(wasmTextToBinary(`
     (module
         (func (export "func_0") (result i32)
          call 0
         )
     )
 `));
 
--- a/js/src/jit-test/tests/wasm/regress/debug-exception-in-fast-import.js
+++ b/js/src/jit-test/tests/wasm/regress/debug-exception-in-fast-import.js
@@ -1,8 +1,9 @@
+// |jit-test| skip-if: !wasmDebugSupport()
 g = newGlobal({newCompartment: true});
 g.parent = this;
 g.eval("(" + function() {
     Debugger(parent).onExceptionUnwind = function(frame) {}
 } + ")()")
 
 o = {};
 
--- a/js/src/jit-test/tests/wasm/regress/debug-osr.js
+++ b/js/src/jit-test/tests/wasm/regress/debug-osr.js
@@ -1,8 +1,9 @@
+// |jit-test| skip-if: !wasmDebugSupport()
 g = newGlobal({newCompartment: true});
 g.parent = this;
 g.eval("(" + function() {
     Debugger(parent).onExceptionUnwind = function(frame) {
         frame.older
     }
 } + ")()");
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -10209,24 +10209,16 @@ static bool SetContextOptions(JSContext*
       enableWasmCranelift = true;
     } else {
       return OptionFailure("wasm-compiler", str);
     }
   }
 
 #ifdef ENABLE_WASM_REFTYPES
   enableWasmGc = op.getBoolOption("wasm-gc");
-#  ifdef ENABLE_WASM_CRANELIFT
-  if (enableWasmGc && enableWasmCranelift) {
-    fprintf(stderr,
-            "Do not combine --wasm-gc and --wasm-compiler=cranelift, they are "
-            "incompatible.\n");
-  }
-  enableWasmGc = enableWasmGc && !enableWasmCranelift;
-#  endif
 #endif
   enableWasmVerbose = op.getBoolOption("wasm-verbose");
   enableTestWasmAwaitTier2 = op.getBoolOption("test-wasm-await-tier2");
   enableAsyncStacks = !op.getBoolOption("no-async-stacks");
   enableStreams = !op.getBoolOption("no-streams");
 #ifdef ENABLE_BIGINT
   enableBigInt = !op.getBoolOption("no-bigint");
 #endif
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -19,16 +19,17 @@
 #include "wasm/WasmCompile.h"
 
 #include "mozilla/Maybe.h"
 #include "mozilla/Unused.h"
 
 #include "jit/ProcessExecutableMemory.h"
 #include "util/Text.h"
 #include "wasm/WasmBaselineCompile.h"
+#include "wasm/WasmCraneliftCompile.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmOpIter.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::jit;
@@ -70,41 +71,65 @@ uint32_t wasm::ObservedCPUFeatures() {
 #else
 #  error "unknown architecture"
 #endif
 }
 
 SharedCompileArgs
 CompileArgs::build(JSContext* cx, ScriptedCaller&& scriptedCaller)
 {
-  CompileArgs* target = cx->new_<CompileArgs>(std::move(scriptedCaller));
-  if (!target) {
-    return nullptr;
-  }
-
-  target->baselineEnabled = cx->options().wasmBaseline();
-  target->ionEnabled = cx->options().wasmIon();
+  bool baseline = BaselineCanCompile() && cx->options().wasmBaseline();
+  bool ion = IonCanCompile() && cx->options().wasmIon();
 #ifdef ENABLE_WASM_CRANELIFT
-  target->craneliftEnabled = cx->options().wasmCranelift();
+  bool cranelift = CraneliftCanCompile() && cx->options().wasmCranelift();
 #else
-  target->craneliftEnabled = false;
+  bool cranelift = false;
 #endif
 
   // Debug information such as source view or debug traps will require
   // additional memory and permanently stay in baseline code, so we try to
   // only enable it when a developer actually cares: when the debugger tab
   // is open.
-  target->debugEnabled = cx->realm()->debuggerObservesAsmJS();
+  bool debug = cx->realm()->debuggerObservesAsmJS();
+  bool gc = cx->options().wasmGc();
+
+  bool sharedMemory = cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
+  bool forceTiering = cx->options().testWasmAwaitTier2() || JitOptions.wasmDelayTier2;
+
+  if (debug || gc) {
+    if (!baseline) {
+      JS_ReportErrorASCII(cx, "can't use wasm debug/gc without baseline");
+      return nullptr;
+    }
+    ion = false;
+    cranelift = false;
+  }
 
-  target->sharedMemoryEnabled =
-      cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
-  target->forceTiering =
-      cx->options().testWasmAwaitTier2() || JitOptions.wasmDelayTier2;
+  if (forceTiering && (!baseline || (!cranelift && !ion))) {
+    // This can happen only in testing, and in this case we don't have a
+    // proper way to signal the error, so just silently override the default,
+    // instead of adding a skip-if directive to every test using debug/gc.
+    forceTiering = false;
+  }
+
+  // HasCompilerSupport() should prevent failure here.
+  MOZ_RELEASE_ASSERT(baseline || ion || cranelift);
 
-  target->gcEnabled = HasReftypesSupport(cx);
+  CompileArgs* target = cx->new_<CompileArgs>(std::move(scriptedCaller));
+  if (!target) {
+    return nullptr;
+  }
+
+  target->baselineEnabled = baseline;
+  target->ionEnabled = ion;
+  target->craneliftEnabled = cranelift;
+  target->debugEnabled = debug;
+  target->sharedMemoryEnabled = sharedMemory;
+  target->forceTiering = forceTiering;
+  target->gcEnabled = gc;
 
   return target;
 }
 
 // Classify the current system as one of a set of recognizable classes.  This
 // really needs to get our tier-1 systems right.
 //
 // TODO: We don't yet have a good measure of how fast a system is.  We
@@ -415,53 +440,46 @@ void CompilerEnvironment::computeParamet
   MOZ_ASSERT(!isComputed());
 
   if (state_ == InitialWithModeTierDebug) {
     computeParameters(gcFeatureOptIn);
     return;
   }
 
   bool gcEnabled = args_->gcEnabled && gcFeatureOptIn;
-  bool baselineFlag = args_->baselineEnabled || gcEnabled;
-  bool ionFlag = args_->ionEnabled && !gcEnabled;
-  bool debugFlag = args_->debugEnabled;
-#ifdef ENABLE_WASM_CRANELIFT
-  bool craneliftFlag = args_->craneliftEnabled;
-#endif
+  bool baselineEnabled = args_->baselineEnabled;
+  bool ionEnabled = args_->ionEnabled;
+  bool debugEnabled = args_->debugEnabled;
+  bool craneliftEnabled = args_->craneliftEnabled;
+  bool forceTiering = args_->forceTiering;
 
-  // Attempt to default to ion if baseline is disabled.
-  bool baselineEnabled = BaselineCanCompile() && baselineFlag;
-  bool debugEnabled = BaselineCanCompile() && debugFlag;
-  bool ionEnabled = IonCanCompile() && (ionFlag || !baselineEnabled);
+  bool hasSecondTier = ionEnabled || craneliftEnabled;
+  MOZ_ASSERT_IF(gcEnabled || debugEnabled, baselineEnabled);
+  MOZ_ASSERT_IF(forceTiering, baselineEnabled && hasSecondTier);
 
   // HasCompilerSupport() should prevent failure here
-  MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled);
+  MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled || craneliftEnabled);
 
   uint32_t codeSectionSize = 0;
 
   SectionRange range;
   if (StartsCodeSection(d.begin(), d.end(), &range)) {
     codeSectionSize = range.size;
   }
 
-  if (baselineEnabled && ionEnabled && !debugEnabled && CanUseExtraThreads() &&
-      (TieringBeneficial(codeSectionSize) || args_->forceTiering)) {
+  if (baselineEnabled && hasSecondTier && CanUseExtraThreads() &&
+      (TieringBeneficial(codeSectionSize) || forceTiering)) {
     mode_ = CompileMode::Tier1;
     tier_ = Tier::Baseline;
   } else {
     mode_ = CompileMode::Once;
-    tier_ = debugEnabled || !ionEnabled ? Tier::Baseline : Tier::Optimized;
+    tier_ = hasSecondTier ? Tier::Optimized : Tier::Baseline;
   }
 
-  optimizedBackend_ = OptimizedBackend::Ion;
-#ifdef ENABLE_WASM_CRANELIFT
-  if (craneliftFlag) {
-    optimizedBackend_ = OptimizedBackend::Cranelift;
-  }
-#endif
+  optimizedBackend_ = craneliftEnabled ? OptimizedBackend::Cranelift : OptimizedBackend::Ion;
 
   debug_ = debugEnabled ? DebugEnabled::True : DebugEnabled::False;
   gcTypes_ = gcEnabled;
   state_ = Computed;
 }
 
 template <class DecoderT>
 static bool DecodeFunctionBody(DecoderT& d, ModuleGenerator& mg,
--- a/js/src/wasm/WasmCraneliftCompile.cpp
+++ b/js/src/wasm/WasmCraneliftCompile.cpp
@@ -25,16 +25,24 @@
 #include "wasm/WasmGenerator.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
+bool wasm::CraneliftCanCompile() {
+#ifdef JS_CODEGEN_X64
+  return true;
+#else
+  return false;
+#endif
+}
+
 static inline SymbolicAddress ToSymbolicAddress(BD_SymbolicAddress bd) {
   switch (bd) {
     case BD_SymbolicAddress::GrowMemory:
       return SymbolicAddress::GrowMemory;
     case BD_SymbolicAddress::CurrentMemory:
       return SymbolicAddress::CurrentMemory;
     case BD_SymbolicAddress::FloorF32:
       return SymbolicAddress::FloorF;
@@ -269,16 +277,19 @@ const GlobalDesc* env_global(const Crane
 }
 
 bool wasm::CraneliftCompileFunctions(const ModuleEnvironment& env,
                                      LifoAlloc& lifo,
                                      const FuncCompileInputVector& inputs,
                                      CompiledCode* code,
                                      ExclusiveDeferredValidationState& dvs,
                                      UniqueChars* error) {
+
+  MOZ_RELEASE_ASSERT(CraneliftCanCompile());
+
   MOZ_ASSERT(env.tier() == Tier::Optimized);
   MOZ_ASSERT(env.optimizedBackend() == OptimizedBackend::Cranelift);
   MOZ_ASSERT(!env.isAsmJS());
 
   AutoCranelift compiler(env);
   if (!compiler.init()) {
     return false;
   }
--- a/js/src/wasm/WasmCraneliftCompile.h
+++ b/js/src/wasm/WasmCraneliftCompile.h
@@ -21,16 +21,18 @@
 
 #include "mozilla/Attributes.h"
 
 #include "wasm/WasmGenerator.h"
 
 namespace js {
 namespace wasm {
 
+MOZ_MUST_USE bool CraneliftCanCompile();
+
 // Generates code with Cranelift.
 MOZ_MUST_USE bool CraneliftCompileFunctions(
     const ModuleEnvironment& env, LifoAlloc& lifo,
     const FuncCompileInputVector& inputs, CompiledCode* code,
     ExclusiveDeferredValidationState& dvs, UniqueChars* error);
 
 }  // namespace wasm
 }  // namespace js
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -31,16 +31,17 @@
 #include "js/Printf.h"
 #include "js/PropertySpec.h"  // JS_{PS,FN}{,_END}
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/Interpreter.h"
 #include "vm/StringType.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmCompile.h"
+#include "wasm/WasmCraneliftCompile.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmStubs.h"
 #include "wasm/WasmValidate.h"
 
 #include "vm/ArrayBufferObject-inl.h"
@@ -97,42 +98,46 @@ bool wasm::HasCompilerSupport(JSContext*
   }
 
 #ifdef JS_SIMULATOR
   if (!Simulator::supportsAtomics()) {
     return false;
   }
 #endif
 
-  return BaselineCanCompile() || IonCanCompile();
+  return BaselineCanCompile() || IonCanCompile() || CraneliftCanCompile();
+}
+
+bool wasm::HasOptimizedCompilerTier(JSContext* cx) {
+  return (cx->options().wasmIon() && IonCanCompile()) ||
+         (cx->options().wasmCranelift() && CraneliftCanCompile());
 }
 
 // Return whether wasm compilation is allowed by prefs.  This check
 // only makes sense if HasCompilerSupport() is true.
 static bool HasAvailableCompilerTier(JSContext* cx) {
   return (cx->options().wasmBaseline() && BaselineCanCompile()) ||
-         (cx->options().wasmIon() && IonCanCompile());
+         HasOptimizedCompilerTier(cx);
 }
 
 bool wasm::HasSupport(JSContext* cx) {
   return cx->options().wasm() && HasCompilerSupport(cx) &&
          HasAvailableCompilerTier(cx);
 }
 
 bool wasm::HasStreamingSupport(JSContext* cx) {
   // This should match EnsureStreamSupport().
-
   return HasSupport(cx) &&
          cx->runtime()->offThreadPromiseState.ref().initialized() &&
          CanUseExtraThreads() && cx->runtime()->consumeStreamCallback &&
          cx->runtime()->reportStreamErrorCallback;
 }
 
 bool wasm::HasCachingSupport(JSContext* cx) {
-  return HasStreamingSupport(cx) && cx->options().wasmIon() && IonCanCompile();
+  return HasStreamingSupport(cx) && HasOptimizedCompilerTier(cx);
 }
 
 static bool ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v,
                                MutableHandleVal val) {
   switch (targetType.code()) {
     case ValType::I32: {
       int32_t i32;
       if (!ToInt32(cx, v, &i32)) {
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -35,16 +35,20 @@ class SharedArrayRawBuffer;
 namespace wasm {
 
 // Return whether WebAssembly can be compiled on this platform.
 // This must be checked and must be true to call any of the top-level wasm
 // eval/compile methods.
 
 bool HasCompilerSupport(JSContext* cx);
 
+// Return whether WebAssembly has support for an optimized compiler backend.
+
+bool HasOptimizedCompilerTier(JSContext* cx);
+
 // Return whether WebAssembly is supported on this platform. This determines
 // whether the WebAssembly object is exposed to JS and takes into account
 // configuration options that disable various modes.
 
 bool HasSupport(JSContext* cx);
 
 // Return whether WebAssembly streaming/caching is supported on this platform.
 // This takes into account prefs and necessary embedding callbacks.