Bug 992267: Odin basic SIMD support; r=luke
☠☠ backed out by 991b62ff5461 ☠ ☠
authorBenjamin Bouvier <benj@benj.me>
Thu, 28 Aug 2014 10:01:44 +0200
changeset 202383 fc9f83afab316d2463d4bd993f0e922c54f2788b
parent 202382 9afc72a12cb96edb53917224c0d1b638418a19a9
child 202384 d9f97b62e3c4c97a069b97e6fa388444c6391778
push id48414
push userlwagner@mozilla.com
push dateFri, 29 Aug 2014 18:59:15 +0000
treeherdermozilla-inbound@fc9f83afab31 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs992267
milestone34.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 992267: Odin basic SIMD support; r=luke * * * Bug 992267: Add SIMD globals support to Odin; r=luke
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSModule.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSValidate.cpp
js/src/builtin/SIMD.cpp
js/src/builtin/SIMD.h
js/src/jit-test/lib/asm.js
js/src/jit-test/tests/asm.js/testSIMD.js
js/src/jit/Lowering.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/shared/BaseAssembler-x86-shared.h
js/src/jit/shared/CodeGenerator-x86-shared.cpp
js/src/jit/shared/Lowering-shared-inl.h
js/src/jit/x64/Assembler-x64.cpp
js/src/jit/x64/Assembler-x64.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x86/Assembler-x86.cpp
js/src/jit/x86/Assembler-x86.h
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/js.msg
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -25,16 +25,17 @@
 #endif
 
 #include "jscntxt.h"
 #include "jsmath.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 
 #include "asmjs/AsmJSModule.h"
+#include "builtin/SIMD.h"
 #include "frontend/BytecodeCompiler.h"
 #include "jit/Ion.h"
 #include "jit/JitCommon.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "vm/StringBuffer.h"
 
@@ -92,81 +93,107 @@ GetDataProperty(JSContext *cx, HandleVal
     if (desc.hasGetterOrSetterObject())
         return LinkFail(cx, "property is not a data property");
 
     v.set(desc.value());
     return true;
 }
 
 static bool
+HasPureCoercion(JSContext *cx, HandleValue v)
+{
+    if (IsVectorObject<Int32x4>(v) || IsVectorObject<Float32x4>(v))
+        return true;
+
+    // Ideally, we'd reject all non-SIMD non-primitives, but Emscripten has a
+    // bug that generates code that passes functions for some imports. To avoid
+    // breaking all the code that contains this bug, we make an exception for
+    // functions that don't have user-defined valueOf or toString, for their
+    // coercions are not observable and coercion via ToNumber/ToInt32
+    // definitely produces NaN/0. We should remove this special case later once
+    // most apps have been built with newer Emscripten.
+    jsid toString = NameToId(cx->names().toString);
+    if (v.toObject().is<JSFunction>() &&
+        HasObjectValueOf(&v.toObject(), cx) &&
+        ClassMethodIsNative(cx, &v.toObject(), &JSFunction::class_, toString, fun_toString))
+    {
+        return true;
+    }
+
+    return false;
+}
+
+static bool
 ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Global &global,
                        HandleValue importVal)
 {
     JS_ASSERT(global.which() == AsmJSModule::Global::Variable);
 
-    void *datum = module.globalVarIndexToGlobalDatum(global.varIndex());
+    void *datum = module.globalVarToGlobalDatum(global);
 
     switch (global.varInitKind()) {
       case AsmJSModule::Global::InitConstant: {
         const AsmJSNumLit &lit = global.varInitNumLit();
-        const Value &v = lit.value();
         switch (lit.which()) {
           case AsmJSNumLit::Fixnum:
           case AsmJSNumLit::NegativeInt:
           case AsmJSNumLit::BigUnsigned:
-            *(int32_t *)datum = v.toInt32();
+            *(int32_t *)datum = lit.scalarValue().toInt32();
             break;
           case AsmJSNumLit::Double:
-            *(double *)datum = v.toDouble();
+            *(double *)datum = lit.scalarValue().toDouble();
             break;
           case AsmJSNumLit::Float:
-            *(float *)datum = static_cast<float>(v.toDouble());
+            *(float *)datum = static_cast<float>(lit.scalarValue().toDouble());
+            break;
+          case AsmJSNumLit::Int32x4:
+            memcpy(datum, lit.simdValue().asInt32x4(), Simd128DataSize);
+            break;
+          case AsmJSNumLit::Float32x4:
+            memcpy(datum, lit.simdValue().asFloat32x4(), Simd128DataSize);
             break;
           case AsmJSNumLit::OutOfRangeInt:
             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("OutOfRangeInt isn't valid in the first place");
         }
         break;
       }
+
       case AsmJSModule::Global::InitImport: {
         RootedPropertyName field(cx, global.varImportField());
         RootedValue v(cx);
         if (!GetDataProperty(cx, importVal, field, &v))
             return false;
 
-        if (!v.isPrimitive()) {
-            // Ideally, we'd reject all non-primitives, but Emscripten has a bug
-            // that generates code that passes functions for some imports. To
-            // avoid breaking all the code that contains this bug, we make an
-            // exception for functions that don't have user-defined valueOf or
-            // toString, for their coercions are not observable and coercion via
-            // ToNumber/ToInt32 definitely produces NaN/0. We should remove this
-            // special case later once most apps have been built with newer
-            // Emscripten.
-            jsid toString = NameToId(cx->names().toString);
-            if (!v.toObject().is<JSFunction>() ||
-                !HasObjectValueOf(&v.toObject(), cx) ||
-                !ClassMethodIsNative(cx, &v.toObject(), &JSFunction::class_, toString, fun_toString))
-            {
-                return LinkFail(cx, "Imported values must be primitives");
-            }
-        }
+        if (!v.isPrimitive() && !HasPureCoercion(cx, v))
+            return LinkFail(cx, "Imported values must be primitives");
 
+        SimdConstant simdConstant;
         switch (global.varInitCoercion()) {
           case AsmJS_ToInt32:
             if (!ToInt32(cx, v, (int32_t *)datum))
                 return false;
             break;
           case AsmJS_ToNumber:
             if (!ToNumber(cx, v, (double *)datum))
                 return false;
             break;
           case AsmJS_FRound:
             if (!RoundFloat32(cx, v, (float *)datum))
                 return false;
             break;
+          case AsmJS_ToInt32x4:
+            if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant))
+                return false;
+            memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
+            break;
+          case AsmJS_ToFloat32x4:
+            if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
+                return false;
+            memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize);
+            break;
         }
         break;
       }
     }
 
     return true;
 }
 
@@ -235,16 +262,113 @@ ValidateMathBuiltinFunction(JSContext *c
     }
 
     if (!IsNativeFunction(v, native))
         return LinkFail(cx, "bad Math.* builtin function");
 
     return true;
 }
 
