Merge m-c to b2g-inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 30 Aug 2014 22:39:33 -0400
changeset 224347 1db35d2c9a2f66db04dc323977fa2aa35ba9db4a
parent 224346 2a6eccc8a67b27c04306b6920c24651ac3f60ee2 (current diff)
parent 224263 7ace84a1570b69bbec068f6ea76daa5427864fb6 (diff)
child 224348 7cbf5e0d28f019e300e9a4b345891efb8783f8ef
child 224364 8c9024c845a9282fbb995050c30f3a4bbd036473
child 224390 e0638ce22c6e8e01bd2ea12246c9eb6c737e4bf6
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone34.0a1
first release with
nightly linux32
1db35d2c9a2f / 34.0a1 / 20140831030206 / files
nightly linux64
1db35d2c9a2f / 34.0a1 / 20140831030206 / files
nightly mac
1db35d2c9a2f / 34.0a1 / 20140831030206 / files
nightly win32
1db35d2c9a2f / 34.0a1 / 20140831030206 / files
nightly win64
1db35d2c9a2f / 34.0a1 / 20140831030206 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound. a=merge
addon-sdk/source/modules/system/moz.build
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -545,16 +545,20 @@ GK_ATOM(lowerFirst, "lower-first")
 GK_ATOM(lowest, "lowest")
 GK_ATOM(lowsrc, "lowsrc")
 GK_ATOM(ltr, "ltr")
 GK_ATOM(lwtheme, "lwtheme")
 GK_ATOM(lwthemetextcolor, "lwthemetextcolor")
 GK_ATOM(main, "main")
 GK_ATOM(map, "map")
 GK_ATOM(manifest, "manifest")
+GK_ATOM(marginBottom, "margin-bottom")
+GK_ATOM(marginLeft, "margin-left")
+GK_ATOM(marginRight, "margin-right")
+GK_ATOM(marginTop, "margin-top")
 GK_ATOM(marginheight, "marginheight")
 GK_ATOM(marginwidth, "marginwidth")
 GK_ATOM(mark, "mark")
 GK_ATOM(marquee, "marquee")
 GK_ATOM(match, "match")
 GK_ATOM(max, "max")
 GK_ATOM(maxheight, "maxheight")
 GK_ATOM(maximum_scale, "maximum-scale")
@@ -1106,16 +1110,17 @@ GK_ATOM(target, "target")
 GK_ATOM(targets, "targets")
 GK_ATOM(tbody, "tbody")
 GK_ATOM(td, "td")
 GK_ATOM(_template, "template")
 GK_ATOM(text_decoration, "text-decoration")
 GK_ATOM(terminate, "terminate")
 GK_ATOM(test, "test")
 GK_ATOM(text, "text")
+GK_ATOM(textAlign, "text-align")
 GK_ATOM(textarea, "textarea")
 GK_ATOM(textbox, "textbox")
 GK_ATOM(textnode, "textnode")
 GK_ATOM(textNodeDirectionalityMap, "textNodeDirectionalityMap")
 GK_ATOM(tfoot, "tfoot")
 GK_ATOM(th, "th")
 GK_ATOM(thead, "thead")
 GK_ATOM(thumb, "thumb")
@@ -2229,20 +2234,16 @@ GK_ATOM(hitregion, "hitregion")
 GK_ATOM(InlineBlockFrame, "InlineBlockFrame")
 GK_ATOM(inlinevalue, "inline")
 GK_ATOM(invalid, "invalid")
 GK_ATOM(item, "item")
 GK_ATOM(itemset, "itemset")
 GK_ATOM(lineNumber, "line-number")
 GK_ATOM(linkedPanel, "linkedpanel")
 GK_ATOM(live, "live")
-GK_ATOM(marginBottom, "margin-bottom")
-GK_ATOM(marginLeft, "margin-left")
-GK_ATOM(marginRight, "margin-right")
-GK_ATOM(marginTop, "margin-top")
 GK_ATOM(menuitemcheckbox, "menuitemcheckbox")
 GK_ATOM(menuitemradio, "menuitemradio")
 GK_ATOM(mixed, "mixed")
 GK_ATOM(multiline, "multiline")
 GK_ATOM(password, "password")
 GK_ATOM(posinset, "posinset")
 GK_ATOM(presentation, "presentation")
 GK_ATOM(progressbar, "progressbar")
@@ -2251,17 +2252,16 @@ GK_ATOM(rowgroup, "rowgroup")
 GK_ATOM(rowheader, "rowheader")
 GK_ATOM(select1, "select1")
 GK_ATOM(setsize, "setsize")
 GK_ATOM(spelling, "spelling")
 GK_ATOM(spinbutton, "spinbutton")
 GK_ATOM(status, "status")
 GK_ATOM(tableCellIndex, "table-cell-index")
 GK_ATOM(tablist, "tablist")
-GK_ATOM(textAlign, "text-align")
 GK_ATOM(textIndent, "text-indent")
 GK_ATOM(textInputType, "text-input-type")
 GK_ATOM(textLineThroughColor, "text-line-through-color")
 GK_ATOM(textLineThroughStyle, "text-line-through-style")
 GK_ATOM(textPosition, "text-position")
 GK_ATOM(textUnderlineColor, "text-underline-color")
 GK_ATOM(textUnderlineStyle, "text-underline-style")
 GK_ATOM(timer, "timer")
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -254,25 +254,34 @@ nsImageLoadingContent::OnStopRequest(img
   // XXXkhuey should this be GetOurCurrentDoc?  Decoding if we're not in
   // the document seems silly.
   bool startedDecoding = false;
   nsIDocument* doc = GetOurOwnerDoc();
   nsIPresShell* shell = doc ? doc->GetShell() : nullptr;
   if (shell && shell->IsVisible() &&
       (!shell->DidInitialize() || shell->IsPaintingSuppressed())) {
 
-    // If we've gotten a frame and that frame has called FrameCreate and that
-    // frame has been reflowed then we know that it checked it's own visibility
-    // so we can trust our visible count and we don't start decode if we are not
-    // visible.
     nsIFrame* f = GetOurPrimaryFrame();
-    if (!mFrameCreateCalled || !f || (f->GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
-        mVisibleCount > 0 || shell->AssumeAllImagesVisible()) {
-      if (NS_SUCCEEDED(mCurrentRequest->StartDecoding())) {
-        startedDecoding = true;
+    // If we haven't gotten a frame yet either we aren't going to (so don't
+    // bother kicking off a decode), or we will get very soon on the next
+    // refresh driver tick when it flushes. And it will most likely be a
+    // specific image type frame (we only create generic (ie inline) type
+    // frames for images that don't have a size, and since we have all the data
+    // we should have the size) which will check its own visibility on its
+    // first reflow.
+    if (f) {
+      // If we've gotten a frame and that frame has called FrameCreate and that
+      // frame has been reflowed then we know that it checked it's own visibility
+      // so we can trust our visible count and we don't start decode if we are not
+      // visible.
+      if (!mFrameCreateCalled || (f->GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
+          mVisibleCount > 0 || shell->AssumeAllImagesVisible()) {
+        if (NS_SUCCEEDED(mCurrentRequest->StartDecoding())) {
+          startedDecoding = true;
+        }
       }
     }
   }
 
   // We want to give the decoder a chance to find errors. If we haven't found
   // an error yet and we've started decoding, either from the above
   // StartDecoding or from some other place, we must only fire these events
   // after we finish decoding.
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -22,16 +22,20 @@ namespace layers {
 static DXGI_FORMAT
 SurfaceFormatToDXGIFormat(gfx::SurfaceFormat aFormat)
 {
   switch (aFormat) {
     case SurfaceFormat::B8G8R8A8:
       return DXGI_FORMAT_B8G8R8A8_UNORM;
     case SurfaceFormat::B8G8R8X8:
       return DXGI_FORMAT_B8G8R8A8_UNORM;
+    case SurfaceFormat::R8G8B8A8:
+      return DXGI_FORMAT_R8G8B8A8_UNORM;
+    case SurfaceFormat::R8G8B8X8:
+      return DXGI_FORMAT_R8G8B8A8_UNORM;
     case SurfaceFormat::A8:
       return DXGI_FORMAT_A8_UNORM;
     default:
       MOZ_ASSERT(false, "unsupported format");
       return DXGI_FORMAT_UNKNOWN;
   }
 }
 
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3380,19 +3380,36 @@ gfxFont::DrawGlyphs(gfxShapedText       
                             }
                             gfxPoint pt(ToDeviceUnits(glyphX, aRunParams.devPerApp),
                                         ToDeviceUnits(aPt->y, aRunParams.devPerApp));
                             gfxFloat advanceDevUnits =
                                 ToDeviceUnits(advance, aRunParams.devPerApp);
                             gfxFloat height = GetMetrics().maxAscent;
                             gfxRect glyphRect(pt.x, pt.y - height,
                                               advanceDevUnits, height);
+
+                            // If there's a fake-italic skew in effect as part
+                            // of the drawTarget's transform, we need to remove
+                            // this before drawing the hexbox. (Bug 983985)
+                            Matrix oldMat;
+                            if (aFontParams.passedInvMatrix) {
+                                oldMat = aRunParams.dt->GetTransform();
+                                aRunParams.dt->SetTransform(
+                                    *aFontParams.passedInvMatrix * oldMat);
+                            }
+
                             gfxFontMissingGlyphs::DrawMissingGlyph(
                                 aRunParams.context, glyphRect, details->mGlyphID,
                                 aShapedText->GetAppUnitsPerDevUnit());
+
+                            // Restore the matrix, if we modified it before
+                            // drawing the hexbox.
+                            if (aFontParams.passedInvMatrix) {
+                                aRunParams.dt->SetTransform(oldMat);
+                            }
                         }
                     } else {
                         gfxPoint glyphXY(*aPt);
                         glyphXY.x += details->mXOffset;
                         glyphXY.y += details->mYOffset;
                         DrawOneGlyph(details->mGlyphID, advance, &glyphXY,
                                      buffer, &emittedGlyphs);
                     }
--- a/js/src/asmjs/AsmJSFrameIterator.cpp
+++ b/js/src/asmjs/AsmJSFrameIterator.cpp
@@ -351,21 +351,21 @@ js::GenerateAsmJSStackOverflowExit(Macro
     // value again. Do not update AsmJSFrame::callerFP as it is not necessary in
     // the non-profiling case (there is no return path from this point) and, in
     // the profiling case, it is already correct.
     Register activation = ABIArgGenerator::NonArgReturnReg0;
     masm.loadAsmJSActivation(activation);
     masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfFP()));
 
     // Prepare the stack for calling C++.
-    if (unsigned stackDec = StackDecrementForCall(sizeof(AsmJSFrame), ShadowStackSpace))
-        masm.subPtr(Imm32(stackDec), StackPointer);
+    if (uint32_t d = StackDecrementForCall(ABIStackAlignment, sizeof(AsmJSFrame), ShadowStackSpace))
+        masm.subPtr(Imm32(d), StackPointer);
 
     // No need to restore the stack; the throw stub pops everything.
-    masm.assertStackAlignment();
+    masm.assertStackAlignment(ABIStackAlignment);
     masm.call(AsmJSImmPtr(AsmJSImm_ReportOverRecursed));
     masm.jump(throwLabel);
 }
 
 void
 js::GenerateAsmJSExitPrologue(MacroAssembler &masm, unsigned framePushed, AsmJSExit::Reason reason,
                               Label *begin)
 {
--- a/js/src/asmjs/AsmJSFrameIterator.h
+++ b/js/src/asmjs/AsmJSFrameIterator.h
@@ -165,12 +165,11 @@ GenerateAsmJSStackOverflowExit(jit::Macr
 
 void
 GenerateAsmJSExitPrologue(jit::MacroAssembler &masm, unsigned framePushed, AsmJSExit::Reason reason,
                           jit::Label *begin);
 void
 GenerateAsmJSExitEpilogue(jit::MacroAssembler &masm, unsigned framePushed, AsmJSExit::Reason reason,
                           jit::Label *profilingReturn);
 
-
 } // namespace js
 
 #endif // asmjs_AsmJSFrameIterator_h
--- 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;
 }
 
@@ -236,16 +263,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))
@@ -372,16 +496,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();
 
@@ -432,24 +564,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:
@@ -459,16 +591,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()) {
@@ -496,26 +642,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,31 +44,49 @@ 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,
     AsmJSMathBuiltin_clz32
 };
 
