Bug 1232205 - Wasm baseline: infrastructure. r=bbouvier, r=luke
authorLars T Hansen <lhansen@mozilla.com>
Tue, 14 Jun 2016 11:22:21 +0100
changeset 341799 893294e2a38721cca2347f567eb6776d4ab21bfb
parent 341798 75285ea7e4aae8fb7d239f58a97390d17fbff9bb
child 341800 5d7856a5357f3caed90111a198406634031886d7
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier, luke
bugs1232205
milestone50.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 1232205 - Wasm baseline: infrastructure. r=bbouvier, r=luke
js/src/asmjs/AsmJS.cpp
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmBaselineCompile.cpp
js/src/asmjs/WasmBaselineCompile.h
js/src/asmjs/WasmGenerator.cpp
js/src/asmjs/WasmGenerator.h
js/src/asmjs/WasmIonCompile.cpp
js/src/asmjs/WasmIonCompile.h
js/src/jsapi.h
js/src/moz.build
js/src/shell/js.cpp
js/src/vm/HelperThreads.cpp
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -2367,17 +2367,17 @@ IsSimdTuple(ModuleValidator& m, ParseNod
     if (CallArgListLength(pn) != GetSimdLanes(global->simdCtorType()))
         return false;
 
     *type = global->simdCtorType();
     return true;
 }
 
 static bool
-IsNumericLiteral(ModuleValidator& m, ParseNode* pn);
+IsNumericLiteral(ModuleValidator& m, ParseNode* pn, bool* isSimd = nullptr);
 
 static NumLit
 ExtractNumericLiteral(ModuleValidator& m, ParseNode* pn);
 
 static inline bool
 IsLiteralInt(ModuleValidator& m, ParseNode* pn, uint32_t* u32);
 
 static bool
@@ -2418,21 +2418,26 @@ IsSimdLiteral(ModuleValidator& m, ParseN
         arg = NextNode(arg);
     }
 
     MOZ_ASSERT(arg == nullptr);
     return true;
 }
 
 static bool
-IsNumericLiteral(ModuleValidator& m, ParseNode* pn)
-{
-    return IsNumericNonFloatLiteral(pn) ||
-           IsFloatLiteral(m, pn) ||
-           IsSimdLiteral(m, pn);
+IsNumericLiteral(ModuleValidator& m, ParseNode* pn, bool* isSimd)
+{
+    if (IsNumericNonFloatLiteral(pn) || IsFloatLiteral(m, pn))
+        return true;
+    if (IsSimdLiteral(m, pn)) {
+        if (isSimd)
+            *isSimd = true;
+        return true;
+    }
+    return false;
 }
 
 // The JS grammar treats -42 as -(42) (i.e., with separate grammar
 // productions) for the unary - and literal 42). However, the asm.js spec
 // recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
 // so fold the two potential parse nodes into a single double value.
 static double
 ExtractNumericNonFloatValue(ParseNode* pn, ParseNode** out = nullptr)
@@ -2751,19 +2756,23 @@ SimdToExpr(SimdType type, SimdOperation 
         break;
 
       default: break;
     }
     MOZ_CRASH("unexpected SIMD (type, operator) combination");
 }
 
 #undef CASE
-#undef I32CASE
-#undef F32CASE
-#undef B32CASE
+#undef I8x16CASE
+#undef I16x8CASE
+#undef I32x4CASE
+#undef F32x4CASE
+#undef B8x16CASE
+#undef B16x8CASE
+#undef B32x4CASE
 #undef ENUMERATE
 
 typedef Vector<PropertyName*, 4, SystemAllocPolicy> NameVector;
 
 // Encapsulates the building of an asm bytecode function from an asm.js function
 // source code, packing the asm.js code into the asm bytecode form that can
 // be decoded and compiled with a FunctionCompiler.
 class MOZ_STACK_CLASS FunctionValidator
@@ -2827,16 +2836,23 @@ class MOZ_STACK_CLASS FunctionValidator
     }
 
     bool finish(uint32_t funcIndex) {
         MOZ_ASSERT(!blockDepth_);
         MOZ_ASSERT(breakableStack_.empty());
         MOZ_ASSERT(continuableStack_.empty());
         MOZ_ASSERT(breakLabels_.empty());
         MOZ_ASSERT(continueLabels_.empty());
+        for (auto iter = locals_.all(); !iter.empty(); iter.popFront()) {
+            if (iter.front().value().type.isSimd()) {
+                setUsesSimd();
+                break;
+            }
+        }
+
         return m_.mg().finishFuncDef(funcIndex, &fg_);
     }
 
     bool fail(ParseNode* pn, const char* str) {
         return m_.fail(pn, str);
     }
 
     bool failf(ParseNode* pn, const char* fmt, ...) {
@@ -2846,16 +2862,26 @@ class MOZ_STACK_CLASS FunctionValidator
         va_end(ap);
         return false;
     }
 
     bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
         return m_.failName(pn, fmt, name);
     }
 