+static PropertyName *
+SimdTypeToName(JSContext *cx, AsmJSSimdType type)
+{
+    switch (type) {
+      case AsmJSSimdType_int32x4:   return cx->names().int32x4;
+      case AsmJSSimdType_float32x4: return cx->names().float32x4;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
+}
+
+static X4TypeDescr::Type
+AsmJSSimdTypeToTypeDescrType(AsmJSSimdType type)
+{
+    switch (type) {
+      case AsmJSSimdType_int32x4: return Int32x4::type;
+      case AsmJSSimdType_float32x4: return Float32x4::type;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSSimdType");
+}
+
+static bool
+ValidateSimdType(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal,
+                 MutableHandleValue out)
+{
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, globalVal, cx->names().SIMD, &v))
+        return false;
+
+    AsmJSSimdType type;
+    if (global.which() == AsmJSModule::Global::SimdCtor)
+        type = global.simdCtorType();
+    else
+        type = global.simdOperationType();
+
+    RootedPropertyName simdTypeName(cx, SimdTypeToName(cx, type));
+    if (!GetDataProperty(cx, v, simdTypeName, &v))
+        return false;
+
+    if (!v.isObject())
+        return LinkFail(cx, "bad SIMD type");
+
+    RootedObject x4desc(cx, &v.toObject());
+    if (!x4desc->is<X4TypeDescr>())
+        return LinkFail(cx, "bad SIMD type");
+
+    if (AsmJSSimdTypeToTypeDescrType(type) != x4desc->as<X4TypeDescr>().type())
+        return LinkFail(cx, "bad SIMD type");
+
+    out.set(v);
+    return true;
+}
+
+static bool
+ValidateSimdType(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
+{
+    RootedValue _(cx);
+    return ValidateSimdType(cx, global, globalVal, &_);
+}
+
+static bool
+ValidateSimdOperation(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
+{
+    // SIMD operations are loaded from the SIMD type, so the type must have been
+    // validated before the operation.
+    RootedValue v(cx);
+    JS_ALWAYS_TRUE(ValidateSimdType(cx, global, globalVal, &v));
+
+    RootedPropertyName opName(cx, global.simdOperationName());
+    if (!GetDataProperty(cx, v, opName, &v))
+        return false;
+
+    Native native = nullptr;
+    switch (global.simdOperationType()) {
+      case AsmJSSimdType_int32x4:
+        switch (global.simdOperation()) {
+          case AsmJSSimdOperation_add: native = simd_int32x4_add; break;
+          case AsmJSSimdOperation_sub: native = simd_int32x4_sub; break;
+          case AsmJSSimdOperation_mul:
+          case AsmJSSimdOperation_div:
+            MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Mul and div shouldn't have been validated in "
+                                                    "the first place");
+        }
+        break;
+      case AsmJSSimdType_float32x4:
+        switch (global.simdOperation()) {
+          case AsmJSSimdOperation_add: native = simd_float32x4_add; break;
+          case AsmJSSimdOperation_sub: native = simd_float32x4_sub; break;
+          case AsmJSSimdOperation_mul: native = simd_float32x4_mul; break;
+          case AsmJSSimdOperation_div: native = simd_float32x4_div; break;
+        }
+        break;
+    }
+    if (!native || !IsNativeFunction(v, native))
+        return LinkFail(cx, "bad SIMD.type.* operation");
+    return true;
+}
+
 static bool
 ValidateConstant(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
 {
     RootedPropertyName field(cx, global.constantName());
     RootedValue v(cx, globalVal);
 
     if (global.constantKind() == AsmJSModule::Global::MathConstant) {
         if (!GetDataProperty(cx, v, cx->names().Math, &v))
@@ -371,16 +495,24 @@ DynamicallyLinkModule(JSContext *cx, Cal
           case AsmJSModule::Global::MathBuiltinFunction:
             if (!ValidateMathBuiltinFunction(cx, global, globalVal))
                 return false;
             break;
           case AsmJSModule::Global::Constant:
             if (!ValidateConstant(cx, global, globalVal))
                 return false;
             break;
+          case AsmJSModule::Global::SimdCtor:
+            if (!ValidateSimdType(cx, global, globalVal))
+                return false;
+            break;
+          case AsmJSModule::Global::SimdOperation:
+            if (!ValidateSimdOperation(cx, global, globalVal))
+                return false;
+            break;
         }
     }
 
     for (unsigned i = 0; i < module.numExits(); i++)
         module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();
 
     module.initGlobalNaN();
 
@@ -431,24 +563,24 @@ CallAsmJS(JSContext *cx, unsigned argc, 
         module.setProfilingEnabled(cx->runtime()->spsProfiler.enabled(), cx);
 
     // An exported function points to the code as well as the exported
     // function's signature, which implies the dynamic coercions performed on
     // the arguments.
     const AsmJSModule::ExportedFunction &func = FunctionToExportedFunction(callee, module);
 
     // The calling convention for an external call into asm.js is to pass an
-    // array of 8-byte values where each value contains either a coerced int32
-    // (in the low word) or double value, with the coercions specified by the
-    // asm.js signature. The external entry point unpacks this array into the
-    // system-ABI-specified registers and stack memory and then calls into the
-    // internal entry point. The return value is stored in the first element of
-    // the array (which, therefore, must have length >= 1).
-
-    js::Vector<uint64_t, 8> coercedArgs(cx);
+    // array of 16-byte values where each value contains either a coerced int32
+    // (in the low word), a double value (in the low dword) or a SIMD vector
+    // value, with the coercions specified by the asm.js signature. The
+    // external entry point unpacks this array into the system-ABI-specified
+    // registers and stack memory and then calls into the internal entry point.
+    // The return value is stored in the first element of the array (which,
+    // therefore, must have length >= 1).
+    js::Vector<AsmJSModule::EntryArg, 8> coercedArgs(cx);
     if (!coercedArgs.resize(Max<size_t>(1, func.numArgs())))
         return false;
 
     RootedValue v(cx);
     for (unsigned i = 0; i < func.numArgs(); ++i) {
         v = i < callArgs.length() ? callArgs[i] : UndefinedValue();
         switch (func.argCoercion(i)) {
           case AsmJS_ToInt32:
@@ -458,16 +590,30 @@ CallAsmJS(JSContext *cx, unsigned argc, 
           case AsmJS_ToNumber:
             if (!ToNumber(cx, v, (double*)&coercedArgs[i]))
                 return false;
             break;
           case AsmJS_FRound:
             if (!RoundFloat32(cx, v, (float *)&coercedArgs[i]))
                 return false;
             break;
+          case AsmJS_ToInt32x4: {
+            SimdConstant simd;
+            if (!ToSimdConstant<Int32x4>(cx, v, &simd))
+                return false;
+            memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
+            break;
+          }
+          case AsmJS_ToFloat32x4: {
+            SimdConstant simd;
+            if (!ToSimdConstant<Float32x4>(cx, v, &simd))
+                return false;
+            memcpy(&coercedArgs[i], simd.asFloat32x4(), Simd128DataSize);
+            break;
+          }
         }
     }
 
     // An asm.js module is specialized to its heap's base address and length
     // which is normally immutable except for the neuter operation that occurs
     // when an ArrayBuffer is transfered. Throw an internal error if we're
     // about to run with a neutered heap.
     if (module.maybeHeapBufferObject() && module.maybeHeapBufferObject()->isNeutered()) {
@@ -495,26 +641,39 @@ CallAsmJS(JSContext *cx, unsigned argc, 
         // returns a primary type, which is the case for all asm.js exported
         // functions, the returned value is discarded and an empty object is
         // returned instead.
         JSObject *obj = NewBuiltinClassInstance(cx, &JSObject::class_);
         callArgs.rval().set(ObjectValue(*obj));
         return true;
     }
 
+    JSObject *x4obj;
     switch (func.returnType()) {
       case AsmJSModule::Return_Void:
         callArgs.rval().set(UndefinedValue());
         break;
       case AsmJSModule::Return_Int32:
         callArgs.rval().set(Int32Value(*(int32_t*)&coercedArgs[0]));
         break;
       case AsmJSModule::Return_Double:
         callArgs.rval().set(NumberValue(*(double*)&coercedArgs[0]));
         break;
+      case AsmJSModule::Return_Int32x4:
+        x4obj = CreateSimd<Int32x4>(cx, (int32_t*)&coercedArgs[0]);
+        if (!x4obj)
+            return false;
+        callArgs.rval().set(ObjectValue(*x4obj));
+        break;
+      case AsmJSModule::Return_Float32x4:
+        x4obj = CreateSimd<Float32x4>(cx, (float*)&coercedArgs[0]);
+        if (!x4obj)
+            return false;
+        callArgs.rval().set(ObjectValue(*x4obj));
+        break;
     }
 
     return true;
 }
 
 static JSFunction *
 NewExportedFunction(JSContext *cx, const AsmJSModule::ExportedFunction &func,
                     HandleObject moduleObj, unsigned exportIndex)
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -298,18 +298,18 @@ AsmJSModule::finish(ExclusiveContext *cx
     uint32_t endAfterCurly = tokenStream.peekTokenPos().end;
     JS_ASSERT(endBeforeCurly >= srcBodyStart_);
     JS_ASSERT(endAfterCurly >= srcBodyStart_);
     pod.srcLength_ = endBeforeCurly - srcStart_;
     pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_;
 
     // The global data section sits immediately after the executable (and
     // other) data allocated by the MacroAssembler, so ensure it is
-    // double-aligned.
-    pod.codeBytes_ = AlignBytes(masm.bytesNeeded(), sizeof(double));
+    // SIMD-aligned.
+    pod.codeBytes_ = AlignBytes(masm.bytesNeeded(), SimdStackAlignment);
 
     // The entire region is allocated via mmap/VirtualAlloc which requires
     // units of pages.
     pod.totalBytes_ = AlignBytes(pod.codeBytes_ + globalDataBytes(), AsmJSPageSize);
 
     JS_ASSERT(!code_);
     code_ = AllocateExecutableMemory(cx, pod.totalBytes_);
     if (!code_)
@@ -513,21 +513,21 @@ TryEnablingIon(JSContext *cx, AsmJSModul
     JSScript *script = fun->nonLazyScript();
     if (!script->hasIonScript())
         return true;
 
     // Currently we can't rectify arguments. Therefore disabling if argc is too low.
     if (fun->nargs() > size_t(argc))
         return true;
 
-    // Normally the types should corresond, since we just ran with those types,
+    // Normally the types should correspond, since we just ran with those types,
     // but there are reports this is asserting. Therefore doing it as a check, instead of DEBUG only.
     if (!types::TypeScript::ThisTypes(script)->hasType(types::Type::UndefinedType()))
         return true;
-    for(uint32_t i = 0; i < fun->nargs(); i++) {
+    for (uint32_t i = 0; i < fun->nargs(); i++) {
         types::StackTypeSet *typeset = types::TypeScript::ArgTypes(script, i);
         types::Type type = types::Type::DoubleType();
         if (!argv[i].isDouble())
             type = types::Type::PrimitiveType(argv[i].extractNonDoubleType());
         if (!typeset->hasType(type))
             return true;
     }
 
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -22,18 +22,20 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsscript.h"
 
 #include "asmjs/AsmJSFrameIterator.h"
 #include "asmjs/AsmJSValidate.h"
+#include "builtin/SIMD.h"
 #include "gc/Marking.h"
 #include "jit/IonMacroAssembler.h"
+#include "jit/IonTypes.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/RegisterSets.h"
 #include "jit/shared/Assembler-shared.h"
 #include "vm/TypedArrayObject.h"
 
 namespace js {
@@ -42,30 +44,48 @@ namespace frontend { class TokenStream; 
 
 using JS::GenericNaN;
 
 // These EcmaScript-defined coercions form the basis of the asm.js type system.
 enum AsmJSCoercion
 {
     AsmJS_ToInt32,
     AsmJS_ToNumber,
-    AsmJS_FRound
+    AsmJS_FRound,
+    AsmJS_ToInt32x4,
+    AsmJS_ToFloat32x4
 };
 
 // The asm.js spec recognizes this set of builtin Math functions.
 enum AsmJSMathBuiltinFunction
 {
     AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
     AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
     AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
     AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt,
     AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul,
     AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max
 };
 
+// Set of known global object SIMD's attributes, i.e. types
+enum AsmJSSimdType
+{
+    AsmJSSimdType_int32x4,
+    AsmJSSimdType_float32x4
+};
+
+// Set of known operations, for a given SIMD type (int32x4, float32x4,...)
+enum AsmJSSimdOperation
+{
+    AsmJSSimdOperation_add,
+    AsmJSSimdOperation_sub,
+    AsmJSSimdOperation_mul,
+    AsmJSSimdOperation_div
+};
+
 // These labels describe positions in the prologue/epilogue of functions while
 // compiling an AsmJSModule.
 struct AsmJSFunctionLabels
 {
     AsmJSFunctionLabels(jit::Label &entry, jit::Label &overflowExit)
       : entry(entry), overflowExit(overflowExit) {}
 
     jit::Label begin;
@@ -92,53 +112,76 @@ class AsmJSNumLit
 {
   public:
     enum Which {
         Fixnum,
         NegativeInt,
         BigUnsigned,
         Double,
         Float,
+        Int32x4,
+        Float32x4,
         OutOfRangeInt = -1
     };
 
   private:
     Which which_;
-    Value value_;
+    union {
+        Value scalar_;
+        jit::SimdConstant simd_;
+    } value;
 
   public:
     static AsmJSNumLit Create(Which w, Value v) {
         AsmJSNumLit lit;
         lit.which_ = w;
-        lit.value_ = v;
+        lit.value.scalar_ = v;
+        JS_ASSERT(!lit.isSimd());
+        return lit;
+    }
+
+    static AsmJSNumLit Create(Which w, jit::SimdConstant c) {
+        AsmJSNumLit lit;
+        lit.which_ = w;
+        lit.value.simd_ = c;
+        JS_ASSERT(lit.isSimd());
         return lit;
     }
 
     Which which() const {
         return which_;
     }
 
     int32_t toInt32() const {
         JS_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned);
-        return value_.toInt32();
+        return value.scalar_.toInt32();
     }
 
     double toDouble() const {
         JS_ASSERT(which_ == Double);
-        return value_.toDouble();
+        return value.scalar_.toDouble();
     }
 
     float toFloat() const {
         JS_ASSERT(which_ == Float);
-        return float(value_.toDouble());
+        return float(value.scalar_.toDouble());
+    }
+
+    Value scalarValue() const {
+        JS_ASSERT(which_ != OutOfRangeInt);
+        return value.scalar_;
     }
 
-    Value value() const {
-        JS_ASSERT(which_ != OutOfRangeInt);
-        return value_;
+    bool isSimd() const {
+        return which_ == Int32x4 || which_ == Float32x4;
+    }
+
+    const jit::SimdConstant &simdValue() const {
+        JS_ASSERT(isSimd());
+        return value.simd_;
     }
 
     bool hasType() const {
         return which_ != OutOfRangeInt;
     }
 };
 
 // An asm.js module represents the collection of functions nested inside a
@@ -152,17 +195,18 @@ class AsmJSNumLit
 //
 // NB: this means that AsmJSModule must be GC-safe.
 class AsmJSModule
 {
   public:
     class Global
     {
       public:
-        enum Which { Variable, FFI, ArrayView, MathBuiltinFunction, Constant };
+        enum Which { Variable, FFI, ArrayView, MathBuiltinFunction, Constant,
+                     SimdCtor, SimdOperation};
         enum VarInitKind { InitConstant, InitImport };
         enum ConstantKind { GlobalConstant, MathConstant };
 
       private:
         struct Pod {
             Which which_;
             union {
                 struct {
@@ -171,16 +215,21 @@ class AsmJSModule
                     union {
                         AsmJSCoercion coercion_;
                         AsmJSNumLit numLit_;
                     } u;
                 } var;
                 uint32_t ffiIndex_;
                 Scalar::Type viewType_;
                 AsmJSMathBuiltinFunction mathBuiltinFunc_;
+                AsmJSSimdType simdCtorType_;
+                struct {
+                    AsmJSSimdType type_;
+                    AsmJSSimdOperation which_;
+                } simdOp;
                 struct {
                     ConstantKind kind_;
                     double value_;
                 } constant;
             } u;
         } pod;
         PropertyName *name_;
 
@@ -191,17 +240,17 @@ class AsmJSModule
             name_ = name;
             JS_ASSERT_IF(name_, name_->isTenured());
         }
 
         void trace(JSTracer *trc) {
             if (name_)
                 MarkStringUnbarriered(trc, &name_, "asm.js global name");
             JS_ASSERT_IF(pod.which_ == Variable && pod.u.var.initKind_ == InitConstant,
-                         !pod.u.var.u.numLit_.value().isMarkable());
+                         !pod.u.var.u.numLit_.scalarValue().isMarkable());
         }
 
       public:
         Global() {}
         Which which() const {
             return pod.which_;
         }
         uint32_t varIndex() const {
@@ -246,16 +295,36 @@ class AsmJSModule
         PropertyName *mathName() const {
             JS_ASSERT(pod.which_ == MathBuiltinFunction);
             return name_;
         }
         AsmJSMathBuiltinFunction mathBuiltinFunction() const {
             JS_ASSERT(pod.which_ == MathBuiltinFunction);
             return pod.u.mathBuiltinFunc_;
         }
+        AsmJSSimdType simdCtorType() const {
+            JS_ASSERT(pod.which_ == SimdCtor);
+            return pod.u.simdCtorType_;
+        }
+        PropertyName *simdCtorName() const {
+            JS_ASSERT(pod.which_ == SimdCtor);
+            return name_;
+        }
+        PropertyName *simdOperationName() const {
+            JS_ASSERT(pod.which_ == SimdOperation);
+            return name_;
+        }
+        AsmJSSimdOperation simdOperation() const {
+            JS_ASSERT(pod.which_ == SimdOperation);
+            return pod.u.simdOp.which_;
+        }
+        AsmJSSimdType simdOperationType() const {
+            JS_ASSERT(pod.which_ == SimdOperation);
+            return pod.u.simdOp.type_;
+        }
         PropertyName *constantName() const {
             JS_ASSERT(pod.which_ == Constant);
             return name_;
         }
         ConstantKind constantKind() const {
             JS_ASSERT(pod.which_ == Constant);
             return pod.u.constant.kind_;
         }
@@ -304,30 +373,36 @@ class AsmJSModule
             ionCodeOffset_ = masm.actualOffset(ionCodeOffset_);
         }
 
         size_t serializedSize() const;
         uint8_t *serialize(uint8_t *cursor) const;
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
         bool clone(ExclusiveContext *cx, Exit *out) const;
     };
-    typedef int32_t (*CodePtr)(uint64_t *args, uint8_t *global);
+
+    struct EntryArg {
+        uint64_t lo;
+        uint64_t hi;
+    };
+    JS_STATIC_ASSERT(sizeof(EntryArg) >= jit::Simd128DataSize);
+    typedef int32_t (*CodePtr)(EntryArg *args, uint8_t *global);
 
     // An Exit holds bookkeeping information about an exit; the ExitDatum
     // struct overlays the actual runtime data stored in the global data
     // section.
     struct ExitDatum
     {
         uint8_t *exit;
         HeapPtrFunction fun;
     };
 
     typedef Vector<AsmJSCoercion, 0, SystemAllocPolicy> ArgCoercionVector;
 
-    enum ReturnType { Return_Int32, Return_Double, Return_Void };
+    enum ReturnType { Return_Int32, Return_Double, Return_Int32x4, Return_Float32x4, Return_Void };
 
     class ExportedFunction
     {
         PropertyName *name_;
         PropertyName *maybeFieldName_;
         ArgCoercionVector argCoercions_;
         struct Pod {
             ReturnType returnType_;
@@ -667,17 +742,18 @@ class AsmJSModule
 
   private:
     struct Pod {
         size_t                            funcPtrTableAndExitBytes_;
         size_t                            functionBytes_; // just the function bodies, no stubs
         size_t                            codeBytes_;     // function bodies and stubs
         size_t                            totalBytes_;    // function bodies, stubs, and global data
         uint32_t                          minHeapLength_;
-        uint32_t                          numGlobalVars_;
+        uint32_t                          numGlobalScalarVars_;
+        uint32_t                          numGlobalSimdVars_;
         uint32_t                          numFFIs_;
         uint32_t                          srcLength_;
         uint32_t                          srcLengthWithRightBrace_;
         bool                              strict_;
         bool                              hasArrayView_;
         bool                              usesSignalHandlers_;
     } pod;
 
@@ -814,30 +890,53 @@ class AsmJSModule
     PropertyName *importArgumentName() const {
         return importArgumentName_;
     }
     PropertyName *bufferArgumentName() const {
         return bufferArgumentName_;
     }
     bool addGlobalVarInit(const AsmJSNumLit &lit, uint32_t *globalIndex) {
         JS_ASSERT(!isFinishedWithModulePrologue());
-        if (pod.numGlobalVars_ == UINT32_MAX)
-            return false;
         Global g(Global::Variable, nullptr);
         g.pod.u.var.initKind_ = Global::InitConstant;
         g.pod.u.var.u.numLit_ = lit;
-        g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++;
+
+        if (lit.isSimd()) {
+            if (pod.numGlobalSimdVars_ == UINT32_MAX)
+                return false;
+            *globalIndex = pod.numGlobalSimdVars_++;
+        } else {
+            if (pod.numGlobalScalarVars_ == UINT32_MAX)
+                return false;
+            *globalIndex = pod.numGlobalScalarVars_++;
+        }
+
+        g.pod.u.var.index_ = *globalIndex;
         return globals_.append(g);
     }
+    static bool IsSimdCoercion(AsmJSCoercion c) {
+        switch (c) {
+          case AsmJS_ToInt32:
+          case AsmJS_ToNumber:
+          case AsmJS_FRound:
+            return false;
+          case AsmJS_ToInt32x4:
+          case AsmJS_ToFloat32x4:
+            return true;
+        }
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSCoercion");
+    }
     bool addGlobalVarImport(PropertyName *name, AsmJSCoercion coercion, uint32_t *globalIndex) {
         JS_ASSERT(!isFinishedWithModulePrologue());
         Global g(Global::Variable, name);
         g.pod.u.var.initKind_ = Global::InitImport;
         g.pod.u.var.u.coercion_ = coercion;
-        g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++;
+        *globalIndex = IsSimdCoercion(coercion) ? pod.numGlobalSimdVars_++
+                                                : pod.numGlobalScalarVars_++;
+        g.pod.u.var.index_ = *globalIndex;
         return globals_.append(g);
     }
     bool addFFI(PropertyName *field, uint32_t *ffiIndex) {
         JS_ASSERT(!isFinishedWithModulePrologue());
         if (pod.numFFIs_ == UINT32_MAX)
             return false;
         Global g(Global::FFI, field);
         g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
@@ -858,16 +957,27 @@ class AsmJSModule
     }
     bool addMathBuiltinConstant(double value, PropertyName *field) {
         JS_ASSERT(!isFinishedWithModulePrologue());
         Global g(Global::Constant, field);
         g.pod.u.constant.value_ = value;
         g.pod.u.constant.kind_ = Global::MathConstant;
         return globals_.append(g);
     }
+    bool addSimdCtor(AsmJSSimdType type, PropertyName *field) {
+        Global g(Global::SimdCtor, field);
+        g.pod.u.simdCtorType_ = type;
+        return globals_.append(g);
+    }
+    bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName *field) {
+        Global g(Global::SimdOperation, field);
+        g.pod.u.simdOp.type_ = type;
+        g.pod.u.simdOp.which_ = op;
+        return globals_.append(g);
+    }
     bool addGlobalConstant(double value, PropertyName *name) {
         JS_ASSERT(!isFinishedWithModulePrologue());
         Global g(Global::Constant, name);
         g.pod.u.constant.value_ = value;
         g.pod.u.constant.kind_ = Global::GlobalConstant;
         return globals_.append(g);
     }
     unsigned numGlobals() const {
@@ -1104,37 +1214,43 @@ class AsmJSModule
     // null if no heap access was found.
     const jit::AsmJSHeapAccess *lookupHeapAccess(void *pc) const;
 
     // The global data section is placed after the executable code (i.e., at
     // offset codeBytes_) in the module's linear allocation. The global data
     // are laid out in this order:
     //   0. a pointer to the current AsmJSActivation
     //   1. a pointer to the heap that was linked to the module
-    //   2. the double float constant NaN.
-    //   3. the float32 constant NaN, padded to sizeof(double).
-    //   4. global variable state (elements are sizeof(uint64_t))
-    //   5. interleaved function-pointer tables and exits. These are allocated
+    //   2. the double float constant NaN
+    //   3. the float32 constant NaN, padded to Simd128DataSize
+    //   4. global SIMD variable state (elements are Simd128DataSize)
+    //   5. global variable state (elements are sizeof(uint64_t))
+    //   6. interleaved function-pointer tables and exits. These are allocated
     //      while type checking function bodies (as exits and uses of
     //      function-pointer tables are encountered).
     size_t offsetOfGlobalData() const {
         JS_ASSERT(isFinished());
         return pod.codeBytes_;
     }
     uint8_t *globalData() const {
         JS_ASSERT(isFinished());
         return code_ + offsetOfGlobalData();
     }
+    size_t globalSimdVarsOffset() const {
+        return AlignBytes(/* 0 */ sizeof(void*) +
+                          /* 1 */ sizeof(void*) +
+                          /* 2 */ sizeof(double) +
+                          /* 3 */ sizeof(float),
+                          jit::Simd128DataSize);
+    }
     size_t globalDataBytes() const {
-        return sizeof(void*) +
-               sizeof(void*) +
-               sizeof(double) +
-               sizeof(double) +
-               pod.numGlobalVars_ * sizeof(uint64_t) +
-               pod.funcPtrTableAndExitBytes_;
+        return globalSimdVarsOffset() +
+               /* 4 */ pod.numGlobalSimdVars_ * jit::Simd128DataSize +
+               /* 5 */ pod.numGlobalScalarVars_ * sizeof(uint64_t) +
+               /* 6 */ pod.funcPtrTableAndExitBytes_;
     }
     static unsigned activationGlobalDataOffset() {
         JS_STATIC_ASSERT(jit::AsmJSActivationGlobalDataOffset == 0);
         return 0;
     }
     AsmJSActivation *&activation() const {
         return *(AsmJSActivation**)(globalData() + activationGlobalDataOffset());
     }
@@ -1159,30 +1275,49 @@ class AsmJSModule
         return nan64GlobalDataOffset() + sizeof(double);
     }
     void initGlobalNaN() {
         MOZ_ASSERT(jit::AsmJSNaN64GlobalDataOffset == nan64GlobalDataOffset());
         MOZ_ASSERT(jit::AsmJSNaN32GlobalDataOffset == nan32GlobalDataOffset());
         *(double *)(globalData() + nan64GlobalDataOffset()) = GenericNaN();
         *(float *)(globalData() + nan32GlobalDataOffset()) = GenericNaN();
     }
-    unsigned globalVariableOffset() const {
-        static_assert((2 * sizeof(void*) + 2 * sizeof(double)) % sizeof(double) == 0,
-                      "Global data should be aligned");
-        return 2 * sizeof(void*) + 2 * sizeof(double);
+    unsigned globalSimdVarIndexToGlobalDataOffset(unsigned i) const {
+        JS_ASSERT(isFinishedWithModulePrologue());
+        JS_ASSERT(i < pod.numGlobalSimdVars_);
+        return globalSimdVarsOffset() +
+               i * jit::Simd128DataSize;
     }
-    unsigned globalVarIndexToGlobalDataOffset(unsigned i) const {
+    unsigned globalScalarVarIndexToGlobalDataOffset(unsigned i) const {
         JS_ASSERT(isFinishedWithModulePrologue());
-        JS_ASSERT(i < pod.numGlobalVars_);
-        return globalVariableOffset() +
+        JS_ASSERT(i < pod.numGlobalScalarVars_);
+        return globalSimdVarsOffset() +
+               pod.numGlobalSimdVars_ * jit::Simd128DataSize +
                i * sizeof(uint64_t);
     }
-    void *globalVarIndexToGlobalDatum(unsigned i) const {
+    void *globalScalarVarIndexToGlobalDatum(unsigned i) const {
+        JS_ASSERT(isFinished());
+        return (void *)(globalData() + globalScalarVarIndexToGlobalDataOffset(i));
+    }
+    void *globalSimdVarIndexToGlobalDatum(unsigned i) const {
         JS_ASSERT(isFinished());
-        return (void *)(globalData() + globalVarIndexToGlobalDataOffset(i));
+        return (void *)(globalData() + globalSimdVarIndexToGlobalDataOffset(i));
+    }
+    void *globalVarToGlobalDatum(const Global &g) const {
+        unsigned index = g.varIndex();
+        if (g.varInitKind() == Global::VarInitKind::InitConstant) {
+            return g.varInitNumLit().isSimd()
+                   ? globalSimdVarIndexToGlobalDatum(index)
+                   : globalScalarVarIndexToGlobalDatum(index);
+        }
+
+        JS_ASSERT(g.varInitKind() == Global::VarInitKind::InitImport);
+        return IsSimdCoercion(g.varInitCoercion())
+               ? globalSimdVarIndexToGlobalDatum(index)
+               : globalScalarVarIndexToGlobalDatum(index);
     }
     uint8_t **globalDataOffsetToFuncPtrTable(unsigned globalDataOffset) const {
         JS_ASSERT(isFinished());
         JS_ASSERT(globalDataOffset < globalDataBytes());
         return (uint8_t **)(globalData() + globalDataOffset);
     }
     unsigned exitIndexToGlobalDataOffset(unsigned exitIndex) const {
         JS_ASSERT(isFinishedWithModulePrologue());
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -27,16 +27,17 @@
 
 #include "jsmath.h"
 #include "jsprf.h"
 #include "prmjtime.h"
 
 #include "asmjs/AsmJSLink.h"
 #include "asmjs/AsmJSModule.h"
 #include "asmjs/AsmJSSignalHandlers.h"
+#include "builtin/SIMD.h"
 #include "frontend/Parser.h"
 #include "jit/CodeGenerator.h"
 #include "jit/CompileWrappers.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
@@ -382,37 +383,50 @@ class Type
 {
   public:
     enum Which {
         Fixnum = AsmJSNumLit::Fixnum,
         Signed = AsmJSNumLit::NegativeInt,
         Unsigned = AsmJSNumLit::BigUnsigned,
         Double = AsmJSNumLit::Double,
         Float = AsmJSNumLit::Float,
+        Int32x4 = AsmJSNumLit::Int32x4,
+        Float32x4 = AsmJSNumLit::Float32x4,
         MaybeDouble,
         MaybeFloat,
         Floatish,
         Int,
         Intish,
         Void
     };
 
   private:
     Which which_;
 
   public:
     Type() : which_(Which(-1)) {}
     static Type Of(const AsmJSNumLit &lit) {
         JS_ASSERT(lit.hasType());
-        JS_ASSERT(Type::Which(lit.which()) >= Fixnum && Type::Which(lit.which()) <= Float);
+        JS_ASSERT(Type::Which(lit.which()) >= Fixnum && Type::Which(lit.which()) <= Float32x4);
         Type t;
         t.which_ = Type::Which(lit.which());
         return t;
     }
     MOZ_IMPLICIT Type(Which w) : which_(w) {}
+    MOZ_IMPLICIT Type(AsmJSSimdType type) {
+        switch (type) {
+          case AsmJSSimdType_int32x4:
+            which_ = Int32x4;
+            return;
+          case AsmJSSimdType_float32x4:
+            which_ = Float32x4;
+            return;
+        }
+        MOZ_CRASH("unexpected AsmJSSimdType");
+    }
 
     bool operator==(Type rhs) const { return which_ == rhs.which_; }
     bool operator!=(Type rhs) const { return which_ != rhs.which_; }
 
     bool isSigned() const {
         return which_ == Signed || which_ == Fixnum;
     }
 
@@ -451,18 +465,30 @@ class Type
     bool isVoid() const {
         return which_ == Void;
     }
 
     bool isExtern() const {
         return isDouble() || isSigned();
     }
 
+    bool isInt32x4() const {
+        return which_ == Int32x4;
+    }
+
+    bool isFloat32x4() const {
+        return which_ == Float32x4;
+    }
+
+    bool isSimd() const {
+        return isInt32x4() || isFloat32x4();
+    }
+
     bool isVarType() const {
-        return isInt() || isDouble() || isFloat();
+        return isInt() || isDouble() || isFloat() || isSimd();
     }
 
     MIRType toMIRType() const {
         switch (which_) {
           case Double:
           case MaybeDouble:
             return MIRType_Double;
           case Float:
@@ -470,34 +496,88 @@ class Type
           case MaybeFloat:
             return MIRType_Float32;
           case Fixnum:
           case Int:
           case Signed:
           case Unsigned:
           case Intish:
             return MIRType_Int32;
+          case Int32x4:
+            return MIRType_Int32x4;
+          case Float32x4:
+            return MIRType_Float32x4;
           case Void:
             return MIRType_None;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type");
     }
 
+    Type simdToScalarType() const {
+        JS_ASSERT(isSimd());
+        switch (which_) {
+          case Int32x4:
+            return Int;
+          case Float32x4:
+            return Float;
+          // Scalar types
+          case Double:
+          case MaybeDouble:
+          case Float:
+          case MaybeFloat:
+          case Floatish:
+          case Fixnum:
+          case Int:
+          case Signed:
+          case Unsigned:
+          case Intish:
+          case Void:
+            break;
+        }
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid SIMD Type");
+    }
+
+    AsmJSSimdType simdToSimdType() const {
+        JS_ASSERT(isSimd());
+        switch (which_) {
+          case Int32x4:
+            return AsmJSSimdType_int32x4;
+          case Float32x4:
+            return AsmJSSimdType_float32x4;
+          // Scalar types
+          case Double:
+          case MaybeDouble:
+          case Float:
+          case MaybeFloat:
+          case Floatish:
+          case Fixnum:
+          case Int:
+          case Signed:
+          case Unsigned:
+          case Intish:
+          case Void:
+            break;
+        }
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid SIMD Type");
+    }
+
     const char *toChars() const {
         switch (which_) {
           case Double:      return "double";
           case MaybeDouble: return "double?";
           case Float:       return "float";
           case Floatish:    return "floatish";
           case MaybeFloat:  return "float?";
           case Fixnum:      return "fixnum";
           case Int:         return "int";
           case Signed:      return "signed";
           case Unsigned:    return "unsigned";
           case Intish:      return "intish";
+          case Int32x4:     return "int32x4";
+          case Float32x4:   return "float32x4";
           case Void:        return "void";
         }
         MOZ_CRASH("Invalid Type");
     }
 };
 
 } /* anonymous namespace */
 
@@ -505,53 +585,61 @@ class Type
 // function.
 class RetType
 {
   public:
     enum Which {
         Void = Type::Void,
         Signed = Type::Signed,
         Double = Type::Double,
-        Float = Type::Float
+        Float = Type::Float,
+        Int32x4 = Type::Int32x4,
+        Float32x4 = Type::Float32x4
     };
 
   private:
     Which which_;
 
   public:
     RetType() : which_(Which(-1)) {}
     MOZ_IMPLICIT RetType(Which w) : which_(w) {}
     MOZ_IMPLICIT RetType(AsmJSCoercion coercion) {
         switch (coercion) {
           case AsmJS_ToInt32: which_ = Signed; break;
           case AsmJS_ToNumber: which_ = Double; break;
           case AsmJS_FRound: which_ = Float; break;
+          case AsmJS_ToInt32x4: which_ = Int32x4; break;
+          case AsmJS_ToFloat32x4: which_ = Float32x4; break;
         }
     }
     Which which() const {
         return which_;
     }
     Type toType() const {
         return Type::Which(which_);
     }
     AsmJSModule::ReturnType toModuleReturnType() const {
         switch (which_) {
           case Void: return AsmJSModule::Return_Void;
           case Signed: return AsmJSModule::Return_Int32;
           case Float: // will be converted to a Double
           case Double: return AsmJSModule::Return_Double;
+          case Int32x4: return AsmJSModule::Return_Int32x4;
+          case Float32x4: return AsmJSModule::Return_Float32x4;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected return type");
     }
     MIRType toMIRType() const {
         switch (which_) {
           case Void: return MIRType_None;
           case Signed: return MIRType_Int32;
           case Double: return MIRType_Double;
           case Float: return MIRType_Float32;
+          case Int32x4: return MIRType_Int32x4;
+          case Float32x4: return MIRType_Float32x4;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected return type");
     }
     bool operator==(RetType rhs) const { return which_ == rhs.which_; }
     bool operator!=(RetType rhs) const { return which_ != rhs.which_; }
 };
 
 // Represents the subset of Type that can be used as a return type of a builtin
@@ -607,32 +695,36 @@ namespace {
 // RetType, the result is Signed since callers (asm.js and non-asm.js) can
 // rely on the return value being Signed.
 class VarType
 {
   public:
     enum Which {
         Int = Type::Int,
         Double = Type::Double,
-        Float = Type::Float
+        Float = Type::Float,
+        Int32x4 = Type::Int32x4,
+        Float32x4 = Type::Float32x4
     };
 
   private:
     Which which_;
 
   public:
     VarType()
       : which_(Which(-1)) {}
     MOZ_IMPLICIT VarType(Which w)
       : which_(w) {}
     MOZ_IMPLICIT VarType(AsmJSCoercion coercion) {
         switch (coercion) {
           case AsmJS_ToInt32: which_ = Int; break;
           case AsmJS_ToNumber: which_ = Double; break;
           case AsmJS_FRound: which_ = Float; break;
+          case AsmJS_ToInt32x4: which_ = Int32x4; break;
+          case AsmJS_ToFloat32x4: which_ = Float32x4; break;
         }
     }
     static VarType Of(const AsmJSNumLit &lit) {
         JS_ASSERT(lit.hasType());
         VarType v;
         switch (lit.which()) {
           case AsmJSNumLit::Fixnum:
           case AsmJSNumLit::NegativeInt:
@@ -640,67 +732,84 @@ class VarType
             v.which_ = Int;
             return v;
           case AsmJSNumLit::Double:
             v.which_ = Double;
             return v;
           case AsmJSNumLit::Float:
             v.which_ = Float;
             return v;
+          case AsmJSNumLit::Int32x4:
+            v.which_ = Int32x4;
+            return v;
+          case AsmJSNumLit::Float32x4:
+            v.which_ = Float32x4;
+            return v;
           case AsmJSNumLit::OutOfRangeInt:
             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("can't be out of range int");
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected literal type");
     }
 
     Which which() const {
         return which_;
     }
     Type toType() const {
         return Type::Which(which_);
     }
     MIRType toMIRType() const {
         switch(which_) {
-          case Int:     return MIRType_Int32;
-          case Double:  return MIRType_Double;
-          case Float:   return MIRType_Float32;
+          case Int:       return MIRType_Int32;
+          case Double:    return MIRType_Double;
+          case Float:     return MIRType_Float32;
+          case Int32x4:   return MIRType_Int32x4;
+          case Float32x4: return MIRType_Float32x4;
         }
-        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("VarType can only be Int, Double or Float");
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("VarType can only be Int, SIMD, Double or Float");
     }
     AsmJSCoercion toCoercion() const {
         switch(which_) {
-          case Int:     return AsmJS_ToInt32;
-          case Double:  return AsmJS_ToNumber;
-          case Float:   return AsmJS_FRound;
+          case Int:       return AsmJS_ToInt32;
+          case Double:    return AsmJS_ToNumber;
+          case Float:     return AsmJS_FRound;
+          case Int32x4:   return AsmJS_ToInt32x4;
+          case Float32x4: return AsmJS_ToFloat32x4;
         }
-        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("VarType can only be Int, Double or Float");
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("VarType can only be Int, SIMD, Double or Float");
     }
     static VarType FromCheckedType(Type type) {
-        JS_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish());
+        JS_ASSERT(type.isInt() || type.isMaybeDouble() || type.isFloatish() || type.isSimd());
         if (type.isMaybeDouble())
             return Double;
         else if (type.isFloatish())
             return Float;
-        else
+        else if (type.isInt())
             return Int;
+        else if (type.isInt32x4())
+            return Int32x4;
+        else if (type.isFloat32x4())
+            return Float32x4;
+        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unknown type in FromCheckedType");
     }
     bool operator==(VarType rhs) const { return which_ == rhs.which_; }
     bool operator!=(VarType rhs) const { return which_ != rhs.which_; }
 };
 
 } /* anonymous namespace */
 
 // Implements <: (subtype) operator when the rhs is a VarType
 static inline bool
 operator<=(Type lhs, VarType rhs)
 {
     switch (rhs.which()) {
-      case VarType::Int:    return lhs.isInt();
-      case VarType::Double: return lhs.isDouble();
-      case VarType::Float:  return lhs.isFloat();
+      case VarType::Int:       return lhs.isInt();
+      case VarType::Double:    return lhs.isDouble();
+      case VarType::Float:     return lhs.isFloat();
+      case VarType::Int32x4:   return lhs.isInt32x4();
+      case VarType::Float32x4: return lhs.isFloat32x4();
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected rhs type");
 }
 
 /*****************************************************************************/
 
 static inline MIRType ToMIRType(MIRType t) { return t; }
 static inline MIRType ToMIRType(VarType t) { return t.toMIRType(); }
@@ -930,32 +1039,39 @@ class MOZ_STACK_CLASS ModuleCompiler
         enum Which {
             Variable,
             ConstantLiteral,
             ConstantImport,
             Function,
             FuncPtrTable,
             FFI,
             ArrayView,
-            MathBuiltinFunction
+            MathBuiltinFunction,
+            SimdCtor,
+            SimdOperation
         };
 
       private:
         Which which_;
         union {
             struct {
                 VarType::Which type_;
                 uint32_t index_;
                 AsmJSNumLit literalValue_;
             } varOrConst;
             uint32_t funcIndex_;
             uint32_t funcPtrTableIndex_;
             uint32_t ffiIndex_;
             Scalar::Type viewType_;
             AsmJSMathBuiltinFunction mathBuiltinFunc_;
+            AsmJSSimdType simdCtorType_;
+            struct {
+                AsmJSSimdType type_;
+                AsmJSSimdOperation which_;
+            } simdOp;
         } u;
 
         friend class ModuleCompiler;
         friend class js::LifoAlloc;
 
         explicit Global(Which which) : which_(which) {}
 
       public:
@@ -995,16 +1111,34 @@ class MOZ_STACK_CLASS ModuleCompiler
         }
         bool isMathFunction() const {
             return which_ == MathBuiltinFunction;
         }
         AsmJSMathBuiltinFunction mathBuiltinFunction() const {
             JS_ASSERT(which_ == MathBuiltinFunction);
             return u.mathBuiltinFunc_;
         }
+        bool isSimdCtor() const {
+            return which_ == SimdCtor;
+        }
+        AsmJSSimdType simdCtorType() const {
+            JS_ASSERT(which_ == SimdCtor);
+            return u.simdCtorType_;
+        }
+        bool isSimdOperation() const {
+            return which_ == SimdOperation;
+        }
+        AsmJSSimdOperation simdOperation() const {
+            JS_ASSERT(which_ == SimdOperation);
+            return u.simdOp.which_;
+        }
+        AsmJSSimdType simdOperationType() const {
+            JS_ASSERT(which_ == SimdOperation);
+            return u.simdOp.type_;
+        }
     };
 
     typedef Vector<const Func*> FuncPtrVector;
 
     class FuncPtrTable
     {
         Signature sig_;
         uint32_t mask_;
@@ -1096,16 +1230,17 @@ class MOZ_STACK_CLASS ModuleCompiler
 
         PropertyName *name;
         unsigned ms;
         unsigned line;
         unsigned column;
     };
 
     typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
+    typedef HashMap<PropertyName*, AsmJSSimdOperation> SimdOperationNameMap;
     typedef HashMap<PropertyName*, Global*> GlobalMap;
     typedef Vector<Func*> FuncVector;
     typedef Vector<AsmJSGlobalAccess> GlobalAccessVector;
     typedef Vector<SlowFunction> SlowFunctionVector;
 
     ExclusiveContext *             cx_;
     AsmJSParser &                  parser_;
 
@@ -1116,63 +1251,73 @@ class MOZ_STACK_CLASS ModuleCompiler
     ParseNode *                    moduleFunctionNode_;
     PropertyName *                 moduleFunctionName_;
 
     GlobalMap                      globals_;
     FuncVector                     functions_;
     FuncPtrTableVector             funcPtrTables_;
     ExitMap                        exits_;
     MathNameMap                    standardLibraryMathNames_;
+    SimdOperationNameMap           standardLibrarySimdOpNames_;
     NonAssertingLabel              stackOverflowLabel_;
     NonAssertingLabel              asyncInterruptLabel_;
     NonAssertingLabel              syncInterruptLabel_;
 
     UniquePtr<char[], JS::FreePolicy> errorString_;
     uint32_t                       errorOffset_;
     bool                           errorOverRecursed_;
 
     int64_t                        usecBefore_;
     SlowFunctionVector             slowFunctions_;
 
     DebugOnly<bool>                finishedFunctionBodies_;
+    bool                           supportsSimd_;
 
     bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltinFunction func) {
         JSAtom *atom = Atomize(cx_, name, strlen(name));
         if (!atom)
             return false;
         MathBuiltin builtin(func);
         return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
     }
     bool addStandardLibraryMathName(const char *name, double cst) {
         JSAtom *atom = Atomize(cx_, name, strlen(name));
         if (!atom)
             return false;
         MathBuiltin builtin(cst);
         return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
     }
+    bool addStandardLibrarySimdOpName(const char *name, AsmJSSimdOperation op) {
+        JSAtom *atom = Atomize(cx_, name, strlen(name));
+        if (!atom)
+            return false;
+        return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op);
+    }
 
   public:
     ModuleCompiler(ExclusiveContext *cx, AsmJSParser &parser)
       : cx_(cx),
         parser_(parser),
         masm_(MacroAssembler::AsmJSToken()),
         moduleLifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
         moduleFunctionNode_(parser.pc->maybeFunction),
         moduleFunctionName_(nullptr),
         globals_(cx),
         functions_(cx),
         funcPtrTables_(cx),
         exits_(cx),
         standardLibraryMathNames_(cx),
+        standardLibrarySimdOpNames_(cx),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false),
         usecBefore_(PRMJ_Now()),
         slowFunctions_(cx),
-        finishedFunctionBodies_(false)
+        finishedFunctionBodies_(false),
+        supportsSimd_(cx->jitSupportsSimd())
     {
         JS_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
     }
 
     ~ModuleCompiler() {
         if (errorString_) {
             JS_ASSERT(errorOffset_ != UINT32_MAX);
             tokenStream().reportAsmJSError(errorOffset_,
@@ -1213,16 +1358,25 @@ class MOZ_STACK_CLASS ModuleCompiler
             !addStandardLibraryMathName("LOG10E", M_LOG10E) ||
             !addStandardLibraryMathName("PI", M_PI) ||
             !addStandardLibraryMathName("SQRT1_2", M_SQRT1_2) ||
             !addStandardLibraryMathName("SQRT2", M_SQRT2))
         {
             return false;
         }
 
+        if (!standardLibrarySimdOpNames_.init() ||
+            !addStandardLibrarySimdOpName("add", AsmJSSimdOperation_add) ||
+            !addStandardLibrarySimdOpName("sub", AsmJSSimdOperation_sub) ||
+            !addStandardLibrarySimdOpName("mul", AsmJSSimdOperation_mul) ||
+            !addStandardLibrarySimdOpName("div", AsmJSSimdOperation_div))
+        {
+            return false;
+        }
+
         uint32_t srcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin;
         uint32_t srcBodyStart = tokenStream().currentToken().pos.end;
 
         // "use strict" should be added to the source if we are in an implicit
         // strict context, see also comment above addUseStrict in
         // js::FunctionToString.
         bool strict = parser_.pc->sc->strict && !parser_.pc->sc->hasExplicitUseStrict();
         module_ = cx_->new_<AsmJSModule>(parser_.ss, srcStart, srcBodyStart, strict,
@@ -1294,16 +1448,17 @@ class MOZ_STACK_CLASS ModuleCompiler
     Label &stackOverflowLabel() { return stackOverflowLabel_; }
     Label &asyncInterruptLabel() { return asyncInterruptLabel_; }
     Label &syncInterruptLabel() { return syncInterruptLabel_; }
     bool hasError() const { return errorString_ != nullptr; }
     const AsmJSModule &module() const { return *module_.get(); }
     uint32_t srcStart() const { return module_->srcStart(); }
     bool usesSignalHandlersForInterrupt() const { return module_->usesSignalHandlersForInterrupt(); }
     bool usesSignalHandlersForOOB() const { return module_->usesSignalHandlersForOOB(); }
+    bool supportsSimd() const { return supportsSimd_; }
 
     ParseNode *moduleFunctionNode() const { return moduleFunctionNode_; }
     PropertyName *moduleFunctionName() const { return moduleFunctionName_; }
 
     const Global *lookupGlobal(PropertyName *name) const {
         if (GlobalMap::Ptr p = globals_.lookup(name))
             return p->value();
         return nullptr;
@@ -1330,16 +1485,23 @@ class MOZ_STACK_CLASS ModuleCompiler
     }
     bool lookupStandardLibraryMathName(PropertyName *name, MathBuiltin *mathBuiltin) const {
         if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) {
             *mathBuiltin = p->value();
             return true;
         }
         return false;
     }
+    bool lookupStandardSimdOpName(PropertyName *name, AsmJSSimdOperation *op) const {
+        if (SimdOperationNameMap::Ptr p = standardLibrarySimdOpNames_.lookup(name)) {
+            *op = p->value();
+            return true;
+        }
+        return false;
+    }
     ExitMap::Range allExits() const {
         return exits_.all();
     }
 
     /***************************************************** Mutable interface */
 
     void initModuleFunctionName(PropertyName *name) { moduleFunctionName_ = name; }
 
@@ -1433,16 +1595,37 @@ class MOZ_STACK_CLASS ModuleCompiler
         if (!module_->addMathBuiltinFunction(func, fieldName))
             return false;
         Global *global = moduleLifo_.new_<Global>(Global::MathBuiltinFunction);
         if (!global)
             return false;
         global->u.mathBuiltinFunc_ = func;
         return globals_.putNew(varName, global);
     }
+    bool addSimdCtor(PropertyName *varName, AsmJSSimdType type, PropertyName *fieldName) {
+        if (!module_->addSimdCtor(type, fieldName))
+            return false;
+        Global *global = moduleLifo_.new_<Global>(Global::SimdCtor);
+        if (!global)
+            return false;
+        global->u.simdCtorType_ = type;
+        return globals_.putNew(varName, global);
+    }
+    bool addSimdOperation(PropertyName *varName, AsmJSSimdType type, AsmJSSimdOperation op,
+                          PropertyName *typeVarName, PropertyName *opName)
+    {
+        if (!module_->addSimdOperation(type, op, opName))
+            return false;
+        Global *global = moduleLifo_.new_<Global>(Global::SimdOperation);
+        if (!global)
+            return false;
+        global->u.simdOp.type_ = type;
+        global->u.simdOp.which_ = op;
+        return globals_.putNew(varName, global);
+    }
   private:
     bool addGlobalDoubleConstant(PropertyName *varName, double constant) {
         Global *global = moduleLifo_.new_<Global>(Global::ConstantLiteral);
         if (!global)
             return false;
         global->u.varOrConst.type_ = VarType::Double;
         global->u.varOrConst.literalValue_ = AsmJSNumLit::Create(AsmJSNumLit::Double,
                                                                  DoubleValue(constant));
@@ -1666,82 +1849,203 @@ IsCallToGlobal(ModuleCompiler &m, ParseN
     if (!callee->isKind(PNK_NAME))
         return false;
 
     *global = m.lookupGlobal(callee->name());
     return !!*global;
 }
 
 static bool
-IsFloatCoercion(ModuleCompiler &m, ParseNode *pn, ParseNode **coercedExpr)
+IsCoercionCall(ModuleCompiler &m, ParseNode *pn, AsmJSCoercion *coercion, ParseNode **coercedExpr)
 {
     const ModuleCompiler::Global *global;
     if (!IsCallToGlobal(m, pn, &global))
         return false;
 
-    if (!global->isMathFunction() || global->mathBuiltinFunction() != AsmJSMathBuiltin_fround)
-        return false;
-
     if (CallArgListLength(pn) != 1)
         return false;
 
     if (coercedExpr)
         *coercedExpr = CallArgList(pn);
 
-    return true;
-}
-
-static bool
-IsNumericFloatLiteral(ModuleCompiler &m, ParseNode *pn)
+    if (global->isMathFunction() && global->mathBuiltinFunction() == AsmJSMathBuiltin_fround) {
+        *coercion = AsmJS_FRound;
+        return true;
+    }
+
+    if (global->isSimdCtor()) {
+        switch (global->simdCtorType()) {
+          case AsmJSSimdType_int32x4:
+            *coercion = AsmJS_ToInt32x4;
+            return true;
+          case AsmJSSimdType_float32x4:
+            *coercion = AsmJS_ToFloat32x4;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static bool
+IsFloatLiteral(ModuleCompiler &m, ParseNode *pn)
 {
     ParseNode *coercedExpr;
-    if (!IsFloatCoercion(m, pn, &coercedExpr))
-        return false;
-
+    AsmJSCoercion coercion;
+    if (!IsCoercionCall(m, pn, &coercion, &coercedExpr) || coercion != AsmJS_FRound)
+        return false;
     return IsNumericNonFloatLiteral(coercedExpr);
 }
 
+static unsigned
+SimdTypeToLength(AsmJSSimdType type)
+{
+    switch (type) {
+      case AsmJSSimdType_float32x4:
+      case AsmJSSimdType_int32x4:
+        return 4;
+    }
+    MOZ_CRASH("unexpected SIMD type");
+}
+
+static bool
+IsSimdTuple(ModuleCompiler &m, ParseNode *pn, AsmJSSimdType *type)
+{
+    const ModuleCompiler::Global *global;
+    if (!IsCallToGlobal(m, pn, &global))
+        return false;
+
+    if (!global->isSimdCtor())
+        return false;
+
+    if (CallArgListLength(pn) != SimdTypeToLength(global->simdCtorType()))
+        return false;
+
+    *type = global->simdCtorType();
+    return true;
+}
+
+static bool
+IsNumericLiteral(ModuleCompiler &m, ParseNode *pn);
+static AsmJSNumLit
+ExtractNumericLiteral(ModuleCompiler &m, ParseNode *pn);
+static inline bool
+IsLiteralInt(ModuleCompiler &m, ParseNode *pn, uint32_t *u32);
+
+static bool
+IsSimdLiteral(ModuleCompiler &m, ParseNode *pn)
+{
+    AsmJSSimdType type;
+    if (!IsSimdTuple(m, pn, &type))
+        return false;
+
+    ParseNode *arg = CallArgList(pn);
+    unsigned length = SimdTypeToLength(type);
+    for (unsigned i = 0; i < length; i++) {
+        if (!IsNumericLiteral(m, arg))
+            return false;
+
+        uint32_t _;
+        switch (type) {
+          case AsmJSSimdType_int32x4:
+            if (!IsLiteralInt(m, arg, &_))
+                return false;
+          case AsmJSSimdType_float32x4:
+            if (!IsNumericNonFloatLiteral(arg))
+                return false;
+        }
+
+        arg = NextNode(arg);
+    }
+
+    JS_ASSERT(arg == nullptr);
+    return true;
+}
+
 static bool
 IsNumericLiteral(ModuleCompiler &m, ParseNode *pn)
 {
     return IsNumericNonFloatLiteral(pn) ||
-           IsNumericFloatLiteral(m, pn);
+           IsFloatLiteral(m, pn) ||
+           IsSimdLiteral(m, pn);
 }
 
 // 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)
-{
-    JS_ASSERT(IsNumericNonFloatLiteral(*pn));
-
-    if ((*pn)->isKind(PNK_NEG)) {
-        *pn = UnaryKid(*pn);
-        return -NumberNodeValue(*pn);
-    }
-
-    return NumberNodeValue(*pn);
+ExtractNumericNonFloatValue(ParseNode *pn, ParseNode **out = nullptr)
+{
+    JS_ASSERT(IsNumericNonFloatLiteral(pn));
+
+    if (pn->isKind(PNK_NEG)) {
+        pn = UnaryKid(pn);
+        if (out)
+            *out = pn;
+        return -NumberNodeValue(pn);
+    }
+
+    return NumberNodeValue(pn);
+}
+
+static AsmJSNumLit
+ExtractSimdValue(ModuleCompiler &m, ParseNode *pn)
+{
+    JS_ASSERT(IsSimdLiteral(m, pn));
+
+    AsmJSSimdType type;
+    JS_ALWAYS_TRUE(IsSimdTuple(m, pn, &type));
+
+    ParseNode *arg = CallArgList(pn);
+    unsigned length = SimdTypeToLength(type);
+    switch (type) {
+      case AsmJSSimdType_int32x4: {
+        JS_ASSERT(length == 4);
+        int32_t val[4];
+        for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) {
+            uint32_t u32;
+            JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
+            val[i] = int32_t(u32);
+        }
+        JS_ASSERT(arg== nullptr);
+        return AsmJSNumLit::Create(AsmJSNumLit::Int32x4, SimdConstant::CreateX4(val));
+      }
+      case AsmJSSimdType_float32x4: {
+        JS_ASSERT(length == 4);
+        float val[4];
+        for (size_t i = 0; i < 4; i++, arg = NextNode(arg))
+            val[i] = float(ExtractNumericNonFloatValue(arg));
+        JS_ASSERT(arg == nullptr);
+        return AsmJSNumLit::Create(AsmJSNumLit::Float32x4, SimdConstant::CreateX4(val));
+      }
+    }
+
+    MOZ_CRASH("Unexpected SIMD type.");
 }
 
 static AsmJSNumLit
 ExtractNumericLiteral(ModuleCompiler &m, ParseNode *pn)
 {
     JS_ASSERT(IsNumericLiteral(m, pn));
 
-    // Float literals are explicitly coerced and thus the coerced literal may be
-    // any valid (non-float) numeric literal.
     if (pn->isKind(PNK_CALL)) {
-        pn = CallArgList(pn);
-        double d = ExtractNumericNonFloatValue(&pn);
-        return AsmJSNumLit::Create(AsmJSNumLit::Float, DoubleValue(d));
-    }
-
-    double d = ExtractNumericNonFloatValue(&pn);
+        // Float literals are explicitly coerced and thus the coerced literal may be
+        // any valid (non-float) numeric literal.
+        if (CallArgListLength(pn) == 1) {
+            pn = CallArgList(pn);
+            double d = ExtractNumericNonFloatValue(pn);
+            return AsmJSNumLit::Create(AsmJSNumLit::Float, DoubleValue(d));
+        }
+
+        JS_ASSERT(CallArgListLength(pn) == 4);
+        return ExtractSimdValue(m, pn);
+    }
+
+    double d = ExtractNumericNonFloatValue(pn, &pn);
 
     // The asm.js spec syntactically distinguishes any literal containing a
     // decimal point or the literal -0 as having double type.
     if (NumberNodeHasFrac(pn) || IsNegativeZero(d))
         return AsmJSNumLit::Create(AsmJSNumLit::Double, DoubleValue(d));
 
     // The syntactic checks above rule out these double values.
     JS_ASSERT(!IsNegativeZero(d));
@@ -1778,16 +2082,18 @@ IsLiteralInt(ModuleCompiler &m, ParseNod
       case AsmJSNumLit::Fixnum:
       case AsmJSNumLit::BigUnsigned:
       case AsmJSNumLit::NegativeInt:
         *u32 = uint32_t(literal.toInt32());
         return true;
       case AsmJSNumLit::Double:
       case AsmJSNumLit::Float:
       case AsmJSNumLit::OutOfRangeInt:
+      case AsmJSNumLit::Int32x4:
+      case AsmJSNumLit::Float32x4:
         return false;
     }
 
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal type");
 }
 
 /*****************************************************************************/
 
@@ -1948,17 +2254,24 @@ class FunctionCompiler
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(i.index()), ins);
             if (!mirGen_->ensureBallast())
                 return false;
         }
         unsigned firstLocalSlot = argTypes.length();
         for (unsigned i = 0; i < varInitializers_.length(); i++) {
             AsmJSNumLit &lit = varInitializers_[i];
-            MConstant *ins = MConstant::NewAsmJS(alloc(), lit.value(), Type::Of(lit).toMIRType());
+            MIRType type = Type::Of(lit).toMIRType();
+
+            MInstruction *ins;
+            if (lit.isSimd())
+               ins = MSimdConstant::New(alloc(), lit.simdValue(), type);
+            else
+               ins = MConstant::NewAsmJS(alloc(), lit.scalarValue(), type);
+
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(firstLocalSlot + i), ins);
             if (!mirGen_->ensureBallast())
                 return false;
         }
         maybeAddInterruptCheck(fn_);
         return true;
     }
@@ -1999,23 +2312,33 @@ class FunctionCompiler
 
     const ModuleCompiler::Global *lookupGlobal(PropertyName *name) const
     {
         if (locals_.has(name))
             return nullptr;
         return m_.lookupGlobal(name);
     }
 
+    bool supportsSimd() const {
+        return m_.supportsSimd();
+    }
+
     /***************************** Code generation (after local scope setup) */
 
     MDefinition *constant(const AsmJSNumLit &lit)
     {
         if (inDeadCode())
             return nullptr;
-        MConstant *constant = MConstant::NewAsmJS(alloc(), lit.value(), Type::Of(lit).toMIRType());
+
+        MInstruction *constant;
+        if (lit.isSimd())
+            constant = MSimdConstant::New(alloc(), lit.simdValue(), Type::Of(lit).toMIRType());
+        else
+            constant = MConstant::NewAsmJS(alloc(), lit.scalarValue(), Type::Of(lit).toMIRType());
+
         curBlock_->add(constant);
         return constant;
     }
 
     MDefinition *constant(Value v, Type t)
     {
         if (inDeadCode())
             return nullptr;
@@ -2059,16 +2382,29 @@ class FunctionCompiler
     {
         if (inDeadCode())
             return nullptr;
         T *ins = T::NewAsmJS(alloc(), lhs, rhs, type);
         curBlock_->add(ins);
         return ins;
     }
 
+    MDefinition *binarySimd(MDefinition *lhs, MDefinition *rhs, MSimdBinaryArith::Operation op,
+                            MIRType type)
+    {
+        if (inDeadCode())
+            return nullptr;
+
+        JS_ASSERT(IsSimdType(lhs->type()) && rhs->type() == lhs->type());
+        JS_ASSERT(lhs->type() == type);
+        MSimdBinaryArith *ins = MSimdBinaryArith::NewAsmJS(alloc(), lhs, rhs, op, type);
+        curBlock_->add(ins);
+        return ins;
+    }
+
     MDefinition *minMax(MDefinition *lhs, MDefinition *rhs, MIRType type, bool isMax) {
         if (inDeadCode())
             return nullptr;
         MMinMax *ins = MMinMax::New(alloc(), lhs, rhs, type, isMax);
         curBlock_->add(ins);
         return ins;
     }
 
@@ -2156,31 +2492,42 @@ class FunctionCompiler
             store->setSkipBoundsCheck(true);
     }
 
     MDefinition *loadGlobalVar(const ModuleCompiler::Global &global)
     {
         if (inDeadCode())
             return nullptr;
 
-        uint32_t index = global.varOrConstIndex();
-        unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(index);
         MIRType type = global.varOrConstType().toMIRType();
+
+        unsigned globalDataOffset;
+        if (IsSimdType(type))
+            globalDataOffset = module().globalSimdVarIndexToGlobalDataOffset(global.varOrConstIndex());
+        else
+            globalDataOffset = module().globalScalarVarIndexToGlobalDataOffset(global.varOrConstIndex());
+
         MAsmJSLoadGlobalVar *load = MAsmJSLoadGlobalVar::New(alloc(), type, globalDataOffset,
                                                              global.isConst());
         curBlock_->add(load);
         return load;
     }
 
     void storeGlobalVar(const ModuleCompiler::Global &global, MDefinition *v)
     {
         if (inDeadCode())
             return;
         JS_ASSERT(!global.isConst());
-        unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(global.varOrConstIndex());
+
+        unsigned globalDataOffset;
+        if (IsSimdType(v->type()))
+            globalDataOffset = module().globalSimdVarIndexToGlobalDataOffset(global.varOrConstIndex());
+        else
+            globalDataOffset = module().globalScalarVarIndexToGlobalDataOffset(global.varOrConstIndex());
+
         curBlock_->add(MAsmJSStoreGlobalVar::New(alloc(), globalDataOffset, v));
     }
 
     void maybeAddInterruptCheck(ParseNode *pn)
     {
         if (inDeadCode())
             return;
 
@@ -2188,16 +2535,41 @@ class FunctionCompiler
             return;
 
         unsigned lineno = 0, column = 0;
         m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &lineno, &column);
         CallSiteDesc callDesc(lineno, column, CallSiteDesc::Relative);
         curBlock_->add(MAsmJSInterruptCheck::New(alloc(), &m().syncInterruptLabel(), callDesc));
     }
 
+    MDefinition *extractSimdElement(SimdLane lane, MDefinition *base, MIRType type)
+    {
+        if (inDeadCode())
+            return nullptr;
+
+        JS_ASSERT(IsSimdType(base->type()));
+        JS_ASSERT(!IsSimdType(type));
+        MSimdExtractElement *ins = MSimdExtractElement::NewAsmJS(alloc(), base, type, lane);
+        curBlock_->add(ins);
+        return ins;
+    }
+
+    template<typename T>
+    MDefinition *constructSimd(MDefinition *x, MDefinition *y, MDefinition *z, MDefinition *w,
+                               MIRType type)
+    {
+        if (inDeadCode())
+            return nullptr;
+
+        JS_ASSERT(IsSimdType(type));
+        T *ins = T::New(alloc(), type, x, y, z, w);
+        curBlock_->add(ins);
+        return ins;
+    }
+
     /***************************************************************** Calls */
 
     // The IonMonkey backend maintains a single stack offset (from the stack
     // pointer to the base of the frame) by adding the total amount of spill
     // space required plus the maximum stack required for argument passing.
     // Since we do not use IonMonkey's MPrepareCall/MPassArg/MCall, we must
     // manually accumulate, for the entire function, the maximum required stack
     // space for argument passing. (This is passed to the CodeGenerator via
@@ -2958,25 +3330,23 @@ CheckTypeAnnotation(ModuleCompiler &m, P
       }
       case PNK_POS: {
         *coercion = AsmJS_ToNumber;
         if (coercedExpr)
             *coercedExpr = UnaryKid(coercionNode);
         return true;
       }
       case PNK_CALL: {
-        *coercion = AsmJS_FRound;
-        if (!IsFloatCoercion(m, coercionNode, coercedExpr))
-            return m.fail(coercionNode, "call must be to fround coercion");
-        return true;
+        if (IsCoercionCall(m, coercionNode, coercion, coercedExpr))
+            return true;
       }
       default:;
     }
 
-    return m.fail(coercionNode, "must be of the form +x, fround(x) or x|0");
+    return m.fail(coercionNode, "must be of the form +x, fround(x), simdType(x) or x|0");
 }
 
 static bool
 CheckGlobalVariableImportExpr(ModuleCompiler &m, PropertyName *varName, AsmJSCoercion coercion,
                               ParseNode *coercedExpr, bool isConst)
 {
     if (!coercedExpr->isKind(PNK_DOT))
         return m.failName(coercedExpr, "invalid import expression for global '%s'", varName);
@@ -3050,54 +3420,133 @@ CheckNewArrayView(ModuleCompiler &m, Pro
         type = Scalar::Float64;
     else
         return m.fail(ctorExpr, "could not match typed array name");
 
     return m.addArrayView(varName, type, field);
 }
 
 static bool
+IsSimdTypeName(ModuleCompiler &m, PropertyName *name, AsmJSSimdType *type)
+{
+    if (name == m.cx()->names().int32x4) {
+        *type = AsmJSSimdType_int32x4;
+        return true;
+    }
+    if (name == m.cx()->names().float32x4) {
+        *type = AsmJSSimdType_float32x4;
+        return true;
+    }
+    return false;
+}
+
+static bool
+IsSimdValidOperationType(AsmJSSimdType type, AsmJSSimdOperation op)
+{
+    switch (op) {
+      case AsmJSSimdOperation_add:
+      case AsmJSSimdOperation_sub:
+        return true;
+      case AsmJSSimdOperation_mul:
+      case AsmJSSimdOperation_div:
+        return type == AsmJSSimdType_float32x4;
+    }
+    return false;
+}
+
+static bool
+CheckGlobalMathImport(ModuleCompiler &m, ParseNode *initNode, PropertyName *varName,
+                      PropertyName *field)
+{
+    // Math builtin, with the form glob.Math.[[builtin]]
+    ModuleCompiler::MathBuiltin mathBuiltin;
+    if (!m.lookupStandardLibraryMathName(field, &mathBuiltin))
+        return m.failName(initNode, "'%s' is not a standard Math builtin", field);
+
+    switch (mathBuiltin.kind) {
+      case ModuleCompiler::MathBuiltin::Function:
+        return m.addMathBuiltinFunction(varName, mathBuiltin.u.func, field);
+      case ModuleCompiler::MathBuiltin::Constant:
+        return m.addMathBuiltinConstant(varName, mathBuiltin.u.cst, field);
+      default:
+        break;
+    }
+    MOZ_CRASH("unexpected or uninitialized math builtin type");
+}
+
+static bool
+CheckGlobalSimdImport(ModuleCompiler &m, ParseNode *initNode, PropertyName *varName,
+                      PropertyName *field)
+{
+    if (!m.supportsSimd())
+        return m.fail(initNode, "SIMD is not supported on this platform");
+
+    // SIMD constructor, with the form glob.SIMD.[[type]]
+    AsmJSSimdType simdType;
+    if (!IsSimdTypeName(m, field, &simdType))
+        return m.failName(initNode, "'%s' is not a standard SIMD type", field);
+    return m.addSimdCtor(varName, simdType, field);
+}
+
+static bool
+CheckGlobalSimdOperationImport(ModuleCompiler &m, const ModuleCompiler::Global *global,
+                               ParseNode *initNode, PropertyName *varName, PropertyName *ctorVarName,
+                               PropertyName *opName)
+{
+    AsmJSSimdType simdType = global->simdCtorType();
+    AsmJSSimdOperation simdOp;
+    if (!m.lookupStandardSimdOpName(opName, &simdOp))
+        return m.failName(initNode, "'%s' is not a standard SIMD operation", opName);
+    if (!IsSimdValidOperationType(simdType, simdOp))
+        return m.failName(initNode, "'%s' is not an operation supported by the SIMD type", opName);
+    return m.addSimdOperation(varName, simdType, simdOp, ctorVarName, opName);
+}
+
+static bool
 CheckGlobalDotImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode)
 {
     ParseNode *base = DotBase(initNode);
     PropertyName *field = DotMember(initNode);
 
     if (base->isKind(PNK_DOT)) {
         ParseNode *global = DotBase(base);
-        PropertyName *math = DotMember(base);
-        if (!IsUseOfName(global, m.module().globalArgumentName()) || math != m.cx()->names().Math)
-            return m.fail(base, "expecting global.Math");
-
-        ModuleCompiler::MathBuiltin mathBuiltin;
-        if (!m.lookupStandardLibraryMathName(field, &mathBuiltin))
-            return m.failName(initNode, "'%s' is not a standard Math builtin", field);
-
-        switch (mathBuiltin.kind) {
-          case ModuleCompiler::MathBuiltin::Function:
-            return m.addMathBuiltinFunction(varName, mathBuiltin.u.func, field);
-          case ModuleCompiler::MathBuiltin::Constant:
-            return m.addMathBuiltinConstant(varName, mathBuiltin.u.cst, field);
-          default:
-            break;
-        }
-        MOZ_CRASH("unexpected or uninitialized math builtin type");
-    }
-
-    if (IsUseOfName(base, m.module().globalArgumentName())) {
+        PropertyName *mathOrSimd = DotMember(base);
+
+        if (!IsUseOfName(global, m.module().globalArgumentName()))
+            return m.failf(base, "expecting %s.*", m.module().globalArgumentName());
+
+        if (mathOrSimd == m.cx()->names().Math)
+            return CheckGlobalMathImport(m, initNode, varName, field);
+        if (mathOrSimd == m.cx()->names().SIMD)
+            return CheckGlobalSimdImport(m, initNode, varName, field);
+        return m.failf(base, "expecting %s.{Math|SIMD}", m.module().globalArgumentName());
+    }
+
+    if (!base->isKind(PNK_NAME))
+        return m.fail(base, "expected name of variable or parameter");
+
+    if (base->name() == m.module().globalArgumentName()) {
         if (field == m.cx()->names().NaN)
             return m.addGlobalConstant(varName, GenericNaN(), field);
         if (field == m.cx()->names().Infinity)
             return m.addGlobalConstant(varName, PositiveInfinity<double>(), field);
         return m.failName(initNode, "'%s' is not a standard global constant", field);
     }
 
-    if (IsUseOfName(base, m.module().importArgumentName()))
+    if (base->name() == m.module().importArgumentName())
         return m.addFFI(varName, field);
 
-    return m.fail(initNode, "expecting c.y where c is either the global or foreign parameter");
+    const ModuleCompiler::Global *global = m.lookupGlobal(base->name());
+    if (!global)
+        return m.failName(initNode, "%s not found in module global scope", base->name());
+
+    if (!global->isSimdCtor())
+        return m.failName(base, "expecting SIMD constructor name, got %s", field);
+
+    return CheckGlobalSimdOperationImport(m, global, initNode, varName, base->name(), field);
 }
 
 static bool
 CheckModuleGlobal(ModuleCompiler &m, ParseNode *var, bool isConst)
 {
     if (!IsDefinition(var))
         return m.fail(var, "import variable names must be unique");
 
@@ -3219,16 +3668,22 @@ CheckFinalReturn(FunctionCompiler &f, Pa
                     *retType = RetType::Signed;
                     break;
                   case AsmJSNumLit::Double:
                     *retType = RetType::Double;
                     break;
                   case AsmJSNumLit::Float:
                     *retType = RetType::Float;
                     break;
+                  case AsmJSNumLit::Int32x4:
+                    *retType = RetType::Int32x4;
+                    break;
+                  case AsmJSNumLit::Float32x4:
+                    *retType = RetType::Float32x4;
+                    break;
                 }
                 return true;
             }
 
             AsmJSCoercion coercion;
             if (!CheckTypeAnnotation(f.m(), coercionNode, &coercion))
                 return false;
 
@@ -3334,16 +3789,18 @@ CheckVarRef(FunctionCompiler &f, ParseNo
             *def = f.loadGlobalVar(*global);
             *type = global->varOrConstType().toType();
             break;
           case ModuleCompiler::Global::Function:
           case ModuleCompiler::Global::FFI:
           case ModuleCompiler::Global::MathBuiltinFunction:
           case ModuleCompiler::Global::FuncPtrTable:
           case ModuleCompiler::Global::ArrayView:
+          case ModuleCompiler::Global::SimdCtor:
+          case ModuleCompiler::Global::SimdOperation:
             return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
         }
         return true;
     }
 
     return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
 }
 
@@ -3356,17 +3813,17 @@ IsLiteralOrConstInt(FunctionCompiler &f,
     if (pn->getKind() != PNK_NAME)
         return false;
 
     PropertyName *name = pn->name();
     const ModuleCompiler::Global *global = f.lookupGlobal(name);
     if (!global || global->which() != ModuleCompiler::Global::ConstantLiteral)
         return false;
 
-    const Value &v = global->constLiteralValue().value();
+    const Value &v = global->constLiteralValue().scalarValue();
     if (!v.isInt32())
         return false;
 
     *u32 = (uint32_t) v.toInt32();
     return true;
 }
 
 static bool
@@ -3493,16 +3950,50 @@ CheckLoadArray(FunctionCompiler &f, Pars
         return false;
 
     *def = f.loadHeap(viewType, pointerDef, needsBoundsCheck);
     *type = TypedArrayLoadType(viewType);
     return true;
 }
 
 static bool
+CheckDotAccess(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type)
+{
+    JS_ASSERT(elem->isKind(PNK_DOT));
+
+    ParseNode *base = DotBase(elem);
+    MDefinition *baseDef;
+    Type baseType;
+    if (!CheckExpr(f, base, &baseDef, &baseType))
+        return false;
+    if (!baseType.isSimd())
+        return f.failf(base, "expected SIMD type, got %s", baseType.toChars());
+
+    ModuleCompiler &m = f.m();
+    PropertyName *field = DotMember(elem);
+
+    SimdLane lane;
+    JSAtomState &names = m.cx()->names();
+    if (field == names.x)
+        lane = LaneX;
+    else if (field == names.y)
+        lane = LaneY;
+    else if (field == names.z)
+        lane = LaneZ;
+    else if (field == names.w)
+        lane = LaneW;
+    else
+        return f.fail(base, "dot access field must be a lane name (x, y, z, w)");
+
+    *type = baseType.simdToScalarType();
+    *def = f.extractSimdElement(lane, baseDef, type->toMIRType());
+    return true;
+}
+
+static bool
 CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type)
 {
     Scalar::Type viewType;
     MDefinition *pointerDef;
     NeedsBoundsCheck needsBoundsCheck;
     if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef, &needsBoundsCheck))
         return false;
 
@@ -3909,16 +4400,18 @@ CheckIsExternType(FunctionCompiler &f, P
 static bool
 CheckFFICall(FunctionCompiler &f, ParseNode *callNode, unsigned ffiIndex, RetType retType,
              MDefinition **def, Type *type)
 {
     PropertyName *calleeName = CallCallee(callNode)->name();
 
     if (retType == RetType::Float)
         return f.fail(callNode, "FFI calls can't return float");
+    if (retType.toType().isSimd())
+        return f.fail(callNode, "FFI calls can't return SIMD values");
 
     FunctionCompiler::Call call(f, callNode, retType);
     if (!CheckCallArgs(f, callNode, CheckIsExternType, &call))
         return false;
 
     unsigned exitIndex;
     if (!f.m().addExit(ffiIndex, calleeName, Move(call.sig()), &exitIndex))
         return false;
@@ -3926,46 +4419,91 @@ CheckFFICall(FunctionCompiler &f, ParseN
     if (!f.ffiCall(exitIndex, call, retType.toMIRType(), def))
         return false;
 
     *type = retType.toType();
     return true;
 }
 
 static bool
+CheckFloatCoercionArg(FunctionCompiler &f, ParseNode *inputNode, Type inputType,
+                      MDefinition *inputDef, MDefinition **def)
+{
+    if (inputType.isMaybeDouble() || inputType.isSigned()) {
+        *def = f.unary<MToFloat32>(inputDef);
+        return true;
+    }
+    if (inputType.isUnsigned()) {
+        *def = f.unary<MAsmJSUnsignedToFloat32>(inputDef);
+        return true;
+    }
+    if (inputType.isFloatish()) {
+        *def = inputDef;
+        return true;
+    }
+
+    return f.failf(inputNode, "%s is not a subtype of signed, unsigned, double? or floatish",
+                   inputType.toChars());
+}
+
+static bool
 CheckCoercedCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type);
 
 static bool
+CheckCoercionArg(FunctionCompiler &f, ParseNode *arg, AsmJSCoercion expected, MDefinition **def,
+                 Type *type)
+{
+    RetType retType(expected);
+    if (arg->isKind(PNK_CALL))
+        return CheckCoercedCall(f, arg, retType, def, type);
+
+    MDefinition *argDef;
+    Type argType;
+    if (!CheckExpr(f, arg, &argDef, &argType))
+        return false;
+
+    switch (expected) {
+      case AsmJS_FRound:
+        if (!CheckFloatCoercionArg(f, arg, argType, argDef, def))
+            return false;
+        break;
+      case AsmJS_ToInt32x4:
+        if (!argType.isInt32x4())
+            return f.fail(arg, "argument to SIMD int32x4 coercion isn't int32x4");
+        *def = argDef;
+        break;
+      case AsmJS_ToFloat32x4:
+        if (!argType.isFloat32x4())
+            return f.fail(arg, "argument to SIMD float32x4 coercion isn't float32x4");
+        *def = argDef;
+        break;
+      case AsmJS_ToInt32:
+      case AsmJS_ToNumber:
+        MOZ_CRASH("not call coercions");
+    }
+
+    *type = retType.toType();
+    return true;
+}
+
+static bool
 CheckMathFRound(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, MathRetType *type)
 {
-    ParseNode *argNode = nullptr;
-    if (!IsFloatCoercion(f.m(), callNode, &argNode))
-        return f.fail(callNode, "invalid call to fround");
-
-    // Make sure to do this before calling CheckCoercedCall
-    *type = MathRetType::Float;
-
-    Type _;
-    if (argNode->isKind(PNK_CALL))
-        return CheckCoercedCall(f, argNode, RetType::Float, def, &_);
-
+    if (CallArgListLength(callNode) != 1)
+        return f.fail(callNode, "Math.fround must be passed 1 argument");
+
+    ParseNode *argNode = CallArgList(callNode);
     MDefinition *argDef;
     Type argType;
-    if (!CheckExpr(f, argNode, &argDef, &argType))
-        return false;
-
-    if (argType.isMaybeDouble() || argType.isSigned())
-        *def = f.unary<MToFloat32>(argDef);
-    else if (argType.isUnsigned())
-        *def = f.unary<MAsmJSUnsignedToFloat32>(argDef);
-    else if (argType.isFloatish())
-        *def = argDef;
-    else
-        return f.failf(argNode, "%s is not a subtype of signed, unsigned, double? or floatish", argType.toChars());
-
+    if (!CheckCoercionArg(f, argNode, AsmJS_FRound, &argDef, &argType))
+        return false;
+
+    JS_ASSERT(argType == Type::Float);
+    *def = argDef;
+    *type = MathRetType::Float;
     return true;
 }
 
 static bool
 CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinFunction func,
                      MDefinition **def, MathRetType *type)
 {
     unsigned arity = 0;
@@ -4038,28 +4576,129 @@ CheckMathBuiltinCall(FunctionCompiler &f
     if (!f.builtinCall(callee, call, varType.toMIRType(), def))
         return false;
 
     *type = MathRetType(opIsDouble ? MathRetType::Double : MathRetType::Floatish);
     return true;
 }
 
 static bool
+CheckBinarySimd(FunctionCompiler &f, ParseNode *call, AsmJSSimdType simdType,
+                MSimdBinaryArith::Operation op, MDefinition **def, Type *type)
+{
+    unsigned numArgs = CallArgListLength(call);
+    if (numArgs != 2)
+        return f.failf(call, "expected 2 arguments to binary arithmetic SIMD operation, got %u", numArgs);
+
+    ParseNode *lhs = CallArgList(call);
+    ParseNode *rhs = NextNode(lhs);
+
+    MDefinition *lhsDef, *rhsDef;
+    Type lhsType, rhsType;
+    if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
+        return false;
+    if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
+        return false;
+
+    Type retType = simdType;
+    JS_ASSERT_IF(retType.isInt32x4(), op != MSimdBinaryArith::Mul && op != MSimdBinaryArith::Div);
+    if (lhsType != retType || rhsType != retType)
+        return f.failf(lhs, "arguments to SIMD binary op should both be %s", retType.toChars());
+
+    *type = retType;
+    *def = f.binarySimd(lhsDef, rhsDef, op, retType.toMIRType());
+    return true;
+}
+
+static bool
+CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
+                       MDefinition **def, Type *type)
+{
+    JS_ASSERT(global->isSimdOperation());
+    switch (global->simdOperation()) {
+      case AsmJSSimdOperation_add:
+        return CheckBinarySimd(f, call, global->simdOperationType(), MSimdBinaryArith::Add, def, type);
+      case AsmJSSimdOperation_sub:
+        return CheckBinarySimd(f, call, global->simdOperationType(), MSimdBinaryArith::Sub, def, type);
+      case AsmJSSimdOperation_mul:
+        return CheckBinarySimd(f, call, global->simdOperationType(), MSimdBinaryArith::Mul, def, type);
+      case AsmJSSimdOperation_div:
+        return CheckBinarySimd(f, call, global->simdOperationType(), MSimdBinaryArith::Div, def, type);
+    }
+    MOZ_CRASH("unexpected simd operation in CheckSimdOperationCall");
+}
+
+static bool
+CheckSimdCtorCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
+                  MDefinition **def, Type *type)
+{
+    JS_ASSERT(call->isKind(PNK_CALL));
+
+    AsmJSCoercion coercion;
+    ParseNode *argNode;
+    if (IsCoercionCall(f.m(), call, &coercion, &argNode))
+        return CheckCoercionArg(f, argNode, coercion, def, type);
+
+    AsmJSSimdType simdType = global->simdCtorType();
+    unsigned numArgs = CallArgListLength(call);
+    unsigned length = SimdTypeToLength(simdType);
+    if (numArgs != length)
+        return f.failName(call, "invalid number of arguments in call to '%s'", CallCallee(call)->name());
+
+    Vector<MDefinition*, 4, SystemAllocPolicy> defs;
+    if (!defs.resize(length))
+        return false;
+
+    argNode = CallArgList(call);
+    size_t i = 0;
+    for (; argNode; argNode = NextNode(argNode), ++i)
+    {
+        JS_ASSERT(i < length);
+
+        Type argType;
+        if (!CheckExpr(f, argNode, &defs[i], &argType))
+            return false;
+
+        switch (simdType) {
+          case AsmJSSimdType_int32x4:
+            if (!argType.isIntish())
+                return f.failf(argNode, "argument %d of Int32x4 ctor isn't a subtype of intish", i);
+            break;
+          case AsmJSSimdType_float32x4:
+            if (!CheckFloatCoercionArg(f, argNode, argType, defs[i], &defs[i]))
+                return false;
+            break;
+        }
+    }
+    JS_ASSERT(i == length);
+
+    *type = simdType;
+    *def = f.constructSimd<MSimdValueX4>(defs[0], defs[1], defs[2], defs[3], type->toMIRType());
+    return true;
+}
+
+static bool
 CheckUncoercedCall(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
 {
     JS_ASSERT(expr->isKind(PNK_CALL));
 
     const ModuleCompiler::Global *global;
-    if (IsCallToGlobal(f.m(), expr, &global) && global->isMathFunction())
-    {
-        MathRetType mathRetType;
-        if (!CheckMathBuiltinCall(f, expr, global->mathBuiltinFunction(), def, &mathRetType))
-            return false;
-        *type = mathRetType.toType();
-        return true;
+    if (IsCallToGlobal(f.m(), expr, &global)) {
+        if (global->isMathFunction()) {
+            MathRetType mathRetType;
+            if (!CheckMathBuiltinCall(f, expr, global->mathBuiltinFunction(), def, &mathRetType))
+                return false;
+            *type = mathRetType.toType();
+            return true;
+        }
+
+        if (global->isSimdCtor())
+            return CheckSimdCtorCall(f, expr, global, def, type);
+        if (global->isSimdOperation())
+            return CheckSimdOperationCall(f, expr, global, def, type);
     }
 
     return f.fail(expr, "all function calls must either be calls to standard lib math functions, "
                         "ignored (via f(); or comma-expression), coerced to signed (via f()|0), "
                         "coerced to float (via fround(f())) or coerced to double (via +f())");
 }
 
 static bool
@@ -4067,16 +4706,20 @@ CheckCoercedMathBuiltinCall(FunctionComp
                             RetType retType, MDefinition **def, Type *type)
 {
     MDefinition *operand;
     MathRetType actualRetType;
     if (!CheckMathBuiltinCall(f, callNode, func, &operand, &actualRetType))
         return false;
 
     switch (retType.which()) {
+      case RetType::Int32x4:
+      case RetType::Float32x4:
+        return f.failf(callNode, "%s is not a vector type", actualRetType.toType().toChars());
+
       case RetType::Double:
         switch (actualRetType.which()) {
           case MathRetType::Double:
             *def = operand;
             break;
           case MathRetType::Float:
           case MathRetType::Signed:
             *def = f.unary<MToDouble>(operand);
@@ -4128,16 +4771,56 @@ CheckCoercedMathBuiltinCall(FunctionComp
     }
 
     JS_ASSERT_IF(retType == RetType::Void || f.inDeadCode(), !*def);
     JS_ASSERT_IF(retType != RetType::Void && !f.inDeadCode(), !!*def);
     return true;
 }
 
 static bool
+CheckCoercedSimdCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
+                     RetType retType, MDefinition **def, Type *type)
+{
+    if (global->isSimdCtor()) {
+        if (!CheckSimdCtorCall(f, call, global, def, type))
+            return false;
+    } else {
+        JS_ASSERT(global->isSimdOperation());
+        if (!CheckSimdOperationCall(f, call, global, def, type))
+            return false;
+    }
+
+    JS_ASSERT(type->isSimd());
+    switch (retType.which()) {
+      case RetType::Signed:
+      case RetType::Double:
+      case RetType::Float:
+        return f.failf(call, "SIMD call returns %s, used as scalar", type->toChars());
+
+      case RetType::Int32x4:
+        if (!type->isInt32x4())
+            return f.failf(call, "SIMD call returns %s, used as int32x4", type->toChars());
+        break;
+
+      case RetType::Float32x4:
+        if (!type->isFloat32x4())
+            return f.failf(call, "SIMD call returns %s, used as float32x4", type->toChars());
+        break;
+
+      case RetType::Void:
+        *def = nullptr;
+        break;
+    }
+
+    JS_ASSERT_IF(retType == RetType::Void || f.inDeadCode(), !*def);
+    JS_ASSERT_IF(retType != RetType::Void && !f.inDeadCode(), !!*def);
+    return true;
+}
+
+static bool
 CheckCoercedCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **def, Type *type)
 {
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
     ParseNode *callee = CallCallee(call);
 
     if (callee->isKind(PNK_ELEM))
         return CheckFuncPtrCall(f, call, retType, def, type);
@@ -4154,16 +4837,19 @@ CheckCoercedCall(FunctionCompiler &f, Pa
           case ModuleCompiler::Global::MathBuiltinFunction:
             return CheckCoercedMathBuiltinCall(f, call, global->mathBuiltinFunction(), retType, def, type);
           case ModuleCompiler::Global::ConstantLiteral:
           case ModuleCompiler::Global::ConstantImport:
           case ModuleCompiler::Global::Variable:
           case ModuleCompiler::Global::FuncPtrTable:
           case ModuleCompiler::Global::ArrayView:
             return f.failName(callee, "'%s' is not callable function", callee->name());
+          case ModuleCompiler::Global::SimdCtor:
+          case ModuleCompiler::Global::SimdOperation:
+            return CheckCoercedSimdCall(f, call, global, retType, def, type);
           case ModuleCompiler::Global::Function:
             break;
         }
     }
 
     return CheckInternalCall(f, call, calleeName, retType, def, type);
 }
 
@@ -4386,16 +5072,18 @@ IsValidIntMultiplyConstant(ModuleCompile
       case AsmJSNumLit::NegativeInt:
         if (abs(literal.toInt32()) < (1<<20))
             return true;
         return false;
       case AsmJSNumLit::BigUnsigned:
       case AsmJSNumLit::Double:
       case AsmJSNumLit::Float:
       case AsmJSNumLit::OutOfRangeInt:
+      case AsmJSNumLit::Int32x4:
+      case AsmJSNumLit::Float32x4:
         return false;
     }
 
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal");
 }
 
 static bool
 CheckMultiply(FunctionCompiler &f, ParseNode *star, MDefinition **def, Type *type)
@@ -4672,16 +5360,17 @@ CheckExpr(FunctionCompiler &f, ParseNode
         return false;
 
     if (IsNumericLiteral(f.m(), expr))
         return CheckNumericLiteral(f, expr, def, type);
 
     switch (expr->getKind()) {
       case PNK_NAME:        return CheckVarRef(f, expr, def, type);
       case PNK_ELEM:        return CheckLoadArray(f, expr, def, type);
+      case PNK_DOT:         return CheckDotAccess(f, expr, def, type);
       case PNK_ASSIGN:      return CheckAssign(f, expr, def, type);
       case PNK_POS:         return CheckPos(f, expr, def, type);
       case PNK_NOT:         return CheckNot(f, expr, def, type);
       case PNK_NEG:         return CheckNeg(f, expr, def, type);
       case PNK_BITNOT:      return CheckBitNot(f, expr, def, type);
       case PNK_COMMA:       return CheckComma(f, expr, def, type);
       case PNK_CONDITIONAL: return CheckConditional(f, expr, def, type);
       case PNK_STAR:        return CheckMultiply(f, expr, def, type);
@@ -5081,16 +5770,18 @@ CheckCaseExpr(FunctionCompiler &f, Parse
       case AsmJSNumLit::NegativeInt:
         *value = literal.toInt32();
         break;
       case AsmJSNumLit::OutOfRangeInt:
       case AsmJSNumLit::BigUnsigned:
         return f.fail(caseExpr, "switch case expression out of integer range");
       case AsmJSNumLit::Double:
       case AsmJSNumLit::Float:
+      case AsmJSNumLit::Int32x4:
+      case AsmJSNumLit::Float32x4:
         return f.fail(caseExpr, "switch case expression must be an integer literal");
     }
 
     return true;
 }
 
 static bool
 CheckDefaultAtEnd(FunctionCompiler &f, ParseNode *stmt)
@@ -5239,16 +5930,20 @@ CheckReturn(FunctionCompiler &f, ParseNo
 
     RetType retType;
     if (type.isSigned())
         retType = RetType::Signed;
     else if (type.isDouble())
         retType = RetType::Double;
     else if (type.isFloat())
         retType = RetType::Float;
+    else if (type.isInt32x4())
+        retType = RetType::Int32x4;
+    else if (type.isFloat32x4())
+        retType = RetType::Float32x4;
     else if (type.isVoid())
         retType = RetType::Void;
     else
         return f.failf(expr, "%s is not a valid return type", type.toChars());
 
     if (!CheckReturnType(f, expr, retType))
         return false;
 
@@ -5950,26 +6645,31 @@ static const RegisterSet NonVolatileRegs
     RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask &
                                    ~(uint32_t(1) << Registers::lr)),
                 FloatRegisterSet(FloatRegisters::NonVolatileMask | (1ULL << FloatRegisters::d15)));
 #else
 static const RegisterSet NonVolatileRegs =
     RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask),
                 FloatRegisterSet(FloatRegisters::NonVolatileMask));
 #endif
+static const FloatRegisterSet NonVolatileSimdRegs = SupportsSimd ? NonVolatileRegs.fpus()
+                                                                 : FloatRegisterSet();
 
 #if defined(JS_CODEGEN_MIPS)
 // Mips is using one more double slot due to stack alignment for double values.
 // Look at MacroAssembler::PushRegsInMask(RegisterSet set)
 static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) +
                                              NonVolatileRegs.fpus().getPushSizeInBytes() +
                                              sizeof(double);
 #else
-static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) +
-                                             NonVolatileRegs.fpus().getPushSizeInBytes();
+static const unsigned FramePushedAfterSave =
+   SupportsSimd ? NonVolatileRegs.gprs().size() * sizeof(intptr_t) +
+                  NonVolatileRegs.fpus().size() * Simd128DataSize
+                : NonVolatileRegs.gprs().size() * sizeof(intptr_t) +
+                  NonVolatileRegs.fpus().getPushSizeInBytes();
 #endif
 static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*);
 
 static bool
 GenerateEntry(ModuleCompiler &m, unsigned exportIndex)
 {
     MacroAssembler &masm = m.masm();
 
@@ -5984,17 +6684,17 @@ GenerateEntry(ModuleCompiler &m, unsigne
     masm.push(ra);
 #elif defined(JS_CODEGEN_X86)
     static const unsigned EntryFrameSize = sizeof(void*);
 #endif
 
     // Save all caller non-volatile registers before we clobber them here and in
     // the asm.js callee (which does not preserve non-volatile registers).
     masm.setFramePushed(0);
-    masm.PushRegsInMask(NonVolatileRegs);
+    masm.PushRegsInMask(NonVolatileRegs, NonVolatileSimdRegs);
     JS_ASSERT(masm.framePushed() == FramePushedAfterSave);
 
     // ARM and MIPS have a globally-pinned GlobalReg (x64 uses RIP-relative
     // addressing, x86 uses immediates in effective addresses). For the
     // AsmJSGlobalRegBias addition, see Assembler-(mips,arm).h.
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     masm.movePtr(IntArgReg1, GlobalReg);
     masm.addPtr(Imm32(AsmJSGlobalRegBias), GlobalReg);
@@ -6035,41 +6735,68 @@ GenerateEntry(ModuleCompiler &m, unsigne
     // Bump the stack for the call.
     PropertyName *funcName = m.module().exportedFunction(exportIndex).name();
     const ModuleCompiler::Func &func = *m.lookupFunction(funcName);
     masm.reserveStack(AlignBytes(StackArgBytes(func.sig().args()), AsmJSStackAlignment));
 
     // Copy parameters out of argv and into the registers/stack-slots specified by
     // the system ABI.
     for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) {
-        unsigned argOffset = iter.index() * sizeof(uint64_t);
+        unsigned argOffset = iter.index() * sizeof(AsmJSModule::EntryArg);
         Address src(argv, argOffset);
+        MIRType type = iter.mirType();
         switch (iter->kind()) {
           case ABIArg::GPR:
             masm.load32(src, iter->gpr());
             break;
           case ABIArg::FPU:
-            if (iter.mirType() == MIRType_Double) {
+            switch (type) {
+              case MIRType_Int32x4:
+                masm.loadUnalignedInt32x4(src, iter->fpu());
+                break;
+              case MIRType_Float32x4:
+                masm.loadUnalignedFloat32x4(src, iter->fpu());
+                break;
+              case MIRType_Double:
                 masm.loadDouble(src, iter->fpu());
-            } else {
-                JS_ASSERT(iter.mirType() == MIRType_Float32);
+                break;
+              case MIRType_Float32:
                 masm.loadFloat32(src, iter->fpu());
+                break;
+              default:
+                MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected FPU type");
+                break;
             }
             break;
           case ABIArg::Stack:
-            if (iter.mirType() == MIRType_Int32) {
+            switch (type) {
+              case MIRType_Int32:
                 masm.load32(src, scratch);
                 masm.storePtr(scratch, Address(StackPointer, iter->offsetFromArgBase()));
-            } else if (iter.mirType() == MIRType_Double) {
+                break;
+              case MIRType_Double:
                 masm.loadDouble(src, ScratchDoubleReg);
                 masm.storeDouble(ScratchDoubleReg, Address(StackPointer, iter->offsetFromArgBase()));
-            } else {
-                JS_ASSERT(iter.mirType() == MIRType_Float32);
+                break;
+              case MIRType_Float32:
                 masm.loadFloat32(src, ScratchFloat32Reg);
                 masm.storeFloat32(ScratchFloat32Reg, Address(StackPointer, iter->offsetFromArgBase()));
+                break;
+              case MIRType_Int32x4:
+                masm.loadUnalignedInt32x4(src, ScratchSimdReg);
+                masm.storeAlignedInt32x4(ScratchSimdReg,
+                                         Address(StackPointer, iter->offsetFromArgBase()));
+                break;
+              case MIRType_Float32x4:
+                masm.loadUnalignedFloat32x4(src, ScratchSimdReg);
+                masm.storeAlignedFloat32x4(ScratchSimdReg,
+                                           Address(StackPointer, iter->offsetFromArgBase()));
+                break;
+              default:
+                MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected stack arg type");
             }
             break;
         }
     }
 
     // Call into the real function.
     masm.assertStackAlignment(AsmJSStackAlignment);
     masm.call(CallSiteDesc(CallSiteDesc::Relative), &func.entry());
@@ -6091,20 +6818,28 @@ GenerateEntry(ModuleCompiler &m, unsigne
         break;
       case RetType::Float:
         masm.convertFloat32ToDouble(ReturnFloat32Reg, ReturnDoubleReg);
         // Fall through as ReturnDoubleReg now contains a Double
       case RetType::Double:
         masm.canonicalizeDouble(ReturnDoubleReg);
         masm.storeDouble(ReturnDoubleReg, Address(argv, 0));
         break;
+      case RetType::Int32x4:
+        // We don't have control on argv alignment, do an unaligned access.
+        masm.storeUnalignedInt32x4(ReturnSimdReg, Address(argv, 0));
+        break;
+      case RetType::Float32x4:
+        // We don't have control on argv alignment, do an unaligned access.
+        masm.storeUnalignedFloat32x4(ReturnSimdReg, Address(argv, 0));
+        break;
     }
 
     // Restore clobbered non-volatile registers of the caller.
-    masm.PopRegsInMask(NonVolatileRegs);
+    masm.PopRegsInMask(NonVolatileRegs, NonVolatileSimdRegs);
     JS_ASSERT(masm.framePushed() == 0);
 
     masm.move32(Imm32(true), ReturnReg);
     masm.ret();
 
     return m.finishGeneratingEntry(exportIndex, &begin) && !masm.oom();
 }
 
@@ -6219,16 +6954,19 @@ GenerateFFIInterpExit(ModuleCompiler &m,
         break;
       case RetType::Double:
         masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToNumber));
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.loadDouble(argv, ReturnDoubleReg);
         break;
       case RetType::Float:
         MOZ_CRASH("Float32 shouldn't be returned from a FFI");
+      case RetType::Int32x4:
+      case RetType::Float32x4:
+        MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
     }
 
     Label profilingReturn;
     GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::SlowFFI, &profilingReturn);
     return m.finishGeneratingInterpExit(exitIndex, &begin, &profilingReturn) && !masm.oom();
 }
 
 // On ARM/MIPS, we need to include an extra word of space at the top of the