+// 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;
@@ -93,53 +113,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
@@ -153,17 +196,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 {
@@ -172,16 +216,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_;
 
@@ -192,17 +241,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 {
@@ -247,16 +296,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_;
         }
@@ -305,30 +374,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_;
@@ -668,17 +743,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;
 
@@ -815,30 +891,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_++;
@@ -859,16 +958,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 {
@@ -1105,37 +1215,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());
     }
@@ -1160,30 +1276,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_,
@@ -1214,16 +1359,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,
@@ -1295,16 +1449,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;
@@ -1331,16 +1486,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; }
 
@@ -1434,16 +1596,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));
@@ -1667,82 +1850,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));
@@ -1779,16 +2083,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");
 }
 
 /*****************************************************************************/
 
@@ -1949,17 +2255,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;
     }
@@ -2000,23 +2313,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;
@@ -2060,16 +2383,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;
     }
 
@@ -2157,31 +2493,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;
 
@@ -2189,16 +2536,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
@@ -2278,17 +2650,17 @@ class FunctionCompiler
 
     void finishCallArgs(Call *call)
     {
         if (inDeadCode())
             return;
         uint32_t parentStackBytes = call->abi_.stackBytesConsumedSoFar();
         uint32_t newStackBytes;
         if (call->childClobbers_) {
-            call->spIncrement_ = AlignBytes(call->maxChildStackBytes_, StackAlignment);
+            call->spIncrement_ = AlignBytes(call->maxChildStackBytes_, AsmJSStackAlignment);
             for (unsigned i = 0; i < call->stackArgs_.length(); i++)
                 call->stackArgs_[i]->incrementOffset(call->spIncrement_);
             newStackBytes = Max(call->prevMaxStackBytes_,
                                 call->spIncrement_ + parentStackBytes);
         } else {
             call->spIncrement_ = 0;
             newStackBytes = Max(call->prevMaxStackBytes_,
                                 Max(call->maxChildStackBytes_, parentStackBytes));
@@ -2959,25 +3331,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);
@@ -3051,54 +3421,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");
 
@@ -3220,16 +3669,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;
 
@@ -3335,16 +3790,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);
 }
 
@@ -3357,17 +3814,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
@@ -3494,16 +3951,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;
 
@@ -3931,16 +4422,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;
@@ -3948,46 +4441,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;
@@ -4061,28 +4599,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
@@ -4090,16 +4729,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);
@@ -4151,16 +4794,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);
@@ -4177,16 +4860,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);
 }
 
@@ -4409,16 +5095,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)
@@ -4695,16 +5383,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);
@@ -5104,16 +5793,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)
@@ -5262,16 +5953,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;
 
@@ -5931,194 +6626,246 @@ CheckModuleReturn(ModuleCompiler &m)
     // (since cx->tempLifoAlloc is marked/released after each function
     // statement) and thus all the identifiers in the return statement will be
     // mistaken as free variables and added to lexdeps. Clear these now.
     m.parser().pc->lexdeps->clear();
     return true;
 }
 
 static void
-AssertStackAlignment(MacroAssembler &masm)
-{
-    JS_ASSERT((sizeof(AsmJSFrame) + masm.framePushed()) % StackAlignment == 0);
-    masm.assertStackAlignment();
+AssertStackAlignment(MacroAssembler &masm, uint32_t alignment)
+{
+    JS_ASSERT((sizeof(AsmJSFrame) + masm.framePushed()) % alignment == 0);
+    masm.assertStackAlignment(alignment);
 }
 
 static unsigned
-StackDecrementForCall(MacroAssembler &masm, unsigned bytesToPush)
-{
-    return StackDecrementForCall(sizeof(AsmJSFrame) + masm.framePushed(), bytesToPush);
+StackDecrementForCall(MacroAssembler &masm, uint32_t alignment, unsigned bytesToPush)
+{
+    return StackDecrementForCall(alignment, sizeof(AsmJSFrame) + masm.framePushed(), bytesToPush);
 }
 
 template <class VectorT>
 static unsigned
 StackArgBytes(const VectorT &argTypes)
 {
     ABIArgIter<VectorT> iter(argTypes);
     while (!iter.done())
         iter++;
     return iter.stackBytesConsumedSoFar();
 }
 
 template <class VectorT>
 static unsigned
-StackDecrementForCall(MacroAssembler &masm, const VectorT &argTypes, unsigned extraBytes = 0)
-{
-    return StackDecrementForCall(masm, StackArgBytes(argTypes) + extraBytes);
+StackDecrementForCall(MacroAssembler &masm, uint32_t alignment, const VectorT &argTypes,
+                      unsigned extraBytes = 0)
+{
+    return StackDecrementForCall(masm, alignment, StackArgBytes(argTypes) + extraBytes);
 }
 
 #if defined(JS_CODEGEN_ARM)
 // The ARM system ABI also includes d15 in the non volatile float registers.
 // Also exclude lr (a.k.a. r14) as we preserve it manually)
 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();
 
     Label begin;
     masm.align(CodeAlignment);
     masm.bind(&begin);
 
+    // Save the return address if it wasn't already saved by the call insn.
 #if defined(JS_CODEGEN_ARM)
     masm.push(lr);
 #elif defined(JS_CODEGEN_MIPS)
     masm.push(ra);
+#elif defined(JS_CODEGEN_X86)
+    static const unsigned EntryFrameSize = sizeof(void*);
 #endif
-    masm.subPtr(Imm32(AsmJSFrameBytesAfterReturnAddress), StackPointer);
+
+    // 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);
-
-    // In constrast to the system ABI, the Ion convention is that all registers
-    // are clobbered by calls. Thus, we must save the caller's non-volatile
-    // registers.
-    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);
 #endif
 
     // ARM, MIPS and x64 have a globally-pinned HeapReg (x86 uses immediates in
     // effective addresses).
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     masm.loadPtr(Address(IntArgReg1, AsmJSModule::heapGlobalDataOffset()), HeapReg);
 #endif
 
-    // Remember the stack pointer in the current AsmJSActivation. This will be
-    // used by error exit paths to set the stack pointer back to what it was
-    // right after the (C++) caller's non-volatile registers were saved so that
-    // they can be restored.
-    Register activation = ABIArgGenerator::NonArgReturnReg0;
-    masm.loadAsmJSActivation(activation);
-    masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfErrorRejoinSP()));
-
-    // Get 'argv' into a non-arg register and save it on the stack.
+    // Put the 'argv' argument into a non-argument/return register so that we
+    // can use 'argv' while we fill in the arguments for the asm.js callee.
+    // Also, save 'argv' on the stack so that we can recover it after the call.
+    // Use a second non-argument/return register as temporary scratch. 
     Register argv = ABIArgGenerator::NonArgReturnReg0;
     Register scratch = ABIArgGenerator::NonArgReturnReg1;
 #if defined(JS_CODEGEN_X86)
-    masm.loadPtr(Address(StackPointer, sizeof(AsmJSFrame) + masm.framePushed()), argv);
+    masm.loadPtr(Address(StackPointer, EntryFrameSize + masm.framePushed()), argv);
 #else
     masm.movePtr(IntArgReg0, argv);
 #endif
     masm.Push(argv);
 
+    // Save the stack pointer to the saved non-volatile registers. We will use
+    // this on two paths: normal return and exceptional return. Since
+    // loadAsmJSActivation uses GlobalReg, we must do this after loading
+    // GlobalReg.
+    JS_ASSERT(masm.framePushed() == FramePushedForEntrySP);
+    masm.loadAsmJSActivation(scratch);
+    masm.storePtr(StackPointer, Address(scratch, AsmJSActivation::offsetOfEntrySP()));
+
+    // Dynamically align the stack since ABIStackAlignment is not necessarily
+    // AsmJSStackAlignment. We'll use entrySP to recover the original stack
+    // pointer on return.
+    masm.andPtr(Imm32(~(AsmJSStackAlignment - 1)), StackPointer);
+
     // Bump the stack for the call.
     PropertyName *funcName = m.module().exportedFunction(exportIndex).name();
     const ModuleCompiler::Func &func = *m.lookupFunction(funcName);
-    unsigned stackDec = StackDecrementForCall(masm, func.sig().args());
-    masm.reserveStack(stackDec);
+    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.
-    AssertStackAlignment(masm);
+    masm.assertStackAlignment(AsmJSStackAlignment);
     masm.call(CallSiteDesc(CallSiteDesc::Relative), &func.entry());
 
-    // Pop the stack and recover the original 'argv' argument passed to the
-    // trampoline (which was pushed on the stack).
-    masm.freeStack(stackDec);
+    // Recover the stack pointer value before dynamic alignment.
+    masm.loadAsmJSActivation(scratch);
+    masm.loadPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP()), StackPointer);
+    masm.setFramePushed(FramePushedForEntrySP);
+
+    // Recover the 'argv' pointer which was saved before aligning the stack.
     masm.Pop(argv);
 
     // Store the return value in argv[0]
     switch (func.sig().retType().which()) {
       case RetType::Void:
         break;
       case RetType::Signed:
         masm.storeValue(JSVAL_TYPE_INT32, ReturnReg, Address(argv, 0));
         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.addPtr(Imm32(AsmJSFrameBytesAfterReturnAddress), StackPointer);
     masm.ret();
 
     return m.finishGeneratingEntry(exportIndex, &begin) && !masm.oom();
 }
 
 static void
 FillArgumentArray(ModuleCompiler &m, const VarTypeVector &argTypes,
                   unsigned offsetToArgs, unsigned offsetToCallerStackArgs,
@@ -6172,17 +6919,17 @@ GenerateFFIInterpExit(ModuleCompiler &m,
     invokeArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray));
 
     // At the point of the call, the stack layout shall be (sp grows to the left):
     //   | stack args | padding | Value argv[] | padding | retaddr | caller stack args |
     // The padding between stack args and argv ensures that argv is aligned. The
     // padding between argv and retaddr ensures that sp is aligned.
     unsigned offsetToArgv = AlignBytes(StackArgBytes(invokeArgTypes), sizeof(double));
     unsigned argvBytes = Max<size_t>(1, exit.sig().args().length()) * sizeof(Value);
-    unsigned framePushed = StackDecrementForCall(masm, offsetToArgv + argvBytes);
+    unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, offsetToArgv + argvBytes);
 
     Label begin;
     GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::SlowFFI, &begin);
 
     // Fill the argument array.
     unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed();
     Register scratch = ABIArgGenerator::NonArgReturnReg0;
     FillArgumentArray(m, exit.sig().args(), offsetToArgv, offsetToCallerStackArgs, scratch);
@@ -6212,17 +6959,17 @@ GenerateFFIInterpExit(ModuleCompiler &m,
     } else {
         masm.computeEffectiveAddress(argv, scratch);
         masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase()));
     }
     i++;
     JS_ASSERT(i.done());
 
     // Make the call, test whether it succeeded, and extract the return value.
-    AssertStackAlignment(masm);
+    AssertStackAlignment(masm, ABIStackAlignment);
     switch (exit.sig().retType().which()) {
       case RetType::Void:
         masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_Ignore));
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         break;
       case RetType::Signed:
         masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToInt32));
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
@@ -6230,16 +6977,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
@@ -6274,26 +7024,26 @@ GenerateFFIIonExit(ModuleCompiler &m, co
     // space required for both calls and take the maximum. In both cases,
     // include space for savedRegBytes, since these go below the Ion/coerce.
 
     // Ion calls use the following stack layout (sp grows to the left):
     //   | return address | descriptor | callee | argc | this | arg1 | arg2 | ...
     unsigned offsetToIonArgs = MaybeRetAddr;
     unsigned ionArgBytes = 3 * sizeof(size_t) + (1 + exit.sig().args().length()) * sizeof(Value);
     unsigned totalIonBytes = offsetToIonArgs + ionArgBytes + savedRegBytes;
-    unsigned ionFrameSize = StackDecrementForCall(masm, totalIonBytes);
+    unsigned ionFrameSize = StackDecrementForCall(masm, AsmJSStackAlignment, totalIonBytes);
 
     // Coercion calls use the following stack layout (sp grows to the left):
     //   | stack args | padding | Value argv[1] | ...
     // The padding between args and argv ensures that argv is aligned.
     MIRTypeVector coerceArgTypes(m.cx());
     coerceArgTypes.infallibleAppend(MIRType_Pointer); // argv
     unsigned offsetToCoerceArgv = AlignBytes(StackArgBytes(coerceArgTypes), sizeof(double));
     unsigned totalCoerceBytes = offsetToCoerceArgv + sizeof(Value) + savedRegBytes;
-    unsigned coerceFrameSize = StackDecrementForCall(masm, totalCoerceBytes);
+    unsigned coerceFrameSize = StackDecrementForCall(masm, AsmJSStackAlignment, totalCoerceBytes);
 
     unsigned framePushed = Max(ionFrameSize, coerceFrameSize);
 
     Label begin;
     GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::IonFFI, &begin);
 
     // 1. Descriptor
     size_t argOffset = offsetToIonArgs;