+    /***************************************************** Attributes */
+
+    void setUsesSimd() {
+        fg_.setUsesSimd();
+    }
+
+    void setUsesAtomics() {
+        fg_.setUsesAtomics();
+    }
+
     /***************************************************** Local scope setup */
 
     bool addLocal(ParseNode* pn, PropertyName* name, Type type) {
         LocalMap::AddPtr p = locals_.lookupForAdd(name);
         if (p)
             return failName(pn, "duplicate local name '%s' not allowed", name);
         return locals_.add(p, name, Local(type, locals_.count()));
     }
@@ -3721,18 +3747,22 @@ IsLiteralOrConst(FunctionValidator& f, P
         const ModuleValidator::Global* global = f.lookupGlobal(pn->name());
         if (!global || global->which() != ModuleValidator::Global::ConstantLiteral)
             return false;
 
         *lit = global->constLiteralValue();
         return true;
     }
 
-    if (!IsNumericLiteral(f.m(), pn))
-        return false;
+    bool isSimd = false;
+    if (!IsNumericLiteral(f.m(), pn, &isSimd))
+        return false;
+
+    if (isSimd)
+        f.setUsesSimd();
 
     *lit = ExtractNumericLiteral(f.m(), pn);
     return true;
 }
 
 static bool
 CheckFinalReturn(FunctionValidator& f, ParseNode* lastNonEmptyStmt)
 {
@@ -4523,16 +4553,18 @@ CheckAtomicsExchange(FunctionValidator& 
     *type = Type::Int;
     return true;
 }
 
 static bool
 CheckAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSAtomicsBuiltinFunction func,
                         Type* type)
 {
+    f.setUsesAtomics();
+
     switch (func) {
       case AsmJSAtomicsBuiltin_compareExchange:
         return CheckAtomicsCompareExchange(f, callNode, type);
       case AsmJSAtomicsBuiltin_exchange:
         return CheckAtomicsExchange(f, callNode, type);
       case AsmJSAtomicsBuiltin_load:
         return CheckAtomicsLoad(f, callNode, type);
       case AsmJSAtomicsBuiltin_store:
@@ -5445,16 +5477,18 @@ CheckSimdSplat(FunctionValidator& f, Par
     *type = opType;
     return true;
 }
 
 static bool
 CheckSimdOperationCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
                        Type* type)
 {
+    f.setUsesSimd();
+
     MOZ_ASSERT(global->isSimdOperation());
 
     SimdType opType = global->simdOperationType();
 
     switch (SimdOperation op = global->simdOperation()) {
       case SimdOperation::Fn_check:
         return CheckSimdCheck(f, call, opType, type);
 
@@ -5537,16 +5571,18 @@ CheckSimdOperationCall(FunctionValidator
     }
     MOZ_CRASH("unexpected simd operation in CheckSimdOperationCall");
 }
 
 static bool
 CheckSimdCtorCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
                   Type* type)
 {
+    f.setUsesSimd();
+
     MOZ_ASSERT(call->isKind(PNK_CALL));
 
     SimdType simdType = global->simdCtorType();
     unsigned length = GetSimdLanes(simdType);
     if (!CheckSimdCallArgs(f, call, length, CheckSimdScalarArgs(simdType)))
         return false;
 
     if (!f.writeSimdOp(simdType, SimdOperation::Constructor))
@@ -5669,17 +5705,20 @@ CheckCoercedAtomicsBuiltinCall(FunctionV
 
 static bool
 CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type)
 {
     MOZ_ASSERT(ret.isCanonical());
 
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
-    if (IsNumericLiteral(f.m(), call)) {
+    bool isSimd = false;
+    if (IsNumericLiteral(f.m(), call, &isSimd)) {
+        if (isSimd)
+            f.setUsesSimd();
         NumLit lit = ExtractNumericLiteral(f.m(), call);
         if (!f.writeConstExpr(lit))
             return false;
         return CoerceResult(f, call, ret, Type::lit(lit), type);
     }
 
     ParseNode* callee = CallCallee(call);
 
@@ -6208,18 +6247,22 @@ CheckBitwise(FunctionValidator& f, Parse
     return true;
 }
 
 static bool
 CheckExpr(FunctionValidator& f, ParseNode* expr, Type* type)
 {
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
-    if (IsNumericLiteral(f.m(), expr))
+    bool isSimd = false;
+    if (IsNumericLiteral(f.m(), expr, &isSimd)) {
+        if (isSimd)
+            f.setUsesSimd();
         return CheckNumericLiteral(f, expr, type);
+    }
 
     switch (expr->getKind()) {
       case PNK_NAME:        return CheckVarRef(f, expr, type);
       case PNK_ELEM:        return CheckLoadArray(f, expr, type);
       case PNK_ASSIGN:      return CheckAssign(f, expr, type);
       case PNK_POS:         return CheckPos(f, expr, type);
       case PNK_NOT:         return CheckNot(f, expr, type);
       case PNK_NEG:         return CheckNeg(f, expr, type);
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -121,19 +121,18 @@ static bool
 CheckValType(JSContext* cx, Decoder& d, ValType type)
 {
     switch (type) {
       case ValType::I32:
       case ValType::F32:
       case ValType::F64:
         return true;
       case ValType::I64:
-#ifndef JS_CPU_X64
-        return Fail(cx, d, "i64 NYI on this platform");
-#endif
+        if (!IsI64Implemented())
+            return Fail(cx, d, "i64 NYI on this platform");
         return true;
       default:
         // Note: it's important not to remove this default since readValType()
         // can return ValType values for which there is no enumerator.
         break;
     }
 
     return Fail(cx, d, "bad type");
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "asmjs/WasmBaselineCompile.h"
+
+bool
+wasm::BaselineCanCompile(const FunctionGenerator* fg)
+{
+    return false;
+}
+
+bool
+wasm::BaselineCompileFunction(IonCompileTask* task)
+{
+    MOZ_ASSERT(task->mode() == IonCompileTask::CompileMode::Baseline);
+
+    MOZ_CRASH("NYI");
+}
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmBaselineCompile.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef asmjs_wasm_baseline_compile_h
+#define asmjs_wasm_baseline_compile_h
+
+#include "asmjs/WasmBinary.h"
+#include "asmjs/WasmIonCompile.h"
+
+namespace js {
+namespace wasm {
+
+class FunctionGenerator;
+
+// Return true if BaselineCompileFunction can generate code for the
+// function held in the FunctionGenerator.  If false is returned a
+// different compilation strategy must be chosen.
+//
+// This allows the baseline compiler to have different capabilities on
+// different platforms and defer to the full Ion compiler if
+// capabilities are missing.  The FunctionGenerator and other data
+// structures contain information about the capabilities that are
+// required to compile the function.
+bool
+BaselineCanCompile(const FunctionGenerator* fg);
+
+// Generate adequate code quickly.
+bool
+BaselineCompileFunction(IonCompileTask* task);
+
+} // namespace wasm
+} // namespace js
+
+#endif // asmjs_wasm_baseline_compile_h
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -15,16 +15,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "asmjs/WasmGenerator.h"
 
 #include "mozilla/EnumeratedRange.h"
 
+#include "asmjs/WasmBaselineCompile.h"
 #include "asmjs/WasmIonCompile.h"
 #include "asmjs/WasmStubs.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
@@ -797,24 +798,28 @@ ModuleGenerator::finishFuncDef(uint32_t 
     auto func = js::MakeUnique<FuncBytes>(Move(fg->bytes_),
                                           funcIndex,
                                           funcSig(funcIndex),
                                           fg->lineOrBytecode_,
                                           Move(fg->callSiteLineNums_));
     if (!func)
         return false;
 
-    fg->task_->init(Move(func));
+    JSRuntime* rt = cx_->compartment()->runtimeFromAnyThread();
+    bool baselineCompile = rt->options().wasmAlwaysBaseline() && BaselineCanCompile(fg);
+
+    fg->task_->init(Move(func), baselineCompile ? IonCompileTask::CompileMode::Baseline
+                                                : IonCompileTask::CompileMode::Ion);
 
     if (parallel_) {
         if (!StartOffThreadWasmCompile(cx_, fg->task_))
             return false;
         outstanding_++;
     } else {
-        if (!IonCompileFunction(fg->task_))
+        if (!CompileFunction(fg->task_))
             return false;
         if (!finishTask(fg->task_))
             return false;
     }
 
     fg->m_ = nullptr;
     fg->task_ = nullptr;
     activeFunc_ = nullptr;
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -207,29 +207,49 @@ class MOZ_STACK_CLASS ModuleGenerator
 // started.
 
 class MOZ_STACK_CLASS FunctionGenerator
 {
     friend class ModuleGenerator;
 
     ModuleGenerator* m_;
     IonCompileTask*  task_;
+    bool             usesSimd_;
+    bool             usesAtomics_;
 
     // Data created during function generation, then handed over to the
     // FuncBytes in ModuleGenerator::finishFunc().
     Bytes            bytes_;
     Uint32Vector     callSiteLineNums_;
 
     uint32_t lineOrBytecode_;
 
   public:
     FunctionGenerator()
-      : m_(nullptr), task_(nullptr), lineOrBytecode_(0)
+      : m_(nullptr), task_(nullptr), usesSimd_(false), usesAtomics_(false), lineOrBytecode_(0)
     {}
 
+    bool usesSimd() const {
+        return usesSimd_;
+    }
+    void setUsesSimd() {
+        usesSimd_ = true;
+    }
+
+    bool usesAtomics() const {
+        return usesAtomics_;
+    }
+    void setUsesAtomics() {
+        usesAtomics_ = true;
+    }
+
+    bool usesSignalsForInterrupts() const {
+        return m_->args().useSignalHandlersForInterrupt;
+    }
+
     Bytes& bytes() {
         return bytes_;
     }
     MOZ_MUST_USE bool addCallSiteLineNum(uint32_t lineno) {
         return callSiteLineNums_.append(lineno);
     }
 };
 
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -13,16 +13,17 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "asmjs/WasmIonCompile.h"
 
+#include "asmjs/WasmBaselineCompile.h"
 #include "asmjs/WasmBinaryIterator.h"
 #include "asmjs/WasmGenerator.h"
 
 #include "jit/CodeGenerator.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
@@ -3388,16 +3389,18 @@ EmitExpr(FunctionCompiler& f)
     }
 
     MOZ_CRASH("unexpected wasm opcode");
 }
 
 bool
 wasm::IonCompileFunction(IonCompileTask* task)
 {
+    MOZ_ASSERT(task->mode() == IonCompileTask::CompileMode::Ion);
+
     const FuncBytes& func = task->func();
     FuncCompileResults& results = task->results();
 
     Decoder d(func.bytes());
 
     // Build the local types vector.
 
     ValTypeVector locals;
@@ -3458,8 +3461,23 @@ wasm::IonCompileFunction(IonCompileTask*
 
         CodeGenerator codegen(&mir, lir, &results.masm());
         if (!codegen.generateWasm(sigIndex, &results.offsets()))
             return false;
     }
 
     return true;
 }
+
+bool
+wasm::CompileFunction(IonCompileTask* task)
+{
+    switch (task->mode()) {
+      case wasm::IonCompileTask::CompileMode::Ion:
+        return wasm::IonCompileFunction(task);
+      case wasm::IonCompileTask::CompileMode::Baseline:
+        return wasm::BaselineCompileFunction(task);
+      case wasm::IonCompileTask::CompileMode::None:
+        MOZ_CRASH("Uninitialized task");
+    }
+    // Silence gcc 5.2.1 warning.
+    return false;
+}
--- a/js/src/asmjs/WasmIonCompile.h
+++ b/js/src/asmjs/WasmIonCompile.h
@@ -93,58 +93,71 @@ class FuncCompileResults
 // IonCompileTask is filled with the wasm code to be compiled on the main
 // validation thread, sent off to an Ion compilation helper thread which creates
 // the FuncCompileResults, and finally sent back to the validation thread. To
 // save time allocating and freeing memory, IonCompileTasks are reset() and
 // reused.
 
 class IonCompileTask
 {
+  public:
+    enum class CompileMode { None, Baseline, Ion };
+
+  private:
     JSRuntime* const           runtime_;
     const ModuleGeneratorData& mg_;
     LifoAlloc                  lifo_;
     UniqueFuncBytes            func_;
+    CompileMode                mode_;
     Maybe<FuncCompileResults>  results_;
 
     IonCompileTask(const IonCompileTask&) = delete;
     IonCompileTask& operator=(const IonCompileTask&) = delete;
 
   public:
     IonCompileTask(JSRuntime* rt, const ModuleGeneratorData& mg, size_t defaultChunkSize)
-      : runtime_(rt), mg_(mg), lifo_(defaultChunkSize), func_(nullptr)
+      : runtime_(rt), mg_(mg), lifo_(defaultChunkSize), func_(nullptr), mode_(CompileMode::None)
     {}
     JSRuntime* runtime() const {
         return runtime_;
     }
     LifoAlloc& lifo() {
         return lifo_;
     }
     const ModuleGeneratorData& mg() const {
         return mg_;
     }
-    void init(UniqueFuncBytes func) {
+    void init(UniqueFuncBytes func, CompileMode mode) {
         MOZ_ASSERT(!func_);
         func_ = Move(func);
         results_.emplace(lifo_);
+        mode_ = mode;
+    }
+    CompileMode mode() const {
+        return mode_;
     }
     const FuncBytes& func() const {
         MOZ_ASSERT(func_);
         return *func_;
     }
     FuncCompileResults& results() {
         return *results_;
     }
     void reset(Bytes* recycled) {
         if (func_)
             *recycled = Move(func_->bytes());
         func_.reset(nullptr);
         results_.reset();
         lifo_.releaseAll();
+        mode_ = CompileMode::None;
     }
 };
 
 MOZ_MUST_USE bool
 IonCompileFunction(IonCompileTask* task);
 
+bool
+CompileFunction(IonCompileTask* task);
+
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_ion_compile_h
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1092,16 +1092,17 @@ namespace JS {
 
 class JS_PUBLIC_API(RuntimeOptions) {
   public:
     RuntimeOptions()
       : baseline_(true),
         ion_(true),
         asmJS_(true),
         wasm_(false),
+        wasmAlwaysBaseline_(false),
         throwOnAsmJSValidationFailure_(false),
         nativeRegExp_(true),
         unboxedArrays_(false),
         asyncStack_(true),
         throwOnDebuggeeWouldRun_(true),
         dumpStackOnDebuggeeWouldRun_(false),
         werror_(false),
         strictMode_(false),
@@ -1144,16 +1145,26 @@ class JS_PUBLIC_API(RuntimeOptions) {
         wasm_ = flag;
         return *this;
     }
     RuntimeOptions& toggleWasm() {
         wasm_ = !wasm_;
         return *this;
     }
 
+    bool wasmAlwaysBaseline() const { return wasmAlwaysBaseline_; }
+    RuntimeOptions& setWasmAlwaysBaseline(bool flag) {
+        wasmAlwaysBaseline_ = flag;
+        return *this;
+    }
+    RuntimeOptions& toggleWasmAlwaysBaseline() {
+        wasmAlwaysBaseline_ = !wasmAlwaysBaseline_;
+        return *this;
+    }
+
     bool throwOnAsmJSValidationFailure() const { return throwOnAsmJSValidationFailure_; }
     RuntimeOptions& setThrowOnAsmJSValidationFailure(bool flag) {
         throwOnAsmJSValidationFailure_ = flag;
         return *this;
     }
     RuntimeOptions& toggleThrowOnAsmJSValidationFailure() {
         throwOnAsmJSValidationFailure_ = !throwOnAsmJSValidationFailure_;
         return *this;
@@ -1219,16 +1230,17 @@ class JS_PUBLIC_API(RuntimeOptions) {
         return *this;
     }
 
   private:
     bool baseline_ : 1;
     bool ion_ : 1;
     bool asmJS_ : 1;
     bool wasm_ : 1;
+    bool wasmAlwaysBaseline_ : 1;
     bool throwOnAsmJSValidationFailure_ : 1;
     bool nativeRegExp_ : 1;
     bool unboxedArrays_ : 1;
     bool asyncStack_ : 1;
     bool throwOnDebuggeeWouldRun_ : 1;
     bool dumpStackOnDebuggeeWouldRun_ : 1;
     bool werror_ : 1;
     bool strictMode_ : 1;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -150,16 +150,17 @@ EXPORTS.js += [
     '../public/Value.h',
     '../public/Vector.h',
     '../public/WeakMapPtr.h',
 ]
 
 UNIFIED_SOURCES += [
     'asmjs/AsmJS.cpp',
     'asmjs/Wasm.cpp',
+    'asmjs/WasmBaselineCompile.cpp',
     'asmjs/WasmBinary.cpp',
     'asmjs/WasmBinaryIterator.cpp',
     'asmjs/WasmBinaryToAST.cpp',
     'asmjs/WasmBinaryToExperimentalText.cpp',
     'asmjs/WasmBinaryToText.cpp',
     'asmjs/WasmCode.cpp',
     'asmjs/WasmFrameIterator.cpp',
     'asmjs/WasmGenerator.cpp',
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -200,16 +200,17 @@ static bool enableCodeCoverage = false;
 static bool enableDisassemblyDumps = false;
 static bool offthreadCompilation = false;
 static bool enableBaseline = false;
 static bool enableIon = false;
 static bool enableAsmJS = false;
 static bool enableNativeRegExp = false;
 static bool enableUnboxedArrays = false;
 static bool enableSharedMemory = SHARED_MEMORY_DEFAULT;
+static bool enableWasmAlwaysBaseline = false;
 #ifdef JS_GC_ZEAL
 static uint32_t gZealBits = 0;
 static uint32_t gZealFrequency = 0;
 #endif
 static bool printTiming = false;
 static const char* jsCacheDir = nullptr;
 static const char* jsCacheAsmJSPath = nullptr;
 static RCFile* gErrFile = nullptr;
@@ -6778,21 +6779,23 @@ ProcessArgs(JSContext* cx, OptionParser*
 static bool
 SetRuntimeOptions(JSRuntime* rt, const OptionParser& op)
 {
     enableBaseline = !op.getBoolOption("no-baseline");
     enableIon = !op.getBoolOption("no-ion");
     enableAsmJS = !op.getBoolOption("no-asmjs");
     enableNativeRegExp = !op.getBoolOption("no-native-regexp");
     enableUnboxedArrays = op.getBoolOption("unboxed-arrays");
+    enableWasmAlwaysBaseline = op.getBoolOption("wasm-always-baseline");
 
     JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline)
                              .setIon(enableIon)
                              .setAsmJS(enableAsmJS)
                              .setWasm(true)
+                             .setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
                              .setNativeRegExp(enableNativeRegExp)
                              .setUnboxedArrays(enableUnboxedArrays);
 
     if (op.getBoolOption("no-unboxed-objects"))
         jit::JitOptions.disableUnboxedObjects = true;
 
     if (const char* str = op.getStringOption("ion-scalar-replacement")) {
         if (strcmp(str, "on") == 0)
@@ -7051,16 +7054,17 @@ SetRuntimeOptions(JSRuntime* rt, const O
 static void
 SetWorkerRuntimeOptions(JSRuntime* rt)
 {
     // Copy option values from the main thread.
     JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline)
                              .setIon(enableIon)
                              .setAsmJS(enableAsmJS)
                              .setWasm(true)
+                             .setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
                              .setNativeRegExp(enableNativeRegExp)
                              .setUnboxedArrays(enableUnboxedArrays);
     rt->setOffthreadIonCompilationEnabled(offthreadCompilation);
     rt->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
 
 #ifdef JS_GC_ZEAL
     if (gZealBits && gZealFrequency) {
 #define ZEAL_MODE(_, value)                        \
@@ -7238,16 +7242,17 @@ main(int argc, char** argv, char** envp)
         || !op.addIntOption('\0', "thread-count", "COUNT", "Use COUNT auxiliary threads "
                             "(default: # of cores - 1)", -1)
         || !op.addBoolOption('\0', "ion", "Enable IonMonkey (default)")
         || !op.addBoolOption('\0', "no-ion", "Disable IonMonkey")
         || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation")
         || !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation")
         || !op.addBoolOption('\0', "no-unboxed-objects", "Disable creating unboxed plain objects")
         || !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays")
+        || !op.addBoolOption('\0', "wasm-always-baseline", "Enable experimental Wasm baseline compiler when possible")
 #ifdef ENABLE_SHARED_ARRAY_BUFFER
         || !op.addStringOption('\0', "shared-memory", "on/off",
                                "SharedArrayBuffer and Atomics "
 #  if SHARED_MEMORY_DEFAULT
                                "(default: on, off to disable)"
 #  else
                                "(default: off, on to enable)"
 #  endif
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1380,17 +1380,17 @@ HelperThread::handleWasmWorkload()
     wasm::IonCompileTask* task = wasmTask();
     {
         AutoUnlockHelperThreadState unlock;
 
         TraceLoggerThread* logger = TraceLoggerForCurrentThread();
         AutoTraceLog logCompile(logger, TraceLogger_WasmCompilation);
 
         PerThreadData::AutoEnterRuntime enter(threadData.ptr(), task->runtime());
-        success = wasm::IonCompileFunction(task);
+        success = wasm::CompileFunction(task);
     }
 
     // On success, try to move work to the finished list.
     if (success)
         success = HelperThreadState().wasmFinishedList().append(task);
 
     // On failure, note the failure for harvesting by the parent.
     if (!success)