@@ -6430,16 +7168,19 @@ GenerateFFIIonExit(ModuleCompiler &m, co
         masm.convertValueToInt32(JSReturnOperand, ReturnDoubleReg, ReturnReg, &oolConvert,
                                  /* -0 check */ false);
         break;
       case RetType::Double:
         masm.convertValueToDouble(JSReturnOperand, ReturnDoubleReg, &oolConvert);
         break;
       case RetType::Float:
         MOZ_CRASH("Float shouldn't be returned from a FFI");
+      case RetType::Int32x4:
+      case RetType::Float32x4:
+        MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
     }
 
     Label done;
     masm.bind(&done);
 
     Label profilingReturn;
     GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::IonFFI, &profilingReturn);
 
@@ -6621,17 +7362,17 @@ GenerateAsyncInterruptExit(ModuleCompile
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     // Be very careful here not to perturb the machine state before saving it
     // to the stack. In particular, add/sub instructions may set conditions in
     // the flags register.
     masm.push(Imm32(0));            // space for resumePC
     masm.pushFlags();               // after this we are safe to use sub
     masm.setFramePushed(0);         // set to zero so we can use masm.framePushed() below
-    masm.PushRegsInMask(AllRegsExceptSP); // save all GP/FP registers (except SP)
+    masm.PushRegsInMask(AllRegsExceptSP, AllRegsExceptSP.fpus()); // save all GP/FP registers (except SP)
 
     Register scratch = ABIArgGenerator::NonArgReturnReg0;
 
     // Store resumePC into the reserved space.
     masm.loadAsmJSActivation(scratch);
     masm.loadPtr(Address(scratch, AsmJSActivation::offsetOfResumePC()), scratch);
     masm.storePtr(scratch, Address(StackPointer, masm.framePushed() + sizeof(void*)));
 
@@ -6646,24 +7387,27 @@ GenerateAsyncInterruptExit(ModuleCompile
     masm.call(AsmJSImmPtr(AsmJSImm_HandleExecutionInterrupt));
 
     masm.branchIfFalseBool(ReturnReg, throwLabel);
 
     // Restore the StackPointer to it's position before the call.
     masm.mov(ABIArgGenerator::NonVolatileReg, StackPointer);
 
     // Restore the machine state to before the interrupt.
-    masm.PopRegsInMask(AllRegsExceptSP); // restore all GP/FP registers (except SP)
+    masm.PopRegsInMask(AllRegsExceptSP, AllRegsExceptSP.fpus()); // restore all GP/FP registers (except SP)
     masm.popFlags();              // after this, nothing that sets conditions
     masm.ret();                   // pop resumePC into PC
 #elif defined(JS_CODEGEN_MIPS)
     // Reserve space to store resumePC.
     masm.subPtr(Imm32(sizeof(intptr_t)), StackPointer);
     // set to zero so we can use masm.framePushed() below.
     masm.setFramePushed(0);
+    // When this platform supports SIMD extensions, we'll need to push high lanes
+    // of SIMD registers as well.
+    JS_STATIC_ASSERT(!SupportsSimd);
     // save all registers,except sp. After this stack is alligned.
     masm.PushRegsInMask(AllRegsExceptSP);
 
     // Save the stack pointer in a non-volatile register.
     masm.movePtr(StackPointer, s0);
     // Align the stack.
     masm.ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
 
@@ -6705,16 +7449,19 @@ GenerateAsyncInterruptExit(ModuleCompile
     // Align the stack.
     masm.ma_and(Imm32(~7), sp, sp);
 
     // Store resumePC into the return PC stack slot.
     masm.loadAsmJSActivation(IntArgReg0);
     masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfResumePC()), IntArgReg1);
     masm.storePtr(IntArgReg1, Address(r6, 14 * sizeof(uint32_t*)));
 
+    // When this platform supports SIMD extensions, we'll need to push and pop
+    // high lanes of SIMD registers as well.
+    JS_STATIC_ASSERT(!SupportsSimd);
     masm.PushRegsInMask(RegisterSet(GeneralRegisterSet(0), FloatRegisterSet(FloatRegisters::AllDoubleMask)));   // save all FP registers
 
     masm.assertStackAlignment(ABIStackAlignment);
     masm.call(AsmJSImm_HandleExecutionInterrupt);
 
     masm.branchIfFalseBool(ReturnReg, throwLabel);
 
     // Restore the machine state to before the interrupt. this will set the pc!
@@ -6786,17 +7533,17 @@ GenerateThrowStub(ModuleCompiler &m, Lab
     // frame.
     Register scratch = ABIArgGenerator::NonArgReturnReg0;
     masm.loadAsmJSActivation(scratch);
     masm.storePtr(ImmWord(0), Address(scratch, AsmJSActivation::offsetOfFP()));
 
     masm.setFramePushed(FramePushedForEntrySP);
     masm.loadPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP()), StackPointer);
     masm.Pop(scratch);