@@ -6384,19 +7134,19 @@ GenerateFFIIonExit(ModuleCompiler &m, co
         masm.loadPtr(Address(reg0, offsetOfJitTop), reg2);
         masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitTop()));
         masm.loadPtr(Address(reg0, offsetOfJitJSContext), reg2);
         masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitJSContext()));
         masm.storePtr(reg3, Address(reg0, offsetOfJitJSContext));
     }
 
     // 2. Call
-    AssertStackAlignment(masm);
+    AssertStackAlignment(masm, AsmJSStackAlignment);
     masm.callIonFromAsmJS(callee);
-    AssertStackAlignment(masm);
+    AssertStackAlignment(masm, AsmJSStackAlignment);
 
     {
         // Disable Activation.
         //
         // This sequence needs three registers, and must preserve the JSReturnReg_Data and
         // JSReturnReg_Type, so there are five live registers.
         JS_ASSERT(JSReturnReg_Data == AsmJSIonExitRegReturnData);
         JS_ASSERT(JSReturnReg_Type == AsmJSIonExitRegReturnType);
@@ -6441,16 +7191,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);
 
@@ -6469,17 +7222,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
         } else {
             masm.computeEffectiveAddress(argv, scratch);
             masm.storePtr(scratch, Address(StackPointer, i->offsetFromArgBase()));
         }
         i++;
         JS_ASSERT(i.done());
 
         // Call coercion function
-        AssertStackAlignment(masm);
+        AssertStackAlignment(masm, ABIStackAlignment);
         switch (exit.sig().retType().which()) {
           case RetType::Signed:
             masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToInt32));
             masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
             masm.unboxInt32(Address(StackPointer, offsetToCoerceArgv), ReturnReg);
             break;
           case RetType::Double:
             masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToNumber));
@@ -6564,17 +7317,17 @@ GenerateBuiltinThunk(ModuleCompiler &m, 
       case AsmJSExit::Builtin_CeilF:
       case AsmJSExit::Builtin_FloorF:
         argTypes.infallibleAppend(MIRType_Float32);
         break;
       case AsmJSExit::Builtin_Limit:
         MOZ_CRASH("Bad builtin");
     }
 
-    uint32_t framePushed = StackDecrementForCall(masm, argTypes);
+    uint32_t framePushed = StackDecrementForCall(masm, ABIStackAlignment, argTypes);
 
     Label begin;
     GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::Builtin(builtin), &begin);
 
     for (ABIArgMIRTypeIter i(argTypes); !i.done(); i++) {
         if (i->kind() != ABIArg::Stack)
             continue;
 #if !defined(JS_CODEGEN_ARM)
@@ -6589,17 +7342,17 @@ GenerateBuiltinThunk(ModuleCompiler &m, 
             masm.loadDouble(srcAddr, ScratchDoubleReg);
             masm.storeDouble(ScratchDoubleReg, dstAddr);
         }
 #else
         MOZ_CRASH("Architecture should have enough registers for all builtin calls");
 #endif
     }
 
-    AssertStackAlignment(masm);
+    AssertStackAlignment(masm, ABIStackAlignment);
     masm.call(BuiltinToImmKind(builtin));
 
     Label profilingReturn;
     GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::Builtin(builtin), &profilingReturn);
     return m.finishGeneratingBuiltinThunk(builtin, &begin, &profilingReturn) && !masm.oom();
 }
 
 static bool
@@ -6632,66 +7385,69 @@ 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*)));
 
     // We know that StackPointer is word-aligned, but not necessarily
     // stack-aligned, so we need to align it dynamically.
     masm.mov(StackPointer, ABIArgGenerator::NonVolatileReg);
-    masm.andPtr(Imm32(~(StackAlignment - 1)), StackPointer);
+    masm.andPtr(Imm32(~(ABIStackAlignment - 1)), StackPointer);
     if (ShadowStackSpace)
         masm.subPtr(Imm32(ShadowStackSpace), StackPointer);
 
-    masm.assertStackAlignment();
+    masm.assertStackAlignment(ABIStackAlignment);
     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(~(StackAlignment - 1)));
+    masm.ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
 
     // Store resumePC into the reserved space.
     masm.loadAsmJSActivation(IntArgReg0);
     masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfResumePC()), IntArgReg1);
     masm.storePtr(IntArgReg1, Address(s0, masm.framePushed()));
 
     // MIPS ABI requires rewserving stack for registes $a0 to $a3.
     masm.subPtr(Imm32(4 * sizeof(intptr_t)), StackPointer);
 
-    masm.assertStackAlignment();
+    masm.assertStackAlignment(ABIStackAlignment);
     masm.call(AsmJSImm_HandleExecutionInterrupt);
 
     masm.addPtr(Imm32(4 * sizeof(intptr_t)), StackPointer);
 
     masm.branchIfFalseBool(ReturnReg, throwLabel);
 
     // This will restore stack to the address before the call.
     masm.movePtr(s0, StackPointer);
@@ -6716,19 +7472,22 @@ 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();
+    masm.assertStackAlignment(ABIStackAlignment);
     masm.call(AsmJSImm_HandleExecutionInterrupt);
 
     masm.branchIfFalseBool(ReturnReg, throwLabel);
 
     // Restore the machine state to before the interrupt. this will set the pc!
     masm.PopRegsInMask(RegisterSet(GeneralRegisterSet(0), FloatRegisterSet(FloatRegisters::AllDoubleMask)));   // restore all FP registers
     masm.mov(r6,sp);
     masm.as_vmsr(r5);
@@ -6762,21 +7521,21 @@ GenerateAsyncInterruptExit(ModuleCompile
 }
 
 static bool
 GenerateSyncInterruptExit(ModuleCompiler &m, Label *throwLabel)
 {
     MacroAssembler &masm = m.masm();
     masm.setFramePushed(0);
 
-    unsigned framePushed = StackDecrementForCall(masm, ShadowStackSpace);
+    unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, ShadowStackSpace);
 
     GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::Interrupt, &m.syncInterruptLabel());
 
-    AssertStackAlignment(masm);
+    AssertStackAlignment(masm, ABIStackAlignment);
     masm.call(AsmJSImmPtr(AsmJSImm_HandleExecutionInterrupt));
     masm.branchIfFalseBool(ReturnReg, throwLabel);
 
     Label profilingReturn;
     GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::Interrupt, &profilingReturn);
     return m.finishGeneratingInterrupt(&m.syncInterruptLabel(), &profilingReturn) && !masm.oom();
 }
 
@@ -6790,27 +7549,27 @@ GenerateThrowStub(ModuleCompiler &m, Lab
 {
     MacroAssembler &masm = m.masm();
     masm.align(CodeAlignment);
     masm.bind(throwLabel);
 
     // We are about to pop all frames in this AsmJSActivation. Set fp to null to
     // maintain the invariant that fp is either null or pointing to a valid
     // frame.
-    Register activation = ABIArgGenerator::NonArgReturnReg0;
-    masm.loadAsmJSActivation(activation);
-    masm.storePtr(ImmWord(0), Address(activation, AsmJSActivation::offsetOfFP()));
-
-    masm.setFramePushed(FramePushedAfterSave);
-    masm.loadPtr(Address(activation, AsmJSActivation::offsetOfErrorRejoinSP()), StackPointer);
-    masm.PopRegsInMask(NonVolatileRegs);
+    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, NonVolatileSimdRegs);
     JS_ASSERT(masm.framePushed() == 0);
 
     masm.mov(ImmWord(0), ReturnReg);
-    masm.addPtr(Imm32(AsmJSFrameBytesAfterReturnAddress), StackPointer);
     masm.ret();
 
     return m.finishGeneratingInlineStub(throwLabel) && !masm.oom();
 }
 
 static bool
 GenerateStubs(ModuleCompiler &m)
 {
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -34,33 +34,54 @@ 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 bool js::IsVectorObject<Int32x4>(HandleValue v);
+template bool js::IsVectorObject<Float32x4>(HandleValue v);
+
+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/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1969,16 +1969,29 @@ EvalReturningScope(JSContext *cx, unsign
     RootedObject scope(cx);
     if (!js::ExecuteInGlobalAndReturnScope(cx, global, script, &scope))
         return false;
 
     args.rval().setObject(*scope);
     return true;
 }
 
+static bool
+IsSimdAvailable(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+#ifdef JS_CODEGEN_NONE
+    bool available = false;
+#else
+    bool available = cx->jitSupportsSimd();
+#endif
+    args.rval().set(BooleanValue(available));
+    return true;
+}
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment')",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc."),
 
     JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
@@ -2152,16 +2165,20 @@ static const JSFunctionSpecWithHelp Test
 "  inferred name based on where the function was defined. This can be\n"
 "  different from the 'name' property on the function."),
 
     JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0,
 "isAsmJSCompilationAvailable",
 "  Returns whether asm.js compilation is currently available or whether it is disabled\n"
 "  (e.g., by the debugger)."),
 
+    JS_FN_HELP("isSimdAvailable", IsSimdAvailable, 0, 0,
+"isSimdAvailable",
+"  Returns true if SIMD extensions are supported on this platform."),
+
     JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0,
 "getCompilerOptions()",
 "Return an object describing some of the JIT compiler options.\n"),
 
     JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
 "isAsmJSModule(fn)",
 "  Returns whether the given value is a function containing \"use asm\" that has been\n"
 "  validated according to the asm.js spec."),
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -139,17 +139,17 @@ NativeRegExpMacroAssembler::GenerateCode
 
 #ifndef JS_CODEGEN_X86
     // The InputOutputData* is stored as an argument, save it on the stack
     // above the frame.
     masm.Push(IntArgReg0);
 #endif
 
     size_t frameSize = sizeof(FrameData) + num_registers_ * sizeof(void *);
-    frameSize = JS_ROUNDUP(frameSize + masm.framePushed(), StackAlignment) - masm.framePushed();
+    frameSize = JS_ROUNDUP(frameSize + masm.framePushed(), ABIStackAlignment) - masm.framePushed();
 
     // Actually emit code to start a new stack frame.
     masm.reserveStack(frameSize);
     masm.checkStackAlignment();
 
     // Check if we have space on the stack.
     Label stack_ok;
     void *stack_limit = &runtime->mainThread.jitStackLimit;
--- 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/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8817,22 +8817,25 @@ CodeGenerator::visitAsmJSCall(LAsmJSCall
             }
         }
     }
 #endif
 
     if (mir->spIncrement())
         masm.freeStack(mir->spIncrement());
 
-    JS_ASSERT((sizeof(AsmJSFrame) + masm.framePushed()) % StackAlignment == 0);
+    JS_ASSERT((sizeof(AsmJSFrame) + masm.framePushed()) % AsmJSStackAlignment == 0);
 
 #ifdef DEBUG
+    static_assert(AsmJSStackAlignment >= ABIStackAlignment,
+                  "The asm.js stack alignment should subsume the ABI-required alignment");
+    static_assert(AsmJSStackAlignment % ABIStackAlignment == 0,
+                  "The asm.js stack alignment should subsume the ABI-required alignment");
     Label ok;
-    JS_ASSERT(IsPowerOfTwo(StackAlignment));
-    masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
+    masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(AsmJSStackAlignment - 1), &ok);
     masm.breakpoint();
     masm.bind(&ok);
 #endif
 
     MAsmJSCall::Callee callee = mir->callee();
     switch (callee.which()) {
       case MAsmJSCall::Callee::Internal:
         masm.call(mir->desc(), callee.internal());
@@ -9061,17 +9064,17 @@ CodeGenerator::visitAsmJSInterruptCheck(
 {
     Register scratch = ToRegister(lir->scratch());
     masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterrupt), scratch);
     masm.load8ZeroExtend(Address(scratch, 0), scratch);
     Label rejoin;
     masm.branch32(Assembler::Equal, scratch, Imm32(0), &rejoin);
     {
         uint32_t stackFixup = ComputeByteAlignment(masm.framePushed() + sizeof(AsmJSFrame),
-                                                   StackAlignment);
+                                                   ABIStackAlignment);
         masm.reserveStack(stackFixup);
         masm.call(lir->funcDesc(), lir->interruptExit());
         masm.freeStack(stackFixup);
     }
     masm.bind(&rejoin);
     return true;
 }
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -3209,8 +3209,14 @@ AutoDebugModeInvalidation::~AutoDebugMod
     }
 }
 
 bool
 jit::JitSupportsFloatingPoint()
 {
     return js::jit::MacroAssembler::SupportsFloatingPoint();
 }
