merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 04 Oct 2016 11:58:07 +0200
changeset 359205 42c95d88aaaa7c2eca1d278399421d437441ac4d
parent 359173 6bfa0e8a9f20f8c700e0ef60021449bc8cbc8601 (current diff)
parent 359204 40f8f400737a01edca17c982fecf3788419e1e91 (diff)
child 359235 edee4625c0be084f87c896dd3fefd72c44ec5bce
child 359285 f09bf13bb350da8cec1867a02129e437c291280e
child 359315 2c203b9d559e09a5bb1f798ea5aa3bb7f477df1b
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
42c95d88aaaa / 52.0a1 / 20161004030204 / files
nightly linux64
42c95d88aaaa / 52.0a1 / 20161004030204 / files
nightly mac
42c95d88aaaa / 52.0a1 / 20161004030204 / files
nightly win32
42c95d88aaaa / 52.0a1 / 20161004030204 / files
nightly win64
42c95d88aaaa / 52.0a1 / 20161004030204 / 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 mozilla-inbound to mozilla-central a=merge
gfx/layers/apz/test/mochitest/test_bug1285070.html
layout/generic/nsGridContainerFrame.cpp
--- a/devtools/client/debugger/new/index.html
+++ b/devtools/client/debugger/new/index.html
@@ -1,13 +1,13 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <!DOCTYPE html>
-<html>
+<html dir="">
   <head>
     <link rel="stylesheet"
           type="text/css"
           href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" />
     <link rel="stylesheet"
           type="text/css"
           href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" />
     <link rel="stylesheet"
--- a/devtools/server/actors/script.js
+++ b/devtools/server/actors/script.js
@@ -1109,17 +1109,17 @@ const ThreadActor = ActorClassWithSpec(t
    * @param EventTarget eventTarget
    *        The target the event was dispatched on.
    * @returns Array
    */
   _getAllEventListeners: function (eventTarget) {
     let els = Cc["@mozilla.org/eventlistenerservice;1"]
                 .getService(Ci.nsIEventListenerService);
 
-    let targets = els.getEventTargetChainFor(eventTarget);
+    let targets = els.getEventTargetChainFor(eventTarget, true);
     let listeners = [];
 
     for (let target of targets) {
       let handlers = els.getListenerInfoFor(target);
       for (let handler of handlers) {
         // Null is returned for all-events handlers, and native event listeners
         // don't provide any listenerObject, which makes them not that useful to
         // a JS debugger.
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -801,60 +801,17 @@ nsIContent::PreHandleEvent(EventChainPre
       parent = aVisitor.mDestInsertionPoints.LastElement();
       aVisitor.mDestInsertionPoints.SetLength(
         aVisitor.mDestInsertionPoints.Length() - 1);
     }
   }
 
   ShadowRoot* thisShadowRoot = ShadowRoot::FromNode(this);
   if (thisShadowRoot) {
-    // The following events must always be stopped at the root node of the node tree:
-    //   abort
-    //   error
-    //   select
-    //   change
-    //   load
-    //   reset
-    //   resize
-    //   scroll
-    //   selectstart
-    bool stopEvent = false;
-    switch (aVisitor.mEvent->mMessage) {
-      case eImageAbort:
-      case eLoadError:
-      case eFormSelect:
-      case eFormChange:
-      case eLoad:
-      case eFormReset:
-      case eResize:
-      case eScroll:
-      case eSelectStart:
-        stopEvent = true;
-        break;
-      case eUnidentifiedEvent:
-        if (aVisitor.mDOMEvent) {
-          nsAutoString eventType;
-          aVisitor.mDOMEvent->GetType(eventType);
-          if (eventType.EqualsLiteral("abort") ||
-              eventType.EqualsLiteral("error") ||
-              eventType.EqualsLiteral("select") ||
-              eventType.EqualsLiteral("change") ||
-              eventType.EqualsLiteral("load") ||
-              eventType.EqualsLiteral("reset") ||
-              eventType.EqualsLiteral("resize") ||
-              eventType.EqualsLiteral("scroll")) {
-            stopEvent = true;
-          }
-        }
-        break;
-      default:
-        break;
-    }
-
-    if (stopEvent) {
+    if (!aVisitor.mEvent->mFlags.mComposed) {
       // If we do stop propagation, we still want to propagate
       // the event to chrome (nsPIDOMWindow::GetParentTarget()).
       // The load event is special in that we don't ever propagate it
       // to chrome.
       nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
       EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
         ? win->GetParentTarget() : nullptr;
 
@@ -902,17 +859,21 @@ nsIContent::PreHandleEvent(EventChainPre
     NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent &&
                    aVisitor.mEventTargetAtParent != insertionParent),
                  "Retargeting and having insertion parent!");
     if (insertionParent) {
       parent = insertionParent;
     }
   }
 
-  if (parent) {
+  if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent &&
+      IsRootOfNativeAnonymousSubtree() && OwnerDoc() &&
+      OwnerDoc()->GetWindow()) {
+    aVisitor.mParentTarget = OwnerDoc()->GetWindow()->GetParentTarget();
+  } else if (parent) {
     aVisitor.mParentTarget = parent;
   } else {
     aVisitor.mParentTarget = GetComposedDoc();
   }
   return NS_OK;
 }
 
 bool
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -16702,16 +16702,17 @@ class CGEventMethod(CGNativeMember):
 
         self.body = fill(
             """
             RefPtr<${nativeType}> e = new ${nativeType}(aOwner);
             bool trusted = e->Init(aOwner);
             e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable);
             $*{members}
             e->SetTrusted(trusted);
+            e->SetComposed(${eventInit}.mComposed);
             $*{holdJS}
             return e.forget();
             """,
             nativeType=self.descriptorProvider.nativeType.split('::')[-1],
             eventType=self.args[0].name,
             eventInit=self.args[1].name,
             members=members,
             holdJS=holdJS)
--- a/dom/bluetooth/common/webapi/BluetoothLeDeviceEvent.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothLeDeviceEvent.cpp
@@ -102,16 +102,17 @@ BluetoothLeDeviceEvent::Constructor(
                                          scanRecord.Data());
     if (!e->mScanRecord) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return nullptr;
     }
   }
 
   e->SetTrusted(trusted);
+  e->SetComposed(aEventInitDict.mComposed);
   return e.forget();
 }
 
 BluetoothDevice*
 BluetoothLeDeviceEvent::GetDevice() const
 {
   return mDevice;
 }
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -2266,18 +2266,20 @@ CanvasRenderingContext2D::SetMozCurrentT
   if (ObjectToMatrix(aCx, aCurrentTransform, newCTM, aError) && newCTM.IsFinite()) {
     mTarget->SetTransform(newCTM);
   }
 }
 
 void
 CanvasRenderingContext2D::GetMozCurrentTransform(JSContext* aCx,
                                                  JS::MutableHandle<JSObject*> aResult,
-                                                 ErrorResult& aError) const
-{
+                                                 ErrorResult& aError)
+{
+  EnsureTarget();
+
   MatrixToJSObject(aCx, mTarget ? mTarget->GetTransform() : Matrix(),
                    aResult, aError);
 }
 
 void
 CanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* aCx,
                                                         JS::Handle<JSObject*> aCurrentTransform,
                                                         ErrorResult& aError)
@@ -2295,18 +2297,20 @@ CanvasRenderingContext2D::SetMozCurrentT
       mTarget->SetTransform(newCTMInverse);
     }
   }
 }
 
 void
 CanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext* aCx,
                                                         JS::MutableHandle<JSObject*> aResult,
-                                                        ErrorResult& aError) const
-{
+                                                        ErrorResult& aError)
+{
+  EnsureTarget();
+
   if (!mTarget) {
     MatrixToJSObject(aCx, Matrix(), aResult, aError);
     return;
   }
 
   Matrix ctm = mTarget->GetTransform();
 
   if (!ctm.Invert()) {
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -348,23 +348,23 @@ public:
   void Arc(double aX, double aY, double aRadius, double aStartAngle,
            double aEndAngle, bool aAnticlockwise, mozilla::ErrorResult& aError);
   void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
                double aRotation, double aStartAngle, double aEndAngle,
                bool aAnticlockwise, ErrorResult& aError);
 
   void GetMozCurrentTransform(JSContext* aCx,
                               JS::MutableHandle<JSObject*> aResult,
-                              mozilla::ErrorResult& aError) const;
+                              mozilla::ErrorResult& aError);
   void SetMozCurrentTransform(JSContext* aCx,
                               JS::Handle<JSObject*> aCurrentTransform,
                               mozilla::ErrorResult& aError);
   void GetMozCurrentTransformInverse(JSContext* aCx,
                                      JS::MutableHandle<JSObject*> aResult,
-                                     mozilla::ErrorResult& aError) const;
+                                     mozilla::ErrorResult& aError);
   void SetMozCurrentTransformInverse(JSContext* aCx,
                                      JS::Handle<JSObject*> aCurrentTransform,
                                      mozilla::ErrorResult& aError);
   void GetFillRule(nsAString& aFillRule);
   void SetFillRule(const nsAString& aFillRule);
   void GetMozDash(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
                   mozilla::ErrorResult& aError);
   void SetMozDash(JSContext* aCx, const JS::Value& aMozDash,
--- a/dom/events/AnimationEvent.cpp
+++ b/dom/events/AnimationEvent.cpp
@@ -47,16 +47,17 @@ AnimationEvent::Constructor(const Global
   e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
 
   InternalAnimationEvent* internalEvent = e->mEvent->AsAnimationEvent();
   internalEvent->mAnimationName = aParam.mAnimationName;
   internalEvent->mElapsedTime = aParam.mElapsedTime;
   internalEvent->mPseudoElement = aParam.mPseudoElement;
 
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 NS_IMETHODIMP
 AnimationEvent::GetAnimationName(nsAString& aAnimationName)
 {
   aAnimationName = mEvent->AsAnimationEvent()->mAnimationName;
   return NS_OK;
--- a/dom/events/ClipboardEvent.cpp
+++ b/dom/events/ClipboardEvent.cpp
@@ -79,16 +79,17 @@ ClipboardEvent::Constructor(const Global
                              Some(aGlobal.GetSubjectPrincipal()), aRv);
       NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
     }
   }
 
   e->InitClipboardEvent(aType, aParam.mBubbles, aParam.mCancelable,
                         clipboardData);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 NS_IMETHODIMP
 ClipboardEvent::GetClipboardData(nsIDOMDataTransfer** aClipboardData)
 {
   NS_IF_ADDREF(*aClipboardData = GetClipboardData());
   return NS_OK;
--- a/dom/events/CustomEvent.cpp
+++ b/dom/events/CustomEvent.cpp
@@ -55,16 +55,17 @@ CustomEvent::Constructor(const GlobalObj
                          ErrorResult& aRv)
 {
   nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<CustomEvent> e = new CustomEvent(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   JS::Rooted<JS::Value> detail(aGlobal.Context(), aParam.mDetail);
   e->InitCustomEvent(aGlobal.Context(), aType, aParam.mBubbles, aParam.mCancelable, detail, aRv);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 JSObject*
 CustomEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::CustomEventBinding::Wrap(aCx, this, aGivenProto);
 }
--- a/dom/events/DeviceMotionEvent.cpp
+++ b/dom/events/DeviceMotionEvent.cpp
@@ -94,17 +94,17 @@ DeviceMotionEvent::Constructor(const Glo
 
   e->mRotationRate = new DeviceRotationRate(e,
     aEventInitDict.mRotationRate.mAlpha,
     aEventInitDict.mRotationRate.mBeta,
     aEventInitDict.mRotationRate.mGamma);
 
   e->mInterval = aEventInitDict.mInterval;
   e->SetTrusted(trusted);
-
+  e->SetComposed(aEventInitDict.mComposed);
   return e.forget();
 }
 
 /******************************************************************************
  * DeviceAcceleration
  *****************************************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DeviceAcceleration, mOwner)
--- a/dom/events/DragEvent.cpp
+++ b/dom/events/DragEvent.cpp
@@ -106,16 +106,17 @@ DragEvent::Constructor(const GlobalObjec
   e->InitDragEvent(aType, aParam.mBubbles, aParam.mCancelable,
                    aParam.mView, aParam.mDetail, aParam.mScreenX,
                    aParam.mScreenY, aParam.mClientX, aParam.mClientY,
                    aParam.mCtrlKey, aParam.mAltKey, aParam.mShiftKey,
                    aParam.mMetaKey, aParam.mButton, aParam.mRelatedTarget,
                    aParam.mDataTransfer);
   e->InitializeExtraMouseEventDictionaryMembers(aParam);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -410,16 +410,17 @@ Event::Constructor(const GlobalObject& a
                    const EventInit& aParam,
                    ErrorResult& aRv)
 {
   nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<Event> e = new Event(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 uint16_t
 Event::EventPhase() const
 {
   // Note, remember to check that this works also
   // if or when Bug 235441 is fixed.
@@ -562,21 +563,24 @@ Event::PreventDefaultInternal(bool aCall
 void
 Event::SetEventType(const nsAString& aEventTypeArg)
 {
   if (mIsMainThreadEvent) {
     mEvent->mSpecifiedEventTypeString.Truncate();
     mEvent->mSpecifiedEventType =
       nsContentUtils::GetEventMessageAndAtom(aEventTypeArg, mEvent->mClass,
                                              &(mEvent->mMessage));
+    mEvent->SetDefaultComposed();
   } else {
     mEvent->mSpecifiedEventType = nullptr;
     mEvent->mMessage = eUnidentifiedEvent;
     mEvent->mSpecifiedEventTypeString = aEventTypeArg;
+    mEvent->SetComposed(aEventTypeArg);
   }
+  mEvent->SetDefaultComposedInNativeAnonymousContent();
 }
 
 void
 Event::InitEvent(const nsAString& aEventTypeArg,
                  bool aCanBubbleArg,
                  bool aCancelableArg)
 {
   // Make sure this event isn't already being dispatched.
@@ -1162,16 +1166,17 @@ Event::Serialize(IPC::Message* aMsg, boo
 
   nsString type;
   GetType(type);
   IPC::WriteParam(aMsg, type);
 
   IPC::WriteParam(aMsg, Bubbles());
   IPC::WriteParam(aMsg, Cancelable());
   IPC::WriteParam(aMsg, IsTrusted());
+  IPC::WriteParam(aMsg, Composed());
 
   // No timestamp serialization for now!
 }
 
 NS_IMETHODIMP_(bool)
 Event::Deserialize(const IPC::Message* aMsg, PickleIterator* aIter)
 {
   nsString type;
@@ -1181,18 +1186,22 @@ Event::Deserialize(const IPC::Message* a
   NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &bubbles), false);
 
   bool cancelable = false;
   NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &cancelable), false);
 
   bool trusted = false;
   NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &trusted), false);
 
+  bool composed = false;
+  NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &composed), false);
+
   InitEvent(type, bubbles, cancelable);
   SetTrusted(trusted);
+  SetComposed(composed);
 
   return true;
 }
 
 NS_IMETHODIMP_(void)
 Event::SetOwner(mozilla::dom::EventTarget* aOwner)
 {
   mOwner = nullptr;
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -165,16 +165,21 @@ public:
     return mEvent->mFlags.mBubbles;
   }
 
   bool Cancelable() const
   {
     return mEvent->mFlags.mCancelable;
   }
 
+  bool Composed() const
+  {
+    return mEvent->mFlags.mComposed;
+  }
+
   // xpidl implementation
   // void PreventDefault();
 
   // You MUST NOT call PreventDefaultJ(JSContext*) from C++ code.  A call of
   // this method always sets Event.defaultPrevented true for web contents.
   // If default action handler calls this, web applications meet wrong
   // defaultPrevented value.
   virtual void PreventDefault(JSContext* aCx);
@@ -269,16 +274,21 @@ protected:
   }
 
   /**
    * IsChrome() returns true if aCx is chrome context or the event is created
    * in chrome's thread.  Otherwise, false.
    */
   bool IsChrome(JSContext* aCx) const;
 
+  void SetComposed(bool aComposed)
+  {
+    mEvent->SetComposed(aComposed);
+  }
+
   mozilla::WidgetEvent*       mEvent;
   RefPtr<nsPresContext>     mPresContext;
   nsCOMPtr<EventTarget>       mExplicitOriginalTarget;
   nsCOMPtr<nsIGlobalObject>   mOwner;
   bool                        mEventIsInternal;
   bool                        mPrivateDataDuplicated;
   bool                        mIsMainThreadEvent;
   // True when popup control check should rely on event.type, not
--- a/dom/events/EventListenerService.cpp
+++ b/dom/events/EventListenerService.cpp
@@ -215,23 +215,25 @@ EventListenerService::GetListenerInfoFor
     NS_ADDREF((*aOutArray)[i] = listenerInfos[i]);
   }
   *aCount = count;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EventListenerService::GetEventTargetChainFor(nsIDOMEventTarget* aEventTarget,
+                                             bool aComposed,
                                              uint32_t* aCount,
                                              nsIDOMEventTarget*** aOutArray)
 {
   *aCount = 0;
   *aOutArray = nullptr;
   NS_ENSURE_ARG(aEventTarget);
   WidgetEvent event(true, eVoidEvent);
+  event.SetComposed(aComposed);
   nsTArray<EventTarget*> targets;
   nsresult rv = EventDispatcher::Dispatch(aEventTarget, nullptr, &event,
                                           nullptr, nullptr, nullptr, &targets);
   NS_ENSURE_SUCCESS(rv, rv);
   int32_t count = targets.Length();
   if (count == 0) {
     return NS_OK;
   }
--- a/dom/events/FocusEvent.cpp
+++ b/dom/events/FocusEvent.cpp
@@ -60,16 +60,17 @@ FocusEvent::Constructor(const GlobalObje
                         ErrorResult& aRv)
 {
   nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<FocusEvent> e = new FocusEvent(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   e->InitFocusEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
                     aParam.mDetail, aParam.mRelatedTarget);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/dom/events/InputEvent.cpp
+++ b/dom/events/InputEvent.cpp
@@ -51,16 +51,17 @@ InputEvent::Constructor(const GlobalObje
   RefPtr<InputEvent> e = new InputEvent(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   auto* view = aParam.mView ? aParam.mView->AsInner() : nullptr;
   e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, view,
                  aParam.mDetail);
   InternalEditorInputEvent* internalEvent = e->mEvent->AsEditorInputEvent();
   internalEvent->mIsComposing = aParam.mIsComposing;
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/dom/events/MouseEvent.cpp
+++ b/dom/events/MouseEvent.cpp
@@ -182,17 +182,17 @@ MouseEvent::Constructor(const GlobalObje
   bool trusted = e->Init(t);
   e->InitMouseEvent(aType, aParam.mBubbles, aParam.mCancelable,
                     aParam.mView, aParam.mDetail, aParam.mScreenX,
                     aParam.mScreenY, aParam.mClientX, aParam.mClientY,
                     aParam.mCtrlKey, aParam.mAltKey, aParam.mShiftKey,
                     aParam.mMetaKey, aParam.mButton, aParam.mRelatedTarget);
   e->InitializeExtraMouseEventDictionaryMembers(aParam);
   e->SetTrusted(trusted);
-
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 void
 MouseEvent::InitNSMouseEvent(const nsAString& aType,
                              bool aCanBubble,
                              bool aCancelable,
                              nsGlobalWindow* aView,
--- a/dom/events/PointerEvent.cpp
+++ b/dom/events/PointerEvent.cpp
@@ -95,16 +95,17 @@ PointerEvent::Constructor(EventTarget* a
   widgetEvent->pressure = aParam.mPressure;
   widgetEvent->tiltX = aParam.mTiltX;
   widgetEvent->tiltY = aParam.mTiltY;
   widgetEvent->inputSource = ConvertStringToPointerType(aParam.mPointerType);
   widgetEvent->mIsPrimary = aParam.mIsPrimary;
   widgetEvent->buttons = aParam.mButtons;
 
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 // static
 already_AddRefed<PointerEvent>
 PointerEvent::Constructor(const GlobalObject& aGlobal,
                           const nsAString& aType,
                           const PointerEventInit& aParam,
--- a/dom/events/SpeechRecognitionError.cpp
+++ b/dom/events/SpeechRecognitionError.cpp
@@ -26,16 +26,17 @@ SpeechRecognitionError::Constructor(cons
                                     const SpeechRecognitionErrorInit& aParam,
                                     ErrorResult& aRv)
 {
   nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<SpeechRecognitionError> e = new SpeechRecognitionError(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   e->InitSpeechRecognitionError(aType, aParam.mBubbles, aParam.mCancelable, aParam.mError, aParam.mMessage);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 void
 SpeechRecognitionError::InitSpeechRecognitionError(const nsAString& aType,
                                                    bool aCanBubble,
                                                    bool aCancelable,
                                                    SpeechRecognitionErrorCode aError,
--- a/dom/events/StorageEvent.cpp
+++ b/dom/events/StorageEvent.cpp
@@ -60,17 +60,17 @@ StorageEvent::Constructor(EventTarget* a
   bool trusted = e->Init(aOwner);
   e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable);
   e->mKey = aEventInitDict.mKey;
   e->mOldValue = aEventInitDict.mOldValue;
   e->mNewValue = aEventInitDict.mNewValue;
   e->mUrl = aEventInitDict.mUrl;
   e->mStorageArea = aEventInitDict.mStorageArea;
   e->SetTrusted(trusted);
-
+  e->SetComposed(aEventInitDict.mComposed);
   return e.forget();
 }
 
 already_AddRefed<StorageEvent>
 StorageEvent::Constructor(const GlobalObject& aGlobal,
                           const nsAString& aType,
                           const StorageEventInit& aEventInitDict,
                           ErrorResult& aRv)
--- a/dom/events/TouchEvent.cpp
+++ b/dom/events/TouchEvent.cpp
@@ -240,16 +240,17 @@ TouchEvent::Constructor(const GlobalObje
   RefPtr<TouchList> touches = e->CopyTouches(aParam.mTouches);
   RefPtr<TouchList> targetTouches = e->CopyTouches(aParam.mTargetTouches);
   RefPtr<TouchList> changedTouches = e->CopyTouches(aParam.mChangedTouches);
   e->InitTouchEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
                     aParam.mDetail, aParam.mCtrlKey, aParam.mAltKey,
                     aParam.mShiftKey, aParam.mMetaKey, touches, targetTouches,
                     changedTouches);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 
 already_AddRefed<TouchList>
 TouchEvent::CopyTouches(const Sequence<OwningNonNull<Touch>>& aTouches)
 {
   RefPtr<TouchList> list = new TouchList(GetParentObject());
--- a/dom/events/TransitionEvent.cpp
+++ b/dom/events/TransitionEvent.cpp
@@ -47,16 +47,17 @@ TransitionEvent::Constructor(const Globa
   e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
 
   InternalTransitionEvent* internalEvent = e->mEvent->AsTransitionEvent();
   internalEvent->mPropertyName = aParam.mPropertyName;
   internalEvent->mElapsedTime = aParam.mElapsedTime;
   internalEvent->mPseudoElement = aParam.mPseudoElement;
 
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 NS_IMETHODIMP
 TransitionEvent::GetPropertyName(nsAString& aPropertyName)
 {
   aPropertyName = mEvent->AsTransitionEvent()->mPropertyName;
   return NS_OK;
--- a/dom/events/UIEvent.cpp
+++ b/dom/events/UIEvent.cpp
@@ -85,16 +85,17 @@ UIEvent::Constructor(const GlobalObject&
                      ErrorResult& aRv)
 {
   nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<UIEvent> e = new UIEvent(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
                  aParam.mDetail);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent, Event,
                                    mView)
 
 NS_IMPL_ADDREF_INHERITED(UIEvent, Event)
 NS_IMPL_RELEASE_INHERITED(UIEvent, Event)
--- a/dom/events/WheelEvent.cpp
+++ b/dom/events/WheelEvent.cpp
@@ -120,16 +120,17 @@ WheelEvent::Constructor(const GlobalObje
                     aParam.mView, aParam.mDetail,
                     aParam.mScreenX, aParam.mScreenY,
                     aParam.mClientX, aParam.mClientY,
                     aParam.mButton, aParam.mRelatedTarget,
                     EmptyString(), aParam.mDeltaX,
                     aParam.mDeltaY, aParam.mDeltaZ, aParam.mDeltaMode);
   e->InitializeExtraMouseEventDictionaryMembers(aParam);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/dom/events/nsIEventListenerService.idl
+++ b/dom/events/nsIEventListenerService.idl
@@ -73,16 +73,17 @@ interface nsIEventListenerService : nsIS
    * Returns an array of event targets.
    * aEventTarget will be at index 0.
    * The objects are the ones that would be used as DOMEvent.currentTarget while
    * dispatching an event to aEventTarget
    * @note Some events, especially 'load', may actually have a shorter
    *       event target chain than what this methods returns.
    */
   void getEventTargetChainFor(in nsIDOMEventTarget aEventTarget,
+                              in boolean composed,
                               [optional] out unsigned long aCount,
                               [retval, array, size_is(aCount)] out
                                 nsIDOMEventTarget aOutArray);
 
   /**
    * Returns true if a event target has any listener for the given type.
    */
   boolean hasListenersFor(in nsIDOMEventTarget aEventTarget,
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -30,17 +30,16 @@ support-files =
   support-files = pointerevent_lostpointercapture_is_first-manual.html
 [test_pointerevent_multiple_primary_pointers_boundary_events-manual.html]
   support-files = pointerevent_multiple_primary_pointers_boundary_events-manual.html
   disabled = should be investigated
 [test_pointerevent_pointercancel_touch-manual.html]
   support-files = pointerevent_pointercancel_touch-manual.html
 [test_pointerevent_pointerdown-manual.html]
   support-files = pointerevent_pointerdown-manual.html
-  disabled = should be investigated
 [test_pointerevent_pointerenter_does_not_bubble-manual.html]
   support-files = pointerevent_pointerenter_does_not_bubble-manual.html
 [test_pointerevent_pointerenter_nohover-manual.html]
   support-files = pointerevent_pointerenter_nohover-manual.html
 [test_pointerevent_pointerId_scope-manual.html]
   support-files =
     test_pointerevent_pointerId_scope-manual.html
     ./resources/pointerevent_pointerId_scope-iframe.html
@@ -86,17 +85,16 @@ support-files =
 [test_pointerevent_pointertype_mouse-manual.html]
   support-files = pointerevent_pointertype_mouse-manual.html
 [test_pointerevent_pointertype_pen-manual.html]
   support-files = pointerevent_pointertype_pen-manual.html
 [test_pointerevent_pointertype_touch-manual.html]
   support-files = pointerevent_pointertype_touch-manual.html
 [test_pointerevent_pointerup-manual.html]
   support-files = pointerevent_pointerup-manual.html
-  disabled = should be investigated
 [test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html]
   support-files = pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html
 [test_pointerevent_pointerup_pointertype-manual.html]
   support-files = pointerevent_pointerup_pointertype-manual.html
 [test_pointerevent_releasepointercapture_events_to_original_target-manual.html]
   support-files = pointerevent_releasepointercapture_events_to_original_target-manual.html
 [test_pointerevent_releasepointercapture_invalid_pointerid-manual.html]
   support-files = pointerevent_releasepointercapture_invalid_pointerid-manual.html
--- a/dom/events/test/test_bug448602.html
+++ b/dom/events/test/test_bug448602.html
@@ -89,17 +89,17 @@ function runTests() {
   is(infos[0].allowsUntrusted, true, "Should allow untrusted events (3)");
   is(SpecialPowers.unwrap(infos[0].listenerObject), l,
      "Should have the right listener object (4)");
 
   // Event target chain tests
   l2 = document.getElementById("testlevel2");
   l3 = document.getElementById("testlevel3");
   var textnode = l3.firstChild;
-  var chain = els.getEventTargetChainFor(textnode, {});
+  var chain = els.getEventTargetChainFor(textnode, true, {});
   ok(chain.length > 3, "Too short event target chain.");
   ok(SpecialPowers.compare(chain[0], textnode), "Wrong chain item (1)");
   ok(SpecialPowers.compare(chain[1], l3), "Wrong chain item (2)");
   ok(SpecialPowers.compare(chain[2], l2), "Wrong chain item (3)");
   ok(SpecialPowers.compare(chain[3], root), "Wrong chain item (4)");
 
   var hasDocumentInChain = false;
   var hasWindowInChain = false;
@@ -162,24 +162,24 @@ function testAllListener() {
     if (infos[i].type == null) {
       ++nullTypes;
     }
   }
   is(nullTypes, 1, "Should have one all-event-listener!");
 
   els.addListenerForAllEvents(root, allListener, false, true, true);
   els.addListenerForAllEvents(root, allListenerTrustedOnly, false, false, true);
-  l3.dispatchEvent(new Event("testevent", { bubbles: true }));
-  dispatchTrusted(l3, { bubbles: true });
+  l3.dispatchEvent(new Event("testevent", { bubbles: true, composed: true }));
+  dispatchTrusted(l3, { bubbles: true, composed: true });
   els.removeListenerForAllEvents(root, allListener, false);
   els.removeListenerForAllEvents(root, allListener, false, true);
   els.removeListenerForAllEvents(root, allListenerTrustedOnly, false, true);
   // make sure removeListenerForAllEvents works.
-  l3.dispatchEvent(new Event("testevent", { bubbles: true }));
-  dispatchTrusted(l3, { bubbles: true });
+  l3.dispatchEvent(new Event("testevent", { bubbles: true, composed : true }));
+  dispatchTrusted(l3, { bubbles: true, composed: true });
 
   // Test the order of event listeners.
   var clickListenerCalled = false;
   var allListenerCalled = false;
   function clickListener() {
     clickListenerCalled = true;
     ok(allListenerCalled, "Should have called '*' listener before normal listener!");
   }
--- a/dom/media/eme/MediaKeyMessageEvent.cpp
+++ b/dom/media/eme/MediaKeyMessageEvent.cpp
@@ -95,16 +95,17 @@ MediaKeyMessageEvent::Constructor(const 
   }
   e->mMessage = ArrayBuffer::Create(aGlobal.Context(), length, data);
   if (!e->mMessage) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
   e->mMessageType = aEventInitDict.mMessageType;
   e->SetTrusted(trusted);
+  e->SetComposed(aEventInitDict.mComposed);
   return e.forget();
 }
 
 void
 MediaKeyMessageEvent::GetMessage(JSContext* cx,
                                  JS::MutableHandle<JSObject*> aMessage,
                                  ErrorResult& aRv)
 {
--- a/dom/notification/NotificationEvent.h
+++ b/dom/notification/NotificationEvent.h
@@ -38,16 +38,17 @@ public:
               const nsAString& aType,
               const NotificationEventInit& aOptions,
               ErrorResult& aRv)
   {
     RefPtr<NotificationEvent> e = new NotificationEvent(aOwner);
     bool trusted = e->Init(aOwner);
     e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
     e->SetTrusted(trusted);
+    e->SetComposed(aOptions.mComposed);
     e->mNotification = aOptions.mNotification;
     e->SetWantsPopupControlCheck(e->IsTrusted());
     return e.forget();
   }
 
   static already_AddRefed<NotificationEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
--- a/dom/tests/mochitest/webcomponents/test_event_dispatch.html
+++ b/dom/tests/mochitest/webcomponents/test_event_dispatch.html
@@ -22,17 +22,17 @@ function eventListener(e) {
 function isEventChain(actual, expected, msg) {
   is(actual.length, expected.length, msg);
   for (var i = 0; i < expected.length; i++) {
     is(actual[i], expected[i], msg + " at " + i);
   }
 
   // Check to make sure the event chain matches what we get back from nsIEventListenerService.getEventTargetChainFor
   if (0 < actual.length) {
-    var chain = els.getEventTargetChainFor(actual[0]); // Events should be dispatched on actual[0].
+    var chain = els.getEventTargetChainFor(actual[0], true); // Events should be dispatched on actual[0].
     for (var i = 0; i < expected.length; i++) {
       ok(SpecialPowers.compare(chain[i], expected[i]), msg + " at " + i + " for nsIEventListenerService");
     }
   }
 }
 
 /*
  * Test 1: Test of event dispatch through a basic ShadowRoot with content a insertion point.
@@ -59,17 +59,17 @@ elemFour.addEventListener("custom", even
 var shadowOne = elemOne.createShadowRoot();
 shadowOne.addEventListener("custom", eventListener);
 
 elemThree.appendChild(elemFour);
 shadowOne.appendChild(elemThree);
 elemOne.appendChild(elemTwo);
 
 var eventChain = [];
-var customEvent = new CustomEvent("custom", { "bubbles" : true });
+var customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemTwo.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemTwo, elemFour, elemThree, shadowOne, elemOne], "Event path for test 1 for event dispatched on elemTwo.");
 
 /*
  * Test 2: Test of event dispatch through a nested ShadowRoots with content insertion points.
  *
  * <div elemFive> --- <shadow-root shadowTwo>
  *       |                       |
@@ -106,17 +106,17 @@ shadowTwo.addEventListener("custom", eve
 
 elemFive.appendChild(elemOne);
 shadowTwo.appendChild(elemFour);
 elemFour.appendChild(elemTwo);
 shadowOne.appendChild(elemSix);
 elemSix.appendChild(elemThree);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemOne.dispatchEvent(customEvent);
 is(elemOne.getDestinationInsertionPoints().length, 2, "yes");
 isEventChain(eventChain, [elemOne, elemThree, elemSix, shadowOne, elemTwo, elemFour, shadowTwo, elemFive], "Event path for test 2 for event dispatched on elemOne.");
 
 /*
  * Test 3: Test of event dispatch through nested ShadowRoot with content insertion points.
  *
  * <div elemOne> ------- <shadow-root shadowOne>
@@ -154,17 +154,17 @@ shadowTwo.addEventListener("custom", eve
 
 elemOne.appendChild(elemTwo);
 shadowOne.appendChild(elemThree);
 elemThree.appendChild(elemFour);
 elemFour.appendChild(elemFive);
 shadowTwo.appendChild(elemSix);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemTwo.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemTwo, elemFive, elemFour, elemSix, shadowTwo, elemThree, shadowOne, elemOne], "Event path for test 3 for event dispatched on elemTwo.");
 
 /*
  * Test 4: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point.
  *
  * <div elemSeven> --- <shadow-root shadowTwo> (younger ShadowRoot)
  *       |         |             |
@@ -218,22 +218,22 @@ elemSeven.appendChild(elemOne);
 shadowTwo.appendChild(elemSix);
 elemSix.appendChild(elemFour);
 elemFour.appendChild(elemTwo);
 shadowThree.appendChild(elemEight);
 shadowThree.appendChild(elemThree);
 shadowOne.appendChild(elemFive);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemOne.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemOne, elemFive, shadowOne, elemThree, shadowThree, elemTwo, elemFour, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemOne.");
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemEight.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemEight, elemFive, shadowOne, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemEight.");
 
 /*
  * Test 5: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
  *
  * <div elemOne> --------- <shadow-root shadowOne>
  *    |      |                        |
@@ -276,22 +276,22 @@ shadowOne.addEventListener("custom", eve
 elemOne.appendChild(elemTwo);
 elemOne.appendChild(elemThree);
 shadowOne.appendChild(elemFour);
 elemFour.appendChild(elemSix);
 elemFour.appendChild(elemFive);
 shadowTwo.appendChild(elemSeven);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemTwo.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemTwo, elemSeven, shadowTwo, elemSix, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemTwo.");
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemThree.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemThree.");
 
 /*
  * Test 6: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
  *
  * <div elemOne> --------- <shadow-root shadowOne>;
  *    |      |                        |
@@ -334,22 +334,22 @@ shadowOne.addEventListener("custom", eve
 elemOne.appendChild(elemTwo);
 elemOne.appendChild(elemThree);
 shadowOne.appendChild(elemFour);
 elemFour.appendChild(elemFive);
 shadowTwo.appendChild(elemSix);
 shadowTwo.appendChild(elemSeven);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemTwo.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemTwo, elemSix, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemTwo.");
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemThree.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemThree.");
 
 /*
  * Test 7: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
  *
  * <div elemOne> --------- <shadow-root shadowOne>
  *    |      |                        |
@@ -398,22 +398,22 @@ elemOne.appendChild(elemTwo);
 elemOne.appendChild(elemThree);
 shadowOne.appendChild(elemFour);
 elemFour.appendChild(elemFive);
 shadowTwo.appendChild(elemEight);
 elemEight.appendChild(elemSix);
 elemEight.appendChild(elemSeven);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemTwo.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemTwo, elemSix, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemTwo.");
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemThree.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemThree, elemSeven, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemThree.");
 
 /*
  * Test 8: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point.
  *
  * <div elemOne> --- <shadow-root shadowOne> (younger ShadowRoot)
  *               |             |
@@ -444,15 +444,15 @@ shadowTwo.addEventListener("custom", eve
 shadowOne = elemOne.createShadowRoot();
 shadowOne.addEventListener("custom", eventListener);
 
 shadowOne.appendChild(elemFour);
 elemFour.appendChild(elemTwo);
 shadowTwo.appendChild(elemThree);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemThree.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemThree, shadowTwo, elemTwo, elemFour, shadowOne, elemOne], "Event path for test 8 for event dispatched on elemThree.");
 
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_event_stopping.html
+++ b/dom/tests/mochitest/webcomponents/test_event_stopping.html
@@ -21,17 +21,17 @@ function eventListener(e) {
 
 function isEventChain(actual, expected, msg) {
   is(actual.length, expected.length, msg);
   for (var i = 0; i < expected.length; i++) {
     is(actual[i], expected[i], msg + " at " + i);
   }
 
   if (0 < actual.length) {
-    var chain = els.getEventTargetChainFor(actual[0]); // Events should be dispatched on actual[0].
+    var chain = els.getEventTargetChainFor(actual[0], false); // Events should be dispatched on actual[0].
     ok(expected.length < chain.length, "There should be additional chrome event targets.");
   }
 }
 
 /*
  * <div elemOne> ------ <shadow-root shadowOne>
  *                                 |
  *                          <span elemTwo>
--- a/dom/webidl/Event.webidl
+++ b/dom/webidl/Event.webidl
@@ -36,16 +36,18 @@ interface Event {
   readonly attribute boolean cancelable;
   void preventDefault();
   [Pure]
   readonly attribute boolean defaultPrevented;
   [ChromeOnly, Pure]
   readonly attribute boolean defaultPreventedByChrome;
   [ChromeOnly, Pure]
   readonly attribute boolean defaultPreventedByContent;
+  [Pure]
+  readonly attribute boolean composed;
 
   [Unforgeable, Pure]
   readonly attribute boolean isTrusted;
   [Pure]
   readonly attribute DOMHighResTimeStamp timeStamp;
 
   void initEvent(DOMString type, boolean bubbles, boolean cancelable);
 };
@@ -64,9 +66,10 @@ partial interface Event {
   [ChromeOnly] readonly attribute boolean isSynthesized;
 
   boolean getPreventDefault();
 };
 
 dictionary EventInit {
   boolean bubbles = false;
   boolean cancelable = false;
+  boolean composed = false;
 };
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -144,16 +144,17 @@ FetchEvent::Constructor(const GlobalObje
                         ErrorResult& aRv)
 {
   RefPtr<EventTarget> owner = do_QueryObject(aGlobal.GetAsSupports());
   MOZ_ASSERT(owner);
   RefPtr<FetchEvent> e = new FetchEvent(owner);
   bool trusted = e->Init(owner);
   e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
   e->SetTrusted(trusted);
+  e->SetComposed(aOptions.mComposed);
   e->mRequest = aOptions.mRequest;
   e->mClientId = aOptions.mClientId;
   e->mIsReload = aOptions.mIsReload;
   return e.forget();
 }
 
 namespace {
 
@@ -1136,16 +1137,17 @@ PushEvent::Constructor(mozilla::dom::Eve
                        const nsAString& aType,
                        const PushEventInit& aOptions,
                        ErrorResult& aRv)
 {
   RefPtr<PushEvent> e = new PushEvent(aOwner);
   bool trusted = e->Init(aOwner);
   e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
   e->SetTrusted(trusted);
+  e->SetComposed(aOptions.mComposed);
   if(aOptions.mData.WasPassed()){
     nsTArray<uint8_t> bytes;
     nsresult rv = ExtractBytesFromData(aOptions.mData.Value(), bytes);
     if (NS_FAILED(rv)) {
       aRv.Throw(rv);
       return nullptr;
     }
     e->mData = new PushMessageData(aOwner, Move(bytes));
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -72,16 +72,17 @@ public:
   Constructor(mozilla::dom::EventTarget* aOwner,
               const nsAString& aType,
               const EventInit& aOptions)
   {
     RefPtr<ExtendableEvent> e = new ExtendableEvent(aOwner);
     bool trusted = e->Init(aOwner);
     e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
     e->SetTrusted(trusted);
+    e->SetComposed(aOptions.mComposed);
     return e.forget();
   }
 
   static already_AddRefed<ExtendableEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const EventInit& aOptions,
               ErrorResult& aRv)
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,15 +1,16 @@
 gfx/harfbuzz status as of 2016-08-21:
 
 This directory contains the harfbuzz source from the 'master' branch of
 https://github.com/behdad/harfbuzz.
 
 Current version: 1.3.0 + recent fixes from trunk,
-up to 547ddb0721365dca985aef5b759d08718f7c5f82.
+up to 547ddb0721365dca985aef5b759d08718f7c5f82
++ the relevant part of https://github.com/behdad/harfbuzz/pull/334.
 
 
 UPDATING:
 
 Note that gfx/harfbuzz/src/hb-version.h is not present in the upstream Git
 repository. It is created at build time by the harfbuzz build system;
 but as we don't use that build system in mozilla, it is necessary to refresh
 this file when updating harfbuzz, and check it into the mozilla tree.
--- a/gfx/harfbuzz/src/hb-glib.cc
+++ b/gfx/harfbuzz/src/hb-glib.cc
@@ -377,24 +377,26 @@ hb_glib_get_unicode_funcs (void)
       HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_UNICODE_FUNC_IMPLEMENT
     }
   };
 
   return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
 }
 
+#if GLIB_CHECK_VERSION(2,31,10)
 /**
  * hb_glib_blob_create:
  *
  * Since: 0.9.38
  **/
 hb_blob_t *
 hb_glib_blob_create (GBytes *gbytes)
 {
   gsize size = 0;
   gconstpointer data = g_bytes_get_data (gbytes, &size);
   return hb_blob_create ((const char *) data,
 			 size,
 			 HB_MEMORY_MODE_READONLY,
 			 g_bytes_ref (gbytes),
 			 (hb_destroy_func_t) g_bytes_unref);
 }
+#endif
--- a/gfx/harfbuzz/src/hb-glib.h
+++ b/gfx/harfbuzz/src/hb-glib.h
@@ -41,15 +41,16 @@ hb_glib_script_to_script (GUnicodeScript
 
 HB_EXTERN GUnicodeScript
 hb_glib_script_from_script (hb_script_t script);
 
 
 HB_EXTERN hb_unicode_funcs_t *
 hb_glib_get_unicode_funcs (void);
 
+#if GLIB_CHECK_VERSION(2,31,10)
 HB_EXTERN hb_blob_t *
 hb_glib_blob_create (GBytes *gbytes);
-
+#endif
 
 HB_END_DECLS
 
 #endif /* HB_GLIB_H */
--- a/gfx/harfbuzz/src/moz.build
+++ b/gfx/harfbuzz/src/moz.build
@@ -54,16 +54,25 @@ UNIFIED_SOURCES += [
     'hb-ot-tag.cc',
     'hb-set.cc',
     'hb-shape.cc',
     'hb-shaper.cc',
     'hb-unicode.cc',
     'hb-warning.cc',
 ]
 
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+    EXPORTS.harfbuzz += [
+        'hb-glib.h',
+    ]
+    UNIFIED_SOURCES += [
+        'hb-glib.cc',
+    ]
+    CXXFLAGS += CONFIG['GLIB_CFLAGS']
+
 # We allow warnings for third-party code that can be updated from upstream.
 ALLOW_COMPILER_WARNINGS = True
 
 FINAL_LIBRARY = 'gkmedias'
 
 DEFINES['PACKAGE_VERSION'] = '"moz"'
 DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
 DEFINES['HAVE_OT'] = 1
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -649,17 +649,19 @@ RotatedContentBuffer::BeginPaint(Painted
           }
 
           if (!result.mDidSelfCopy) {
             destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
             CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
                          &destDTBuffer, &destDTBufferOnWhite);
             if (!destDTBuffer ||
                 (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
-              gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height)))) << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+              if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) {
+                gfxCriticalNote << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+              }
               return result;
             }
           }
         }
       } else {
         mBufferRect = destBufferRect;
         mBufferRotation = newRotation;
       }
@@ -671,17 +673,19 @@ RotatedContentBuffer::BeginPaint(Painted
       mBufferRotation = IntPoint(0,0);
     }
   } else {
     // The buffer's not big enough, so allocate a new one
     CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
                  &destDTBuffer, &destDTBufferOnWhite);
     if (!destDTBuffer ||
         (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
-      gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height)))) << "Failed 2 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+      if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) {
+        gfxCriticalNote << "Failed 2 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+      }
       return result;
     }
   }
 
   NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
                "If we're resampling, we need to validate the entire buffer");
 
   // If we have no buffered data already, then destBuffer will be a fresh buffer
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1299195.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width; initial-scale=1.0">
+  <title>Test pointer events are dispatched once for touch tap</title>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript">
+    /** Test for Bug 1299195 **/
+    function runTests() {
+      let target0 = document.getElementById("target0");
+      let mouseup_count = 0;
+      let mousedown_count = 0;
+      let pointerup_count = 0;
+      let pointerdown_count = 0;
+
+      target0.addEventListener("mouseup", () => {
+        ++mouseup_count;
+        if (mouseup_count == 2) {
+          is(mousedown_count, 2, "Double tap with touch should fire 2 mousedown events");
+          is(mouseup_count, 2, "Double tap with touch should fire 2 mouseup events");
+          is(pointerdown_count, 2, "Double tap with touch should fire 2 pointerdown events");
+          is(pointerup_count, 2, "Double tap with touch should fire 2 pointerup events");
+          subtestDone();
+        }
+      });
+      target0.addEventListener("mousedown", () => {
+        ++mousedown_count;
+      });
+      target0.addEventListener("pointerup", () => {
+        ++pointerup_count;
+      });
+      target0.addEventListener("pointerdown", () => {
+        ++pointerdown_count;
+      });
+      synthesizeNativeTap(document.getElementById('target0'), 100, 100);
+      synthesizeNativeTap(document.getElementById('target0'), 100, 100);
+    }
+    waitUntilApzStable().then(runTests);
+    </script>
+</head>
+<body>
+  <div id="target0" style="width: 200px; height: 200px; background: green"></div>
+</body>
+</html>
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -24,16 +24,17 @@ support-files =
   helper_scroll_inactive_perspective.html
   helper_scroll_inactive_zindex.html
   helper_bug1280013.html
   helper_tall.html
   helper_drag_scroll.html
   helper_touch_action_complex.html
   helper_tap_fullzoom.html
   helper_bug1162771.html
+  helper_bug1299195.html
 tags = apz
 [test_bug982141.html]
 [test_bug1151663.html]
 [test_bug1277814.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_wheel_scroll.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_wheel_transactions.html]
@@ -58,12 +59,12 @@ skip-if = (toolkit == 'android') # wheel
 [test_group_mouseevents.html]
 skip-if = (toolkit == 'android') # mouse events not supported on mobile
 [test_touch_listeners_impacting_wheel.html]
 skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X
 [test_bug1253683.html]
 skip-if = (os == 'android') || (os == 'b2g') # uses wheel events which are not supported on mobile
 [test_group_zoom.html]
 skip-if = (toolkit != 'android') # only android supports zoom
-[test_bug1285070.html]
+[test_group_pointerevents.html]
 # Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
 # On OS X we don't support touch events at all.
 skip-if = (toolkit == 'windows') || (toolkit == 'cocoa')
rename from gfx/layers/apz/test/mochitest/test_bug1285070.html
rename to gfx/layers/apz/test/mochitest/test_group_pointerevents.html
--- a/gfx/layers/apz/test/mochitest/test_bug1285070.html
+++ b/gfx/layers/apz/test/mochitest/test_group_pointerevents.html
@@ -7,17 +7,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Test for Bug 1285070</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   var subtests = [
-    {'file': 'helper_bug1285070.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]}
+    {'file': 'helper_bug1285070.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]},
+    {'file': 'helper_bug1299195.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]}
   ];
 
   if (isApzEnabled()) {
     SimpleTest.waitForExplicitFinish();
     window.onload = function() {
       runSubtestsSeriallyInFreshWindows(subtests)
       .then(SimpleTest.finish);
     };
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -184,16 +184,22 @@ ClientLayerManager::CreateReadbackLayer(
 {
   RefPtr<ReadbackLayer> layer = new ClientReadbackLayer(this);
   return layer.forget();
 }
 
 void
 ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
 {
+  MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder");
+  if (!mForwarder->IPCOpen()) {
+    gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died.";
+    return;
+  }
+
   mInTransaction = true;
   mTransactionStart = TimeStamp::Now();
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   MOZ_LAYERS_LOG(("[----- BeginTransaction"));
   Log();
 #endif
 
@@ -268,22 +274,16 @@ bool
 ClientLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
                                            void* aCallbackData,
                                            EndTransactionFlags)
 {
   PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal",
     js::ProfileEntry::Category::GRAPHICS);
 
   if (!mForwarder || !mForwarder->IPCOpen()) {
-    gfxCriticalError() << "LayerManager::EndTransaction while IPC is dead.";
-    // Pointless to try to render since the content cannot be sent to the
-    // compositor. We should not get here in the first place but I suspect
-    // This is happening during shutdown, tab-switch or some other scenario
-    // where we already started tearing the resources down but something
-    // triggered painting anyway.
     return false;
   }
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   MOZ_LAYERS_LOG(("  ----- (beginning paint)"));
   Log();
 #endif
   profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_START);
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -397,18 +397,19 @@ public:
 // our layer tree to a parent process.  Record the new layer creation
 // in the current open transaction as a side effect.
 template<typename CreatedMethod> void
 CreateShadowFor(ClientLayer* aLayer,
                 ClientLayerManager* aMgr,
                 CreatedMethod aMethod)
 {
   PLayerChild* shadow = aMgr->AsShadowForwarder()->ConstructShadowFor(aLayer);
-  // XXX error handling
-  MOZ_ASSERT(shadow, "failed to create shadow");
+  if (!shadow) {
+    return;
+  }
 
   aLayer->SetShadow(shadow);
   (aMgr->AsShadowForwarder()->*aMethod)(aLayer);
   aMgr->Hold(aLayer->AsLayer());
 }
 
 #define CREATE_SHADOW(_type)                                       \
   CreateShadowFor(layer, this,                                     \
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1066,17 +1066,16 @@ TextureClient::CreateForDrawing(TextureF
                                 int32_t aMaxTextureSize,
                                 BackendSelector aSelector,
                                 TextureFlags aTextureFlags,
                                 TextureAllocationFlags aAllocFlags)
 {
   gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(aLayersBackend, aSelector);
 
   // also test the validity of aAllocator
-  MOZ_ASSERT(aAllocator && aAllocator->IPCOpen());
   if (!aAllocator || !aAllocator->IPCOpen()) {
     return nullptr;
   }
 
   if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
     return nullptr;
   }
 
@@ -1163,17 +1162,16 @@ TextureClient::CreateForDrawing(TextureF
 already_AddRefed<TextureClient>
 TextureClient::CreateFromSurface(KnowsCompositor* aAllocator,
                                  gfx::SourceSurface* aSurface,
                                  BackendSelector aSelector,
                                  TextureFlags aTextureFlags,
                                  TextureAllocationFlags aAllocFlags)
 {
   // also test the validity of aAllocator
-  MOZ_ASSERT(aAllocator && aAllocator->GetTextureForwarder()->IPCOpen());
   if (!aAllocator || !aAllocator->GetTextureForwarder()->IPCOpen()) {
     return nullptr;
   }
 
   gfx::IntSize size = aSurface->GetSize();
 
   if (!gfx::Factory::AllowedSurfaceSize(size)) {
     return nullptr;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -687,17 +687,19 @@ ShadowLayerForwarder::EndTransaction(Inf
 
     MOZ_LAYERS_LOG(("[LayersForwarder] OpSetLayerAttributes(%p)\n", mutant));
 
     mTxn->AddEdit(OpSetLayerAttributes(nullptr, Shadow(shadow), attrs));
   }
 
   AutoTArray<Edit, 10> cset;
   size_t nCsets = mTxn->mCset.size() + mTxn->mPaints.size();
-  MOZ_ASSERT(nCsets > 0 || mTxn->RotationChanged(), "should have bailed by now");
+  if (nCsets == 0 && !mTxn->RotationChanged()) {
+    return true;
+  }
 
   cset.SetCapacity(nCsets);
   if (!mTxn->mCset.empty()) {
     cset.AppendElements(&mTxn->mCset.front(), mTxn->mCset.size());
   }
   // Paints after non-paint ops, including attribute changes.  See
   // above.
   if (!mTxn->mPaints.empty()) {
--- a/gfx/thebes/gfxFontconfigFonts.cpp
+++ b/gfx/thebes/gfxFontconfigFonts.cpp
@@ -11,16 +11,17 @@
 #include "gfxContext.h"
 #ifdef MOZ_WIDGET_GTK
 #include "gfxPlatformGtk.h"
 #endif
 #include "gfxFontconfigFonts.h"
 #include "gfxFT2FontBase.h"
 #include "gfxFT2Utils.h"
 #include "harfbuzz/hb.h"
+#include "harfbuzz/hb-glib.h"
 #include "harfbuzz/hb-ot.h"
 #include "nsUnicodeProperties.h"
 #include "nsUnicodeScriptCodes.h"
 #include "gfxFontconfigUtils.h"
 #include "gfxUserFontSet.h"
 #include "gfxFontConstants.h"
 #include "nsGkAtoms.h"
 #include "nsILanguageAtomService.h"
@@ -1618,17 +1619,17 @@ gfxPangoFontGroup::FindFontForChar(uint3
     }
 
     // Our MOZ_SCRIPT_* codes may not match the PangoScript enumeration values
     // (if we're using ICU's codes), so convert by mapping through ISO 15924 tag.
     // Note that PangoScript is defined to be compatible with GUnicodeScript:
     // https://developer.gnome.org/pango/stable/pango-Scripts-and-Languages.html#PangoScript
     const hb_tag_t scriptTag = GetScriptTagForCode(aRunScript);
     const PangoScript script =
-      (const PangoScript)g_unicode_script_from_iso15924(scriptTag);
+      (const PangoScript)hb_glib_script_from_script(hb_script_from_iso15924_tag(scriptTag));
 
     // Might be nice to call pango_language_includes_script only once for the
     // run rather than for each character.
     PangoLanguage *scriptLang;
     if ((!basePattern ||
          !pango_language_includes_script(mPangoLanguage, script)) &&
         (scriptLang = pango_script_get_sample_language(script))) {
         fontSet = GetFontSet(scriptLang);
--- a/intl/locale/moz.build
+++ b/intl/locale/moz.build
@@ -27,16 +27,17 @@ XPIDL_MODULE = 'locale'
 EXPORTS += [
     'nsCollation.h',
     'nsCollationCID.h',
     'nsDateTimeFormatCID.h',
     'nsIDateTimeFormat.h',
     'nsILanguageAtomService.h',
     'nsIPlatformCharset.h',
     'nsPosixLocale.h',
+    'nsUConvPropertySearch.h',
     'nsWin32Locale.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsCollation.cpp',
     'nsIDateTimeFormat.cpp',
     'nsLanguageAtomService.cpp',
     'nsLocale.cpp',
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -1042,17 +1042,17 @@ wasm::GenerateInterruptStub(MacroAssembl
                             FloatRegisterSet(uint32_t(0))));
 
     // Save both the APSR and FPSCR in non-volatile registers.
     masm.as_mrs(r4);
     masm.as_vmrs(r5);
     // Save the stack pointer in a non-volatile register.
     masm.mov(sp,r6);
     // Align the stack.
-    masm.ma_and(Imm32(~7), sp, sp);
+    masm.as_bic(sp, sp, Imm8(7));
 
     // Store resumePC into the return PC stack slot.
     masm.loadWasmActivationFromSymbolicAddress(IntArgReg0);
     masm.loadPtr(Address(IntArgReg0, WasmActivation::offsetOfResumePC()), IntArgReg1);
     masm.storePtr(IntArgReg1, Address(r6, 14 * sizeof(uint32_t*)));
 
     // Save all FP registers
     static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too.");
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -886,21 +886,16 @@ BytecodeEmitter::EmitterScope::enterLexi
             return false;
 
         if (!tdzCache->noteTDZCheck(bce, bi.name(), CheckTDZ))
             return false;
     }
 
     updateFrameFixedSlots(bce, bi);
 
-    // Put frame slots in TDZ. Environment slots are poisoned during
-    // environment creation.
-    if (!deadZoneFrameSlotRange(bce, firstFrameSlot, frameSlotEnd()))
-        return false;
-
     // Create and intern the VM scope.
     auto createScope = [kind, bindings, firstFrameSlot](ExclusiveContext* cx,
                                                         HandleScope enclosing)
     {
         return LexicalScope::create(cx, kind, bindings, firstFrameSlot, enclosing);
     };
     if (!internScope(bce, createScope))
         return false;
@@ -910,16 +905,24 @@ BytecodeEmitter::EmitterScope::enterLexi
         if (!bce->emitInternedScopeOp(index(), JSOP_PUSHLEXICALENV))
             return false;
     }
 
     // Lexical scopes need notes to be mapped from a pc.
     if (!appendScopeNote(bce))
         return false;
 
+    // Put frame slots in TDZ. Environment slots are poisoned during
+    // environment creation.
+    //
+    // This must be done after appendScopeNote to be considered in the extent
+    // of the scope.
+    if (!deadZoneFrameSlotRange(bce, firstFrameSlot, frameSlotEnd()))
+        return false;
+
     return checkEnvironmentChainLength(bce);
 }
 
 bool
 BytecodeEmitter::EmitterScope::enterNamedLambda(BytecodeEmitter* bce, FunctionBox* funbox)
 {
     MOZ_ASSERT(this == bce->innermostEmitterScope);
     MOZ_ASSERT(funbox->namedLambdaBindings());
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2836,17 +2836,18 @@ Parser<ParseHandler>::checkFunctionDefin
         handler.setOp(pn, kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA);
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, bool tryAnnexB)
+Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKind kind,
+                                                bool tryAnnexB)
 {
     // When a lazily-parsed function is called, we only fully parse (and emit)
     // that function, not any of its nested children. The initial syntax-only
     // parse recorded the free variables of nested functions and their extents,
     // so we can skip over them after accounting for their free variables.
 
     RootedFunction fun(context, handler.nextLazyInnerFunction());
     MOZ_ASSERT(!fun->isLegacyGenerator());
@@ -2862,22 +2863,31 @@ Parser<FullParseHandler>::skipLazyInnerF
     PropagateTransitiveParseFlags(lazy, pc->sc());
 
     // The position passed to tokenStream.advance() is an offset of the sort
     // returned by userbuf.offset() and expected by userbuf.rawCharPtrAt(),
     // while LazyScript::{begin,end} offsets are relative to the outermost
     // script source.
     Rooted<LazyScript*> lazyOuter(context, handler.lazyOuterFunction());
     uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column();
-    return tokenStream.advance(fun->lazyScript()->end() - userbufBase);
+    if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase))
+        return false;
+
+    if (kind == Statement && fun->isExprBody()) {
+        if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+            return false;
+    }
+
+    return true;
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, bool tryAnnexB)
+Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind,
+                                                  bool tryAnnexB)
 {
     MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing");
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
                                                      TokenKind* ttp)
@@ -2965,17 +2975,17 @@ Parser<ParseHandler>::functionDefinition
     bool tryAnnexB = false;
     if (!checkFunctionDefinition(funName, pn, kind, generatorKind, &tryAnnexB))
         return null();
 
     // When fully parsing a LazyScript, we do not fully reparse its inner
     // functions, which are also lazy. Instead, their free variables and
     // source extents are recorded and may be skipped.
     if (handler.canSkipLazyInnerFunctions()) {
-        if (!skipLazyInnerFunction(pn, tryAnnexB))
+        if (!skipLazyInnerFunction(pn, kind, tryAnnexB))
             return null();
         return pn;
     }
 
     RootedObject proto(context);
     if (generatorKind == StarGenerator) {
         // If we are off the main thread, the generator meta-objects have
         // already been created by js::StartOffThreadParseScript, so cx will not
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1254,17 +1254,17 @@ class Parser final : private JS::AutoGCR
     bool declareFunctionThis();
     Node newInternalDotName(HandlePropertyName name);
     Node newThisName();
     Node newDotGeneratorName();
     bool declareDotGeneratorName();
 
     bool checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind,
                                  GeneratorKind generatorKind, bool* tryAnnexB);
-    bool skipLazyInnerFunction(Node pn, bool tryAnnexB);
+    bool skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, bool tryAnnexB);
     bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
                        InHandling inHandling, FunctionSyntaxKind kind,
                        GeneratorKind generatorKind, bool tryAnnexB,
                        Directives inheritedDirectives, Directives* newDirectives);
     bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, InHandling inHandling,
                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
                                      bool tryAnnexB, Directives inheritedDirectives,
                                      Directives* newDirectives);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/bug-1298809.js
@@ -0,0 +1,6 @@
+function f() {
+  if (0)
+    function g() x;
+  else;
+}
+f();
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2731,16 +2731,30 @@ MBinaryBitwiseInstruction::foldsTo(TempA
         return folded;
 
     return this;
 }
 
 MDefinition*
 MBinaryBitwiseInstruction::foldUnnecessaryBitop()
 {
+    // Fold unsigned shift right operator when the second operand is zero and
+    // the only use is an unsigned modulo. Thus, the expression
+    // |(x >>> 0) % y| becomes |x % y|.
+    if (isUrsh() && hasOneDefUse() && getOperand(1)->isConstant()) {
+        MConstant* constant = getOperand(1)->toConstant();
+        if (constant->type() == MIRType::Int32 && constant->toInt32() == 0) {
+            for (MUseDefIterator use(this); use; use++) {
+                if (use.def()->isMod() && use.def()->toMod()->isUnsigned())
+                    return getOperand(0);
+                break;
+            }
+        }
+    }
+
     if (specialization_ != MIRType::Int32)
         return this;
 
     // Eliminate bitwise operations that are no-ops when used on integer
     // inputs, such as (x | 0).
 
     MDefinition* lhs = getOperand(0);
     MDefinition* rhs = getOperand(1);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7134,33 +7134,61 @@ class MDiv : public MBinaryArithInstruct
         return unsigned_ == other->isUnsigned();
     }
 
     ALLOW_CLONE(MDiv)
 };
 
 class MMod : public MBinaryArithInstruction
 {
+  public:
+    enum class PossiblyUnsigned {
+        NotPossible,
+        LHSPossible,
+        RHSPossible,
+        BothPossible,
+    };
+
+  protected:
     bool unsigned_;
     bool canBeNegativeDividend_;
     bool canBePowerOfTwoDivisor_;
     bool canBeDivideByZero_;
     bool trapOnError_;
 
+    PossiblyUnsigned possiblyUnsigned_;
+
     MMod(MDefinition* left, MDefinition* right, MIRType type)
       : MBinaryArithInstruction(left, right),
         unsigned_(false),
         canBeNegativeDividend_(true),
         canBePowerOfTwoDivisor_(true),
         canBeDivideByZero_(true),
-        trapOnError_(false)
+        trapOnError_(false),
+        possiblyUnsigned_(PossiblyUnsigned::NotPossible)
     {
         if (type != MIRType::Value)
             specialization_ = type;
         setResultType(type);
+
+        if (left->isUrsh() && left->getOperand(1)->isConstant()) {
+            MConstant* constant = left->getOperand(1)->toConstant();
+            if (constant->type() == MIRType::Int32 && constant->toInt32() == 0)
+                possiblyUnsigned_ = PossiblyUnsigned::LHSPossible;
+        }
+
+        if (right->isUrsh() && right->getOperand(1)->isConstant()) {
+            MConstant* constant = right->getOperand(1)->toConstant();
+            if (constant->type() == MIRType::Int32 && constant->toInt32() == 0) {
+                if (possiblyUnsigned_ == PossiblyUnsigned::NotPossible)
+                    possiblyUnsigned_ = PossiblyUnsigned::RHSPossible;
+                else
+                    possiblyUnsigned_ = PossiblyUnsigned::BothPossible;
+            }
+        }
     }
 
   public:
     INSTRUCTION_HEADER(Mod)
     static MMod* New(TempAllocator& alloc, MDefinition* left, MDefinition* right) {
         return new(alloc) MMod(left, right, MIRType::Value);
     }
     static MMod* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1556,23 +1556,50 @@ MMod::computeRange(TempAllocator& alloc)
 {
     if (specialization() != MIRType::Int32 && specialization() != MIRType::Double)
         return;
     Range lhs(getOperand(0));
     Range rhs(getOperand(1));
 
     // If either operand is a NaN, the result is NaN. This also conservatively
     // handles Infinity cases.
-    if (!lhs.hasInt32Bounds() || !rhs.hasInt32Bounds())
+    if ((possiblyUnsigned_ == PossiblyUnsigned::NotPossible &&
+         !lhs.hasInt32Bounds()) || !rhs.hasInt32Bounds()) 
+    {
         return;
+    }
 
     // If RHS can be zero, the result can be NaN.
     if (rhs.lower() <= 0 && rhs.upper() >= 0)
         return;
 
+    // (x >>> 0) % y is an unsigned modulo operation but the lhs' range is not
+    // always >= 0. The lhs range assumes a signed integer 32 bit while the
+    // value is unsigned 32 bit. That breaks the assumption that range >= 0.
+    if (specialization() == MIRType::Int32) {
+        switch (possiblyUnsigned_) {
+          case PossiblyUnsigned::NotPossible:
+            break;
+          case PossiblyUnsigned::LHSPossible:
+            if (rhs.lower() > 0 && !rhs.canHaveFractionalPart())
+                unsigned_ = true;
+            break;
+          case PossiblyUnsigned::RHSPossible:
+            if (lhs.lower() >= 0 && !lhs.canHaveFractionalPart())
+                unsigned_ = true;
+            break;
+          case PossiblyUnsigned::BothPossible:
+            if (lhs.lower() >= 0 && !lhs.canHaveFractionalPart())
+                unsigned_ = true;
+            else if (rhs.lower() > 0 && !rhs.canHaveFractionalPart())
+                unsigned_ = true;
+            break;
+        }
+    }
+
     // If both operands are non-negative integers, we can optimize this to an
     // unsigned mod.
     if (specialization() == MIRType::Int32 && lhs.lower() >= 0 && rhs.lower() > 0 &&
         !lhs.canHaveFractionalPart() && !rhs.canHaveFractionalPart())
     {
         unsigned_ = true;
     }
 
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -1112,17 +1112,17 @@ Imm8::EncodeTwoImms(uint32_t imm)
     imm2 = ((imm >> (32 - imm2shift)) | (imm << imm2shift)) & 0xff;
     MOZ_ASSERT((imm1shift & 0x1) == 0);
     MOZ_ASSERT((imm2shift & 0x1) == 0);
     return TwoImm8mData(datastore::Imm8mData(imm1, imm1shift >> 1),
                         datastore::Imm8mData(imm2, imm2shift >> 1));
 }
 
 ALUOp
-jit::ALUNeg(ALUOp op, Register dest, Imm32* imm, Register* negDest)
+jit::ALUNeg(ALUOp op, Register dest, Register scratch, Imm32* imm, Register* negDest)
 {
     // Find an alternate ALUOp to get the job done, and use a different imm.
     *negDest = dest;
     switch (op) {
       case OpMov:
         *imm = Imm32(~imm->value);
         return OpMvn;
       case OpMvn:
@@ -1144,17 +1144,17 @@ jit::ALUNeg(ALUOp op, Register dest, Imm
         *imm = Imm32(-imm->value);
         return OpCmn;
       case OpCmn:
         *imm = Imm32(-imm->value);
         return OpCmp;
       case OpTst:
         MOZ_ASSERT(dest == InvalidReg);
         *imm = Imm32(~imm->value);
-        *negDest = ScratchRegister;
+        *negDest = scratch;
         return OpBic;
         // orr has orn on thumb2 only.
       default:
         return OpInvalid;
     }
 }
 
 bool
@@ -3416,8 +3416,13 @@ Assembler::GetPoolMaxOffset()
         char* poolMaxOffsetStr = getenv("ASM_POOL_MAX_OFFSET");
         uint32_t poolMaxOffset;
         if (poolMaxOffsetStr && sscanf(poolMaxOffsetStr, "%u", &poolMaxOffset) == 1)
             AsmPoolMaxOffset = poolMaxOffset;
         isSet = true;
     }
     return AsmPoolMaxOffset;
 }
+
+SecondScratchRegisterScope::SecondScratchRegisterScope(MacroAssembler &masm)
+  : AutoRegisterScope(masm, masm.getSecondScratchReg())
+{
+}
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -50,16 +50,21 @@ static constexpr Register ScratchRegiste
 // of code thinks it has exclusive ownership of the scratch register.
 struct ScratchRegisterScope : public AutoRegisterScope
 {
     explicit ScratchRegisterScope(MacroAssembler& masm)
       : AutoRegisterScope(masm, ScratchRegister)
     { }
 };
 
+struct SecondScratchRegisterScope : public AutoRegisterScope
+{
+    explicit SecondScratchRegisterScope(MacroAssembler& masm);
+};
+
 static constexpr Register OsrFrameReg = r3;
 static constexpr Register ArgumentsRectifierReg = r8;
 static constexpr Register CallTempReg0 = r5;
 static constexpr Register CallTempReg1 = r6;
 static constexpr Register CallTempReg2 = r7;
 static constexpr Register CallTempReg3 = r8;
 static constexpr Register CallTempReg4 = r0;
 static constexpr Register CallTempReg5 = r1;
@@ -404,17 +409,17 @@ enum VFPOp {
     OpvAbs  = 0xB << 20 | 0x3 << 6,
     OpvNeg  = 0xB << 20 | 0x1 << 6 | 0x1 << 16,
     OpvSqrt = 0xB << 20 | 0x3 << 6 | 0x1 << 16,
     OpvCmp  = 0xB << 20 | 0x1 << 6 | 0x4 << 16,
     OpvCmpz  = 0xB << 20 | 0x1 << 6 | 0x5 << 16
 };
 
 // Negate the operation, AND negate the immediate that we were passed in.
-ALUOp ALUNeg(ALUOp op, Register dest, Imm32* imm, Register* negDest);
+ALUOp ALUNeg(ALUOp op, Register dest, Register scratch, Imm32* imm, Register* negDest);
 bool can_dbl(ALUOp op);
 bool condsAreSafe(ALUOp op);
 
 // If there is a variant of op that has a dest (think cmp/sub) return that
 // variant of it.
 ALUOp getDestVariant(ALUOp op);
 
 static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -66,17 +66,17 @@ OutOfLineBailout::accept(CodeGeneratorAR
 void
 CodeGeneratorARM::visitTestIAndBranch(LTestIAndBranch* test)
 {
     const LAllocation* opd = test->getOperand(0);
     MBasicBlock* ifTrue = test->ifTrue();
     MBasicBlock* ifFalse = test->ifFalse();
 
     // Test the operand
-    masm.ma_cmp(ToRegister(opd), Imm32(0));
+    masm.as_cmp(ToRegister(opd), Imm8(0));
 
     if (isNextBlock(ifFalse->lir())) {
         jumpToBlock(ifTrue, Assembler::NonZero);
     } else if (isNextBlock(ifTrue->lir())) {
         jumpToBlock(ifFalse, Assembler::Zero);
     } else {
         jumpToBlock(ifFalse, Assembler::Zero);
         jumpToBlock(ifTrue);
@@ -86,39 +86,47 @@ CodeGeneratorARM::visitTestIAndBranch(LT
 void
 CodeGeneratorARM::visitCompare(LCompare* comp)
 {
     Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop());
     const LAllocation* left = comp->getOperand(0);
     const LAllocation* right = comp->getOperand(1);
     const LDefinition* def = comp->getDef(0);
 
-    if (right->isConstant())
-        masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)));
-    else if (right->isRegister())
+    ScratchRegisterScope scratch(masm);
+
+    if (right->isConstant()) {
+        masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)), scratch);
+    } else if (right->isRegister()) {
         masm.ma_cmp(ToRegister(left), ToRegister(right));
-    else
-        masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)));
+    } else {
+        SecondScratchRegisterScope scratch2(masm);
+        masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)), scratch, scratch2);
+    }
     masm.ma_mov(Imm32(0), ToRegister(def));
     masm.ma_mov(Imm32(1), ToRegister(def), cond);
 }
 
 void
 CodeGeneratorARM::visitCompareAndBranch(LCompareAndBranch* comp)
 {
     Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop());
     const LAllocation* left = comp->left();
     const LAllocation* right = comp->right();
 
-    if (right->isConstant())
-        masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)));
-    else if (right->isRegister())
+    ScratchRegisterScope scratch(masm);
+
+    if (right->isConstant()) {
+        masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)), scratch);
+    } else if (right->isRegister()) {
         masm.ma_cmp(ToRegister(left), ToRegister(right));
-    else
-        masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)));
+    } else {
+        SecondScratchRegisterScope scratch2(masm);
+        masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)), scratch, scratch2);
+    }
     emitBranch(cond, comp->ifTrue(), comp->ifFalse());
 }
 
 bool
 CodeGeneratorARM::generateOutOfLineCode()
 {
     if (!CodeGeneratorShared::generateOutOfLineCode())
         return false;
@@ -276,18 +284,20 @@ CodeGeneratorARM::visitSqrtF(LSqrtF* ins
 
 void
 CodeGeneratorARM::visitAddI(LAddI* ins)
 {
     const LAllocation* lhs = ins->getOperand(0);
     const LAllocation* rhs = ins->getOperand(1);
     const LDefinition* dest = ins->getDef(0);
 
+    ScratchRegisterScope scratch(masm);
+
     if (rhs->isConstant())
-        masm.ma_add(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC);
+        masm.ma_add(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch, SetCC);
     else if (rhs->isRegister())
         masm.ma_add(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), SetCC);
     else
         masm.ma_add(ToRegister(lhs), Operand(ToAddress(rhs)), ToRegister(dest), SetCC);
 
     if (ins->snapshot())
         bailoutIf(Assembler::Overflow, ins->snapshot());
 }
@@ -310,18 +320,20 @@ CodeGeneratorARM::visitAddI64(LAddI64* l
 
 void
 CodeGeneratorARM::visitSubI(LSubI* ins)
 {
     const LAllocation* lhs = ins->getOperand(0);
     const LAllocation* rhs = ins->getOperand(1);
     const LDefinition* dest = ins->getDef(0);
 
+    ScratchRegisterScope scratch(masm);
+
     if (rhs->isConstant())
-        masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC);
+        masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch, SetCC);
     else if (rhs->isRegister())
         masm.ma_sub(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), SetCC);
     else
         masm.ma_sub(ToRegister(lhs), Operand(ToAddress(rhs)), ToRegister(dest), SetCC);
 
     if (ins->snapshot())
         bailoutIf(Assembler::Overflow, ins->snapshot());
 }
@@ -353,23 +365,23 @@ CodeGeneratorARM::visitMulI(LMulI* ins)
 
     if (rhs->isConstant()) {
         // Bailout when this condition is met.
         Assembler::Condition c = Assembler::Overflow;
         // Bailout on -0.0
         int32_t constant = ToInt32(rhs);
         if (mul->canBeNegativeZero() && constant <= 0) {
             Assembler::Condition bailoutCond = (constant == 0) ? Assembler::LessThan : Assembler::Equal;
-            masm.ma_cmp(ToRegister(lhs), Imm32(0));
+            masm.as_cmp(ToRegister(lhs), Imm8(0));
             bailoutIf(bailoutCond, ins->snapshot());
         }
         // TODO: move these to ma_mul.
         switch (constant) {
           case -1:
-            masm.ma_rsb(ToRegister(lhs), Imm32(0), ToRegister(dest), SetCC);
+            masm.as_rsb(ToRegister(dest), ToRegister(lhs), Imm8(0), SetCC);
             break;
           case 0:
             masm.ma_mov(Imm32(0), ToRegister(dest));
             return; // Escape overflow check;
           case 1:
             // Nop
             masm.ma_mov(ToRegister(lhs), ToRegister(dest));
             return; // Escape overflow check;
@@ -417,41 +429,44 @@ CodeGeneratorARM::visitMulI(LMulI* ins)
                         masm.as_cmp(ToRegister(lhs), asr(ToRegister(dest), shift));
                         c = Assembler::NotEqual;
                         handled = true;
                     }
                 }
             }
 
             if (!handled) {
+                ScratchRegisterScope scratch(masm);
                 if (mul->canOverflow())
-                    c = masm.ma_check_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), c);
+                    c = masm.ma_check_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch, c);
                 else
-                    masm.ma_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest));
+                    masm.ma_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch);
             }
           }
         }
         // Bailout on overflow.
         if (mul->canOverflow())
             bailoutIf(c, ins->snapshot());
     } else {
         Assembler::Condition c = Assembler::Overflow;
 
-        if (mul->canOverflow())
-            c = masm.ma_check_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), c);
-        else
+        if (mul->canOverflow()) {
+            ScratchRegisterScope scratch(masm);
+            c = masm.ma_check_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), scratch, c);
+        } else {
             masm.ma_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest));
+        }
 
         // Bailout on overflow.
         if (mul->canOverflow())
             bailoutIf(c, ins->snapshot());
 
         if (mul->canBeNegativeZero()) {
             Label done;
-            masm.ma_cmp(ToRegister(dest), Imm32(0));
+            masm.as_cmp(ToRegister(dest), Imm8(0));
             masm.ma_b(&done, Assembler::NotEqual);
 
             // Result is -0 if lhs or rhs is negative.
             masm.ma_cmn(ToRegister(lhs), ToRegister(rhs));
             bailoutIf(Assembler::Signed, ins->snapshot());
 
             masm.bind(&done);
         }
@@ -498,24 +513,26 @@ CodeGeneratorARM::visitMulI64(LMulI64* l
         masm.mul64(ToOperandOrRegister64(rhs), ToRegister64(lhs), temp);
     }
 }
 
 void
 CodeGeneratorARM::divICommon(MDiv* mir, Register lhs, Register rhs, Register output,
                              LSnapshot* snapshot, Label& done)
 {
+    ScratchRegisterScope scratch(masm);
+
     if (mir->canBeNegativeOverflow()) {
         // Handle INT32_MIN / -1;
         // The integer division will give INT32_MIN, but we want -(double)INT32_MIN.
 
         // Sets EQ if lhs == INT32_MIN.
-        masm.ma_cmp(lhs, Imm32(INT32_MIN));
+        masm.ma_cmp(lhs, Imm32(INT32_MIN), scratch);
         // If EQ (LHS == INT32_MIN), sets EQ if rhs == -1.
-        masm.ma_cmp(rhs, Imm32(-1), Assembler::Equal);
+        masm.ma_cmp(rhs, Imm32(-1), scratch, Assembler::Equal);
         if (mir->canTruncateOverflow()) {
             if (mir->trapOnError()) {
                 masm.ma_b(wasm::JumpTarget::IntegerOverflow, Assembler::Equal);
             } else {
                 // (-INT32_MIN)|0 = INT32_MIN
                 Label skip;
                 masm.ma_b(&skip, Assembler::NotEqual);
                 masm.ma_mov(Imm32(INT32_MIN), output);
@@ -525,17 +542,17 @@ CodeGeneratorARM::divICommon(MDiv* mir, 
         } else {
             MOZ_ASSERT(mir->fallible());
             bailoutIf(Assembler::Equal, snapshot);
         }
     }
 
     // Handle divide by zero.
     if (mir->canBeDivideByZero()) {
-        masm.ma_cmp(rhs, Imm32(0));
+        masm.as_cmp(rhs, Imm8(0));
         if (mir->canTruncateInfinities()) {
             if (mir->trapOnError()) {
                 masm.ma_b(wasm::JumpTarget::IntegerDivideByZero, Assembler::Equal);
             } else {
                 // Infinity|0 == 0
                 Label skip;
                 masm.ma_b(&skip, Assembler::NotEqual);
                 masm.ma_mov(Imm32(0), output);
@@ -546,19 +563,19 @@ CodeGeneratorARM::divICommon(MDiv* mir, 
             MOZ_ASSERT(mir->fallible());
             bailoutIf(Assembler::Equal, snapshot);
         }
     }
 
     // Handle negative 0.
     if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) {
         Label nonzero;
-        masm.ma_cmp(lhs, Imm32(0));
+        masm.as_cmp(lhs, Imm8(0));
         masm.ma_b(&nonzero, Assembler::NotEqual);
-        masm.ma_cmp(rhs, Imm32(0));
+        masm.as_cmp(rhs, Imm8(0));
         MOZ_ASSERT(mir->fallible());
         bailoutIf(Assembler::LessThan, snapshot);
         masm.bind(&nonzero);
     }
 }
 
 void
 CodeGeneratorARM::visitDivI(LDivI* ins)
@@ -610,17 +627,17 @@ CodeGeneratorARM::visitSoftDivI(LSoftDiv
     if (gen->compilingAsmJS())
         masm.callWithABI(wasm::SymbolicAddress::aeabi_idivmod);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
 
     // idivmod returns the quotient in r0, and the remainder in r1.
     if (!mir->canTruncateRemainder()) {
         MOZ_ASSERT(mir->fallible());
-        masm.ma_cmp(r1, Imm32(0));
+        masm.as_cmp(r1, Imm8(0));
         bailoutIf(Assembler::NonZero, ins->snapshot());
     }
 
     masm.bind(&done);
 }
 
 void
 CodeGeneratorARM::visitDivPowTwoI(LDivPowTwoI* ins)
@@ -682,18 +699,18 @@ CodeGeneratorARM::modICommon(MMod* mir, 
     // There are three cases: (Y < 0), (Y == 0) and (Y > 0).
     // If (Y < 0), then we compare X with 0, and bail if X == 0.
     // If (Y == 0), then we simply want to bail. Since this does not set the
     // flags necessary for LT to trigger, we don't test X, and take the bailout
     // because the EQ flag is set.
     // If (Y > 0), we don't set EQ, and we don't trigger LT, so we don't take
     // the bailout.
     if (mir->canBeDivideByZero() || mir->canBeNegativeDividend()) {
-        masm.ma_cmp(rhs, Imm32(0));
-        masm.ma_cmp(lhs, Imm32(0), Assembler::LessThan);
+        masm.as_cmp(rhs, Imm8(0));
+        masm.as_cmp(lhs, Imm8(0), Assembler::LessThan);
         if (mir->isTruncated()) {
             if (mir->trapOnError()) {
                 masm.ma_b(wasm::JumpTarget::IntegerDivideByZero, Assembler::Equal);
             } else {
                 // NaN|0 == 0 and (0 % -X)|0 == 0
                 Label skip;
                 masm.ma_b(&skip, Assembler::NotEqual);
                 masm.ma_mov(Imm32(0), output);
@@ -717,28 +734,31 @@ CodeGeneratorARM::visitModI(LModI* ins)
     MMod* mir = ins->mir();
 
     // Save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0.
     masm.ma_mov(lhs, callTemp);
 
     Label done;
     modICommon(mir, lhs, rhs, output, ins->snapshot(), done);
 
-    masm.ma_smod(lhs, rhs, output);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_smod(lhs, rhs, output, scratch);
+    }
 
     // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0.
     if (mir->canBeNegativeDividend()) {
         if (mir->isTruncated()) {
             // -0.0|0 == 0
         } else {
             MOZ_ASSERT(mir->fallible());
             // See if X < 0
-            masm.ma_cmp(output, Imm32(0));
+            masm.as_cmp(output, Imm8(0));
             masm.ma_b(&done, Assembler::NotEqual);
-            masm.ma_cmp(callTemp, Imm32(0));
+            masm.as_cmp(callTemp, Imm8(0));
             bailoutIf(Assembler::Signed, ins->snapshot());
         }
     }
 
     masm.bind(&done);
 }
 
 void
@@ -751,23 +771,27 @@ CodeGeneratorARM::visitSoftModI(LSoftMod
     Register callTemp = ToRegister(ins->callTemp());
     MMod* mir = ins->mir();
     Label done;
 
     // Save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0.
     MOZ_ASSERT(callTemp.code() > r3.code() && callTemp.code() < r12.code());
     masm.ma_mov(lhs, callTemp);
 
+
     // Prevent INT_MIN % -1;
     // The integer division will give INT_MIN, but we want -(double)INT_MIN.
     if (mir->canBeNegativeDividend()) {
-        // Sets EQ if lhs == INT_MIN
-        masm.ma_cmp(lhs, Imm32(INT_MIN));
-        // If EQ (LHS == INT_MIN), sets EQ if rhs == -1
-        masm.ma_cmp(rhs, Imm32(-1), Assembler::Equal);
+        {
+            ScratchRegisterScope scratch(masm);
+            // Sets EQ if lhs == INT_MIN
+            masm.ma_cmp(lhs, Imm32(INT_MIN), scratch);
+            // If EQ (LHS == INT_MIN), sets EQ if rhs == -1
+            masm.ma_cmp(rhs, Imm32(-1), scratch, Assembler::Equal);
+        }
         if (mir->isTruncated()) {
             // (INT_MIN % -1)|0 == 0
             Label skip;
             masm.ma_b(&skip, Assembler::NotEqual);
             masm.ma_mov(Imm32(0), output);
             masm.ma_b(&done);
             masm.bind(&skip);
         } else {
@@ -788,39 +812,43 @@ CodeGeneratorARM::visitSoftModI(LSoftMod
 
     // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0
     if (mir->canBeNegativeDividend()) {
         if (mir->isTruncated()) {
             // -0.0|0 == 0
         } else {
             MOZ_ASSERT(mir->fallible());
             // See if X < 0
-            masm.ma_cmp(r1, Imm32(0));
+            masm.as_cmp(r1, Imm8(0));
             masm.ma_b(&done, Assembler::NotEqual);
-            masm.ma_cmp(callTemp, Imm32(0));
+            masm.as_cmp(callTemp, Imm8(0));
             bailoutIf(Assembler::Signed, ins->snapshot());
         }
     }
     masm.bind(&done);
 }
 
 void
 CodeGeneratorARM::visitModPowTwoI(LModPowTwoI* ins)
 {
     Register in = ToRegister(ins->getOperand(0));
     Register out = ToRegister(ins->getDef(0));
     MMod* mir = ins->mir();
     Label fin;
     // bug 739870, jbramley has a different sequence that may help with speed
     // here.
+
     masm.ma_mov(in, out, SetCC);
     masm.ma_b(&fin, Assembler::Zero);
-    masm.ma_rsb(Imm32(0), out, LeaveCC, Assembler::Signed);
-    masm.ma_and(Imm32((1 << ins->shift()) - 1), out);
-    masm.ma_rsb(Imm32(0), out, SetCC, Assembler::Signed);
+    masm.as_rsb(out, out, Imm8(0), LeaveCC, Assembler::Signed);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_and(Imm32((1 << ins->shift()) - 1), out, scratch);
+    }
+    masm.as_rsb(out, out, Imm8(0), SetCC, Assembler::Signed);
     if (mir->canBeNegativeDividend()) {
         if (!mir->isTruncated()) {
             MOZ_ASSERT(mir->fallible());
             bailoutIf(Assembler::Zero, ins->snapshot());
         } else {
             // -0|0 == 0
         }
     }
@@ -830,17 +858,22 @@ CodeGeneratorARM::visitModPowTwoI(LModPo
 void
 CodeGeneratorARM::visitModMaskI(LModMaskI* ins)
 {
     Register src = ToRegister(ins->getOperand(0));
     Register dest = ToRegister(ins->getDef(0));
     Register tmp1 = ToRegister(ins->getTemp(0));
     Register tmp2 = ToRegister(ins->getTemp(1));
     MMod* mir = ins->mir();
-    masm.ma_mod_mask(src, dest, tmp1, tmp2, ins->shift());
+
+    ScratchRegisterScope scratch(masm);
+    SecondScratchRegisterScope scratch2(masm);
+
+    masm.ma_mod_mask(src, dest, tmp1, tmp2, scratch, scratch2, ins->shift());
+
     if (mir->canBeNegativeDividend()) {
         if (!mir->isTruncated()) {
             MOZ_ASSERT(mir->fallible());
             bailoutIf(Assembler::Zero, ins->snapshot());
         } else {
             // -0|0 == 0
         }
     }
@@ -859,33 +892,36 @@ CodeGeneratorARM::visitBitNotI(LBitNotI*
 }
 
 void
 CodeGeneratorARM::visitBitOpI(LBitOpI* ins)
 {
     const LAllocation* lhs = ins->getOperand(0);
     const LAllocation* rhs = ins->getOperand(1);
     const LDefinition* dest = ins->getDef(0);
+
+    ScratchRegisterScope scratch(masm);
+
     // All of these bitops should be either imm32's, or integer registers.
     switch (ins->bitop()) {
       case JSOP_BITOR:
         if (rhs->isConstant())
-            masm.ma_orr(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest));
+            masm.ma_orr(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest), scratch);
         else
             masm.ma_orr(ToRegister(rhs), ToRegister(lhs), ToRegister(dest));
         break;
       case JSOP_BITXOR:
         if (rhs->isConstant())
-            masm.ma_eor(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest));
+            masm.ma_eor(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest), scratch);
         else
             masm.ma_eor(ToRegister(rhs), ToRegister(lhs), ToRegister(dest));
         break;
       case JSOP_BITAND:
         if (rhs->isConstant())
-            masm.ma_and(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest));
+            masm.ma_and(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest), scratch);
         else
             masm.ma_and(ToRegister(rhs), ToRegister(lhs), ToRegister(dest));
         break;
       default:
         MOZ_CRASH("unexpected binary opcode");
     }
 }
 
@@ -913,42 +949,42 @@ CodeGeneratorARM::visitShiftI(LShiftI* i
             break;
           case JSOP_URSH:
             if (shift) {
                 masm.ma_lsr(Imm32(shift), lhs, dest);
             } else {
                 // x >>> 0 can overflow.
                 masm.ma_mov(lhs, dest);
                 if (ins->mir()->toUrsh()->fallible()) {
-                    masm.ma_cmp(dest, Imm32(0));
+                    masm.as_cmp(dest, Imm8(0));
                     bailoutIf(Assembler::LessThan, ins->snapshot());
                 }
             }
             break;
           default:
             MOZ_CRASH("Unexpected shift op");
         }
     } else {
         // The shift amounts should be AND'ed into the 0-31 range since arm
         // shifts by the lower byte of the register (it will attempt to shift by
         // 250 if you ask it to).
-        masm.ma_and(Imm32(0x1F), ToRegister(rhs), dest);
+        masm.as_and(dest, ToRegister(rhs), Imm8(0x1F));
 
         switch (ins->bitop()) {
           case JSOP_LSH:
             masm.ma_lsl(dest, lhs, dest);
             break;
           case JSOP_RSH:
             masm.ma_asr(dest, lhs, dest);
             break;
           case JSOP_URSH:
             masm.ma_lsr(dest, lhs, dest);
             if (ins->mir()->toUrsh()->fallible()) {
                 // x >>> 0 can overflow.
-                masm.ma_cmp(dest, Imm32(0));
+                masm.as_cmp(dest, Imm8(0));
                 bailoutIf(Assembler::LessThan, ins->snapshot());
             }
             break;
           default:
             MOZ_CRASH("Unexpected shift op");
         }
     }
 }
@@ -964,17 +1000,17 @@ CodeGeneratorARM::visitUrshD(LUrshD* ins
 
     if (rhs->isConstant()) {
         int32_t shift = ToInt32(rhs) & 0x1F;
         if (shift)
             masm.ma_lsr(Imm32(shift), lhs, temp);
         else
             masm.ma_mov(lhs, temp);
     } else {
-        masm.ma_and(Imm32(0x1F), ToRegister(rhs), temp);
+        masm.as_and(temp, ToRegister(rhs), Imm8(0x1F));
         masm.ma_lsr(temp, lhs, temp);
     }
 
     masm.convertUInt32ToDouble(temp, out);
 }
 
 void
 CodeGeneratorARM::visitClzI(LClzI* ins)
@@ -1114,20 +1150,22 @@ CodeGeneratorARM::emitTableSwitchDispatc
     // current instruction *PLUS 8*. This means that ldr foo, [pc, +0] reads
     // $pc+8. In other words, there is an empty word after the branch into the
     // switch table before the table actually starts. Since the only other
     // unhandled case is the default case (both out of range high and out of
     // range low) I then insert a branch to default case into the extra slot,
     // which ensures we don't attempt to execute the address table.
     Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
 
+    ScratchRegisterScope scratch(masm);
+
     int32_t cases = mir->numCases();
     // Lower value with low value.
-    masm.ma_sub(index, Imm32(mir->low()), index, SetCC);
-    masm.ma_rsb(index, Imm32(cases - 1), index, SetCC, Assembler::NotSigned);
+    masm.ma_sub(index, Imm32(mir->low()), index, scratch, SetCC);
+    masm.ma_rsb(index, Imm32(cases - 1), index, scratch, SetCC, Assembler::NotSigned);
     // Inhibit pools within the following sequence because we are indexing into
     // a pc relative table. The region will have one instruction for ma_ldr, one
     // for ma_b, and each table case takes one word.
     AutoForbidPools afp(&masm, 1 + 1 + cases);
     masm.ma_ldr(DTRAddr(pc, DtrRegImmShift(index, LSL, 2)), pc, Offset, Assembler::NotSigned);
     masm.ma_b(defaultcase);
 
     // To fill in the CodeLabels for the case entries, we need to first generate
@@ -1257,21 +1295,22 @@ CodeGeneratorARM::visitRoundF(LRoundF* l
     masm.roundf(input, output, &bail, tmp);
     bailoutFrom(&bail, lir->snapshot());
 }
 
 void
 CodeGeneratorARM::emitRoundDouble(FloatRegister src, Register dest, Label* fail)
 {
     ScratchDoubleScope scratch(masm);
+    ScratchRegisterScope scratchReg(masm);
 
     masm.ma_vcvt_F64_I32(src, scratch);
     masm.ma_vxfer(scratch, dest);
-    masm.ma_cmp(dest, Imm32(0x7fffffff));
-    masm.ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
+    masm.ma_cmp(dest, Imm32(0x7fffffff), scratchReg);
+    masm.ma_cmp(dest, Imm32(0x80000000), scratchReg, Assembler::NotEqual);
     masm.ma_b(fail, Assembler::Equal);
 }
 
 void
 CodeGeneratorARM::visitTruncateDToInt32(LTruncateDToInt32* ins)
 {
     emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()), ins->mir());
 }
@@ -1375,18 +1414,20 @@ CodeGeneratorARM::visitBoxFloatingPoint(
 void
 CodeGeneratorARM::visitUnbox(LUnbox* unbox)
 {
     // Note that for unbox, the type and payload indexes are switched on the
     // inputs.
     MUnbox* mir = unbox->mir();
     Register type = ToRegister(unbox->type());
 
+    ScratchRegisterScope scratch(masm);
+
     if (mir->fallible()) {
-        masm.ma_cmp(type, Imm32(MIRTypeToTag(mir->type())));
+        masm.ma_cmp(type, Imm32(MIRTypeToTag(mir->type())), scratch);
         bailoutIf(Assembler::NotEqual, unbox->snapshot());
     }
 }
 
 void
 CodeGeneratorARM::visitDouble(LDouble* ins)
 {
     const LDefinition* out = ins->getDef(0);
@@ -1578,18 +1619,19 @@ CodeGeneratorARM::visitCompareBitwiseAnd
     jumpToBlock(notEqual, Assembler::NotEqual);
     masm.cmp32(lhs.payloadReg(), rhs.payloadReg());
     emitBranch(cond, lir->ifTrue(), lir->ifFalse());
 }
 
 void
 CodeGeneratorARM::visitBitAndAndBranch(LBitAndAndBranch* baab)
 {
+    ScratchRegisterScope scratch(masm);
     if (baab->right()->isConstant())
-        masm.ma_tst(ToRegister(baab->left()), Imm32(ToInt32(baab->right())));
+        masm.ma_tst(ToRegister(baab->left()), Imm32(ToInt32(baab->right())), scratch);
     else
         masm.ma_tst(ToRegister(baab->left()), ToRegister(baab->right()));
     emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse());
 }
 
 void
 CodeGeneratorARM::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir)
 {
@@ -1601,28 +1643,28 @@ CodeGeneratorARM::visitAsmJSUInt32ToFloa
 {
     masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
 }
 
 void
 CodeGeneratorARM::visitNotI(LNotI* ins)
 {
     // It is hard to optimize !x, so just do it the basic way for now.
-    masm.ma_cmp(ToRegister(ins->input()), Imm32(0));
+    masm.as_cmp(ToRegister(ins->input()), Imm8(0));
     masm.emitSet(Assembler::Equal, ToRegister(ins->output()));
 }
 
 void
 CodeGeneratorARM::visitNotI64(LNotI64* lir)
 {
     Register64 input = ToRegister64(lir->getInt64Operand(0));
     Register output = ToRegister(lir->output());
 
     masm.ma_orr(input.low, input.high, output);
-    masm.ma_cmp(output, Imm32(0));
+    masm.as_cmp(output, Imm8(0));
     masm.emitSet(Assembler::Equal, output);
 }
 
 void
 CodeGeneratorARM::visitNotD(LNotD* ins)
 {
     // Since this operation is not, we want to set a bit if the double is
     // falsey, which means 0.0, -0.0 or NaN. When comparing with 0, an input of
@@ -1635,17 +1677,17 @@ CodeGeneratorARM::visitNotD(LNotD* ins)
     // TODO There are three variations here to compare performance-wise.
     bool nocond = true;
     if (nocond) {
         // Load the value into the dest register.
         masm.as_vmrs(dest);
         masm.ma_lsr(Imm32(28), dest, dest);
         // 28 + 2 = 30
         masm.ma_alu(dest, lsr(dest, 2), dest, OpOrr);
-        masm.ma_and(Imm32(1), dest);
+        masm.as_and(dest, dest, Imm8(1));
     } else {
         masm.as_vmrs(pc);
         masm.ma_mov(Imm32(0), dest);
         masm.ma_mov(Imm32(1), dest, Assembler::Equal);
         masm.ma_mov(Imm32(1), dest, Assembler::Overflow);
     }
 }
 
@@ -1663,60 +1705,64 @@ CodeGeneratorARM::visitNotF(LNotF* ins)
     // TODO There are three variations here to compare performance-wise.
     bool nocond = true;
     if (nocond) {
         // Load the value into the dest register.
         masm.as_vmrs(dest);
         masm.ma_lsr(Imm32(28), dest, dest);
         // 28 + 2 = 30
         masm.ma_alu(dest, lsr(dest, 2), dest, OpOrr);
-        masm.ma_and(Imm32(1), dest);
+        masm.as_and(dest, dest, Imm8(1));
     } else {
         masm.as_vmrs(pc);
         masm.ma_mov(Imm32(0), dest);
         masm.ma_mov(Imm32(1), dest, Assembler::Equal);
         masm.ma_mov(Imm32(1), dest, Assembler::Overflow);
     }
 }
 
 void
 CodeGeneratorARM::visitGuardShape(LGuardShape* guard)
 {
     Register obj = ToRegister(guard->input());
     Register tmp = ToRegister(guard->tempInt());
 
+    ScratchRegisterScope scratch(masm);
     masm.ma_ldr(DTRAddr(obj, DtrOffImm(ShapedObject::offsetOfShape())), tmp);
-    masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->shape()));
+    masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->shape()), scratch);
 
     bailoutIf(Assembler::NotEqual, guard->snapshot());
 }
 
 void
 CodeGeneratorARM::visitGuardObjectGroup(LGuardObjectGroup* guard)
 {
     Register obj = ToRegister(guard->input());
     Register tmp = ToRegister(guard->tempInt());
     MOZ_ASSERT(obj != tmp);
 
+    ScratchRegisterScope scratch(masm);
     masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfGroup())), tmp);
-    masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->group()));
+    masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->group()), scratch);
 
     Assembler::Condition cond =
         guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
     bailoutIf(cond, guard->snapshot());
 }
 
 void
 CodeGeneratorARM::visitGuardClass(LGuardClass* guard)
 {
     Register obj = ToRegister(guard->input());
     Register tmp = ToRegister(guard->tempInt());
 
+    ScratchRegisterScope scratch(masm);
+
     masm.loadObjClass(obj, tmp);
-    masm.ma_cmp(tmp, Imm32((uint32_t)guard->mir()->getClass()));
+    masm.ma_cmp(tmp, Imm32((uint32_t)guard->mir()->getClass()), scratch);
     bailoutIf(Assembler::NotEqual, guard->snapshot());
 }
 
 void
 CodeGeneratorARM::generateInvalidateEpilogue()
 {
     // Ensure that there is enough space in the buffer for the OsiPoint patching
     // to occur. Otherwise, we could overwrite the invalidation epilogue.
@@ -2125,17 +2171,17 @@ CodeGeneratorARM::visitAtomicTypedArrayE
 }
 
 void
 CodeGeneratorARM::visitAsmSelect(LAsmSelect* ins)
 {
     MIRType mirType = ins->mir()->type();
 
     Register cond = ToRegister(ins->condExpr());
-    masm.ma_cmp(cond, Imm32(0));
+    masm.as_cmp(cond, Imm8(0));
 
     if (mirType == MIRType::Int32) {
         Register falseExpr = ToRegister(ins->falseExpr());
         Register out = ToRegister(ins->output());
         MOZ_ASSERT(ToRegister(ins->trueExpr()) == out, "true expr input is reused for output");
         masm.ma_mov(falseExpr, out, LeaveCC, Assembler::Zero);
         return;
     }
@@ -2258,57 +2304,60 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAs
       default: MOZ_CRASH("unexpected array type");
     }
 
     if (ptr->isConstant()) {
         MOZ_ASSERT(!mir->needsBoundsCheck());
         int32_t ptrImm = ptr->toConstant()->toInt32();
         MOZ_ASSERT(ptrImm >= 0);
         if (isFloat) {
+            ScratchRegisterScope scratch(masm);
             VFPRegister vd(ToFloatRegister(ins->output()));
             if (size == 32)
-                masm.ma_vldr(Address(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always);
+                masm.ma_vldr(Address(HeapReg, ptrImm), vd.singleOverlay(), scratch, Assembler::Always);
             else
-                masm.ma_vldr(Address(HeapReg, ptrImm), vd, Assembler::Always);
+                masm.ma_vldr(Address(HeapReg, ptrImm), vd, scratch, Assembler::Always);
         }  else {
+            ScratchRegisterScope scratch(masm);
             masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm),
-                                  ToRegister(ins->output()), Offset, Assembler::Always);
+                                  ToRegister(ins->output()), scratch, Offset, Assembler::Always);
         }
     } else {
+        ScratchRegisterScope scratch(masm);
         Register ptrReg = ToRegister(ptr);
         if (isFloat) {
             FloatRegister output = ToFloatRegister(ins->output());
             if (size == 32)
                 output = output.singleOverlay();
 
             Assembler::Condition cond = Assembler::Always;
             if (mir->needsBoundsCheck()) {
                 BufferOffset cmp = masm.as_cmp(ptrReg, Imm8(0));
                 masm.append(wasm::BoundsCheck(cmp.getOffset()));
 
                 size_t nanOffset = size == 32 ? wasm::NaN32GlobalDataOffset : wasm::NaN64GlobalDataOffset;
-                masm.ma_vldr(Address(GlobalReg, nanOffset - AsmJSGlobalRegBias), output,
+                masm.ma_vldr(Address(GlobalReg, nanOffset - AsmJSGlobalRegBias), output, scratch,
                              Assembler::AboveOrEqual);
                 cond = Assembler::Below;
             }
 
-            masm.ma_vldr(output, HeapReg, ptrReg, 0, cond);
+            masm.ma_vldr(output, HeapReg, ptrReg, scratch, 0, cond);
         } else {
             Register output = ToRegister(ins->output());
 
             Assembler::Condition cond = Assembler::Always;
             if (mir->needsBoundsCheck()) {
                 uint32_t cmpOffset = masm.as_cmp(ptrReg, Imm8(0)).getOffset();
                 masm.append(wasm::BoundsCheck(cmpOffset));
 
                 masm.ma_mov(Imm32(0), output, Assembler::AboveOrEqual);
                 cond = Assembler::Below;
             }
 
-            masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, output, Offset, cond);
+            masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, output, scratch, Offset, cond);
         }
     }
 }
 
 template <typename T>
 void
 CodeGeneratorARM::emitWasmLoad(T* lir)
 {
@@ -2317,52 +2366,53 @@ CodeGeneratorARM::emitWasmLoad(T* lir)
     uint32_t offset = mir->offset();
     MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptr());
     Scalar::Type type = mir->accessType();
 
     // Maybe add the offset.
     if (offset || type == Scalar::Int64) {
+        ScratchRegisterScope scratch(masm);
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
         if (offset)
-            masm.ma_add(Imm32(offset), ptrPlusOffset);
+            masm.ma_add(Imm32(offset), ptrPlusOffset, scratch);
         ptr = ptrPlusOffset;
     } else {
         MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
     }
 
     bool isSigned = type == Scalar::Int8 || type == Scalar::Int16 || type == Scalar::Int32 ||
                     type == Scalar::Int64;
     unsigned byteSize = mir->byteSize();
 
     memoryBarrier(mir->barrierBefore());
 
     if (mir->type() == MIRType::Int64) {
         Register64 output = ToOutRegister64(lir);
         if (type == Scalar::Int64) {
             MOZ_ASSERT(INT64LOW_OFFSET == 0);
             masm.ma_dataTransferN(IsLoad, 32, /* signed = */ false, HeapReg, ptr, output.low);
-            masm.ma_add(Imm32(INT64HIGH_OFFSET), ptr);
+            masm.as_add(ptr, ptr, Imm8(INT64HIGH_OFFSET));
             masm.ma_dataTransferN(IsLoad, 32, isSigned, HeapReg, ptr, output.high);
         } else {
             masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.low);
             if (isSigned)
                 masm.ma_asr(Imm32(31), output.low, output.high);
             else
                 masm.ma_mov(Imm32(0), output.high);
         }
     } else {
         AnyRegister output = ToAnyRegister(lir->output());
         bool isFloat = output.isFloat();
         if (isFloat) {
             MOZ_ASSERT((byteSize == 4) == output.fpu().isSingle());
             ScratchRegisterScope scratch(masm);
             masm.ma_add(HeapReg, ptr, scratch);
-            masm.ma_vldr(Address(scratch, 0), output.fpu());
+            masm.ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), output.fpu());
         } else {
             masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.gpr());
         }
     }
 
     memoryBarrier(mir->barrierAfter());
 }
 
@@ -2383,18 +2433,20 @@ void
 CodeGeneratorARM::emitWasmUnalignedLoad(T* lir)
 {
     const MWasmLoad* mir = lir->mir();
 
     uint32_t offset = mir->offset();
     MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptrCopy());
-    if (offset)
-        masm.ma_add(Imm32(offset), ptr);
+    if (offset) {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_add(Imm32(offset), ptr, scratch);
+    }
 
     // Add HeapReg to ptr, so we can use base+index addressing in the byte loads.
     masm.ma_add(HeapReg, ptr);
 
     unsigned byteSize = mir->byteSize();
     Scalar::Type type = mir->accessType();
     bool isSigned = type == Scalar::Int8 || type == Scalar::Int16 || type == Scalar::Int32 ||
                     type == Scalar::Int64;
@@ -2462,17 +2514,18 @@ CodeGeneratorARM::visitWasmUnalignedLoad
 
 void
 CodeGeneratorARM::visitWasmAddOffset(LWasmAddOffset* lir)
 {
     MWasmAddOffset* mir = lir->mir();
     Register base = ToRegister(lir->base());
     Register out = ToRegister(lir->output());
 
-    masm.ma_add(base, Imm32(mir->offset()), out, SetCC);
+    ScratchRegisterScope scratch(masm);
+    masm.ma_add(base, Imm32(mir->offset()), out, scratch, SetCC);
     masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::CarrySet);
 }
 
 template <typename T>
 void
 CodeGeneratorARM::emitWasmStore(T* lir)
 {
     const MWasmStore* mir = lir->mir();
@@ -2481,41 +2534,42 @@ CodeGeneratorARM::emitWasmStore(T* lir)
     MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptr());
     unsigned byteSize = mir->byteSize();
     Scalar::Type type = mir->accessType();
 
     // Maybe add the offset.
     if (offset || type == Scalar::Int64) {
+        ScratchRegisterScope scratch(masm);
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
         if (offset)
-            masm.ma_add(Imm32(offset), ptrPlusOffset);
+            masm.ma_add(Imm32(offset), ptrPlusOffset, scratch);
         ptr = ptrPlusOffset;
     } else {
         MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
     }
 
     memoryBarrier(mir->barrierBefore());
 
     if (type == Scalar::Int64) {
         MOZ_ASSERT(INT64LOW_OFFSET == 0);
 
         Register64 value = ToRegister64(lir->getInt64Operand(lir->ValueIndex));
         masm.ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ false, HeapReg, ptr, value.low);
-        masm.ma_add(Imm32(INT64HIGH_OFFSET), ptr);
+        masm.as_add(ptr, ptr, Imm8(INT64HIGH_OFFSET));
         masm.ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ true, HeapReg, ptr, value.high);
     } else {
         AnyRegister value = ToAnyRegister(lir->getOperand(lir->ValueIndex));
         if (value.isFloat()) {
+            ScratchRegisterScope scratch(masm);
             FloatRegister val = value.fpu();
             MOZ_ASSERT((byteSize == 4) == val.isSingle());
-            ScratchRegisterScope scratch(masm);
             masm.ma_add(HeapReg, ptr, scratch);
-            masm.ma_vstr(val, Address(scratch, 0));
+            masm.ma_vstr(val, Operand(Address(scratch, 0)).toVFPAddr());
         } else {
             bool isSigned = type == Scalar::Uint32 || type == Scalar::Int32; // see AsmJSStoreHeap;
             Register val = value.gpr();
             masm.ma_dataTransferN(IsStore, 8 * byteSize /* bits */, isSigned, HeapReg, ptr, val);
         }
     }
 
     memoryBarrier(mir->barrierAfter());
@@ -2538,18 +2592,20 @@ void
 CodeGeneratorARM::emitWasmUnalignedStore(T* lir)
 {
     const MWasmStore* mir = lir->mir();
 
     uint32_t offset = mir->offset();
     MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptrCopy());
-    if (offset)
-        masm.ma_add(Imm32(offset), ptr);
+    if (offset) {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_add(Imm32(offset), ptr, scratch);
+    }
 
     // Add HeapReg to ptr, so we can use base+index addressing in the byte loads.
     masm.ma_add(HeapReg, ptr);
 
     MIRType mirType = mir->value()->type();
 
     memoryBarrier(mir->barrierAfter());
 
@@ -2625,39 +2681,42 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->value()));
             Address addr(HeapReg, ptrImm);
             if (size == 32)
                 masm.storeFloat32(vd, addr);
             else
                 masm.storeDouble(vd, addr);
         } else {
+            ScratchRegisterScope scratch(masm);
             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
-                                  ToRegister(ins->value()), Offset, Assembler::Always);
+                                  ToRegister(ins->value()), scratch, Offset, Assembler::Always);
         }
     } else {
         Register ptrReg = ToRegister(ptr);
 
         Assembler::Condition cond = Assembler::Always;
         if (mir->needsBoundsCheck()) {
             BufferOffset cmp = masm.as_cmp(ptrReg, Imm8(0));
             masm.append(wasm::BoundsCheck(cmp.getOffset()));
 
             cond = Assembler::Below;
         }
 
         if (isFloat) {
+            ScratchRegisterScope scratch(masm);
             FloatRegister value = ToFloatRegister(ins->value());
             if (size == 32)
                 value = value.singleOverlay();
 
-            masm.ma_vstr(value, HeapReg, ptrReg, 0, 0, Assembler::Below);
+            masm.ma_vstr(value, HeapReg, ptrReg, scratch, 0, Assembler::Below);
         } else {
+            ScratchRegisterScope scratch(masm);
             Register value = ToRegister(ins->value());
-            masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, value, Offset, cond);
+            masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, value, scratch, Offset, cond);
         }
     }
 }
 
 void
 CodeGeneratorARM::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
 {
     MAsmJSCompareExchangeHeap* mir = ins->mir();
@@ -2836,23 +2895,27 @@ CodeGeneratorARM::visitAsmJSAtomicBinopC
     }
 }
 
 void
 CodeGeneratorARM::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins)
 {
     const MAsmJSPassStackArg* mir = ins->mir();
     Address dst(StackPointer, mir->spOffset());
+    ScratchRegisterScope scratch(masm);
+    SecondScratchRegisterScope scratch2(masm);
+
     if (ins->arg()->isConstant()) {
-        masm.ma_storeImm(Imm32(ToInt32(ins->arg())), dst);
+        masm.ma_mov(Imm32(ToInt32(ins->arg())), scratch);
+        masm.ma_str(scratch, dst, scratch2);
     } else {
         if (ins->arg()->isGeneralReg())
-            masm.ma_str(ToRegister(ins->arg()), dst);
+            masm.ma_str(ToRegister(ins->arg()), dst, scratch);
         else
-            masm.ma_vstr(ToFloatRegister(ins->arg()), dst);
+            masm.ma_vstr(ToFloatRegister(ins->arg()), dst, scratch);
     }
 }
 
 void
 CodeGeneratorARM::visitUDiv(LUDiv* ins)
 {
     Register lhs = ToRegister(ins->lhs());
     Register rhs = ToRegister(ins->rhs());
@@ -2861,17 +2924,17 @@ CodeGeneratorARM::visitUDiv(LUDiv* ins)
     Label done;
     generateUDivModZeroCheck(rhs, output, &done, ins->snapshot(), ins->mir());
 
     masm.ma_udiv(lhs, rhs, output);
 
     // Check for large unsigned result - represent as double.
     if (!ins->mir()->isTruncated()) {
         MOZ_ASSERT(ins->mir()->fallible());
-        masm.ma_cmp(output, Imm32(0));
+        masm.as_cmp(output, Imm8(0));
         bailoutIf(Assembler::LessThan, ins->snapshot());
     }
 
     // Check for non-zero remainder if not truncating to int.
     if (!ins->mir()->canTruncateRemainder()) {
         MOZ_ASSERT(ins->mir()->fallible());
         {
             ScratchRegisterScope scratch(masm);
@@ -2890,38 +2953,41 @@ CodeGeneratorARM::visitUMod(LUMod* ins)
 {
     Register lhs = ToRegister(ins->lhs());
     Register rhs = ToRegister(ins->rhs());
     Register output = ToRegister(ins->output());
 
     Label done;
     generateUDivModZeroCheck(rhs, output, &done, ins->snapshot(), ins->mir());
 
-    masm.ma_umod(lhs, rhs, output);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_umod(lhs, rhs, output, scratch);
+    }
 
     // Check for large unsigned result - represent as double.
     if (!ins->mir()->isTruncated()) {
         MOZ_ASSERT(ins->mir()->fallible());
-        masm.ma_cmp(output, Imm32(0));
+        masm.as_cmp(output, Imm8(0));
         bailoutIf(Assembler::LessThan, ins->snapshot());
     }
 
     if (done.used())
         masm.bind(&done);
 }
 
 template<class T>
 void
 CodeGeneratorARM::generateUDivModZeroCheck(Register rhs, Register output, Label* done,
                                            LSnapshot* snapshot, T* mir)
 {
     if (!mir)
         return;
     if (mir->canBeDivideByZero()) {
-        masm.ma_cmp(rhs, Imm32(0));
+        masm.as_cmp(rhs, Imm8(0));
         if (mir->isTruncated()) {
             if (mir->trapOnError()) {
                 masm.ma_b(wasm::JumpTarget::IntegerDivideByZero, Assembler::Equal);
             } else {
                 Label skip;
                 masm.ma_b(&skip, Assembler::NotEqual);
                 // Infinity|0 == 0
                 masm.ma_mov(Imm32(0), output);
@@ -2962,98 +3028,108 @@ CodeGeneratorARM::visitSoftUDivOrMod(LSo
     if (gen->compilingAsmJS())
         masm.callWithABI(wasm::SymbolicAddress::aeabi_uidivmod);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_uidivmod));
 
     // uidivmod returns the quotient in r0, and the remainder in r1.
     if (div && !div->canTruncateRemainder()) {
         MOZ_ASSERT(div->fallible());
-        masm.ma_cmp(r1, Imm32(0));
+        masm.as_cmp(r1, Imm8(0));
         bailoutIf(Assembler::NonZero, ins->snapshot());
     }
 
     // Bailout for big unsigned results
     if ((div && !div->isTruncated()) || (mod && !mod->isTruncated())) {
         DebugOnly<bool> isFallible = (div && div->fallible()) || (mod && mod->fallible());
         MOZ_ASSERT(isFallible);
-        masm.ma_cmp(output, Imm32(0));
+        masm.as_cmp(output, Imm8(0));
         bailoutIf(Assembler::LessThan, ins->snapshot());
     }
 
     masm.bind(&done);
 }
 
 void
 CodeGeneratorARM::visitEffectiveAddress(LEffectiveAddress* ins)
 {
     const MEffectiveAddress* mir = ins->mir();
     Register base = ToRegister(ins->base());
     Register index = ToRegister(ins->index());
     Register output = ToRegister(ins->output());
+
+    ScratchRegisterScope scratch(masm);
+
     masm.as_add(output, base, lsl(index, mir->scale()));
-    masm.ma_add(Imm32(mir->displacement()), output);
+    masm.ma_add(Imm32(mir->displacement()), output, scratch);
 }
 
 void
 CodeGeneratorARM::visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins)
 {
     const MWasmLoadGlobalVar* mir = ins->mir();
     unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
+
+    ScratchRegisterScope scratch(masm);
+
     if (mir->type() == MIRType::Int32) {
-        masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output()));
+        masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output()), scratch);
     } else if (mir->type() == MIRType::Float32) {
         VFPRegister vd(ToFloatRegister(ins->output()));
-        masm.ma_vldr(Address(GlobalReg, addr), vd.singleOverlay());
+        masm.ma_vldr(Address(GlobalReg, addr), vd.singleOverlay(), scratch);
     } else {
         MOZ_ASSERT(mir->type() == MIRType::Double);
-        masm.ma_vldr(Address(GlobalReg, addr), ToFloatRegister(ins->output()));
+        masm.ma_vldr(Address(GlobalReg, addr), ToFloatRegister(ins->output()), scratch);
     }
 }
 
 void
 CodeGeneratorARM::visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins)
 {
     const MWasmLoadGlobalVar* mir = ins->mir();
     unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
     MOZ_ASSERT(mir->type() == MIRType::Int64);
     Register64 output = ToOutRegister64(ins);
 
-    masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64LOW_OFFSET), output.low);
-    masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), output.high);
+    ScratchRegisterScope scratch(masm);
+    masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64LOW_OFFSET), output.low, scratch);
+    masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), output.high, scratch);
 }
 
 void
 CodeGeneratorARM::visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins)
 {
     const MWasmStoreGlobalVar* mir = ins->mir();
     MIRType type = mir->value()->type();
 
+    ScratchRegisterScope scratch(masm);
+
     unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
     if (type == MIRType::Int32) {
-        masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value()));
+        masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value()), scratch);
     } else if (type == MIRType::Float32) {
         VFPRegister vd(ToFloatRegister(ins->value()));
-        masm.ma_vstr(vd.singleOverlay(), Address(GlobalReg, addr));
+        masm.ma_vstr(vd.singleOverlay(), Address(GlobalReg, addr), scratch);
     } else {
         MOZ_ASSERT(type == MIRType::Double);
-        masm.ma_vstr(ToFloatRegister(ins->value()), Address(GlobalReg, addr));
+        masm.ma_vstr(ToFloatRegister(ins->value()), Address(GlobalReg, addr), scratch);
     }
 }
 
 void
 CodeGeneratorARM::visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins)
 {
     const MWasmStoreGlobalVar* mir = ins->mir();
     unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
     MOZ_ASSERT (mir->value()->type() == MIRType::Int64);
     Register64 input = ToRegister64(ins->value());
 
-    masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64LOW_OFFSET), input.low);
-    masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), input.high);
+    ScratchRegisterScope scratch(masm);
+    masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64LOW_OFFSET), input.low, scratch);
+    masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), input.high, scratch);
 }
 
 void
 CodeGeneratorARM::visitNegI(LNegI* ins)
 {
     Register input = ToRegister(ins->input());
     masm.ma_neg(input, ToRegister(ins->output()));
 }
@@ -3123,52 +3199,53 @@ CodeGeneratorARM::visitWasmTruncateToInt
             masm.compareFloat(input, input);
         else
             MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
 
         masm.ma_b(ool->entry(), Assembler::VFP_Unordered);
     }
 
     ScratchDoubleScope scratchScope(masm);
+    ScratchRegisterScope scratchReg(masm);
     FloatRegister scratch = scratchScope.uintOverlay();
 
     // ARM conversion instructions clamp the value to ensure it fits within the
     // target's type bounds, so every time we see those, we need to check the
     // input.
     if (mir->isUnsigned()) {
         if (fromType == MIRType::Double)
             masm.ma_vcvt_F64_U32(input, scratch);
         else if (fromType == MIRType::Float32)
             masm.ma_vcvt_F32_U32(input, scratch);
         else
             MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
 
         masm.ma_vxfer(scratch, output);
 
         // int32_t(UINT32_MAX) == -1.
-        masm.ma_cmp(output, Imm32(-1));
-        masm.ma_cmp(output, Imm32(0), Assembler::NotEqual);
+        masm.ma_cmp(output, Imm32(-1), scratchReg);
+        masm.as_cmp(output, Imm8(0), Assembler::NotEqual);
         masm.ma_b(ool->entry(), Assembler::Equal);
 
         masm.bind(ool->rejoin());
         return;
     }
 
     scratch = scratchScope.sintOverlay();
 
     if (fromType == MIRType::Double)
         masm.ma_vcvt_F64_I32(input, scratch);
     else if (fromType == MIRType::Float32)
         masm.ma_vcvt_F32_I32(input, scratch);
     else
         MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
 
     masm.ma_vxfer(scratch, output);
-    masm.ma_cmp(output, Imm32(INT32_MAX));
-    masm.ma_cmp(output, Imm32(INT32_MIN), Assembler::NotEqual);
+    masm.ma_cmp(output, Imm32(INT32_MAX), scratchReg);
+    masm.ma_cmp(output, Imm32(INT32_MIN), scratchReg, Assembler::NotEqual);
     masm.ma_b(ool->entry(), Assembler::Equal);
 
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGeneratorARM::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
 {
@@ -3194,18 +3271,19 @@ CodeGeneratorARM::visitWasmTruncateToInt
     masm.passABIArg(inputDouble, MoveOp::DOUBLE);
     if (lir->mir()->isUnsigned())
         masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToUint64);
     else
         masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToInt64);
 
     masm.Pop(input);
 
-    masm.ma_cmp(output.high, Imm32(0x80000000));
-    masm.ma_cmp(output.low, Imm32(0x00000000), Assembler::Equal);
+    ScratchRegisterScope scratch(masm);
+    masm.ma_cmp(output.high, Imm32(0x80000000), scratch);
+    masm.as_cmp(output.low, Imm8(0x00000000), Assembler::Equal);
     masm.ma_b(ool->entry(), Assembler::Equal);
 
     masm.bind(ool->rejoin());
 
     MOZ_ASSERT(ReturnReg64 == output);
 }
 
 void
@@ -3331,21 +3409,23 @@ CodeGeneratorARM::visitCopySignF(LCopySi
     FloatRegister output = ToFloatRegister(ins->getDef(0));
 
     Register lhsi = ToRegister(ins->getTemp(0));
     Register rhsi = ToRegister(ins->getTemp(1));
 
     masm.ma_vxfer(lhs, lhsi);
     masm.ma_vxfer(rhs, rhsi);
 
+    ScratchRegisterScope scratch(masm);
+
     // Clear lhs's sign.
-    masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi);
+    masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi, scratch);
 
     // Keep rhs's sign.
-    masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi);
+    masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi, scratch);
 
     // Combine.
     masm.ma_orr(lhsi, rhsi, rhsi);
 
     masm.ma_vxfer(rhsi, output);
 }
 
 void
@@ -3357,21 +3437,23 @@ CodeGeneratorARM::visitCopySignD(LCopySi
 
     Register lhsi = ToRegister(ins->getTemp(0));
     Register rhsi = ToRegister(ins->getTemp(1));
 
     // Manipulate high words of double inputs.
     masm.as_vxfer(lhsi, InvalidReg, lhs, Assembler::FloatToCore, Assembler::Always, 1);
     masm.as_vxfer(rhsi, InvalidReg, rhs, Assembler::FloatToCore, Assembler::Always, 1);
 
+    ScratchRegisterScope scratch(masm);
+
     // Clear lhs's sign.
-    masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi);
+    masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi, scratch);
 
     // Keep rhs's sign.
-    masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi);
+    masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi, scratch);
 
     // Combine.
     masm.ma_orr(lhsi, rhsi, rhsi);
 
     // Reconstruct the output.
     masm.as_vxfer(lhsi, InvalidReg, lhs, Assembler::FloatToCore, Assembler::Always, 0);
     masm.ma_vxfer(lhsi, rhsi, output);
 }
@@ -3675,23 +3757,24 @@ void
 CodeGeneratorARM::visitAsmSelectI64(LAsmSelectI64* lir)
 {
     Register cond = ToRegister(lir->condExpr());
     const LInt64Allocation falseExpr = lir->falseExpr();
 
     Register64 out = ToOutRegister64(lir);
     MOZ_ASSERT(ToRegister64(lir->trueExpr()) == out, "true expr is reused for input");
 
-    masm.ma_cmp(cond, Imm32(0));
+    masm.as_cmp(cond, Imm8(0));
     if (falseExpr.low().isRegister()) {
         masm.ma_mov(ToRegister(falseExpr.low()), out.low, LeaveCC, Assembler::Equal);
         masm.ma_mov(ToRegister(falseExpr.high()), out.high, LeaveCC, Assembler::Equal);
     } else {
-        masm.ma_ldr(ToAddress(falseExpr.low()), out.low, Offset, Assembler::Equal);
-        masm.ma_ldr(ToAddress(falseExpr.high()), out.high, Offset, Assembler::Equal);
+        ScratchRegisterScope scratch(masm);
+        masm.ma_ldr(ToAddress(falseExpr.low()), out.low, scratch, Offset, Assembler::Equal);
+        masm.ma_ldr(ToAddress(falseExpr.high()), out.high, scratch, Offset, Assembler::Equal);
     }
 }
 
 void
 CodeGeneratorARM::visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir)
 {
     MOZ_ASSERT(lir->mir()->type() == MIRType::Double);
     MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64);
@@ -3742,13 +3825,13 @@ CodeGeneratorARM::visitCtzI64(LCtzI64* l
     masm.move32(Imm32(0), output.high);
 }
 
 void
 CodeGeneratorARM::visitTestI64AndBranch(LTestI64AndBranch* lir)
 {
     Register64 input = ToRegister64(lir->getInt64Operand(0));
 
-    masm.ma_cmp(input.high, Imm32(0));
+    masm.as_cmp(input.high, Imm8(0));
     jumpToBlock(lir->ifTrue(), Assembler::NonZero);
-    masm.ma_cmp(input.low, Imm32(0));
+    masm.as_cmp(input.low, Imm8(0));
     emitBranch(Assembler::NonZero, lir->ifTrue(), lir->ifFalse());
 }
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -65,46 +65,52 @@ void
 MacroAssembler::and32(Register src, Register dest)
 {
     ma_and(src, dest, SetCC);
 }
 
 void
 MacroAssembler::and32(Imm32 imm, Register dest)
 {
-    ma_and(imm, dest, SetCC);
+    ScratchRegisterScope scratch(*this);
+    ma_and(imm, dest, scratch, SetCC);
 }
 
 void
 MacroAssembler::and32(Imm32 imm, const Address& dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(dest, scratch);
-    ma_and(imm, scratch);
-    store32(scratch, dest);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(dest, scratch, scratch2);
+    ma_and(imm, scratch, scratch2);
+    ma_str(scratch, dest, scratch2);
 }
 
 void
 MacroAssembler::and32(const Address& src, Register dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(src, scratch);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(src, scratch, scratch2);
     ma_and(scratch, dest, SetCC);
 }
 
 void
 MacroAssembler::andPtr(Register src, Register dest)
 {
     ma_and(src, dest);
 }
 
 void
 MacroAssembler::andPtr(Imm32 imm, Register dest)
 {
-    ma_and(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_and(imm, dest, scratch);
 }
 
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
     if (imm.low().value != int32_t(0xFFFFFFFF))
         and32(imm.low(), dest.low);
     if (imm.hi().value != int32_t(0xFFFFFFFF))
@@ -133,38 +139,42 @@ void
 MacroAssembler::or32(Register src, Register dest)
 {
     ma_orr(src, dest);
 }
 
 void
 MacroAssembler::or32(Imm32 imm, Register dest)
 {
-    ma_orr(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_orr(imm, dest, scratch);
 }
 
 void
 MacroAssembler::or32(Imm32 imm, const Address& dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(dest, scratch);
-    ma_orr(imm, scratch);
-    store32(scratch, dest);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(dest, scratch, scratch2);
+    ma_orr(imm, scratch, scratch2);
+    ma_str(scratch, dest, scratch2);
 }
 
 void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     ma_orr(src, dest);
 }
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
-    ma_orr(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_orr(imm, dest, scratch);
 }
 
 void
 MacroAssembler::and64(Register64 src, Register64 dest)
 {
     and32(src.low, dest.low);
     and32(src.high, dest.high);
 }
@@ -187,109 +197,121 @@ void
 MacroAssembler::xor32(Register src, Register dest)
 {
     ma_eor(src, dest, SetCC);
 }
 
 void
 MacroAssembler::xor32(Imm32 imm, Register dest)
 {
-    ma_eor(imm, dest, SetCC);
+    ScratchRegisterScope scratch(*this);
+    ma_eor(imm, dest, scratch, SetCC);
 }
 
 void
 MacroAssembler::xorPtr(Register src, Register dest)
 {
     ma_eor(src, dest);
 }
 
 void
 MacroAssembler::xorPtr(Imm32 imm, Register dest)
 {
-    ma_eor(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_eor(imm, dest, scratch);
 }
 
 // ===============================================================
 // Arithmetic functions
 
 void
 MacroAssembler::add32(Register src, Register dest)
 {
     ma_add(src, dest, SetCC);
 }
 
 void
 MacroAssembler::add32(Imm32 imm, Register dest)
 {
-    ma_add(imm, dest, SetCC);
+    ScratchRegisterScope scratch(*this);
+    ma_add(imm, dest, scratch, SetCC);
 }
 
 void
 MacroAssembler::add32(Imm32 imm, const Address& dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(dest, scratch);
-    ma_add(imm, scratch, SetCC);
-    store32(scratch, dest);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(dest, scratch, scratch2);
+    ma_add(imm, scratch, scratch2, SetCC);
+    ma_str(scratch, dest, scratch2);
 }
 
 void
 MacroAssembler::addPtr(Register src, Register dest)
 {
     ma_add(src, dest);
 }
 
 void
 MacroAssembler::addPtr(Imm32 imm, Register dest)
 {
-    ma_add(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_add(imm, dest, scratch);
 }
 
 void
 MacroAssembler::addPtr(ImmWord imm, Register dest)
 {
     addPtr(Imm32(imm.value), dest);
 }
 
 void
 MacroAssembler::addPtr(Imm32 imm, const Address& dest)
 {
     ScratchRegisterScope scratch(*this);
-    loadPtr(dest, scratch);
-    addPtr(imm, scratch);
-    storePtr(scratch, dest);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(dest, scratch, scratch2);
+    ma_add(imm, scratch, scratch2);
+    ma_str(scratch, dest, scratch2);
 }
 
 void
 MacroAssembler::addPtr(const Address& src, Register dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(src, scratch);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(src, scratch, scratch2);
     ma_add(scratch, dest, SetCC);
 }
 
 void
 MacroAssembler::add64(Register64 src, Register64 dest)
 {
     ma_add(src.low, dest.low, SetCC);
     ma_adc(src.high, dest.high);
 }
 
 void
 MacroAssembler::add64(Imm32 imm, Register64 dest)
 {
-    ma_add(imm, dest.low, SetCC);
-    ma_adc(Imm32(0), dest.high, LeaveCC);
+    ScratchRegisterScope scratch(*this);
+    ma_add(imm, dest.low, scratch, SetCC);
+    as_adc(dest.high, dest.high, Imm8(0), LeaveCC);
 }
 
 void
 MacroAssembler::add64(Imm64 imm, Register64 dest)
 {
-    ma_add(imm.low(), dest.low, SetCC);
-    ma_adc(imm.hi(), dest.high, LeaveCC);
+    ScratchRegisterScope scratch(*this);
+    ma_add(imm.low(), dest.low, scratch, SetCC);
+    ma_adc(imm.hi(), dest.high, scratch, LeaveCC);
 }
 
 void
 MacroAssembler::addDouble(FloatRegister src, FloatRegister dest)
 {
     ma_vadd(dest, src, dest);
 }
 
@@ -303,68 +325,77 @@ void
 MacroAssembler::sub32(Register src, Register dest)
 {
     ma_sub(src, dest, SetCC);
 }
 
 void
 MacroAssembler::sub32(Imm32 imm, Register dest)
 {
-    ma_sub(imm, dest, SetCC);
+    ScratchRegisterScope scratch(*this);
+    ma_sub(imm, dest, scratch, SetCC);
 }
 
 void
 MacroAssembler::sub32(const Address& src, Register dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(src, scratch);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(src, scratch, scratch2);
     ma_sub(scratch, dest, SetCC);
 }
 
 void
 MacroAssembler::subPtr(Register src, Register dest)
 {
     ma_sub(src, dest);
 }
 
 void
 MacroAssembler::subPtr(Register src, const Address& dest)
 {
     ScratchRegisterScope scratch(*this);
-    loadPtr(dest, scratch);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(dest, scratch, scratch2);
     ma_sub(src, scratch);
-    storePtr(scratch, dest);
+    ma_str(scratch, dest, scratch2);
 }
 
 void
 MacroAssembler::subPtr(Imm32 imm, Register dest)
 {
-    ma_sub(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_sub(imm, dest, scratch);
 }
 
 void
 MacroAssembler::subPtr(const Address& addr, Register dest)
 {
     ScratchRegisterScope scratch(*this);
-    loadPtr(addr, scratch);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(addr, scratch, scratch2);
     ma_sub(scratch, dest);
 }
 
 void
 MacroAssembler::sub64(Register64 src, Register64 dest)
 {
     ma_sub(src.low, dest.low, SetCC);
     ma_sbc(src.high, dest.high, LeaveCC);
 }
 
 void
 MacroAssembler::sub64(Imm64 imm, Register64 dest)
 {
-    ma_sub(imm.low(), dest.low, SetCC);
-    ma_sbc(imm.hi(), dest.high, LeaveCC);
+    ScratchRegisterScope scratch(*this);
+    ma_sub(imm.low(), dest.low, scratch, SetCC);
+    ma_sbc(imm.hi(), dest.high, scratch, LeaveCC);
 }
 
 void
 MacroAssembler::subDouble(FloatRegister src, FloatRegister dest)
 {
     ma_vsub(dest, src, dest);
 }
 
@@ -383,52 +414,56 @@ MacroAssembler::mul32(Register rhs, Regi
 void
 MacroAssembler::mul64(Imm64 imm, const Register64& dest)
 {
     // LOW32  = LOW(LOW(dest) * LOW(imm));
     // HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
     //        + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
     //        + HIGH(LOW(dest) * LOW(imm)) [carry]
 
+    ScratchRegisterScope scratch(*this);
+    SecondScratchRegisterScope scratch2(*this);
+
     // HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
-    ma_mov(Imm32(imm.value & 0xFFFFFFFFL), ScratchRegister);
-    as_mul(dest.high, dest.high, ScratchRegister);
+    ma_mov(Imm32(imm.value & 0xFFFFFFFFL), scratch);
+    as_mul(dest.high, dest.high, scratch);
 
     // high:low = LOW(dest) * LOW(imm);
-    as_umull(secondScratchReg_, ScratchRegister, dest.low, ScratchRegister);
+    as_umull(scratch2, scratch, dest.low, scratch);
 
     // HIGH(dest) += high;
-    as_add(dest.high, dest.high, O2Reg(secondScratchReg_));
+    as_add(dest.high, dest.high, O2Reg(scratch2));
 
     // HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
     if (((imm.value >> 32) & 0xFFFFFFFFL) == 5)
-        as_add(secondScratchReg_, dest.low, lsl(dest.low, 2));
+        as_add(scratch2, dest.low, lsl(dest.low, 2));
     else
         MOZ_CRASH("Not supported imm");
-    as_add(dest.high, dest.high, O2Reg(secondScratchReg_));
+    as_add(dest.high, dest.high, O2Reg(scratch2));
 
     // LOW(dest) = low;
-    ma_mov(ScratchRegister, dest.low);
+    ma_mov(scratch, dest.low);
 }
 
 void
 MacroAssembler::mul64(Imm64 imm, const Register64& dest, const Register temp)
 {
     // LOW32  = LOW(LOW(dest) * LOW(src));                                  (1)
     // HIGH32 = LOW(HIGH(dest) * LOW(src)) [multiply src into upper bits]   (2)
     //        + LOW(LOW(dest) * HIGH(src)) [multiply dest into upper bits]  (3)
     //        + HIGH(LOW(dest) * LOW(src)) [carry]                          (4)
 
     MOZ_ASSERT(temp != dest.high && temp != dest.low);
 
     // Compute mul64
-    ma_mul(dest.high, imm.low(), dest.high); // (2)
-    ma_mul(dest.low, imm.hi(), temp); // (3)
+    ScratchRegisterScope scratch(*this);
+    ma_mul(dest.high, imm.low(), dest.high, scratch); // (2)
+    ma_mul(dest.low, imm.hi(), temp, scratch); // (3)
     ma_add(dest.high, temp, temp);
-    ma_umull(dest.low, imm.low(), dest.high, dest.low); // (4) + (1)
+    ma_umull(dest.low, imm.low(), dest.high, dest.low, scratch); // (4) + (1)
     ma_add(temp, dest.high, dest.high);
 }
 
 void
 MacroAssembler::mul64(const Register64& src, const Register64& dest, const Register temp)
 {
     // LOW32  = LOW(LOW(dest) * LOW(src));                                  (1)
     // HIGH32 = LOW(HIGH(dest) * LOW(src)) [multiply src into upper bits]   (2)
@@ -462,39 +497,44 @@ void
 MacroAssembler::mulDouble(FloatRegister src, FloatRegister dest)
 {
     ma_vmul(dest, src, dest);
 }
 
 void
 MacroAssembler::mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest)
 {
-    movePtr(imm, ScratchRegister);
-    loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
-    mulDouble(ScratchDoubleReg, dest);
+    ScratchRegisterScope scratch(*this);
+    ScratchDoubleScope scratchDouble(*this);
+
+    movePtr(imm, scratch);
+    ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble);
+    mulDouble(scratchDouble, dest);
 }
 
 void
 MacroAssembler::quotient32(Register rhs, Register srcDest, bool isUnsigned)
 {
     MOZ_ASSERT(HasIDIV());
     if (isUnsigned)
         ma_udiv(srcDest, rhs, srcDest);
     else
         ma_sdiv(srcDest, rhs, srcDest);
 }
 
 void
 MacroAssembler::remainder32(Register rhs, Register srcDest, bool isUnsigned)
 {
     MOZ_ASSERT(HasIDIV());
+
+    ScratchRegisterScope scratch(*this);
     if (isUnsigned)
-        ma_umod(srcDest, rhs, srcDest);
+        ma_umod(srcDest, rhs, srcDest, scratch);
     else
-        ma_smod(srcDest, rhs, srcDest);
+        ma_smod(srcDest, rhs, srcDest, scratch);
 }
 
 void
 MacroAssembler::divFloat32(FloatRegister src, FloatRegister dest)
 {
     ma_vdiv_f32(dest, src, dest);
 }
 
@@ -509,34 +549,34 @@ MacroAssembler::inc64(AbsoluteAddress de
 {
     ScratchRegisterScope scratch(*this);
 
     ma_strd(r0, r1, EDtrAddr(sp, EDtrOffImm(-8)), PreIndex);
 
     ma_mov(Imm32((int32_t)dest.addr), scratch);
     ma_ldrd(EDtrAddr(scratch, EDtrOffImm(0)), r0, r1);
 
-    ma_add(Imm32(1), r0, SetCC);
-    ma_adc(Imm32(0), r1, LeaveCC);
+    as_add(r0, r0, Imm8(1), SetCC);
+    as_adc(r1, r1, Imm8(0), LeaveCC);
 
     ma_strd(r0, r1, EDtrAddr(scratch, EDtrOffImm(0)));
     ma_ldrd(EDtrAddr(sp, EDtrOffImm(8)), r0, r1, PostIndex);
 }
 
 void
 MacroAssembler::neg32(Register reg)
 {
     ma_neg(reg, reg, SetCC);
 }
 
 void
 MacroAssembler::neg64(Register64 reg)
 {
-    ma_rsb(Imm32(0), reg.low, SetCC);
-    ma_rsc(Imm32(0), reg.high);
+    as_rsb(reg.low, reg.low, Imm8(0), SetCC);
+    as_rsc(reg.high, reg.high, Imm8(0));
 }
 
 void
 MacroAssembler::negateDouble(FloatRegister reg)
 {
     ma_vneg(reg, reg);
 }
 
@@ -626,23 +666,23 @@ MacroAssembler::lshift64(Imm32 imm, Regi
 
 void
 MacroAssembler::lshift64(Register unmaskedShift, Register64 dest)
 {
     // dest.high = dest.high << shift | dest.low << shift - 32 | dest.low >> 32 - shift
     // Note: one of the two dest.low shift will always yield zero due to negative shift.
 
     ScratchRegisterScope shift(*this);
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.high, lsl(dest.high, shift));
-    ma_sub(shift, Imm32(32), shift);
+    as_sub(shift, shift, Imm8(32));
     as_orr(dest.high, dest.high, lsl(dest.low, shift));
     ma_neg(shift, shift);
     as_orr(dest.high, dest.high, lsr(dest.low, shift));
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.low, lsl(dest.low, shift));
 }
 
 void
 MacroAssembler::lshift32(Register src, Register dest)
 {
     ma_lsl(src, dest, dest);
 }
@@ -705,29 +745,29 @@ MacroAssembler::rshift64Arithmetic(Regis
     Label proceed;
 
     // dest.low = dest.low >>> shift | dest.high <<< 32 - shift
     // if (shift - 32 >= 0)
     //   dest.low |= dest.high >>> shift - 32
     // Note: Negative shifts yield a zero as result, except for the signed
     //       right shift. Therefore we need to test for it and only do it if
     //       it isn't negative.
+    ScratchRegisterScope shift(*this);
 
-    ScratchRegisterScope shift(*this);
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.low, lsr(dest.low, shift));
-    ma_rsb(shift, Imm32(32), shift);
+    as_rsb(shift, shift, Imm8(32));
     as_orr(dest.low, dest.low, lsl(dest.high, shift));
     ma_neg(shift, shift, SetCC);
     ma_b(&proceed, Signed);
 
     as_orr(dest.low, dest.low, asr(dest.high, shift));
 
     bind(&proceed);
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.high, asr(dest.high, shift));
 }
 
 void
 MacroAssembler::rshift32Arithmetic(Register src, Register dest)
 {
     ma_asr(src, dest, dest);
 }
@@ -759,23 +799,23 @@ MacroAssembler::rshift64(Imm32 imm, Regi
 
 void
 MacroAssembler::rshift64(Register unmaskedShift, Register64 dest)
 {
     // dest.low = dest.low >> shift | dest.high >> shift - 32 | dest.high << 32 - shift
     // Note: one of the two dest.high shifts will always yield zero due to negative shift.
 
     ScratchRegisterScope shift(*this);
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.low, lsr(dest.low, shift));
-    ma_sub(shift, Imm32(32), shift);
+    as_sub(shift, shift, Imm8(32));
     as_orr(dest.low, dest.low, lsr(dest.high, shift));
     ma_neg(shift, shift);
     as_orr(dest.low, dest.low, lsl(dest.high, shift));
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.high, lsr(dest.high, shift));
 }
 
 // ===============================================================
 // Rotate functions
 void
 MacroAssembler::rotateLeft(Imm32 count, Register input, Register dest)
 {
@@ -783,17 +823,18 @@ MacroAssembler::rotateLeft(Imm32 count, 
         ma_rol(count, input, dest);
     else
         ma_mov(input, dest);
 }
 
 void
 MacroAssembler::rotateLeft(Register count, Register input, Register dest)
 {
-    ma_rol(count, input, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_rol(count, input, dest, scratch);
 }
 
 void
 MacroAssembler::rotateLeft64(Imm32 count, Register64 input, Register64 dest, Register temp)
 {
     MOZ_ASSERT(temp == InvalidReg);
     MOZ_ASSERT(input.low != dest.high && input.high != dest.low);
 
@@ -828,45 +869,44 @@ MacroAssembler::rotateLeft64(Register sh
     MOZ_ASSERT(temp != src.low && temp != src.high);
     MOZ_ASSERT(shift != src.low && shift != src.high);
     MOZ_ASSERT(temp != InvalidReg);
 
     ScratchRegisterScope shift_value(*this);
     Label high, done;
 
     ma_mov(src.high, temp);
-    ma_and(Imm32(0x3f), shift, shift_value);
-
-    ma_cmp(shift_value, Imm32(32));
+    as_and(shift_value, shift, Imm8(0x3f));
+    as_cmp(shift_value, Imm8(32));
     ma_b(&high, GreaterThanOrEqual);
 
     // high = high << shift | low >> 32 - shift
     // low = low << shift | high >> 32 - shift
     as_mov(dest.high, lsl(src.high, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.high, dest.high, lsr(src.low, shift_value));
 
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_mov(dest.low, lsl(src.low, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.low, dest.low, lsr(temp, shift_value));
 
     ma_b(&done);
 
     // A 32 - 64 shift is a 0 - 32 shift in the other direction.
     bind(&high);
-    ma_rsb(Imm32(64), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(64));
 
     as_mov(dest.high, lsr(src.high, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.high, dest.high, lsl(src.low, shift_value));
 
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_mov(dest.low, lsr(src.low, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.low, dest.low, lsl(temp, shift_value));
 
     bind(&done);
 }
 
 void
 MacroAssembler::rotateRight(Imm32 count, Register input, Register dest)
 {
@@ -919,45 +959,44 @@ MacroAssembler::rotateRight64(Register s
     MOZ_ASSERT(temp != src.low && temp != src.high);
     MOZ_ASSERT(shift != src.low && shift != src.high);
     MOZ_ASSERT(temp != InvalidReg);
 
     ScratchRegisterScope shift_value(*this);
     Label high, done;
 
     ma_mov(src.high, temp);
-    ma_and(Imm32(0x3f), shift, shift_value);
-
-    ma_cmp(shift_value, Imm32(32));
+    as_and(shift_value, shift, Imm8(0x3f));
+    as_cmp(shift_value, Imm8(32));
     ma_b(&high, GreaterThanOrEqual);
 
     // high = high >> shift | low << 32 - shift
     // low = low >> shift | high << 32 - shift
     as_mov(dest.high, lsr(src.high, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.high, dest.high, lsl(src.low, shift_value));
 
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_mov(dest.low, lsr(src.low, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.low, dest.low, lsl(temp, shift_value));
 
     ma_b(&done);
 
     // A 32 - 64 shift is a 0 - 32 shift in the other direction.
     bind(&high);
-    ma_rsb(Imm32(64), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(64));
 
     as_mov(dest.high, lsl(src.high, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.high, dest.high, lsr(src.low, shift_value));
 
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_mov(dest.low, lsl(src.low, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.low, dest.low, lsr(temp, shift_value));
 
     bind(&done);
 }
 
 // ===============================================================
 // Bit counting functions
 
@@ -966,63 +1005,67 @@ MacroAssembler::clz32(Register src, Regi
 {
     ma_clz(src, dest);
 }
 
 void
 MacroAssembler::clz64(Register64 src, Register dest)
 {
     ScratchRegisterScope scratch(*this);
+
     ma_clz(src.high, scratch);
-    ma_cmp(scratch, Imm32(32));
+    as_cmp(scratch, Imm8(32));
     ma_mov(scratch, dest, LeaveCC, NotEqual);
     ma_clz(src.low, dest, Equal);
-    ma_add(Imm32(32), dest, LeaveCC, Equal);
+    as_add(dest, dest, Imm8(32), LeaveCC, Equal);
 }
 
 void
 MacroAssembler::ctz32(Register src, Register dest, bool knownNotZero)
 {
-    ma_ctz(src, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_ctz(src, dest, scratch);
 }
 
 void
 MacroAssembler::ctz64(Register64 src, Register dest)
 {
     Label done, high;
-
-    ma_cmp(src.low, Imm32(0));
+    as_cmp(src.low, Imm8(0));
     ma_b(&high, Equal);
 
     ctz32(src.low, dest, /* knownNotZero = */ true);
     ma_b(&done);
 
     bind(&high);
     ctz32(src.high, dest, /* knownNotZero = */ false);
-    ma_add(Imm32(32), dest);
+    as_add(dest, dest, Imm8(32));
 
     bind(&done);
 }
 
 void
 MacroAssembler::popcnt32(Register input,  Register output, Register tmp)
 {
     // Equivalent to GCC output of mozilla::CountPopulation32()
 
+    ScratchRegisterScope scratch(*this);
+
     if (input != output)
         ma_mov(input, output);
     as_mov(tmp, asr(output, 1));
-    ma_and(Imm32(0x55555555), tmp);
+    ma_and(Imm32(0x55555555), tmp, scratch);
     ma_sub(output, tmp, output);
     as_mov(tmp, asr(output, 2));
-    ma_and(Imm32(0x33333333), output);
-    ma_and(Imm32(0x33333333), tmp);
+    ma_mov(Imm32(0x33333333), scratch);
+    ma_and(scratch, output);
+    ma_and(scratch, tmp);
     ma_add(output, tmp, output);
     as_add(output, output, lsr(output, 4));
-    ma_and(Imm32(0xF0F0F0F), output);
+    ma_and(Imm32(0xF0F0F0F), output, scratch);
     as_add(output, output, lsl(output, 8));
     as_add(output, output, lsl(output, 16));
     as_mov(output, asr(output, 24));
 }
 
 void
 MacroAssembler::popcnt64(Register64 src, Register64 dest, Register tmp)
 {
@@ -1053,70 +1096,103 @@ MacroAssembler::branch32(Condition cond,
     ma_cmp(lhs, rhs);
     ma_b(label, cond);
 }
 
 template <class L>
 void
 MacroAssembler::branch32(Condition cond, Register lhs, Imm32 rhs, L label)
 {
-    ma_cmp(lhs, rhs);
+    ScratchRegisterScope scratch(*this);
+
+    ma_cmp(lhs, rhs, scratch);
     ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch32(Condition cond, const Address& lhs, Register rhs, Label* label)
 {
     ScratchRegisterScope scratch(*this);
-    load32(lhs, scratch);
-    branch32(cond, scratch, rhs, label);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, rhs);
+    ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch32(Condition cond, const Address& lhs, Imm32 rhs, Label* label)
 {
-    // branch32 will use ScratchRegister.
-    AutoRegisterScope scratch(*this, secondScratchReg_);
-    load32(lhs, scratch);
-    branch32(cond, scratch, rhs, label);
+    ScratchRegisterScope scratch(*this);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, rhs, scratch2);
+    ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch32(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
-    loadPtr(lhs, scratch2); // ma_cmp will use the scratch register.
-    ma_cmp(scratch2, rhs);
+    ScratchRegisterScope scratch(*this);
+
+    // Load into scratch.
+    movePtr(ImmWord(uintptr_t(lhs.addr)), scratch);
+    ma_ldr(DTRAddr(scratch, DtrOffImm(0)), scratch);
+
+    ma_cmp(scratch, rhs);
     ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
-    loadPtr(lhs, scratch2); // ma_cmp will use the scratch register.
-    ma_cmp(scratch2, rhs);
+    ScratchRegisterScope scratch(*this);
+    SecondScratchRegisterScope scratch2(*this);
+
+    // Load into scratch.
+    movePtr(ImmWord(uintptr_t(lhs.addr)), scratch);
+    ma_ldr(DTRAddr(scratch, DtrOffImm(0)), scratch);
+
+    ma_cmp(scratch, rhs, scratch2);
     ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label* label)
 {
-    // branch32 will use ScratchRegister.
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
-    load32(lhs, scratch2);
+    SecondScratchRegisterScope scratch2(*this);
+    {
+        ScratchRegisterScope scratch(*this);
+
+        Register base = lhs.base;
+        uint32_t scale = Imm32::ShiftOf(lhs.scale).value;
+
+        // Load lhs into scratch2.
+        if (lhs.offset != 0) {
+            ma_add(base, Imm32(lhs.offset), scratch, scratch2);
+            ma_ldr(DTRAddr(scratch, DtrRegImmShift(lhs.index, LSL, scale)), scratch2);
+        } else {
+            ma_ldr(DTRAddr(base, DtrRegImmShift(lhs.index, LSL, scale)), scratch2);
+        }
+    }
     branch32(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
 {
     ScratchRegisterScope scratch(*this);
-    loadPtr(lhs, scratch);
-    branch32(cond, scratch, rhs, label);
+    SecondScratchRegisterScope scratch2(*this);
+
+    movePtr(lhs, scratch);
+    ma_ldr(DTRAddr(scratch, DtrOffImm(0)), scratch);
+
+    ma_cmp(scratch, rhs, scratch2);
+    ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
 {
     MOZ_ASSERT(cond == Assembler::NotEqual,
                "other condition codes not supported");
 
@@ -1292,68 +1368,71 @@ void
 MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmPtr rhs, Label* label)
 {
     branchPtr(cond, lhs, ImmWord(uintptr_t(rhs.value)), label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmGCPtr rhs, Label* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
+    SecondScratchRegisterScope scratch2(*this);
     loadPtr(lhs, scratch2);
     branchPtr(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmWord rhs, Label* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
+    SecondScratchRegisterScope scratch2(*this);
     loadPtr(lhs, scratch2);
     branchPtr(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
 {
-    ScratchRegisterScope scratch(*this);
-    loadPtr(lhs, scratch);
-    branchPtr(cond, scratch, rhs, label);
+    SecondScratchRegisterScope scratch2(*this);
+    loadPtr(lhs, scratch2);
+    branchPtr(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, ImmWord rhs, Label* label)
 {
-    ScratchRegisterScope scratch(*this);
-    loadPtr(lhs, scratch);
-    branchPtr(cond, scratch, rhs, label);
+    SecondScratchRegisterScope scratch2(*this);
+    loadPtr(lhs, scratch2);
+    branchPtr(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, Label* label)
 {
-    ScratchRegisterScope scratch(*this);
-    loadPtr(lhs, scratch);
-    branchPtr(cond, scratch, rhs, label);
+    SecondScratchRegisterScope scratch2(*this);
+    loadPtr(lhs, scratch2);
+    branchPtr(cond, scratch2, rhs, label);
 }
 
 template <typename T>
-CodeOffsetJump
+inline CodeOffsetJump
 MacroAssembler::branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label)
 {
-    ma_cmp(lhs, rhs);
+    cmpPtr(lhs, rhs);
     return jumpWithPatch(label, cond);
 }
 
 template <typename T>
-CodeOffsetJump
+inline CodeOffsetJump
 MacroAssembler::branchPtrWithPatch(Condition cond, Address lhs, T rhs, RepatchLabel* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
-    ma_ldr(lhs, scratch2);
-    ma_cmp(scratch2, rhs);
+    SecondScratchRegisterScope scratch2(*this);
+    {
+        ScratchRegisterScope scratch(*this);
+        ma_ldr(lhs, scratch2, scratch);
+    }
+    cmpPtr(scratch2, rhs);
     return jumpWithPatch(label, cond);
 }
 
 void
 MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
 {
     branchPtr(cond, lhs, rhs, label);
 }
@@ -1386,21 +1465,23 @@ void
 MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
 {
     branchTruncateFloat32ToInt32(src, dest, fail);
 }
 
 void
 MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
 {
-    ScratchFloat32Scope scratch(*this);
-    ma_vcvt_F32_I32(src, scratch.sintOverlay());
-    ma_vxfer(scratch, dest);
-    ma_cmp(dest, Imm32(0x7fffffff));
-    ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
+    ScratchFloat32Scope scratchFloat32(*this);
+    ScratchRegisterScope scratch(*this);
+
+    ma_vcvt_F32_I32(src, scratchFloat32.sintOverlay());
+    ma_vxfer(scratchFloat32, dest);
+    ma_cmp(dest, Imm32(0x7fffffff), scratch);
+    ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
 
 void
 MacroAssembler::branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                              Label* label)
 {
     compareDouble(lhs, rhs);
@@ -1435,86 +1516,87 @@ MacroAssembler::branchTruncateDoubleMayb
 // was clamped to INT_MIN/INT_MAX, and we can test it. NOTE: if the value
 // really was supposed to be INT_MAX / INT_MIN then it will be wrong.
 //
 // 2. Convert the floating point value to an integer, if it did not fit, then it
 // set one or two bits in the fpcsr. Check those.
 void
 MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
 {
-    ScratchDoubleScope scratch(*this);
-    FloatRegister scratchSIntReg = scratch.sintOverlay();
+    ScratchDoubleScope scratchDouble(*this);
+    FloatRegister scratchSIntReg = scratchDouble.sintOverlay();
+    ScratchRegisterScope scratch(*this);
 
     ma_vcvt_F64_I32(src, scratchSIntReg);
     ma_vxfer(scratchSIntReg, dest);
-    ma_cmp(dest, Imm32(0x7fffffff));
-    ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
+    ma_cmp(dest, Imm32(0x7fffffff), scratch);
+    ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
 
 template <typename T, typename L>
 void
 MacroAssembler::branchAdd32(Condition cond, T src, Register dest, L label)
 {
     add32(src, dest);
     as_b(label, cond);
 }
 
 template <typename T>
 void
 MacroAssembler::branchSub32(Condition cond, T src, Register dest, Label* label)
 {
-    ma_sub(src, dest, SetCC);
+    sub32(src, dest);
     j(cond, label);
 }
 
 void
 MacroAssembler::decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
 {
-    ma_sub(rhs, lhs, SetCC);
+    ScratchRegisterScope scratch(*this);
+    ma_sub(rhs, lhs, scratch, SetCC);
     as_b(label, cond);
 }
 
 template <class L>
 void
 MacroAssembler::branchTest32(Condition cond, Register lhs, Register rhs, L label)
 {
     MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
     // x86 likes test foo, foo rather than cmp foo, #0.
     // Convert the former into the latter.
     if (lhs == rhs && (cond == Zero || cond == NonZero))
-        ma_cmp(lhs, Imm32(0));
+        as_cmp(lhs, Imm8(0));
     else
         ma_tst(lhs, rhs);
     ma_b(label, cond);
 }
 
 template <class L>
 void
 MacroAssembler::branchTest32(Condition cond, Register lhs, Imm32 rhs, L label)
 {
     MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
-    ma_tst(lhs, rhs);
+    ScratchRegisterScope scratch(*this);
+    ma_tst(lhs, rhs, scratch);
     ma_b(label, cond);
 }
 
 void
 MacroAssembler::branchTest32(Condition cond, const Address& lhs, Imm32 rhs, Label* label)
 {
-    // branchTest32 will use ScratchRegister.
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
+    SecondScratchRegisterScope scratch2(*this);
     load32(lhs, scratch2);
     branchTest32(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branchTest32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
 {
-    // branchTest32 will use ScratchRegister.
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
+    SecondScratchRegisterScope scratch2(*this);
     load32(lhs, scratch2);
     branchTest32(cond, scratch2, rhs, label);
 }
 
 template <class L>
 void
 MacroAssembler::branchTestPtr(Condition cond, Register lhs, Register rhs, L label)
 {
@@ -1533,21 +1615,23 @@ MacroAssembler::branchTestPtr(Condition 
     branchTest32(cond, lhs, rhs, label);
 }
 
 template <class L>
 void
 MacroAssembler::branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
                              L label)
 {
+    ScratchRegisterScope scratch(*this);
+
     if (cond == Assembler::Zero) {
         MOZ_ASSERT(lhs.low == rhs.low);
         MOZ_ASSERT(lhs.high == rhs.high);
-        ma_orr(lhs.low, lhs.high, ScratchRegister);
-        branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
+        ma_orr(lhs.low, lhs.high, scratch);
+        branchTestPtr(cond, scratch, scratch, label);
     } else {
         MOZ_CRASH("Unsupported condition");
     }
 }
 
 void
 MacroAssembler::branchTestUndefined(Condition cond, Register tag, Label* label)
 {
@@ -1920,35 +2004,41 @@ MacroAssembler::branchTestMagic(Conditio
     branch32(cond, ToPayload(valaddr), Imm32(why), label);
 }
 
 // ========================================================================
 // Memory access primitives.
 void
 MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr)
 {
-    ma_vstr(src, addr);
+    ScratchRegisterScope scratch(*this);
+    ma_vstr(src, addr, scratch);
 }
 void
 MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr)
 {
+    ScratchRegisterScope scratch(*this);
+    SecondScratchRegisterScope scratch2(*this);
     uint32_t scale = Imm32::ShiftOf(addr.scale).value;
-    ma_vstr(src, addr.base, addr.index, scale, addr.offset);
+    ma_vstr(src, addr.base, addr.index, scratch, scratch2, scale, addr.offset);
 }
 
 void
 MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
 {
-    ma_vstr(src.asSingle(), addr);
+    ScratchRegisterScope scratch(*this);
+    ma_vstr(src.asSingle(), addr, scratch);
 }
 void
 MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
 {
+    ScratchRegisterScope scratch(*this);
+    SecondScratchRegisterScope scratch2(*this);
     uint32_t scale = Imm32::ShiftOf(addr.scale).value;
-    ma_vstr(src.asSingle(), addr.base, addr.index, scale, addr.offset);
+    ma_vstr(src.asSingle(), addr.base, addr.index, scratch, scratch2, scale, addr.offset);
 }
 
 void
 MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest)
 {
     MOZ_CRASH("NYI");
 }
 void
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -40,48 +40,49 @@ isValueDTRDCandidate(ValueOperand& val)
     return true;
 }
 
 void
 MacroAssemblerARM::convertBoolToInt32(Register source, Register dest)
 {
     // Note that C++ bool is only 1 byte, so zero extend it to clear the
     // higher-order bits.
-    ma_and(Imm32(0xff), source, dest);
+    as_and(dest, source, Imm8(0xff));
 }
 
 void
 MacroAssemblerARM::convertInt32ToDouble(Register src, FloatRegister dest_)
 {
     // Direct conversions aren't possible.
     VFPRegister dest = VFPRegister(dest_);
     as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat);
     as_vcvt(dest, dest.sintOverlay());
 }
 
 void
 MacroAssemblerARM::convertInt32ToDouble(const Address& src, FloatRegister dest)
 {
     ScratchDoubleScope scratch(asMasm());
-    ma_vldr(src, scratch);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_vldr(src, scratch, scratch2);
     as_vcvt(dest, VFPRegister(scratch).sintOverlay());
 }
 
 void
 MacroAssemblerARM::convertInt32ToDouble(const BaseIndex& src, FloatRegister dest)
 {
     Register base = src.base;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     if (src.offset != 0) {
-        ma_mov(base, scratch);
+        ma_add(base, Imm32(src.offset), scratch, scratch2);
         base = scratch;
-        ma_add(Imm32(src.offset), base);
     }
     ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), scratch);
     convertInt32ToDouble(scratch, dest);
 }
 
 void
 MacroAssemblerARM::convertUInt32ToDouble(Register src, FloatRegister dest_)
 {
@@ -98,22 +99,27 @@ MacroAssemblerARMCompat::convertUInt64To
 {
     return false;
 }
 
 void
 MacroAssemblerARMCompat::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
 {
     MOZ_ASSERT(temp == Register::Invalid());
+    ScratchDoubleScope scratchDouble(asMasm());
+
     convertUInt32ToDouble(src.high, dest);
-    movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), ScratchRegister);
-    loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
-    asMasm().mulDouble(ScratchDoubleReg, dest);
-    convertUInt32ToDouble(src.low, ScratchDoubleReg);
-    asMasm().addDouble(ScratchDoubleReg, dest);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), scratch);
+        ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble);
+    }
+    asMasm().mulDouble(scratchDouble, dest);
+    convertUInt32ToDouble(src.low, scratchDouble);
+    asMasm().addDouble(scratchDouble, dest);
 }
 
 void
 MacroAssemblerARM::convertUInt32ToFloat32(Register src, FloatRegister dest_)
 {
     // Direct conversions aren't possible.
     VFPRegister dest = VFPRegister(dest_);
     as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat);
@@ -132,33 +138,35 @@ void MacroAssemblerARM::convertDoubleToF
 void
 MacroAssemblerARM::convertDoubleToInt32(FloatRegister src, Register dest,
                                         Label* fail, bool negativeZeroCheck)
 {
     // Convert the floating point value to an integer, if it did not fit, then
     // when we convert it *back* to a float, it will have a different value,
     // which we can test.
     ScratchDoubleScope scratchDouble(asMasm());
+    ScratchRegisterScope scratch(asMasm());
+
     FloatRegister scratchSIntReg = scratchDouble.sintOverlay();
 
     ma_vcvt_F64_I32(src, scratchSIntReg);
     // Move the value into the dest register.
     ma_vxfer(scratchSIntReg, dest);
     ma_vcvt_I32_F64(scratchSIntReg, scratchDouble);
     ma_vcmp(src, scratchDouble);
     as_vmrs(pc);
     ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
 
     if (negativeZeroCheck) {
-        ma_cmp(dest, Imm32(0));
+        as_cmp(dest, Imm8(0));
         // Test and bail for -0.0, when integer result is 0. Move the top word
         // of the double into the output reg, if it is non-zero, then the
         // original value was -0.0.
         as_vxfer(dest, InvalidReg, src, FloatToCore, Assembler::Equal, 1);
-        ma_cmp(dest, Imm32(0x80000000), Assembler::Equal);
+        ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::Equal);
         ma_b(fail, Assembler::Equal);
     }
 }
 
 // Checks whether a float32 is representable as a 32-bit integer. If so, the
 // integer is written to the output register. Otherwise, a bailout is taken to
 // the given snapshot. This function overwrites the scratch float register.
 void
@@ -166,40 +174,41 @@ MacroAssemblerARM::convertFloat32ToInt32
                                          Label* fail, bool negativeZeroCheck)
 {
     // Converting the floating point value to an integer and then converting it
     // back to a float32 would not work, as float to int32 conversions are
     // clamping (e.g. float(INT32_MAX + 1) would get converted into INT32_MAX
     // and then back to float(INT32_MAX + 1)).  If this ever happens, we just
     // bail out.
     ScratchFloat32Scope scratchFloat(asMasm());
+    ScratchRegisterScope scratch(asMasm());
 
     FloatRegister ScratchSIntReg = scratchFloat.sintOverlay();
     ma_vcvt_F32_I32(src, ScratchSIntReg);
 
     // Store the result
     ma_vxfer(ScratchSIntReg, dest);
 
     ma_vcvt_I32_F32(ScratchSIntReg, scratchFloat);
     ma_vcmp(src, scratchFloat);
     as_vmrs(pc);
     ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
 
     // Bail out in the clamped cases.
-    ma_cmp(dest, Imm32(0x7fffffff));
-    ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
+    ma_cmp(dest, Imm32(0x7fffffff), scratch);
+    ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 
     if (negativeZeroCheck) {
-        ma_cmp(dest, Imm32(0));
+        as_cmp(dest, Imm8(0));
         // Test and bail for -0.0, when integer result is 0. Move the float into
         // the output reg, and if it is non-zero then the original value was
         // -0.0
         as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore, Assembler::Equal, 0);
-        ma_cmp(dest, Imm32(0x80000000), Assembler::Equal);
+        ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::Equal);
         ma_b(fail, Assembler::Equal);
     }
 }
 
 void
 MacroAssemblerARM::convertFloat32ToDouble(FloatRegister src, FloatRegister dest)
 {
     MOZ_ASSERT(dest.isDouble());
@@ -214,17 +223,18 @@ MacroAssemblerARM::convertInt32ToFloat32
     as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat);
     as_vcvt(dest.singleOverlay(), dest.sintOverlay());
 }
 
 void
 MacroAssemblerARM::convertInt32ToFloat32(const Address& src, FloatRegister dest)
 {
     ScratchFloat32Scope scratch(asMasm());
-    ma_vldr(src, scratch);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_vldr(src, scratch, scratch2);
     as_vcvt(dest, VFPRegister(scratch).sintOverlay());
 }
 
 bool
 MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op,
                            SBit s, Condition c)
 {
     if ((s == SetCC && ! condsAreSafe(op)) || !can_dbl(op))
@@ -241,22 +251,23 @@ MacroAssemblerARM::alu_dbl(Register src1
     // don't do second operation if first operation overflowed. This preserves
     // the overflow condition code. Unfortunately, it is horribly brittle.
     as_alu(dest, src1, Operand2(both.fst), interop, LeaveCC, c);
     as_alu(dest, dest, Operand2(both.snd), op, s, c);
     return true;
 }
 
 void
-MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest,
+MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, AutoRegisterScope& scratch,
                           ALUOp op, SBit s, Condition c)
 {
     // ma_mov should be used for moves.
     MOZ_ASSERT(op != OpMov);
     MOZ_ASSERT(op != OpMvn);
+    MOZ_ASSERT(src1 != scratch);
 
     // As it turns out, if you ask for a compare-like instruction you *probably*
     // want it to set condition codes.
     if (dest == InvalidReg)
         MOZ_ASSERT(s == SetCC);
 
     // The operator gives us the ability to determine how this can be used.
     Imm8 imm8 = Imm8(imm.value);
@@ -264,24 +275,22 @@ MacroAssemblerARM::ma_alu(Register src1,
     if (!imm8.invalid) {
         as_alu(dest, src1, imm8, op, s, c);
         return;
     }
 
     // One instruction, negated:
     Imm32 negImm = imm;
     Register negDest;
-    ALUOp negOp = ALUNeg(op, dest, &negImm, &negDest);
+    ALUOp negOp = ALUNeg(op, dest, scratch, &negImm, &negDest);
     Imm8 negImm8 = Imm8(negImm.value);
-    // 'add r1, r2, -15' can be replaced with 'sub r1, r2, 15'. For bonus
-    // points, dest can be replaced (nearly always invalid => ScratchRegister)
+    // 'add r1, r2, -15' can be replaced with 'sub r1, r2, 15'.
+    // The dest can be replaced (InvalidReg => scratch).
     // This is useful if we wish to negate tst. tst has an invalid (aka not
-    // used) dest, but its negation is bic *requires* a dest. We can accomodate,
-    // but it will need to clobber *something*, and the scratch register isn't
-    // being used, so...
+    // used) dest, but its negation bic requires a dest.
     if (negOp != OpInvalid && !negImm8.invalid) {
         as_alu(negDest, src1, negImm8, negOp, s, c);
         return;
     }
 
     // Start by attempting to generate a two instruction form. Some things
     // cannot be made into two-inst forms correctly. Namely, adds dest, src,
     // 0xffff. Since we want the condition codes (and don't know which ones
@@ -291,29 +300,16 @@ MacroAssemblerARM::ma_alu(Register src1,
     // instruction variant.
     if (alu_dbl(src1, imm, dest, op, s, c))
         return;
 
     // And try with its negative.
     if (negOp != OpInvalid && alu_dbl(src1, negImm, negDest, negOp, s, c))
         return;
 
-    // Often this code is called with dest as the ScratchRegister.  The register
-    // is logically owned by the caller after this call.
-    const Register& scratch = ScratchRegister;
-    MOZ_ASSERT(src1 != scratch);
-#ifdef DEBUG
-    if (dest != scratch) {
-        // If the destination register is not the scratch register, double check
-        // that the current function does not erase the content of the scratch
-        // register.
-        ScratchRegisterScope assertScratch(asMasm());
-    }
-#endif
-
     ma_mov(imm, scratch, c);
     as_alu(dest, src1, O2Reg(scratch), op, s, c);
 }
 
 void
 MacroAssemblerARM::ma_alu(Register src1, Operand op2, Register dest, ALUOp op,
             SBit s, Assembler::Condition c)
 {
@@ -394,22 +390,19 @@ MacroAssemblerARM::ma_mov(Imm32 imm, Reg
     // Try mov with Imm8 operand.
     Imm8 imm8 = Imm8(imm.value);
     if (!imm8.invalid) {
         as_alu(dest, InvalidReg, imm8, OpMov, LeaveCC, c);
         return;
     }
 
     // Try mvn with Imm8 operand.
-    Imm32 negImm = imm;
-    Register negDest;
-    ALUOp negOp = ALUNeg(OpMov, dest, &negImm, &negDest);
-    Imm8 negImm8 = Imm8(negImm.value);
-    if (negOp != OpInvalid && !negImm8.invalid) {
-        as_alu(negDest, InvalidReg, negImm8, negOp, LeaveCC, c);
+    Imm8 negImm8 = Imm8(~imm.value);
+    if (!negImm8.invalid) {
+        as_alu(dest, InvalidReg, negImm8, OpMvn, LeaveCC, c);
         return;
     }
 
     // Try movw/movt.
     if (HasMOVWT()) {
         // ARMv7 supports movw/movt. movw zero-extends its 16 bit argument,
         // so we can set the register this way. movt leaves the bottom 16
         // bits in tact, so we always need a movw.
@@ -490,20 +483,19 @@ MacroAssemblerARM::ma_asr(Register shift
 
 void
 MacroAssemblerARM::ma_ror(Register shift, Register src, Register dst)
 {
     as_mov(dst, ror(src, shift));
 }
 
 void
-MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst)
-{
-    ScratchRegisterScope scratch(asMasm());
-    ma_rsb(shift, Imm32(32), scratch);
+MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst, AutoRegisterScope& scratch)
+{
+    as_rsb(scratch, shift, Imm8(32));
     as_mov(dst, ror(src, scratch));
 }
 
 // Move not (dest <- ~src)
 void
 MacroAssemblerARM::ma_mvn(Register src1, Register dest, SBit s, Assembler::Condition c)
 {
     as_alu(dest, InvalidReg, O2Reg(src1), OpMvn, s, c);
@@ -526,33 +518,34 @@ MacroAssemblerARM::ma_and(Register src, 
 void
 MacroAssemblerARM::ma_and(Register src1, Register src2, Register dest,
                           SBit s, Assembler::Condition c)
 {
     as_and(dest, src1, O2Reg(src2), s, c);
 }
 
 void
-MacroAssemblerARM::ma_and(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
-{
-    ma_alu(dest, imm, dest, OpAnd, s, c);
-}
-
-void
-MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest,
+MacroAssemblerARM::ma_and(Imm32 imm, Register dest, AutoRegisterScope& scratch,
                           SBit s, Assembler::Condition c)
 {
-    ma_alu(src1, imm, dest, OpAnd, s, c);
+    ma_alu(dest, imm, dest, scratch, OpAnd, s, c);
+}
+
+void
+MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch,
+                          SBit s, Assembler::Condition c)
+{
+    ma_alu(src1, imm, dest, scratch, OpAnd, s, c);
 }
 
 // Bit clear (dest <- dest & ~imm) or (dest <- src1 & ~src2).
 void
-MacroAssemblerARM::ma_bic(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
-{
-    ma_alu(dest, imm, dest, OpBic, s, c);
+MacroAssemblerARM::ma_bic(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpBic, s, c);
 }
 
 // Exclusive or.
 void
 MacroAssemblerARM::ma_eor(Register src, Register dest, SBit s, Assembler::Condition c)
 {
     ma_eor(dest, src, dest, s, c);
 }
@@ -560,26 +553,26 @@ MacroAssemblerARM::ma_eor(Register src, 
 void
 MacroAssemblerARM::ma_eor(Register src1, Register src2, Register dest,
                           SBit s, Assembler::Condition c)
 {
     as_eor(dest, src1, O2Reg(src2), s, c);
 }
 
 void
-MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
-{
-    ma_alu(dest, imm, dest, OpEor, s, c);
-}
-
-void
-MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest,
+MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpEor, s, c);
+}
+
+void
+MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch,
        SBit s, Assembler::Condition c)
 {
-    ma_alu(src1, imm, dest, OpEor, s, c);
+    ma_alu(src1, imm, dest, scratch, OpEor, s, c);
 }
 
 // Or.
 void
 MacroAssemblerARM::ma_orr(Register src, Register dest, SBit s, Assembler::Condition c)
 {
     ma_orr(dest, src, dest, s, c);
 }
@@ -587,53 +580,53 @@ MacroAssemblerARM::ma_orr(Register src, 
 void
 MacroAssemblerARM::ma_orr(Register src1, Register src2, Register dest,
                           SBit s, Assembler::Condition c)
 {
     as_orr(dest, src1, O2Reg(src2), s, c);
 }
 
 void
-MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
-{
-    ma_alu(dest, imm, dest, OpOrr, s, c);
-}
-
-void
-MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest,
+MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpOrr, s, c);
+}
+
+void
+MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch,
                           SBit s, Assembler::Condition c)
 {
-    ma_alu(src1, imm, dest, OpOrr, s, c);
+    ma_alu(src1, imm, dest, scratch, OpOrr, s, c);
 }
 
 // Arithmetic-based ops.
 // Add with carry.
 void
-MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpAdc, s, c);
+MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpAdc, s, c);
 }
 
 void
 MacroAssemblerARM::ma_adc(Register src, Register dest, SBit s, Condition c)
 {
     as_alu(dest, dest, O2Reg(src), OpAdc, s, c);
 }
 
 void
 MacroAssemblerARM::ma_adc(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpAdc, s, c);
 }
 
 // Add.
 void
-MacroAssemblerARM::ma_add(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpAdd, s, c);
+MacroAssemblerARM::ma_add(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpAdd, s, c);
 }
 
 void
 MacroAssemblerARM::ma_add(Register src1, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, O2Reg(src1), dest, OpAdd, s, c);
 }
 
@@ -645,45 +638,45 @@ MacroAssemblerARM::ma_add(Register src1,
 
 void
 MacroAssemblerARM::ma_add(Register src1, Operand op, Register dest, SBit s, Condition c)
 {
     ma_alu(src1, op, dest, OpAdd, s, c);
 }
 
 void
-MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, SBit s, Condition c)
-{
-    ma_alu(src1, op, dest, OpAdd, s, c);
+MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(src1, op, dest, scratch, OpAdd, s, c);
 }
 
 // Subtract with carry.
 void
-MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpSbc, s, c);
+MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpSbc, s, c);
 }
 
 void
 MacroAssemblerARM::ma_sbc(Register src1, Register dest, SBit s, Condition c)
 {
     as_alu(dest, dest, O2Reg(src1), OpSbc, s, c);
 }
 
 void
 MacroAssemblerARM::ma_sbc(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpSbc, s, c);
 }
 
 // Subtract.
 void
-MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpSub, s, c);
+MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpSub, s, c);
 }
 
 void
 MacroAssemblerARM::ma_sub(Register src1, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, Operand(src1), dest, OpSub, s, c);
 }
 
@@ -695,51 +688,51 @@ MacroAssemblerARM::ma_sub(Register src1,
 
 void
 MacroAssemblerARM::ma_sub(Register src1, Operand op, Register dest, SBit s, Condition c)
 {
     ma_alu(src1, op, dest, OpSub, s, c);
 }
 
 void
-MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, SBit s, Condition c)
-{
-    ma_alu(src1, op, dest, OpSub, s, c);
+MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(src1, op, dest, scratch, OpSub, s, c);
 }
 
 // Reverse subtract.
 void
-MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpRsb, s, c);
+MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpRsb, s, c);
 }
 
 void
 MacroAssemblerARM::ma_rsb(Register src1, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(dest), OpRsb, s, c);
 }
 
 void
 MacroAssemblerARM::ma_rsb(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpRsb, s, c);
 }
 
 void
-MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, SBit s, Condition c)
-{
-    ma_alu(src1, op2, dest, OpRsb, s, c);
+MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(src1, op2, dest, scratch, OpRsb, s, c);
 }
 
 // Reverse subtract with carry.
 void
-MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpRsc, s, c);
+MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpRsc, s, c);
 }
 
 void
 MacroAssemblerARM::ma_rsc(Register src1, Register dest, SBit s, Condition c)
 {
     as_alu(dest, dest, O2Reg(src1), OpRsc, s, c);
 }
 
@@ -747,102 +740,109 @@ void
 MacroAssemblerARM::ma_rsc(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpRsc, s, c);
 }
 
 // Compares/tests.
 // Compare negative (sets condition codes as src1 + src2 would).
 void
-MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm, Condition c)
-{
-    ma_alu(src1, imm, InvalidReg, OpCmn, SetCC, c);
+MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c)
+{
+    ma_alu(src1, imm, InvalidReg, scratch, OpCmn, SetCC, c);
 }
 
 void
 MacroAssemblerARM::ma_cmn(Register src1, Register src2, Condition c)
 {
     as_alu(InvalidReg, src2, O2Reg(src1), OpCmn, SetCC, c);
 }
 
 void
 MacroAssemblerARM::ma_cmn(Register src1, Operand op, Condition c)
 {
     MOZ_CRASH("Feature NYI");
 }
 
 // Compare (src - src2).
 void
-MacroAssemblerARM::ma_cmp(Register src1, Imm32 imm, Condition c)
-{
-    ma_alu(src1, imm, InvalidReg, OpCmp, SetCC, c);
-}
-
-void
-MacroAssemblerARM::ma_cmp(Register src1, ImmWord ptr, Condition c)
-{
-    ma_cmp(src1, Imm32(ptr.value), c);
-}
-
-void
-MacroAssemblerARM::ma_cmp(Register src1, ImmGCPtr ptr, Condition c)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_cmp(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c)
+{
+    ma_alu(src1, imm, InvalidReg, scratch, OpCmp, SetCC, c);
+}
+
+void
+MacroAssemblerARM::ma_cmp(Register src1, ImmTag tag, Condition c)
+{
+    // ImmTag comparisons can always be done without use of a scratch register.
+    Imm8 negtag = Imm8(-tag.value);
+    MOZ_ASSERT(!negtag.invalid);
+    as_cmn(src1, negtag, c);
+}
+
+void
+MacroAssemblerARM::ma_cmp(Register src1, ImmWord ptr, AutoRegisterScope& scratch, Condition c)
+{
+    ma_cmp(src1, Imm32(ptr.value), scratch, c);
+}
+
+void
+MacroAssemblerARM::ma_cmp(Register src1, ImmGCPtr ptr, AutoRegisterScope& scratch, Condition c)
+{
     ma_mov(ptr, scratch);
     ma_cmp(src1, scratch, c);
 }
 
 void
-MacroAssemblerARM::ma_cmp(Register src1, Operand op, Condition c)
+MacroAssemblerARM::ma_cmp(Register src1, Operand op, AutoRegisterScope& scratch,
+                          AutoRegisterScope& scratch2, Condition c)
 {
     switch (op.getTag()) {
       case Operand::OP2:
         as_cmp(src1, op.toOp2(), c);
         break;
-      case Operand::MEM: {
-        ScratchRegisterScope scratch(asMasm());
-        ma_ldr(op.toAddress(), scratch);
+      case Operand::MEM:
+        ma_ldr(op.toAddress(), scratch, scratch2);
         as_cmp(src1, O2Reg(scratch), c);
         break;
-      }
       default:
         MOZ_CRASH("trying to compare FP and integer registers");
     }
 }
 
 void
 MacroAssemblerARM::ma_cmp(Register src1, Register src2, Condition c)
 {
     as_cmp(src1, O2Reg(src2), c);
 }
 
 // Test for equality, (src1 ^ src2).
 void
-MacroAssemblerARM::ma_teq(Register src1, Imm32 imm, Condition c)
-{
-    ma_alu(src1, imm, InvalidReg, OpTeq, SetCC, c);
+MacroAssemblerARM::ma_teq(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c)
+{
+    ma_alu(src1, imm, InvalidReg, scratch, OpTeq, SetCC, c);
 }
 
 void
 MacroAssemblerARM::ma_teq(Register src1, Register src2, Condition c)
 {
     as_tst(src1, O2Reg(src2), c);
 }
 
 void
 MacroAssemblerARM::ma_teq(Register src1, Operand op, Condition c)
 {
     as_teq(src1, op.toOp2(), c);
 }
 
 // Test (src1 & src2).
 void
-MacroAssemblerARM::ma_tst(Register src1, Imm32 imm, Condition c)
-{
-    ma_alu(src1, imm, InvalidReg, OpTst, SetCC, c);
+MacroAssemblerARM::ma_tst(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c)
+{
+    ma_alu(src1, imm, InvalidReg, scratch, OpTst, SetCC, c);
 }
 
 void
 MacroAssemblerARM::ma_tst(Register src1, Register src2, Condition c)
 {
     as_tst(src1, O2Reg(src2), c);
 }
 
@@ -854,81 +854,79 @@ MacroAssemblerARM::ma_tst(Register src1,
 
 void
 MacroAssemblerARM::ma_mul(Register src1, Register src2, Register dest)
 {
     as_mul(dest, src1, src2);
 }
 
 void
-MacroAssemblerARM::ma_mul(Register src1, Imm32 imm, Register dest)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_mul(Register src1, Imm32 imm, Register dest, AutoRegisterScope& scratch)
+{
     ma_mov(imm, scratch);
     as_mul(dest, src1, scratch);
 }
 
 Assembler::Condition
-MacroAssemblerARM::ma_check_mul(Register src1, Register src2, Register dest, Condition cond)
-{
-    ScratchRegisterScope scratch(asMasm());
-
-    // TODO: this operation is illegal on armv6 and earlier if src2 ==
-    // ScratchRegister or src2 == dest.
+MacroAssemblerARM::ma_check_mul(Register src1, Register src2, Register dest,
+                                AutoRegisterScope& scratch, Condition cond)
+{
+    // TODO: this operation is illegal on armv6 and earlier
+    // if src2 == scratch or src2 == dest.
     if (cond == Equal || cond == NotEqual) {
         as_smull(scratch, dest, src1, src2, SetCC);
         return cond;
     }
 
     if (cond == Overflow) {
         as_smull(scratch, dest, src1, src2);
         as_cmp(scratch, asr(dest, 31));
         return NotEqual;
     }
 
     MOZ_CRASH("Condition NYI");
 }
 
 Assembler::Condition
-MacroAssemblerARM::ma_check_mul(Register src1, Imm32 imm, Register dest, Condition cond)
-{
-    ScratchRegisterScope scratch(asMasm());
-
+MacroAssemblerARM::ma_check_mul(Register src1, Imm32 imm, Register dest,
+                                AutoRegisterScope& scratch, Condition cond)
+{
     ma_mov(imm, scratch);
+
     if (cond == Equal || cond == NotEqual) {
         as_smull(scratch, dest, scratch, src1, SetCC);
         return cond;
     }
 
     if (cond == Overflow) {
         as_smull(scratch, dest, scratch, src1);
         as_cmp(scratch, asr(dest, 31));
         return NotEqual;
     }
 
     MOZ_CRASH("Condition NYI");
 }
 
 void
-MacroAssemblerARM::ma_umull(Register src1, Imm32 imm, Register destHigh, Register destLow)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_umull(Register src1, Imm32 imm, Register destHigh, Register destLow,
+                            AutoRegisterScope& scratch)
+{
     ma_mov(imm, scratch);
     as_umull(destHigh, destLow, src1, scratch);
 }
 
 void
 MacroAssemblerARM::ma_umull(Register src1, Register src2, Register destHigh, Register destLow)
 {
     as_umull(destHigh, destLow, src1, src2);
 }
 
 void
 MacroAssemblerARM::ma_mod_mask(Register src, Register dest, Register hold, Register tmp,
-                               int32_t shift)
+                               AutoRegisterScope& scratch, AutoRegisterScope& scratch2, int32_t shift)
 {
     // We wish to compute x % (1<<y) - 1 for a known constant, y.
     //
     // 1. Let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit dividend as
     // a number in base b, namely c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n
     //
     // 2. Since both addition and multiplication commute with modulus:
     //   x % C == (c_0 + c_1*b + ... + c_n*b^n) % C ==
@@ -939,78 +937,70 @@ MacroAssemblerARM::ma_mod_mask(Register 
     //
     // Each c_n can easily be computed by a shift/bitextract, and the modulus
     // can be maintained by simply subtracting by C whenever the number gets
     // over C.
     int32_t mask = (1 << shift) - 1;
     Label head;
 
     // Register 'hold' holds -1 if the value was negative, 1 otherwise. The
-    // ScratchRegister holds the remaining bits that have not been processed lr
+    // scratch reg holds the remaining bits that have not been processed lr
     // serves as a temporary location to store extracted bits into as well as
     // holding the trial subtraction as a temp value dest is the accumulator
     // (and holds the final result)
     //
     // Move the whole value into tmp, setting the codition codes so we can muck
     // with them later.
-    //
-    // Note that we cannot use ScratchRegister in place of tmp here, as ma_and
-    // below on certain architectures move the mask into ScratchRegister before
-    // performing the bitwise and.
     as_mov(tmp, O2Reg(src), SetCC);
     // Zero out the dest.
     ma_mov(Imm32(0), dest);
     // Set the hold appropriately.
     ma_mov(Imm32(1), hold);
     ma_mov(Imm32(-1), hold, Signed);
-    ma_rsb(Imm32(0), tmp, SetCC, Signed);
+    as_rsb(tmp, tmp, Imm8(0), SetCC, Signed);
 
     // Begin the main loop.
     bind(&head);
     {
-        AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-
-        // Extract the bottom bits into lr.
-        ma_and(Imm32(mask), tmp, scratch2);
+        // Extract the bottom bits.
+        ma_and(Imm32(mask), tmp, scratch, scratch2);
         // Add those bits to the accumulator.
-        ma_add(scratch2, dest, dest);
+        ma_add(scratch, dest, dest);
         // Do a trial subtraction, this is the same operation as cmp, but we store
         // the dest.
-        ma_sub(dest, Imm32(mask), scratch2, SetCC);
+        ma_sub(dest, Imm32(mask), scratch, scratch2, SetCC);
         // If (sum - C) > 0, store sum - C back into sum, thus performing a modulus.
-        ma_mov(scratch2, dest, LeaveCC, NotSigned);
+        ma_mov(scratch, dest, LeaveCC, NotSigned);
         // Get rid of the bits that we extracted before, and set the condition codes.
         as_mov(tmp, lsr(tmp, shift), SetCC);
         // If the shift produced zero, finish, otherwise, continue in the loop.
         ma_b(&head, NonZero);
     }
 
     // Check the hold to see if we need to negate the result. Hold can only be
     // 1 or -1, so this will never set the 0 flag.
-    ma_cmp(hold, Imm32(0));
+    as_cmp(hold, Imm8(0));
     // If the hold was non-zero, negate the result to be in line with what JS
     // wants this will set the condition codes if we try to negate.
-    ma_rsb(Imm32(0), dest, SetCC, Signed);
+    as_rsb(dest, dest, Imm8(0), SetCC, Signed);
     // Since the Zero flag is not set by the compare, we can *only* set the Zero
     // flag in the rsb, so Zero is set iff we negated zero (e.g. the result of
     // the computation was -0.0).
 }
 
 void
-MacroAssemblerARM::ma_smod(Register num, Register div, Register dest)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_smod(Register num, Register div, Register dest, AutoRegisterScope& scratch)
+{
     as_sdiv(scratch, num, div);
     as_mls(dest, num, scratch, div);
 }
 
 void
-MacroAssemblerARM::ma_umod(Register num, Register div, Register dest)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_umod(Register num, Register div, Register dest, AutoRegisterScope& scratch)
+{
     as_udiv(scratch, num, div);
     as_mls(dest, num, scratch, div);
 }
 
 // Division
 void
 MacroAssemblerARM::ma_sdiv(Register num, Register div, Register dest, Condition cond)
 {
@@ -1026,60 +1016,52 @@ MacroAssemblerARM::ma_udiv(Register num,
 // Miscellaneous instructions.
 void
 MacroAssemblerARM::ma_clz(Register src, Register dest, Condition cond)
 {
     as_clz(dest, src, cond);
 }
 
 void
-MacroAssemblerARM::ma_ctz(Register src, Register dest)
+MacroAssemblerARM::ma_ctz(Register src, Register dest, AutoRegisterScope& scratch)
 {
     // int c = __clz(a & -a);
     // return a ? 31 - c : c;
-
-    ScratchRegisterScope scratch(asMasm());
     as_rsb(scratch, src, Imm8(0), SetCC);
     as_and(dest, src, O2Reg(scratch), LeaveCC);
     as_clz(dest, dest);
     as_rsb(dest, dest, Imm8(0x1F), LeaveCC, Assembler::NotEqual);
 }
 
 // Memory.
 // Shortcut for when we know we're transferring 32 bits of data.
 void
 MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Imm32 offset, Register rt,
-                          Index mode, Assembler::Condition cc)
-{
-    ma_dataTransferN(ls, 32, true, rn, offset, rt, mode, cc);
-}
-
-void
-MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Register rm, Register rt,
-                          Index mode, Assembler::Condition cc)
-{
-    MOZ_CRASH("Feature NYI");
+                          AutoRegisterScope& scratch, Index mode, Assembler::Condition cc)
+{
+    ma_dataTransferN(ls, 32, true, rn, offset, rt, scratch, mode, cc);
+}
+
+void
+MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt, const Address& addr,
+                          AutoRegisterScope& scratch, Index mode, Condition cc)
+{
+    ma_dataTransferN(ls, 32, true, addr.base, Imm32(addr.offset), rt, scratch, mode, cc);
 }
 
 void
 MacroAssemblerARM::ma_str(Register rt, DTRAddr addr, Index mode, Condition cc)
 {
     as_dtr(IsStore, 32, mode, rt, addr, cc);
 }
 
 void
-MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt, const Address& addr, Index mode, Condition cc)
-{
-    ma_dataTransferN(ls, 32, true, addr.base, Imm32(addr.offset), rt, mode, cc);
-}
-
-void
-MacroAssemblerARM::ma_str(Register rt, const Address& addr, Index mode, Condition cc)
-{
-    ma_dtr(IsStore, rt, addr, mode, cc);
+MacroAssemblerARM::ma_str(Register rt, const Address& addr, AutoRegisterScope& scratch, Index mode, Condition cc)
+{
+    ma_dtr(IsStore, rt, addr, scratch, mode, cc);
 }
 
 void
 MacroAssemblerARM::ma_strd(Register rt, DebugOnly<Register> rt2, EDtrAddr addr, Index mode, Condition cc)
 {
     MOZ_ASSERT((rt.code() & 1) == 0);
     MOZ_ASSERT(rt2.value.code() == rt.code() + 1);
     as_extdtr(IsStore, 64, true, mode, rt, addr, cc);
@@ -1087,19 +1069,19 @@ MacroAssemblerARM::ma_strd(Register rt, 
 
 void
 MacroAssemblerARM::ma_ldr(DTRAddr addr, Register rt, Index mode, Condition cc)
 {
     as_dtr(IsLoad, 32, mode, rt, addr, cc);
 }
 
 void
-MacroAssemblerARM::ma_ldr(const Address& addr, Register rt, Index mode, Condition cc)
-{
-    ma_dtr(IsLoad, rt, addr, mode, cc);
+MacroAssemblerARM::ma_ldr(const Address& addr, Register rt, AutoRegisterScope& scratch, Index mode, Condition cc)
+{
+    ma_dtr(IsLoad, rt, addr, scratch, mode, cc);
 }
 
 void
 MacroAssemblerARM::ma_ldrb(DTRAddr addr, Register rt, Index mode, Condition cc)
 {
     as_dtr(IsLoad, 8, mode, rt, addr, cc);
 }
 
@@ -1142,50 +1124,64 @@ void
 MacroAssemblerARM::ma_strb(Register rt, DTRAddr addr, Index mode, Condition cc)
 {
     as_dtr(IsStore, 8, mode, rt, addr, cc);
 }
 
 // Specialty for moving N bits of data, where n == 8,16,32,64.
 BufferOffset
 MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
-                                    Register rn, Register rm, Register rt,
-                                    Index mode, Assembler::Condition cc, unsigned shiftAmount)
-{
+                                    Register rn, Register rm, Register rt, AutoRegisterScope& scratch,
+                                    Index mode, Assembler::Condition cc, Scale scale)
+{
+    MOZ_ASSERT(size == 8 || size == 16 || size == 32 || size == 64);
+
     if (size == 32 || (size == 8 && !IsSigned))
-        return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, shiftAmount)), cc);
-
-    ScratchRegisterScope scratch(asMasm());
-
-    if (shiftAmount != 0) {
-        MOZ_ASSERT(rn != scratch);
-        MOZ_ASSERT(rt != scratch);
-        ma_lsl(Imm32(shiftAmount), rm, scratch);
+        return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, scale)), cc);
+
+    if (scale != TimesOne) {
+        ma_lsl(Imm32(scale), rm, scratch);
         rm = scratch;
     }
 
     return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)), cc);
 }
 
+// No scratch register is required if scale is TimesOne.
 BufferOffset
 MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
-                                    Register rn, Imm32 offset, Register rt,
+                                    Register rn, Register rm, Register rt,
                                     Index mode, Assembler::Condition cc)
 {
+    MOZ_ASSERT(size == 8 || size == 16 || size == 32 || size == 64);
+    if (size == 32 || (size == 8 && !IsSigned))
+        return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, TimesOne)), cc);
+    return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)), cc);
+}
+
+
+BufferOffset
+MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
+                                    Register rn, Imm32 offset, Register rt, AutoRegisterScope& scratch,
+                                    Index mode, Assembler::Condition cc)
+{
+    MOZ_ASSERT(!(ls == IsLoad && mode == PostIndex && rt == pc),
+               "Large-offset PostIndex loading into PC requires special logic: see ma_popn_pc().");
+
     int off = offset.value;
 
     // We can encode this as a standard ldr.
     if (size == 32 || (size == 8 && !IsSigned) ) {
         if (off < 4096 && off > -4096) {
             // This encodes as a single instruction, Emulating mode's behavior
             // in a multi-instruction sequence is not necessary.
             return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrOffImm(off)), cc);
         }
 
-        // We cannot encode this offset in a a single ldr. For mode == index,
+        // We cannot encode this offset in a single ldr. For mode == index,
         // try to encode it as |add scratch, base, imm; ldr dest, [scratch, +offset]|.
         // This does not wark for mode == PreIndex or mode == PostIndex.
         // PreIndex is simple, just do the add into the base register first,
         // then do a PreIndex'ed load. PostIndexed loads can be tricky.
         // Normally, doing the load with an index of 0, then doing an add would
         // work, but if the destination is the PC, you don't get to execute the
         // instruction after the branch, which will lead to the base register
         // not being updated correctly. Explicitly handle this case, without
@@ -1193,56 +1189,23 @@ MacroAssemblerARM::ma_dataTransferN(Load
 
         // mode == Offset
         //  add   scratch, base, offset_hi
         //  ldr   dest, [scratch, +offset_lo]
         //
         // mode == PreIndex
         //  add   base, base, offset_hi
         //  ldr   dest, [base, +offset_lo]!
-        //
-        // mode == PostIndex, dest == pc
-        //  ldr   scratch, [base]
-        //  add   base, base, offset_hi
-        //  add   base, base, offset_lo
-        //  mov   dest, scratch
-        // PostIndex with the pc as the destination needs to be handled
-        // specially, since in the code below, the write into 'dest' is going to
-        // alter the control flow, so the following instruction would never get
-        // emitted.
-        //
-        // mode == PostIndex, dest != pc
-        //  ldr   dest, [base], offset_lo
-        //  add   base, base, offset_hi
-
-        if (rt == pc && mode == PostIndex && ls == IsLoad) {
-            ScratchRegisterScope scratch(asMasm());
-            ma_mov(rn, scratch);
-            ma_alu(rn, offset, rn, OpAdd);
-            return as_dtr(IsLoad, size, Offset, pc, DTRAddr(scratch, DtrOffImm(0)), cc);
-        }
-
-        // Often this code is called with rt as the ScratchRegister.
-        // The register is logically owned by the caller, so we cannot ask
-        // for exclusive ownership here. If full checking is desired,
-        // this function should take an explicit scratch register argument.
-        const Register& scratch = ScratchRegister;
-        MOZ_ASSERT(rn != scratch);
 
         int bottom = off & 0xfff;
         int neg_bottom = 0x1000 - bottom;
-        // For a regular offset, base == ScratchRegister does what we want.
-        // Modify the scratch register, leaving the actual base unscathed.
-        Register base = scratch;
-        // For the preindex case, we want to just re-use rn as the base
-        // register, so when the base register is updated *before* the load, rn
-        // is updated.
-        if (mode == PreIndex)
-            base = rn;
+
+        MOZ_ASSERT(rn != scratch);
         MOZ_ASSERT(mode != PostIndex);
+
         // At this point, both off - bottom and off + neg_bottom will be
         // reasonable-ish quantities.
         //
         // Note a neg_bottom of 0x1000 can not be encoded as an immediate
         // negative offset in the instruction and this occurs when bottom is
         // zero, so this case is guarded against below.
         if (off < 0) {
             Operand2 sub_off = Imm8(-(off - bottom)); // sub_off = bottom - off
@@ -1279,18 +1242,16 @@ MacroAssemblerARM::ma_dataTransferN(Load
                 as_add(scratch, rn, sub_off, LeaveCC,  cc);
                 return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(-neg_bottom)), cc);
             }
         }
 
         ma_mov(offset, scratch);
         return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(scratch, LSL, 0)));
     } else {
-        ScratchRegisterScope scratch(asMasm());
-
         // Should attempt to use the extended load/store instructions.
         if (off < 256 && off > -256)
             return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffImm(off)), cc);
 
         // We cannot encode this offset in a single extldr. Try to encode it as
         // an add scratch, base, imm; extldr dest, [scratch, +offset].
         int bottom = off & 0xff;
         int neg_bottom = 0x100 - bottom;
@@ -1346,31 +1307,48 @@ MacroAssemblerARM::ma_dataTransferN(Load
         ma_mov(offset, scratch);
         return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(scratch)), cc);
     }
 }
 
 void
 MacroAssemblerARM::ma_pop(Register r)
 {
-    ma_dtr(IsLoad, sp, Imm32(4), r, PostIndex);
+    as_dtr(IsLoad, 32, PostIndex, r, DTRAddr(sp, DtrOffImm(4)));
+}
+
+void
+MacroAssemblerARM::ma_popn_pc(Imm32 n, AutoRegisterScope& scratch, AutoRegisterScope& scratch2)
+{
+    // pc <- [sp]; sp += n
+    int32_t nv = n.value;
+
+    if (nv < 4096 && nv >= -4096) {
+        as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(nv)));
+    } else {
+        ma_mov(sp, scratch);
+        ma_add(Imm32(n), sp, scratch2);
+        as_dtr(IsLoad, 32, Offset, pc, DTRAddr(scratch, DtrOffImm(0)));
+    }
 }
 
 void
 MacroAssemblerARM::ma_push(Register r)
 {
-    // Pushing sp is not well defined: use two instructions.
-    if (r == sp) {
-        ScratchRegisterScope scratch(asMasm());
-        ma_mov(sp, scratch);
-        ma_dtr(IsStore, sp, Imm32(-4), scratch, PreIndex);
-        return;
-    }
-
-    ma_dtr(IsStore, sp, Imm32(-4), r, PreIndex);
+    MOZ_ASSERT(r != sp, "Use ma_push_sp().");
+    as_dtr(IsStore, 32, PreIndex, r, DTRAddr(sp, DtrOffImm(-4)));
+}
+
+void
+MacroAssemblerARM::ma_push_sp(Register r, AutoRegisterScope& scratch)
+{
+    // Pushing sp is not well-defined: use two instructions.
+    MOZ_ASSERT(r == sp);
+    ma_mov(sp, scratch);
+    as_dtr(IsStore, 32, PreIndex, scratch, DTRAddr(sp, DtrOffImm(-4)));
 }
 
 void
 MacroAssemblerARM::ma_vpop(VFPRegister r)
 {
     startFloatTransferM(IsLoad, sp, IA, WriteBack);
     transferFloatReg(r);
     finishFloatTransfer();
@@ -1720,26 +1698,25 @@ MacroAssemblerARM::ma_vxfer(Register src
 
 void
 MacroAssemblerARM::ma_vxfer(Register src1, Register src2, FloatRegister dest, Condition cc)
 {
     as_vxfer(src1, src2, VFPRegister(dest), CoreToFloat, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vdtr(LoadStore ls, const Address& addr, VFPRegister rt, Condition cc)
+MacroAssemblerARM::ma_vdtr(LoadStore ls, const Address& addr, VFPRegister rt,
+                           AutoRegisterScope& scratch, Condition cc)
 {
     int off = addr.offset;
     MOZ_ASSERT((off & 3) == 0);
     Register base = addr.base;
     if (off > -1024 && off < 1024)
         return as_vdtr(ls, rt, Operand(addr).toVFPAddr(), cc);
 
-    ScratchRegisterScope scratch(asMasm());
-
     // We cannot encode this offset in a a single ldr. Try to encode it as an
     // add scratch, base, imm; ldr dest, [scratch, +offset].
     int bottom = off & (0xff << 2);
     int neg_bottom = (0x100 << 2) - bottom;
     // At this point, both off - bottom and off + neg_bottom will be
     // reasonable-ish quantities.
     //
     // Note a neg_bottom of 0x400 can not be encoded as an immediate negative
@@ -1775,60 +1752,70 @@ MacroAssemblerARM::ma_vdtr(LoadStore ls,
         if (!sub_off.invalid && bottom != 0) {
             // Guarded against by: bottom != 0
             MOZ_ASSERT(neg_bottom < 0x400);
             // sub_off = neg_bottom + off
             as_add(scratch, base, sub_off, LeaveCC, cc);
             return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(-neg_bottom)), cc);
         }
     }
-    ma_add(base, Imm32(off), scratch, LeaveCC, cc);
+
+    // Safe to use scratch as dest, since ma_add() overwrites dest at the end
+    // and can't use it as internal scratch since it may also == base.
+    ma_add(base, Imm32(off), scratch, scratch, LeaveCC, cc);
     return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(0)), cc);
 }
 
 BufferOffset
 MacroAssemblerARM::ma_vldr(VFPAddr addr, VFPRegister dest, Condition cc)
 {
     return as_vdtr(IsLoad, dest, addr, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, Condition cc)
-{
-    return ma_vdtr(IsLoad, addr, dest, cc);
+MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, AutoRegisterScope& scratch, Condition cc)
+{
+    return ma_vdtr(IsLoad, addr, dest, scratch, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, int32_t shift,
-                           Condition cc)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch,
+                           int32_t shift, Condition cc)
+{
     as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
-    return ma_vldr(Address(scratch, 0), src, cc);
+    return as_vdtr(IsLoad, src, Operand(Address(scratch, 0)).toVFPAddr(), cc);
 }
 
 BufferOffset
 MacroAssemblerARM::ma_vstr(VFPRegister src, VFPAddr addr, Condition cc)
 {
     return as_vdtr(IsStore, src, addr, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr, Condition cc)
-{
-    return ma_vdtr(IsStore, addr, src, cc);
+MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr, AutoRegisterScope& scratch, Condition cc)
+{
+    return ma_vdtr(IsStore, addr, src, scratch, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
-                           int32_t offset, Condition cc)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch,
+                           AutoRegisterScope& scratch2, int32_t shift, int32_t offset, Condition cc)
+{
     as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
-    return ma_vstr(src, Address(scratch, offset), cc);
+    return ma_vstr(src, Address(scratch, offset), scratch2, cc);
+}
+
+// Without an offset, no second scratch register is necessary.
+BufferOffset
+MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch,
+                           int32_t shift, Condition cc)
+{
+    as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
+    return as_vdtr(IsStore, src, Operand(Address(scratch, 0)).toVFPAddr(), cc);
 }
 
 bool
 MacroAssemblerARMCompat::buildOOLFakeExitFrame(void* fakeReturnAddr)
 {
     DebugOnly<uint32_t> initialDepth = asMasm().framePushed();
     uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS,
                                               ExitFrameLayout::Size());
@@ -1892,117 +1879,126 @@ MacroAssemblerARMCompat::movePtr(wasm::S
 {
     append(AsmJSAbsoluteAddress(CodeOffset(currentOffset()), imm));
     ma_movPatchable(Imm32(-1), dest, Always);
 }
 
 void
 MacroAssemblerARMCompat::load8ZeroExtend(const Address& address, Register dest)
 {
-    ma_dataTransferN(IsLoad, 8, false, address.base, Imm32(address.offset), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsLoad, 8, false, address.base, Imm32(address.offset), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::load8ZeroExtend(const BaseIndex& src, Register dest)
 {
     Register base = src.base;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
 
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
     if (src.offset == 0) {
         ma_ldrb(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
     } else {
-        ScratchRegisterScope scratch(asMasm());
-        ma_add(base, Imm32(src.offset), scratch);
+        ma_add(base, Imm32(src.offset), scratch, scratch2);
         ma_ldrb(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest);
     }
 }
 
 void
 MacroAssemblerARMCompat::load8SignExtend(const Address& address, Register dest)
 {
-    ma_dataTransferN(IsLoad, 8, true, address.base, Imm32(address.offset), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsLoad, 8, true, address.base, Imm32(address.offset), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::load8SignExtend(const BaseIndex& src, Register dest)
 {
     Register index = src.index;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     // ARMv7 does not have LSL on an index register with an extended load.
     if (src.scale != TimesOne) {
         ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
         index = scratch;
     }
 
     if (src.offset != 0) {
         if (index != scratch) {
             ma_mov(index, scratch);
             index = scratch;
         }
-        ma_add(Imm32(src.offset), index);
+        ma_add(Imm32(src.offset), index, scratch2);
     }
     ma_ldrsb(EDtrAddr(src.base, EDtrOffReg(index)), dest);
 }
 
 void
 MacroAssemblerARMCompat::load16ZeroExtend(const Address& address, Register dest)
 {
-    ma_dataTransferN(IsLoad, 16, false, address.base, Imm32(address.offset), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsLoad, 16, false, address.base, Imm32(address.offset), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::load16ZeroExtend(const BaseIndex& src, Register dest)
 {
     Register index = src.index;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     // ARMv7 does not have LSL on an index register with an extended load.
     if (src.scale != TimesOne) {
         ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
         index = scratch;
     }
 
     if (src.offset != 0) {
         if (index != scratch) {
             ma_mov(index, scratch);
             index = scratch;
         }
-        ma_add(Imm32(src.offset), index);
+        ma_add(Imm32(src.offset), index, scratch2);
     }
     ma_ldrh(EDtrAddr(src.base, EDtrOffReg(index)), dest);
 }
 
 void
 MacroAssemblerARMCompat::load16SignExtend(const Address& address, Register dest)
 {
-    ma_dataTransferN(IsLoad, 16, true, address.base, Imm32(address.offset), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsLoad, 16, true, address.base, Imm32(address.offset), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::load16SignExtend(const BaseIndex& src, Register dest)
 {
     Register index = src.index;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     // We don't have LSL on index register yet.
     if (src.scale != TimesOne) {
         ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
         index = scratch;
     }
 
     if (src.offset != 0) {
         if (index != scratch) {
             ma_mov(index, scratch);
             index = scratch;
         }
-        ma_add(Imm32(src.offset), index);
+        ma_add(Imm32(src.offset), index, scratch2);
     }
     ma_ldrsh(EDtrAddr(src.base, EDtrOffReg(index)), dest);
 }
 
 void
 MacroAssemblerARMCompat::load32(const Address& address, Register dest)
 {
     loadPtr(address, dest);
@@ -2018,34 +2014,35 @@ void
 MacroAssemblerARMCompat::load32(AbsoluteAddress address, Register dest)
 {
     loadPtr(address, dest);
 }
 
 void
 MacroAssemblerARMCompat::loadPtr(const Address& address, Register dest)
 {
-    ma_ldr(address, dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_ldr(address, dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::loadPtr(const BaseIndex& src, Register dest)
 {
     Register base = src.base;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
 
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
     if (src.offset != 0) {
-        ScratchRegisterScope scratch(asMasm());
-        ma_mov(base, scratch);
-        ma_add(Imm32(src.offset), scratch);
+        ma_add(base, Imm32(src.offset), scratch, scratch2);
         ma_ldr(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest);
-        return;
+    } else {
+        ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
     }
-
-    ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
 }
 
 void
 MacroAssemblerARMCompat::loadPtr(AbsoluteAddress address, Register dest)
 {
     MOZ_ASSERT(dest != pc); // Use dest as a scratch register.
     movePtr(ImmWord(uintptr_t(address.addr)), dest);
     loadPtr(Address(dest, 0), dest);
@@ -2057,160 +2054,202 @@ MacroAssemblerARMCompat::loadPtr(wasm::S
     MOZ_ASSERT(dest != pc); // Use dest as a scratch register.
     movePtr(address, dest);
     loadPtr(Address(dest, 0), dest);
 }
 
 void
 MacroAssemblerARMCompat::loadPrivate(const Address& address, Register dest)
 {
-    ma_ldr(ToPayload(address), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_ldr(ToPayload(address), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::loadDouble(const Address& address, FloatRegister dest)
 {
-    ma_vldr(address, dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_vldr(address, dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::loadDouble(const BaseIndex& src, FloatRegister dest)
 {
     // VFP instructions don't even support register Base + register Index modes,
     // so just add the index, then handle the offset like normal.
     Register base = src.base;
     Register index = src.index;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
     int32_t offset = src.offset;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
     as_add(scratch, base, lsl(index, scale));
-    ma_vldr(Address(scratch, offset), dest);
+    ma_vldr(Address(scratch, offset), dest, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::loadFloatAsDouble(const Address& address, FloatRegister dest)
 {
+    ScratchRegisterScope scratch(asMasm());
+
     VFPRegister rt = dest;
-    ma_vldr(address, rt.singleOverlay());
+    ma_vldr(address, rt.singleOverlay(), scratch);
     as_vcvt(rt, rt.singleOverlay());
 }
 
 void
 MacroAssemblerARMCompat::loadFloatAsDouble(const BaseIndex& src, FloatRegister dest)
 {
     // VFP instructions don't even support register Base + register Index modes,
     // so just add the index, then handle the offset like normal.
     Register base = src.base;
     Register index = src.index;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
     int32_t offset = src.offset;
     VFPRegister rt = dest;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
     as_add(scratch, base, lsl(index, scale));
-    ma_vldr(Address(scratch, offset), rt.singleOverlay());
+    ma_vldr(Address(scratch, offset), rt.singleOverlay(), scratch2);
     as_vcvt(rt, rt.singleOverlay());
 }
 
 void
 MacroAssemblerARMCompat::loadFloat32(const Address& address, FloatRegister dest)
 {
-    ma_vldr(address, VFPRegister(dest).singleOverlay());
+    ScratchRegisterScope scratch(asMasm());
+    ma_vldr(address, VFPRegister(dest).singleOverlay(), scratch);
 }
 
 void
 MacroAssemblerARMCompat::loadFloat32(const BaseIndex& src, FloatRegister dest)
 {
     // VFP instructions don't even support register Base + register Index modes,
     // so just add the index, then handle the offset like normal.
     Register base = src.base;
     Register index = src.index;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
     int32_t offset = src.offset;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
     as_add(scratch, base, lsl(index, scale));
-    ma_vldr(Address(scratch, offset), VFPRegister(dest).singleOverlay());
+    ma_vldr(Address(scratch, offset), VFPRegister(dest).singleOverlay(), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::store8(Imm32 imm, const Address& address)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    SecondScratchRegisterScope scratch2(asMasm());
     ma_mov(imm, scratch2);
     store8(scratch2, address);
 }
 
 void
 MacroAssemblerARMCompat::store8(Register src, const Address& address)
 {
-    ma_dataTransferN(IsStore, 8, false, address.base, Imm32(address.offset), src);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsStore, 8, false, address.base, Imm32(address.offset), src, scratch);
 }
 
 void
 MacroAssemblerARMCompat::store8(Imm32 imm, const BaseIndex& dest)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    ma_mov(imm, scratch2);
-    store8(scratch2, dest);
+    Register base = dest.base;
+    uint32_t scale = Imm32::ShiftOf(dest.scale).value;
+
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    if (dest.offset != 0) {
+        ma_add(base, Imm32(dest.offset), scratch, scratch2);
+        ma_mov(imm, scratch2);
+        ma_strb(scratch2, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
+    } else {
+        ma_mov(imm, scratch2);
+        ma_strb(scratch2, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
+    }
 }
 
 void
 MacroAssemblerARMCompat::store8(Register src, const BaseIndex& dest)
 {
     Register base = dest.base;
     uint32_t scale = Imm32::ShiftOf(dest.scale).value;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     if (dest.offset != 0) {
-        ma_add(base, Imm32(dest.offset), scratch);
-        base = scratch;
+        ma_add(base, Imm32(dest.offset), scratch, scratch2);
+        ma_strb(src, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
+    } else {
+        ma_strb(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
     }
-    ma_strb(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
 }
 
 void
 MacroAssemblerARMCompat::store16(Imm32 imm, const Address& address)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    SecondScratchRegisterScope scratch2(asMasm());
     ma_mov(imm, scratch2);
     store16(scratch2, address);
 }
 
 void
 MacroAssemblerARMCompat::store16(Register src, const Address& address)
 {
-    ma_dataTransferN(IsStore, 16, false, address.base, Imm32(address.offset), src);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsStore, 16, false, address.base, Imm32(address.offset), src, scratch);
 }
 
 void
 MacroAssemblerARMCompat::store16(Imm32 imm, const BaseIndex& dest)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    Register index = dest.index;
+
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    // We don't have LSL on index register yet.
+    if (dest.scale != TimesOne) {
+        ma_lsl(Imm32::ShiftOf(dest.scale), index, scratch);
+        index = scratch;
+    }
+
+    if (dest.offset != 0) {
+        ma_add(index, Imm32(dest.offset), scratch, scratch2);
+        index = scratch;
+    }
+
     ma_mov(imm, scratch2);
-    store16(scratch2, dest);
+    ma_strh(scratch2, EDtrAddr(dest.base, EDtrOffReg(index)));
 }
 
 void
 MacroAssemblerARMCompat::store16(Register src, const BaseIndex& address)
 {
     Register index = address.index;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     // We don't have LSL on index register yet.
     if (address.scale != TimesOne) {
         ma_lsl(Imm32::ShiftOf(address.scale), index, scratch);
         index = scratch;
     }
 
     if (address.offset != 0) {
-        ma_add(index, Imm32(address.offset), scratch);
+        ma_add(index, Imm32(address.offset), scratch, scratch2);
         index = scratch;
     }
     ma_strh(src, EDtrAddr(address.base, EDtrOffReg(index)));
 }
 
 void
 MacroAssemblerARMCompat::store32(Register src, AbsoluteAddress address)
 {
@@ -2221,42 +2260,57 @@ void
 MacroAssemblerARMCompat::store32(Register src, const Address& address)
 {
     storePtr(src, address);
 }
 
 void
 MacroAssemblerARMCompat::store32(Imm32 src, const Address& address)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    move32(src, scratch2);
-    storePtr(scratch2, address);
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+    move32(src, scratch);
+    ma_str(scratch, address, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::store32(Imm32 imm, const BaseIndex& dest)
 {
+    Register base = dest.base;
+    uint32_t scale = Imm32::ShiftOf(dest.scale).value;
+
     ScratchRegisterScope scratch(asMasm());
-    ma_mov(imm, scratch);
-    store32(scratch, dest);
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    if (dest.offset != 0) {
+        ma_add(base, Imm32(dest.offset), scratch, scratch2);
+        ma_mov(imm, scratch2);
+        ma_str(scratch2, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
+    } else {
+        ma_mov(imm, scratch);
+        ma_str(scratch, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
+    }
+
 }
 
 void
 MacroAssemblerARMCompat::store32(Register src, const BaseIndex& dest)
 {
     Register base = dest.base;
     uint32_t scale = Imm32::ShiftOf(dest.scale).value;
 
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     if (dest.offset != 0) {
-        ma_add(base, Imm32(dest.offset), scratch2);
-        base = scratch2;
+        ma_add(base, Imm32(dest.offset), scratch, scratch2);
+        ma_str(src, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
+    } else {
+        ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
     }
-    ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
 }
 
 template <typename T>
 void
 MacroAssemblerARMCompat::storePtr(ImmWord imm, T address)
 {
     ScratchRegisterScope scratch(asMasm());
     movePtr(imm, scratch);
@@ -2286,31 +2340,32 @@ MacroAssemblerARMCompat::storePtr(ImmGCP
 }
 
 template void MacroAssemblerARMCompat::storePtr<Address>(ImmGCPtr imm, Address address);
 template void MacroAssemblerARMCompat::storePtr<BaseIndex>(ImmGCPtr imm, BaseIndex address);
 
 void
 MacroAssemblerARMCompat::storePtr(Register src, const Address& address)
 {
-    ma_str(src, address);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_str(src, address, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::storePtr(Register src, const BaseIndex& address)
 {
     store32(src, address);
 }
 
 void
 MacroAssemblerARMCompat::storePtr(Register src, AbsoluteAddress dest)
 {
     ScratchRegisterScope scratch(asMasm());
     movePtr(ImmWord(uintptr_t(dest.addr)), scratch);
-    storePtr(src, Address(scratch, 0));
+    ma_str(src, DTRAddr(scratch, DtrOffImm(0)));
 }
 
 // Note: this function clobbers the input register.
 void
 MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
 {
     if (HasVFPv3()) {
         Label notSplit;
@@ -2326,33 +2381,32 @@ MacroAssembler::clampDoubleToUint8(Float
         }
 
         // Move the fixed point value into an integer register.
         {
             ScratchFloat32Scope scratchFloat(*this);
             as_vxfer(output, InvalidReg, scratchFloat.uintOverlay(), FloatToCore);
         }
 
+        ScratchRegisterScope scratch(*this);
+
         // See if this value *might* have been an exact integer after adding
         // 0.5. This tests the 1/2 through 1/16,777,216th places, but 0.5 needs
         // to be tested out to the 1/140,737,488,355,328th place.
-        ma_tst(output, Imm32(0x00ffffff));
+        ma_tst(output, Imm32(0x00ffffff), scratch);
         // Convert to a uint8 by shifting out all of the fraction bits.
         ma_lsr(Imm32(24), output, output);
         // If any of the bottom 24 bits were non-zero, then we're good, since
         // this number can't be exactly XX.0
         ma_b(&notSplit, NonZero);
-        {
-            ScratchRegisterScope scratch(*this);
-            as_vxfer(scratch, InvalidReg, input, FloatToCore);
-            ma_cmp(scratch, Imm32(0));
-        }
+        as_vxfer(scratch, InvalidReg, input, FloatToCore);
+        as_cmp(scratch, Imm8(0));
         // If the lower 32 bits of the double were 0, then this was an exact number,
         // and it should be even.
-        ma_bic(Imm32(1), output, LeaveCC, Zero);
+        as_bic(output, output, Imm8(1), LeaveCC, Zero);
         bind(&notSplit);
     } else {
         ScratchDoubleScope scratchDouble(*this);
         MOZ_ASSERT(input != scratchDouble);
         loadConstantDouble(0.5, scratchDouble);
 
         Label outOfRange;
         ma_vcmpz(input);
@@ -2360,115 +2414,120 @@ MacroAssembler::clampDoubleToUint8(Float
         ma_vadd(input, scratchDouble, input);
         // Do the conversion to an integer.
         as_vcvt(VFPRegister(scratchDouble).uintOverlay(), VFPRegister(input));
         // Copy the converted value out.
         as_vxfer(output, InvalidReg, scratchDouble, FloatToCore);
         as_vmrs(pc);
         ma_mov(Imm32(0), output, Overflow);  // NaN => 0
         ma_b(&outOfRange, Overflow);  // NaN
-        ma_cmp(output, Imm32(0xff));
+        as_cmp(output, Imm8(0xff));
         ma_mov(Imm32(0xff), output, Above);
         ma_b(&outOfRange, Above);
         // Convert it back to see if we got the same value back.
         as_vcvt(scratchDouble, VFPRegister(scratchDouble).uintOverlay());
         // Do the check.
         as_vcmp(scratchDouble, input);
         as_vmrs(pc);
-        ma_bic(Imm32(1), output, LeaveCC, Zero);
+        as_bic(output, output, Imm8(1), LeaveCC, Zero);
         bind(&outOfRange);
     }
 }
 
 void
 MacroAssemblerARMCompat::cmp32(Register lhs, Imm32 rhs)
 {
-    MOZ_ASSERT(lhs != ScratchRegister);
-    ma_cmp(lhs, rhs);
+    ScratchRegisterScope scratch(asMasm());
+    ma_cmp(lhs, rhs, scratch);
 }
 
 void
 MacroAssemblerARMCompat::cmp32(Register lhs, Register rhs)
 {
     ma_cmp(lhs, rhs);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmWord rhs)
 {
-    MOZ_ASSERT(lhs != ScratchRegister);
-    ma_cmp(lhs, Imm32(rhs.value));
+    cmp32(lhs, Imm32(rhs.value));
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmPtr rhs)
 {
-    return cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
+    cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(Register lhs, Register rhs)
 {
     ma_cmp(lhs, rhs);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmGCPtr rhs)
 {
-    ma_cmp(lhs, rhs);
+    ScratchRegisterScope scratch(asMasm());
+    ma_cmp(lhs, rhs, scratch);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(Register lhs, Imm32 rhs)
 {
-    ma_cmp(lhs, rhs);
+    cmp32(lhs, rhs);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Register rhs)
 {
     ScratchRegisterScope scratch(asMasm());
-    loadPtr(lhs, scratch);
-    cmpPtr(scratch, rhs);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, rhs);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmWord rhs)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    loadPtr(lhs, scratch2);
-    ma_cmp(scratch2, Imm32(rhs.value));
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, Imm32(rhs.value), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmPtr rhs)
 {
     cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmGCPtr rhs)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    loadPtr(lhs, scratch2);
-    ma_cmp(scratch2, rhs);
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, rhs, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Imm32 rhs)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    loadPtr(lhs, scratch2);
-    ma_cmp(scratch2, rhs);
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, rhs, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::setStackArg(Register reg, uint32_t arg)
 {
-    ma_dataTransferN(IsStore, 32, true, sp, Imm32(arg * sizeof(intptr_t)), reg);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsStore, 32, true, sp, Imm32(arg * sizeof(intptr_t)), reg, scratch);
 }
 
 void
 MacroAssemblerARMCompat::minMaxDouble(FloatRegister srcDest, FloatRegister second, bool canBeNaN,
                                       bool isMax)
 {
     FloatRegister first = srcDest;
 
@@ -2605,17 +2664,18 @@ MacroAssemblerARMCompat::testBoolean(Ass
     return cond;
 }
 
 Assembler::Condition
 MacroAssemblerARMCompat::testDouble(Assembler::Condition cond, const ValueOperand& value)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual;
-    ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR));
+    ScratchRegisterScope scratch(asMasm());
+    ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR), scratch);
     return actual;
 }
 
 Assembler::Condition
 MacroAssemblerARMCompat::testNull(Assembler::Condition cond, const ValueOperand& value)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_NULL));
@@ -2965,40 +3025,43 @@ MacroAssemblerARMCompat::unboxNonDouble(
 {
     if (operand.payloadReg() != dest)
         ma_mov(operand.payloadReg(), dest);
 }
 
 void
 MacroAssemblerARMCompat::unboxNonDouble(const Address& src, Register dest)
 {
-    ma_ldr(ToPayload(src), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_ldr(ToPayload(src), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::unboxNonDouble(const BaseIndex& src, Register dest)
 {
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
     ma_alu(src.base, lsl(src.index, src.scale), scratch, OpAdd);
-    ma_ldr(Address(scratch, src.offset), dest);
+    ma_ldr(Address(scratch, src.offset), dest, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::unboxDouble(const ValueOperand& operand, FloatRegister dest)
 {
     MOZ_ASSERT(dest.isDouble());
     as_vxfer(operand.payloadReg(), operand.typeReg(),
              VFPRegister(dest), CoreToFloat);
 }
 
 void
 MacroAssemblerARMCompat::unboxDouble(const Address& src, FloatRegister dest)
 {
     MOZ_ASSERT(dest.isDouble());
-    ma_vldr(src, dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_vldr(src, dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::unboxValue(const ValueOperand& src, AnyRegister dest)
 {
     if (dest.isFloat()) {
         Label notInt32, end;
         asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
@@ -3031,17 +3094,17 @@ MacroAssemblerARMCompat::boxNonDouble(JS
     ma_mov(ImmType(type), dest.typeReg());
 }
 
 void
 MacroAssemblerARMCompat::boolValueToDouble(const ValueOperand& operand, FloatRegister dest)
 {
     VFPRegister d = VFPRegister(dest);
     loadConstantDouble(1.0, dest);
-    ma_cmp(operand.payloadReg(), Imm32(0));
+    as_cmp(operand.payloadReg(), Imm8(0));
     // If the source is 0, then subtract the dest from itself, producing 0.
     as_vsub(d, d, d, Equal);
 }
 
 void
 MacroAssemblerARMCompat::int32ValueToDouble(const ValueOperand& operand, FloatRegister dest)
 {
     VFPRegister vfpdest = VFPRegister(dest);
@@ -3053,17 +3116,17 @@ MacroAssemblerARMCompat::int32ValueToDou
     as_vcvt(vfpdest, scratch.sintOverlay());
 }
 
 void
 MacroAssemblerARMCompat::boolValueToFloat32(const ValueOperand& operand, FloatRegister dest)
 {
     VFPRegister d = VFPRegister(dest).singleOverlay();
     loadConstantFloat32(1.0, dest);
-    ma_cmp(operand.payloadReg(), Imm32(0));
+    as_cmp(operand.payloadReg(), Imm8(0));
     // If the source is 0, then subtract the dest from itself, producing 0.
     as_vsub(d, d, d, Equal);
 }
 
 void
 MacroAssemblerARMCompat::int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest)
 {
     // Transfer the integral value to a floating point register.
@@ -3089,27 +3152,31 @@ MacroAssemblerARMCompat::loadConstantFlo
 void
 MacroAssemblerARMCompat::loadInt32OrDouble(const Address& src, FloatRegister dest)
 {
     Label notInt32, end;
 
     // If it's an int, convert to a double.
     {
         ScratchRegisterScope scratch(asMasm());
-
-        ma_ldr(ToType(src), scratch);
+        SecondScratchRegisterScope scratch2(asMasm());
+
+        ma_ldr(ToType(src), scratch, scratch2);
         asMasm().branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
-        ma_ldr(ToPayload(src), scratch);
+        ma_ldr(ToPayload(src), scratch, scratch2);
         convertInt32ToDouble(scratch, dest);
         ma_b(&end);
     }
 
     // Not an int, just load as double.
     bind(&notInt32);
-    ma_vldr(src, dest);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_vldr(src, dest, scratch);
+    }
     bind(&end);
 }
 
 void
 MacroAssemblerARMCompat::loadInt32OrDouble(Register base, Register index,
                                            FloatRegister dest, int32_t shift)
 {
     Label notInt32, end;
@@ -3118,30 +3185,30 @@ MacroAssemblerARMCompat::loadInt32OrDoub
 
     ScratchRegisterScope scratch(asMasm());
 
     // If it's an int, convert it to double.
     ma_alu(base, lsl(index, shift), scratch, OpAdd);
 
     // Since we only have one scratch register, we need to stomp over it with
     // the tag.
-    ma_ldr(Address(scratch, NUNBOX32_TYPE_OFFSET), scratch);
+    ma_ldr(DTRAddr(scratch, DtrOffImm(NUNBOX32_TYPE_OFFSET)), scratch);
     asMasm().branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
 
     // Implicitly requires NUNBOX32_PAYLOAD_OFFSET == 0: no offset provided
     ma_ldr(DTRAddr(base, DtrRegImmShift(index, LSL, shift)), scratch);
     convertInt32ToDouble(scratch, dest);
     ma_b(&end);
 
     // Not an int, just load as double.
     bind(&notInt32);
     // First, recompute the offset that had been stored in the scratch register
     // since the scratch register was overwritten loading in the type.
     ma_alu(base, lsl(index, shift), scratch, OpAdd);
-    ma_vldr(Address(scratch, 0), dest);
+    ma_vldr(VFPAddr(scratch, VFPOffImm(0)), dest);
     bind(&end);
 }
 
 void
 MacroAssemblerARMCompat::loadConstantDouble(double dp, FloatRegister dest)
 {
     loadConstantDouble(wasm::RawF64(dp), dest);
 }
@@ -3174,24 +3241,26 @@ MacroAssemblerARMCompat::testDoubleTruth
     as_vmrs(pc);
     as_cmp(r0, O2Reg(r0), Overflow);
     return truthy ? NonZero : Zero;
 }
 
 Register
 MacroAssemblerARMCompat::extractObject(const Address& address, Register scratch)
 {
-    ma_ldr(ToPayload(address), scratch);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(ToPayload(address), scratch, scratch2);
     return scratch;
 }
 
 Register
 MacroAssemblerARMCompat::extractTag(const Address& address, Register scratch)
 {
-    ma_ldr(ToType(address), scratch);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(ToType(address), scratch, scratch2);
     return scratch;
 }
 
 Register
 MacroAssemblerARMCompat::extractTag(const BaseIndex& address, Register scratch)
 {
     ma_alu(address.base, lsl(address.index, address.scale), scratch, OpAdd, LeaveCC);
     return extractTag(Address(scratch, address.offset), scratch);
@@ -3215,18 +3284,19 @@ MacroAssemblerARMCompat::moveValue(const
 }
 
 /////////////////////////////////////////////////////////////////
 // X86/X64-common (ARM too now) interface.
 /////////////////////////////////////////////////////////////////
 void
 MacroAssemblerARMCompat::storeValue(ValueOperand val, const Address& dst)
 {
-    ma_str(val.payloadReg(), ToPayload(dst));
-    ma_str(val.typeReg(), ToType(dst));
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_str(val.payloadReg(), ToPayload(dst), scratch2);
+    ma_str(val.typeReg(), ToType(dst), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::storeValue(ValueOperand val, const BaseIndex& dest)
 {
     ScratchRegisterScope scratch(asMasm());
 
     if (isValueDTRDCandidate(val) && Abs(dest.offset) <= 255) {
@@ -3321,21 +3391,23 @@ MacroAssemblerARMCompat::loadValue(Addre
             transferReg(val.typeReg());
             finishDataTransfer();
             return;
         }
     }
     // Ensure that loading the payload does not erase the pointer to the Value
     // in memory.
     if (type.base != val.payloadReg()) {
-        ma_ldr(payload, val.payloadReg());
-        ma_ldr(type, val.typeReg());
+        SecondScratchRegisterScope scratch2(asMasm());
+        ma_ldr(payload, val.payloadReg(), scratch2);
+        ma_ldr(type, val.typeReg(), scratch2);
     } else {
-        ma_ldr(type, val.typeReg());
-        ma_ldr(payload, val.payloadReg());
+        SecondScratchRegisterScope scratch2(asMasm());
+        ma_ldr(type, val.typeReg(), scratch2);
+        ma_ldr(payload, val.payloadReg(), scratch2);
     }
 }
 
 void
 MacroAssemblerARMCompat::tagValue(JSValueType type, Register payload, ValueOperand dest)
 {
     MOZ_ASSERT(dest.typeReg() != dest.payloadReg());
     if (payload != dest.payloadReg())
@@ -3349,131 +3421,135 @@ MacroAssemblerARMCompat::pushValue(Value
     ma_push(val.typeReg());
     ma_push(val.payloadReg());
 }
 
 void
 MacroAssemblerARMCompat::pushValue(const Address& addr)
 {
     ScratchRegisterScope scratch(asMasm());
-    ma_ldr(ToType(addr), scratch);
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    ma_ldr(ToType(addr), scratch, scratch2);
     ma_push(scratch);
-    ma_ldr(ToPayloadAfterStackPush(addr), scratch);
+    ma_ldr(ToPayloadAfterStackPush(addr), scratch, scratch2);
     ma_push(scratch);
 }
 
 void
 MacroAssemblerARMCompat::popValue(ValueOperand val)
 {
     ma_pop(val.payloadReg());
     ma_pop(val.typeReg());
 }
 
 void
 MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     jsval_layout jv = JSVAL_TO_IMPL(val);
     if (val.isMarkable())
-        ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch2);
+        ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch);
     else
-        ma_mov(Imm32(jv.s.payload.i32), scratch2);
-    ma_str(scratch2, ToPayload(dest));
+        ma_mov(Imm32(jv.s.payload.i32), scratch);
+    ma_str(scratch, ToPayload(dest), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::storePayload(Register src, const Address& dest)
 {
-    ma_str(src, ToPayload(dest));
+    ScratchRegisterScope scratch(asMasm());
+    ma_str(src, ToPayload(dest), scratch);
 }
 
 void
 MacroAssemblerARMCompat::storePayload(const Value& val, const BaseIndex& dest)
 {
     unsigned shift = ScaleToShift(dest.scale);
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     jsval_layout jv = JSVAL_TO_IMPL(val);
     if (val.isMarkable())
         ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch);
     else
         ma_mov(Imm32(jv.s.payload.i32), scratch);
 
     // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
     // << shift + imm] cannot be encoded into a single instruction, and cannot
     // be integrated into the as_dtr call.
     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
 
     // If an offset is used, modify the base so that a [base + index << shift]
     // instruction format can be used.
     if (dest.offset != 0)
-        ma_add(dest.base, Imm32(dest.offset), dest.base);
+        ma_add(dest.base, Imm32(dest.offset), dest.base, scratch2);
 
     as_dtr(IsStore, 32, Offset, scratch,
            DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
 
     // Restore the original value of the base, if necessary.
     if (dest.offset != 0)
-        ma_sub(dest.base, Imm32(dest.offset), dest.base);
+        ma_sub(dest.base, Imm32(dest.offset), dest.base, scratch);
 }
 
 void
 MacroAssemblerARMCompat::storePayload(Register src, const BaseIndex& dest)
 {
     unsigned shift = ScaleToShift(dest.scale);
     MOZ_ASSERT(shift < 32);
 
+    ScratchRegisterScope scratch(asMasm());
+
     // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
     // << shift + imm] cannot be encoded into a single instruction, and cannot
     // be integrated into the as_dtr call.
     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
 
     // Save/restore the base if the BaseIndex has an offset, as above.
     if (dest.offset != 0)
-        ma_add(dest.base, Imm32(dest.offset), dest.base);
+        ma_add(dest.base, Imm32(dest.offset), dest.base, scratch);
 
     // Technically, shift > -32 can be handle by changing LSL to ASR, but should
     // never come up, and this is one less code path to get wrong.
     as_dtr(IsStore, 32, Offset, src, DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
 
     if (dest.offset != 0)
-        ma_sub(dest.base, Imm32(dest.offset), dest.base);
+        ma_sub(dest.base, Imm32(dest.offset), dest.base, scratch);
 }
 
 void
 MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const Address& dest)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    ma_mov(tag, scratch2);
-    ma_str(scratch2, ToType(dest));
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    ma_mov(tag, scratch);
+    ma_str(scratch, ToType(dest), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest)
 {
     Register base = dest.base;
     Register index = dest.index;
     unsigned shift = ScaleToShift(dest.scale);
-    MOZ_ASSERT(base != ScratchRegister);
-    MOZ_ASSERT(index != ScratchRegister);
-
-    // A value needs to be store a value int base + index << shift + 4.
-    // ARM cannot handle this in a single operand, so a temp register is
-    // required. However, the scratch register is presently in use to hold the
-    // immediate that is being stored into said memory location. Work around
-    // this by modifying the base so the valid [base + index << shift] format
-    // can be used, then restore it.
-    ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    MOZ_ASSERT(base != scratch && base != scratch2);
+    MOZ_ASSERT(index != scratch && index != scratch2);
+
+    ma_add(base, Imm32(dest.offset + NUNBOX32_TYPE_OFFSET), scratch2, scratch);
     ma_mov(tag, scratch);
-    ma_str(scratch, DTRAddr(base, DtrRegImmShift(index, LSL, shift)));
-    ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
+    ma_str(scratch, DTRAddr(scratch2, DtrRegImmShift(index, LSL, shift)));
 }
 
 void
 MacroAssemblerARM::ma_call(ImmPtr dest)
 {
     ma_movPatchable(dest, CallReg, Always);
     as_blx(CallReg);
 }
@@ -3523,78 +3599,94 @@ MacroAssemblerARMCompat::checkStackAlign
 }
 
 void
 MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler)
 {
     // Reserve space for exception information.
     int size = (sizeof(ResumeFromException) + 7) & ~7;
 
-    ma_sub(Imm32(size), sp);
+    Imm8 size8(size);
+    as_sub(sp, sp, size8);
     ma_mov(sp, r0);
 
     // Call the handler.
     asMasm().setupUnalignedABICall(r1);
     asMasm().passABIArg(r0);
     asMasm().callWithABI(handler);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
-    ma_ldr(Address(sp, offsetof(ResumeFromException, kind)), r0);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, kind)), r0, scratch);
+    }
+
     asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME),
                       &entryFrame);
     asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
     asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
     asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FORCED_RETURN),
                       &return_);
     asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
 
     breakpoint(); // Invalid kind.
 
     // No exception handler. Load the error value, load the new stack pointer
     // and return from the entry frame.
     bind(&entryFrame);
     moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch);
+    }
 
     // We're going to be returning by the ion calling convention, which returns
     // by ??? (for now, I think ldr pc, [sp]!)
     as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4)));
 
     // If we found a catch handler, this must be a baseline frame. Restore state
     // and jump to the catch block.
     bind(&catch_);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0, scratch);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11, scratch);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch);
+    }
     jump(r0);
 
     // If we found a finally block, this must be a baseline frame. Push two
     // values expected by JSOP_RETSUB: BooleanValue(true) and the exception.
     bind(&finally);
     ValueOperand exception = ValueOperand(r1, r2);
     loadValue(Operand(sp, offsetof(ResumeFromException, exception)), exception);
-
-    ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0, scratch);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11, scratch);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch);
+    }
 
     pushValue(BooleanValue(true));
     pushValue(exception);
     jump(r0);
 
     // Only used in debug mode. Return BaselineFrame->returnValue() to the
     // caller.
     bind(&return_);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11, scratch);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch);
+    }
     loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand);
     ma_mov(r11, sp);
     pop(r11);
 
     // If profiling is enabled, then update the lastProfilingFrame to refer to caller
     // frame before returning.
     {
         Label skipProfilingInstrumentation;
@@ -3606,29 +3698,34 @@ MacroAssemblerARMCompat::handleFailureWi
         bind(&skipProfilingInstrumentation);
     }
 
     ret();
 
     // If we are bailing out to baseline to handle an exception, jump to the
     // bailout tail stub.
     bind(&bailout);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), r2);
-    ma_mov(Imm32(BAILOUT_RETURN_OK), r0);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r1);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), r2, scratch);
+        ma_mov(Imm32(BAILOUT_RETURN_OK), r0);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r1, scratch);
+    }
     jump(r1);
 }
 
 Assembler::Condition
 MacroAssemblerARMCompat::testStringTruthy(bool truthy, const ValueOperand& value)
 {
     Register string = value.payloadReg();
     ScratchRegisterScope scratch(asMasm());
-    ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLength()), scratch);
-    ma_cmp(scratch, Imm32(0));
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLength()), scratch, scratch2);
+    as_cmp(scratch, Imm8(0));
     return truthy ? Assembler::NotEqual : Assembler::Equal;
 }
 
 void
 MacroAssemblerARMCompat::floor(FloatRegister input, Register output, Label* bail)
 {
     Label handleZero;
     Label handleNeg;
@@ -3651,31 +3748,31 @@ MacroAssemblerARMCompat::floor(FloatRegi
     ma_mov(output, output, SetCC);
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
     // Move the top word of the double into the output reg, if it is non-zero,
     // then the original value was -0.0.
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
     // Negative case, negate, then start dancing.
     ma_vneg(input, input);
     ma_vcvt_F64_U32(input, scratchDouble.uintOverlay());
     ma_vxfer(scratchDouble.uintOverlay(), output);
     ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble);
     compareDouble(scratchDouble, input);
-    ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
+    as_add(output, output, Imm8(1), LeaveCC, NotEqual);
     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
     // result will still be a negative number.
-    ma_rsb(output, Imm32(0), output, SetCC);
+    as_rsb(output, output, Imm8(0), SetCC);
     // Flip the negated input back to its original value.
     ma_vneg(input, input);
     // If the result looks non-negative, then this value didn't actually fit
     // into the int range, and special handling is required. Zero is also caught
     // by this case, but floor of a negative number should never be zero.
     ma_b(bail, NotSigned);
 
     bind(&fin);
@@ -3705,34 +3802,34 @@ MacroAssemblerARMCompat::floorf(FloatReg
     ma_mov(output, output, SetCC);
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
     // Move the top word of the double into the output reg, if it is non-zero,
     // then the original value was -0.0.
     as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
     // Negative case, negate, then start dancing.
     {
         ScratchFloat32Scope scratch(asMasm());
         ma_vneg_f32(input, input);
         ma_vcvt_F32_U32(input, scratch.uintOverlay());
         ma_vxfer(VFPRegister(scratch).uintOverlay(), output);
         ma_vcvt_U32_F32(scratch.uintOverlay(), scratch);
         compareFloat(scratch, input);
-        ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
+        as_add(output, output, Imm8(1), LeaveCC, NotEqual);
     }
     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
     // result will still be a negative number.
-    ma_rsb(output, Imm32(0), output, SetCC);
+    as_rsb(output, output, Imm8(0), SetCC);
     // Flip the negated input back to its original value.
     ma_vneg_f32(input, input);
     // If the result looks non-negative, then this value didn't actually fit
     // into the int range, and special handling is required. Zero is also caught
     // by this case, but floor of a negative number should never be zero.
     ma_b(bail, NotSigned);
 
     bind(&fin);
@@ -3768,28 +3865,28 @@ MacroAssemblerARMCompat::ceil(FloatRegis
     ma_neg(output, output, SetCC);
     ma_b(bail, NotSigned);
     ma_b(&fin);
 
     // Test for 0.0 / -0.0: if the top word of the input double is not zero,
     // then it was -0 and we need to bail out.
     bind(&handleZero);
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
     // non integer values, maybe bail if overflow.
     bind(&handlePos);
     ma_vcvt_F64_U32(input, ScratchUIntReg);
     ma_vxfer(ScratchUIntReg, output);
     ma_vcvt_U32_F64(ScratchUIntReg, scratchDouble);
     compareDouble(scratchDouble, input);
-    ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
+    as_add(output, output, Imm8(1), LeaveCC, NotEqual);
     // Bail out if the add overflowed or the result is non positive.
     ma_mov(output, output, SetCC);
     ma_b(bail, Signed);
     ma_b(bail, Zero);
 
     bind(&fin);
 }
 
@@ -3829,33 +3926,33 @@ MacroAssemblerARMCompat::ceilf(FloatRegi
         ma_b(bail, NotSigned);
         ma_b(&fin);
     }
 
     // Test for 0.0 / -0.0: if the top word of the input double is not zero,
     // then it was -0 and we need to bail out.
     bind(&handleZero);
     as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
     // non integer values, maybe bail if overflow.
     bind(&handlePos);
     {
         ScratchDoubleScope scratchDouble(asMasm());
         FloatRegister scratchFloat = scratchDouble.asSingle();
         FloatRegister scratchUInt = scratchDouble.uintOverlay();
 
         ma_vcvt_F32_U32(input, scratchUInt);
         ma_vxfer(scratchUInt, output);
         ma_vcvt_U32_F32(scratchUInt, scratchFloat);
         compareFloat(scratchFloat, input);
-        ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
+        as_add(output, output, Imm8(1), LeaveCC, NotEqual);
 
         // Bail on overflow or non-positive result.
         ma_mov(output, output, SetCC);
         ma_b(bail, Signed);
         ma_b(bail, Zero);
     }
 
     bind(&fin);
@@ -3921,17 +4018,17 @@ MacroAssemblerARMCompat::round(FloatRegi
     ma_mov(output, output, SetCC);
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
     // Move the top word of the double into the output reg, if it is non-zero,
     // then the original value was -0.0
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
     // Negative case, negate, then start dancing. This number may be positive,
     // since we added 0.5.
 
     // Add 0.5 to negative numbers, store the result into tmp
@@ -3941,20 +4038,20 @@ MacroAssemblerARMCompat::round(FloatRegi
     ma_vcvt_F64_U32(tmp, scratchDouble.uintOverlay());
     ma_vxfer(VFPRegister(scratchDouble).uintOverlay(), output);
 
     // -output is now a correctly rounded value, unless the original value was
     // exactly halfway between two integers, at which point, it has been rounded
     // away from zero, when it should be rounded towards \infty.
     ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble);
     compareDouble(scratchDouble, tmp);
-    ma_sub(output, Imm32(1), output, LeaveCC, Equal);
+    as_sub(output, output, Imm8(1), LeaveCC, Equal);
     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
     // result will still be a negative number.
-    ma_rsb(output, Imm32(0), output, SetCC);
+    as_rsb(output, output, Imm8(0), SetCC);
 
     // If the result looks non-negative, then this value didn't actually fit
     // into the int range, and special handling is required, or it was zero,
     // which means the result is actually -0.0 which also requires special
     // handling.
     ma_b(bail, NotSigned);
 
     bind(&fin);
@@ -3998,17 +4095,17 @@ MacroAssemblerARMCompat::roundf(FloatReg
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
 
     // Move the whole float32 into the output reg, if it is non-zero, then the
     // original value was -0.0.
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 0);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
 
     // Add 0.5 to negative numbers, storing the result into tmp.
     ma_vneg_f32(input, tmp);
     loadConstantFloat32(0.5f, scratchFloat);
@@ -4029,22 +4126,22 @@ MacroAssemblerARMCompat::roundf(FloatReg
     Label flipSign;
     ma_b(&flipSign, Equal);
 
     // -output is now a correctly rounded value, unless the original value was
     // exactly halfway between two integers, at which point, it has been rounded
     // away from zero, when it should be rounded towards \infty.
     ma_vcvt_U32_F32(tmp.uintOverlay(), tmp);
     compareFloat(tmp, scratchFloat);
-    ma_sub(output, Imm32(1), output, LeaveCC, Equal);
+    as_sub(output, output, Imm8(1), LeaveCC, Equal);
 
     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
     // result will still be a negative number.
     bind(&flipSign);
-    ma_rsb(output, Imm32(0), output, SetCC);
+    as_rsb(output, output, Imm8(0), SetCC);
 
     // If the result looks non-negative, then this value didn't actually fit
     // into the int range, and special handling is required, or it was zero,
     // which means the result is actually -0.0 which also requires special
     // handling.
     ma_b(bail, NotSigned);
 
     bind(&fin);
@@ -4067,29 +4164,33 @@ namespace jit {
 template<>
 Register
 MacroAssemblerARMCompat::computePointer<BaseIndex>(const BaseIndex& src, Register r)
 {
     Register base = src.base;
     Register index = src.index;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
     int32_t offset = src.offset;
+
+    ScratchRegisterScope scratch(asMasm());
+
     as_add(r, base, lsl(index, scale));
     if (offset != 0)
-        ma_add(r, Imm32(offset), r);
+        ma_add(r, Imm32(offset), r, scratch);
     return r;
 }
 
 template<>
 Register
 MacroAssemblerARMCompat::computePointer<Address>(const Address& src, Register r)
 {
+    ScratchRegisterScope scratch(asMasm());
     if (src.offset == 0)
         return src.base;
-    ma_add(src.base, Imm32(src.offset), r);
+    ma_add(src.base, Imm32(src.offset), r, scratch);
     return r;
 }
 
 } // namespace jit
 } // namespace js
 
 template<typename T>
 void
@@ -4132,17 +4233,17 @@ template<typename T>
 void
 MacroAssemblerARMCompat::compareExchangeARMv7(int nbytes, bool signExtend, const T& mem,
                                               Register oldval, Register newval, Register output)
 {
     Label again;
     Label done;
     ma_dmb(BarrierST);
 
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    SecondScratchRegisterScope scratch2(asMasm());
     Register ptr = computePointer(mem, scratch2);
 
     ScratchRegisterScope scratch(asMasm());
 
     bind(&again);
     switch (nbytes) {
       case 1:
         as_ldrexb(output, ptr);
@@ -4228,17 +4329,17 @@ template<typename T>
 void
 MacroAssemblerARMCompat::atomicExchangeARMv7(int nbytes, bool signExtend, const T& mem,
                                              Register value, Register output)
 {
     Label again;
     Label done;
     ma_dmb(BarrierST);
 
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    SecondScratchRegisterScope scratch2(asMasm());
     Register ptr = computePointer(mem, scratch2);
 
     ScratchRegisterScope scratch(asMasm());
 
     bind(&again);
     switch (nbytes) {
       case 1:
         as_ldrexb(output, ptr);
@@ -4337,17 +4438,17 @@ void
 MacroAssemblerARMCompat::atomicFetchOpARMv7(int nbytes, bool signExtend, AtomicOp op,
                                             const Register& value, const T& mem, Register flagTemp,
                                             Register output)
 {
     MOZ_ASSERT(flagTemp != InvalidReg);
 
     Label again;
 
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    SecondScratchRegisterScope scratch2(asMasm());
     Register ptr = computePointer(mem, scratch2);
 
     ma_dmb();
 
     ScratchRegisterScope scratch(asMasm());
 
     bind(&again);
     switch (nbytes) {
@@ -4454,17 +4555,17 @@ template<typename T>
 void
 MacroAssemblerARMCompat::atomicEffectOpARMv7(int nbytes, AtomicOp op, const Register& value,
                                              const T& mem, Register flagTemp)
 {
     MOZ_ASSERT(flagTemp != InvalidReg);
 
     Label again;
 
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    SecondScratchRegisterScope scratch2(asMasm());
     Register ptr = computePointer(mem, scratch2);
 
     ma_dmb();
 
     ScratchRegisterScope scratch(asMasm());
 
     bind(&again);
     switch (nbytes) {
@@ -4763,17 +4864,17 @@ MacroAssembler::PopRegsInMaskIgnore(Live
         freeStack(reservedG);
     }
     MOZ_ASSERT(diffG == 0);
 }
 
 void
 MacroAssembler::Push(Register reg)
 {
-    ma_push(reg);
+    push(reg);
     adjustFrame(sizeof(intptr_t));
 }
 
 void
 MacroAssembler::Push(const Imm32 imm)
 {
     push(imm);
     adjustFrame(sizeof(intptr_t));
@@ -4826,18 +4927,19 @@ MacroAssembler::Pop(const ValueOperand& 
 {
     popValue(val);
     adjustFrame(-sizeof(Value));
 }
 
 void
 MacroAssembler::reserveStack(uint32_t amount)
 {
+    ScratchRegisterScope scratch(*this);
     if (amount)
-        ma_sub(Imm32(amount), sp);
+        ma_sub(Imm32(amount), sp, scratch);
     adjustFrame(amount);
 }
 
 // ===============================================================
 // Simple call functions.
 
 CodeOffset
 MacroAssembler::call(Register reg)
@@ -4915,17 +5017,17 @@ MacroAssembler::thunkWithPatch()
 
     // Inhibit pools since these three words must be contiguous so that the offset
     // calculations below are valid.
     AutoForbidPools afp(this, 3);
 
     // When pc is used, the read value is the address of the instruction + 8.
     // This is exactly the address of the uint32 word we want to load.
     ScratchRegisterScope scratch(*this);
-    ma_ldr(Address(pc, 0), scratch);
+    ma_ldr(DTRAddr(pc, DtrOffImm(0)), scratch);
 
     // Branch by making pc the destination register.
     ma_add(pc, scratch, pc, LeaveCC, Always);
 
     // Allocate space which will be patched by patchThunk().
     CodeOffset u32Offset(currentOffset());
     writeInst(UINT32_MAX);
 
@@ -5000,17 +5102,17 @@ MacroAssembler::popReturnAddress()
 void
 MacroAssembler::setupUnalignedABICall(Register scratch)
 {
     setupABICall();
     dynamicAlignment_ = true;
 
     ma_mov(sp, scratch);
     // Force sp to be aligned.
-    ma_and(Imm32(~(ABIStackAlignment - 1)), sp, sp);
+    as_bic(sp, sp, Imm8(ABIStackAlignment - 1));
     ma_push(scratch);
 }
 
 void
 MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS)
 {
     MOZ_ASSERT(inCall_);
     uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
@@ -5099,17 +5201,20 @@ MacroAssembler::callWithABINoProfiler(Re
 }
 
 void
 MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
 {
     // Load the callee in r12, no instruction between the ldr and call should
     // clobber it. Note that we can't use fun.base because it may be one of the
     // IntArg registers clobbered before the call.
-    ma_ldr(fun, r12);
+    {
+        ScratchRegisterScope scratch(*this);
+        ma_ldr(fun, r12, scratch);
+    }
     uint32_t stackAdjust;
     callWithABIPre(&stackAdjust);
     call(r12);
     callWithABIPost(stackAdjust, result);
 }
 
 // ===============================================================
 // Jit Frames.
@@ -5137,17 +5242,17 @@ MacroAssembler::pushFakeReturnAddress(Re
 
 // ===============================================================
 // Branch functions
 
 void
 MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr, Register temp,
                                         Label* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
+    SecondScratchRegisterScope scratch2(*this);
 
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     MOZ_ASSERT(ptr != temp);
     MOZ_ASSERT(ptr != scratch2);
 
     ma_lsr(Imm32(gc::ChunkShift), ptr, scratch2);
     ma_lsl(Imm32(gc::ChunkShift), scratch2, scratch2);
     load32(Address(scratch2, gc::ChunkLocationOffset), scratch2);
@@ -5190,22 +5295,24 @@ MacroAssembler::branchTestValue(Conditio
     MOZ_ASSERT(cond == Equal || cond == NotEqual);
     // If cond == NotEqual, branch when a.payload != b.payload || a.tag !=
     // b.tag. If the payloads are equal, compare the tags. If the payloads are
     // not equal, short circuit true (NotEqual).
     //
     // If cand == Equal, branch when a.payload == b.payload && a.tag == b.tag.
     // If the payloads are equal, compare the tags. If the payloads are not
     // equal, short circuit false (NotEqual).
+    ScratchRegisterScope scratch(*this);
+
     jsval_layout jv = JSVAL_TO_IMPL(rhs);
     if (rhs.isMarkable())
-        ma_cmp(lhs.payloadReg(), ImmGCPtr(reinterpret_cast<gc::Cell*>(rhs.toGCThing())));
+        ma_cmp(lhs.payloadReg(), ImmGCPtr(reinterpret_cast<gc::Cell*>(rhs.toGCThing())), scratch);
     else
-        ma_cmp(lhs.payloadReg(), Imm32(jv.s.payload.i32));
-    ma_cmp(lhs.typeReg(), Imm32(jv.s.tag), Equal);
+        ma_cmp(lhs.payloadReg(), Imm32(jv.s.payload.i32), scratch);
+    ma_cmp(lhs.typeReg(), Imm32(jv.s.tag), scratch, Equal);
     ma_b(label, cond);
 }
 
 // ========================================================================
 // Memory access primitives.
 template <typename T>
 void
 MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
@@ -5241,31 +5348,35 @@ MacroAssemblerARM::emitUnalignedLoad(boo
                                      Register dest, unsigned offset)
 {
     // Preconditions.
     MOZ_ASSERT(ptr != tmp);
     MOZ_ASSERT(ptr != dest);
     MOZ_ASSERT(tmp != dest);
     MOZ_ASSERT(byteSize <= 4);
 
+    ScratchRegisterScope scratch(asMasm());
+
     for (unsigned i = 0; i < byteSize; i++) {
         // Only the last byte load shall be signed, if needed.
         bool signedByteLoad = isSigned && (i == byteSize - 1);
-        ma_dataTransferN(IsLoad, 8, signedByteLoad, ptr, Imm32(offset + i), i ? tmp : dest);
+        ma_dataTransferN(IsLoad, 8, signedByteLoad, ptr, Imm32(offset + i), i ? tmp : dest, scratch);
         if (i)
             as_orr(dest, dest, lsl(tmp, 8 * i));
     }
 }
 
 void
 MacroAssemblerARM::emitUnalignedStore(unsigned byteSize, Register ptr, Register val,
                                       unsigned offset)
 {
     // Preconditions.
     MOZ_ASSERT(ptr != val);
     MOZ_ASSERT(byteSize <= 4);
 
+    ScratchRegisterScope scratch(asMasm());
+
     for (unsigned i = 0; i < byteSize; i++) {
-        ma_dataTransferN(IsStore, 8 /* bits */, /* signed */ false, ptr, Imm32(offset + i), val);
+        ma_dataTransferN(IsStore, 8 /* bits */, /* signed */ false, ptr, Imm32(offset + i), val, scratch);
         if (i < byteSize - 1)
             ma_lsr(Imm32(8), val, val);
     }
 }
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -39,16 +39,21 @@ class MacroAssemblerARM : public Assembl
     // On ARM, some instructions require a second scratch register. This
     // register defaults to lr, since it's non-allocatable (as it can be
     // clobbered by some instructions). Allow the baseline compiler to override
     // this though, since baseline IC stubs rely on lr holding the return
     // address.
     Register secondScratchReg_;
 
   public:
+    Register getSecondScratchReg() const {
+        return secondScratchReg_;
+    }
+
+  public:
     // Higher level tag testing code.
     // TODO: Can probably remove the Operand versions.
     Operand ToPayload(Operand base) const {
         return Operand(Register::FromCode(base.base()), base.disp());
     }
     Address ToPayload(const Address& base) const {
         return base;
     }
@@ -98,22 +103,20 @@ class MacroAssemblerARM : public Assembl
     // Somewhat direct wrappers for the low-level assembler funcitons
     // bitops. Attempt to encode a virtual alu instruction using two real
     // instructions.
   private:
     bool alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op,
                  SBit s, Condition c);
 
   public:
+    void ma_alu(Register src1, Imm32 imm, Register dest, AutoRegisterScope& scratch,
+                ALUOp op, SBit s = LeaveCC, Condition c = Always);
     void ma_alu(Register src1, Operand2 op2, Register dest, ALUOp op,
                 SBit s = LeaveCC, Condition c = Always);
-    void ma_alu(Register src1, Imm32 imm, Register dest,
-                ALUOp op,
-                SBit s =  LeaveCC, Condition c = Always);
-
     void ma_alu(Register src1, Operand op2, Register dest, ALUOp op,
                 SBit s = LeaveCC, Condition c = Always);
     void ma_nop();
 
     void ma_movPatchable(Imm32 imm, Register dest, Assembler::Condition c);
     void ma_movPatchable(ImmPtr imm, Register dest, Assembler::Condition c);
 
     static void ma_mov_patch(Imm32 imm, Register dest, Assembler::Condition c,
@@ -136,193 +139,204 @@ class MacroAssemblerARM : public Assembl
     void ma_asr(Imm32 shift, Register src, Register dst);
     void ma_ror(Imm32 shift, Register src, Register dst);
     void ma_rol(Imm32 shift, Register src, Register dst);
 
     void ma_lsl(Register shift, Register src, Register dst);
     void ma_lsr(Register shift, Register src, Register dst);
     void ma_asr(Register shift, Register src, Register dst);
     void ma_ror(Register shift, Register src, Register dst);
-    void ma_rol(Register shift, Register src, Register dst);
+    void ma_rol(Register shift, Register src, Register dst, AutoRegisterScope& scratch);
 
     // Move not (dest <- ~src)
     void ma_mvn(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always);
 
     // Negate (dest <- -src) implemented as rsb dest, src, 0
     void ma_neg(Register src, Register dest,
                 SBit s = LeaveCC, Condition c = Always);
 
     // And
     void ma_and(Register src, Register dest,
                 SBit s = LeaveCC, Condition c = Always);
 
     void ma_and(Register src1, Register src2, Register dest,
                 SBit s = LeaveCC, Condition c = Always);
 
-    void ma_and(Imm32 imm, Register dest,
+    void ma_and(Imm32 imm, Register dest, AutoRegisterScope& scratch,
                 SBit s = LeaveCC, Condition c = Always);
 
-    void ma_and(Imm32 imm, Register src1, Register dest,
+    void ma_and(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch,
                 SBit s = LeaveCC, Condition c = Always);
 
     // Bit clear (dest <- dest & ~imm) or (dest <- src1 & ~src2)
-    void ma_bic(Imm32 imm, Register dest,
+    void ma_bic(Imm32 imm, Register dest, AutoRegisterScope& scratch,
                 SBit s = LeaveCC, Condition c = Always);
 
     // Exclusive or
     void ma_eor(Register src, Register dest,
                 SBit s = LeaveCC, Condition c = Always);
 
     void ma_eor(Register src1, Register src2, Register dest,
                 SBit s = LeaveCC, Condition c = Always);
 
-    void ma_eor(Imm32 imm, Register dest,
+    void ma_eor(Imm32 imm, Register dest, AutoRegisterScope& scratch,
                 SBit s = LeaveCC, Condition c = Always);
 
-    void ma_eor(Imm32 imm, Register src1, Register dest,
+    void ma_eor(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch,
                 SBit s = LeaveCC, Condition c = Always);
 
-
     // Or
     void ma_orr(Register src, Register dest,
                 SBit s = LeaveCC, Condition c = Always);
 
     void ma_orr(Register src1, Register src2, Register dest,
                 SBit s = LeaveCC, Condition c = Always);
 
-    void ma_orr(Imm32 imm, Register dest,
+    void ma_orr(Imm32 imm, Register dest, AutoRegisterScope& scratch,
                 SBit s = LeaveCC, Condition c = Always);
 
-    void ma_orr(Imm32 imm, Register src1, Register dest,
+    void ma_orr(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch,
                 SBit s = LeaveCC, Condition c = Always);
 
 
     // Arithmetic based ops.
     // Add with carry:
-    void ma_adc(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always);
+    void ma_adc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s = LeaveCC, Condition c = Always);
     void ma_adc(Register src, Register dest, SBit s = LeaveCC, Condition c = Always);
     void ma_adc(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always);
 
     // Add:
-    void ma_add(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always);
+    void ma_add(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s = LeaveCC, Condition c = Always);
     void ma_add(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always);
     void ma_add(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always);
     void ma_add(Register src1, Operand op, Register dest, SBit s = LeaveCC, Condition c = Always);
-    void ma_add(Register src1, Imm32 op, Register dest, SBit s = LeaveCC, Condition c = Always);
+    void ma_add(Register src1, Imm32 op, Register dest, AutoRegisterScope& scratch,
+                SBit s = LeaveCC, Condition c = Always);
 
     // Subtract with carry:
-    void ma_sbc(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always);
+    void ma_sbc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s = LeaveCC, Condition c = Always);
     void ma_sbc(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always);
     void ma_sbc(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always);
 
     // Subtract:
-    void ma_sub(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always);
+    void ma_sub(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s = LeaveCC, Condition c = Always);
     void ma_sub(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always);
     void ma_sub(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always);
     void ma_sub(Register src1, Operand op, Register dest, SBit s = LeaveCC, Condition c = Always);
-    void ma_sub(Register src1, Imm32 op, Register dest, SBit s = LeaveCC, Condition c = Always);
+    void ma_sub(Register src1, Imm32 op, Register dest, AutoRegisterScope& scratch,
+                SBit s = LeaveCC, Condition c = Always);
 
     // Reverse subtract:
-    void ma_rsb(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always);
+    void ma_rsb(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s = LeaveCC, Condition c = Always);
     void ma_rsb(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always);
     void ma_rsb(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always);
-    void ma_rsb(Register src1, Imm32 op2, Register dest, SBit s = LeaveCC, Condition c = Always);
+    void ma_rsb(Register src1, Imm32 op2, Register dest, AutoRegisterScope& scratch,
+                SBit s = LeaveCC, Condition c = Always);
 
     // Reverse subtract with carry:
-    void ma_rsc(Imm32 imm, Register dest, SBit s = LeaveCC, Condition c = Always);
+    void ma_rsc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s = LeaveCC, Condition c = Always);
     void ma_rsc(Register src1, Register dest, SBit s = LeaveCC, Condition c = Always);
     void ma_rsc(Register src1, Register src2, Register dest, SBit s = LeaveCC, Condition c = Always);
 
     // Compares/tests.
     // Compare negative (sets condition codes as src1 + src2 would):
-    void ma_cmn(Register src1, Imm32 imm, Condition c = Always);
+    void ma_cmn(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c = Always);
     void ma_cmn(Register src1, Register src2, Condition c = Always);
     void ma_cmn(Register src1, Operand op, Condition c = Always);
 
     // Compare (src - src2):
-    void ma_cmp(Register src1, Imm32 imm, Condition c = Always);
-    void ma_cmp(Register src1, ImmWord ptr, Condition c = Always);
-    void ma_cmp(Register src1, ImmGCPtr ptr, Condition c = Always);
-    void ma_cmp(Register src1, Operand op, Condition c = Always);
+    void ma_cmp(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c = Always);
+    void ma_cmp(Register src1, ImmTag tag, Condition c = Always);
+    void ma_cmp(Register src1, ImmWord ptr, AutoRegisterScope& scratch, Condition c = Always);
+    void ma_cmp(Register src1, ImmGCPtr ptr, AutoRegisterScope& scratch, Condition c = Always);
+    void ma_cmp(Register src1, Operand op, AutoRegisterScope& scratch, AutoRegisterScope& scratch2,
+                Condition c = Always);
     void ma_cmp(Register src1, Register src2, Condition c = Always);
 
     // Test for equality, (src1 ^ src2):
-    void ma_teq(Register src1, Imm32 imm, Condition c = Always);
+    void ma_teq(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c = Always);
     void ma_teq(Register src1, Register src2, Condition c = Always);
     void ma_teq(Register src1, Operand op, Condition c = Always);
 
     // Test (src1 & src2):
-    void ma_tst(Register src1, Imm32 imm, Condition c = Always);
+    void ma_tst(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c = Always);
     void ma_tst(Register src1, Register src2, Condition c = Always);
     void ma_tst(Register src1, Operand op, Condition c = Always);
 
     // Multiplies. For now, there are only two that we care about.
     void ma_mul(Register src1, Register src2, Register dest);
-    void ma_mul(Register src1, Imm32 imm, Register dest);
-    Condition ma_check_mul(Register src1, Register src2, Register dest, Condition cond);
-    Condition ma_check_mul(Register src1, Imm32 imm, Register dest, Condition cond);
+    void ma_mul(Register src1, Imm32 imm, Register dest, AutoRegisterScope& scratch);
+    Condition ma_check_mul(Register src1, Register src2, Register dest,
+                           AutoRegisterScope& scratch, Condition cond);
+    Condition ma_check_mul(Register src1, Imm32 imm, Register dest,
+                           AutoRegisterScope& scratch, Condition cond);
 
+    void ma_umull(Register src1, Imm32 imm, Register destHigh, Register destLow, AutoRegisterScope& scratch);
     void ma_umull(Register src1, Register src2, Register destHigh, Register destLow);
-    void ma_umull(Register src1, Imm32 imm, Register destHigh, Register destLow);
 
     // Fast mod, uses scratch registers, and thus needs to be in the assembler
     // implicitly assumes that we can overwrite dest at the beginning of the
     // sequence.
     void ma_mod_mask(Register src, Register dest, Register hold, Register tmp,
-                     int32_t shift);
+                     AutoRegisterScope& scratch, AutoRegisterScope& scratch2, int32_t shift);
 
     // Mod - depends on integer divide instructions being supported.
-    void ma_smod(Register num, Register div, Register dest);
-    void ma_umod(Register num, Register div, Register dest);
+    void ma_smod(Register num, Register div, Register dest, AutoRegisterScope& scratch);
+    void ma_umod(Register num, Register div, Register dest, AutoRegisterScope& scratch);
 
     // Division - depends on integer divide instructions being supported.
     void ma_sdiv(Register num, Register div, Register dest, Condition cond = Always);
     void ma_udiv(Register num, Register div, Register dest, Condition cond = Always);
     // Misc operations
     void ma_clz(Register src, Register dest, Condition cond = Always);
-    void ma_ctz(Register src, Register dest);
+    void ma_ctz(Register src, Register dest, AutoRegisterScope& scratch);
     // Memory:
     // Shortcut for when we know we're transferring 32 bits of data.
-    void ma_dtr(LoadStore ls, Register rn, Imm32 offset, Register rt,
+    void ma_dtr(LoadStore ls, Register rn, Imm32 offset, Register rt, AutoRegisterScope& scratch,
+                Index mode = Offset, Condition cc = Always);
+    void ma_dtr(LoadStore ls, Register rt, const Address& addr, AutoRegisterScope& scratch,
+                Index mode, Condition cc);
+
+    void ma_str(Register rt, DTRAddr addr, Index mode = Offset, Condition cc = Always);
+    void ma_str(Register rt, const Address& addr, AutoRegisterScope& scratch,
                 Index mode = Offset, Condition cc = Always);
 
-    void ma_dtr(LoadStore ls, Register rn, Register rm, Register rt,
+    void ma_ldr(DTRAddr addr, Register rt, Index mode = Offset, Condition cc = Always);
+    void ma_ldr(const Address& addr, Register rt, AutoRegisterScope& scratch,
                 Index mode = Offset, Condition cc = Always);
 
-
-    void ma_str(Register rt, DTRAddr addr, Index mode = Offset, Condition cc = Always);
-    void ma_str(Register rt, const Address& addr, Index mode = Offset, Condition cc = Always);
-    void ma_dtr(LoadStore ls, Register rt, const Address& addr, Index mode, Condition cc);
-
-    void ma_ldr(DTRAddr addr, Register rt, Index mode = Offset, Condition cc = Always);
-    void ma_ldr(const Address& addr, Register rt, Index mode = Offset, Condition cc = Always);
-
     void ma_ldrb(DTRAddr addr, Register rt, Index mode = Offset, Condition cc = Always);
     void ma_ldrh(EDtrAddr addr, Register rt, Index mode = Offset, Condition cc = Always);
     void ma_ldrsh(EDtrAddr addr, Register rt, Index mode = Offset, Condition cc = Always);
     void ma_ldrsb(EDtrAddr addr, Register rt, Index mode = Offset, Condition cc = Always);
     void ma_ldrd(EDtrAddr addr, Register rt, DebugOnly<Register> rt2, Index mode = Offset,
                  Condition cc = Always);
     void ma_strb(Register rt, DTRAddr addr, Index mode = Offset, Condition cc = Always);
     void ma_strh(Register rt, EDtrAddr addr, Index mode = Offset, Condition cc = Always);
     void ma_strd(Register rt, DebugOnly<Register> rt2, EDtrAddr addr, Index mode = Offset,
                  Condition cc = Always);
 
     // Specialty for moving N bits of data, where n == 8,16,32,64.
     BufferOffset ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
-                                  Register rn, Register rm, Register rt,
+                                  Register rn, Register rm, Register rt, AutoRegisterScope& scratch,
                                   Index mode = Offset, Condition cc = Always,
-                                  unsigned scale = TimesOne);
+                                  Scale scale = TimesOne);
 
     BufferOffset ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
-                                  Register rn, Imm32 offset, Register rt,
+                                  Register rn, Register rm, Register rt,
+                                  Index mode = Offset, Condition cc = Always);
+
+    BufferOffset ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
+                                  Register rn, Imm32 offset, Register rt, AutoRegisterScope& scratch,
                                   Index mode = Offset, Condition cc = Always);
 
     void ma_pop(Register r);
+    void ma_popn_pc(Imm32 n, AutoRegisterScope& scratch, AutoRegisterScope& scratch2);
     void ma_push(Register r);
+    void ma_push_sp(Register r, AutoRegisterScope& scratch);
 
     void ma_vpop(VFPRegister r);
     void ma_vpush(VFPRegister r);
 
     // Barriers.
     void ma_dmb(BarrierOption option=BarrierSY);
     void ma_dsb(BarrierOption option=BarrierSY);
 
@@ -392,27 +406,30 @@ class MacroAssemblerARM : public Assembl
     // Transfer (do not coerce) a double into a couple of gpr.
     void ma_vxfer(VFPRegister src, Register dest1, Register dest2, Condition cc = Always);
 
     // Transfer (do not coerce) a gpr into a float
     void ma_vxfer(Register src, FloatRegister dest, Condition cc = Always);
     // Transfer (do not coerce) a couple of gpr into a double
     void ma_vxfer(Register src1, Register src2, FloatRegister dest, Condition cc = Always);
 
-    BufferOffset ma_vdtr(LoadStore ls, const Address& addr, VFPRegister dest, Condition cc = Always);
+    BufferOffset ma_vdtr(LoadStore ls, const Address& addr, VFPRegister dest, AutoRegisterScope& scratch,
+                         Condition cc = Always);
 
     BufferOffset ma_vldr(VFPAddr addr, VFPRegister dest, Condition cc = Always);
-    BufferOffset ma_vldr(const Address& addr, VFPRegister dest, Condition cc = Always);
-    BufferOffset ma_vldr(VFPRegister src, Register base, Register index,
+    BufferOffset ma_vldr(const Address& addr, VFPRegister dest, AutoRegisterScope& scratch, Condition cc = Always);
+    BufferOffset ma_vldr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch,
                          int32_t shift = defaultShift, Condition cc = Always);
 
     BufferOffset ma_vstr(VFPRegister src, VFPAddr addr, Condition cc = Always);
-    BufferOffset ma_vstr(VFPRegister src, const Address& addr, Condition cc = Always);
-    BufferOffset ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
-                         int32_t offset, Condition cc = Always);
+    BufferOffset ma_vstr(VFPRegister src, const Address& addr, AutoRegisterScope& scratch, Condition cc = Always);
+    BufferOffset ma_vstr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch,
+                         AutoRegisterScope& scratch2, int32_t shift, int32_t offset, Condition cc = Always);
+    BufferOffset ma_vstr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch,
+                         int32_t shift, Condition cc = Always);
 
     void ma_call(ImmPtr dest);
 
     // Float registers can only be loaded/stored in continuous runs when using
     // vstm/vldm. This function breaks set into continuous runs and loads/stores
     // them at [rm]. rm will be modified and left in a state logically suitable
     // for the next load/store. Returns the offset from [dm] for the logical
     // next load/store.
@@ -534,66 +551,74 @@ class MacroAssemblerARMCompat : public M
     }
     void shortJumpSizedNop() {
         ma_nop();
     }
     void ret() {
         ma_pop(pc);
     }
     void retn(Imm32 n) {
-        // pc <- [sp]; sp += n
-        ma_dtr(IsLoad, sp, n, pc, PostIndex);
+        ScratchRegisterScope scratch(asMasm());
+        SecondScratchRegisterScope scratch2(asMasm());
+        ma_popn_pc(n, scratch, scratch2);
     }
     void push(Imm32 imm) {
         ScratchRegisterScope scratch(asMasm());
         ma_mov(imm, scratch);
         ma_push(scratch);
     }
     void push(ImmWord imm) {
         push(Imm32(imm.value));
     }
     void push(ImmGCPtr imm) {
         ScratchRegisterScope scratch(asMasm());
         ma_mov(imm, scratch);
         ma_push(scratch);
     }
     void push(const Address& addr) {
         ScratchRegisterScope scratch(asMasm());
-        ma_ldr(addr, scratch);
+        SecondScratchRegisterScope scratch2(asMasm());
+        ma_ldr(addr, scratch, scratch2);
         ma_push(scratch);
     }
     void push(Register reg) {
-        ma_push(reg);
+        if (reg == sp) {
+            ScratchRegisterScope scratch(asMasm());
+            ma_push_sp(reg, scratch);
+        } else {
+            ma_push(reg);
+        }
     }
     void push(FloatRegister reg) {
         ma_vpush(VFPRegister(reg));
     }
     void pushWithPadding(Register reg, const Imm32 extraSpace) {
+        ScratchRegisterScope scratch(asMasm());
         Imm32 totSpace = Imm32(extraSpace.value + 4);
-        ma_dtr(IsStore, sp, totSpace, reg, PreIndex);
+        ma_dtr(IsStore, sp, totSpace, reg, scratch, PreIndex);
     }
     void pushWithPadding(Imm32 imm, const Imm32 extraSpace) {
-        AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+        ScratchRegisterScope scratch(asMasm());
+        SecondScratchRegisterScope scratch2(asMasm());
         Imm32 totSpace = Imm32(extraSpace.value + 4);
-        // ma_dtr may need the scratch register to adjust the stack, so use the
-        // second scratch register.
-        ma_mov(imm, scratch2);
-        ma_dtr(IsStore, sp, totSpace, scratch2, PreIndex);
+        ma_mov(imm, scratch);
+        ma_dtr(IsStore, sp, totSpace, scratch, scratch2, PreIndex);
     }
 
     void pop(Register reg) {
         ma_pop(reg);
     }
     void pop(FloatRegister reg) {
         ma_vpop(VFPRegister(reg));
     }
 
     void popN(Register reg, Imm32 extraSpace) {
+        ScratchRegisterScope scratch(asMasm());
         Imm32 totSpace = Imm32(extraSpace.value + 4);
-        ma_dtr(IsLoad, sp, totSpace, reg, PostIndex);
+        ma_dtr(IsLoad, sp, totSpace, reg, scratch, PostIndex);
     }
 
     CodeOffset toggledJump(Label* label);
 
     // Emit a BLX or NOP instruction. ToggleCall can be used to patch this
     // instruction.
     CodeOffset toggledCall(JitCode* target, bool enabled);
 
@@ -619,36 +644,39 @@ class MacroAssemblerARMCompat : public M
     void jump(JitCode* code) {
         branch(code);
     }
     void jump(Register reg) {
         ma_bx(reg);
     }
     void jump(const Address& addr) {
         ScratchRegisterScope scratch(asMasm());
-        ma_ldr(addr, scratch);
+        SecondScratchRegisterScope scratch2(asMasm());
+        ma_ldr(addr, scratch, scratch2);
         ma_bx(scratch);
     }
     void jump(wasm::JumpTarget target) {
         as_b(target);
     }
 
     void negl(Register reg) {
         ma_neg(reg, reg, SetCC);
     }
     void test32(Register lhs, Register rhs) {
         ma_tst(lhs, rhs);
     }
     void test32(Register lhs, Imm32 imm) {
-        ma_tst(lhs, imm);
+        ScratchRegisterScope scratch(asMasm());
+        ma_tst(lhs, imm, scratch);
     }
     void test32(const Address& addr, Imm32 imm) {
         ScratchRegisterScope scratch(asMasm());
-        ma_ldr(addr, scratch);
-        ma_tst(scratch, imm);
+        SecondScratchRegisterScope scratch2(asMasm());
+        ma_ldr(addr, scratch, scratch2);
+        ma_tst(scratch, imm, scratch2);
     }
     void testPtr(Register lhs, Register rhs) {
         test32(lhs, rhs);
     }
 
     // Returns the register containing the type tag.
     Register splitTagForTest(const ValueOperand& value) {
         return value.typeReg();
@@ -720,17 +748,17 @@ class MacroAssemblerARMCompat : public M
     void unboxObject(const Address& src, Register dest) { unboxNonDouble(src, dest); }
     void unboxObject(const BaseIndex& src, Register dest) { unboxNonDouble(src, dest); }
     void unboxDouble(const ValueOperand& src, FloatRegister dest);
     void unboxDouble(const Address& src, FloatRegister dest);
     void unboxValue(const ValueOperand& src, AnyRegister dest);
     void unboxPrivate(const ValueOperand& src, Register dest);
 
     void notBoolean(const ValueOperand& val) {
-        ma_eor(Imm32(1), val.payloadReg());
+        as_eor(val.payloadReg(), val.payloadReg(), Imm8(1));
     }
 
     // Boxing code.
     void boxDouble(FloatRegister src, const ValueOperand& dest);
     void boxNonDouble(JSValueType type, Register src, const ValueOperand& dest);
 
     // Extended unboxing API. If the payload is already in a register, returns
     // that register. Otherwise, provides a move to the given scratch register,
@@ -774,20 +802,22 @@ class MacroAssemblerARMCompat : public M
 
     CodeOffsetJump jumpWithPatch(RepatchLabel* label, Condition cond = Always,
                                  Label* documentation = nullptr);
     CodeOffsetJump backedgeJump(RepatchLabel* label, Label* documentation) {
         return jumpWithPatch(label, Always, documentation);
     }
 
     void loadUnboxedValue(Address address, MIRType type, AnyRegister dest) {
-        if (dest.isFloat())
+        if (dest.isFloat()) {
             loadInt32OrDouble(address, dest.fpu());
-        else
-            ma_ldr(address, dest.gpr());
+        } else {
+            ScratchRegisterScope scratch(asMasm());
+            ma_ldr(address, dest.gpr(), scratch);
+        }
     }
 
     void loadUnboxedValue(BaseIndex address, MIRType type, AnyRegister dest) {
         if (dest.isFloat())
             loadInt32OrDouble(address.base, address.index, dest.fpu(), address.scale);
         else
             load32(address, dest.gpr());
     }
@@ -834,40 +864,100 @@ class MacroAssemblerARMCompat : public M
         if (s1 != d1)
             ma_mov(s1, d1);
     }
 
     void storeValue(ValueOperand val, const Address& dst);
     void storeValue(ValueOperand val, const BaseIndex& dest);
     void storeValue(JSValueType type, Register reg, BaseIndex dest) {
         ScratchRegisterScope scratch(asMasm());
+        SecondScratchRegisterScope scratch2(asMasm());
+
+        int32_t payloadoffset = dest.offset + NUNBOX32_PAYLOAD_OFFSET;
+        int32_t typeoffset = dest.offset + NUNBOX32_TYPE_OFFSET;
+
         ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd);
-        storeValue(type, reg, Address(scratch, dest.offset));
+
+        // Store the payload.
+        if (payloadoffset < 4096 && payloadoffset > -4096)
+            ma_str(reg, DTRAddr(scratch, DtrOffImm(payloadoffset)));
+        else
+            ma_str(reg, Address(scratch, payloadoffset), scratch2);
+
+        // Store the type.
+        if (typeoffset < 4096 && typeoffset > -4096) {
+            // Encodable as DTRAddr, so only two instructions needed.
+            ma_mov(ImmTag(JSVAL_TYPE_TO_TAG(type)), scratch2);
+            ma_str(scratch2, DTRAddr(scratch, DtrOffImm(typeoffset)));
+        } else {
+            // Since there are only two scratch registers, the offset must be
+            // applied early using a third instruction to be safe.
+            ma_add(Imm32(typeoffset), scratch, scratch2);
+            ma_mov(ImmTag(JSVAL_TYPE_TO_TAG(type)), scratch2);
+            ma_str(scratch2, DTRAddr(scratch, DtrOffImm(0)));
+        }
     }
     void storeValue(JSValueType type, Register reg, Address dest) {
-        ma_str(reg, dest);
-        AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-        ma_mov(ImmTag(JSVAL_TYPE_TO_TAG(type)), scratch2);
-        ma_str(scratch2, Address(dest.base, dest.offset + 4));
+        ScratchRegisterScope scratch(asMasm());
+        SecondScratchRegisterScope scratch2(asMasm());
+
+        ma_str(reg, dest, scratch2);
+        ma_mov(ImmTag(JSVAL_TYPE_TO_TAG(type)), scratch);
+        ma_str(scratch, Address(dest.base, dest.offset + NUNBOX32_TYPE_OFFSET), scratch2);
     }
     void storeValue(const Value& val, const Address& dest) {
-        AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+        ScratchRegisterScope scratch(asMasm());
+        SecondScratchRegisterScope scratch2(asMasm());
+
         jsval_layout jv = JSVAL_TO_IMPL(val);
-        ma_mov(Imm32(jv.s.tag), scratch2);
-        ma_str(scratch2, ToType(dest));
+        ma_mov(Imm32(jv.s.tag), scratch);
+        ma_str(scratch, ToType(dest), scratch2);
         if (val.isMarkable())
-            ma_mov(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())), scratch2);
+            ma_mov(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())), scratch);
         else
-            ma_mov(Imm32(jv.s.payload.i32), scratch2);
-        ma_str(scratch2, ToPayload(dest));
+            ma_mov(Imm32(jv.s.payload.i32), scratch);
+        ma_str(scratch, ToPayload(dest), scratch2);
     }
     void storeValue(const Value& val, BaseIndex dest) {
         ScratchRegisterScope scratch(asMasm());
+        SecondScratchRegisterScope scratch2(asMasm());
+        jsval_layout jv = JSVAL_TO_IMPL(val);
+
+        int32_t typeoffset = dest.offset + NUNBOX32_TYPE_OFFSET;
+        int32_t payloadoffset = dest.offset + NUNBOX32_PAYLOAD_OFFSET;
+
         ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd);
-        storeValue(val, Address(scratch, dest.offset));
+
+        // Store the type.
+        if (typeoffset < 4096 && typeoffset > -4096) {
+            ma_mov(Imm32(jv.s.tag), scratch2);
+            ma_str(scratch2, DTRAddr(scratch, DtrOffImm(typeoffset)));
+        } else {
+            ma_add(Imm32(typeoffset), scratch, scratch2);
+            ma_mov(Imm32(jv.s.tag), scratch2);
+            ma_str(scratch2, DTRAddr(scratch, DtrOffImm(0)));
+            // Restore scratch for the payload store.
+            ma_alu(dest.base, lsl(dest.index, dest.scale), scratch, OpAdd);
+        }
+
+        // Store the payload, marking if necessary.
+        if (payloadoffset < 4096 && payloadoffset > -4096) {
+            if (val.isMarkable())
+                ma_mov(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())), scratch2);
+            else
+                ma_mov(Imm32(jv.s.payload.i32), scratch2);
+            ma_str(scratch2, DTRAddr(scratch, DtrOffImm(payloadoffset)));
+        } else {
+            ma_add(Imm32(payloadoffset), scratch, scratch2);
+            if (val.isMarkable())
+                ma_mov(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())), scratch2);
+            else
+                ma_mov(Imm32(jv.s.payload.i32), scratch2);
+            ma_str(scratch2, DTRAddr(scratch, DtrOffImm(0)));
+        }
     }
     void storeValue(const Address& src, const Address& dest, Register temp) {
         load32(ToType(src), temp);
         store32(temp, ToType(dest));
 
         load32(ToPayload(src), temp);
         store32(temp, ToPayload(dest));
     }
@@ -1398,64 +1488,64 @@ class MacroAssemblerARMCompat : public M
     bool buildOOLFakeExitFrame(void* fakeReturnAddr);
 
   public:
     CodeOffset labelForPatch() {
         return CodeOffset(nextOffset().getOffset());
     }
 
     void computeEffectiveAddress(const Address& address, Register dest) {
-        ma_add(address.base, Imm32(address.offset), dest, LeaveCC);
+        ScratchRegisterScope scratch(asMasm());
+        ma_add(address.base, Imm32(address.offset), dest, scratch, LeaveCC);
     }
     void computeEffectiveAddress(const BaseIndex& address, Register dest) {
+        ScratchRegisterScope scratch(asMasm());
         ma_alu(address.base, lsl(address.index, address.scale), dest, OpAdd, LeaveCC);
         if (address.offset)
-            ma_add(dest, Imm32(address.offset), dest, LeaveCC);
+            ma_add(dest, Imm32(address.offset), dest, scratch, LeaveCC);
     }
     void floor(FloatRegister input, Register output, Label* handleNotAnInt);
     void floorf(FloatRegister input, Register output, Label* handleNotAnInt);
     void ceil(FloatRegister input, Register output, Label* handleNotAnInt);
     void ceilf(FloatRegister input, Register output, Label* handleNotAnInt);
     void round(FloatRegister input, Register output, Label* handleNotAnInt, FloatRegister tmp);
     void roundf(FloatRegister input, Register output, Label* handleNotAnInt, FloatRegister tmp);
 
     void clampCheck(Register r, Label* handleNotAnInt) {
         // Check explicitly for r == INT_MIN || r == INT_MAX
         // This is the instruction sequence that gcc generated for this
         // operation.
         ScratchRegisterScope scratch(asMasm());
-        ma_sub(r, Imm32(0x80000001), scratch);
-        ma_cmn(scratch, Imm32(3));
+        SecondScratchRegisterScope scratch2(asMasm());
+        ma_sub(r, Imm32(0x80000001), scratch, scratch2);
+        as_cmn(scratch, Imm8(3));
         ma_b(handleNotAnInt, Above);
     }
 
     void lea(Operand addr, Register dest) {
-        ma_add(addr.baseReg(), Imm32(addr.disp()), dest);
+        ScratchRegisterScope scratch(asMasm());
+        ma_add(addr.baseReg(), Imm32(addr.disp()), dest, scratch);
     }
 
     void abiret() {
         as_bx(lr);
     }
 
-    void ma_storeImm(Imm32 c, const Address& dest) {
-        ma_mov(c, lr);
-        ma_str(lr, dest);
-    }
-
     void moveFloat32(FloatRegister src, FloatRegister dest, Condition cc = Always) {
         as_vmov(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(), cc);
     }
 
     void loadWasmGlobalPtr(uint32_t globalDataOffset, Register dest) {
         loadPtr(Address(GlobalReg, globalDataOffset - AsmJSGlobalRegBias), dest);
     }
     void loadWasmPinnedRegsFromTls() {
-        loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, memoryBase)), HeapReg);
-        loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, globalData)), GlobalReg);
-        ma_add(Imm32(AsmJSGlobalRegBias), GlobalReg);
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(WasmTlsReg, offsetof(wasm::TlsData, memoryBase)), HeapReg, scratch);
+        ma_ldr(Address(WasmTlsReg, offsetof(wasm::TlsData, globalData)), GlobalReg, scratch);
+        ma_add(Imm32(AsmJSGlobalRegBias), GlobalReg, scratch);
     }
 
     // Instrumentation for entering and leaving the profiler.
     void profilerEnterFrame(Register framePtr, Register scratch);
     void profilerExitFrame();
 };
 
 typedef MacroAssemblerARMCompat MacroAssemblerSpecific;
--- a/js/src/jit/arm/MoveEmitter-arm.cpp
+++ b/js/src/jit/arm/MoveEmitter-arm.cpp
@@ -84,275 +84,289 @@ MoveEmitterARM::tempReg()
     // used for address computations, such as those found when we attempt to
     // access values more than 4096 off of the stack pointer. Instead, use lr,
     // the LinkRegister.
     spilledReg_ = r14;
     if (pushedAtSpill_ == -1) {
         masm.Push(spilledReg_);
         pushedAtSpill_ = masm.framePushed();
     } else {
-        masm.ma_str(spilledReg_, spillSlot());
+        ScratchRegisterScope scratch(masm);
+        masm.ma_str(spilledReg_, spillSlot(), scratch);
     }
     return spilledReg_;
 }
 
 void
 MoveEmitterARM::breakCycle(const MoveOperand& from, const MoveOperand& to,
                            MoveOp::Type type, uint32_t slotId)
 {
     // There is some pattern:
     //   (A -> B)
     //   (B -> A)
     //
     // This case handles (A -> B), which we reach first. We save B, then allow
     // the original move to continue.
+
+    ScratchRegisterScope scratch(masm);
+
     switch (type) {
       case MoveOp::FLOAT32:
         if (to.isMemory()) {
-            VFPRegister temp = ScratchFloat32Reg;
-            masm.ma_vldr(toAddress(to), temp);
+            ScratchFloat32Scope scratchFloat32(masm);
+            masm.ma_vldr(toAddress(to), scratchFloat32, scratch);
             // Since it is uncertain if the load will be aligned or not
             // just fill both of them with the same value.
-            masm.ma_vstr(temp, cycleSlot(slotId, 0));
-            masm.ma_vstr(temp, cycleSlot(slotId, 4));
+            masm.ma_vstr(scratchFloat32, cycleSlot(slotId, 0), scratch);
+            masm.ma_vstr(scratchFloat32, cycleSlot(slotId, 4), scratch);
         } else if (to.isGeneralReg()) {
             // Since it is uncertain if the load will be aligned or not
             // just fill both of them with the same value.
-            masm.ma_str(to.reg(), cycleSlot(slotId, 0));
-            masm.ma_str(to.reg(), cycleSlot(slotId, 4));
+            masm.ma_str(to.reg(), cycleSlot(slotId, 0), scratch);
+            masm.ma_str(to.reg(), cycleSlot(slotId, 4), scratch);
         } else {
             FloatRegister src = to.floatReg();
             // Just always store the largest possible size. Currently, this is
             // a double. When SIMD is added, two doubles will need to be stored.
-            masm.ma_vstr(src.doubleOverlay(), cycleSlot(slotId, 0));
+            masm.ma_vstr(src.doubleOverlay(), cycleSlot(slotId, 0), scratch);
         }
         break;
       case MoveOp::DOUBLE:
         if (to.isMemory()) {
-            ScratchDoubleScope scratch(masm);
-            masm.ma_vldr(toAddress(to), scratch);
-            masm.ma_vstr(scratch, cycleSlot(slotId, 0));
+            ScratchDoubleScope scratchDouble(masm);
+            masm.ma_vldr(toAddress(to), scratchDouble, scratch);
+            masm.ma_vstr(scratchDouble, cycleSlot(slotId, 0), scratch);
         } else if (to.isGeneralRegPair()) {
-            ScratchDoubleScope scratch(masm);
-            masm.ma_vxfer(to.evenReg(), to.oddReg(), scratch);
-            masm.ma_vstr(scratch, cycleSlot(slotId, 0));
+            ScratchDoubleScope scratchDouble(masm);
+            masm.ma_vxfer(to.evenReg(), to.oddReg(), scratchDouble);
+            masm.ma_vstr(scratchDouble, cycleSlot(slotId, 0), scratch);
         } else {
-            masm.ma_vstr(to.floatReg().doubleOverlay(), cycleSlot(slotId, 0));
+            masm.ma_vstr(to.floatReg().doubleOverlay(), cycleSlot(slotId, 0), scratch);
         }
         break;
       case MoveOp::INT32:
       case MoveOp::GENERAL:
         // an non-vfp value
         if (to.isMemory()) {
             Register temp = tempReg();
-            masm.ma_ldr(toAddress(to), temp);
-            masm.ma_str(temp, cycleSlot(0,0));
+            masm.ma_ldr(toAddress(to), temp, scratch);
+            masm.ma_str(temp, cycleSlot(0,0), scratch);
         } else {
             if (to.reg() == spilledReg_) {
                 // If the destination was spilled, restore it first.
-                masm.ma_ldr(spillSlot(), spilledReg_);
+                masm.ma_ldr(spillSlot(), spilledReg_, scratch);
                 spilledReg_ = InvalidReg;
             }
-            masm.ma_str(to.reg(), cycleSlot(0,0));
+            masm.ma_str(to.reg(), cycleSlot(0,0), scratch);
         }
         break;
       default:
         MOZ_CRASH("Unexpected move type");
     }
 }
 
 void
 MoveEmitterARM::completeCycle(const MoveOperand& from, const MoveOperand& to, MoveOp::Type type, uint32_t slotId)
 {
     // There is some pattern:
     //   (A -> B)
     //   (B -> A)
     //
     // This case handles (B -> A), which we reach last. We emit a move from the
     // saved value of B, to A.
+
+    ScratchRegisterScope scratch(masm);
+
     switch (type) {
       case MoveOp::FLOAT32:
         MOZ_ASSERT(!to.isGeneralRegPair());
         if (to.isMemory()) {
-            ScratchFloat32Scope scratch(masm);
-            masm.ma_vldr(cycleSlot(slotId, 0), scratch);
-            masm.ma_vstr(scratch, toAddress(to));
+            ScratchFloat32Scope scratchFloat32(masm);
+            masm.ma_vldr(cycleSlot(slotId, 0), scratchFloat32, scratch);
+            masm.ma_vstr(scratchFloat32, toAddress(to), scratch);
         } else if (to.isGeneralReg()) {
             MOZ_ASSERT(type == MoveOp::FLOAT32);
-            masm.ma_ldr(toAddress(from), to.reg());
+            masm.ma_ldr(toAddress(from), to.reg(), scratch);
         } else {
             uint32_t offset = 0;
             if ((!from.isMemory()) && from.floatReg().numAlignedAliased() == 1)
                 offset = sizeof(float);
-            masm.ma_vldr(cycleSlot(slotId, offset), to.floatReg());
+            masm.ma_vldr(cycleSlot(slotId, offset), to.floatReg(), scratch);
         }
         break;
       case MoveOp::DOUBLE:
         MOZ_ASSERT(!to.isGeneralReg());
         if (to.isMemory()) {
-            ScratchDoubleScope scratch(masm);
-            masm.ma_vldr(cycleSlot(slotId, 0), scratch);
-            masm.ma_vstr(scratch, toAddress(to));
+            ScratchDoubleScope scratchDouble(masm);
+            masm.ma_vldr(cycleSlot(slotId, 0), scratchDouble, scratch);
+            masm.ma_vstr(scratchDouble, toAddress(to), scratch);
         } else if (to.isGeneralRegPair()) {
             MOZ_ASSERT(type == MoveOp::DOUBLE);
-            ScratchDoubleScope scratch(masm);
-            masm.ma_vldr(toAddress(from), scratch);
-            masm.ma_vxfer(scratch, to.evenReg(), to.oddReg());
+            ScratchDoubleScope scratchDouble(masm);
+            masm.ma_vldr(toAddress(from), scratchDouble, scratch);
+            masm.ma_vxfer(scratchDouble, to.evenReg(), to.oddReg());
         } else {
             uint32_t offset = 0;
             if ((!from.isMemory()) && from.floatReg().numAlignedAliased() == 1)
                 offset = sizeof(float);
-            masm.ma_vldr(cycleSlot(slotId, offset), to.floatReg());
+            masm.ma_vldr(cycleSlot(slotId, offset), to.floatReg(), scratch);
         }
         break;
       case MoveOp::INT32:
       case MoveOp::GENERAL:
         MOZ_ASSERT(slotId == 0);
         if (to.isMemory()) {
             Register temp = tempReg();
-            masm.ma_ldr(cycleSlot(slotId, 0), temp);
-            masm.ma_str(temp, toAddress(to));
+            masm.ma_ldr(cycleSlot(slotId, 0), temp, scratch);
+            masm.ma_str(temp, toAddress(to), scratch);
         } else {
             if (to.reg() == spilledReg_) {
                 // Make sure we don't re-clobber the spilled register later.
                 spilledReg_ = InvalidReg;
             }
-            masm.ma_ldr(cycleSlot(slotId, 0), to.reg());
+            masm.ma_ldr(cycleSlot(slotId, 0), to.reg(), scratch);
         }
         break;
       default:
         MOZ_CRASH("Unexpected move type");
     }
 }
 
 void
 MoveEmitterARM::emitMove(const MoveOperand& from, const MoveOperand& to)
 {
     // Register pairs are used to store Double values during calls.
     MOZ_ASSERT(!from.isGeneralRegPair());
     MOZ_ASSERT(!to.isGeneralRegPair());
 
+    ScratchRegisterScope scratch(masm);
+
     if (to.isGeneralReg() && to.reg() == spilledReg_) {
         // If the destination is the spilled register, make sure we
         // don't re-clobber its value.
         spilledReg_ = InvalidReg;
     }
 
     if (from.isGeneralReg()) {
         if (from.reg() == spilledReg_) {
             // If the source is a register that has been spilled, make sure
             // to load the source back into that register.
-            masm.ma_ldr(spillSlot(), spilledReg_);
+            masm.ma_ldr(spillSlot(), spilledReg_, scratch);
             spilledReg_ = InvalidReg;
         }
         if (to.isMemoryOrEffectiveAddress())
-            masm.ma_str(from.reg(), toAddress(to));
+            masm.ma_str(from.reg(), toAddress(to), scratch);
         else
             masm.ma_mov(from.reg(), to.reg());
     } else if (to.isGeneralReg()) {
         MOZ_ASSERT(from.isMemoryOrEffectiveAddress());
         if (from.isMemory())
-            masm.ma_ldr(toAddress(from), to.reg());
+            masm.ma_ldr(toAddress(from), to.reg(), scratch);
         else
-            masm.ma_add(from.base(), Imm32(from.disp()), to.reg());
+            masm.ma_add(from.base(), Imm32(from.disp()), to.reg(), scratch);
     } else {
         // Memory to memory gpr move.
         Register reg = tempReg();
 
         MOZ_ASSERT(from.isMemoryOrEffectiveAddress());
         if (from.isMemory())
-            masm.ma_ldr(toAddress(from), reg);
+            masm.ma_ldr(toAddress(from), reg, scratch);
         else
-            masm.ma_add(from.base(), Imm32(from.disp()), reg);
+            masm.ma_add(from.base(), Imm32(from.disp()), reg, scratch);
         MOZ_ASSERT(to.base() != reg);
-        masm.ma_str(reg, toAddress(to));
+        masm.ma_str(reg, toAddress(to), scratch);
     }
 }
 
 void
 MoveEmitterARM::emitFloat32Move(const MoveOperand& from, const MoveOperand& to)
 {
     // Register pairs are used to store Double values during calls.
     MOZ_ASSERT(!from.isGeneralRegPair());
     MOZ_ASSERT(!to.isGeneralRegPair());
 
+    ScratchRegisterScope scratch(masm);
+
     if (from.isFloatReg()) {
         if (to.isFloatReg())
             masm.ma_vmov_f32(from.floatReg(), to.floatReg());
         else if (to.isGeneralReg())
             masm.ma_vxfer(from.floatReg(), to.reg());
         else
-            masm.ma_vstr(VFPRegister(from.floatReg()).singleOverlay(), toAddress(to));
+            masm.ma_vstr(VFPRegister(from.floatReg()).singleOverlay(), toAddress(to), scratch);
     } else if (from.isGeneralReg()) {
-        if (to.isFloatReg())
+        if (to.isFloatReg()) {
             masm.ma_vxfer(from.reg(), to.floatReg());
-        else if (to.isGeneralReg())
+        } else if (to.isGeneralReg()) {
             masm.ma_mov(from.reg(), to.reg());
-        else
-            masm.ma_str(from.reg(), toAddress(to));
+        } else {
+            masm.ma_str(from.reg(), toAddress(to), scratch);
+        }
     } else if (to.isFloatReg()) {
-        masm.ma_vldr(toAddress(from), VFPRegister(to.floatReg()).singleOverlay());
+        masm.ma_vldr(toAddress(from), VFPRegister(to.floatReg()).singleOverlay(), scratch);
     } else if (to.isGeneralReg()) {
-        masm.ma_ldr(toAddress(from), to.reg());
+        masm.ma_ldr(toAddress(from), to.reg(), scratch);
     } else {
         // Memory to memory move.
         MOZ_ASSERT(from.isMemory());
-        FloatRegister reg = ScratchFloat32Reg;
-        masm.ma_vldr(toAddress(from), VFPRegister(reg).singleOverlay());
-        masm.ma_vstr(VFPRegister(reg).singleOverlay(), toAddress(to));
+        ScratchFloat32Scope scratchFloat32(masm);
+        masm.ma_vldr(toAddress(from), scratchFloat32, scratch);
+        masm.ma_vstr(scratchFloat32, toAddress(to), scratch);
     }
 }
 
 void
 MoveEmitterARM::emitDoubleMove(const MoveOperand& from, const MoveOperand& to)
 {
     // Registers are used to store pointers / int32 / float32 values.
     MOZ_ASSERT(!from.isGeneralReg());
     MOZ_ASSERT(!to.isGeneralReg());
 
+    ScratchRegisterScope scratch(masm);
+
     if (from.isFloatReg()) {
         if (to.isFloatReg())
             masm.ma_vmov(from.floatReg(), to.floatReg());
         else if (to.isGeneralRegPair())
             masm.ma_vxfer(from.floatReg(), to.evenReg(), to.oddReg());
         else
-            masm.ma_vstr(from.floatReg(), toAddress(to));
+            masm.ma_vstr(from.floatReg(), toAddress(to), scratch);
     } else if (from.isGeneralRegPair()) {
         if (to.isFloatReg())
             masm.ma_vxfer(from.evenReg(), from.oddReg(), to.floatReg());
         else if (to.isGeneralRegPair()) {
             MOZ_ASSERT(!from.aliases(to));
             masm.ma_mov(from.evenReg(), to.evenReg());
             masm.ma_mov(from.oddReg(), to.oddReg());
         } else {
-            FloatRegister reg = ScratchDoubleReg;
-            masm.ma_vxfer(from.evenReg(), from.oddReg(), reg);
-            masm.ma_vstr(reg, toAddress(to));
+            ScratchDoubleScope scratchDouble(masm);
+            masm.ma_vxfer(from.evenReg(), from.oddReg(), scratchDouble);
+            masm.ma_vstr(scratchDouble, toAddress(to), scratch);
         }
     } else if (to.isFloatReg()) {
-        masm.ma_vldr(toAddress(from), to.floatReg());
+        masm.ma_vldr(toAddress(from), to.floatReg(), scratch);
     } else if (to.isGeneralRegPair()) {
         MOZ_ASSERT(from.isMemory());
         Address src = toAddress(from);
         // Note: We can safely use the MoveOperand's displacement here,
         // even if the base is SP: MoveEmitter::toOperand adjusts
         // SP-relative operands by the difference between the current
         // stack usage and stackAdjust, which emitter.finish() resets to
         // 0.
         //
         // Warning: if the offset isn't within [-255,+255] then this
         // will assert-fail (or, if non-debug, load the wrong words).
         // Nothing uses such an offset at the time of this writing.
         masm.ma_ldrd(EDtrAddr(src.base, EDtrOffImm(src.offset)), to.evenReg(), to.oddReg());
     } else {
         // Memory to memory move.
         MOZ_ASSERT(from.isMemory());
-        ScratchDoubleScope scratch(masm);
-        masm.ma_vldr(toAddress(from), scratch);
-        masm.ma_vstr(scratch, toAddress(to));
+        ScratchDoubleScope scratchDouble(masm);
+        masm.ma_vldr(toAddress(from), scratchDouble, scratch);
+        masm.ma_vstr(scratchDouble, toAddress(to), scratch);
     }
 }
 
 void
 MoveEmitterARM::emit(const MoveOp& move)
 {
     const MoveOperand& from = move.from();
     const MoveOperand& to = move.to();
@@ -400,12 +414,14 @@ MoveEmitterARM::assertDone()
     MOZ_ASSERT(inCycle_ == 0);
 }
 
 void
 MoveEmitterARM::finish()
 {
     assertDone();
 
-    if (pushedAtSpill_ != -1 && spilledReg_ != InvalidReg)
-        masm.ma_ldr(spillSlot(), spilledReg_);
+    if (pushedAtSpill_ != -1 && spilledReg_ != InvalidReg) {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_ldr(spillSlot(), spilledReg_, scratch);
+    }
     masm.freeStack(masm.framePushed() - pushedAtStart_);
 }
--- a/js/src/jit/arm/SharedIC-arm.cpp
+++ b/js/src/jit/arm/SharedIC-arm.cpp
@@ -54,36 +54,40 @@ ICBinaryArith_Int32::Compiler::generateS
         masm.mov(scratchReg, R0.payloadReg());
         break;
       case JSOP_SUB:
         masm.ma_sub(R0.payloadReg(), R1.payloadReg(), scratchReg, SetCC);
         masm.j(Assembler::Overflow, &failure);
         masm.mov(scratchReg, R0.payloadReg());
         break;
       case JSOP_MUL: {
+        ScratchRegisterScope scratch(masm);
         Assembler::Condition cond = masm.ma_check_mul(R0.payloadReg(), R1.payloadReg(), scratchReg,
-                                                      Assembler::Overflow);
+                                                      scratch, Assembler::Overflow);
         masm.j(cond, &failure);
 
-        masm.ma_cmp(scratchReg, Imm32(0));
+        masm.as_cmp(scratchReg, Imm8(0));
         masm.j(Assembler::Equal, &maybeNegZero);
 
         masm.mov(scratchReg, R0.payloadReg());
         break;
       }
       case JSOP_DIV:
       case JSOP_MOD: {
         // Check for INT_MIN / -1, it results in a double.
-        masm.ma_cmp(R0.payloadReg(), Imm32(INT_MIN));
-        masm.ma_cmp(R1.payloadReg(), Imm32(-1), Assembler::Equal);
-        masm.j(Assembler::Equal, &failure);
+        {
+            ScratchRegisterScope scratch(masm);
+            masm.ma_cmp(R0.payloadReg(), Imm32(INT_MIN), scratch);
+            masm.ma_cmp(R1.payloadReg(), Imm32(-1), scratch, Assembler::Equal);
+            masm.j(Assembler::Equal, &failure);
+        }
 
         // Check for both division by zero and 0 / X with X < 0 (results in -0).
-        masm.ma_cmp(R1.payloadReg(), Imm32(0));
-        masm.ma_cmp(R0.payloadReg(), Imm32(0), Assembler::LessThan);
+        masm.as_cmp(R1.payloadReg(), Imm8(0));
+        masm.as_cmp(R0.payloadReg(), Imm8(0), Assembler::LessThan);
         masm.j(Assembler::Equal, &failure);
 
         // The call will preserve registers r4-r11. Save R0 and the link
         // register.
         MOZ_ASSERT(R1 == ValueOperand(r5, r4));
         MOZ_ASSERT(R0 == ValueOperand(r3, r2));
         masm.moveValue(R0, savedValue);
 
@@ -113,27 +117,27 @@ ICBinaryArith_Int32::Compiler::generateS
       case JSOP_BITXOR:
         masm.ma_eor(R1.payloadReg(), R0.payloadReg(), R0.payloadReg());
         break;
       case JSOP_BITAND:
         masm.ma_and(R1.payloadReg(), R0.payloadReg(), R0.payloadReg());
         break;
       case JSOP_LSH:
         // ARM will happily try to shift by more than 0x1f.
-        masm.ma_and(Imm32(0x1F), R1.payloadReg(), R1.payloadReg());
+        masm.as_and(R1.payloadReg(), R1.payloadReg(), Imm8(0x1F));
         masm.ma_lsl(R1.payloadReg(), R0.payloadReg(), R0.payloadReg());
         break;
       case JSOP_RSH:
-        masm.ma_and(Imm32(0x1F), R1.payloadReg(), R1.payloadReg());
+        masm.as_and(R1.payloadReg(), R1.payloadReg(), Imm8(0x1F));
         masm.ma_asr(R1.payloadReg(), R0.payloadReg(), R0.payloadReg());
         break;
       case JSOP_URSH:
-        masm.ma_and(Imm32(0x1F), R1.payloadReg(), scratchReg);
+        masm.as_and(scratchReg, R1.payloadReg(), Imm8(0x1F));
         masm.ma_lsr(scratchReg, R0.payloadReg(), scratchReg);
-        masm.ma_cmp(scratchReg, Imm32(0));
+        masm.as_cmp(scratchReg, Imm8(0));
         if (allowDouble_) {
             Label toUint;
             masm.j(Assembler::LessThan, &toUint);
 
             // Move result and box for return.
             masm.mov(scratchReg, R0.payloadReg());
             EmitReturnFromIC(masm);
 
@@ -191,17 +195,17 @@ ICUnaryArith_Int32::Compiler::generateSt
       case JSOP_BITNOT:
         masm.ma_mvn(R0.payloadReg(), R0.payloadReg());
         break;
       case JSOP_NEG:
         // Guard against 0 and MIN_INT, both result in a double.
         masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(0x7fffffff), &failure);
 
         // Compile -x as 0 - x.
-        masm.ma_rsb(R0.payloadReg(), Imm32(0), R0.payloadReg());
+        masm.as_rsb(R0.payloadReg(), R0.payloadReg(), Imm8(0));
         break;
       default:
         MOZ_CRASH("Unexpected op");
     }
 
     EmitReturnFromIC(masm);
 
     masm.bind(&failure);
--- a/js/src/jit/arm/SharedICHelpers-arm.h
+++ b/js/src/jit/arm/SharedICHelpers-arm.h
@@ -82,21 +82,24 @@ inline void
 EmitBaselineTailCallVM(JitCode* target, MacroAssembler& masm, uint32_t argSize)
 {
     // We assume during this that R0 and R1 have been pushed, and that R2 is
     // unused.
     MOZ_ASSERT(R2 == ValueOperand(r1, r0));
 
     // Compute frame size.
     masm.movePtr(BaselineFrameReg, r0);
-    masm.ma_add(Imm32(BaselineFrame::FramePointerOffset), r0);
+    masm.as_add(r0, r0, Imm8(BaselineFrame::FramePointerOffset));
     masm.ma_sub(BaselineStackReg, r0);
 
     // Store frame size without VMFunction arguments for GC marking.
-    masm.ma_sub(r0, Imm32(argSize), r1);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_sub(r0, Imm32(argSize), r1, scratch);
+    }
     masm.store32(r1, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
 
     // Push frame descriptor and perform the tail call.
     // ICTailCallReg (lr) already contains the return address (as we keep
     // it there through the stub calls), but the VMWrapper code being called
     // expects the return address to also be pushed on the stack.
     MOZ_ASSERT(ICTailCallReg == lr);
     masm.makeFrameDescriptor(r0, JitFrame_BaselineJS, ExitFrameLayout::Size());
@@ -128,17 +131,17 @@ EmitIonTailCallVM(JitCode* target, Macro
 }
 
 inline void
 EmitBaselineCreateStubFrameDescriptor(MacroAssembler& masm, Register reg, uint32_t headerSize)
 {
     // Compute stub frame size. We have to add two pointers: the stub reg and
     // previous frame pointer pushed by EmitEnterStubFrame.
     masm.mov(BaselineFrameReg, reg);
-    masm.ma_add(Imm32(sizeof(void*) * 2), reg);
+    masm.as_add(reg, reg, Imm8(sizeof(void*) * 2));
     masm.ma_sub(BaselineStackReg, reg);
 
     masm.makeFrameDescriptor(reg, JitFrame_BaselineStub, headerSize);
 }
 
 inline void
 EmitBaselineCallVM(JitCode* target, MacroAssembler& masm)
 {
@@ -169,17 +172,17 @@ static const uint32_t STUB_FRAME_SAVED_S
 
 inline void
 EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch)
 {
     MOZ_ASSERT(scratch != ICTailCallReg);
 
     // Compute frame size.
     masm.mov(BaselineFrameReg, scratch);
-    masm.ma_add(Imm32(BaselineFrame::FramePointerOffset), scratch);
+    masm.as_add(scratch, scratch, Imm8(BaselineFrame::FramePointerOffset));
     masm.ma_sub(BaselineStackReg, scratch);
 
     masm.store32(scratch, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
 
     // Note: when making changes here, don't forget to update STUB_FRAME_SIZE if
     // needed.
 
     // Push frame descriptor and return address.
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -169,17 +169,17 @@ JitRuntime::generateEnterJIT(JSContext* 
     // enough space reserved for pushing the arguments, and the JitFrameLayout.
     // The stack pointer is also aligned on the alignment expected by the Jit
     // frames.
     //
     // At the end the register r4, is a pointer to the stack where the first
     // argument is expected by the Jit frame.
     //
     aasm->as_sub(r4, sp, O2RegImmShift(r1, LSL, 3));    // r4 = sp - argc*8
-    masm.ma_and(Imm32(~(JitStackAlignment - 1)), r4, r4);
+    aasm->as_bic(r4, r4, Imm8(JitStackAlignment - 1));
     // r4 is now the aligned on the bottom of the list of arguments.
     static_assert(sizeof(JitFrameLayout) % JitStackAlignment == 0,
       "No need to consider the JitFrameLayout for aligning the stack");
     // sp' = ~(JitStackAlignment - 1) & (sp - argc * sizeof(Value)) - sizeof(JitFrameLayout)
     aasm->as_sub(sp, r4, Imm8(sizeof(JitFrameLayout)));
 
     // Get a copy of the number of args to use as a decrement counter, also set
     // the zero condition code.
@@ -312,17 +312,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         // If OSR-ing, then emit instrumentation for setting lastProfilerFrame
         // if profiler instrumentation is enabled.
         {
             Label skipProfilingInstrumentation;
             Register realFramePtr = numStackValues;
             AbsoluteAddress addressOfEnabled(cx->runtime()->spsProfiler.addressOfEnabled());
             masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0),
                           &skipProfilingInstrumentation);
-            masm.ma_add(framePtr, Imm32(sizeof(void*)), realFramePtr);
+            masm.as_add(realFramePtr, framePtr, Imm8(sizeof(void*)));
             masm.profilerEnterFrame(realFramePtr, scratch);
             masm.bind(&skipProfilingInstrumentation);
         }
 
         masm.jump(jitcode);
 
         // OOM: Load error value, discard return address and previous frame
         // pointer and return.
@@ -395,30 +395,31 @@ JitRuntime::generateInvalidator(JSContex
     // See large comment in x86's JitRuntime::generateInvalidator.
     MacroAssembler masm(cx);
     // At this point, one of two things has happened:
     // 1) Execution has just returned from C code, which left the stack aligned
     // 2) Execution has just returned from Ion code, which left the stack unaligned.
     // The old return address should not matter, but we still want the stack to
     // be aligned, and there is no good reason to automatically align it with a
     // call to setupUnalignedABICall.
-    masm.ma_and(Imm32(~7), sp, sp);
+    masm.as_bic(sp, sp, Imm8(7));
     masm.startDataTransferM(IsStore, sp, DB, WriteBack);
     // We don't have to push everything, but this is likely easier.
     // Setting regs_.
     for (uint32_t i = 0; i < Registers::Total; i++)
         masm.transferReg(Register::FromCode(i));
     masm.finishDataTransfer();
 
     // Since our datastructures for stack inspection are compile-time fixed,
     // if there are only 16 double registers, then we need to reserve
     // space on the stack for the missing 16.
     if (FloatRegisters::ActualTotalPhys() != FloatRegisters::TotalPhys) {
+        ScratchRegisterScope scratch(masm);
         int missingRegs = FloatRegisters::TotalPhys - FloatRegisters::ActualTotalPhys();
-        masm.ma_sub(Imm32(missingRegs * sizeof(double)), sp);
+        masm.ma_sub(Imm32(missingRegs * sizeof(double)), sp, scratch);
     }
 
     masm.startFloatTransferM(IsStore, sp, DB, WriteBack);
     for (uint32_t i = 0; i < FloatRegisters::ActualTotalPhys(); i++)
         masm.transferFloatReg(FloatRegister(i, FloatRegister::Double));
     masm.finishFloatTransfer();
 
     masm.ma_mov(sp, r0);
@@ -429,22 +430,28 @@ JitRuntime::generateInvalidator(JSContex
     masm.reserveStack(sizeOfBailoutInfo);
     masm.mov(sp, r2);
     masm.setupAlignedABICall();
     masm.passABIArg(r0);
     masm.passABIArg(r1);
     masm.passABIArg(r2);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout));
 
-    masm.ma_ldr(Address(sp, 0), r2);
-    masm.ma_ldr(Address(sp, sizeOfBailoutInfo), r1);
+    masm.ma_ldr(DTRAddr(sp, DtrOffImm(0)), r2);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_ldr(Address(sp, sizeOfBailoutInfo), r1, scratch);
+    }
     // Remove the return address, the IonScript, the register state
     // (InvaliationBailoutStack) and the space that was allocated for the return
     // value.
-    masm.ma_add(sp, Imm32(sizeof(InvalidationBailoutStack) + sizeOfRetval + sizeOfBailoutInfo), sp);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_add(sp, Imm32(sizeof(InvalidationBailoutStack) + sizeOfRetval + sizeOfBailoutInfo), sp, scratch);
+    }
     // Remove the space that this frame was using before the bailout (computed
     // by InvalidationBailout)
     masm.ma_add(sp, r1, sp);
 
     // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2.
     JitCode* bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
     masm.branch(bailoutTail);
 
@@ -470,66 +477,72 @@ JitRuntime::generateArgumentsRectifier(J
     // Including |this|, there are (|nargs| + 1) arguments to copy.
     MOZ_ASSERT(ArgumentsRectifierReg == r8);
 
     // Copy number of actual arguments into r0.
     masm.ma_ldr(DTRAddr(sp, DtrOffImm(RectifierFrameLayout::offsetOfNumActualArgs())), r0);
 
     // Load the number of |undefined|s to push into r6.
     masm.ma_ldr(DTRAddr(sp, DtrOffImm(RectifierFrameLayout::offsetOfCalleeToken())), r1);
-    masm.ma_and(Imm32(CalleeTokenMask), r1, r6);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_and(Imm32(CalleeTokenMask), r1, r6, scratch);
+    }
     masm.ma_ldrh(EDtrAddr(r6, EDtrOffImm(JSFunction::offsetOfNargs())), r6);
 
     masm.ma_sub(r6, r8, r2);
 
     // Get the topmost argument.
-    masm.ma_alu(sp, lsl(r8, 3), r3, OpAdd); // r3 <- r3 + nargs * 8
-    masm.ma_add(r3, Imm32(sizeof(RectifierFrameLayout)), r3);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_alu(sp, lsl(r8, 3), r3, OpAdd); // r3 <- r3 + nargs * 8
+        masm.ma_add(r3, Imm32(sizeof(RectifierFrameLayout)), r3, scratch);
+    }
 
     {
         Label notConstructing;
 
         masm.branchTest32(Assembler::Zero, r1, Imm32(CalleeToken_FunctionConstructing),
                           &notConstructing);
 
         // Add sizeof(Value) to overcome |this|
-        masm.ma_dataTransferN(IsLoad, 64, true, r3, Imm32(8), r4, Offset);
-        masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex);
+        masm.as_extdtr(IsLoad, 64, true, Offset, r4, EDtrAddr(r3, EDtrOffImm(8)));
+        masm.as_extdtr(IsStore, 64, true, PreIndex, r4, EDtrAddr(sp, EDtrOffImm(-8)));
 
         // Include the newly pushed newTarget value in the frame size
         // calculated below.
         masm.add32(Imm32(1), r6);
 
         masm.bind(&notConstructing);
     }
 
     // Push undefined.
     masm.moveValue(UndefinedValue(), r5, r4);
     {
         Label undefLoopTop;
         masm.bind(&undefLoopTop);
-        masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex);
-        masm.ma_sub(r2, Imm32(1), r2, SetCC);
+        masm.as_extdtr(IsStore, 64, true, PreIndex, r4, EDtrAddr(sp, EDtrOffImm(-8)));
+        masm.as_sub(r2, r2, Imm8(1), SetCC);
 
         masm.ma_b(&undefLoopTop, Assembler::NonZero);
     }
 
     // Push arguments, |nargs| + 1 times (to include |this|).
     {
         Label copyLoopTop;
         masm.bind(&copyLoopTop);
-        masm.ma_dataTransferN(IsLoad, 64, true, r3, Imm32(-8), r4, PostIndex);
-        masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex);
+        masm.as_extdtr(IsLoad, 64, true, PostIndex, r4, EDtrAddr(r3, EDtrOffImm(-8)));
+        masm.as_extdtr(IsStore, 64, true, PreIndex, r4, EDtrAddr(sp, EDtrOffImm(-8)));
 
-        masm.ma_sub(r8, Imm32(1), r8, SetCC);
+        masm.as_sub(r8, r8, Imm8(1), SetCC);
         masm.ma_b(&copyLoopTop, Assembler::NotSigned);
     }
 
     // translate the framesize from values into bytes
-    masm.ma_add(r6, Imm32(1), r6);
+    masm.as_add(r6, r6, Imm8(1));
     masm.ma_lsl(Imm32(3), r6, r6);
 
     // Construct sizeDescriptor.
     masm.makeFrameDescriptor(r6, JitFrame_Rectifier, JitFrameLayout::Size());
 
     // Construct JitFrameLayout.
     masm.ma_push(r0); // actual arguments.
     masm.ma_push(r1); // callee token
@@ -546,17 +559,20 @@ JitRuntime::generateArgumentsRectifier(J
     //  ...
     // argN
     // num actual args
     // callee token
     // sizeDescriptor     <- sp now
     // return address
 
     // Remove the rectifier frame.
-    masm.ma_dtr(IsLoad, sp, Imm32(12), r4, PostIndex);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_dtr(IsLoad, sp, Imm32(12), r4, scratch, PostIndex);
+    }
 
     // arg1
     //  ...
     // argN               <- sp now; r4 <- frame descriptor
     // num actual args
     // callee token
     // sizeDescriptor
     // return address
@@ -595,22 +611,24 @@ PushBailoutFrame(MacroAssembler& masm, u
 
     masm.startDataTransferM(IsStore, sp, DB, WriteBack);
     // We don't have to push everything, but this is likely easier.
     // Setting regs_.
     for (uint32_t i = 0; i < Registers::Total; i++)
         masm.transferReg(Register::FromCode(i));
     masm.finishDataTransfer();
 
+    ScratchRegisterScope scratch(masm);
+
     // Since our datastructures for stack inspection are compile-time fixed,
     // if there are only 16 double registers, then we need to reserve
     // space on the stack for the missing 16.
     if (FloatRegisters::ActualTotalPhys() != FloatRegisters::TotalPhys) {
         int missingRegs = FloatRegisters::TotalPhys - FloatRegisters::ActualTotalPhys();
-        masm.ma_sub(Imm32(missingRegs * sizeof(double)), sp);
+        masm.ma_sub(Imm32(missingRegs * sizeof(double)), sp, scratch);
     }
     masm.startFloatTransferM(IsStore, sp, DB, WriteBack);
     for (uint32_t i = 0; i < FloatRegisters::ActualTotalPhys(); i++)
         masm.transferFloatReg(FloatRegister(i, FloatRegister::Double));
     masm.finishFloatTransfer();
 
     // STEP 1b: Push both the "return address" of the function call (the address
     //          of the instruction after the call that we used to get here) as
@@ -653,46 +671,52 @@ GenerateBailoutThunk(JSContext* cx, Macr
     // both the snapshotoffset as well as the: masm.as_sub(sp, sp, Imm8(4));
 
     // Set the old (4-byte aligned) value of the sp as the first argument.
     masm.passABIArg(r0);
     masm.passABIArg(r1);
 
     // Sp % 8 == 0
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout));
-    masm.ma_ldr(Address(sp, 0), r2);
-    masm.ma_add(sp, Imm32(sizeOfBailoutInfo), sp);
+    masm.ma_ldr(DTRAddr(sp, DtrOffImm(0)), r2);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_add(sp, Imm32(sizeOfBailoutInfo), sp, scratch);
+    }
+
     // Common size of a bailout frame.
     uint32_t bailoutFrameSize = 0
         + sizeof(void*) // frameClass
         + sizeof(RegisterDump);
 
     if (frameClass == NO_FRAME_SIZE_CLASS_ID) {
         // Make sure the bailout frame size fits into the offset for a load.
         masm.as_dtr(IsLoad, 32, Offset,
                     r4, DTRAddr(sp, DtrOffImm(4)));
         // Used to be: offsetof(BailoutStack, frameSize_)
         // This structure is no longer available to us :(
         // We add 12 to the bailoutFrameSize because:
         // sizeof(uint32_t) for the tableOffset that was pushed onto the stack
         // sizeof(uintptr_t) for the snapshotOffset;
         // alignment to round the uintptr_t up to a multiple of 8 bytes.
-        masm.ma_add(sp, Imm32(bailoutFrameSize+12), sp);
+        ScratchRegisterScope scratch(masm);
+        masm.ma_add(sp, Imm32(bailoutFrameSize+12), sp, scratch);
         masm.as_add(sp, sp, O2Reg(r4));
     } else {
+        ScratchRegisterScope scratch(masm);
         uint32_t frameSize = FrameSizeClass::FromClass(frameClass).frameSize();
         masm.ma_add(Imm32(// The frame that was added when we entered the most
                           // recent function.
                           frameSize
                           // The size of the "return address" that was dumped on
                           // the stack.
                           + sizeof(void*)
                           // Everything else that was pushed on the stack.
                           + bailoutFrameSize)
-                    , sp);
+                    , sp, scratch);
     }
 
     // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2.
     JitCode* bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
     masm.branch(bailoutTail);
 }
 
 JitCode*
@@ -772,17 +796,18 @@ JitRuntime::generateVMWrapper(JSContext*
     masm.enterExitFrame(&f);
     masm.loadJSContext(cxreg);
 
     // Save the base of the argument set stored on the stack.
     Register argsBase = InvalidReg;
     if (f.explicitArgs) {
         argsBase = r5;
         regs.take(argsBase);
-        masm.ma_add(sp, Imm32(ExitFrameLayout::SizeWithFooter()), argsBase);
+        ScratchRegisterScope scratch(masm);
+        masm.ma_add(sp, Imm32(ExitFrameLayout::SizeWithFooter()), argsBase, scratch);
     }
 
     // Reserve space for the outparameter.
     Register outReg = InvalidReg;
     switch (f.outParam) {
       case Type_Value:
         outReg = r4;
         regs.take(outReg);
@@ -1150,17 +1175,20 @@ JitRuntime::generateProfilerExitFrameTai
 #endif
 
     // Load the frame descriptor into |scratch1|, figure out what to do depending on its type.
     masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfDescriptor()), scratch1);
 
     // Going into the conditionals, we will have:
     //      FrameDescriptor.size in scratch1
     //      FrameDescriptor.type in scratch2
-    masm.ma_and(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1, scratch2);
+    {
+        ScratchRegisterScope asmScratch(masm);
+        masm.ma_and(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1, scratch2, asmScratch);
+    }
     masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch1);
 
     // Handling of each case is dependent on FrameDescriptor.type
     Label handle_IonJS;
     Label handle_BaselineStub;
     Label handle_Rectifier;
     Label handle_IonAccessorIC;
     Label handle_Entry;
@@ -1196,17 +1224,17 @@ JitRuntime::generateProfilerExitFrameTai
         // returning directly to an IonJS frame.  Store return addr to frame
         // in lastProfilingCallSite.
         masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfReturnAddress()), scratch2);
         masm.storePtr(scratch2, lastProfilingCallSite);
 
         // Store return frame in lastProfilingFrame.
         // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size();
         masm.ma_add(StackPointer, scratch1, scratch2);
-        masm.ma_add(scratch2, Imm32(JitFrameLayout::Size()), scratch2);
+        masm.as_add(scratch2, scratch2, Imm8(JitFrameLayout::Size()));
         masm.storePtr(scratch2, lastProfilingFrame);
         masm.ret();
     }
 
     //
     // JitFrame_BaselineStub
     //
     // Look past the stub and store the frame pointer to
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -2738,17 +2738,17 @@ CrossAxisPositionTracker::
                            const ReflowInput& aReflowInput,
                            nscoord aContentBoxCrossSize,
                            bool aIsCrossSizeDefinite,
                            const FlexboxAxisTracker& aAxisTracker)
   : PositionTracker(aAxisTracker.GetCrossAxis(),
                     aAxisTracker.IsCrossAxisReversed()),
     mPackingSpaceRemaining(0),
     mNumPackingSpacesRemaining(0),
-    mAlignContent(aReflowInput.mStylePosition->ComputedAlignContent())
+    mAlignContent(aReflowInput.mStylePosition->mAlignContent)
 {
   MOZ_ASSERT(aFirstLine, "null first line pointer");
 
   // 'normal' behaves as 'stretch'
   if (mAlignContent == NS_STYLE_ALIGN_NORMAL) {
     mAlignContent = NS_STYLE_ALIGN_STRETCH;
   }
 
@@ -4062,17 +4062,17 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
           crossAxisPosnTracker.GetPosition() + firstLineBaselineOffset,
           contentBoxCrossSize, aReflowInput, aAxisTracker);
     }
   }
 
   const auto justifyContent = IsLegacyBox(aReflowInput.mStyleDisplay,
                                           mStyleContext) ?
     ConvertLegacyStyleToJustifyContent(StyleXUL()) :
-    aReflowInput.mStylePosition->ComputedJustifyContent();
+    aReflowInput.mStylePosition->mJustifyContent;
 
   for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
     // Main-Axis Alignment - Flexbox spec section 9.5
     // ==============================================
     line->PositionItemsInMainAxis(justifyContent,
                                   aContentBoxMainSize,
                                   aAxisTracker);
 
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -4070,17 +4070,17 @@ nsGridContainerFrame::Tracks::Initialize
 
       // [align|justify]-content:[last-]baseline.
       // https://drafts.csswg.org/css-align-3/#baseline-align-content
       // "[...] and its computed 'align-self' or 'justify-self' (whichever
       // affects its block axis) is 'stretch' or 'self-start' ('self-end').
       // For this purpose, the 'start', 'end', 'flex-start', and 'flex-end'
       // values of 'align-self' are treated as either 'self-start' or
       // 'self-end', whichever they end up equivalent to.
-      auto alignContent = child->StylePosition()->ComputedAlignContent();
+      auto alignContent = child->StylePosition()->mAlignContent;
       alignContent &= ~NS_STYLE_ALIGN_FLAG_BITS;
       if (alignContent == NS_STYLE_ALIGN_BASELINE ||
           alignContent == NS_STYLE_ALIGN_LAST_BASELINE) {
         const auto selfAlignEdge = alignContent == NS_STYLE_ALIGN_BASELINE ?
           NS_STYLE_ALIGN_SELF_START : NS_STYLE_ALIGN_SELF_END;
         bool validCombo = selfAlignment == NS_STYLE_ALIGN_NORMAL ||
                           selfAlignment == NS_STYLE_ALIGN_STRETCH ||
                           selfAlignment == selfAlignEdge;
@@ -4672,18 +4672,18 @@ nsGridContainerFrame::Tracks::AlignJusti
   WritingMode            aWM,
   const LogicalSize&     aContainerSize)
 {
   if (mSizes.IsEmpty()) {
     return;
   }
 
   const bool isAlign = mAxis == eLogicalAxisBlock;
-  auto valueAndFallback = isAlign ? aStyle->ComputedAlignContent() :
-                                    aStyle->ComputedJustifyContent();
+  auto valueAndFallback = isAlign ? aStyle->mAlignContent :
+                                    aStyle->mJustifyContent;
   bool overflowSafe;
   auto alignment = ::GetAlignJustifyValue(valueAndFallback, aWM, isAlign,
                                           &overflowSafe);
   if (alignment == NS_STYLE_ALIGN_NORMAL) {
     MOZ_ASSERT(valueAndFallback == NS_STYLE_ALIGN_NORMAL,
                "*-content:normal cannot be specified with explicit fallback");
     alignment = NS_STYLE_ALIGN_STRETCH;
     valueAndFallback = alignment; // we may need a fallback for 'stretch' below
--- a/layout/reftests/ogg-video/poster-4.html
+++ b/layout/reftests/ogg-video/poster-4.html
@@ -1,11 +1,11 @@
 <!DOCTYPE HTML>
 <html class="reftest-wait">
-<body style="background:white;">
+<body style="background:white;"
+      onload="document.getElementById('v').poster = ''; setTimeout(function(){document.documentElement.className = '';}, 0);">
 <!-- Test if we show video frame after removing valid poster. -->
 <video src="black140x100.ogv"
        preload="auto"
        id="v"
-       onload="document.getElementById('v').poster = ''; setTimeout(function(){document.documentElement.className = '';}, 0);"
        poster="blue250x200.png"></video>
 </body>
 </html>
--- a/layout/reftests/ogg-video/poster-7.html
+++ b/layout/reftests/ogg-video/poster-7.html
@@ -1,11 +1,11 @@
 <!DOCTYPE HTML>
 <html class="reftest-wait">
-<body style="background:white;">
+<body style="background:white;"
+      onload="document.getElementById('v').poster = 'red140x100.png'; setTimeout(function(){document.documentElement.className = '';}, 0);">
 <!-- Test that poster frame changes when you change the poster attribute. -->
 <video src="black140x100.ogv"
        preload="none"
        id="v"
-       onload="document.getElementById('v').poster = 'red140x100.png'; setTimeout(function(){document.documentElement.className = '';}, 0);"
        poster="blue250x200.png"></video>
 </body>
 </html>
--- a/layout/reftests/webm-video/poster-4.html
+++ b/layout/reftests/webm-video/poster-4.html
@@ -1,11 +1,11 @@
 <!DOCTYPE HTML>
 <html class="reftest-wait">
-<body style="background:white;">
+<body style="background:white;"
+      onload="document.getElementById('v').poster = ''; setTimeout(function(){document.documentElement.className = '';}, 0);">
 <!-- Test if we show video frame after removing valid poster. -->
 <video src="black140x100.webm"
        preload="auto"
        id="v"
-       onload="document.getElementById('v').poster = ''; setTimeout(function(){document.documentElement.className = '';}, 0);"
        poster="blue250x200.png"></video>
 </body>
 </html>
--- a/layout/reftests/webm-video/poster-7.html
+++ b/layout/reftests/webm-video/poster-7.html
@@ -1,11 +1,11 @@
 <!DOCTYPE HTML>
 <html class="reftest-wait">
-<body style="background:white;">
+<body style="background:white;"
+      onload="document.getElementById('v').poster = 'red140x100.png'; setTimeout(function(){document.documentElement.className = '';}, 0);">
 <!-- Test that poster frame changes when you change the poster attribute. -->
 <video src="black140x100.webm"
        preload="none"
        id="v"
-       onload="document.getElementById('v').poster = 'red140x100.png'; setTimeout(function(){document.documentElement.className = '';}, 0);"
        poster="blue250x200.png"></video>
 </body>
 </html>
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -599,29 +599,27 @@ Gecko_GetAtomAsUTF16(nsIAtom* aAtom, uin
   return reinterpret_cast<const uint16_t*>(static_cast<const char16_t*>(aAtom->GetUTF16String()));
 }
 
 bool
 Gecko_AtomEqualsUTF8(nsIAtom* aAtom, const char* aString, uint32_t aLength)
 {
   // XXXbholley: We should be able to do this without converting, I just can't
   // find the right thing to call.
-  nsAutoString atomStr;
-  aAtom->ToString(atomStr);
+  nsDependentAtomString atomStr(aAtom);
   NS_ConvertUTF8toUTF16 inStr(nsDependentCSubstring(aString, aLength));
   return atomStr.Equals(inStr);
 }
 
 bool
 Gecko_AtomEqualsUTF8IgnoreCase(nsIAtom* aAtom, const char* aString, uint32_t aLength)
 {
   // XXXbholley: We should be able to do this without converting, I just can't
   // find the right thing to call.
-  nsAutoString atomStr;
-  aAtom->ToString(atomStr);
+  nsDependentAtomString atomStr(aAtom);
   NS_ConvertUTF8toUTF16 inStr(nsDependentCSubstring(aString, aLength));
   return nsContentUtils::EqualsIgnoreASCIICase(atomStr, inStr);
 }
 
 void
 Gecko_FontFamilyList_Clear(FontFamilyList* aList) {
   aList->Clear();
 }
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -4366,33 +4366,33 @@ nsComputedDOMStyle::DoGetOrder()
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetAlignContent()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   nsAutoString str;
-  auto align = StylePosition()->ComputedAlignContent();
+  auto align = StylePosition()->mAlignContent;
   nsCSSValue::AppendAlignJustifyValueToString(align & NS_STYLE_ALIGN_ALL_BITS, str);
   auto fallback = align >> NS_STYLE_ALIGN_ALL_SHIFT;
   if (fallback) {
     str.Append(' ');
     nsCSSValue::AppendAlignJustifyValueToString(fallback, str);
   }
   val->SetString(str);
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetAlignItems()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   nsAutoString str;
-  auto align = StylePosition()->ComputedAlignItems();
+  auto align = StylePosition()->mAlignItems;
   nsCSSValue::AppendAlignJustifyValueToString(align, str);
   val->SetString(str);
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetAlignSelf()
 {
@@ -4404,17 +4404,17 @@ nsComputedDOMStyle::DoGetAlignSelf()
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetJustifyContent()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   nsAutoString str;
-  auto justify = StylePosition()->ComputedJustifyContent();
+  auto justify = StylePosition()->mJustifyContent;
   nsCSSValue::AppendAlignJustifyValueToString(justify & NS_STYLE_JUSTIFY_ALL_BITS, str);
   auto fallback = justify >> NS_STYLE_JUSTIFY_ALL_SHIFT;
   if (fallback) {
     MOZ_ASSERT(nsCSSProps::ValueToKeywordEnum(fallback & ~NS_STYLE_JUSTIFY_FLAG_BITS,
                                               nsCSSProps::kAlignSelfPosition)
                != eCSSKeyword_UNKNOWN, "unknown fallback value");
     str.Append(' ');
     nsCSSValue::AppendAlignJustifyValueToString(fallback, str);
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1687,17 +1687,17 @@ nsStylePosition::WidthCoordDependsOnCont
 
 uint8_t
 nsStylePosition::UsedAlignSelf(nsStyleContext* aParent) const
 {
   if (mAlignSelf != NS_STYLE_ALIGN_AUTO) {
     return mAlignSelf;
   }
   if (MOZ_LIKELY(aParent)) {
-    auto parentAlignItems = aParent->StylePosition()->ComputedAlignItems();
+    auto parentAlignItems = aParent->StylePosition()->mAlignItems;
     MOZ_ASSERT(!(parentAlignItems & NS_STYLE_ALIGN_LEGACY),
                "align-items can't have 'legacy'");
     return parentAlignItems;
   }
   return NS_STYLE_ALIGN_NORMAL;
 }
 
 uint8_t
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1703,37 +1703,22 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   static nsChangeHint DifferenceAlwaysHandledForDescendants() {
     // CalcDifference can return all of the reflow hints that are
     // sometimes handled for descendants as hints not handled for
     // descendants.
     return nsChangeHint(0);
   }
 
   /**
-   * Return the computed value for 'align-content'.
-   */
-  uint16_t ComputedAlignContent() const { return mAlignContent; }
-
-  /**
-   * Return the computed value for 'align-items'.
-   */
-  uint8_t ComputedAlignItems() const { return mAlignItems; }
-
-  /**
    * Return the used value for 'align-self' given our parent StyleContext
    * aParent (or null for the root).
    */
   uint8_t UsedAlignSelf(nsStyleContext* aParent) const;
 
   /**
-   * Return the computed value for 'justify-content'.
-   */
-  uint16_t ComputedJustifyContent() const { return mJustifyContent; }
-
-  /**
    * Return the computed value for 'justify-items' given our parent StyleContext
    * aParent (or null for the root).
    */
   uint8_t ComputedJustifyItems(nsStyleContext* aParent) const;
 
   /**
    * Return the used value for 'justify-self' given our parent StyleContext
    * aParent (or null for the root).
@@ -1750,25 +1735,27 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   nsStyleCoord  mMaxHeight;             // [reset] coord, percent, calc, none
   nsStyleCoord  mFlexBasis;             // [reset] coord, percent, enum, calc, auto
   nsStyleCoord  mGridAutoColumnsMin;    // [reset] coord, percent, enum, calc, flex
   nsStyleCoord  mGridAutoColumnsMax;    // [reset] coord, percent, enum, calc, flex
   nsStyleCoord  mGridAutoRowsMin;       // [reset] coord, percent, enum, calc, flex
   nsStyleCoord  mGridAutoRowsMax;       // [reset] coord, percent, enum, calc, flex
   uint8_t       mGridAutoFlow;          // [reset] enumerated. See nsStyleConsts.h
   mozilla::StyleBoxSizing mBoxSizing;   // [reset] see nsStyleConsts.h
-private:
-  friend class nsRuleNode;
 
   uint16_t      mAlignContent;          // [reset] fallback value in the high byte
   uint8_t       mAlignItems;            // [reset] see nsStyleConsts.h
-public:
   uint8_t       mAlignSelf;             // [reset] see nsStyleConsts.h
+  uint16_t      mJustifyContent;        // [reset] fallback value in the high byte
 private:
-  uint16_t      mJustifyContent;        // [reset] fallback value in the high byte
+  friend class nsRuleNode;
+
+  // mJustifyItems should only be read via ComputedJustifyItems(), which
+  // lazily resolves its "auto" value. nsRuleNode needs direct access so
+  // it can set mJustifyItems' value when populating this struct.
   uint8_t       mJustifyItems;          // [reset] see nsStyleConsts.h
 public:
   uint8_t       mJustifySelf;           // [reset] see nsStyleConsts.h
   uint8_t       mFlexDirection;         // [reset] see nsStyleConsts.h
   uint8_t       mFlexWrap;              // [reset] see nsStyleConsts.h
   uint8_t       mObjectFit;             // [reset] see nsStyleConsts.h
   int32_t       mOrder;                 // [reset] integer
   float         mFlexGrow;              // [reset] float
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -874,19 +874,25 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
     if (!shouldGenerateClipMaskLayer && !shouldGenerateMaskLayer) {
       MOZ_ASSERT(opacity != 1.0f);
 
       matSR.SetContext(&context);
       SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
                          offsetToUserSpace, true);
     }
 
-    context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
-                                  opacityApplied ?  1.0 : opacity,
-                                  maskSurface, maskTransform);
+    if (aParams.layerManager->GetRoot()->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) {
+      context.PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA,
+                                         opacityApplied ?  1.0 : opacity,
+                                         maskSurface, maskTransform);
+    } else {
+      context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
+                                    opacityApplied ?  1.0 : opacity,
+                                    maskSurface, maskTransform);
+    }
   }
 
   /* If this frame has only a trivial clipPath, set up cairo's clipping now so
    * we can just do normal painting and get it clipped appropriately.
    */
   if (shouldApplyClipPath || shouldApplyBasicShape) {
     context.Save();
     SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
--- a/layout/svg/nsSVGMaskFrame.cpp
+++ b/layout/svg/nsSVGMaskFrame.cpp
@@ -278,27 +278,21 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(gf
   }
   RefPtr<DataSourceSurface> maskSurface = maskSnapshot->GetDataSurface();
   DataSourceSurface::MappedSurface map;
   if (!maskSurface->Map(DataSourceSurface::MapType::READ, &map)) {
     return nullptr;
   }
 
   // Create alpha channel mask for output
-  RefPtr<DrawTarget> destMaskDT =
-    Factory::CreateDrawTarget(BackendType::CAIRO, maskSurfaceSize,
-                              SurfaceFormat::A8);
-  if (!destMaskDT) {
+  RefPtr<DataSourceSurface> destMaskSurface =
+    Factory::CreateDataSourceSurface(maskSurfaceSize, SurfaceFormat::A8);
+  if (!destMaskSurface) {
     return nullptr;
   }
-  RefPtr<SourceSurface> destMaskSnapshot = destMaskDT->Snapshot();
-  if (!destMaskSnapshot) {
-    return nullptr;
-  }
-  RefPtr<DataSourceSurface> destMaskSurface = destMaskSnapshot->GetDataSurface();
   DataSourceSurface::MappedSurface destMap;
   if (!destMaskSurface->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
     return nullptr;
   }
 
   uint8_t maskType;
   if (aMaskOp == NS_STYLE_MASK_MODE_MATCH_SOURCE) {
     maskType = StyleSVGReset()->mMaskType;
--- a/parser/html/javasrc/HtmlAttributes.java
+++ b/parser/html/javasrc/HtmlAttributes.java
@@ -57,17 +57,17 @@ public final class HtmlAttributes implem
 
     private int mode;
 
     private int length;
 
     private @Auto AttributeName[] names;
 
     private @Auto String[] values; // XXX perhaps make this @NoLength?
-
+    
     // CPPONLY: private @Auto int[] lines; // XXX perhaps make this @NoLength?
 
     // [NOCPP[
 
     private String idValue;
 
     private int xmlnsLength;
 
--- a/parser/html/javasrc/Tokenizer.java
+++ b/parser/html/javasrc/Tokenizer.java
@@ -812,16 +812,18 @@ public class Tokenizer implements Locato
             // ]NOCPP]
             return HtmlAttributes.EMPTY_ATTRIBUTES;
             // [NOCPP[
         }
         // ]NOCPP]
     }
 
     @Inline private void appendCharRefBuf(char c) {
+        // CPPONLY: assert charRefBufLen < charRefBuf.length:
+        // CPPONLY:     "RELEASE: Attempted to overrun charRefBuf!";
         charRefBuf[charRefBufLen++] = c;
     }
 
     @Inline private void clearCharRefBufAndAppend(char c) {
         charRefBuf[0] = c;
         charRefBufLen = 1;
     }
 
@@ -845,17 +847,23 @@ public class Tokenizer implements Locato
     }
 
     /**
      * Appends to the buffer.
      * 
      * @param c
      *            the UTF-16 code unit to append
      */
-    private void appendStrBuf(char c) {
+    @Inline private void appendStrBuf(char c) {
+        // CPPONLY: assert strBufLen < strBuf.length: "Previous buffer length insufficient.";
+        // CPPONLY: if (strBufLen == strBuf.length) {
+        // CPPONLY:     if (!EnsureBufferSpace(1)) {
+        // CPPONLY:         assert false: "RELEASE: Unable to recover from buffer reallocation failure";
+        // CPPONLY:     } // TODO: Add telemetry when outer if fires but inner does not
+        // CPPONLY: }
         strBuf[strBufLen++] = c;
     }
 
     /**
      * The buffer as a String. Currently only used for error reporting.
      * 
      * <p>
      * C++ memory note: The return value must be released.
@@ -946,24 +954,25 @@ public class Tokenizer implements Locato
             case FATAL:
                 fatal("The document is not mappable to XML 1.0 due to two consecutive hyphens in a comment.");
                 break;
         }
         // ]NOCPP]
     }
 
     private void appendStrBuf(@NoLength char[] buffer, int offset, int length) {
-        int reqLen = strBufLen + length;
-        if (strBuf.length < reqLen) {
-            char[] newBuf = new char[reqLen + (reqLen >> 1)];
-            System.arraycopy(strBuf, 0, newBuf, 0, strBuf.length);
-            strBuf = newBuf;
-        }
+        int newLen = strBufLen + length;
+        // CPPONLY: assert newLen <= strBuf.length: "Previous buffer length insufficient.";
+        // CPPONLY: if (strBuf.length < newLen) {
+        // CPPONLY:     if (!EnsureBufferSpace(length)) {
+        // CPPONLY:         assert false: "RELEASE: Unable to recover from buffer reallocation failure";
+        // CPPONLY:     } // TODO: Add telemetry when outer if fires but inner does not
+        // CPPONLY: }
         System.arraycopy(buffer, offset, strBuf, strBufLen, length);
-        strBufLen = reqLen;
+        strBufLen = newLen;
     }
 
     /**
      * Append the contents of the char reference buffer to the main one.
      */
     @Inline private void appendCharRefBufToStrBuf() {
         appendStrBuf(charRefBuf, 0, charRefBufLen);
     }
--- a/parser/html/nsHtml5Tokenizer.cpp
+++ b/parser/html/nsHtml5Tokenizer.cpp
@@ -216,22 +216,16 @@ nsHtml5Tokenizer::emitOrAppendCharRefBuf
     appendCharRefBufToStrBuf();
   } else {
     if (charRefBufLen > 0) {
       tokenHandler->characters(charRefBuf, 0, charRefBufLen);
     }
   }
 }
 
-void 
-nsHtml5Tokenizer::appendStrBuf(char16_t c)
-{
-  strBuf[strBufLen++] = c;
-}
-
 nsString* 
 nsHtml5Tokenizer::strBufToString()
 {
   return nsHtml5Portability::newStringFromBuffer(strBuf, 0, strBufLen, tokenHandler);
 }
 
 void 
 nsHtml5Tokenizer::strBufToDoctypeName()
@@ -245,24 +239,25 @@ nsHtml5Tokenizer::emitStrBuf()
   if (strBufLen > 0) {
     tokenHandler->characters(strBuf, 0, strBufLen);
   }
 }
 
 void 
 nsHtml5Tokenizer::appendStrBuf(char16_t* buffer, int32_t offset, int32_t length)
 {
-  int32_t reqLen = strBufLen + length;
-  if (strBuf.length < reqLen) {
-    jArray<char16_t,int32_t> newBuf = jArray<char16_t,int32_t>::newJArray(reqLen + (reqLen >> 1));
-    nsHtml5ArrayCopy::arraycopy(strBuf, newBuf, strBuf.length);
-    strBuf = newBuf;
+  int32_t newLen = strBufLen + length;
+  MOZ_ASSERT(newLen <= strBuf.length, "Previous buffer length insufficient.");
+  if (MOZ_UNLIKELY(strBuf.length < newLen)) {
+    if (MOZ_UNLIKELY(!EnsureBufferSpace(length))) {
+      MOZ_CRASH("Unable to recover from buffer reallocation failure");
+    }
   }
   nsHtml5ArrayCopy::arraycopy(buffer, offset, strBuf, strBufLen, length);
-  strBufLen = reqLen;
+  strBufLen = newLen;
 }
 
 void 
 nsHtml5Tokenizer::emitComment(int32_t provisionalHyphens, int32_t pos)
 {
   tokenHandler->comment(strBuf, 0, strBufLen - provisionalHyphens);
   cstart = pos + 1;
 }
--- a/parser/html/nsHtml5Tokenizer.h
+++ b/parser/html/nsHtml5Tokenizer.h
@@ -153,16 +153,17 @@ class nsHtml5Tokenizer
     {
       return line;
     }
 
     nsHtml5HtmlAttributes* emptyAttributes();
   private:
     inline void appendCharRefBuf(char16_t c)
     {
+      MOZ_RELEASE_ASSERT(charRefBufLen < charRefBuf.length, "Attempted to overrun charRefBuf!");
       charRefBuf[charRefBufLen++] = c;
     }
 
     inline void clearCharRefBufAndAppend(char16_t c)
     {
       charRefBuf[0] = c;
       charRefBufLen = 1;
     }
@@ -174,17 +175,27 @@ class nsHtml5Tokenizer
       strBufLen = 1;
     }
 
     inline void clearStrBuf()
     {
       strBufLen = 0;
     }
 
-    void appendStrBuf(char16_t c);
+    inline void appendStrBuf(char16_t c)
+    {
+      MOZ_ASSERT(strBufLen < strBuf.length, "Previous buffer length insufficient.");
+      if (MOZ_UNLIKELY(strBufLen == strBuf.length)) {
+        if (MOZ_UNLIKELY(!EnsureBufferSpace(1))) {
+          MOZ_CRASH("Unable to recover from buffer reallocation failure");
+        }
+      }
+      strBuf[strBufLen++] = c;
+    }
+
   protected:
     nsString* strBufToString();
   private:
     void strBufToDoctypeName();
     void emitStrBuf();
     inline void appendSecondHyphenToBogusComment()
     {
       appendStrBuf('-');
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -950,18 +950,18 @@ nsHtml5TreeBuilder::elementPopped(int32_
     mSpeculativeLoadQueue.AppendElement()->InitEndPicture();
   }
   return;
 }
 
 void
 nsHtml5TreeBuilder::accumulateCharacters(const char16_t* aBuf, int32_t aStart, int32_t aLength)
 {
-  MOZ_ASSERT(charBufferLen + aLength <= charBuffer.length,
-             "About to memcpy past the end of the buffer!");
+  MOZ_RELEASE_ASSERT(charBufferLen + aLength <= charBuffer.length,
+                     "About to memcpy past the end of the buffer!");
   memcpy(charBuffer + charBufferLen, aBuf + aStart, sizeof(char16_t) * aLength);
   charBufferLen += aLength;
 }
 
 bool
 nsHtml5TreeBuilder::EnsureBufferSpace(size_t aLength)
 {
   // TODO: Unify nsHtml5Tokenizer::strBuf and nsHtml5TreeBuilder::charBuffer
--- a/security/nss/.gitignore
+++ b/security/nss/.gitignore
@@ -1,5 +1,15 @@
 *~
 *.swp
 *OPT.OBJ/
 *DBG.OBJ/
 *DBG.OBJD/
+*.bak
+*.out
+*.rej
+*.patch
+GPATH
+GRTAGS
+GTAGS
+#*
+.#*
+.ycm_extra_conf.py*
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-233a44e96b22
+e7553afc7665
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -40,21 +40,16 @@ queue.filter(task => {
     }
   }
 
   return true;
 });
 
 queue.map(task => {
   if (task.collection == "asan") {
-    // Disable LSan on BoGo runs, for now.
-    if (task.tests == "bogo") {
-      task.env.ASAN_OPTIONS = "detect_leaks=0";
-    }
-
     // CRMF and FIPS tests still leak, unfortunately.
     if (task.tests == "crmf" || task.tests == "fips") {
       task.env.ASAN_OPTIONS = "detect_leaks=0";
     }
 
     // SSL(standard) runs on ASan take some time.
     if (task.tests == "ssl" && task.cycle == "standard") {
       task.maxRunTime = 7200;
@@ -63,16 +58,20 @@ queue.map(task => {
 
   if (task.collection == "arm-debug") {
     // These tests take quite some time on our poor ARM devices.
     if (task.tests == "chains" || (task.tests == "ssl" && task.cycle == "standard")) {
       task.maxRunTime = 14400;
     }
   }
 
+  // Enable TLS 1.3 for every task.
+  task.env = task.env || {};
+  task.env.NSS_ENABLE_TLS_1_3 = "1";
+
   return task;
 });
 
 /*****************************************************************************/
 
 export default async function main() {
   await scheduleLinux("Linux 32 (opt)", {
     env: {BUILD_OPT: "1"},
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/external_tests/der_gtest/der_getint_unittest.cc
+++ b/security/nss/external_tests/der_gtest/der_getint_unittest.cc
@@ -43,31 +43,43 @@ TEST_F(DERIntegerDecodingTest, DecodeLon
   TestGetInteger(-126, der, sizeof(der));
 }
 
 TEST_F(DERIntegerDecodingTest, DecodeLong130) {
   unsigned char der[] = {0x00, 0x82};
   TestGetInteger(130, der, sizeof(der));
 }
 
+TEST_F(DERIntegerDecodingTest, DecodeLong130Padded) {
+  unsigned char der[sizeof(long) * 2] = {0};
+  der[sizeof(der) - 1] = {0x82};
+  TestGetInteger(130, der, sizeof(der));
+}
+
 TEST_F(DERIntegerDecodingTest, DecodeLong0) {
   unsigned char der[] = {0x00};
   TestGetInteger(0, der, sizeof(der));
 }
 
 TEST_F(DERIntegerDecodingTest, DecodeLong1) {
   unsigned char der[] = {0x01};
   TestGetInteger(1, der, sizeof(der));
 }
 
 TEST_F(DERIntegerDecodingTest, DecodeLongMinus1) {
   unsigned char der[] = {0xFF};
   TestGetInteger(-1, der, sizeof(der));
 }
 
+TEST_F(DERIntegerDecodingTest, DecodeLongMinus1Padded) {
+  unsigned char der[sizeof(long) * 2];
+  memset(der, 0xFF, sizeof(der));
+  TestGetInteger(-1, der, sizeof(der));
+}
+
 TEST_F(DERIntegerDecodingTest, DecodeLongMax) {
   unsigned char der[sizeof(long)];
   GetDerLongMax(der, sizeof(long));
   TestGetInteger(LONG_MAX, der, sizeof(der));
 }
 
 TEST_F(DERIntegerDecodingTest, DecodeLongMin) {
   unsigned char der[sizeof(long)];
--- a/security/nss/external_tests/nss_bogo_shim/config.h
+++ b/security/nss/external_tests/nss_bogo_shim/config.h
@@ -10,26 +10,29 @@
 
 #ifndef config_h_
 #define config_h_
 
 #include <cassert>
 
 #include <iostream>
 #include <map>
+#include <memory>
 #include <queue>
 #include <string>
 #include <typeinfo>
 
 // Abstract base class for a given config flag.
 class ConfigEntryBase {
  public:
   ConfigEntryBase(const std::string& name, const std::string& type)
       : name_(name), type_(type) {}
 
+  virtual ~ConfigEntryBase() {}
+
   const std::string& type() const { return type_; }
   virtual bool Parse(std::queue<const char*>* args) = 0;
 
  protected:
   bool ParseInternal(std::queue<const char*>* args, std::string* out);
   bool ParseInternal(std::queue<const char*>* args, int* out);
   bool ParseInternal(std::queue<const char*>* args, bool* out);
 
@@ -57,33 +60,34 @@ class ConfigEntry : public ConfigEntryBa
 class Config {
  public:
   enum Status { kOK, kUnknownFlag, kMalformedArgument, kMissingValue };
 
   Config() : entries_() {}
 
   template <typename T>
   void AddEntry(const std::string& name, T init) {
-    entries_[name] = new ConfigEntry<T>(name, init);
+    entries_[name] = std::unique_ptr<ConfigEntryBase>(
+      new ConfigEntry<T>(name, init));
   }
 
   Status ParseArgs(int argc, char** argv);
 
   template <typename T>
   T get(const std::string& key) const {
     auto e = entry(key);
     assert(e->type() == typeid(T).name());
     return static_cast<const ConfigEntry<T>*>(e)->get();
   }
 
  private:
   static std::string XformFlag(const std::string& arg);
 
-  std::map<std::string, ConfigEntryBase*> entries_;
+  std::map<std::string, std::unique_ptr<ConfigEntryBase>> entries_;
 
   const ConfigEntryBase* entry(const std::string& key) const {
     auto e = entries_.find(key);
     if (e == entries_.end()) return nullptr;
-    return e->second;
+    return e->second.get();
   }
 };
 
 #endif  // config_h_
--- a/security/nss/external_tests/nss_bogo_shim/config.json
+++ b/security/nss/external_tests/nss_bogo_shim/config.json
@@ -6,19 +6,19 @@
         "SendSNIWarningAlert":"Draft version mismatch (NSS=15, BoGo=14)",
         "UnknownCipher":"Draft version mismatch (NSS=15, BoGo=14)",
         "UnknownCurve":"Draft version mismatch (NSS=15, BoGo=14)",
         "*TrailingMessageData*":"Draft version mismatch (NSS=15, BoGo=14)",
         "*Downgrade-TLS12*":"Draft version mismatch (NSS=15, BoGo=14)",
         "UnknownCurve":"Draft version mismatch (NSS=15, BoGo=14)",
         "*MissingKeyShare*":"Draft version mismatch (NSS=15, BoGo=14)",
         "SecondClientHelloWrongCurve":"Draft version mismatch (NSS=15, BoGo=14)",
-        "SendHelloRetryRequest":"Expects CurveX25519",
-        "SendHelloRetryRequest-2":"Expects CurveX25519",
-        "KeyUpdate":"KeyUpdate Unimplemented",
+        "*HelloRetryRequest*":"Draft version mismatch (NSS=15, BoGo=14)",
+        "*PartialEncryptedExtensionsWithServerHello*":"Draft version mismatch (NSS=15, BoGo=14)",
+        "*KeyUpdate*":"KeyUpdate Unimplemented",
         "ClientAuth-NoFallback-TLS13":"Disagreement about alerts. Bug 1294975",
         "ClientAuth-SHA1-Fallback":"Disagreement about alerts. Bug 1294975",
         "SendWarningAlerts-TLS13":"NSS needs to trigger on warning alerts",
         "NoSupportedCurves":"This tests a non-spec behavior for TLS 1.2 and expects the wrong alert for TLS 1.3",
         "SendEmptyRecords":"Tests a non-spec behavior in BoGo where it chokes on too many empty records",
         "LargePlaintext":"NSS needs to check for over-long records. Bug 1294978",
         "TLS13-RC4-MD5-server":"This fails properly but returns an unexpected error. Not a bug but needs cleanup",
         "*VersionTolerance":"BoGo expects us to negotiate 1.3 but we negotiate 1.2 because BoGo didn't send draft version",
--- a/security/nss/external_tests/nss_bogo_shim/nss_bogo_shim.cc
+++ b/security/nss/external_tests/nss_bogo_shim/nss_bogo_shim.cc
@@ -13,16 +13,18 @@
 #include "prio.h"
 #include "prnetdb.h"
 #include "ssl.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 #include "nsskeys.h"
 
+bool exitCodeUnimplemented = false;
+
 std::string FormatError(PRErrorCode code) {
   return std::string(":") + PORT_ErrorToName(code) + ":" + ":" +
          PORT_ErrorToString(code);
 }
 
 class TestAgent {
  public:
   TestAgent(const Config& cfg)
@@ -104,34 +106,33 @@ class TestAgent {
     return true;
   }
 
   bool SetupKeys() {
     SECStatus rv;
 
     if (cfg_.get<std::string>("key-file") != "") {
       key_ = ReadPrivateKey(cfg_.get<std::string>("key-file"));
-      if (!key_) exit(89);  // Temporary to handle our inability to handle ECDSA
+      if (!key_) {
+        // Temporary to handle our inability to handle ECDSA.
+        exitCodeUnimplemented = true;
+        return false;
+      }
     }
     if (cfg_.get<std::string>("cert-file") != "") {
       cert_ = ReadCertificate(cfg_.get<std::string>("cert-file"));
       if (!cert_) return false;
     }
     if (cfg_.get<bool>("server")) {
       // Server
       rv = SSL_ConfigServerCert(ssl_fd_, cert_, key_, nullptr, 0);
       if (rv != SECSuccess) {
         std::cerr << "Couldn't configure server cert\n";
         return false;
       }
-      rv = SSL_ConfigServerSessionIDCache(1024, 0, 0, ".");
-      if (rv != SECSuccess) {
-        std::cerr << "Couldn't configure session cache\n";
-        return false;
-      }
     } else {
       // Client.
 
       // Needed because server certs are not entirely valid.
       rv = SSL_AuthCertificateHook(ssl_fd_, AuthCertificateHook, this);
       if (rv != SECSuccess) return false;
 
       if (key_ && cert_) {
@@ -268,48 +269,72 @@ std::unique_ptr<const Config> ReadConfig
   cfg->AddEntry<std::string>("key-file", "");
   cfg->AddEntry<std::string>("cert-file", "");
 
   auto rv = cfg->ParseArgs(argc, argv);
   switch (rv) {
     case Config::kOK:
       break;
     case Config::kUnknownFlag:
-      exit(89);
-      break;
+      exitCodeUnimplemented = true;
     default:
-      exit(1);
+      return nullptr;
   }
 
   // Needed to change to std::unique_ptr<const Config>
   return std::move(cfg);
 }
 
-void RunCycle(std::unique_ptr<const Config>& cfg) {
+
+bool RunCycle(std::unique_ptr<const Config>& cfg) {
   std::unique_ptr<TestAgent> agent(TestAgent::Create(*cfg));
-  if (!agent) {
-    exit(1);
+  return agent && agent->DoExchange() == SECSuccess;
+}
+
+int GetExitCode(bool success) {
+  if (exitCodeUnimplemented) {
+    return 89;
   }
 
-  SECStatus rv = agent->DoExchange();
-  if (rv) {
-    exit(1);
+  if (success) {
+    return 0;
   }
+
+  return 1;
 }
 
 int main(int argc, char** argv) {
   std::unique_ptr<const Config> cfg = ReadConfig(argc, argv);
+  if (!cfg) {
+    return GetExitCode(false);
+  }
 
-  SECStatus rv = NSS_NoDB_Init(nullptr);
-  if (rv != SECSuccess) return 1;
-  rv = NSS_SetDomesticPolicy();
-  if (rv != SECSuccess) return 1;
+  if (cfg->get<bool>("server")) {
+    if (SSL_ConfigServerSessionIDCache(1024, 0, 0, ".") != SECSuccess) {
+      std::cerr << "Couldn't configure session cache\n";
+      return 1;
+    }
+  }
+
+  if (NSS_NoDB_Init(nullptr) != SECSuccess) {
+    return 1;
+  }
 
   // Run a single test cycle.
-  RunCycle(cfg);
+  bool success = RunCycle(cfg);
 
-  if (cfg->get<bool>("resume")) {
+  if (success && cfg->get<bool>("resume")) {
     std::cout << "Resuming" << std::endl;
-    RunCycle(cfg);
+    success = RunCycle(cfg);
   }
 
-  exit(0);
+  SSL_ClearSessionCache();
+
+  if (cfg->get<bool>("server")) {
+    SSL_ShutdownServerSessionIDCache();
+  }
+
+  if (NSS_Shutdown() != SECSuccess) {
+    success = false;
+  }
+
+  return GetExitCode(success);
 }
--- a/security/nss/external_tests/ssl_gtest/Makefile
+++ b/security/nss/external_tests/ssl_gtest/Makefile
@@ -28,16 +28,20 @@ include $(CORE_DEPTH)/coreconf/config.mk
 include ../common/gtest.mk
 
 CFLAGS += -I$(CORE_DEPTH)/lib/ssl
 
 ifdef NSS_SSL_ENABLE_ZLIB
 include $(CORE_DEPTH)/coreconf/zlib.mk
 endif
 
+ifndef NSS_ENABLE_TLS_1_3
+NSS_DISABLE_TLS_1_3=1
+endif
+
 ifdef NSS_DISABLE_TLS_1_3
 # Run parameterized tests only, for which we can easily exclude TLS 1.3
 CPPSRCS := $(filter-out $(shell grep -l '^TEST_F' $(CPPSRCS)), $(CPPSRCS))
 CFLAGS += -DNSS_DISABLE_TLS_1_3
 endif
 
 #######################################################################
 # (5) Execute "global" rules. (OPTIONAL)                              #
--- a/security/nss/external_tests/ssl_gtest/libssl_internals.c
+++ b/security/nss/external_tests/ssl_gtest/libssl_internals.c
@@ -63,16 +63,19 @@ SECStatus SSLInt_UpdateSSLv2ClientRandom
     return rv;
   }
 
   rv = ssl3_RestartHandshakeHashes(ss);
   if (rv != SECSuccess) {
     return rv;
   }
 
+  // Ensure we don't overrun hs.client_random.
+  rnd_len = PR_MIN(SSL3_RANDOM_LENGTH, rnd_len);
+
   // Zero the client_random struct.
   PORT_Memset(&ss->ssl3.hs.client_random, 0, SSL3_RANDOM_LENGTH);
 
   // Copy over the challenge bytes.
   size_t offset = SSL3_RANDOM_LENGTH - rnd_len;
   PORT_Memcpy(&ss->ssl3.hs.client_random.rand[offset], rnd, rnd_len);
 
   // Rehash the SSLv2 client hello message.
--- a/security/nss/external_tests/ssl_gtest/ssl_agent_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_agent_unittest.cc
@@ -13,61 +13,36 @@
 #include "databuffer.h"
 #include "tls_agent.h"
 #include "tls_connect.h"
 #include "tls_filter.h"
 #include "tls_parser.h"
 
 namespace nss_test {
 
-// This is a 1-RTT ClientHello with ECDHE and DHE.
+// This is a 1-RTT ClientHello with ECDHE.
 const static uint8_t kCannedTls13ClientHello[] = {
-    0x01, 0x00, 0x01, 0xfc, 0x03, 0x04, 0x77, 0x5c, 0x3a, 0xd8, 0x3f, 0x43,
-    0x63, 0x98, 0xfa, 0x68, 0xfb, 0x01, 0x39, 0xff, 0x7c, 0x1a, 0x51, 0xa7,
-    0x92, 0xda, 0x97, 0xf5, 0x15, 0x78, 0xb3, 0xbb, 0x26, 0xa7, 0xed, 0x6f,
-    0x69, 0x71, 0x00, 0x00, 0x2a, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc,
-    0xa8, 0x13, 0x01, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9e, 0xcc,
-    0xaa, 0x00, 0x33, 0x00, 0x32, 0x00, 0x39, 0x00, 0x38, 0x00, 0x16, 0x00,
-    0x13, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x01,
-    0x00, 0x01, 0xa9, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x09, 0x00, 0x00, 0x06,
+    0x01, 0x00, 0x00, 0xcf, 0x03, 0x03, 0x6c, 0xb3, 0x46, 0x81, 0xc8, 0x1a,
+    0xf9, 0xd2, 0x05, 0x97, 0x48, 0x7c, 0xa8, 0x31, 0x03, 0x1c, 0x06, 0xa8,
+    0x62, 0xb1, 0x90, 0xd6, 0x21, 0x44, 0x7f, 0xc1, 0x9b, 0x87, 0x3e, 0xad,
+    0x91, 0x85, 0x00, 0x00, 0x06, 0x13, 0x01, 0x13, 0x03, 0x13, 0x02, 0x01,
+    0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x09, 0x00, 0x00, 0x06,
     0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00,
-    0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01,
-    0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0xff, 0x02, 0x00, 0x02, 0x00,
-    0x0f, 0x00, 0x28, 0x01, 0x4b, 0x01, 0x49, 0x00, 0x17, 0x00, 0x41, 0x04,
-    0xbf, 0x31, 0xb4, 0x29, 0x96, 0xf4, 0xe6, 0x4a, 0xe3, 0xea, 0x87, 0x05,
-    0x38, 0x0e, 0x68, 0x02, 0xbc, 0x4a, 0x5d, 0x90, 0xed, 0xe7, 0xaa, 0x8e,
-    0xb8, 0x42, 0x84, 0xaa, 0x3a, 0x4f, 0x2b, 0xe3, 0x52, 0x9a, 0x9a, 0x76,
-    0xab, 0xf8, 0x2e, 0x59, 0xea, 0xcd, 0x2b, 0x2f, 0x03, 0x18, 0xd2, 0x0c,
-    0xc9, 0x07, 0x15, 0xca, 0xe6, 0x61, 0xf7, 0x79, 0x9f, 0xfe, 0xc5, 0x10,
-    0x40, 0x9e, 0x38, 0x33, 0x01, 0x00, 0x01, 0x00, 0xd8, 0x80, 0x1f, 0x06,
-    0x9a, 0xbb, 0xf7, 0xbb, 0xd4, 0x5c, 0x75, 0x1d, 0x8e, 0x09, 0x27, 0xad,
-    0x08, 0xb8, 0x16, 0x0f, 0x4f, 0x50, 0x79, 0xe1, 0x7e, 0xd4, 0x3b, 0xc0,
-    0x57, 0xcc, 0x00, 0x5e, 0x28, 0xd8, 0xb3, 0x16, 0x7f, 0x36, 0x48, 0x75,
-    0x8d, 0x03, 0xa4, 0x71, 0x86, 0x06, 0xf0, 0xe7, 0x57, 0x47, 0x35, 0xf0,
-    0x04, 0xfb, 0xf7, 0x6c, 0x7a, 0xdd, 0x05, 0x93, 0x53, 0x16, 0x12, 0x49,
-    0xbe, 0x35, 0x67, 0x47, 0x6e, 0x3a, 0x91, 0xef, 0x50, 0x09, 0x14, 0x98,
-    0x8b, 0x83, 0xc4, 0x62, 0x77, 0xf3, 0x57, 0x53, 0x3f, 0xf4, 0x82, 0xc0,
-    0x70, 0x25, 0x19, 0x9d, 0x93, 0xe2, 0xb9, 0x7b, 0xb4, 0x83, 0x31, 0xef,
-    0xd8, 0x3b, 0xd5, 0x25, 0x70, 0x64, 0x29, 0xa2, 0xc2, 0xc5, 0x73, 0x9a,
-    0xfe, 0x27, 0xca, 0xc0, 0x55, 0x34, 0x91, 0x95, 0x05, 0xbf, 0x5e, 0x54,
-    0x4d, 0x95, 0x43, 0x3d, 0x54, 0x6a, 0x89, 0x0b, 0x5e, 0xab, 0x08, 0x7b,
-    0xf8, 0x38, 0x0a, 0x56, 0x51, 0x9d, 0xbc, 0xdd, 0x46, 0xa9, 0xfc, 0x95,
-    0xe9, 0x75, 0x1c, 0xc8, 0x18, 0x7f, 0xed, 0xa9, 0xca, 0xb6, 0x5e, 0x77,
-    0x63, 0x33, 0xb1, 0xb5, 0x68, 0xce, 0xa5, 0x98, 0xec, 0x8c, 0x34, 0x98,
-    0x1c, 0xa9, 0xa5, 0x84, 0xec, 0xe6, 0xba, 0x0b, 0x11, 0xbf, 0x40, 0xa5,
-    0xf0, 0x3c, 0xd5, 0xd3, 0xac, 0x2f, 0x46, 0xed, 0xab, 0xc0, 0xc1, 0x78,
-    0x3f, 0x18, 0x64, 0x5b, 0xff, 0x31, 0xeb, 0x74, 0x06, 0x92, 0x42, 0x1e,
-    0x90, 0xf7, 0xea, 0xa5, 0x02, 0x33, 0x8e, 0x01, 0xe3, 0xfa, 0x70, 0x82,
-    0xe5, 0xe7, 0x67, 0x8b, 0x96, 0x20, 0x13, 0x2e, 0x65, 0x86, 0xab, 0x28,
-    0xc8, 0x1b, 0xfe, 0xb4, 0x98, 0xed, 0xa4, 0xa0, 0xee, 0xf9, 0x53, 0x74,
-    0x30, 0xac, 0x79, 0x2d, 0xf2, 0x92, 0xd0, 0x5e, 0x10, 0xd7, 0xb9, 0x41,
-    0x00, 0x0d, 0x00, 0x18, 0x00, 0x16, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01,
-    0x02, 0x01, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03, 0x05, 0x02,
-    0x04, 0x02, 0x02, 0x02, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    0x0a, 0x00, 0x12, 0x00, 0x10, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01,
+    0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x00, 0x28, 0x00,
+    0x47, 0x00, 0x45, 0x00, 0x17, 0x00, 0x41, 0x04, 0x86, 0x4a, 0xb9, 0xdc,
+    0x6a, 0x38, 0xa7, 0xce, 0xe7, 0xc2, 0x4f, 0xa6, 0x28, 0xb9, 0xdc, 0x65,
+    0xbf, 0x73, 0x47, 0x3c, 0x9c, 0x65, 0x8c, 0x47, 0x6d, 0x57, 0x22, 0x8a,
+    0xc2, 0xb3, 0xc6, 0x80, 0x72, 0x86, 0x08, 0x86, 0x8f, 0x52, 0xc5, 0xcb,
+    0xbf, 0x2a, 0xb5, 0x59, 0x64, 0xcc, 0x0c, 0x49, 0x95, 0x36, 0xe4, 0xd9,
+    0x2f, 0xd4, 0x24, 0x66, 0x71, 0x6f, 0x5d, 0x70, 0xe2, 0xa0, 0xea, 0x26,
+    0x00, 0x2b, 0x00, 0x03, 0x02, 0x7f, 0x10, 0x00, 0x0d, 0x00, 0x20, 0x00,
+    0x1e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03, 0x08, 0x04, 0x08,
+    0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x01, 0x04,
+    0x02, 0x05, 0x02, 0x06, 0x02, 0x02, 0x02};
 
 const static uint8_t kCannedTls13ServerHello[] = {
     0x03, 0x04, 0x21, 0x12, 0xa7, 0xa7, 0x0d, 0x85, 0x8b, 0xb8, 0x0c, 0xbb,
     0xdc, 0xa6, 0xfd, 0x97, 0xfe, 0x31, 0x26, 0x49, 0x2d, 0xa8, 0x6c, 0x7b,
     0x65, 0x30, 0x71, 0x00, 0x31, 0x03, 0x2b, 0x94, 0xe2, 0x16, 0x13, 0x01,
     0x00, 0x4d, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x28, 0x00, 0x45, 0x00, 0x17,
     0x00, 0x41, 0x04, 0x10, 0x97, 0x3d, 0x7a, 0xcf, 0xa2, 0x34, 0xe3, 0x69,
     0xc4, 0xdd, 0x1e, 0xf2, 0xd6, 0xc0, 0x9a, 0x3e, 0xf5, 0x41, 0xf3, 0x03,
--- a/security/nss/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_ciphersuite_unittest.cc
@@ -298,22 +298,24 @@ static const SSLNamedGroup kDummyNamedGr
     static_cast<SSLNamedGroup>(0)};
 static const auto kDummyNamedGroupParams =
     ::testing::ValuesIn(kDummyNamedGroupParamsArr);
 static const TlsSignatureScheme kDummySignatureSchemesParamsArr[] = {
     kTlsSignatureNone};
 static const auto kDummySignatureSchemesParams =
     ::testing::ValuesIn(kDummySignatureSchemesParamsArr);
 
+#ifndef NSS_DISABLE_TLS_1_3
 static TlsSignatureScheme kSignatureSchemesParamsArr[] = {
     kTlsSignatureRsaPkcs1Sha256,       kTlsSignatureRsaPkcs1Sha384,
     kTlsSignatureRsaPkcs1Sha512,       kTlsSignatureEcdsaSecp256r1Sha256,
     kTlsSignatureEcdsaSecp384r1Sha384, kTlsSignatureRsaPssSha256,
     kTlsSignatureRsaPssSha384,         kTlsSignatureRsaPssSha512,
 };
+#endif
 
 INSTANTIATE_CIPHER_TEST_P(RC4, Stream, V10ToV12, kDummyNamedGroupParams,
                           kDummySignatureSchemesParams,
                           TLS_RSA_WITH_RC4_128_SHA,
                           TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
                           TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
                           TLS_ECDH_RSA_WITH_RC4_128_SHA,
                           TLS_ECDHE_RSA_WITH_RC4_128_SHA);
@@ -358,25 +360,27 @@ INSTANTIATE_CIPHER_TEST_P(
     CBCDatagram, Datagram, V11V12, kDummyNamedGroupParams,
     kDummySignatureSchemesParams, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
     TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
     TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
     TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
     TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
     TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
     TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA);
+#ifndef NSS_DISABLE_TLS_1_3
 INSTANTIATE_CIPHER_TEST_P(TLS13, All, V13,
                           ::testing::ValuesIn(kFasterDHEGroups),
                           ::testing::ValuesIn(kSignatureSchemesParamsArr),
                           TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256,
                           TLS_AES_256_GCM_SHA384);
 INSTANTIATE_CIPHER_TEST_P(TLS13AllGroups, All, V13,
                           ::testing::ValuesIn(kAllDHEGroups),
                           ::testing::Values(kTlsSignatureEcdsaSecp384r1Sha384),
                           TLS_AES_256_GCM_SHA384);
+#endif
 
 // Fields are: version, cipher suite, bulk cipher name, secretKeySize
 struct SecStatusParams {
   uint16_t version;
   uint16_t cipher_suite;
   std::string name;
   int keySize;
 };
--- a/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_extension_unittest.cc
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ssl.h"
+#include "ssl3prot.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 #include <memory>
 
 #include "tls_connect.h"
 #include "tls_filter.h"
 #include "tls_parser.h"
@@ -176,16 +177,26 @@ class TlsExtensionTest12
 };
 
 class TlsExtensionTest13 : public TlsExtensionTestBase,
                            public ::testing::WithParamInterface<std::string> {
  public:
   TlsExtensionTest13()
       : TlsExtensionTestBase(TlsConnectTestBase::ToMode(GetParam()),
                              SSL_LIBRARY_VERSION_TLS_1_3) {}
+
+  void ConnectWithReplacementVersionList(uint16_t version) {
+    DataBuffer versions_buf;
+
+    size_t index = versions_buf.Write(0, 2, 1);
+    versions_buf.Write(index, version, 2);
+    client_->SetPacketFilter(new TlsExtensionReplacer(
+        ssl_tls13_supported_versions_xtn, versions_buf));
+    ConnectExpectFail();
+  }
 };
 
 class TlsExtensionTest13Stream : public TlsExtensionTestBase {
  public:
   TlsExtensionTest13Stream()
       : TlsExtensionTestBase(STREAM, SSL_LIBRARY_VERSION_TLS_1_3) {}
 };
 
@@ -486,52 +497,16 @@ TEST_P(TlsExtensionTest12, SignatureAlgo
 
 // Temporary test to verify that we choke on an empty ClientKeyShare.
 // This test will fail when we implement HelloRetryRequest.
 TEST_P(TlsExtensionTest13, EmptyClientKeyShare) {
   ClientHelloErrorTest(new TlsExtensionTruncator(ssl_tls13_key_share_xtn, 2),
                        kTlsAlertHandshakeFailure);
 }
 
-TEST_P(TlsExtensionTest13, DropDraftVersion) {
-  EnsureTlsSetup();
-  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
-                           SSL_LIBRARY_VERSION_TLS_1_3);
-  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
-                           SSL_LIBRARY_VERSION_TLS_1_3);
-  client_->SetPacketFilter(
-      new TlsExtensionDropper(ssl_tls13_draft_version_xtn));
-  ConnectExpectFail();
-  // This will still fail (we can't just modify ClientHello without consequence)
-  // but the error is discovered later.
-  EXPECT_EQ(SSL_ERROR_DECRYPT_ERROR_ALERT, client_->error_code());
-  EXPECT_EQ(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE, server_->error_code());
-}
-
-TEST_P(TlsExtensionTest13, DropDraftVersionAndFail) {
-  EnsureTlsSetup();
-  // Since this is setup as TLS 1.3 only, expect the handshake to fail rather
-  // than just falling back to TLS 1.2.
-  client_->SetPacketFilter(
-      new TlsExtensionDropper(ssl_tls13_draft_version_xtn));
-  ConnectExpectFail();
-  EXPECT_EQ(SSL_ERROR_PROTOCOL_VERSION_ALERT, client_->error_code());
-  EXPECT_EQ(SSL_ERROR_UNSUPPORTED_VERSION, server_->error_code());
-}
-
-TEST_P(TlsExtensionTest13, ModifyDraftVersionAndFail) {
-  EnsureTlsSetup();
-  // As above, dropping back to 1.2 fails.
-  client_->SetPacketFilter(
-      new TlsExtensionDamager(ssl_tls13_draft_version_xtn, 1));
-  ConnectExpectFail();
-  EXPECT_EQ(SSL_ERROR_PROTOCOL_VERSION_ALERT, client_->error_code());
-  EXPECT_EQ(SSL_ERROR_UNSUPPORTED_VERSION, server_->error_code());
-}
-
 // These tests only work in stream mode because the client sends a
 // cleartext alert which causes a MAC error on the server. With
 // stream this causes handshake failure but with datagram, the
 // packet gets dropped.
 TEST_F(TlsExtensionTest13Stream, DropServerKeyShare) {
   EnsureTlsSetup();
   server_->SetPacketFilter(new TlsExtensionDropper(ssl_tls13_key_share_xtn));
   ConnectExpectFail();
@@ -707,16 +682,53 @@ TEST_F(TlsExtensionTest13Stream, ResumeB
   DataBuffer empty;
   client_->SetPacketFilter(
       new TlsPreSharedKeyReplacer(nullptr, 0, nullptr, 0, &auth_modes, 1));
   ConnectExpectFail();
   client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
   server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
 }
 
+// In these tests, we downgrade to TLS 1.2, causing the
+// server to negotiate TLS 1.2.
+// 1. Both sides only support TLS 1.3, so we get a cipher version
+//    error.
+TEST_P(TlsExtensionTest13, RemoveTls13FromVersionList) {
+  ConnectWithReplacementVersionList(SSL_LIBRARY_VERSION_TLS_1_2);
+  client_->CheckErrorCode(SSL_ERROR_PROTOCOL_VERSION_ALERT);
+  server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
+}
+
+// 2. Server supports 1.2 and 1.3, client supports 1.2, so we
+//    can't negotiate any ciphers.
+TEST_P(TlsExtensionTest13, RemoveTls13FromVersionListServerV12) {
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  ConnectWithReplacementVersionList(SSL_LIBRARY_VERSION_TLS_1_2);
+  client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+  server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
+}
+
+// 3. Server supports 1.2 and 1.3, client supports 1.2 and 1.3
+// but advertises 1.2 (because we changed things).
+TEST_P(TlsExtensionTest13, RemoveTls13FromVersionListBothV12) {
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+  ConnectWithReplacementVersionList(SSL_LIBRARY_VERSION_TLS_1_2);
+#ifndef TLS_1_3_DRAFT_VERSION
+  client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+  server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+#else
+  client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
+  server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
+#endif
+}
+
 INSTANTIATE_TEST_CASE_P(ExtensionStream, TlsExtensionTestGeneric,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesStream,
                                            TlsConnectTestBase::kTlsVAll));
 INSTANTIATE_TEST_CASE_P(ExtensionDatagram, TlsExtensionTestGeneric,
                         ::testing::Combine(TlsConnectTestBase::kTlsModesAll,
                                            TlsConnectTestBase::kTlsV11Plus));
 INSTANTIATE_TEST_CASE_P(ExtensionDatagramOnly, TlsExtensionTestDtls,
                         TlsConnectTestBase::kTlsV11Plus);
--- a/security/nss/external_tests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_resumption_unittest.cc
@@ -416,12 +416,11 @@ TEST_F(TlsConnectTest, TestTls13Resumpti
   // Check that the cipher suite is reported the same on both sides, though in
   // TLS 1.3 resumption actually negotiates a different cipher suite.
   uint16_t resumed_suite;
   EXPECT_TRUE(server_->cipher_suite(&resumed_suite));
   EXPECT_EQ(original_suite, resumed_suite);
   EXPECT_TRUE(client_->cipher_suite(&resumed_suite));
   EXPECT_EQ(original_suite, resumed_suite);
 
-  // TODO(ekr@rtfm.com): This will change when we fix bug 1257047.
-  ASSERT_EQ(initialTicket, c2->extension());
+  ASSERT_NE(initialTicket, c2->extension());
 }
 }  // namespace nss_test
--- a/security/nss/external_tests/ssl_gtest/ssl_version_unittest.cc
+++ b/security/nss/external_tests/ssl_gtest/ssl_version_unittest.cc
@@ -1,47 +1,28 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "secerr.h"
 #include "ssl.h"
+#include "ssl3prot.h"
 #include "sslerr.h"
 #include "sslproto.h"
 
 #include "gtest_utils.h"
 #include "scoped_ptrs.h"
 #include "tls_connect.h"
 #include "tls_filter.h"
 #include "tls_parser.h"
 
 namespace nss_test {
 
-// Set the version number in the ClientHello.
-class TlsInspectorClientHelloVersionSetter : public TlsHandshakeFilter {
- public:
-  TlsInspectorClientHelloVersionSetter(uint16_t version) : version_(version) {}
-
-  virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
-                                               const DataBuffer& input,
-                                               DataBuffer* output) {
-    if (header.handshake_type() == kTlsHandshakeClientHello) {
-      *output = input;
-      output->Write(0, version_, 2);
-      return CHANGE;
-    }
-    return KEEP;
-  }
-
- private:
-  uint16_t version_;
-};
-
 TEST_P(TlsConnectStream, ServerNegotiateTls10) {
   uint16_t minver, maxver;
   client_->GetVersionRange(&minver, &maxver);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, maxver);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0,
                            SSL_LIBRARY_VERSION_TLS_1_0);
   Connect();
 }
@@ -62,16 +43,17 @@ TEST_P(TlsConnectGeneric, ServerNegotiat
 
   uint16_t minver, maxver;
   client_->GetVersionRange(&minver, &maxver);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, maxver);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_2);
   Connect();
 }
+#ifndef TLS_1_3_DRAFT_VERSION
 
 // Test the ServerRandom version hack from
 // [draft-ietf-tls-tls13-11 Section 6.3.1.1].
 // The first three tests test for active tampering. The next
 // two validate that we can also detect fallback using the
 // SSL_SetDowngradeCheckVersion() API.
 TEST_F(TlsConnectTest, TestDowngradeDetectionToTls11) {
   client_->SetPacketFilter(
@@ -86,16 +68,17 @@ TEST_F(DtlsConnectTest, TestDtlsVersion1
       new TlsInspectorClientHelloVersionSetter(((~0x0101) & 0xffff)));
   ConnectExpectFail();
   // It's kind of surprising that SSL_ERROR_NO_CYPHER_OVERLAP is
   // what is returned here, but this is deliberate in ssl3_HandleAlert().
   EXPECT_EQ(SSL_ERROR_NO_CYPHER_OVERLAP, client_->error_code());
   EXPECT_EQ(SSL_ERROR_UNSUPPORTED_VERSION, server_->error_code());
 }
 
+// Disabled as long as we have draft version.
 TEST_F(TlsConnectTest, TestDowngradeDetectionToTls12) {
   EnsureTlsSetup();
   client_->SetPacketFilter(
       new TlsInspectorClientHelloVersionSetter(SSL_LIBRARY_VERSION_TLS_1_2));
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_3);
@@ -133,16 +116,17 @@ TEST_F(TlsConnectTest, TestFallbackFromT
   client_->SetDowngradeCheckVersion(SSL_LIBRARY_VERSION_TLS_1_3);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_2);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   ConnectExpectFail();
   ASSERT_EQ(SSL_ERROR_RX_MALFORMED_SERVER_HELLO, client_->error_code());
 }
+#endif
 
 // The TLS v1.3 spec section C.4 states that 'Implementations MUST NOT send or
 // accept any records with a version less than { 3, 0 }'. Thus we will not
 // allow version ranges including both SSL v3 and TLS v1.3.
 TEST_F(TlsConnectTest, DisallowSSLv3HelloWithTLSv13Enabled) {
   SECStatus rv;
   SSLVersionRange vrange = {SSL_LIBRARY_VERSION_3_0,
                             SSL_LIBRARY_VERSION_TLS_1_3};
@@ -173,23 +157,18 @@ TEST_P(TlsConnectStream, ConnectTls10And
   // |version_|.
   client_->PrepareForRenegotiate();
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, test_version);
   // Reset version and cipher suite so that the preinfo callback
   // doesn't fail.
   server_->ResetPreliminaryInfo();
   server_->StartRenegotiate();
   Handshake();
-  if (test_version < SSL_LIBRARY_VERSION_TLS_1_3) {
-    client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
-    server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
-  } else {
-    client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
-    server_->CheckErrorCode(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
-  }
+  client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
+  server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 TEST_P(TlsConnectStream, ConnectTls10AndClientRenegotiateHigher) {
   if (version_ == SSL_LIBRARY_VERSION_TLS_1_0) {
     return;
   }
   // Set the client so it will accept any version from 1.0
   // to |version_|.
@@ -205,23 +184,18 @@ TEST_P(TlsConnectStream, ConnectTls10And
   // |version_|.
   server_->PrepareForRenegotiate();
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_0, test_version);
   // Reset version and cipher suite so that the preinfo callback
   // doesn't fail.
   server_->ResetPreliminaryInfo();
   client_->StartRenegotiate();
   Handshake();
-  if (test_version < SSL_LIBRARY_VERSION_TLS_1_3) {
-    client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
-    server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
-  } else {
-    client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
-    server_->CheckErrorCode(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
-  }
+  client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_VERSION);
+  server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 TEST_F(TlsConnectTest, Tls13RejectsRehandshakeClient) {
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1,
                            SSL_LIBRARY_VERSION_TLS_1_3);
   Connect();
@@ -252,9 +226,67 @@ TEST_P(TlsConnectGeneric, AlertBeforeSer
   TlsAgentTestBase::MakeRecord(mode_, kTlsAlertType,
                                SSL_LIBRARY_VERSION_TLS_1_0, kWarningAlert,
                                PR_ARRAY_SIZE(kWarningAlert), &alert);
   client_->adapter()->PacketReceived(alert);
   Handshake();
   CheckConnected();
 }
 
+class Tls13NoSupportedVersions : public TlsConnectStreamTls12 {
+ protected:
+  void Run(uint16_t overwritten_client_version, uint16_t max_server_version) {
+    client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                             SSL_LIBRARY_VERSION_TLS_1_2);
+    server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, max_server_version);
+    client_->SetPacketFilter(
+        new TlsInspectorClientHelloVersionSetter(overwritten_client_version));
+    auto capture =
+        new TlsInspectorRecordHandshakeMessage(kTlsHandshakeServerHello);
+    server_->SetPacketFilter(capture);
+    ConnectExpectFail();
+    client_->CheckErrorCode(SSL_ERROR_DECRYPT_ERROR_ALERT);
+    server_->CheckErrorCode(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
+    const DataBuffer& server_hello = capture->buffer();
+    ASSERT_GT(server_hello.len(), 2U);
+    uint32_t ver;
+    ASSERT_TRUE(server_hello.Read(0, 2, &ver));
+    ASSERT_EQ(static_cast<uint32_t>(SSL_LIBRARY_VERSION_TLS_1_2), ver);
+  }
+};