-    masm.PopRegsInMask(NonVolatileRegs);
+    masm.PopRegsInMask(NonVolatileRegs, NonVolatileSimdRegs);
     JS_ASSERT(masm.framePushed() == 0);
 
     masm.mov(ImmWord(0), ReturnReg);
     masm.ret();
 
     return m.finishGeneratingInlineStub(throwLabel) && !masm.oom();
 }
 
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -34,33 +34,51 @@ extern const JSFunctionSpec Int32x4Metho
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // X4
 
 static const char *laneNames[] = {"lane 0", "lane 1", "lane 2", "lane3"};
 
 template<typename V>
-static bool
-IsVectorObject(HandleValue v)
+bool
+js::IsVectorObject(HandleValue v)
 {
     if (!v.isObject())
         return false;
 
     JSObject &obj = v.toObject();
     if (!obj.is<TypedObject>())
         return false;
 
     TypeDescr &typeRepr = obj.as<TypedObject>().typeDescr();
     if (typeRepr.kind() != type::X4)
         return false;
 
     return typeRepr.as<X4TypeDescr>().type() == V::type;
 }
 
+template<typename V>
+bool
+js::ToSimdConstant(JSContext *cx, HandleValue v, jit::SimdConstant *out)
+{
+    typedef typename V::Elem Elem;
+    if (!IsVectorObject<V>(v)) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR);
+        return false;
+    }
+
+    Elem *mem = reinterpret_cast<Elem *>(v.toObject().as<TypedObject>().typedMem());
+    *out = jit::SimdConstant::CreateX4(mem);
+    return true;
+}
+
+template bool js::ToSimdConstant<Int32x4>(JSContext *cx, HandleValue v, jit::SimdConstant *out);
+template bool js::ToSimdConstant<Float32x4>(JSContext *cx, HandleValue v, jit::SimdConstant *out);
+
 template<typename Elem>
 static Elem
 TypedObjectMemory(HandleValue v)
 {
     TypedObject &obj = v.toObject().as<TypedObject>();
     MOZ_ASSERT(!obj.owner().isNeutered());
     return reinterpret_cast<Elem>(obj.typedMem());
 }
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -161,16 +161,22 @@ struct Int32x4 {
     static void setReturn(CallArgs &args, Elem value) {
         args.rval().setInt32(value);
     }
 };
 
 template<typename V>
 JSObject *CreateSimd(JSContext *cx, typename V::Elem *data);
 