+
+bool
+jit::JitSupportsSimd()
+{
+    return js::jit::MacroAssembler::SupportsSimd();
+}
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -202,13 +202,14 @@ void TraceIonScripts(JSTracer* trc, JSSc
 
 void RequestInterruptForIonCode(JSRuntime *rt, JSRuntime::InterruptMode mode);
 
 bool RematerializeAllFrames(JSContext *cx, JSCompartment *comp);
 bool UpdateForDebugMode(JSContext *maybecx, JSCompartment *comp,
                         AutoDebugModeInvalidation &invalidate);
 
 bool JitSupportsFloatingPoint();
+bool JitSupportsSimd();
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_Ion_h */
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -1070,17 +1070,17 @@ JitActivationIterator::jitStackRange(uin
     end = reinterpret_cast<uintptr_t *>(frames.prevFp());
 }
 
 #ifdef JS_CODEGEN_MIPS
 uint8_t *
 alignDoubleSpillWithOffset(uint8_t *pointer, int32_t offset)
 {
     uint32_t address = reinterpret_cast<uint32_t>(pointer);
-    address = (address - offset) & ~(StackAlignment - 1);
+    address = (address - offset) & ~(ABIStackAlignment - 1);
     return reinterpret_cast<uint8_t *>(address);
 }
 
 static void
 MarkJitExitFrameCopiedArguments(JSTracer *trc, const VMFunction *f, IonExitFooterFrame *footer)
 {
     uint8_t *doubleArgs = reinterpret_cast<uint8_t *>(footer);
     doubleArgs = alignDoubleSpillWithOffset(doubleArgs, sizeof(intptr_t));
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -1421,21 +1421,21 @@ class MacroAssembler : public MacroAssem
     }
 
     void icRestoreLive(RegisterSet &liveRegs, AfterICSaveLive &aic) {
         restoreFrameAlignmentForICArguments(aic);
         JS_ASSERT(framePushed() == aic.initialStack);
         PopRegsInMask(liveRegs);
     }
 
-    void assertStackAlignment() {
+    void assertStackAlignment(uint32_t alignment) {
 #ifdef DEBUG
         Label ok;
-        JS_ASSERT(IsPowerOfTwo(StackAlignment));
-        branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
+        JS_ASSERT(IsPowerOfTwo(alignment));
+        branchTestPtr(Assembler::Zero, StackPointer, Imm32(alignment - 1), &ok);
         breakpoint();
         bind(&ok);
 #endif
     }
 };
 
 static inline Assembler::DoubleCondition
 JSOpToDoubleCondition(JSOp op)
@@ -1503,18 +1503,18 @@ JSOpToCondition(JSOp op, bool isSigned)
             return Assembler::AboveOrEqual;
           default:
             MOZ_CRASH("Unrecognized comparison operation");
         }
     }
 }
 
 static inline size_t
-StackDecrementForCall(size_t bytesAlreadyPushed, size_t bytesToPush)
+StackDecrementForCall(uint32_t alignment, size_t bytesAlreadyPushed, size_t bytesToPush)
 {
     return bytesToPush +
-           ComputeByteAlignment(bytesAlreadyPushed + bytesToPush, StackAlignment);
+           ComputeByteAlignment(bytesAlreadyPushed + bytesToPush, alignment);
 }
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonMacroAssembler_h */
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -1581,20 +1581,20 @@ class LIRGraph
     }
     uint32_t localSlotCount() const {
         return localSlotCount_;
     }
     // Return the localSlotCount() value rounded up so that it satisfies the
     // platform stack alignment requirement, and so that it's a multiple of
     // the number of slots per Value.
     uint32_t paddedLocalSlotCount() const {
-        // Round to StackAlignment, but also round to at least sizeof(Value) in
-        // case that's greater, because StackOffsetOfPassedArg rounds argument
-        // slots to 8-byte boundaries.
-        size_t Alignment = Max(size_t(StackAlignment), sizeof(Value));
+        // Round to ABIStackAlignment, but also round to at least sizeof(Value)
+        // in case that's greater, because StackOffsetOfPassedArg rounds
+        // argument slots to 8-byte boundaries.
+        size_t Alignment = Max(size_t(ABIStackAlignment), sizeof(Value));
         return AlignBytes(localSlotCount(), Alignment);
     }
     size_t paddedLocalSlotsSize() const {
         return paddedLocalSlotCount();
     }
     void setArgumentSlotCount(uint32_t argumentSlotCount) {
         argumentSlotCount_ = argumentSlotCount;
     }
--- 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
@@ -11182,17 +11182,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/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -140,27 +140,28 @@ static MOZ_CONSTEXPR_VAR FloatRegister d
 static MOZ_CONSTEXPR_VAR FloatRegister d14 = {FloatRegisters::d14, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d15 = {FloatRegisters::d15, VFPRegister::Double};
 
 
 // For maximal awesomeness, 8 should be sufficent. ldrd/strd (dual-register
 // load/store) operate in a single cycle when the address they are dealing with
 // is 8 byte aligned. Also, the ARM abi wants the stack to be 8 byte aligned at
 // function boundaries. I'm trying to make sure this is always true.
-static const uint32_t StackAlignment = 8;
+static const uint32_t ABIStackAlignment = 8;
 static const uint32_t CodeAlignment = 8;
-static const bool StackKeptAligned = true;
 
 // This boolean indicates whether we support SIMD instructions flavoured for
 // this architecture or not. Rather than a method in the LIRGenerator, it is
 // here such that it is accessible from the entire codebase. Once full support
 // for SIMD is reached on all tier-1 platforms, this constant can be deleted.
 static const bool SupportsSimd = false;
 static const uint32_t SimdStackAlignment = 8;
 
+static const uint32_t AsmJSStackAlignment = SimdStackAlignment;
+
 static const Scale ScalePointer = TimesFour;
 
 class Instruction;
 class InstBranchImm;
 uint32_t RM(Register r);
 uint32_t RS(Register r);
 uint32_t RD(Register r);
 uint32_t RT(Register r);
@@ -1547,16 +1548,19 @@ class Assembler : public AssemblerShared
 
   public:
     static void TraceJumpRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
     static void TraceDataRelocations(JSTracer *trc, JitCode *code, CompactBufferReader &reader);
 
     static bool SupportsFloatingPoint() {
         return HasVFP();
     }
+    static bool SupportsSimd() {
+        return js::jit::SupportsSimd;
+    }
 
   protected:
     void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) {
         enoughMemory_ &= jumps_.append(RelativePatch(target.value, kind));
         if (kind == Relocation::JITCODE)
             writeRelocation(src);
     }
 
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1875,17 +1875,17 @@ void
 MacroAssemblerARMCompat::freeStack(Register amount)
 {
     ma_add(amount, sp);
 }
 
 void
 MacroAssembler::PushRegsInMask(RegisterSet set, FloatRegisterSet simdSet)
 {
-    JS_ASSERT(!SupportsSimd && simdSet.size() == 0);
+    JS_ASSERT(!SupportsSimd() && simdSet.size() == 0);
     int32_t diffF = set.fpus().getPushSizeInBytes();
     int32_t diffG = set.gprs().size() * sizeof(intptr_t);
 
     if (set.gprs().size() > 1) {
         adjustFrame(diffG);
         startDataTransferM(IsStore, StackPointer, DB, WriteBack);
         for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) {
             diffG -= sizeof(intptr_t);
@@ -1904,17 +1904,17 @@ MacroAssembler::PushRegsInMask(RegisterS
     adjustFrame(diffF);
     diffF += transferMultipleByRuns(set.fpus(), IsStore, StackPointer, DB);
     JS_ASSERT(diffF == 0);
 }
 
 void
 MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore, FloatRegisterSet simdSet)
 {
-    JS_ASSERT(!SupportsSimd && simdSet.size() == 0);
+    JS_ASSERT(!SupportsSimd() && simdSet.size() == 0);
     int32_t diffG = set.gprs().size() * sizeof(intptr_t);
     int32_t diffF = set.fpus().getPushSizeInBytes();
     const int32_t reservedG = diffG;
     const int32_t reservedF = diffF;
 
     // ARM can load multiple registers at once, but only if we want back all
     // the registers we previously saved to the stack.
     if (ignore.empty(true)) {
@@ -3773,17 +3773,17 @@ void
 MacroAssemblerARMCompat::setupUnalignedABICall(uint32_t args, Register scratch)
 {
     setupABICall(args);
     dynamicAlignment_ = true;
 
     ma_mov(sp, scratch);
 
     // Force sp to be aligned.
-    ma_and(Imm32(~(StackAlignment - 1)), sp, sp);
+    ma_and(Imm32(~(ABIStackAlignment - 1)), sp, sp);
     ma_push(scratch);
 }
 
 #if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
 void
 MacroAssemblerARMCompat::passHardFpABIArg(const MoveOperand &from, MoveOp::Type type)
 {
     MoveOperand to;
@@ -3932,17 +3932,17 @@ void
 MacroAssemblerARMCompat::passABIArg(FloatRegister freg, MoveOp::Type type)
 {
     passABIArg(MoveOperand(freg), type);
 }
 
 void MacroAssemblerARMCompat::checkStackAlignment()
 {
 #ifdef DEBUG
-    ma_tst(sp, Imm32(StackAlignment - 1));
+    ma_tst(sp, Imm32(ABIStackAlignment - 1));
     breakpoint(NonZero);
 #endif
 }
 
 void
 MacroAssemblerARMCompat::callWithABIPre(uint32_t *stackAdjust, bool callFromAsmJS)
 {
     JS_ASSERT(inCall_);
@@ -3951,21 +3951,21 @@ MacroAssemblerARMCompat::callWithABIPre(
 #if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
     if (UseHardFpABI())
         *stackAdjust += 2*((usedFloatSlots_ > NumFloatArgRegs) ? usedFloatSlots_ - NumFloatArgRegs : 0) * sizeof(intptr_t);
 #endif
     uint32_t alignmentAtPrologue = callFromAsmJS ? sizeof(AsmJSFrame) : 0;
 
     if (!dynamicAlignment_) {
         *stackAdjust += ComputeByteAlignment(framePushed_ + *stackAdjust + alignmentAtPrologue,
-                                             StackAlignment);
+                                             ABIStackAlignment);
     } else {
         // sizeof(intptr_t) accounts for the saved stack pointer pushed by
         // setupUnalignedABICall.
-        *stackAdjust += ComputeByteAlignment(*stackAdjust + sizeof(intptr_t), StackAlignment);
+        *stackAdjust += ComputeByteAlignment(*stackAdjust + sizeof(intptr_t), ABIStackAlignment);
     }
 
     reserveStack(*stackAdjust);
 
     // Position all arguments.
     {
         enoughMemory_ = enoughMemory_ && moveResolver_.resolve();
         if (!enoughMemory_)
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -2112,17 +2112,17 @@ Simulator::softwareInterrupt(SimInstruct
         int32_t arg3 = get_register(r3);
         int32_t *stack_pointer = reinterpret_cast<int32_t*>(get_register(sp));
         int32_t arg4 = stack_pointer[0];
         int32_t arg5 = stack_pointer[1];
 
         int32_t saved_lr = get_register(lr);
         intptr_t external = reinterpret_cast<intptr_t>(redirection->nativeFunction());
 
-        bool stack_aligned = (get_register(sp) & (StackAlignment - 1)) == 0;
+        bool stack_aligned = (get_register(sp) & (ABIStackAlignment - 1)) == 0;
         if (!stack_aligned) {
             fprintf(stderr, "Runtime call with unaligned stack!\n");
             MOZ_CRASH();
         }
 
         if (single_stepping_)
             single_step_callback_(single_step_callback_arg_, this, nullptr);
 
@@ -4253,17 +4253,17 @@ Simulator::call(uint8_t* entry, int argu
         set_register(r3, va_arg(parameters, int32_t));
 
     // Remaining arguments passed on stack.
     int original_stack = get_register(sp);
     int entry_stack = original_stack;
     if (argument_count >= 4)
         entry_stack -= (argument_count - 4) * sizeof(int32_t);
 
-    entry_stack &= ~StackAlignment;
+    entry_stack &= ~ABIStackAlignment;
 
     // Store remaining arguments on stack, from low to high memory.
     intptr_t *stack_argument = reinterpret_cast<intptr_t*>(entry_stack);
     for (int i = 4; i < argument_count; i++)
         stack_argument[i - 4] = va_arg(parameters, int32_t);
     va_end(parameters);
     set_register(sp, entry_stack);
 
--- a/js/src/jit/mips/Assembler-mips.h
+++ b/js/src/jit/mips/Assembler-mips.h
@@ -153,29 +153,30 @@ static MOZ_CONSTEXPR_VAR FloatRegister f
 static MOZ_CONSTEXPR_VAR FloatRegister f22 = { FloatRegisters::f22, FloatRegister::Double };
 static MOZ_CONSTEXPR_VAR FloatRegister f24 = { FloatRegisters::f24, FloatRegister::Double };
 static MOZ_CONSTEXPR_VAR FloatRegister f26 = { FloatRegisters::f26, FloatRegister::Double };
 static MOZ_CONSTEXPR_VAR FloatRegister f28 = { FloatRegisters::f28, FloatRegister::Double };
 static MOZ_CONSTEXPR_VAR FloatRegister f30 = { FloatRegisters::f30, FloatRegister::Double };
 
 // MIPS CPUs can only load multibyte data that is "naturally"
 // four-byte-aligned, sp register should be eight-byte-aligned.
-static const uint32_t StackAlignment = 8;
+static const uint32_t ABIStackAlignment = 8;
 static const uint32_t CodeAlignment = 4;
-static const bool StackKeptAligned = true;
 
 // This boolean indicates whether we support SIMD instructions flavoured for
 // this architecture or not. Rather than a method in the LIRGenerator, it is
 // here such that it is accessible from the entire codebase. Once full support
 // for SIMD is reached on all tier-1 platforms, this constant can be deleted.
 static const bool SupportsSimd = false;
 // TODO this is just a filler to prevent a build failure. The MIPS SIMD
 // alignment requirements still need to be explored.
 static const uint32_t SimdStackAlignment = 8;
 
+static const uint32_t AsmJSStackAlignment = SimdStackAlignment;
+
 static const Scale ScalePointer = TimesFour;
 
 // MIPS instruction types
 //                +---------------------------------------------------------------+
 //                |    6      |    5    |    5    |    5    |    5    |    6      |
 //                +---------------------------------------------------------------+
 // Register type  |  Opcode   |    Rs   |    Rt   |    Rd   |    Sa   | Function  |
 //                +---------------------------------------------------------------+
@@ -233,17 +234,16 @@ static const uint32_t Imm16Mask = ((1 <<
 static const uint32_t Imm26Mask = ((1 << Imm26Bits) - 1) << Imm26Shift;
 static const uint32_t Imm28Mask = ((1 << Imm28Bits) - 1) << Imm28Shift;
 static const uint32_t RSMask = ((1 << RSBits) - 1) << RSShift;
 static const uint32_t RTMask = ((1 << RTBits) - 1) << RTShift;
 static const uint32_t RDMask = ((1 << RDBits) - 1) << RDShift;
 static const uint32_t SAMask = ((1 << SABits) - 1) << SAShift;
 static const uint32_t FunctionMask = ((1 << FunctionBits) - 1) << FunctionShift;
 static const uint32_t RegMask = Registers::Total - 1;
-static const uint32_t StackAlignmentMask = StackAlignment - 1;
 
 static const uint32_t MAX_BREAK_CODE = 1024 - 1;
 
 class Instruction;
 class InstReg;
 class InstImm;
 class InstJump;
 class BranchInstBlock;
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -1569,17 +1569,17 @@ MacroAssembler::PushRegsInMask(RegisterS
         diffG -= sizeof(intptr_t);
         storePtr(*iter, Address(StackPointer, diffG));
     }
     MOZ_ASSERT(diffG == 0);
 
     // Double values have to be aligned. We reserve extra space so that we can
     // start writing from the first aligned location.
     // We reserve a whole extra double so that the buffer has even size.
-    ma_and(SecondScratchReg, sp, Imm32(~(StackAlignment - 1)));
+    ma_and(SecondScratchReg, sp, Imm32(~(ABIStackAlignment - 1)));
     reserveStack(diffF + sizeof(double));
 
     for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); iter++) {
         if ((*iter).code() % 2 == 0)
             as_sd(*iter, SecondScratchReg, -diffF);
         diffF -= sizeof(double);
     }
     MOZ_ASSERT(diffF == 0);
@@ -1591,17 +1591,17 @@ MacroAssembler::PopRegsInMaskIgnore(Regi
     JS_ASSERT(!SupportsSimd && simdSet.size() == 0);
     int32_t diffG = set.gprs().size() * sizeof(intptr_t);
     int32_t diffF = set.fpus().getPushSizeInBytes();
     const int32_t reservedG = diffG;
     const int32_t reservedF = diffF;
 
     // Read the buffer form the first aligned location.
     ma_addu(SecondScratchReg, sp, Imm32(reservedF + sizeof(double)));
-    ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(StackAlignment - 1)));
+    ma_and(SecondScratchReg, SecondScratchReg, Imm32(~(ABIStackAlignment - 1)));
 
     for (FloatRegisterForwardIterator iter(set.fpus().reduceSetForPush()); iter.more(); iter++) {
         if (!ignore.has(*iter) && ((*iter).code() % 2 == 0))
             // Use assembly l.d because we have alligned the stack.
             as_ld(*iter, SecondScratchReg, -diffF);
         diffF -= sizeof(double);
     }
     freeStack(reservedF + sizeof(double));
@@ -3153,17 +3153,17 @@ MacroAssemblerMIPSCompat::setupUnaligned
 {
     setupABICall(args);
     dynamicAlignment_ = true;
 
     ma_move(scratch, StackPointer);
 
     // Force sp to be aligned
     ma_subu(StackPointer, StackPointer, Imm32(sizeof(uint32_t)));
-    ma_and(StackPointer, StackPointer, Imm32(~(StackAlignment - 1)));
+    ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
     as_sw(scratch, StackPointer, 0);
 }
 
 void
 MacroAssemblerMIPSCompat::passABIArg(const MoveOperand &from, MoveOp::Type type)
 {
     ++passedArgs_;
     if (!enoughMemory_)
@@ -3254,48 +3254,48 @@ MacroAssemblerMIPSCompat::passABIArg(Flo
     passABIArg(MoveOperand(freg), type);
 }
 
 void
 MacroAssemblerMIPSCompat::checkStackAlignment()
 {
 #ifdef DEBUG
     Label aligned;
-    as_andi(ScratchRegister, sp, StackAlignment - 1);
+    as_andi(ScratchRegister, sp, ABIStackAlignment - 1);
     ma_b(ScratchRegister, zero, &aligned, Equal, ShortJump);
     as_break(MAX_BREAK_CODE);
     bind(&aligned);
 #endif
 }
 
 void
 MacroAssemblerMIPSCompat::alignStackPointer()
 {
     movePtr(StackPointer, SecondScratchReg);
     subPtr(Imm32(sizeof(uintptr_t)), StackPointer);
-    andPtr(Imm32(~(StackAlignment - 1)), StackPointer);
+    andPtr(Imm32(~(ABIStackAlignment - 1)), StackPointer);
     storePtr(SecondScratchReg, Address(StackPointer, 0));
 }
 
 void
 MacroAssemblerMIPSCompat::restoreStackPointer()
 {
     loadPtr(Address(StackPointer, 0), StackPointer);
 }
 
 void
 MacroAssembler::alignFrameForICArguments(AfterICSaveLive &aic)
 {
-    if (framePushed() % StackAlignment != 0) {
-        aic.alignmentPadding = StackAlignment - (framePushed() % StackAlignment);
+    if (framePushed() % ABIStackAlignment != 0) {
+        aic.alignmentPadding = ABIStackAlignment - (framePushed() % StackAlignment);
         reserveStack(aic.alignmentPadding);
     } else {
         aic.alignmentPadding = 0;
     }
-    MOZ_ASSERT(framePushed() % StackAlignment == 0);
+    MOZ_ASSERT(framePushed() % ABIStackAlignment == 0);
     checkStackAlignment();
 }
 
 void
 MacroAssembler::restoreFrameAlignmentForICArguments(AfterICSaveLive &aic)
 {
     if (aic.alignmentPadding != 0)
         freeStack(aic.alignmentPadding);
@@ -3311,20 +3311,20 @@ MacroAssemblerMIPSCompat::callWithABIPre
 
     *stackAdjust += usedArgSlots_ > NumIntArgRegs ?
                     usedArgSlots_ * sizeof(intptr_t) :
                     NumIntArgRegs * sizeof(intptr_t);
 
     uint32_t alignmentAtPrologue = callFromAsmJS ? sizeof(AsmJSFrame) : 0;
 
     if (dynamicAlignment_) {
-        *stackAdjust += ComputeByteAlignment(*stackAdjust, StackAlignment);
+        *stackAdjust += ComputeByteAlignment(*stackAdjust, ABIStackAlignment);
     } else {
         *stackAdjust += ComputeByteAlignment(framePushed_ + alignmentAtPrologue + *stackAdjust,
-                                             StackAlignment);
+                                             ABIStackAlignment);
     }
 
     reserveStack(*stackAdjust);
 
     // Save $ra because call is going to clobber it. Restore it in
     // callWithABIPost. NOTE: This is needed for calls from BaselineIC.
     // Maybe we can do this differently.
     ma_sw(ra, Address(StackPointer, *stackAdjust - sizeof(intptr_t)));
@@ -3439,17 +3439,17 @@ MacroAssemblerMIPSCompat::callWithABI(co
     callWithABIPost(stackAdjust, result);
 
 }
 
 void
 MacroAssemblerMIPSCompat::handleFailureWithHandler(void *handler)
 {
     // Reserve space for exception information.
-    int size = (sizeof(ResumeFromException) + StackAlignment) & ~(StackAlignment - 1);
+    int size = (sizeof(ResumeFromException) + ABIStackAlignment) & ~(ABIStackAlignment - 1);
     ma_subu(StackPointer, StackPointer, Imm32(size));
     ma_move(a0, StackPointer); // Use a0 since it is a first function argument
 
     // Ask for an exception handler.
     setupUnalignedABICall(1, a1);
     passABIArg(a0);
     callWithABI(handler);
 
--- a/js/src/jit/mips/Simulator-mips.cpp
+++ b/js/src/jit/mips/Simulator-mips.cpp
@@ -1866,17 +1866,17 @@ Simulator::softwareInterrupt(SimInstruct
         int32_t arg5 = stack_pointer[5];
 
         // This is dodgy but it works because the C entry stubs are never moved.
         // See comment in codegen-arm.cc and bug 1242173.
         int32_t saved_ra = getRegister(ra);
 
         intptr_t external = reinterpret_cast<intptr_t>(redirection->nativeFunction());
 
-        bool stack_aligned = (getRegister(sp) & (StackAlignment - 1)) == 0;
+        bool stack_aligned = (getRegister(sp) & (ABIStackAlignment - 1)) == 0;
         if (!stack_aligned) {
             fprintf(stderr, "Runtime call with unaligned stack!\n");
             MOZ_CRASH();
         }
 
         switch (redirection->type()) {
           case Args_General0: {
             Prototype_General0 target = reinterpret_cast<Prototype_General0>(external);
@@ -3400,17 +3400,17 @@ Simulator::call(uint8_t *entry, int argu
     int original_stack = getRegister(sp);
     // Compute position of stack on entry to generated code.
     int entry_stack = original_stack;
     if (argument_count > kCArgSlotCount)
         entry_stack = entry_stack - argument_count * sizeof(int32_t);
     else
         entry_stack = entry_stack - kCArgsSlotsSize;
 
-    entry_stack &= ~StackAlignment;
+    entry_stack &= ~ABIStackAlignment;
 
     intptr_t *stack_argument = reinterpret_cast<intptr_t*>(entry_stack);
 
     // Setup the arguments.
     for (int i = 0; i < argument_count; i++) {
         js::jit::Register argReg;
         if (GetIntArgReg(i, &argReg))
             setRegister(argReg.code(), va_arg(parameters, int32_t));
--- a/js/src/jit/none/Architecture-none.h
+++ b/js/src/jit/none/Architecture-none.h
@@ -11,16 +11,17 @@
 // platforms, so include it here to avoid inadvertent build bustage.
 #include "jit/IonSpewer.h"
 
 namespace js {
 namespace jit {
 
 static const bool SupportsSimd = false;
 static const uint32_t SimdStackAlignment = 0;
+static const uint32_t AsmJSStackAlignment = 0;
 
 class Registers
 {
   public:
     typedef uint8_t Code;
     typedef uint8_t SetType;
 
     static uint32_t SetSize(SetType) { MOZ_CRASH(); }
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -63,19 +63,18 @@ static MOZ_CONSTEXPR_VAR Register JSRetu
 #if defined(JS_NUNBOX32)
 static MOZ_CONSTEXPR_VAR ValueOperand JSReturnOperand(InvalidReg, InvalidReg);
 #elif defined(JS_PUNBOX64)
 static MOZ_CONSTEXPR_VAR ValueOperand JSReturnOperand(InvalidReg);
 #else
 #error "Bad architecture"
 #endif
 
-static const uint32_t StackAlignment = 8;
+static const uint32_t ABIStackAlignment = 4;
 static const uint32_t CodeAlignment = 4;
-static const bool StackKeptAligned = false;
 
 static const Scale ScalePointer = TimesOne;
 
 class Assembler : public AssemblerShared
 {
   public:
     enum Condition {
         Equal,
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -635,19 +635,19 @@ class CallSite : public CallSiteDesc
     // address on all archs (whether or not the call instruction pushes the
     // return address (x86/x64) or the prologue does (ARM/MIPS)).
     uint32_t stackDepth() const { return stackDepth_; }
 };
 
 typedef Vector<CallSite, 0, SystemAllocPolicy> CallSiteVector;
 
 // As an invariant across architectures, within asm.js code:
-//   $sp % StackAlignment = (sizeof(AsmJSFrame) + masm.framePushed) % StackAlignment
+//   $sp % AsmJSStackAlignment = (sizeof(AsmJSFrame) + masm.framePushed) % AsmJSStackAlignment
 // Thus, AsmJSFrame represents the bytes pushed after the call (which occurred
-// with a StackAlignment-aligned StackPointer) that are not included in
+// with a AsmJSStackAlignment-aligned StackPointer) that are not included in
 // masm.framePushed.
 struct AsmJSFrame
 {
     // The caller's saved frame pointer. In non-profiling mode, internal
     // asm.js-to-asm.js calls don't update fp and thus don't save the caller's
     // frame pointer; the space is reserved, however, so that profiling mode can
     // reuse the same function body without recompiling.
     uint8_t *callerFP;
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -921,16 +921,17 @@ class AssemblerX86Shared : public Assemb
     }
 
 #ifdef DEBUG
     static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
 #endif
     static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }
     static bool HasSSE41() { return CPUInfo::IsSSE41Present(); }
     static bool SupportsFloatingPoint() { return CPUInfo::IsSSE2Present(); }
+    static bool SupportsSimd() { return CPUInfo::IsSSE2Present(); }
 
     // The below cmpl methods switch the lhs and rhs when it invokes the
     // macroassembler to conform with intel standard.  When calling this
     // function put the left operand on the left as you would expect.
     void cmpl(Register lhs, Register rhs) {
         masm.cmpl_rr(rhs.code(), lhs.code());
     }
     void cmpl(Register lhs, const Operand &rhs) {
--- 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-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -64,76 +64,44 @@ CodeGeneratorShared::CodeGeneratorShared
     checkOsiPointRegisters(js_JitOptions.checkOsiPointRegisters),
 #endif
     frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()),
     frameInitialAdjustment_(0)
 {
     if (!gen->compilingAsmJS())
         masm.setInstrumentation(&sps_);
 
-    // Since asm.js uses the system ABI which does not necessarily use a
-    // regular array where all slots are sizeof(Value), it maintains the max
-    // argument stack depth separately.
     if (gen->compilingAsmJS()) {
+        // Since asm.js uses the system ABI which does not necessarily use a
+        // regular array where all slots are sizeof(Value), it maintains the max
+        // argument stack depth separately.
         JS_ASSERT(graph->argumentSlotCount() == 0);
         frameDepth_ += gen->maxAsmJSStackArgBytes();
 
+        // If the function uses any SIMD, we may need to insert padding so that
+        // local slots are aligned for SIMD.
+        if (gen->usesSimd()) {
+            frameInitialAdjustment_ = ComputeByteAlignment(sizeof(AsmJSFrame), AsmJSStackAlignment);
+            frameDepth_ += frameInitialAdjustment_;
+        }
+
         // An MAsmJSCall does not align the stack pointer at calls sites but instead
-        // relies on the a priori stack adjustment (in the prologue) on platforms
-        // (like x64) which require the stack to be aligned.
-        if (StackKeptAligned || gen->performsCall() || gen->usesSimd()) {
-            unsigned alignmentAtCall = sizeof(AsmJSFrame) + frameDepth_;
-            unsigned firstFixup = 0;
-            if (unsigned rem = alignmentAtCall % StackAlignment)
-                frameDepth_ += (firstFixup = StackAlignment - rem);
-
-            if (gen->usesSimd())
-                setupSimdAlignment(firstFixup);
-        }
+        // relies on the a priori stack adjustment. This must be the last
+        // adjustment of frameDepth_.
+        if (gen->performsCall())
+            frameDepth_ += ComputeByteAlignment(sizeof(AsmJSFrame) + frameDepth_, AsmJSStackAlignment);
 
         // FrameSizeClass is only used for bailing, which cannot happen in
         // asm.js code.
         frameClass_ = FrameSizeClass::None();
     } else {
         frameClass_ = FrameSizeClass::FromDepth(frameDepth_);
     }
 }
 
-void
-CodeGeneratorShared::setupSimdAlignment(unsigned fixup)
-{
-    JS_STATIC_ASSERT(SimdStackAlignment % StackAlignment == 0);
-    //  At this point, we have:
-    //      (frameDepth_ + sizeof(AsmJSFrame)) % StackAlignment == 0
-    //  which means we can add as many SimdStackAlignment as needed.
-
-    //  The next constraint is to have all stack slots
-    //  aligned for SIMD. That's done by having the first stack slot
-    //  aligned. We need an offset such that:
-    //      (frameDepth_ - offset) % SimdStackAlignment == 0
-    frameInitialAdjustment_ = frameDepth_ % SimdStackAlignment;
-
-    //  We need to ensure that the first stack slot is actually
-    //  located in this frame and not beforehand, when taking this
-    //  offset into account, i.e.:
-    //      frameDepth_ - initial adjustment >= frameDepth_ - fixup
-    //  <=>                            fixup >= initial adjustment
-    //
-    //  For instance, on x86 with gcc, if the initial frameDepth
-    //  % 16 is 8, then the fixup is 0, although the initial
-    //  adjustment is 8. The first stack slot would be located at
-    //  frameDepth - 8 in this case, which is obviously before
-    //  frameDepth.
-    //
-    //  If that's not the case, we add SimdStackAlignment to the
-    //  fixup, which will keep on satisfying other constraints.
-    if (frameInitialAdjustment_ > int32_t(fixup))
-        frameDepth_ += SimdStackAlignment;
-}
-
 bool
 CodeGeneratorShared::generateOutOfLineCode()
 {
     JSScript *topScript = sps_.getPushed();
     for (size_t i = 0; i < outOfLineCode_.length(); i++) {
         // Add native => bytecode mapping entries for OOL sites.
         // Not enabled on asm.js yet since asm doesn't contain bytecode mappings.
         if (!gen->compilingAsmJS()) {
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -491,18 +491,16 @@ class CodeGeneratorShared : public LInst
 // This function is not used for MIPS. MIPS has branchToBlock.
 #ifndef JS_CODEGEN_MIPS
     void jumpToBlock(MBasicBlock *mir, Assembler::Condition cond);
 #endif
 
   private:
     void generateInvalidateEpilogue();
 
-    void setupSimdAlignment(unsigned fixup);
-
   public:
     CodeGeneratorShared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
     template <class ArgSeq, class StoreOutputTo>
     bool visitOutOfLineCallVM(OutOfLineCallVM<ArgSeq, StoreOutputTo> *ool);
 
     bool visitOutOfLineTruncateSlow(OutOfLineTruncateSlow *ool);
--- 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
@@ -179,29 +179,28 @@ class ABIArgGenerator
     static const Register NonArg_VolatileReg;
     static const Register NonReturn_VolatileReg0;
 };
 
 static MOZ_CONSTEXPR_VAR Register OsrFrameReg = IntArgReg3;
 
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = rdx;
 
-// GCC stack is aligned on 16 bytes, but we don't maintain the invariant in
-// jitted code.
-static const uint32_t StackAlignment = 16;
-static const bool StackKeptAligned = false;
+static const uint32_t ABIStackAlignment = 16;
 static const uint32_t CodeAlignment = 8;
 
 // This boolean indicates whether we support SIMD instructions flavoured for
 // this architecture or not. Rather than a method in the LIRGenerator, it is
 // here such that it is accessible from the entire codebase. Once full support
 // for SIMD is reached on all tier-1 platforms, this constant can be deleted.
 static const bool SupportsSimd = true;
 static const uint32_t SimdStackAlignment = 16;
 
+static const uint32_t AsmJSStackAlignment = SimdStackAlignment;
+
 static const Scale ScalePointer = TimesEight;
 
 } // namespace jit
 } // namespace js
 
 #include "jit/shared/Assembler-x86-shared.h"
 
 namespace js {
@@ -598,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/x64/MacroAssembler-x64.cpp
+++ b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -195,17 +195,17 @@ MacroAssemblerX64::setupAlignedABICall(u
 
 void
 MacroAssemblerX64::setupUnalignedABICall(uint32_t args, Register scratch)
 {
     setupABICall(args);
     dynamicAlignment_ = true;
 
     movq(rsp, scratch);
-    andq(Imm32(~(StackAlignment - 1)), rsp);
+    andq(Imm32(~(ABIStackAlignment - 1)), rsp);
     push(scratch);
 }
 
 void
 MacroAssemblerX64::passABIArg(const MoveOperand &from, MoveOp::Type type)
 {
     MoveOperand to;
     switch (type) {
@@ -265,21 +265,21 @@ void
 MacroAssemblerX64::callWithABIPre(uint32_t *stackAdjust)
 {
     JS_ASSERT(inCall_);
     JS_ASSERT(args_ == passedIntArgs_ + passedFloatArgs_);
 
     if (dynamicAlignment_) {
         *stackAdjust = stackForCall_
                      + ComputeByteAlignment(stackForCall_ + sizeof(intptr_t),
-                                            StackAlignment);
+                                            ABIStackAlignment);
     } else {
         *stackAdjust = stackForCall_
                      + ComputeByteAlignment(stackForCall_ + framePushed_,
-                                            StackAlignment);
+                                            ABIStackAlignment);
     }
 
     reserveStack(*stackAdjust);
 
     // Position all arguments.
     {
         enoughMemory_ &= moveResolver_.resolve();
         if (!enoughMemory_)
@@ -288,17 +288,17 @@ MacroAssemblerX64::callWithABIPre(uint32
         MoveEmitter emitter(*this);
         emitter.emit(moveResolver_);
         emitter.finish();
     }
 
 #ifdef DEBUG
     {
         Label good;
-        testq(rsp, Imm32(StackAlignment - 1));
+        testq(rsp, Imm32(ABIStackAlignment - 1));
         j(Equal, &good);
         breakpoint();
         bind(&good);
     }
 #endif
 }
 
 void
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -546,17 +546,16 @@ JitRuntime::generateBailoutHandler(JSCon
 #endif
 
     return code;
 }
 
 JitCode *
 JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
 {
-    JS_ASSERT(!StackKeptAligned);
     JS_ASSERT(functionWrappers_);
     JS_ASSERT(functionWrappers_->initialized());
     VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
     if (p)
         return p->value();
 
     // Generate a separated code for the wrapper.
     MacroAssembler masm;
--- 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
@@ -103,33 +103,34 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = edx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = edi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = eax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = esi;
 
-// GCC stack is aligned on 16 bytes, but we don't maintain the invariant in
-// jitted code.
+// GCC stack is aligned on 16 bytes. Ion does not maintain this for internal
+// calls. asm.js code does.
 #if defined(__GNUC__)
-static const uint32_t StackAlignment = 16;
+static const uint32_t ABIStackAlignment = 16;
 #else
-static const uint32_t StackAlignment = 4;
+static const uint32_t ABIStackAlignment = 4;
 #endif
-static const bool StackKeptAligned = false;
 static const uint32_t CodeAlignment = 8;
 
 // This boolean indicates whether we support SIMD instructions flavoured for
 // this architecture or not. Rather than a method in the LIRGenerator, it is
 // here such that it is accessible from the entire codebase. Once full support
 // for SIMD is reached on all tier-1 platforms, this constant can be deleted.
 static const bool SupportsSimd = true;
 static const uint32_t SimdStackAlignment = 16;
 
+static const uint32_t AsmJSStackAlignment = SimdStackAlignment;
+
 struct ImmTag : public Imm32
 {
     ImmTag(JSValueTag mask)
       : Imm32(int32_t(mask))
     { }
 };
 
 struct ImmType : public ImmTag
@@ -517,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);
@@ -541,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/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -222,17 +222,17 @@ MacroAssemblerX86::setupAlignedABICall(u
 
 void
 MacroAssemblerX86::setupUnalignedABICall(uint32_t args, Register scratch)
 {
     setupABICall(args);
     dynamicAlignment_ = true;
 
     movl(esp, scratch);
-    andl(Imm32(~(StackAlignment - 1)), esp);
+    andl(Imm32(~(ABIStackAlignment - 1)), esp);
     push(scratch);
 }
 
 void
 MacroAssemblerX86::passABIArg(const MoveOperand &from, MoveOp::Type type)
 {
     ++passedArgs_;
     MoveOperand to = MoveOperand(StackPointer, stackForCall_);
@@ -262,21 +262,21 @@ void
 MacroAssemblerX86::callWithABIPre(uint32_t *stackAdjust)
 {
     JS_ASSERT(inCall_);
     JS_ASSERT(args_ == passedArgs_);
 
     if (dynamicAlignment_) {
         *stackAdjust = stackForCall_
                      + ComputeByteAlignment(stackForCall_ + sizeof(intptr_t),
-                                            StackAlignment);
+                                            ABIStackAlignment);
     } else {
         *stackAdjust = stackForCall_
                      + ComputeByteAlignment(stackForCall_ + framePushed_,
-                                            StackAlignment);
+                                            ABIStackAlignment);
     }
 
     reserveStack(*stackAdjust);
 
     // Position all arguments.
     {
         enoughMemory_ &= moveResolver_.resolve();
         if (!enoughMemory_)
@@ -286,17 +286,17 @@ MacroAssemblerX86::callWithABIPre(uint32
         emitter.emit(moveResolver_);
         emitter.finish();
     }
 
 #ifdef DEBUG
     {
         // Check call alignment.
         Label good;
-        testl(esp, Imm32(StackAlignment - 1));
+        testl(esp, Imm32(ABIStackAlignment - 1));
         j(Equal, &good);
         breakpoint();
         bind(&good);
     }
 #endif
 }
 
 void
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -585,17 +585,16 @@ JitRuntime::generateBailoutHandler(JSCon
 #endif
 
     return code;
 }
 
 JitCode *
 JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
 {
-    JS_ASSERT(!StackKeptAligned);
     JS_ASSERT(functionWrappers_);
     JS_ASSERT(functionWrappers_->initialized());
     VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
     if (p)
         return p->value();
 
     // Generate a separated code for the wrapper.
     MacroAssembler masm;
--- 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")
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -290,16 +290,17 @@ struct ThreadSafeContext : ContextFriend
     void *runtimeAddressForJit() { return runtime_; }
     void *runtimeAddressOfInterrupt() { return &runtime_->interrupt; }
     void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
     void *stackLimitAddressForJitCode(StackKind kind);
     size_t gcSystemPageSize() { return gc::SystemPageSize(); }
     bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); }
     bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); }
     bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
+    bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; }
 
     // Thread local data that may be accessed freely.
     DtoaState *dtoaState() {
         return perThreadData->dtoaState;
     }
 };
 
 struct HelperThread;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -206,16 +206,17 @@ JSRuntime::JSRuntime(JSRuntime *parentRu
     atomsCompartment_(nullptr),
     staticStrings(nullptr),
     commonNames(nullptr),
     permanentAtoms(nullptr),
     wellKnownSymbols(nullptr),
     wrapObjectCallbacks(&DefaultWrapObjectCallbacks),
     preserveWrapperCallback(nullptr),
     jitSupportsFloatingPoint(false),
+    jitSupportsSimd(false),
     ionPcScriptCache(nullptr),
     threadPool(this),
     defaultJSContextCallback(nullptr),
     ctypesActivityCallback(nullptr),
     forkJoinWarmup(0),
     offthreadIonCompilationEnabled_(true),
     parallelParsingEnabled_(true),
 #ifdef DEBUG
@@ -310,16 +311,17 @@ JSRuntime::init(uint32_t maxbytes, uint3
     simulatorRuntime_ = js::jit::CreateSimulatorRuntime();
     if (!simulatorRuntime_)
         return false;
 #endif
 
     nativeStackBase = GetNativeStackBase();
 
     jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
+    jitSupportsSimd = js::jit::JitSupportsSimd();
 
     signalHandlersInstalled_ = EnsureAsmJSSignalHandlersInstalled(this);
     canUseSignalHandlers_ = signalHandlersInstalled_ && !SignalBasedTriggersDisabled();
 
     if (!spsProfiler.init())
         return false;
 
     return true;
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1269,16 +1269,17 @@ struct JSRuntime : public JS::shadow::Ru
     js::ScriptDataTable scriptDataTable_;
   public:
     js::ScriptDataTable &scriptDataTable() {
         JS_ASSERT(currentThreadHasExclusiveAccess());
         return scriptDataTable_;
     }
 
     bool                jitSupportsFloatingPoint;
+    bool                jitSupportsSimd;
 
     // Used to reset stack limit after a signaled interrupt (i.e. jitStackLimit_ = -1)
     // has been noticed by Ion/Baseline.
     void resetJitStackLimit();
 
     // Cache for jit::GetPcScript().
     js::jit::PcScriptCache *ionPcScriptCache;
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1545,17 +1545,17 @@ jit::JitActivation::markRematerializedFr
         return;
     for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront())
         RematerializedFrame::MarkInVector(trc, e.front().value());
 }
 
 AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
   : Activation(cx, AsmJS),
     module_(module),
-    errorRejoinSP_(nullptr),
+    entrySP_(nullptr),
     profiler_(nullptr),
     resumePC_(nullptr),
     fp_(nullptr),
     exitReason_(AsmJSExit::None)
 {
     if (cx->runtime()->spsProfiler.enabled()) {
         // Use a profiler string that matches jsMatch regex in
         // browser/devtools/profiler/cleopatra/js/parserWorker.js.
@@ -1568,17 +1568,17 @@ AsmJSActivation::AsmJSActivation(JSConte
     prevAsmJSForModule_ = module.activation();
     module.activation() = this;
 
     prevAsmJS_ = cx->mainThread().asmJSActivationStack_;
 
     JSRuntime::AutoLockForInterrupt lock(cx->runtime());
     cx->mainThread().asmJSActivationStack_ = this;
 
-    (void) errorRejoinSP_;  // squelch GCC warning
+    (void) entrySP_;  // squelch GCC warning
 }
 
 AsmJSActivation::~AsmJSActivation()
 {
     if (profiler_)
         profiler_->exitAsmJS();
 
     JS_ASSERT(fp_ == nullptr);
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1477,17 +1477,17 @@ class InterpreterFrameIterator
 // JitActivation interleaved with Ion/Baseline jit code. This would allow
 // efficient calls back and forth but requires that we can walk the stack for
 // all kinds of jit code.
 class AsmJSActivation : public Activation
 {
     AsmJSModule &module_;
     AsmJSActivation *prevAsmJS_;
     AsmJSActivation *prevAsmJSForModule_;
-    void *errorRejoinSP_;
+    void *entrySP_;
     SPSProfiler *profiler_;
     void *resumePC_;
     uint8_t *fp_;
     AsmJSExit::Reason exitReason_;
 
   public:
     AsmJSActivation(JSContext *cx, AsmJSModule &module);
     ~AsmJSActivation();
@@ -1507,17 +1507,17 @@ class AsmJSActivation : public Activatio
     // Returns the reason why asm.js code called out of asm.js code.
     AsmJSExit::Reason exitReason() const { return exitReason_; }
 
     // Read by JIT code:
     static unsigned offsetOfContext() { return offsetof(AsmJSActivation, cx_); }
     static unsigned offsetOfResumePC() { return offsetof(AsmJSActivation, resumePC_); }
 
     // Written by JIT code:
-    static unsigned offsetOfErrorRejoinSP() { return offsetof(AsmJSActivation, errorRejoinSP_); }
+    static unsigned offsetOfEntrySP() { return offsetof(AsmJSActivation, entrySP_); }
     static unsigned offsetOfFP() { return offsetof(AsmJSActivation, fp_); }
     static unsigned offsetOfExitReason() { return offsetof(AsmJSActivation, exitReason_); }
 
     // Set from SIGSEGV handler:
     void setResumePC(void *pc) { resumePC_ = pc; }
 };
 
 // A FrameIter walks over the runtime's stack of JS script activations,
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -22,16 +22,17 @@
 #include "nsISelectionController.h"
 #include "nsIPresShell.h"
 #include "nsRegion.h"
 #include "nsStyleStructInlines.h"
 #include "nsStyleTransformMatrix.h"
 #include "gfxMatrix.h"
 #include "gfxPrefs.h"
 #include "nsSVGIntegrationUtils.h"
+#include "nsSVGUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsIScrollableFrame.h"
 #include "nsIFrameInlines.h"
 #include "nsThemeConstants.h"
 #include "LayerTreeInvalidation.h"
 
 #include "imgIContainer.h"
 #include "BasicLayers.h"
@@ -5494,16 +5495,50 @@ bool nsDisplaySVGEffects::TryMerge(nsDis
     return false;
   nsDisplaySVGEffects* other = static_cast<nsDisplaySVGEffects*>(aItem);
   MergeFromTrackingMergedFrames(other);
   mEffectsBounds.UnionRect(mEffectsBounds,
     other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame));
   return true;
 }
 
+gfxRect
+nsDisplaySVGEffects::BBoxInUserSpace() const
+{
+  return nsSVGUtils::GetBBox(mFrame);
+}
+
+gfxPoint
+nsDisplaySVGEffects::UserSpaceOffset() const
+{
+  return nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(mFrame);
+}
+
+void
+nsDisplaySVGEffects::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+                                               const nsDisplayItemGeometry* aGeometry,
+                                               nsRegion* aInvalidRegion)
+{
+  const nsDisplaySVGEffectsGeometry* geometry =
+    static_cast<const nsDisplaySVGEffectsGeometry*>(aGeometry);
+  bool snap;
+  nsRect bounds = GetBounds(aBuilder, &snap);
+  if (geometry->mFrameOffsetToReferenceFrame != ToReferenceFrame() ||
+      geometry->mUserSpaceOffset != UserSpaceOffset() ||
+      !geometry->mBBox.IsEqualInterior(BBoxInUserSpace())) {
+    // Filter and mask output can depend on the location of the frame's user
+    // space and on the frame's BBox. We need to invalidate if either of these
+    // change relative to the reference frame.
+    // Invalidations from our inactive layer manager are not enough to catch
+    // some of these cases because filters can produce output even if there's
+    // nothing in the filter input.
+    aInvalidRegion->Or(bounds, geometry->mBounds);
+  }
+}
+
 #ifdef MOZ_DUMP_PAINTING
 void
 nsDisplaySVGEffects::PrintEffects(nsACString& aTo)
 {
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -3145,23 +3145,27 @@ public:
 
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters) MOZ_OVERRIDE;
  
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE;
-  
+
+  gfxRect BBoxInUserSpace() const;
+  gfxPoint UserSpaceOffset() const;
+
+  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE
+  {
+    return new nsDisplaySVGEffectsGeometry(this, aBuilder);
+  }
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
-                                         nsRegion* aInvalidRegion) MOZ_OVERRIDE
-  {
-    // We don't need to compute an invalidation region since we have LayerTreeInvalidation
-  }
+                                         nsRegion* aInvalidRegion) MOZ_OVERRIDE;
 
   void PaintAsLayer(nsDisplayListBuilder* aBuilder,
                     nsRenderingContext* aCtx,
                     LayerManager* aManager);
 
 #ifdef MOZ_DUMP_PAINTING
   void PrintEffects(nsACString& aTo);
 #endif
--- a/layout/base/nsDisplayListInvalidation.cpp
+++ b/layout/base/nsDisplayListInvalidation.cpp
@@ -89,8 +89,22 @@ nsDisplayBoxShadowInnerGeometry::MoveBy(
   mPaddingRect.MoveBy(aOffset);
 }
 
 nsDisplayBoxShadowOuterGeometry::nsDisplayBoxShadowOuterGeometry(nsDisplayItem* aItem,
     nsDisplayListBuilder* aBuilder, float aOpacity)
   : nsDisplayItemGenericGeometry(aItem, aBuilder)
   , mOpacity(aOpacity)
 {}
+
+nsDisplaySVGEffectsGeometry::nsDisplaySVGEffectsGeometry(nsDisplaySVGEffects* aItem, nsDisplayListBuilder* aBuilder)
+  : nsDisplayItemGeometry(aItem, aBuilder)
+  , mBBox(aItem->BBoxInUserSpace())
+  , mUserSpaceOffset(aItem->UserSpaceOffset())
+  , mFrameOffsetToReferenceFrame(aItem->ToReferenceFrame())
+{}
+
+void
+nsDisplaySVGEffectsGeometry::MoveBy(const nsPoint& aOffset)
+{
+  mBounds.MoveBy(aOffset);
+  mFrameOffsetToReferenceFrame += aOffset;
+}
--- a/layout/base/nsDisplayListInvalidation.h
+++ b/layout/base/nsDisplayListInvalidation.h
@@ -4,21 +4,23 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NSDISPLAYLISTINVALIDATION_H_
 #define NSDISPLAYLISTINVALIDATION_H_
 
 #include "mozilla/Attributes.h"
 #include "nsRect.h"
 #include "nsColor.h"
+#include "gfxRect.h"
 
 class nsDisplayItem;
 class nsDisplayListBuilder;
 class nsDisplayBackgroundImage;
 class nsDisplayThemedBackground;
+class nsDisplaySVGEffects;
 
 /**
  * This stores the geometry of an nsDisplayItem, and the area
  * that will be affected when painting the item.
  *
  * It is used to retain information about display items so they
  * can be compared against new display items in the next paint.
  */
@@ -136,9 +138,21 @@ public:
                               nscolor aColor)
     : nsDisplayItemBoundsGeometry(aItem, aBuilder)
     , mColor(aColor)
   { }
 
   nscolor mColor;
 };
 
+class nsDisplaySVGEffectsGeometry : public nsDisplayItemGeometry
+{
+public:
+  nsDisplaySVGEffectsGeometry(nsDisplaySVGEffects* aItem, nsDisplayListBuilder* aBuilder);
+
+  virtual void MoveBy(const nsPoint& aOffset) MOZ_OVERRIDE;
+
+  gfxRect mBBox;
+  gfxPoint mUserSpaceOffset;
+  nsPoint mFrameOffsetToReferenceFrame;
+};
+
 #endif /*NSDISPLAYLISTINVALIDATION_H_*/
--- a/layout/mathml/nsMathMLContainerFrame.cpp
+++ b/layout/mathml/nsMathMLContainerFrame.cpp
@@ -466,16 +466,17 @@ nsMathMLContainerFrame::FinalizeReflow(n
 
   // Place() will call FinishReflowChild() when placeOrigin is true but if
   // it returns before reaching FinishReflowChild() due to errors we need
   // to fulfill the reflow protocol by calling DidReflow for the child frames
   // that still needs it here (or we may crash - bug 366012).
   // If placeOrigin is false we should reach Place() with aPlaceOrigin == true
   // through Stretch() eventually.
   if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
+    GatherAndStoreOverflow(&aDesiredSize);
     DidReflowChildren(GetFirstPrincipalChild());
     return rv;
   }
 
   bool parentWillFireStretch = false;
   if (!placeOrigin) {
     // This means the rect.x and rect.y of our children were not set!!
     // Don't go without checking to see if our parent will later fire a Stretch() command
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1021564-1.html
@@ -0,0 +1,46 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>When the filtered element's BBox relative to the page changes, the filtered element needs to be invalidated</title>
+
+<style>
+
+#spacer {
+  height: 100px;
+}
+
+#filtered {
+  width: 100px;
+  height: 100px;
+  background-color: lime;
+  filter: url(#filter);
+}
+
+</style>
+
+<svg height="0">
+  <defs>
+    <filter id="filter" filterUnits="objectBoundingBox"
+            x="0%" y="0%" width="100%" height="100%"
+            color-interpolation-filters="sRGB">
+      <feMerge><feMergeNode/></feMerge>
+    </filter>
+  </defs>
+</svg>
+
+<div id="spacer"></div>
+
+<div id="filtered"></div>
+
+<script>
+
+window.addEventListener("MozReftestInvalidate", function () {
+  document.getElementById("spacer").style.height = "50px";
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1021564-2.html
@@ -0,0 +1,45 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>When the filtered element's BBox relative to the page changes, the filtered element needs to be invalidated</title>
+
+<style>
+
+#spacer {
+  height: 100px;
+}
+
+#filtered {
+  width: 100px;
+  height: 100px;
+  filter: url(#filter);
+}
+
+</style>
+
+<svg height="0">
+  <defs>
+    <filter id="filter" filterUnits="objectBoundingBox"
+            x="0%" y="0%" width="100%" height="100%"
+            color-interpolation-filters="sRGB">
+      <feFlood flood-color="lime"/>
+    </filter>
+  </defs>
+</svg>
+
+<div id="spacer"></div>
+
+<div id="filtered"></div>
+
+<script>
+
+window.addEventListener("MozReftestInvalidate", function () {
+  document.getElementById("spacer").style.height = "50px";
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1021564-3.html
@@ -0,0 +1,46 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>When the filtered element's BBox relative to the page changes, the filtered element needs to be invalidated</title>
+
+<style>
+
+#spacer {
+  height: 100px;
+}
+
+#filtered {
+  width: 100px;
+  height: 100px;
+  background-color: lime;
+  filter: url(#filter);
+}
+
+</style>
+
+<svg height="0">
+  <defs>
+    <filter id="filter" filterUnits="userSpaceOnUse"
+            x="0" y="0" width="100" height="100"
+            color-interpolation-filters="sRGB">
+      <feMerge><feMergeNode/></feMerge>
+    </filter>
+  </defs>
+</svg>
+
+<div id="spacer"></div>
+
+<div id="filtered"></div>
+
+<script>
+
+window.addEventListener("MozReftestInvalidate", function () {
+  document.getElementById("spacer").style.height = "50px";
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1021564-4.html
@@ -0,0 +1,45 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>When the filtered element's BBox relative to the page changes, the filtered element needs to be invalidated</title>
+
+<style>
+
+#spacer {
+  height: 100px;
+}
+
+#filtered {
+  width: 100px;
+  height: 100px;
+  filter: url(#filter);
+}
+
+</style>
+
+<svg height="0">
+  <defs>
+    <filter id="filter" filterUnits="userSpaceOnUse"
+            x="0" y="0" width="100" height="100"
+            color-interpolation-filters="sRGB">
+      <feFlood flood-color="lime"/>
+    </filter>
+  </defs>
+</svg>
+
+<div id="spacer"></div>
+
+<div id="filtered"></div>
+
+<script>
+
+window.addEventListener("MozReftestInvalidate", function () {
+  document.getElementById("spacer").style.height = "50px";
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1021564-ref.html
@@ -0,0 +1,28 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>When the filtered element's BBox relative to the page changes, the filtered element needs to be invalidated</title>
+
+<style>
+
+#spacer {
+  height: 50px;
+}
+
+#filtered {
+  width: 100px;
+  height: 100px;
+  background-color: lime;
+}
+
+</style>
+
+<svg height="0"></svg>
+
+<div id="spacer"></div>
+
+<div id="filtered"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1025914-1-ref.html
@@ -0,0 +1,44 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>Make sure that scrolling #scrolledBox into view paints the scrolled strip even while #coveringFixedBar covers that strip</title>
+
+<style>
+
+html {
+  overflow: hidden;
+}
+
+body {
+  margin: 0;
+}
+
+#coveringFixedBar {
+  position: absolute;
+  left: 10px;
+  top: 0;
+  width: 380px;
+  height: 20px;
+  background: blue;
+  z-index: 100;
+}
+
+#scrolledBox {
+  position: relative;
+  margin: 0 100px;
+  opacity: 0.9;
+  width: 200px;
+  height: 200px;
+  background: lime;
+  border: 1px solid black;
+}
+
+</style>
+
+<div id="coveringFixedBar"></div>
+
+<div id="scrolledBox"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1025914-1.html
@@ -0,0 +1,59 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Make sure that scrolling #scrolledBox into view paints the scrolled strip even while #coveringFixedBar covers that strip</title>
+
+<style>
+
+html {
+  overflow: hidden;
+}
+
+body {
+  margin: 0;
+  height: 2000px;
+}
+
+#coveringFixedBar {
+  position: fixed;
+  left: 10px;
+  top: 0;
+  width: 380px;
+  height: 20px;
+  background: blue;
+  z-index: 100;
+}
+
+#scrolledBox {
+  position: relative;
+  margin: 0 100px;
+  opacity: 0.9;
+  width: 200px;
+  height: 200px;
+  background: lime;
+  border: 1px solid black;
+}
+
+</style>
+
+<div id="coveringFixedBar"></div>
+
+<div id="scrolledBox"></div>
+
+<script>
+
+document.documentElement.scrollTop = 40;
+
+window.addEventListener("MozReftestInvalidate", function () {
+  document.documentElement.scrollTop = 20;
+  window.requestAnimationFrame(function () {
+    document.documentElement.scrollTop = 0;
+    document.documentElement.removeAttribute("class");
+  });
+});
+
+</script>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1810,18 +1810,23 @@ skip-if(Android) == 966510-2.html 966510
 == 987680-1.html 987680-1-ref.html
 fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,24) == 991046-1.html 991046-1-ref.html
 pref(layout.css.overflow-clip-box.enabled,true) == 992447.html 992447-ref.html
 == 1003425-1.html 1003425-1-ref.html
 == 1003425-2.html 1003425-2-ref.html
 pref(layout.css.sticky.enabled,true) == 1005405-1.html 1005405-1-ref.html
 fuzzy-if(/^Windows\x20NT\x205\.1/.test(http.oscpu),255,1) == 1013054-1.html 1013054-1-ref.html
 pref(layout.css.will-change.enabled,true) == 1018522-1.html 1018522-1-ref.html
+== 1021564-1.html 1021564-ref.html
+== 1021564-2.html 1021564-ref.html
+== 1021564-3.html 1021564-ref.html
+== 1021564-4.html 1021564-ref.html
 pref(browser.display.use_document_fonts,0) == 1022481-1.html 1022481-1-ref.html
 == 1022612-1.html 1022612-1-ref.html
 == 1024473-1.html 1024473-1-ref.html
+== 1025914-1.html 1025914-1-ref.html
 == 1042104-1.html 1042104-1-ref.html
 == 1044198-1.html 1044198-1-ref.html
 == 1049499-1.html 1049499-1-ref.html
 == 1050788-1.html about:blank
 == 1053035-1-flex.html 1053035-1-ref.html
 test-pref(layout.css.grid.enabled,true) == 1053035-1-grid.html 1053035-1-ref.html
 == 1059167-1.html 1059167-1-ref.html
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -130,16 +130,17 @@ TamperOnce(SECItem& item,
     }
     if (!memcmp(foundFirstByte, from, fromLen)) {
       if (alreadyFoundMatch) {
         return Result::FATAL_ERROR_INVALID_ARGS;
       }
       alreadyFoundMatch = true;
       memmove(foundFirstByte, to, toLen);
       p = foundFirstByte + toLen;
+      remaining -= toLen;
     } else {
       p = foundFirstByte + 1;
       --remaining;
     }
   }
 }
 
 Result
--- a/toolkit/xre/WindowsCrtPatch.h
+++ b/toolkit/xre/WindowsCrtPatch.h
@@ -87,17 +87,17 @@ PatchModuleImports(HMODULE module, PIMAG
   RVAPtr<IMAGE_IMPORT_DESCRIPTOR> descriptor(module, importDirectory->VirtualAddress);
 
   for (; descriptor->OriginalFirstThunk; ++descriptor) {
     RVAPtr<char> importedModule(module, descriptor->Name);
     if (!stricmp(importedModule, "kernel32.dll")) {
       RVAPtr<IMAGE_THUNK_DATA> thunk(module, descriptor->OriginalFirstThunk);
       for (; thunk->u1.AddressOfData; ++thunk) {
         RVAPtr<IMAGE_IMPORT_BY_NAME> import(module, thunk->u1.AddressOfData);
-        if (!strcmp(import->Name, "GetLogicalProcessorInformation")) {
+        if (!strcmp((char*)import->Name, "GetLogicalProcessorInformation")) {
           memcpy(import->Name, "DebugBreak", sizeof("DebugBreak"));
         }
       }
     }
   }
 }
 
 PIMAGE_NT_HEADERS NTAPI