+template<typename V>
+bool IsVectorObject(HandleValue v);
+
+template<typename V>
+bool ToSimdConstant(JSContext *cx, HandleValue v, jit::SimdConstant *out);
+
 #define DECLARE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags) \
 extern bool                                                          \
 simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp);
 FLOAT32X4_FUNCTION_LIST(DECLARE_SIMD_FLOAT32X4_FUNCTION)
 #undef DECLARE_SIMD_FLOAT32X4_FUNCTION
 
 #define DECLARE_SIMD_INT32x4_FUNCTION(Name, Func, Operands, Flags)   \
 extern bool                                                          \
--- a/js/src/jit-test/lib/asm.js
+++ b/js/src/jit-test/lib/asm.js
@@ -103,18 +103,18 @@ function assertAsmLinkFail(f)
 
     assertEq(isAsmJSModule(f), true);
 
     // Verify no error is thrown with warnings off
     var ret = f.apply(null, Array.slice(arguments, 1));
 
     assertEq(isAsmJSFunction(ret), false);
     if (typeof ret === 'object')
-        for (f of ret)
-            assertEq(isAsmJSFunction(f), false);
+        for (var i in ret)
+            assertEq(isAsmJSFunction(ret[i]), false);
 
     // Turn on warnings-as-errors
     var oldOpts = options("werror");
     assertEq(oldOpts.indexOf("werror"), -1);
 
     // Verify an error is thrown
     var caught = false;
     try {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testSIMD.js
@@ -0,0 +1,657 @@
+load(libdir + "asm.js");
+var heap = new ArrayBuffer(4096);
+
+// Set to true to see more JS debugging spew
+const DEBUG = false;
+
+if (!isSimdAvailable() || typeof SIMD === 'undefined') {
+    DEBUG && print("won't run tests as simd extensions aren't activated yet");
+    quit(0);
+}
+
+const I32 = 'var i4 = glob.SIMD.int32x4;'
+const I32A = 'var i4a = i4.add;'
+const I32S = 'var i4s = i4.sub;'
+const F32 = 'var f4 = glob.SIMD.float32x4;'
+const F32A = 'var f4a = f4.add;'
+const F32S = 'var f4s = f4.sub;'
+const F32M = 'var f4m = f4.mul;'
+const F32D = 'var f4d = f4.div;'
+const FROUND = 'var f32=glob.Math.fround;'
+
+const INT32_MAX = Math.pow(2, 31) - 1;
+const INT32_MIN = INT32_MAX + 1 | 0;
+
+const assertEqFFI = {assertEq:assertEq};
+
+function assertEqX4(real, expected) {
+    assertEq(real.x, expected[0]);
+    assertEq(real.y, expected[1]);
+    assertEq(real.z, expected[2]);
+    assertEq(real.w, expected[3]);
+}
+
+function CheckI4(header, code, expected) {
+    // code needs to contain a local called x
+    header = USE_ASM + I32 + header;
+    var lanes = ['x', 'y', 'z', 'w'];
+    for (var i = 0; i < 4; ++i) {
+        var lane = lanes[i];
+        assertEq(asmLink(asmCompile('glob', header + ';function f() {' + code + ';return x.' + lane + '|0} return f'), this)(), expected[i]);
+    }
+}
+
+function CheckF4(header, code, expected) {
+    // code needs to contain a local called x
+    var lanes = ['x', 'y', 'z', 'w'];
+    header = USE_ASM + F32 + header;
+    for (var i = 0; i < 4; ++i) {
+        var lane = lanes[i];
+        assertEq(asmLink(asmCompile('glob', header + ';function f() {' + code + ';return +x.' + lane + '} return f'), this)(), Math.fround(expected[i]));
+    }
+}
+
+try {
+
+// 1. Constructors
+
+// 1.1 Compilation
+assertAsmTypeFail('glob', USE_ASM + "var i4 = int32x4               ; return {}") ;
+assertAsmTypeFail('glob', USE_ASM + "var i4 = glob.int32x4          ; return {}") ;
+assertAsmTypeFail('glob', USE_ASM + "var i4 = glob.globglob.int32x4 ; return {}") ;
+assertAsmTypeFail('glob', USE_ASM + "var i4 = glob.Math.int32x4     ; return {}") ;
+assertAsmTypeFail('glob', USE_ASM + "var herd = glob.SIMD.ponyX4    ; return {}") ;
+
+// 1.2 Linking
+assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + I32 + "return {}"), {});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + I32 + "return {}"), {SIMD: 42});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + I32 + "return {}"), {SIMD: Math.fround});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + I32 + "return {}"), {SIMD: {}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + I32 + "return {}"), {SIMD: {int32x4: 42}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + I32 + "return {}"), {SIMD: {int32x4: Math.fround}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + I32 + "return {}"), {SIMD: {int32x4: new Array}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + I32 + "return {}"), {SIMD: {int32x4: SIMD.float32x4}});
+
+[Type, int32] = [TypedObject.StructType, TypedObject.int32];
+var MyStruct = new Type({'x': int32, 'y': int32, 'z': int32, 'w': int32});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + I32 + "return {}"), {SIMD: {int32x4: MyStruct}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + I32 + "return {}"), {SIMD: {int32x4: new MyStruct}});
+
+assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {} return f"), {SIMD:{int32x4: SIMD.int32x4}})(), undefined);
+
+assertAsmLinkFail(asmCompile('glob', USE_ASM + F32 + "return {}"), {SIMD: {float32x4: 42}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + F32 + "return {}"), {SIMD: {float32x4: Math.fround}});
+assertAsmLinkFail(asmCompile('glob', USE_ASM + F32 + "return {}"), {SIMD: {float32x4: new Array}});
+assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {} return f"), {SIMD:{float32x4: SIMD.float32x4}})(), undefined);
+
+// 1.3 Correctness
+// 1.3.1 Local variables declarations
+assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Int32x4(1,2,3,4);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4;} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4();} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1, 2);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1, 2, 3);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1, 2, 3, 4.0);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1, 2.0, 3, 4);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4a(1,2,3,4);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1,2,3,2+2|0);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1,2,3," + (INT32_MIN - 1) + ");} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(i4(1,2,3,4));} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {var x=i4(1,2,3,4);} return f"), this)(), undefined);
+assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {var x=i4(1,2,3," + (INT32_MAX + 1) + ");} return f"), this)(), undefined);
+
+assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4;} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4();} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3);} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1.,2.,3.);} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1.,2.,f32(3.),4.);} return f");
+assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {var x=f4(1.,2.,3.,4.);} return f"), this)(), undefined);
+assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4);} return f"), this)(), undefined);
+assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3," + (INT32_MIN - 1) + ");} return f"), this)(), undefined);
+assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3," + (INT32_MAX + 1) + ");} return f"), this)(), undefined);
+
+// Places where NumLit can creep in
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f(i) {i=i|0; var z=0; switch(i|0) {case i4(1,2,3,4): z=1; break; default: z=2; break;}} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f(i) {i=i|0; var z=0; return i * i4(1,2,3,4) | 0;} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f(i) {var x=i4(1,2,3,i4(4,5,6,7))} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "function f(i) {var x=i4(1,2,3,f4(4,5,6,7))} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "function f(i) {var x=f4(1,2,3,i4(4,5,6,7))} return f");
+
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {return i4(1,2,3,4);} return f"), this)(), [1, 2, 3, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {return i4(i4(1,2,3,4));} return f"), this)(), [1, 2, 3, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {return f4(1,2,3,4);} return f"), this)(), [1, 2, 3, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {return f4(f4(1,2,3,4));} return f"), this)(), [1, 2, 3, 4]);
+
+// Int32x4 ctor should accept int?
+assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + I32 + "var i32=new glob.Int32Array(heap); function f(i) {i=i|0; return i4(i4(i32[i>>2], 2, 3, 4))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [0, 2, 3, 4]);
+// Float32x4 ctor should accept floatish, i.e. float || float? || floatish
+assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + F32 + "var f32=new glob.Float32Array(heap); function f(i) {i=i|0; return f4(f4(f32[i>>2], 2, 3, 4))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [NaN, 2, 3, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "var f32=glob.Math.fround; function f(i) {i=i|0; return f4(f4(f32(1) + f32(2), 2, 3, 4))} return f"), this, {}, new ArrayBuffer(0x10000))(0x20000), [3, 2, 3, 4]);
+
+// 1.3.2 Reading values out of lanes
+assertAsmTypeFail('glob', USE_ASM + "function f() {var x=1; return x.y | 0;} return f");
+assertAsmTypeFail('glob', USE_ASM + "function f() {var x=1; return (x + x).y | 0;} return f");
+assertAsmTypeFail('glob', USE_ASM + "function f() {var x=1.; return x.y | 0;} return f");
+assertAsmTypeFail('glob', USE_ASM + "var f32=glob.Math.fround;" + I32 + "function f() {var x=f32(1); return x.y | 0;} return f");
+
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1,2,3,4); return x.length|0;} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=i4(1,2,3,4).y; return x|0;} return f");
+
+CheckI4('', 'var x=i4(0,0,0,0)', [0,0,0,0]);
+CheckI4('', 'var x=i4(1,2,3,4)', [1,2,3,4]);
+CheckI4('', 'var x=i4(' + INT32_MIN + ',2,3,' + INT32_MAX + ')', [INT32_MIN,2,3,INT32_MAX]);
+CheckI4('', 'var x=i4(1,2,3,4); var y=i4(5,6,7,8)', [1,2,3,4]);
+CheckI4('', 'var a=1; var b=i4(9,8,7,6); var c=13.37; var x=i4(1,2,3,4); var y=i4(5,6,7,8)', [1,2,3,4]);
+CheckI4('', 'var y=i4(5,6,7,8); var x=i4(1,2,3,4)', [1,2,3,4]);
+
+CheckF4('', 'var x=f4(' + INT32_MAX + ', 2, 3, ' + INT32_MIN + ')', [INT32_MAX, 2, 3, INT32_MIN]);
+CheckF4('', 'var x=f4(' + (INT32_MAX + 1) + ', 2, 3, 4)', [INT32_MAX + 1, 2, 3, 4]);
+CheckF4('', 'var x=f4(1.3, 2.4, 3.5, 98.76)', [1.3, 2.4, 3.5, 98.76]);
+CheckF4('', 'var x=f4(13.37, 2., 3., -0)', [13.37, 2, 3, -0]);
+
+// 1.3.3. Variable assignments
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4();} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2, 3);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1.0, 2, 3, 4);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2.0, 3, 4);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2, 3.0, 4);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2, 3, 4.0);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2, 3, x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); var c=4.0; x=i4(1, 2, 3, +c);} return f");
+
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + I32 + "var i32=new glob.Int32Array(heap); function f() {var x=i4(1,2,3,4); i32[0] = x;} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + I32 + "var i32=new glob.Int32Array(heap); function f() {var x=i4(1,2,3,4); x = i32[0];} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + F32 + "var f32=new glob.Float32Array(heap); function f() {var x=f4(1,2,3,4); f32[0] = x;} return f");
+assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + F32 + "var f32=new glob.Int32Array(heap); function f() {var x=f4(1,2,3,4); x = f32[0];} return f");
+
+CheckI4('', 'var x=i4(1,2,3,4); x=i4(5,6,7,8)', [5, 6, 7, 8]);
+CheckI4('', 'var x=i4(1,2,3,4); var c=6; x=i4(5,c|0,7,8)', [5, 6, 7, 8]);
+CheckI4('', 'var x=i4(8,7,6,5); x=i4(x.w|0,x.z|0,x.y|0,x.x|0)', [5, 6, 7, 8]);
+
+CheckF4(FROUND, 'var x=f4(1,2,3,4); var y=f32(7.); x=f4(5,6,y,8)', [5, 6, 7, 8]);
+CheckF4(FROUND, 'var x=f4(1,2,3,4); x=f4(f32(5.),6.,7.,8.)', [5, 6, 7, 8]);
+CheckF4(FROUND, 'var x=f4(1,2,3,4); x=f4(f32(5),6,7,8)', [5, 6, 7, 8]);
+CheckF4(FROUND, 'var x=f4(1,2,3,4); x=f4(f32(5.),f32(6.),f32(7.),f32(8.))', [5, 6, 7, 8]);
+CheckF4('', 'var x=f4(1.,2.,3.,4.); x=f4(5.,6.,7.,8.)', [5, 6, 7, 8]);
+CheckF4('', 'var x=f4(1.,2.,3.,4.); x=f4(1,2,3,4)', [1, 2, 3, 4]);
+CheckF4(FROUND, 'var x=f4(1.,2.,3.,4.); var y=f32(7.); x=f4(9, 4, 2, 1)', [9, 4, 2, 1]);
+CheckF4('', 'var x=f4(8.,7.,6.,5.); x=f4(x.w, x.z, x.y, x.x)', [5, 6, 7, 8]);
+
+// 1.3.4 Return values
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=1; return i4(x)} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=1; return i4(x + x)} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f() {var x=1.; return i4(x)} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + FROUND + "function f() {var x=f32(1.); return i4(x)} return f");
+
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f() {var x=i4(1,2,3,4); return i4(x)} return f"), this)(), [1,2,3,4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4); return f4(x)} return f"), this)(), [1,2,3,4]);
+
+// 1.3.5 Coerce and pass arguments
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {x=i4();} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x) {x=i4(1,2,3,4);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "function f(x,y) {x=i4(y);y=+y} return f");
+
+var i32x4 = SIMD.int32x4(1, 3, 3, 7);
+assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + "function f(x) {x=i4(x)} return f"), this)(i32x4), undefined);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + "function f(x) {x=i4(x); return i4(x);} return f"), this)(i32x4), [1,3,3,7]);
+
+var f32x4 = SIMD.float32x4(13.37, 42.42, -0, NaN);
+assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + "function f(x) {x=f4(x)} return f"), this)(f32x4), undefined);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + "function f(x) {x=f4(x); return f4(x);} return f"), this)(f32x4),
+           [Math.fround(13.37), Math.fround(42.42), -0, NaN]);
+
+function assertCaught(f) {
+    var caught = false;
+    try {
+        f.apply(null, Array.prototype.slice.call(arguments, 1));
+    } catch (e) {
+        DEBUG && print('Assert caught: ', e, '\n', e.stack);
+        assertEq(e instanceof TypeError, true);
+        caught = true;
+    }
+    assertEq(caught, true);
+}
+
+var f = asmLink(asmCompile('glob', USE_ASM + F32 + "function f(x) {x=f4(x); return f4(x);} return f"), this);
+assertCaught(f);
+assertCaught(f, 1);
+assertCaught(f, {});
+assertCaught(f, "I sincerely am a SIMD typed object.");
+assertCaught(f, SIMD.int32x4(1,2,3,4));
+
+var f = asmLink(asmCompile('glob', USE_ASM + I32 + "function f(x) {x=i4(x); return i4(x);} return f"), this);
+assertCaught(f);
+assertCaught(f, 1);
+assertCaught(f, {});
+assertCaught(f, "I sincerely am a SIMD typed object.");
+assertCaught(f, SIMD.float32x4(4,3,2,1));
+
+// 1.3.6 Globals
+// 1.3.6.1 Local globals
+// Read
+assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); function f() {var x=4; x=g|0;} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); function f() {var x=4.; x=+g;} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); var f32=glob.Math.fround; function f() {var x=f32(4.); x=f32(g);} return f");
+
+assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f() {var x=4; x=g|0;} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f() {var x=4.; x=+g;} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); var f32=glob.Math.fround; function f() {var x=f32(4.); x=f32(g);} return f");
+
+assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); x=i4(g);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); x=f4(g);} return f");
+
+assertAsmTypeFail('glob', USE_ASM + I32 + "var g=0; function f() {var x=i4(1,2,3,4); x=g|0;} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var g=0.; function f() {var x=i4(1,2,3,4); x=+g;} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var f32=glob.Math.fround; var g=f32(0.); function f() {var x=i4(1,2,3,4); x=f32(g);} return f");
+
+assertAsmTypeFail('glob', USE_ASM + F32 + "var g=0; function f() {var x=f4(0.,0.,0.,0.); x=g|0;} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + "var g=0.; function f() {var x=f4(0.,0.,0.,0.); x=+g;} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + "var f32=glob.Math.fround; var g=f32(0.); function f() {var x=f4(0.,0.,0.,0.); x=f32(g);} return f");
+
+CheckI4('var x=i4(1,2,3,4)', '', [1, 2, 3, 4]);
+CheckI4('var _=42; var h=i4(5,5,5,5); var __=13.37; var x=i4(4,7,9,2);', '', [4,7,9,2]);
+
+CheckF4('var x=f4(1.,2.,3.,4.)', '', [1, 2, 3, 4]);
+CheckF4('var _=42; var h=f4(5.,5.,5.,5.); var __=13.37; var x=f4(4.,13.37,9.,-0.);', '', [4, 13.37, 9, -0]);
+CheckF4('var x=f4(1,2,3,4)', '', [1, 2, 3, 4]);
+
+// Write
+assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); function f() {var x=4; g=x|0;} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); function f() {var x=4.; g=+x;} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var g=i4(1,2,3,4); var f32=glob.Math.fround; function f() {var x=f32(4.); g=f32(x);} return f");
+
+assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f() {var x=4; g=x|0;} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); function f() {var x=4.; g=+x;} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); var f32=glob.Math.fround; function f() {var x=f32(4.); g=f32(x);} return f");
+
+assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); g=i4(x);} return f");
+assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); g=f4(x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); g=f4(x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); g=i4(x);} return f");
+
+CheckI4('var x=i4(0,0,0,0);', 'x=i4(1,2,3,4)', [1,2,3,4]);
+CheckF4('var x=f4(0.,0.,0.,0.);', 'x=f4(5.,3.,4.,2.)', [5,3,4,2]);
+
+CheckI4('var x=i4(0,0,0,0); var y=42; var z=3.9; var w=13.37', 'x=i4(1,2,3,4); y=24; z=4.9; w=23.10;', [1,2,3,4]);
+CheckF4('var x=f4(0,0,0,0); var y=42; var z=3.9; var w=13.37', 'x=f4(1,2,3,4); y=24; z=4.9; w=23.10;', [1,2,3,4]);
+
+// 1.3.6.2 Imported globals
+// Read
+var int32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + I32 + "var g=i4(ffi.g); function f() {return i4(g)} return f"), this, {g: SIMD.int32x4(1,2,3,4)})();
+assertEq(int32x4.x, 1);
+assertEq(int32x4.y, 2);
+assertEq(int32x4.z, 3);
+assertEq(int32x4.w, 4);
+
+for (var v of [1, {}, "totally legit SIMD variable", SIMD.float32x4(1,2,3,4)])
+    assertCaught(asmCompile('glob', 'ffi', USE_ASM + I32 + "var g=i4(ffi.g); function f() {return i4(g)} return f"), this, {g: v});
+
+var float32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + F32 + "var g=f4(ffi.g); function f() {return f4(g)} return f"), this, {g: SIMD.float32x4(1,2,3,4)})();
+assertEq(float32x4.x, 1);
+assertEq(float32x4.y, 2);
+assertEq(float32x4.z, 3);
+assertEq(float32x4.w, 4);
+
+for (var v of [1, {}, "totally legit SIMD variable", SIMD.int32x4(1,2,3,4)])
+    assertCaught(asmCompile('glob', 'ffi', USE_ASM + F32 + "var g=f4(ffi.g); function f() {return f4(g)} return f"), this, {g: v});
+
+// Write
+var int32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + I32 + "var g=i4(ffi.g); function f() {g=i4(4,5,6,7); return i4(g)} return f"), this, {g: SIMD.int32x4(1,2,3,4)})();
+assertEq(int32x4.x, 4);
+assertEq(int32x4.y, 5);
+assertEq(int32x4.z, 6);
+assertEq(int32x4.w, 7);
+
+var float32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + F32 + "var g=f4(ffi.g); function f() {g=f4(4.,5.,6.,7.); return f4(g)} return f"), this, {g: SIMD.float32x4(1,2,3,4)})();
+assertEq(float32x4.x, 4);
+assertEq(float32x4.y, 5);
+assertEq(float32x4.z, 6);
+assertEq(float32x4.w, 7);
+
+// 2. SIMD operations
+// 2.1 Compilation
+assertAsmTypeFail('glob', USE_ASM + "var add = int32x4.add; return {}");
+assertAsmTypeFail('glob', USE_ASM + I32A + I32 + "return {}");
+assertAsmTypeFail('glob', USE_ASM + "var g = 3; var add = g.add; return {}");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var func = i4.doTheHarlemShake; return {}");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var div = i4.div; return {}");
+assertAsmTypeFail('glob', USE_ASM + "var f32 = glob.Math.fround; var i4a = f32.add; return {}");
+
+// 2.2 Linking
+assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + I32 + I32A + "function f() {} return f"), {});
+assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + I32 + I32A + "function f() {} return f"), {SIMD: Math.fround});
+
+var oldInt32x4Add = SIMD.int32x4.add;
+var code = asmCompile('glob', USE_ASM + I32 + I32A + "return {}");
+for (var v of [42, Math.fround, SIMD.float32x4.add, function(){}, SIMD.int32x4.mul]) {
+    SIMD.int32x4.add = v;
+    assertAsmLinkFail(code, {SIMD: {int32x4: SIMD.int32x4}});
+}
+SIMD.int32x4.add = oldInt32x4Add; // finally replace the add function with the original one
+assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + I32A + "function f() {} return f"), {SIMD: {int32x4: SIMD.int32x4}})(), undefined);
+
+// 2.3. Binary arithmetic operations
+// 2.3.1 Additions
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4a();} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4a(x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4a(x, x, x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4a(13, 37);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4a(23.10, 19.89);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4a(x, 42);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4a(x, 13.37);} return f");
+
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); var y=4; x=i4a(x, y);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(0,0,0,0); var y=4; x=i4a(y, y);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(0,0,0,0); var y=4; y=i4a(x, x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); x=i4a(x, y);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); y=i4a(x, y);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); y=i4a(x, x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); y=f4a(x, x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); y=f4a(x, y);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + F32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); x=f4a(y, y);} return f");
+
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + 'function f() {var x=i4(1,2,3,4); var y=0; y=i4(x,x)|0} return f');
+assertAsmTypeFail('glob', USE_ASM + I32 + I32A + 'function f() {var x=i4(1,2,3,4); var y=0.; y=+i4(x,x)} return f');
+
+CheckI4(I32A, 'var z=i4(1,2,3,4); var y=i4(0,1,0,3); var x=i4(0,0,0,0); x=i4a(z,y)', [1,3,3,7]);
+CheckI4(I32A, 'var x=i4(2,3,4,5); var y=i4(0,1,0,3); x=i4a(x,y)', [2,4,4,8]);
+CheckI4(I32A, 'var x=i4(1,2,3,4); x=i4a(x,x)', [2,4,6,8]);
+CheckI4(I32A, 'var x=i4(' + INT32_MAX + ',2,3,4); var y=i4(1,1,0,3); x=i4a(x,y)', [INT32_MIN,3,3,7]);
+CheckI4(I32A, 'var x=i4(' + INT32_MAX + ',2,3,4); var y=i4(1,1,0,3); x=i4(i4a(x,y))', [INT32_MIN,3,3,7]);
+
+CheckF4(F32A, 'var x=f4(1,2,3,4); x=f4a(x,x)', [2,4,6,8]);
+CheckF4(F32A, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4a(x,y)', [5,5,8,6]);
+CheckF4(F32A, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4a(x,y)', [Math.fround(13.37) + 4,5,8,6]);
+CheckF4(F32A, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4(f4a(x,y))', [Math.fround(13.37) + 4,5,8,6]);
+
+// 2.3.2. Subtracts
+CheckI4(I32S, 'var x=i4(1,2,3,4); var y=i4(-1,1,0,2); x=i4s(x,y)', [2,1,3,2]);
+CheckI4(I32S, 'var x=i4(5,4,3,2); var y=i4(1,2,3,4); x=i4s(x,y)', [4,2,0,-2]);
+CheckI4(I32S, 'var x=i4(1,2,3,4); x=i4s(x,x)', [0,0,0,0]);
+CheckI4(I32S, 'var x=i4(' + INT32_MIN + ',2,3,4); var y=i4(1,1,0,3); x=i4s(x,y)', [INT32_MAX,1,3,1]);
+CheckI4(I32S, 'var x=i4(' + INT32_MIN + ',2,3,4); var y=i4(1,1,0,3); x=i4(i4s(x,y))', [INT32_MAX,1,3,1]);
+
+CheckF4(F32S, 'var x=f4(1,2,3,4); x=f4s(x,x)', [0,0,0,0]);
+CheckF4(F32S, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4s(x,y)', [-3,-1,-2,2]);
+CheckF4(F32S, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4s(x,y)', [Math.fround(13.37) - 4,-1,-2,2]);
+CheckF4(F32S, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4(f4s(x,y))', [Math.fround(13.37) - 4,-1,-2,2]);
+
+// 2.3.3. Multiplications / Divisions
+assertAsmTypeFail('glob', USE_ASM + I32 + "var f4m=i4.mul; function f() {} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var f4d=i4.div; function f() {} return f");
+
+CheckF4(F32M, 'var x=f4(1,2,3,4); x=f4m(x,x)', [1,4,9,16]);
+CheckF4(F32M, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [4,6,15,8]);
+CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [Math.fround(13.37) * 4,6,15,8]);
+CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4(f4m(x,y))', [Math.fround(13.37) * 4,6,15,8]);
+
+// Test NaN
+var f32x4 = SIMD.float32x4(0, NaN, -0, NaN);
+var another = SIMD.float32x4(NaN, -1, -0, NaN);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32M + "function f(x, y) {x=f4(x); y=f4(y); x=f4m(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, 0, NaN]);
+
+CheckF4(F32D, 'var x=f4(1,2,3,4); x=f4d(x,x)', [1,1,1,1]);
+CheckF4(F32D, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4d(x,y)', [1/4,2/3,3/5,2]);
+CheckF4(F32D, 'var x=f4(13.37,1,1,4); var y=f4(4,0,-0.,2); x=f4d(x,y)', [Math.fround(13.37) / 4,+Infinity,-Infinity,2]);
+
+// Test NaN
+var f32x4 = SIMD.float32x4(0, 0, -0, NaN);
+var another = SIMD.float32x4(0, -0, 0, 0);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32D + "function f(x,y) {x=f4(x); y=f4(y); x=f4d(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, NaN, NaN]);
+
+// Dead code
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f(){var x=i4(1,2,3,4); return i4(x); x=i4(5,6,7,8); return i4(x);} return f'), this)(), [1, 2, 3, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f(){var x=i4(1,2,3,4); var c=0; return i4(x); c=x.x|0; return i4(x);} return f'), this)(), [1, 2, 3, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32A + 'function f(){var x=i4(1,2,3,4); var c=0; return i4(x); x=i4a(x,x); return i4(x);} return f'), this)(), [1, 2, 3, 4]);
+assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32S + 'function f(){var x=i4(1,2,3,4); var c=0; return i4(x); x=i4s(x,x); return i4(x);} return f'), this)(), [1, 2, 3, 4]);
+
+// 3. Function calls
+// 3.1. No math builtins
+assertAsmTypeFail('glob', USE_ASM + I32 + "var fround=glob.Math.fround; function f() {var x=i4(1,2,3,4); return +fround(x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var sin=glob.Math.sin; function f() {var x=i4(1,2,3,4); return +sin(x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var ceil=glob.Math.ceil; function f() {var x=i4(1,2,3,4); return +ceil(x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var pow=glob.Math.pow; function f() {var x=i4(1,2,3,4); return +pow(1.0, x);} return f");
+
+assertAsmTypeFail('glob', USE_ASM + I32 + "var fround=glob.Math.fround; function f() {var x=i4(1,2,3,4); x=i4(fround(3));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var sin=glob.Math.sin; function f() {var x=i4(1,2,3,4); x=i4(sin(3.0));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var ceil=glob.Math.sin; function f() {var x=i4(1,2,3,4); x=i4(ceil(3.0));} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + "var pow=glob.Math.pow; function f() {var x=i4(1,2,3,4); x=i4(pow(1.0, 2.0));} return f");
+
+// 3.2. FFI calls
+// Can't pass SIMD arguments to FFI
+assertAsmTypeFail('glob', 'ffi', USE_ASM + I32 + "var func=ffi.func; function f() {var x=i4(1,2,3,4); func(x);} return f");
+assertAsmTypeFail('glob', 'ffi', USE_ASM + F32 + "var func=ffi.func; function f() {var x=f4(1,2,3,4); func(x);} return f");
+
+// Can't have FFI return SIMD values
+assertAsmTypeFail('glob', 'ffi', USE_ASM + I32 + "var func=ffi.func; function f() {var x=i4(1,2,3,4); x=i4(func());} return f");
+assertAsmTypeFail('glob', 'ffi', USE_ASM + F32 + "var func=ffi.func; function f() {var x=f4(1,2,3,4); x=f4(func());} return f");
+
+// 3.3 Internal calls
+// asm.js -> asm.js
+// Retrieving values from asm.js
+var code = USE_ASM + I32 + I32A + `
+    var check = ffi.check;
+
+    function g() {
+        var i = 0;
+        var y = i4(0,0,0,0);
+        var tmp = i4(0,0,0,0); var z = i4(1,1,1,1);
+        var w = i4(5,5,5,5);
+        for (; (i|0) < 30; i = i + 1 |0)
+            y = i4a(z, y);
+        y = i4a(w, y);
+        check(y.x | 0, y.y | 0, y.z | 0, y.w | 0);
+        return i4(y);
+    }
+
+    function f(x) {
+        x = i4(x);
+        var y = i4(0,0,0,0);
+        y = i4(g());
+        check(y.x | 0, y.y | 0, y.z | 0, y.w | 0);
+        return i4(x);
+    }
+    return f;
+`;
+
+var v4 = SIMD.int32x4(1,2,3,4);
+function check(x, y, z, w) {
+    assertEq(x, 35);
+    assertEq(y, 35);
+    assertEq(z, 35);
+    assertEq(w, 35);
+}
+var ffi = {check};
+assertEqX4(asmLink(asmCompile('glob', 'ffi', code), this, ffi)(v4), [1,2,3,4]);
+
+// Passing arguments from asm.js to asm.js
+// TODO make this code look better with templatized strings
+var code = USE_ASM + I32 + I32A + `
+    var assertEq = ffi.assertEq;
+
+    function internal([args]) {
+        [coerc]
+        assertEq([last].x | 0, [i] | 0);
+        assertEq([last].y | 0, [i] + 1 |0);
+        assertEq([last].z | 0, [i] + 2 |0);
+        assertEq([last].w | 0, [i] + 3 |0);
+    }
+
+    function external() {
+        [decls]
+        internal([args]);
+    }
+    return external;
+`;
+
+var ffi = {assertEq};
+var args = '';
+var decls = '';
+var coerc = '';
+for (var i = 1; i < 10; ++i) {
+    var j = i;
+    args += ((i > 1) ? ', ':'') + 'x' + i;
+    decls += 'var x' + i + ' = i4(' + j++ + ', ' + j++ + ', ' + j++ + ', ' + j++ + ');\n';
+    coerc += 'x' + i + ' = i4(x' + i + ');\n';
+    last = 'x' + i;
+    var c = code.replace(/\[args\]/g, args)
+                .replace(/\[last\]/g, last)
+                .replace(/\[decls\]/i, decls)
+                .replace(/\[coerc\]/i, coerc)
+                .replace(/\[i\]/g, i);
+    asmLink(asmCompile('glob', 'ffi', c), this, ffi)();
+}
+
+// Stress-test for register spilling code and stack depth checks
+var code = `
+    "use asm";
+    var i4 = glob.SIMD.int32x4;
+    var i4a = i4.add;
+    var assertEq = ffi.assertEq;
+    function g() {
+        var x = i4(1,2,3,4);
+        var y = i4(2,3,4,5);
+        var z = i4(0,0,0,0);
+        z = i4a(x, y);
+        assertEq(z.x | 0, 3);
+        assertEq(z.y | 0, 5);
+        assertEq(z.z | 0, 7);
+        assertEq(z.w | 0, 9);
+    }
+    return g
+`
+asmLink(asmCompile('glob', 'ffi', code), this, assertEqFFI)();
+
+(function() {
+    var code = `
+        "use asm";
+        var i4 = glob.SIMD.int32x4;
+        var i4a = i4.add;
+        var assertEq = ffi.assertEq;
+        var one = ffi.one;
+
+        // Function call with arguments on the stack (1 on x64, 3 on x86)
+        function h(x1, x2, x3, x4, x5, x6, x7) {
+            x1=x1|0
+            x2=x2|0
+            x3=x3|0
+            x4=x4|0
+            x5=x5|0
+            x6=x6|0
+            x7=x7|0
+            return x1 + x2 |0
+        }
+
+        function g() {
+            var x = i4(1,2,3,4);
+            var y = i4(2,3,4,5);
+            var z = i4(0,0,0,0);
+            var w = 1;
+            z = i4a(x, y);
+            w = w + (one() | 0) | 0;
+            assertEq(z.x | 0, 3);
+            assertEq(z.y | 0, 5);
+            assertEq(z.z | 0, 7);
+            assertEq(z.w | 0, 9);
+            h(1, 2, 3, 4, 42, 42, 42)|0
+            return w | 0;
+        }
+        return g
+    `;
+
+    asmLink(asmCompile('glob', 'ffi', code), this, {assertEq: assertEq, one: () => 1})();
+})();
+
+// Function calls with mixed arguments on the stack (SIMD and scalar). In the
+// worst case (x64), we have 6 int arg registers and 8 float registers.
+(function() {
+    var code = `
+        "use asm";
+        var i4 = glob.SIMD.int32x4;
+        function h(
+            // In registers:
+            gpr1, gpr2, gpr3, gpr4, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8,
+            // On the stack:
+            sint1, ssimd1, sdouble1, ssimd2, sint2, sint3, sint4, ssimd3, sdouble2
+            )
+        {
+            gpr1=gpr1|0;
+            gpr2=gpr2|0;
+            gpr3=gpr3|0;
+            gpr4=gpr4|0;
+
+            xmm1=+xmm1;
+            xmm2=+xmm2;
+            xmm3=+xmm3;
+            xmm4=+xmm4;
+            xmm5=+xmm5;
+            xmm6=+xmm6;
+            xmm7=+xmm7;
+            xmm8=+xmm8;
+
+            sint1=sint1|0;
+            ssimd1=i4(ssimd1);
+            sdouble1=+sdouble1;
+            ssimd2=i4(ssimd2);
+            sint2=sint2|0;
+            sint3=sint3|0;
+            sint4=sint4|0;
+            ssimd3=i4(ssimd3);
+            sdouble2=+sdouble2;
+
+            return (ssimd1.x|0) + (ssimd2.y|0) + (ssimd3.z|0) + sint2 + gpr3 | 0;
+        }
+
+        function g() {
+            var simd1 = i4(1,2,3,4);
+            var simd2 = i4(5,6,7,8);
+            var simd3 = i4(9,10,11,12);
+            return h(1, 2, 3, 4,
+                     1., 2., 3., 4., 5., 6., 7., 8.,
+                     5, simd1, 9., simd2, 6, 7, 8, simd3, 10.) | 0;
+        }
+        return g
+    `;
+
+    assertEq(asmLink(asmCompile('glob', 'ffi', code), this)(), 1 + 6 + 11 + 6 + 3);
+})();
+
+// Check that the interrupt callback doesn't erase high components of simd
+// registers:
+
+// WARNING: must be the last test in this file
+(function() {
+    var iters = 2000000;
+    var code = `
+    "use asm";
+    var i4 = glob.SIMD.int32x4;
+    var i4a = i4.add;
+    function _() {
+        var i = 0;
+        var n = i4(0,0,0,0);
+        var one = i4(1,1,1,1);
+        for (; (i>>>0) < ` + iters + `; i=(i+1)>>>0) {
+            n = i4a(n, one);
+        }
+        return i4(n);
+    }
+    return _;`;
+    // This test relies on the fact that setting the timeout will call the
+    // interrupt callback at fixed intervals, even before the timeout.
+    timeout(1000);
+    var x4 = asmLink(asmCompile('glob', code), this)();
+    assertEq(x4.x, iters);
+    assertEq(x4.y, iters);
+    assertEq(x4.z, iters);
+    assertEq(x4.w, iters);
+})();
+
+} catch(e) {
+    print('Stack:', e.stack)
+    print('Error:', e)
+    throw e;
+}
+
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3548,46 +3548,48 @@ LIRGenerator::visitAsmJSLoadFFIFunc(MAsm
 
 bool
 LIRGenerator::visitAsmJSParameter(MAsmJSParameter *ins)
 {
     ABIArg abi = ins->abi();
     if (abi.argInRegister())
         return defineFixed(new(alloc()) LAsmJSParameter, ins, LAllocation(abi.reg()));
 
-    JS_ASSERT(IsNumberType(ins->type()));
+    JS_ASSERT(IsNumberType(ins->type()) || IsSimdType(ins->type()));
     return defineFixed(new(alloc()) LAsmJSParameter, ins, LArgument(abi.offsetFromArgBase()));
 }
 
 bool
 LIRGenerator::visitAsmJSReturn(MAsmJSReturn *ins)
 {
     MDefinition *rval = ins->getOperand(0);
     LAsmJSReturn *lir = new(alloc()) LAsmJSReturn;
     if (rval->type() == MIRType_Float32)
         lir->setOperand(0, useFixed(rval, ReturnFloat32Reg));
     else if (rval->type() == MIRType_Double)
         lir->setOperand(0, useFixed(rval, ReturnDoubleReg));
+    else if (IsSimdType(rval->type()))
+        lir->setOperand(0, useFixed(rval, ReturnSimdReg));
     else if (rval->type() == MIRType_Int32)
         lir->setOperand(0, useFixed(rval, ReturnReg));
     else
         MOZ_ASSUME_UNREACHABLE("Unexpected asm.js return type");
     return add(lir);
 }
 
 bool
 LIRGenerator::visitAsmJSVoidReturn(MAsmJSVoidReturn *ins)
 {
     return add(new(alloc()) LAsmJSVoidReturn);
 }
 
 bool
 LIRGenerator::visitAsmJSPassStackArg(MAsmJSPassStackArg *ins)
 {
-    if (IsFloatingPointType(ins->arg()->type())) {
+    if (IsFloatingPointType(ins->arg()->type()) || IsSimdType(ins->arg()->type())) {
         JS_ASSERT(!ins->arg()->isEmittedAtUses());
         return add(new(alloc()) LAsmJSPassStackArg(useRegisterAtStart(ins->arg())), ins);
     }
 
     return add(new(alloc()) LAsmJSPassStackArg(useRegisterOrConstantAtStart(ins->arg())), ins);
 }
 
 bool
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -488,16 +488,17 @@ MConstant *
 MConstant::New(TempAllocator &alloc, const Value &v, types::CompilerConstraintList *constraints)
 {
     return new(alloc) MConstant(v, constraints);
 }
 
 MConstant *
 MConstant::NewAsmJS(TempAllocator &alloc, const Value &v, MIRType type)
 {
+    JS_ASSERT(!IsSimdType(type));
     MConstant *constant = new(alloc) MConstant(v, nullptr);
     constant->setResultType(type);
     return constant;
 }
 
 MConstant *
 MConstant::NewConstraintlessObject(TempAllocator &alloc, JSObject *v)
 {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11179,17 +11179,17 @@ class MAsmJSStoreHeap : public MBinaryIn
     }
 };
 
 class MAsmJSLoadGlobalVar : public MNullaryInstruction
 {
     MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset, bool isConstant)
       : globalDataOffset_(globalDataOffset), isConstant_(isConstant)
     {
-        JS_ASSERT(IsNumberType(type));
+        JS_ASSERT(IsNumberType(type) || IsSimdType(type));
         setResultType(type);
         setMovable();
     }
 
     unsigned globalDataOffset_;
     bool isConstant_;
 
   public:
--- a/js/src/jit/shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/shared/BaseAssembler-x86-shared.h
@@ -2973,16 +2973,31 @@ public:
 
     void movss_rm(XMMRegisterID src, const void* address)
     {
         spew("movss      %s, %p",
              nameFPReg(src), address);
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address);
     }
+
+    void movdqa_rm(XMMRegisterID src, const void* address)
+    {
+        spew("movdqa     %s, %p",
+             nameFPReg(src), address);
+        m_formatter.prefix(PRE_SSE_66);
+        m_formatter.twoByteOp(OP2_MOVDQ_WdqVdq, (RegisterID)src, address);
+    }
+
+    void movaps_rm(XMMRegisterID src, const void* address)
+    {
+        spew("movaps     %s, %p",
+             nameFPReg(src), address);
+        m_formatter.twoByteOp(OP2_MOVPS_WpsVps, (RegisterID)src, address);
+    }
 #else
     JmpSrc movsd_ripr(XMMRegisterID dst)
     {
         spew("movsd      ?(%%rip), %s",
              nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteRipOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, 0);
         return JmpSrc(m_formatter.size());
@@ -2998,16 +3013,39 @@ public:
     JmpSrc movsd_rrip(XMMRegisterID src)
     {
         spew("movsd      %s, ?(%%rip)",
              nameFPReg(src));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteRipOp(OP2_MOVSD_WsdVsd, (RegisterID)src, 0);
         return JmpSrc(m_formatter.size());
     }
+    JmpSrc movss_rrip(XMMRegisterID src)
+    {
+        spew("movss      %s, ?(%%rip)",
+             nameFPReg(src));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteRipOp(OP2_MOVSD_WsdVsd, (RegisterID)src, 0);
+        return JmpSrc(m_formatter.size());
+    }
+    JmpSrc movdqa_rrip(XMMRegisterID src)
+    {
+        spew("movdqa      %s, ?(%%rip)",
+             nameFPReg(src));
+        m_formatter.prefix(PRE_SSE_66);
+        m_formatter.twoByteRipOp(OP2_MOVDQ_WdqVdq, (RegisterID)src, 0);
+        return JmpSrc(m_formatter.size());
+    }
+    JmpSrc movaps_rrip(XMMRegisterID src)
+    {
+        spew("movaps      %s, ?(%%rip)",
+             nameFPReg(src));
+        m_formatter.twoByteRipOp(OP2_MOVPS_WpsVps, (RegisterID)src, 0);
+        return JmpSrc(m_formatter.size());
+    }
 #endif
 
     void movaps_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("movaps     %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.twoByteOp(OP2_MOVAPS_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -314,20 +314,36 @@ CodeGeneratorX86Shared::visitCompareFAnd
 bool
 CodeGeneratorX86Shared::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins)
 {
     const MAsmJSPassStackArg *mir = ins->mir();
     Address dst(StackPointer, mir->spOffset());
     if (ins->arg()->isConstant()) {
         masm.storePtr(ImmWord(ToInt32(ins->arg())), dst);
     } else {
-        if (ins->arg()->isGeneralReg())
+        if (ins->arg()->isGeneralReg()) {
             masm.storePtr(ToRegister(ins->arg()), dst);
-        else
-            masm.storeDouble(ToFloatRegister(ins->arg()), dst);
+        } else {
+            switch (mir->input()->type()) {
+              case MIRType_Double:
+              case MIRType_Float32:
+                masm.storeDouble(ToFloatRegister(ins->arg()), dst);
+                return true;
+              // StackPointer is SimdStackAlignment-aligned and ABIArgGenerator guarantees stack
+              // offsets are SimdStackAlignment-aligned.
+              case MIRType_Int32x4:
+                masm.storeAlignedInt32x4(ToFloatRegister(ins->arg()), dst);
+                return true;
+              case MIRType_Float32x4:
+                masm.storeAlignedFloat32x4(ToFloatRegister(ins->arg()), dst);
+                return true;
+              default: break;
+            }
+            MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected mir type in AsmJSPassStackArg");
+        }
     }
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
 {
     if (ool->dest().isFloat()) {
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -149,16 +149,22 @@ LIRGeneratorShared::defineReturn(LInstru
 #endif
         break;
       case MIRType_Float32:
         lir->setDef(0, LDefinition(vreg, LDefinition::FLOAT32, LFloatReg(ReturnFloat32Reg)));
         break;
       case MIRType_Double:
         lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnDoubleReg)));
         break;
+      case MIRType_Int32x4:
+        lir->setDef(0, LDefinition(vreg, LDefinition::INT32X4, LFloatReg(ReturnSimdReg)));
+        break;
+      case MIRType_Float32x4:
+        lir->setDef(0, LDefinition(vreg, LDefinition::FLOAT32X4, LFloatReg(ReturnSimdReg)));
+        break;
       default:
         LDefinition::Type type = LDefinition::TypeFrom(mir->type());
         JS_ASSERT(type != LDefinition::DOUBLE && type != LDefinition::FLOAT32);
         lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ReturnReg)));
         break;
     }
 
     mir->setVirtualRegister(vreg);
--- a/js/src/jit/x64/Assembler-x64.cpp
+++ b/js/src/jit/x64/Assembler-x64.cpp
@@ -25,29 +25,45 @@ ABIArgGenerator::ABIArgGenerator()
 {}
 
 ABIArg
 ABIArgGenerator::next(MIRType type)
 {
 #if defined(XP_WIN)
     JS_STATIC_ASSERT(NumIntArgRegs == NumFloatArgRegs);
     if (regIndex_ == NumIntArgRegs) {
-        current_ = ABIArg(stackOffset_);
-        stackOffset_ += sizeof(uint64_t);
+        if (IsSimdType(type)) {
+            // On Win64, >64 bit args need to be passed by reference, but asm.js
+            // doesn't allow passing SIMD values to FFIs. The only way to reach
+            // here is asm to asm calls, so we can break the ABI here.
+            stackOffset_ = AlignBytes(stackOffset_, SimdStackAlignment);
+            current_ = ABIArg(stackOffset_);
+            stackOffset_ += Simd128DataSize;
+        } else {
+            stackOffset_ += sizeof(uint64_t);
+            current_ = ABIArg(stackOffset_);
+        }
         return current_;
     }
     switch (type) {
       case MIRType_Int32:
       case MIRType_Pointer:
         current_ = ABIArg(IntArgRegs[regIndex_++]);
         break;
       case MIRType_Float32:
       case MIRType_Double:
         current_ = ABIArg(FloatArgRegs[regIndex_++]);
         break;
+      case MIRType_Int32x4:
+      case MIRType_Float32x4:
+        // On Win64, >64 bit args need to be passed by reference, but asm.js
+        // doesn't allow passing SIMD values to FFIs. The only way to reach
+        // here is asm to asm calls, so we can break the ABI here.
+        current_ = ABIArg(FloatArgRegs[regIndex_++]);
+        break;
       default:
         MOZ_CRASH("Unexpected argument type");
     }
     return current_;
 #else
     switch (type) {
       case MIRType_Int32:
       case MIRType_Pointer:
@@ -62,16 +78,26 @@ ABIArgGenerator::next(MIRType type)
       case MIRType_Float32:
         if (floatRegIndex_ == NumFloatArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
         current_ = ABIArg(FloatArgRegs[floatRegIndex_++]);
         break;
+      case MIRType_Int32x4:
+      case MIRType_Float32x4:
+        if (floatRegIndex_ == NumFloatArgRegs) {
+            stackOffset_ = AlignBytes(stackOffset_, SimdStackAlignment);
+            current_ = ABIArg(stackOffset_);
+            stackOffset_ += Simd128DataSize;
+            break;
+        }
+        current_ = ABIArg(FloatArgRegs[floatRegIndex_++]);
+        break;
       default:
         MOZ_CRASH("Unexpected argument type");
     }
     return current_;
 #endif
 }
 
 // Avoid r11, which is the MacroAssembler's ScratchReg.
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -597,22 +597,40 @@ class Assembler : public AssemblerX86Sha
         return CodeOffsetLabel(masm.movl_ripr(dest.code()).offset());
     }
     CodeOffsetLabel loadRipRelativeInt64(Register dest) {
         return CodeOffsetLabel(masm.movq_ripr(dest.code()).offset());
     }
     CodeOffsetLabel loadRipRelativeDouble(FloatRegister dest) {
         return CodeOffsetLabel(masm.movsd_ripr(dest.code()).offset());
     }
+    CodeOffsetLabel loadRipRelativeFloat32(FloatRegister dest) {
+        return CodeOffsetLabel(masm.movss_ripr(dest.code()).offset());
+    }
+    CodeOffsetLabel loadRipRelativeInt32x4(FloatRegister dest) {
+        return CodeOffsetLabel(masm.movdqa_ripr(dest.code()).offset());
+    }
+    CodeOffsetLabel loadRipRelativeFloat32x4(FloatRegister dest) {
+        return CodeOffsetLabel(masm.movaps_ripr(dest.code()).offset());
+    }
     CodeOffsetLabel storeRipRelativeInt32(Register dest) {
         return CodeOffsetLabel(masm.movl_rrip(dest.code()).offset());
     }
     CodeOffsetLabel storeRipRelativeDouble(FloatRegister dest) {
         return CodeOffsetLabel(masm.movsd_rrip(dest.code()).offset());
     }
+    CodeOffsetLabel storeRipRelativeFloat32(FloatRegister dest) {
+        return CodeOffsetLabel(masm.movss_rrip(dest.code()).offset());
+    }
+    CodeOffsetLabel storeRipRelativeInt32x4(FloatRegister dest) {
+        return CodeOffsetLabel(masm.movdqa_rrip(dest.code()).offset());
+    }
+    CodeOffsetLabel storeRipRelativeFloat32x4(FloatRegister dest) {
+        return CodeOffsetLabel(masm.movaps_rrip(dest.code()).offset());
+    }
     CodeOffsetLabel leaRipRelative(Register dest) {
         return CodeOffsetLabel(masm.leaq_rip(dest.code()).offset());
     }
 
     void loadAsmJSActivation(Register dest) {
         CodeOffsetLabel label = loadRipRelativeInt64(dest);
         append(AsmJSGlobalAccess(label, AsmJSActivationGlobalDataOffset));
     }
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -344,38 +344,77 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LA
     return true;
 }
 
 bool
 CodeGeneratorX64::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
 {
     MAsmJSLoadGlobalVar *mir = ins->mir();
 
+    MIRType type = mir->type();
+    JS_ASSERT(IsNumberType(type) || IsSimdType(type));
+
     CodeOffsetLabel label;
-    if (mir->type() == MIRType_Int32)
+    switch (type) {
+      case MIRType_Int32:
         label = masm.loadRipRelativeInt32(ToRegister(ins->output()));
-    else
+        break;
+      case MIRType_Float32:
+        label = masm.loadRipRelativeFloat32(ToFloatRegister(ins->output()));
+        break;
+      case MIRType_Double:
         label = masm.loadRipRelativeDouble(ToFloatRegister(ins->output()));
+        break;
+      // Aligned access: code is aligned on PageSize + there is padding
+      // before the global data section.
+      case MIRType_Int32x4:
+        label = masm.loadRipRelativeInt32x4(ToFloatRegister(ins->output()));
+        break;
+      case MIRType_Float32x4:
+        label = masm.loadRipRelativeFloat32x4(ToFloatRegister(ins->output()));
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("unexpected type in visitAsmJSLoadGlobalVar");
+    }
+
     masm.append(AsmJSGlobalAccess(label, mir->globalDataOffset()));
     return true;
 }
 
 bool
 CodeGeneratorX64::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
 {
     MAsmJSStoreGlobalVar *mir = ins->mir();
 
     MIRType type = mir->value()->type();
-    JS_ASSERT(IsNumberType(type));
+    JS_ASSERT(IsNumberType(type) || IsSimdType(type));
 
     CodeOffsetLabel label;
-    if (type == MIRType_Int32)
+    switch (type) {
+      case MIRType_Int32:
         label = masm.storeRipRelativeInt32(ToRegister(ins->value()));
-    else
+        break;
+      case MIRType_Float32:
+        label = masm.storeRipRelativeFloat32(ToFloatRegister(ins->value()));
+        break;
+      case MIRType_Double:
         label = masm.storeRipRelativeDouble(ToFloatRegister(ins->value()));
+        break;
+      // Aligned access: code is aligned on PageSize + there is padding
+      // before the global data section.
+      case MIRType_Int32x4:
+        label = masm.storeRipRelativeInt32x4(ToFloatRegister(ins->value()));
+        break;
+      case MIRType_Float32x4:
+        label = masm.storeRipRelativeFloat32x4(ToFloatRegister(ins->value()));
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("unexpected type in visitAsmJSStoreGlobalVar");
+    }
+
     masm.append(AsmJSGlobalAccess(label, mir->globalDataOffset()));
     return true;
 }
 
 bool
 CodeGeneratorX64::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins)
 {
     MAsmJSLoadFuncPtr *mir = ins->mir();
--- a/js/src/jit/x86/Assembler-x86.cpp
+++ b/js/src/jit/x86/Assembler-x86.cpp
@@ -14,26 +14,36 @@ using namespace js::jit;
 ABIArgGenerator::ABIArgGenerator()
   : stackOffset_(0),
     current_()
 {}
 
 ABIArg
 ABIArgGenerator::next(MIRType type)
 {
-    current_ = ABIArg(stackOffset_);
     switch (type) {
       case MIRType_Int32:
       case MIRType_Pointer:
+        current_ = ABIArg(stackOffset_);
         stackOffset_ += sizeof(uint32_t);
         break;
       case MIRType_Float32: // Float32 moves are actually double moves
       case MIRType_Double:
+        current_ = ABIArg(stackOffset_);
         stackOffset_ += sizeof(uint64_t);
         break;
+      case MIRType_Int32x4:
+      case MIRType_Float32x4:
+        // SIMD values aren't passed in or out of C++, so we can make up
+        // whatever internal ABI we like. visitAsmJSPassArg assumes
+        // SimdStackAlignment.
+        stackOffset_ = AlignBytes(stackOffset_, SimdStackAlignment);
+        current_ = ABIArg(stackOffset_);
+        stackOffset_ += Simd128DataSize;
+        break;
       default:
         MOZ_CRASH("Unexpected argument type");
     }
     return current_;
 }
 
 const Register ABIArgGenerator::NonArgReturnReg0 = ecx;
 const Register ABIArgGenerator::NonArgReturnReg1 = edx;
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -518,16 +518,26 @@ class Assembler : public AssemblerX86Sha
         masm.movss_mr(src.addr, dest.code());
         return CodeOffsetLabel(masm.currentOffset());
     }
     CodeOffsetLabel movsdWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.movsd_mr(src.addr, dest.code());
         return CodeOffsetLabel(masm.currentOffset());
     }
+    CodeOffsetLabel movdqaWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movdqa_mr(src.addr, dest.code());
+        return CodeOffsetLabel(masm.currentOffset());
+    }
+    CodeOffsetLabel movapsWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movaps_mr(src.addr, dest.code());
+        return CodeOffsetLabel(masm.currentOffset());
+    }
 
     // Store to *dest where dest can be patched.
     CodeOffsetLabel movbWithPatch(Register src, PatchedAbsoluteAddress dest) {
         masm.movb_rm(src.code(), dest.addr);
         return CodeOffsetLabel(masm.currentOffset());
     }
     CodeOffsetLabel movwWithPatch(Register src, PatchedAbsoluteAddress dest) {
         masm.movw_rm(src.code(), dest.addr);
@@ -542,16 +552,26 @@ class Assembler : public AssemblerX86Sha
         masm.movss_rm(src.code(), dest.addr);
         return CodeOffsetLabel(masm.currentOffset());
     }
     CodeOffsetLabel movsdWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) {
         JS_ASSERT(HasSSE2());
         masm.movsd_rm(src.code(), dest.addr);
         return CodeOffsetLabel(masm.currentOffset());
     }
+    CodeOffsetLabel movdqaWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movdqa_rm(src.code(), dest.addr);
+        return CodeOffsetLabel(masm.currentOffset());
+    }
+    CodeOffsetLabel movapsWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movaps_rm(src.code(), dest.addr);
+        return CodeOffsetLabel(masm.currentOffset());
+    }
 
     void loadAsmJSActivation(Register dest) {
         CodeOffsetLabel label = movlWithPatch(PatchedAbsoluteAddress(), dest);
         append(AsmJSGlobalAccess(label, AsmJSActivationGlobalDataOffset));
     }
 };
 
 // Get a register in which we plan to put a quantity that will be used as an
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -457,44 +457,74 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LA
     return true;
 }
 
 bool
 CodeGeneratorX86::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
 {
     MAsmJSLoadGlobalVar *mir = ins->mir();
     MIRType type = mir->type();
-    JS_ASSERT(IsNumberType(type));
+    JS_ASSERT(IsNumberType(type) || IsSimdType(type));
 
     CodeOffsetLabel label;
-    if (type == MIRType_Int32)
+    switch (type) {
+      case MIRType_Int32:
         label = masm.movlWithPatch(PatchedAbsoluteAddress(), ToRegister(ins->output()));
-    else if (type == MIRType_Float32)
+        break;
+      case MIRType_Float32:
         label = masm.movssWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output()));
-    else
+        break;
+      case MIRType_Double:
         label = masm.movsdWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output()));
+        break;
+      // Aligned access: code is aligned on PageSize + there is padding
+      // before the global data section.
+      case MIRType_Int32x4:
+        label = masm.movdqaWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output()));
+        break;
+      case MIRType_Float32x4:
+        label = masm.movapsWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output()));
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("unexpected type in visitAsmJSLoadGlobalVar");
+    }
     masm.append(AsmJSGlobalAccess(label, mir->globalDataOffset()));
     return true;
 }
 
 bool
 CodeGeneratorX86::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins)
 {
     MAsmJSStoreGlobalVar *mir = ins->mir();
 
     MIRType type = mir->value()->type();
-    JS_ASSERT(IsNumberType(type));
+    JS_ASSERT(IsNumberType(type) || IsSimdType(type));
 
     CodeOffsetLabel label;
-    if (type == MIRType_Int32)
+    switch (type) {
+      case MIRType_Int32:
         label = masm.movlWithPatch(ToRegister(ins->value()), PatchedAbsoluteAddress());
-    else if (type == MIRType_Float32)
+        break;
+      case MIRType_Float32:
         label = masm.movssWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress());
-    else
+        break;
+      case MIRType_Double:
         label = masm.movsdWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress());
+        break;
+      // Aligned access: code is aligned on PageSize + there is padding
+      // before the global data section.
+      case MIRType_Int32x4:
+        label = masm.movdqaWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress());
+        break;
+      case MIRType_Float32x4:
+        label = masm.movapsWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress());
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("unexpected type in visitAsmJSStoreGlobalVar");
+    }
     masm.append(AsmJSGlobalAccess(label, mir->globalDataOffset()));
     return true;
 }
 
 bool
 CodeGeneratorX86::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins)
 {
     MAsmJSLoadFuncPtr *mir = ins->mir();
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -286,16 +286,17 @@ MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND,     0
 MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT,     0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer")
 MSG_DEF(JSMSG_SEMI_BEFORE_STMNT,       0, JSEXN_SYNTAXERR, "missing ; before statement")
 MSG_DEF(JSMSG_SOURCE_TOO_LONG,         0, JSEXN_RANGEERR, "source is too long")
 MSG_DEF(JSMSG_STRICT_CODE_LET_EXPR_STMT, 0, JSEXN_ERR, "strict mode code may not contain unparenthesized let expression statements")
 MSG_DEF(JSMSG_STRICT_CODE_WITH,        0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements")
 MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
 MSG_DEF(JSMSG_SYNTAX_ERROR,            0, JSEXN_SYNTAXERR, "syntax error")
 MSG_DEF(JSMSG_TEMPLSTR_UNTERM_EXPR,    0, JSEXN_SYNTAXERR, "missing } in template string")
+MSG_DEF(JSMSG_SIMD_NOT_A_VECTOR,       0, JSEXN_TYPEERR, "value isn't a SIMD value object")
 MSG_DEF(JSMSG_TOO_MANY_CASES,          0, JSEXN_INTERNALERR, "too many switch cases")
 MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS,     0, JSEXN_SYNTAXERR, "too many catch variables")
 MSG_DEF(JSMSG_TOO_MANY_CON_ARGS,       0, JSEXN_SYNTAXERR, "too many constructor arguments")
 MSG_DEF(JSMSG_TOO_MANY_DEFAULTS,       0, JSEXN_SYNTAXERR, "more than one switch default")
 MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS,       0, JSEXN_SYNTAXERR, "too many function arguments")
 MSG_DEF(JSMSG_TOO_MANY_LOCALS,         0, JSEXN_SYNTAXERR, "too many local variables")
 MSG_DEF(JSMSG_TOUGH_BREAK,             0, JSEXN_SYNTAXERR, "unlabeled break must be inside loop or switch")
 MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT,   0, JSEXN_SYNTAXERR, "function statement requires a name")