Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 17 Aug 2015 17:00:42 -0700
changeset 258053 6ae3e9ff53b2bae8d95a90c9f25368fd81fa357e
parent 258022 b41e31b71828f9b6f59394945c172f0f57826072 (current diff)
parent 258052 03753a3e80b83118062560a2126368a28be8281d (diff)
child 258072 90d9b7c391d38ae118865bd87b5d011feee6dded
push id29241
push userkwierso@gmail.com
push dateTue, 18 Aug 2015 00:00:46 +0000
treeherdermozilla-central@6ae3e9ff53b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge
security/manager/ssl/tests/unit/test_keysize/ee_prime256v1_256-int_prime256v1_256-root_secp224r1_224.der
security/manager/ssl/tests/unit/test_keysize/ee_prime256v1_256-int_prime256v1_256-root_secp256k1_256.der
security/manager/ssl/tests/unit/test_keysize/ee_prime256v1_256-int_rsa_1016-root_prime256v1_256.der
security/manager/ssl/tests/unit/test_keysize/ee_prime256v1_256-int_secp224r1_224-root_prime256v1_256.der
security/manager/ssl/tests/unit/test_keysize/ee_rsa_1016-int_rsa_1024-root_rsa_1024.der
security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1016-root_rsa_1024.der
security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1016.der
security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1024.der
security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_prime256v1_256-root_prime256v1_256.der
security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_prime256v1_256-root_rsa_2048.der
security/manager/ssl/tests/unit/test_keysize/ee_secp256k1_256-int_prime256v1_256-root_prime256v1_256.der
security/manager/ssl/tests/unit/test_keysize/ee_secp384r1_384-int_prime256v1_256-root_rsa_2048.der
security/manager/ssl/tests/unit/test_keysize/ee_secp521r1_521-int_secp384r1_384-root_prime256v1_256.der
security/manager/ssl/tests/unit/test_keysize/generate.py
security/manager/ssl/tests/unit/test_keysize/int_prime256v1_256-root_prime256v1_256.der
security/manager/ssl/tests/unit/test_keysize/int_prime256v1_256-root_rsa_2048.der
security/manager/ssl/tests/unit/test_keysize/int_prime256v1_256-root_secp224r1_224.der
security/manager/ssl/tests/unit/test_keysize/int_prime256v1_256-root_secp256k1_256.der
security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_prime256v1_256.der
security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_rsa_1024.der
security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1016.der
security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1024.der
security/manager/ssl/tests/unit/test_keysize/int_secp224r1_224-root_prime256v1_256.der
security/manager/ssl/tests/unit/test_keysize/int_secp384r1_384-root_prime256v1_256.der
security/manager/ssl/tests/unit/test_keysize/root_prime256v1_256.der
security/manager/ssl/tests/unit/test_keysize/root_rsa_1016.der
security/manager/ssl/tests/unit/test_keysize/root_rsa_1024.der
security/manager/ssl/tests/unit/test_keysize/root_rsa_2048.der
security/manager/ssl/tests/unit/test_keysize/root_secp224r1_224.der
security/manager/ssl/tests/unit/test_keysize/root_secp256k1_256.der
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -219,17 +219,17 @@ DocAccessibleParent::AddChildDoc(DocAcce
   MOZ_ASSERT(outerDoc);
 
   aChildDoc->mParent = outerDoc;
   outerDoc->SetChildDoc(aChildDoc);
   mChildDocs.AppendElement(aChildDoc);
   aChildDoc->mParentDoc = this;
 
   if (aCreating) {
-    ProxyCreated(aChildDoc, 0);
+    ProxyCreated(aChildDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
   }
 
   return true;
 }
 
 bool
 DocAccessibleParent::RecvShutdown()
 {
--- a/browser/devtools/performance/test/browser_perf-theme-toggle-01.js
+++ b/browser/devtools/performance/test/browser_perf-theme-toggle-01.js
@@ -9,16 +9,18 @@
 const { setTheme } = require("devtools/shared/theme");
 
 const LIGHT_BG = "#fcfcfc";
 const DARK_BG = "#14171a";
 
 setTheme("dark");
 Services.prefs.setBoolPref(MEMORY_PREF, false);
 
+requestLongerTimeout(2);
+
 function* spawnTest() {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, $, OverviewView, document: doc } = panel.panelWin;
 
   yield startRecording(panel);
   let markers = OverviewView.graphs.get("timeline");
   is(markers.backgroundColor, DARK_BG,
     "correct theme on load for markers.");
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -26,8 +26,9 @@ objdir:build
 gyp.pth:media/webrtc/trunk/tools/gyp/pylib
 pyasn1.pth:python/pyasn1
 pyasn1_modules.pth:python/pyasn1-modules
 bitstring.pth:python/bitstring
 redo.pth:python/redo
 requests.pth:python/requests
 rsa.pth:python/rsa
 futures.pth:python/futures
+ecc.pth:python/PyECC
--- a/config/check_macroassembler_style.py
+++ b/config/check_macroassembler_style.py
@@ -28,23 +28,30 @@ import re
 import subprocess
 import sys
 from check_utils import get_all_toplevel_filenames
 
 architecture_independent = set([ 'generic' ])
 all_architecture_names = set([ 'x86', 'x64', 'arm', 'arm64', 'mips32' ])
 all_shared_architecture_names = set([ 'x86_shared', 'arm', 'arm64', 'mips32' ])
 
+reBeforeArg = "(?<=[(,\s])"
+reArgType = "(?P<type>[\w\s:*&]+)"
+reArgName = "(?P<name>\s\w+)"
+reArgDefault = "(?P<default>(?:\s=[^,)]+)?)"
+reAfterArg = "(?=[,)])"
+reMatchArg = re.compile(reBeforeArg + reArgType + reArgName + reArgDefault + reAfterArg)
+
 def get_normalized_signatures(signature, fileAnnot = None):
     # Remove semicolon.
     signature = signature.replace(';', ' ')
     # Normalize spaces.
     signature = re.sub(r'\s+', ' ', signature).strip()
-    # Remove argument names.
-    signature = re.sub(r'(?P<type>(?:[(]|,\s)[\w\s:*&]+)(?P<name>\s\w+)(?=[,)])', '\g<type>', signature)
+    # Match arguments, and keep only the type.
+    signature = reMatchArg.sub('\g<type>', signature)
     # Remove class name
     signature = signature.replace('MacroAssembler::', '')
 
     # Extract list of architectures
     archs = ['generic']
     if fileAnnot:
         archs = [fileAnnot['arch']]
 
@@ -69,21 +76,26 @@ def get_normalized_signatures(signature,
     inline = False
     if fileAnnot:
         inline = fileAnnot['inline']
 
     if 'inline ' in signature:
         signature = re.sub(r'inline\s+', '', signature)
         inline = True
 
-    return [
-        { 'arch': a, 'sig': 'inline ' + signature }
+    inlinePrefx = ''
+    if inline:
+        inlinePrefx = 'inline '
+    signatures =  [
+        { 'arch': a, 'sig': inlinePrefx + signature }
         for a in archs
     ]
 
+    return signatures
+
 file_suffixes = set([
     a.replace('_', '-') for a in
     all_architecture_names.union(all_shared_architecture_names)
 ])
 def get_file_annotation(filename):
     origFilename = filename
     filename = filename.split('/')[-1]
 
--- a/dom/base/test/file_webaudioLoop.html
+++ b/dom/base/test/file_webaudioLoop.html
@@ -4,25 +4,20 @@ var ac = new AudioContext();
 fetch("audio.ogg").then(response => {
   return response.arrayBuffer();
 }).then(ab => {
   return ac.decodeAudioData(ab);
 }).then(ab => {
   var src = ac.createBufferSource();
   src.buffer = ab;
   src.loop = true;
+  src.loopStart = 0;
+  src.loopEnd = ab.duration;
   src.start();
   src.connect(ac.destination);
-  setTimeout(() => {
-    if (ac.state == "running") {
-      parent.runTest();
-    } else {
-      setTimeout(arguments.callee, 0);
-    }
-  });
 });
 
 var suspendPromise;
 function suspendAC() {
   suspendPromise = ac.suspend();
 }
 
 var resumePromise;
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -299,16 +299,17 @@ skip-if = buildapp == 'mulet' || buildap
 [test_messagemanager_targetchain.html]
 [test_named_frames.html]
 [test_navigator_resolve_identity.html]
 [test_navigator_language.html]
 [test_noAudioNotification.html]
 [test_noAudioNotificationOnMutedElement.html]
 [test_noAudioNotificationOnMutedOrVolume0Element.html]
 [test_noAudioNotificationOnVolume0Element.html]
+[test_noWebAudioNotification.html]
 [test_openDialogChromeOnly.html]
 [test_open_null_features.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Fails on b2g-desktop, tracked in bug 1011874
 [test_postMessage_solidus.html]
 [test_pluginAudioNotification.html]
 skip-if = (buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android') # Plugins don't work on Android and/or B2G/Mulet
 [test_screen_orientation.html]
 [test_settimeout_extra_arguments.html]
copy from dom/base/test/test_webaudioNotification.html
copy to dom/base/test/test_noWebAudioNotification.html
--- a/dom/base/test/test_webaudioNotification.html
+++ b/dom/base/test/test_noWebAudioNotification.html
@@ -1,67 +1,49 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>Test for audio controller in windows</title>
+  <title>Test for video controller in windows</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <pre id="test">
 </pre>
-<iframe></iframe>
 
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
-
-var expectedNotification = null;
-var iframe = null;
+SimpleTest.requestFlakyTimeout("Testing an event not happening");
 
 var observer = {
   observe: function(subject, topic, data) {
-    is(topic, "audio-playback", "audio-playback received");
-    is(data, expectedNotification, "This is the right notification");
-    SimpleTest.executeSoon(runTest);
+    ok(false, "should not receive audio-playback notification!");
   }
 };
 
 var observerService = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
                                    .getService(SpecialPowers.Ci.nsIObserverService);
 
+var ac;
+
 var tests = [
   function() {
-    iframe = document.querySelector("iframe");
     SpecialPowers.pushPrefEnv({"set": [["media.useAudioChannelService", true]]}, runTest);
   },
 
   function() {
-    iframe.src = "file_webaudioLoop.html";
-  },
-
-  function() {
     observerService.addObserver(observer, "audio-playback", false);
     ok(true, "Observer set");
     runTest();
   },
 
   function() {
-    expectedNotification = 'inactive';
-    iframe.contentWindow.suspendAC();
-  },
-
-  function() {
-    expectedNotification = 'active';
-    iframe.contentWindow.resumeAC();
-  },
-
-  function() {
-    expectedNotification = 'inactive';
-    iframe.contentWindow.closeAC();
+    ac = new AudioContext();
+    setTimeout(runTest, 100);
   },
 
   function() {
     observerService.removeObserver(observer, "audio-playback");
     ok(true, "Observer removed");
     runTest();
   }
 ];
@@ -71,14 +53,14 @@ function runTest() {
     SimpleTest.finish();
     return;
   }
 
   var test = tests.shift();
   test();
 }
 
-onload = runTest;
+runTest();
 
 </script>
 </body>
 </html>
 
--- a/dom/base/test/test_webaudioNotification.html
+++ b/dom/base/test/test_webaudioNotification.html
@@ -30,23 +30,24 @@ var observerService = SpecialPowers.Cc["
 
 var tests = [
   function() {
     iframe = document.querySelector("iframe");
     SpecialPowers.pushPrefEnv({"set": [["media.useAudioChannelService", true]]}, runTest);
   },
 
   function() {
-    iframe.src = "file_webaudioLoop.html";
+    observerService.addObserver(observer, "audio-playback", false);
+    ok(true, "Observer set");
+    runTest();
   },
 
   function() {
-    observerService.addObserver(observer, "audio-playback", false);
-    ok(true, "Observer set");
-    runTest();
+    iframe.src = "file_webaudioLoop.html";
+    expectedNotification = 'active';
   },
 
   function() {
     expectedNotification = 'inactive';
     iframe.contentWindow.suspendAC();
   },
 
   function() {
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -850,17 +850,17 @@ AudioContext::Suspend(ErrorResult& aRv)
     return promise.forget();
   }
 
   if (mAudioContextState == AudioContextState::Suspended) {
     promise->MaybeResolve(JS::UndefinedHandleValue);
     return promise.forget();
   }
 
-  Destination()->DestroyAudioChannelAgent();
+  Destination()->Suspend();
 
   MediaStream* ds = DestinationStream();
   if (ds) {
     ds->BlockStreamIfNeeded();
   }
 
   mPromiseGripArray.AppendElement(promise);
   Graph()->ApplyAudioContextOperation(DestinationStream()->AsAudioNodeStream(),
@@ -890,17 +890,17 @@ AudioContext::Resume(ErrorResult& aRv)
     return promise.forget();
   }
 
   if (mAudioContextState == AudioContextState::Running) {
     promise->MaybeResolve(JS::UndefinedHandleValue);
     return promise.forget();
   }
 
-  Destination()->CreateAudioChannelAgent();
+  Destination()->Resume();
 
   MediaStream* ds = DestinationStream();
   if (ds) {
     ds->UnblockStreamIfNeeded();
   }
 
   mPromiseGripArray.AppendElement(promise);
   Graph()->ApplyAudioContextOperation(DestinationStream()->AsAudioNodeStream(),
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -238,29 +238,34 @@ private:
 };
 
 class DestinationNodeEngine final : public AudioNodeEngine
 {
 public:
   explicit DestinationNodeEngine(AudioDestinationNode* aNode)
     : AudioNodeEngine(aNode)
     , mVolume(1.0f)
-    , mLastInputMuted(false)
+    , mLastInputMuted(true)
+    , mSuspended(false)
   {
     MOZ_ASSERT(aNode);
   }
 
   virtual void ProcessBlock(AudioNodeStream* aStream,
                             const AudioChunk& aInput,
                             AudioChunk* aOutput,
                             bool* aFinished) override
   {
     *aOutput = aInput;
     aOutput->mVolume *= mVolume;
 
+    if (mSuspended) {
+      return;
+    }
+
     bool newInputMuted = aInput.IsNull() || aInput.IsMuted();
     if (newInputMuted != mLastInputMuted) {
       mLastInputMuted = newInputMuted;
 
       nsRefPtr<InputMutedRunnable> runnable =
         new InputMutedRunnable(aStream, newInputMuted);
       aStream->Graph()->
         DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
@@ -269,28 +274,40 @@ public:
 
   virtual void SetDoubleParameter(uint32_t aIndex, double aParam) override
   {
     if (aIndex == VOLUME) {
       mVolume = aParam;
     }
   }
 
+  virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override
+  {
+    if (aIndex == SUSPENDED) {
+      mSuspended = !!aParam;
+      if (mSuspended) {
+        mLastInputMuted = true;
+      }
+    }
+  }
+
   enum Parameters {
     VOLUME,
+    SUSPENDED,
   };
 
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
 private:
   float mVolume;
   bool mLastInputMuted;
+  bool mSuspended;
 };
 
 static bool UseAudioChannelService()
 {
   return Preferences::GetBool("media.useAudioChannelService");
 }
 
 static bool UseAudioChannelAPI()
@@ -447,16 +464,30 @@ AudioDestinationNode::Mute()
 void
 AudioDestinationNode::Unmute()
 {
   MOZ_ASSERT(Context() && !Context()->IsOffline());
   SendDoubleParameterToStream(DestinationNodeEngine::VOLUME, 1.0f);
 }
 
 void
+AudioDestinationNode::Suspend()
+{
+  DestroyAudioChannelAgent();
+  SendInt32ParameterToStream(DestinationNodeEngine::SUSPENDED, 1);
+}
+
+void
+AudioDestinationNode::Resume()
+{
+  CreateAudioChannelAgent();
+  SendInt32ParameterToStream(DestinationNodeEngine::SUSPENDED, 0);
+}
+
+void
 AudioDestinationNode::OfflineShutdown()
 {
   MOZ_ASSERT(Context() && Context()->IsOffline(),
              "Should only be called on a valid OfflineAudioContext");
 
   MediaStreamGraph::DestroyNonRealtimeInstance(mStream->Graph());
   mOfflineRenderingRef.Drop(this);
 }
@@ -507,17 +538,22 @@ NS_IMETHODIMP
 AudioDestinationNode::WindowAudioCaptureChanged()
 {
   MOZ_ASSERT(mAudioChannelAgent);
 
   if (!mStream || Context()->IsOffline()) {
     return NS_OK;
   }
 
-  bool captured = GetOwner()->GetAudioCaptured();
+  nsCOMPtr<nsPIDOMWindow> ownerWindow = GetOwner();
+  if (!ownerWindow) {
+    return NS_OK;
+  }
+
+  bool captured = ownerWindow->GetAudioCaptured();
 
   if (captured != mCaptured) {
     if (captured) {
       nsCOMPtr<nsPIDOMWindow> window = Context()->GetParentObject();
       uint64_t id = window->WindowID();
       mCaptureStreamPort =
         mStream->Graph()->ConnectToCaptureStream(id, mStream);
     } else {
@@ -608,20 +644,16 @@ AudioDestinationNode::CreateAudioChannel
     mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
   }
 
   mAudioChannelAgent = new AudioChannelAgent();
   mAudioChannelAgent->InitWithWeakCallback(GetOwner(),
                                            static_cast<int32_t>(mAudioChannel),
                                            this);
 
-  // The AudioChannelAgent must start playing immediately in order to avoid
-  // race conditions with mozinterruptbegin/end events.
-  InputMuted(false);
-
   WindowAudioCaptureChanged();
 }
 
 void
 AudioDestinationNode::NotifyStableState()
 {
   mExtraCurrentTimeUpdatedSinceLastStableState = false;
 }
@@ -687,17 +719,20 @@ AudioDestinationNode::SetIsOnlyNodeForCo
 }
 
 void
 AudioDestinationNode::InputMuted(bool aMuted)
 {
   MOZ_ASSERT(Context() && !Context()->IsOffline());
 
   if (!mAudioChannelAgent) {
-    return;
+    if (aMuted) {
+      return;
+    }
+    CreateAudioChannelAgent();
   }
 
   if (aMuted) {
     mAudioChannelAgent->NotifyStoppedPlaying(nsIAudioChannelAgent::AUDIO_AGENT_NOTIFY);
     return;
   }
 
   float volume = 0.0;
--- a/dom/media/webaudio/AudioDestinationNode.h
+++ b/dom/media/webaudio/AudioDestinationNode.h
@@ -48,16 +48,19 @@ public:
                                ErrorResult& aRv) override;
 
   // Returns the stream or null after unlink.
   AudioNodeStream* Stream() { return mStream; }
 
   void Mute();
   void Unmute();
 
+  void Suspend();
+  void Resume();
+
   void StartRendering(Promise* aPromise);
 
   void OfflineShutdown();
 
   AudioChannel MozAudioChannelType() const;
   void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
 
   virtual void NotifyMainThreadStreamFinished() override;
--- a/dom/workers/ServiceWorker.cpp
+++ b/dom/workers/ServiceWorker.cpp
@@ -47,21 +47,16 @@ ServiceWorker::ServiceWorker(nsPIDOMWind
   : DOMEventTargetHelper(aWindow),
     mInfo(aInfo),
     mSharedWorker(aSharedWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aInfo);
   MOZ_ASSERT(mSharedWorker);
 
-  if (aWindow) {
-    mDocument = aWindow->GetExtantDoc();
-    mWindow = aWindow->GetOuterWindow();
-  }
-
   // This will update our state too.
   mInfo->AppendWorker(this);
 }
 
 ServiceWorker::~ServiceWorker()
 {
   AssertIsOnMainThread();
   mInfo->RemoveWorker(this);
@@ -69,17 +64,17 @@ ServiceWorker::~ServiceWorker()
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorker, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(ServiceWorker, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorker)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorker, DOMEventTargetHelper,
-                                   mSharedWorker, mDocument, mWindow)
+                                   mSharedWorker)
 
 JSObject*
 ServiceWorker::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   AssertIsOnMainThread();
 
   return ServiceWorkerBinding::Wrap(aCx, this, aGivenProto);
 }
@@ -90,30 +85,32 @@ ServiceWorker::GetScriptURL(nsString& aU
   CopyUTF8toUTF16(mInfo->ScriptSpec(), aURL);
 }
 
 void
 ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                            const Optional<Sequence<JS::Value>>& aTransferable,
                            ErrorResult& aRv)
 {
-  WorkerPrivate* workerPrivate = GetWorkerPrivate();
-  MOZ_ASSERT(workerPrivate);
-
   if (State() == ServiceWorkerState::Redundant) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  MOZ_ASSERT(mDocument && mWindow,
-             "Cannot call PostMessage on a ServiceWorker object that doesn't "
-             "have a window");
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetParentObject());
+  if (!window || !window->GetExtantDoc()) {
+    NS_WARNING("Trying to call post message from an invalid dom object.");
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
 
-  nsAutoPtr<ServiceWorkerClientInfo> clientInfo(
-    new ServiceWorkerClientInfo(mDocument, mWindow));
+  WorkerPrivate* workerPrivate = GetWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+
+  nsAutoPtr<ServiceWorkerClientInfo> clientInfo(new ServiceWorkerClientInfo(window->GetExtantDoc()));
 
   workerPrivate->PostMessageToServiceWorker(aCx, aMessage, aTransferable,
                                             clientInfo, aRv);
 }
 
 WorkerPrivate*
 ServiceWorker::GetWorkerPrivate() const
 {
--- a/dom/workers/ServiceWorker.h
+++ b/dom/workers/ServiceWorker.h
@@ -6,17 +6,16 @@
 
 #ifndef mozilla_dom_workers_serviceworker_h__
 #define mozilla_dom_workers_serviceworker_h__
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState.
 
-class nsIDocument;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 namespace workers {
 
 class ServiceWorkerInfo;
@@ -87,19 +86,15 @@ private:
   ServiceWorkerState mState;
   const nsRefPtr<ServiceWorkerInfo> mInfo;
 
   // To allow ServiceWorkers to potentially drop the backing DOMEventTargetHelper and
   // re-instantiate it later, they simply own a SharedWorker member that
   // can be released and recreated as required rather than re-implement some of
   // the SharedWorker logic.
   nsRefPtr<SharedWorker> mSharedWorker;
-  // We need to keep the document and window alive for PostMessage to be able
-  // to access them.
-  nsCOMPtr<nsIDocument> mDocument;
-  nsCOMPtr<nsPIDOMWindow> mWindow;
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workers_serviceworker_h__
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -24,18 +24,17 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Se
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClient)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClient)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClient)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc,
-                                                 nsPIDOMWindow* aWindow)
+ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc)
   : mWindowId(0)
 {
   MOZ_ASSERT(aDoc);
   nsresult rv = aDoc->GetId(mClientId);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to get the UUID of the document.");
   }
 
@@ -50,17 +49,17 @@ ServiceWorkerClientInfo::ServiceWorkerCl
   mVisibilityState = aDoc->VisibilityState();
 
   ErrorResult result;
   mFocused = aDoc->HasFocus(result);
   if (result.Failed()) {
     NS_WARNING("Failed to get focus information.");
   }
 
-  nsRefPtr<nsGlobalWindow> outerWindow = static_cast<nsGlobalWindow*>(aWindow);
+  nsRefPtr<nsGlobalWindow> outerWindow = static_cast<nsGlobalWindow*>(aDoc->GetWindow());
   MOZ_ASSERT(outerWindow);
   if (!outerWindow->IsTopLevelWindow()) {
     mFrameType = FrameType::Nested;
   } else if (outerWindow->HadOriginalOpener()) {
     mFrameType = FrameType::Auxiliary;
   } else {
     mFrameType = FrameType::Top_level;
   }
--- a/dom/workers/ServiceWorkerClient.h
+++ b/dom/workers/ServiceWorkerClient.h
@@ -10,34 +10,33 @@
 
 #include "nsCOMPtr.h"
 #include "nsWrapperCache.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ClientBinding.h"
 
 class nsIDocument;
-class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 namespace workers {
 
 class ServiceWorkerClient;
 class ServiceWorkerWindowClient;
 
 // Used as a container object for information needed to create
 // client objects.
 class ServiceWorkerClientInfo final
 {
   friend class ServiceWorkerClient;
   friend class ServiceWorkerWindowClient;
 
 public:
-  ServiceWorkerClientInfo(nsIDocument* aDoc, nsPIDOMWindow* aWindow);
+  explicit ServiceWorkerClientInfo(nsIDocument* aDoc);
 
 private:
   nsString mClientId;
   uint64_t mWindowId;
   nsString mUrl;
 
   // Window Clients
   VisibilityState mVisibilityState;
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -3891,17 +3891,17 @@ ServiceWorkerManager::DispatchFetchEvent
     NS_NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception);
 
   nsAutoPtr<ServiceWorkerClientInfo> clientInfo;
 
   if (!isNavigation) {
     MOZ_ASSERT(aDoc);
     aRv = GetDocumentController(aDoc->GetInnerWindow(), failRunnable,
                                 getter_AddRefs(serviceWorker));
-    clientInfo = new ServiceWorkerClientInfo(aDoc, aDoc->GetWindow());
+    clientInfo = new ServiceWorkerClientInfo(aDoc);
   } else {
     nsCOMPtr<nsIChannel> internalChannel;
     aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
     nsCOMPtr<nsIURI> uri;
@@ -4271,17 +4271,17 @@ EnumControlledDocuments(nsISupports* aKe
   }
 
   nsCOMPtr<nsIDocument> document = do_QueryInterface(aKey);
 
   if (!document || !document->GetWindow()) {
     return PL_DHASH_NEXT;
   }
 
-  ServiceWorkerClientInfo clientInfo(document, document->GetWindow());
+  ServiceWorkerClientInfo clientInfo(document);
   data->mDocuments.AppendElement(clientInfo);
 
   return PL_DHASH_NEXT;
 }
 
 static void
 FireControllerChangeOnDocument(nsIDocument* aDocument)
 {
--- a/dom/workers/ServiceWorkerWindowClient.cpp
+++ b/dom/workers/ServiceWorkerWindowClient.cpp
@@ -85,19 +85,24 @@ public:
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
     nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
     UniquePtr<ServiceWorkerClientInfo> clientInfo;
 
     if (window) {
-      nsContentUtils::DispatchChromeEvent(window->GetExtantDoc(), window->GetOuterWindow(), NS_LITERAL_STRING("DOMServiceWorkerFocusClient"), true, true);
-      clientInfo.reset(new ServiceWorkerClientInfo(window->GetDocument(),
-                                                   window->GetOuterWindow()));
+      nsCOMPtr<nsIDocument> doc = window->GetDocument();
+      if (doc) {
+        nsContentUtils::DispatchChromeEvent(doc,
+                                            window->GetOuterWindow(),
+                                            NS_LITERAL_STRING("DOMServiceWorkerFocusClient"),
+                                            true, true);
+        clientInfo.reset(new ServiceWorkerClientInfo(doc));
+      }
     }
 
     DispatchResult(Move(clientInfo));
     return NS_OK;
   }
 
 private:
   void
--- a/editor/libeditor/IMETextTxn.cpp
+++ b/editor/libeditor/IMETextTxn.cpp
@@ -218,17 +218,22 @@ IMETextTxn::SetIMESelection(nsEditor& aE
       NS_ASSERTION(!textRange.Length(), "nsEditor doesn't support wide caret");
       int32_t caretOffset = static_cast<int32_t>(
         aOffsetInNode +
           std::min(textRange.mStartOffset, aLengthOfCompositionString));
       MOZ_ASSERT(caretOffset >= 0 &&
                  static_cast<uint32_t>(caretOffset) <= maxOffset);
       rv = selection->Collapse(aTextNode, caretOffset);
       setCaret = setCaret || NS_SUCCEEDED(rv);
-      NS_ASSERTION(setCaret, "Failed to collapse normal selection");
+      if (NS_WARN_IF(!setCaret)) {
+        continue;
+      }
+      // If caret range is specified explicitly, we should show the caret if
+      // it should be so.
+      aEditor.HideCaret(false);
       continue;
     }
 
     // If the clause length is 0, it should be a bug.
     if (!textRange.Length()) {
       NS_WARNING("Any clauses must not be empty");
       continue;
     }
@@ -287,15 +292,17 @@ IMETextTxn::SetIMESelection(nsEditor& aE
   if (!setCaret) {
     int32_t caretOffset =
       static_cast<int32_t>(aOffsetInNode + aLengthOfCompositionString);
     MOZ_ASSERT(caretOffset >= 0 &&
                static_cast<uint32_t>(caretOffset) <= maxOffset);
     rv = selection->Collapse(aTextNode, caretOffset);
     NS_ASSERTION(NS_SUCCEEDED(rv),
                  "Failed to set caret at the end of composition string");
+    // If caret range isn't specified explicitly, we should hide the caret.
+    aEditor.HideCaret(true);
   }
 
   rv = selection->EndBatchChanges();
   NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to end batch changes");
 
   return rv;
 }
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -142,27 +142,30 @@ nsEditor::nsEditor()
 ,  mDirection(eNone)
 ,  mDocDirtyState(-1)
 ,  mSpellcheckCheckboxState(eTriUnset)
 ,  mShouldTxnSetSelection(true)
 ,  mDidPreDestroy(false)
 ,  mDidPostCreate(false)
 ,  mDispatchInputEvent(true)
 ,  mIsInEditAction(false)
+,  mHidingCaret(false)
 {
 }
 
 nsEditor::~nsEditor()
 {
   NS_ASSERTION(!mDocWeak || mDidPreDestroy, "Why PreDestroy hasn't been called?");
 
   if (mComposition) {
     mComposition->OnEditorDestroyed();
     mComposition = nullptr;
   }
+  // If this editor is still hiding the caret, we need to restore it.
+  HideCaret(false);
   mTxnMgr = nullptr;
 
   delete mPhonetic;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsEditor)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEditor)
@@ -459,16 +462,18 @@ nsEditor::PreDestroy(bool aDestroyingFra
   if (mInlineSpellChecker)
     mInlineSpellChecker->Cleanup(aDestroyingFrames);
 
   // tell our listeners that the doc is going away
   NotifyDocumentListeners(eDocumentToBeDestroyed);
 
   // Unregister event listeners
   RemoveEventListeners();
+  // If this editor is still hiding the caret, we need to restore it.
+  HideCaret(false);
   mActionListeners.Clear();
   mEditorObservers.Clear();
   mDocStateListeners.Clear();
   mInlineSpellChecker = nullptr;
   mSpellcheckCheckboxState = eTriUnset;
   mRootElement = nullptr;
 
   mDidPreDestroy = true;
@@ -2060,16 +2065,20 @@ nsEditor::EndIMEComposition()
     nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
     if (plcTxn) {
       DebugOnly<nsresult> rv = plcTxn->Commit();
       NS_ASSERTION(NS_SUCCEEDED(rv),
                    "nsIAbsorbingTransaction::Commit() failed");
     }
   }
 
+  // Composition string may have hidden the caret.  Therefore, we need to
+  // cancel it here.
+  HideCaret(false);
+
   /* reset the data we need to construct a transaction */
   mIMETextNode = nullptr;
   mIMETextOffset = 0;
   mIMETextLength = 0;
   mComposition->EndHandlingComposition(this);
   mComposition = nullptr;
 
   // notify editor observers of action
@@ -5253,8 +5262,28 @@ nsEditor::GetIMESelectionStartOffsetIn(n
         MOZ_ASSERT(range->EndOffset() >= 0,
                    "start offset shouldn't be negative");
         minOffset = std::min(minOffset, range->EndOffset());
       }
     }
   }
   return minOffset < INT32_MAX ? minOffset : -1;
 }
+
+void
+nsEditor::HideCaret(bool aHide)
+{
+  if (mHidingCaret == aHide) {
+    return;
+  }
+
+  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+  NS_ENSURE_TRUE_VOID(presShell);
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  NS_ENSURE_TRUE_VOID(caret);
+
+  mHidingCaret = aHide;
+  if (aHide) {
+    caret->AddForceHide();
+  } else {
+    caret->RemoveForceHide();
+  }
+}
--- a/editor/libeditor/nsEditor.h
+++ b/editor/libeditor/nsEditor.h
@@ -819,16 +819,23 @@ public:
 
   /**
    * FindBetterInsertionPoint() tries to look for better insertion point which
    * is typically the nearest text node and offset in it.
    */
   void FindBetterInsertionPoint(nsCOMPtr<nsINode>& aNode,
                                 int32_t& aOffset);
 
+  /**
+   * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent
+   * with nsCaret::RemoveForceHide().  This does NOT set visibility of
+   * nsCaret.  Therefore, this is stateless.
+   */
+  void HideCaret(bool aHide);
+
 protected:
   enum Tristate {
     eTriUnset,
     eTriFalse,
     eTriTrue
   };
   // Spellchecking
   nsCString mContentMIMEType;       // MIME type of the doc we are editing.
@@ -878,16 +885,17 @@ protected:
   int8_t            mDocDirtyState;      // -1 = not initialized
   uint8_t           mSpellcheckCheckboxState; // a Tristate value
 
   bool mShouldTxnSetSelection;  // turn off for conservative selection adjustment by txns
   bool mDidPreDestroy;    // whether PreDestroy has been called
   bool mDidPostCreate;    // whether PostCreate has been called
   bool mDispatchInputEvent;
   bool mIsInEditAction;   // true while the instance is handling an edit action
+  bool mHidingCaret;      // whether caret is hidden forcibly.
 
   friend bool NSCanUnload(nsISupports* serviceMgr);
   friend class nsAutoTxnsConserveSelection;
   friend class nsAutoSelectionReset;
   friend class nsAutoRules;
   friend class nsRangeUpdater;
 };
 
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -711,21 +711,24 @@ APZCTreeManager::GetTouchInputBlockAPZC(
 }
 
 nsEventStatus
 APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
                                    ScrollableLayerGuid* aOutTargetGuid,
                                    uint64_t* aOutInputBlockId)
 {
   if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
-    // If we are in an overscrolled state and a second finger goes down,
+    // If we are panned into overscroll and a second finger goes down,
     // ignore that second touch point completely. The touch-start for it is
     // dropped completely; subsequent touch events until the touch-end for it
     // will have this touch point filtered out.
-    if (mApzcForInputBlock && BuildOverscrollHandoffChain(mApzcForInputBlock)->HasOverscrolledApzc()) {
+    // (By contrast, if we're in overscroll but not panning, such as after
+    // putting two fingers down during an overscroll animation, we process the
+    // second touch and proceed to pinch.)
+    if (mApzcForInputBlock && BuildOverscrollHandoffChain(mApzcForInputBlock)->HasApzcPannedIntoOverscroll()) {
       if (mRetainedTouchIdentifier == -1) {
         mRetainedTouchIdentifier = mApzcForInputBlock->GetLastTouchIdentifier();
       }
       return nsEventStatus_eConsumeNoDefault;
     }
 
     // NS_TOUCH_START event contains all active touches of the current
     // session thus resetting mTouchCount.
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -1037,16 +1037,20 @@ public:
   // Returns whether or not this apzc contains the given screen point within
   // its composition bounds.
   bool Contains(const ScreenIntPoint& aPoint) const;
 
   bool IsOverscrolled() const {
     return mX.IsOverscrolled() || mY.IsOverscrolled();
   }
 
+  bool IsPannedIntoOverscroll() const {
+    return IsOverscrolled() && IsInPanningState();
+  }
+
 private:
   /* This is the cumulative CSS transform for all the layers from (and including)
    * the parent APZC down to (but excluding) this one. */
   Matrix4x4 mAncestorTransform;
 
 
   /* ===================================================================
    * The functions and members in this section are used for sharing the
--- a/gfx/layers/apz/src/OverscrollHandoffState.cpp
+++ b/gfx/layers/apz/src/OverscrollHandoffState.cpp
@@ -157,19 +157,19 @@ OverscrollHandoffChain::CanScrollInDirec
       return true;
     }
   }
 
   return false;
 }
 
 bool
-OverscrollHandoffChain::HasOverscrolledApzc() const
+OverscrollHandoffChain::HasApzcPannedIntoOverscroll() const
 {
-  return AnyApzc(&AsyncPanZoomController::IsOverscrolled);
+  return AnyApzc(&AsyncPanZoomController::IsPannedIntoOverscroll);
 }
 
 bool
 OverscrollHandoffChain::HasFastFlungApzc() const
 {
   return AnyApzc(&AsyncPanZoomController::IsFlingingFast);
 }
 
--- a/gfx/layers/apz/src/OverscrollHandoffState.h
+++ b/gfx/layers/apz/src/OverscrollHandoffState.h
@@ -75,18 +75,18 @@ public:
   // has room to be panned.
   bool CanBePanned(const AsyncPanZoomController* aApzc) const;
 
   // Determine whether the given APZC, or any APZC further in the chain,
   // can scroll in the given direction.
   bool CanScrollInDirection(const AsyncPanZoomController* aApzc,
                             Layer::ScrollDirection aDirection) const;
 
-  // Determine whether any APZC along this handoff chain is overscrolled.
-  bool HasOverscrolledApzc() const;
+  // Determine whether any APZC along this handoff chain is panned into overscroll.
+  bool HasApzcPannedIntoOverscroll() const;
 
   // Determine whether any APZC along this handoff chain has been flung fast.
   bool HasFastFlungApzc() const;
 
   nsRefPtr<AsyncPanZoomController> FindFirstScrollable(const ScrollWheelInput& aInput) const;
 
 private:
   std::vector<nsRefPtr<AsyncPanZoomController>> mChain;
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -10982,16 +10982,21 @@ GenerateEntry(ModuleCompiler& m, unsigne
     for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) {
         unsigned argOffset = iter.index() * sizeof(AsmJSModule::EntryArg);
         Address src(argv, argOffset);
         MIRType type = iter.mirType();
         switch (iter->kind()) {
           case ABIArg::GPR:
             masm.load32(src, iter->gpr());
             break;
+#ifdef JS_CODEGEN_REGISTER_PAIR
+          case ABIArg::GPR_PAIR:
+            MOZ_CRASH("AsmJS uses hardfp for function calls.");
+            break;
+#endif
           case ABIArg::FPU: {
             static_assert(sizeof(AsmJSModule::EntryArg) >= jit::Simd128DataSize,
                           "EntryArg must be big enough to store SIMD values");
             switch (type) {
               case MIRType_Int32x4:
                 masm.loadUnalignedInt32x4(src, iter->fpu());
                 break;
               case MIRType_Float32x4:
@@ -11094,16 +11099,21 @@ FillArgumentArray(ModuleCompiler& m, con
     MacroAssembler& masm = m.masm();
 
     for (ABIArgTypeIter i(argTypes); !i.done(); i++) {
         Address dstAddr(masm.getStackPointer(), offsetToArgs + i.index() * sizeof(Value));
         switch (i->kind()) {
           case ABIArg::GPR:
             masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dstAddr);
             break;
+#ifdef JS_CODEGEN_REGISTER_PAIR
+          case ABIArg::GPR_PAIR:
+            MOZ_CRASH("AsmJS uses hardfp for function calls.");
+            break;
+#endif
           case ABIArg::FPU:
             masm.canonicalizeDouble(i->fpu());
             masm.storeDouble(i->fpu(), dstAddr);
             break;
           case ABIArg::Stack:
             if (i.mirType() == MIRType_Int32) {
                 Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase());
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -341,16 +341,17 @@ function isOverridableField(initialCSU, 
         if (field == 'GetWindowProxy' || field == 'GetWindowProxyPreserveColor')
             return false;
     }
     return true;
 }
 
 function listGCTypes() {
     return [
+        'js::gc::Cell',
         'JSObject',
         'JSString',
         'JSFatInlineString',
         'JSExternalString',
         'js::Shape',
         'js::AccessorShape',
         'js::BaseShape',
         'JSScript',
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -397,17 +397,17 @@ NativeRegExpMacroAssembler::GenerateCode
         volatileRegs.add(Register::FromCode(Registers::lr));
 #elif defined(JS_CODEGEN_MIPS32)
         volatileRegs.add(Register::FromCode(Registers::ra));
 #endif
         volatileRegs.takeUnchecked(temp0);
         volatileRegs.takeUnchecked(temp1);
         masm.PushRegsInMask(volatileRegs);
 
-        masm.setupUnalignedABICall(1, temp0);
+        masm.setupUnalignedABICall(temp0);
         masm.passABIArg(temp1);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, GrowBacktrackStack));
         masm.storeCallResult(temp0);
 
         masm.PopRegsInMask(volatileRegs);
 
         // If return false, we have failed to grow the stack, and
         // must exit with a stack-overflow exception. Do this in the caller
@@ -809,17 +809,17 @@ NativeRegExpMacroAssembler::CheckNotBack
         // Found by adding negative string-end offset of current position
         // to end of string.
         masm.addPtr(input_end_pointer, current_position);
 
         // Parameters are
         //   Address byte_offset1 - Address captured substring's start.
         //   Address byte_offset2 - Address of current character position.
         //   size_t byte_length - length of capture in bytes(!)
-        masm.setupUnalignedABICall(3, temp0);
+        masm.setupUnalignedABICall(temp0);
         masm.passABIArg(current_character);
         masm.passABIArg(current_position);
         masm.passABIArg(temp1);
         int (*fun)(const char16_t*, const char16_t*, size_t) = CaseInsensitiveCompareStrings;
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
         masm.storeCallResult(temp0);
 
         masm.PopRegsInMask(volatileRegs);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -20,16 +20,17 @@
 #endif
 #include "jit/SharedICHelpers.h"
 #include "jit/VMFunctions.h"
 #include "vm/ScopeObject.h"
 #include "vm/TraceLogging.h"
 
 #include "jsscriptinlines.h"
 
+#include "jit/MacroAssembler-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script)
   : BaselineCompilerSpecific(cx, alloc, script),
@@ -486,17 +487,17 @@ BaselineCompiler::emitOutOfLinePostBarri
     // On ARM, save the link register before calling.  It contains the return
     // address.  The |masm.ret()| later will pop this into |pc| to return.
     masm.push(lr);
 #elif defined(JS_CODEGEN_MIPS32)
     masm.push(ra);
 #endif
     masm.pushValue(R0);
 
-    masm.setupUnalignedABICall(2, scratch);
+    masm.setupUnalignedABICall(scratch);
     masm.movePtr(ImmPtr(cx->runtime()), scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(objReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
 
     masm.popValue(R0);
     masm.ret();
     return true;
@@ -581,17 +582,17 @@ BaselineCompiler::emitStackCheck(bool ea
     return true;
 }
 
 void
 BaselineCompiler::emitIsDebuggeeCheck()
 {
     if (compileDebugInstrumentation_) {
         masm.Push(BaselineFrameReg);
-        masm.setupUnalignedABICall(1, R0.scratchReg());
+        masm.setupUnalignedABICall(R0.scratchReg());
         masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
         masm.passABIArg(R0.scratchReg());
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::FrameIsDebuggeeCheck));
         masm.Pop(BaselineFrameReg);
     }
 }
 
 typedef bool (*DebugPrologueFn)(JSContext*, BaselineFrame*, jsbytecode*, bool*);
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/DebugOnly.h"
 
 #include "jit/JitcodeMap.h"
 #include "jit/Linker.h"
 #include "jit/PerfSpewer.h"
 
 #include "jit/JitFrames-inl.h"
+#include "jit/MacroAssembler-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 
 struct DebugModeOSREntry
@@ -1074,17 +1075,17 @@ EmitBaselineDebugModeOSRHandlerTail(Macr
     } else {
         masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR0)));
         masm.pushValue(Address(temp, offsetof(BaselineDebugModeOSRInfo, valueR1)));
     }
     masm.push(BaselineFrameReg);
     masm.push(Address(temp, offsetof(BaselineDebugModeOSRInfo, resumeAddr)));
 
     // Call a stub to free the allocated info.
-    masm.setupUnalignedABICall(1, temp);
+    masm.setupUnalignedABICall(temp);
     masm.loadBaselineFramePtr(BaselineFrameReg, temp);
     masm.passABIArg(temp);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBaselineDebugModeOSR));
 
     // Restore saved values.
     AllocatableGeneralRegisterSet jumpRegs(GeneralRegisterSet::All());
     if (returnFromCallVM) {
         jumpRegs.take(ReturnReg);
@@ -1126,17 +1127,17 @@ JitRuntime::generateBaselineDebugModeOSR
     CodeOffsetLabel noFrameRegPopOffset(masm.currentOffset());
 
     // Record the stack pointer for syncing.
     masm.moveStackPtrTo(syncedStackStart);
     masm.push(ReturnReg);
     masm.push(BaselineFrameReg);
 
     // Call a stub to fully initialize the info.
-    masm.setupUnalignedABICall(3, temp);
+    masm.setupUnalignedABICall(temp);
     masm.loadBaselineFramePtr(BaselineFrameReg, temp);
     masm.passABIArg(temp);
     masm.passABIArg(syncedStackStart);
     masm.passABIArg(ReturnReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, SyncBaselineDebugModeOSRInfo));
 
     // Discard stack values depending on how many were unsynced, as we always
     // have a fully synced stack in the recompile handler. We arrive here via
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1807,17 +1807,17 @@ ICToBool_Object::Compiler::generateStubC
     masm.moveValue(BooleanValue(true), R0);
     EmitReturnFromIC(masm);
 
     masm.bind(&ifFalse);
     masm.moveValue(BooleanValue(false), R0);
     EmitReturnFromIC(masm);
 
     masm.bind(&slowPath);
-    masm.setupUnalignedABICall(1, scratch);
+    masm.setupUnalignedABICall(scratch);
     masm.passABIArg(objReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::EmulatesUndefined));
     masm.convertBoolToInt32(ReturnReg, ReturnReg);
     masm.xor32(Imm32(1), ReturnReg);
     masm.tagValue(JSVAL_TYPE_BOOLEAN, ReturnReg, R0);
     EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
@@ -2260,17 +2260,17 @@ ICBinaryArith_Double::Compiler::generate
         break;
       case JSOP_MUL:
         masm.mulDouble(FloatReg1, FloatReg0);
         break;
       case JSOP_DIV:
         masm.divDouble(FloatReg1, FloatReg0);
         break;
       case JSOP_MOD:
-        masm.setupUnalignedABICall(2, R0.scratchReg());
+        masm.setupUnalignedABICall(R0.scratchReg());
         masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
         masm.passABIArg(FloatReg1, MoveOp::DOUBLE);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NumberMod), MoveOp::DOUBLE);
         MOZ_ASSERT(ReturnDoubleReg == FloatReg0);
         break;
       default:
         MOZ_CRASH("Unexpected op");
     }
@@ -2388,17 +2388,17 @@ ICBinaryArith_DoubleWithInt32::Compiler:
     {
         Label doneTruncate;
         Label truncateABICall;
         masm.branchTruncateDouble(FloatReg0, scratchReg, &truncateABICall);
         masm.jump(&doneTruncate);
 
         masm.bind(&truncateABICall);
         masm.push(intReg);
-        masm.setupUnalignedABICall(1, scratchReg);
+        masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
         masm.callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
         masm.storeCallResult(scratchReg);
         masm.pop(intReg);
 
         masm.bind(&doneTruncate);
     }
 
@@ -2540,17 +2540,17 @@ ICUnaryArith_Double::Compiler::generateS
         Register scratchReg = R1.scratchReg();
 
         Label doneTruncate;
         Label truncateABICall;
         masm.branchTruncateDouble(FloatReg0, scratchReg, &truncateABICall);
         masm.jump(&doneTruncate);
 
         masm.bind(&truncateABICall);
-        masm.setupUnalignedABICall(1, scratchReg);
+        masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
         masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
         masm.storeCallResult(scratchReg);
 
         masm.bind(&doneTruncate);
         masm.not32(scratchReg);
         masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
     }
@@ -11022,17 +11022,17 @@ ICCall_Native::Compiler::generateStubCod
 
     Register scratch = regs.takeAny();
     EmitCreateStubFrameDescriptor(masm, scratch);
     masm.push(scratch);
     masm.push(ICTailCallReg);
     masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
 
     // Execute call.
-    masm.setupUnalignedABICall(3, scratch);
+    masm.setupUnalignedABICall(scratch);
     masm.loadJSContext(scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(argcReg);
     masm.passABIArg(vpReg);
 
 #ifdef JS_SIMULATOR
     // The simulator requires VM calls to be redirected to a special swi
     // instruction to handle them, so we store the redirected pointer in the
@@ -11120,17 +11120,17 @@ ICCall_ClassHook::Compiler::generateStub
     masm.push(argcReg);
 
     EmitCreateStubFrameDescriptor(masm, scratch);
     masm.push(scratch);
     masm.push(ICTailCallReg);
     masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
 
     // Execute call.
-    masm.setupUnalignedABICall(3, scratch);
+    masm.setupUnalignedABICall(scratch);
     masm.loadJSContext(scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(argcReg);
     masm.passABIArg(vpReg);
     masm.callWithABI(Address(ICStubReg, ICCall_ClassHook::offsetOfNative()));
 
     // Test for failure.
     masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
@@ -11523,17 +11523,17 @@ ICTableSwitch::Compiler::generateStubCod
 
         // N.B. -0 === 0, so convert -0 to a 0 int32.
         masm.convertDoubleToInt32(FloatReg0, key, &outOfRange, /* negativeZeroCheck = */ false);
     } else {
         // Pass pointer to double value.
         masm.pushValue(R0);
         masm.moveStackPtrTo(R0.scratchReg());
 
-        masm.setupUnalignedABICall(1, scratch);
+        masm.setupUnalignedABICall(scratch);
         masm.passABIArg(R0.scratchReg());
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, DoubleValueToInt32ForSwitch));
 
         // If the function returns |true|, the value has been converted to
         // int32.
         masm.movePtr(ReturnReg, scratch);
         masm.popValue(R0);
         masm.branchIfFalseBool(scratch, &outOfRange);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -378,17 +378,17 @@ CodeGenerator::visitFloat32ToInt32(LFloa
 
 void
 CodeGenerator::emitOOLTestObject(Register objreg,
                                  Label* ifEmulatesUndefined,
                                  Label* ifDoesntEmulateUndefined,
                                  Register scratch)
 {
     saveVolatile(scratch);
-    masm.setupUnalignedABICall(1, scratch);
+    masm.setupUnalignedABICall(scratch);
     masm.passABIArg(objreg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::EmulatesUndefined));
     masm.storeCallResult(scratch);
     restoreVolatile(scratch);
 
     masm.branchIfTrueBool(scratch, ifEmulatesUndefined);
     masm.jump(ifDoesntEmulateUndefined);
 }
@@ -1142,17 +1142,17 @@ PrepareAndExecuteRegExp(JSContext* cx, M
     if (input.volatile_())
         volatileRegs.add(input);
     if (regexp.volatile_())
         volatileRegs.add(regexp);
 
     // Execute the RegExp.
     masm.computeEffectiveAddress(Address(masm.getStackPointer(), inputOutputDataStartOffset), temp2);
     masm.PushRegsInMask(volatileRegs);
-    masm.setupUnalignedABICall(1, temp3);
+    masm.setupUnalignedABICall(temp3);
     masm.passABIArg(temp2);
     masm.callWithABI(codePointer);
     masm.PopRegsInMask(volatileRegs);
 
     Label success;
     masm.branch32(Assembler::Equal, matchResultAddress,
                   Imm32(RegExpRunStatus_Success_NotFound), notFound);
     masm.branch32(Assembler::Equal, matchResultAddress,
@@ -2716,17 +2716,17 @@ CodeGenerator::visitOutOfLineCallPostWri
         objreg = ToRegister(obj);
         regs.takeUnchecked(objreg);
     }
 
     Register runtimereg = regs.takeAny();
     masm.mov(ImmPtr(GetJitContext()->runtime), runtimereg);
 
     void (*fun)(JSRuntime*, JSObject*) = isGlobal ? PostGlobalWriteBarrier : PostWriteBarrier;
-    masm.setupUnalignedABICall(2, regs.takeAny());
+    masm.setupUnalignedABICall(regs.takeAny());
     masm.passABIArg(runtimereg);
     masm.passABIArg(objreg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
 
     restoreLiveVolatile(ool->lir());
 
     masm.jump(ool->rejoin());
 }
@@ -2818,17 +2818,17 @@ CodeGenerator::visitCallNative(LCallNati
     // Construct native exit frame.
     uint32_t safepointOffset;
     masm.buildFakeExitFrame(tempReg, &safepointOffset);
     masm.enterFakeExitFrame(NativeExitFrameLayout::Token());
 
     markSafepointAt(safepointOffset, call);
 
     // Construct and execute call.
-    masm.setupUnalignedABICall(3, tempReg);
+    masm.setupUnalignedABICall(tempReg);
     masm.passABIArg(argContextReg);
     masm.passABIArg(argUintNReg);
     masm.passABIArg(argVpReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
 
     // Test for failure.
     masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
 
@@ -2937,17 +2937,17 @@ CodeGenerator::visitCallDOMNative(LCallD
     // Construct native exit frame.
     uint32_t safepointOffset;
     masm.buildFakeExitFrame(argJSContext, &safepointOffset);
     masm.enterFakeExitFrame(IonDOMMethodExitFrameLayout::Token());
 
     markSafepointAt(safepointOffset, call);
 
     // Construct and execute call.
-    masm.setupUnalignedABICall(4, argJSContext);
+    masm.setupUnalignedABICall(argJSContext);
 
     masm.loadJSContext(argJSContext);
 
     masm.passABIArg(argJSContext);
     masm.passABIArg(argObj);
     masm.passABIArg(argPrivate);
     masm.passABIArg(argArgs);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->jitInfo()->method));
@@ -3478,17 +3478,17 @@ CodeGenerator::visitGetDynamicName(LGetD
     Register temp3 = ToRegister(lir->temp3());
 
     masm.loadJSContext(temp3);
 
     /* Make space for the outparam. */
     masm.adjustStack(-int32_t(sizeof(Value)));
     masm.moveStackPtrTo(temp2);
 
-    masm.setupUnalignedABICall(4, temp1);
+    masm.setupUnalignedABICall(temp1);
     masm.passABIArg(temp3);
     masm.passABIArg(scopeChain);
     masm.passABIArg(name);
     masm.passABIArg(temp2);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, GetDynamicName));
 
     const ValueOperand out = ToOutValue(lir);
 
@@ -3501,17 +3501,17 @@ CodeGenerator::visitGetDynamicName(LGetD
 }
 
 void
 CodeGenerator::emitFilterArgumentsOrEval(LInstruction* lir, Register string,
                                          Register temp1, Register temp2)
 {
     masm.loadJSContext(temp2);
 
-    masm.setupUnalignedABICall(2, temp1);
+    masm.setupUnalignedABICall(temp1);
     masm.passABIArg(temp2);
     masm.passABIArg(string);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, FilterArgumentsOrEval));
 
     Label bail;
     masm.branchIfFalseBool(ReturnReg, &bail);
     bailoutFrom(&bail, lir->snapshot());
 }
@@ -3878,17 +3878,17 @@ CodeGenerator::emitAssertObjectOrStringR
 
         masm.assumeUnreachable("MIR instruction returned object with unexpected type");
 
         masm.bind(&ok);
     }
 
     // Check that we have a valid GC pointer.
     saveVolatile();
-    masm.setupUnalignedABICall(2, temp);
+    masm.setupUnalignedABICall(temp);
     masm.loadJSContext(temp);
     masm.passABIArg(temp);
     masm.passABIArg(input);
 
     void* callee;
     switch (type) {
       case MIRType_Object:
         callee = JS_FUNC_TO_DATA_PTR(void*, AssertValidObjectPtr);
@@ -3951,17 +3951,17 @@ CodeGenerator::emitAssertResultV(const V
     }
 
     // Check that we have a valid GC pointer.
     saveVolatile();
 
     masm.pushValue(input);
     masm.moveStackPtrTo(temp1);
 
-    masm.setupUnalignedABICall(2, temp2);
+    masm.setupUnalignedABICall(temp2);
     masm.loadJSContext(temp2);
     masm.passABIArg(temp2);
     masm.passABIArg(temp1);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, AssertValidValue));
     masm.popValue(input);
     restoreVolatile();
 
     masm.bind(&done);
@@ -4213,30 +4213,30 @@ CodeGenerator::visitNewDerivedTypedObjec
 
 void
 CodeGenerator::visitAtan2D(LAtan2D* lir)
 {
     Register temp = ToRegister(lir->temp());
     FloatRegister y = ToFloatRegister(lir->y());
     FloatRegister x = ToFloatRegister(lir->x());
 
-    masm.setupUnalignedABICall(2, temp);
+    masm.setupUnalignedABICall(temp);
     masm.passABIArg(y, MoveOp::DOUBLE);
     masm.passABIArg(x, MoveOp::DOUBLE);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ecmaAtan2), MoveOp::DOUBLE);
 
     MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg);
 }
 
 void
 CodeGenerator::visitHypot(LHypot* lir)
 {
     Register temp = ToRegister(lir->temp());
     uint32_t numArgs = lir->numArgs();
-    masm.setupUnalignedABICall(numArgs, temp);
+    masm.setupUnalignedABICall(temp);
 
     for (uint32_t i = 0 ; i < numArgs; ++i)
         masm.passABIArg(ToFloatRegister(lir->getOperand(i)), MoveOp::DOUBLE);
 
     switch(numArgs) {
       case 2:
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ecmaHypot), MoveOp::DOUBLE);
         break;
@@ -5095,17 +5095,17 @@ void
 CodeGenerator::visitSetDisjointTypedElements(LSetDisjointTypedElements* lir)
 {
     Register target = ToRegister(lir->target());
     Register targetOffset = ToRegister(lir->targetOffset());
     Register source = ToRegister(lir->source());
 
     Register temp = ToRegister(lir->temp());
 
-    masm.setupUnalignedABICall(3, temp);
+    masm.setupUnalignedABICall(temp);
     masm.passABIArg(target);
     masm.passABIArg(targetOffset);
     masm.passABIArg(source);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::SetDisjointTypedElements));
 }
 
 void
 CodeGenerator::visitTypedObjectDescr(LTypedObjectDescr* lir)
@@ -5226,52 +5226,49 @@ void
 CodeGenerator::visitPowI(LPowI* ins)
 {
     FloatRegister value = ToFloatRegister(ins->value());
     Register power = ToRegister(ins->power());
     Register temp = ToRegister(ins->temp());
 
     MOZ_ASSERT(power != temp);
 
-    // In all implementations, setupUnalignedABICall() relinquishes use of
-    // its scratch register. We can therefore save an input register by
-    // reusing the scratch register to pass constants to callWithABI.
-    masm.setupUnalignedABICall(2, temp);
+    masm.setupUnalignedABICall(temp);
     masm.passABIArg(value, MoveOp::DOUBLE);
     masm.passABIArg(power);
 
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::powi), MoveOp::DOUBLE);
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 }
 
 void
 CodeGenerator::visitPowD(LPowD* ins)
 {
     FloatRegister value = ToFloatRegister(ins->value());
     FloatRegister power = ToFloatRegister(ins->power());
     Register temp = ToRegister(ins->temp());
 
-    masm.setupUnalignedABICall(2, temp);
+    masm.setupUnalignedABICall(temp);
     masm.passABIArg(value, MoveOp::DOUBLE);
     masm.passABIArg(power, MoveOp::DOUBLE);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ecmaPow), MoveOp::DOUBLE);
 
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 }
 
 void
 CodeGenerator::visitMathFunctionD(LMathFunctionD* ins)
 {
     Register temp = ToRegister(ins->temp());
     FloatRegister input = ToFloatRegister(ins->input());
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 
+    masm.setupUnalignedABICall(temp);
+
     const MathCache* mathCache = ins->mir()->cache();
-
-    masm.setupUnalignedABICall(mathCache ? 2 : 1, temp);
     if (mathCache) {
         masm.movePtr(ImmPtr(mathCache), temp);
         masm.passABIArg(temp);
     }
     masm.passABIArg(input, MoveOp::DOUBLE);
 
 #   define MAYBE_CACHED(fcn) (mathCache ? (void*)fcn ## _impl : (void*)fcn ## _uncached)
 
@@ -5360,17 +5357,17 @@ CodeGenerator::visitMathFunctionD(LMathF
 
 void
 CodeGenerator::visitMathFunctionF(LMathFunctionF* ins)
 {
     Register temp = ToRegister(ins->temp());
     FloatRegister input = ToFloatRegister(ins->input());
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnFloat32Reg);
 
-    masm.setupUnalignedABICall(1, temp);
+    masm.setupUnalignedABICall(temp);
     masm.passABIArg(input, MoveOp::FLOAT32);
 
     void* funptr = nullptr;
     switch (ins->mir()->function()) {
       case MMathFunction::Floor: funptr = JS_FUNC_TO_DATA_PTR(void*, floorf);           break;
       case MMathFunction::Round: funptr = JS_FUNC_TO_DATA_PTR(void*, math_roundf_impl); break;
       case MMathFunction::Ceil:  funptr = JS_FUNC_TO_DATA_PTR(void*, ceilf);            break;
       default:
@@ -5384,17 +5381,17 @@ void
 CodeGenerator::visitModD(LModD* ins)
 {
     FloatRegister lhs = ToFloatRegister(ins->lhs());
     FloatRegister rhs = ToFloatRegister(ins->rhs());
     Register temp = ToRegister(ins->temp());
 
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 
-    masm.setupUnalignedABICall(2, temp);
+    masm.setupUnalignedABICall(temp);
     masm.passABIArg(lhs, MoveOp::DOUBLE);
     masm.passABIArg(rhs, MoveOp::DOUBLE);
 
     if (gen->compilingAsmJS())
         masm.callWithABI(AsmJSImm_ModD, MoveOp::DOUBLE);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NumberMod), MoveOp::DOUBLE);
 }
@@ -6212,17 +6209,17 @@ JitRuntime::generateMallocStub(JSContext
     regs.takeUnchecked(regNBytes);
     LiveRegisterSet save(regs.asLiveSet());
     masm.PushRegsInMask(save);
 
     const Register regTemp = regs.takeAnyGeneral();
     const Register regRuntime = regTemp;
     MOZ_ASSERT(regTemp != regNBytes);
 
-    masm.setupUnalignedABICall(2, regTemp);
+    masm.setupUnalignedABICall(regTemp);
     masm.movePtr(ImmPtr(cx->runtime()), regRuntime);
     masm.passABIArg(regRuntime);
     masm.passABIArg(regNBytes);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, MallocWrapper));
     masm.storeCallResult(regReturn);
 
     masm.PopRegsInMask(save);
     masm.ret();
@@ -6250,17 +6247,17 @@ JitRuntime::generateFreeStub(JSContext* 
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     regs.takeUnchecked(regSlots);
     LiveRegisterSet save(regs.asLiveSet());
     masm.PushRegsInMask(save);
 
     const Register regTemp = regs.takeAnyGeneral();
     MOZ_ASSERT(regTemp != regSlots);
 
-    masm.setupUnalignedABICall(1, regTemp);
+    masm.setupUnalignedABICall(regTemp);
     masm.passABIArg(regSlots);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js_free));
 
     masm.PopRegsInMask(save);
 
     masm.ret();
 
     Linker linker(masm);
@@ -6291,17 +6288,17 @@ JitRuntime::generateLazyLinkStub(JSConte
     // restore it once the lazy link is complete.
     Address descriptor(masm.getStackPointer(), CommonFrameLayout::offsetOfDescriptor());
     size_t convertToExitFrame = JitFrameLayout::Size() - ExitFrameLayout::Size();
     masm.addPtr(Imm32(convertToExitFrame << FRAMESIZE_SHIFT), descriptor);
 
     masm.enterFakeExitFrame(LazyLinkExitFrameLayout::Token());
     masm.PushStubCode();
 
-    masm.setupUnalignedABICall(1, temp0);
+    masm.setupUnalignedABICall(temp0);
     masm.loadJSContext(temp0);
     masm.passABIArg(temp0);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, LazyLinkTopActivation));
 
     masm.leaveExitFrame(/* stub code */ sizeof(JitCode*));
 
     masm.addPtr(Imm32(- (convertToExitFrame << FRAMESIZE_SHIFT)), descriptor);
 
@@ -7089,17 +7086,17 @@ CodeGenerator::emitArrayPopShift(LInstru
 
     if (mir->mode() == MArrayPopShift::Shift) {
         // Don't save the temp registers.
         LiveRegisterSet temps;
         temps.add(elementsTemp);
         temps.add(lengthTemp);
 
         saveVolatile(temps);
-        masm.setupUnalignedABICall(1, lengthTemp);
+        masm.setupUnalignedABICall(lengthTemp);
         masm.passABIArg(obj);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::ArrayShiftMoveElements));
         restoreVolatile(temps);
     }
 
     masm.bind(&done);
     masm.bind(ool->rejoin());
 }
@@ -8843,17 +8840,17 @@ CodeGenerator::visitOutOfLineTypeOfV(Out
 
     ValueOperand input = ToValue(ins, LTypeOfV::Input);
     Register temp = ToTempUnboxRegister(ins->tempToUnbox());
     Register output = ToRegister(ins->output());
 
     Register obj = masm.extractObject(input, temp);
 
     saveVolatile(output);
-    masm.setupUnalignedABICall(2, output);
+    masm.setupUnalignedABICall(output);
     masm.passABIArg(obj);
     masm.movePtr(ImmPtr(GetJitContext()->runtime), output);
     masm.passABIArg(output);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::TypeOfObjectOperation));
     masm.storeCallResult(output);
     restoreVolatile(output);
 
     masm.jump(ool->rejoin());
@@ -9662,17 +9659,17 @@ CodeGenerator::visitGetDOMProperty(LGetD
     masm.moveStackPtrTo(ObjectReg);
 
     uint32_t safepointOffset;
     masm.buildFakeExitFrame(JSContextReg, &safepointOffset);
     masm.enterFakeExitFrame(IonDOMExitFrameLayout::GetterToken());
 
     markSafepointAt(safepointOffset, ins);
 
-    masm.setupUnalignedABICall(4, JSContextReg);
+    masm.setupUnalignedABICall(JSContextReg);
 
     masm.loadJSContext(JSContextReg);
 
     masm.passABIArg(JSContextReg);
     masm.passABIArg(ObjectReg);
     masm.passABIArg(PrivateReg);
     masm.passABIArg(ValueReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun()));
@@ -9752,17 +9749,17 @@ CodeGenerator::visitSetDOMProperty(LSetD
     masm.moveStackPtrTo(ObjectReg);
 
     uint32_t safepointOffset;
     masm.buildFakeExitFrame(JSContextReg, &safepointOffset);
     masm.enterFakeExitFrame(IonDOMExitFrameLayout::SetterToken());
 
     markSafepointAt(safepointOffset, ins);
 
-    masm.setupUnalignedABICall(4, JSContextReg);
+    masm.setupUnalignedABICall(JSContextReg);
 
     masm.loadJSContext(JSContextReg);
 
     masm.passABIArg(JSContextReg);
     masm.passABIArg(ObjectReg);
     masm.passABIArg(PrivateReg);
     masm.passABIArg(ValueReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ins->mir()->fun()));
@@ -9820,17 +9817,17 @@ CodeGenerator::visitIsCallable(LIsCallab
 void
 CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool)
 {
     LIsCallable* ins = ool->ins();
     Register object = ToRegister(ins->object());
     Register output = ToRegister(ins->output());
 
     saveVolatile(output);
-    masm.setupUnalignedABICall(1, output);
+    masm.setupUnalignedABICall(output);
     masm.passABIArg(object);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ObjectIsCallable));
     masm.storeCallResult(output);
     // C++ compilers like to only use the bottom byte for bools, but we need to maintain the entire
     // register.
     masm.and32(Imm32(0xFF), output);
     restoreVolatile(output);
     masm.jump(ool->rejoin());
@@ -10187,17 +10184,17 @@ CodeGenerator::visitThrowUninitializedLe
 
 void
 CodeGenerator::visitDebugger(LDebugger* ins)
 {
     Register cx = ToRegister(ins->getTemp(0));
     Register temp = ToRegister(ins->getTemp(1));
 
     masm.loadJSContext(cx);
-    masm.setupUnalignedABICall(1, temp);
+    masm.setupUnalignedABICall(temp);
     masm.passABIArg(cx);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, GlobalHasLiveOnDebuggerStatement));
 
     Label bail;
     masm.branchIfTrueBool(ReturnReg, &bail);
     bailoutFrom(&bail, ins->snapshot());
 }
 
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -943,17 +943,17 @@ EmitGetterCall(JSContext* cx, MacroAssem
         masm.Push(argUintNReg);
         attacher.pushStubCodePointer(masm);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
         masm.enterFakeExitFrame(IonOOLNativeExitFrameLayout::Token());
 
         // Construct and execute call.
-        masm.setupUnalignedABICall(3, scratchReg);
+        masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argUintNReg);
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
 
         // Test for failure.
         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
@@ -1001,17 +1001,17 @@ EmitGetterCall(JSContext* cx, MacroAssem
 
         masm.loadJSContext(argJSContextReg);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
         masm.enterFakeExitFrame(IonOOLPropertyOpExitFrameLayout::Token());
 
         // Make the call.
-        masm.setupUnalignedABICall(4, scratchReg);
+        masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argObjReg);
         masm.passABIArg(argIdReg);
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target));
 
         // Test for failure.
         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
@@ -1581,17 +1581,17 @@ EmitCallProxyGet(JSContext* cx, MacroAss
 
     masm.loadJSContext(argJSContextReg);
 
     if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
         return false;
     masm.enterFakeExitFrame(IonOOLProxyExitFrameLayout::Token());
 
     // Make the call.
-    masm.setupUnalignedABICall(5, scratch);
+    masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContextReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argIdReg);
     masm.passABIArg(argVpReg);
     masm.callWithABI(getFunction);
 
     // Test for failure.
@@ -2217,17 +2217,17 @@ EmitObjectOpResultCheck(MacroAssembler& 
         argObjReg);
     masm.computeEffectiveAddress(
         Address(masm.getStackPointer(), FrameLayout::offsetOfId()),
         argIdReg);
     masm.move32(Imm32(strict), argStrictReg);
     masm.computeEffectiveAddress(
         Address(masm.getStackPointer(), FrameLayout::offsetOfObjectOpResult()),
         argResultReg);
-    masm.setupUnalignedABICall(5, scratchReg);
+    masm.setupUnalignedABICall(scratchReg);
     masm.passABIArg(argJSContextReg);
     masm.passABIArg(argObjReg);
     masm.passABIArg(argIdReg);
     masm.passABIArg(argStrictReg);
     masm.passABIArg(argResultReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ReportStrictErrorOrWarning));
     masm.branchIfFalseBool(ReturnReg, failure);
 
@@ -2292,17 +2292,17 @@ EmitCallProxySet(JSContext* cx, MacroAss
 
     masm.loadJSContext(argJSContextReg);
 
     if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
         return false;
     masm.enterFakeExitFrame(IonOOLProxyExitFrameLayout::Token());
 
     // Make the call.
-    masm.setupUnalignedABICall(5, scratch);
+    masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContextReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argIdReg);
     masm.passABIArg(argValueReg);
     masm.passABIArg(argStrictReg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ProxySetProperty));
 
     // Test for error.
@@ -2501,17 +2501,17 @@ GenerateCallSetter(JSContext* cx, IonScr
         masm.Push(argUintNReg);
         attacher.pushStubCodePointer(masm);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
         masm.enterFakeExitFrame(IonOOLNativeExitFrameLayout::Token());
 
         // Make the call
-        masm.setupUnalignedABICall(3, scratchReg);
+        masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argUintNReg);
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
 
         // Test for failure.
         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
@@ -2565,17 +2565,17 @@ GenerateCallSetter(JSContext* cx, IonScr
 
         masm.loadJSContext(argJSContextReg);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
         masm.enterFakeExitFrame(IonOOLSetterOpExitFrameLayout::Token());
 
         // Make the call.
-        masm.setupUnalignedABICall(5, scratchReg);
+        masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argObjReg);
         masm.passABIArg(argIdReg);
         masm.passABIArg(argValueReg);
         masm.passABIArg(argResultReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target));
 
         // Test for error.
@@ -2810,17 +2810,17 @@ GenerateAddSlot(JSContext* cx, MacroAsse
         Register temp2 = regs.takeAnyGeneral();
 
         if (obj->is<UnboxedPlainObject>()) {
             // Pass the expando object to the stub.
             masm.Push(object);
             masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
         }
 
-        masm.setupUnalignedABICall(3, temp1);
+        masm.setupUnalignedABICall(temp1);
         masm.loadJSContext(temp1);
         masm.passABIArg(temp1);
         masm.passABIArg(object);
         masm.move32(Imm32(newNumDynamicSlots), temp2);
         masm.passABIArg(temp2);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NativeObject::growSlotsStatic));
 
         // Branch on ReturnReg before restoring volatile registers, so
@@ -3458,17 +3458,17 @@ GetElementIC::attachGetProp(JSContext* c
     masm.PushRegsInMask(volatileRegs);
 
     Register objReg = object();
     MOZ_ASSERT(objReg != scratch);
 
     if (!volatileRegs.has(objReg))
         masm.push(objReg);
 
-    masm.setupUnalignedABICall(2, scratch);
+    masm.setupUnalignedABICall(scratch);
     masm.movePtr(ImmGCPtr(name), objReg);
     masm.passABIArg(objReg);
     masm.unboxString(val, scratch);
     masm.passABIArg(scratch);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, EqualStringsHelper));
     masm.mov(ReturnReg, scratch);
 
     if (!volatileRegs.has(objReg))
@@ -3815,17 +3815,17 @@ GenerateGetTypedOrUnboxedArrayElement(JS
         // Part 2: Call to translate the str into index
         AllocatableRegisterSet regs(RegisterSet::Volatile());
         LiveRegisterSet save(regs.asLiveSet());
         masm.PushRegsInMask(save);
         regs.takeUnchecked(str);
 
         Register temp = regs.takeAnyGeneral();
 
-        masm.setupUnalignedABICall(1, temp);
+        masm.setupUnalignedABICall(temp);
         masm.passABIArg(str);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, GetIndexFromString));
         masm.mov(ReturnReg, indexReg);
 
         LiveRegisterSet ignore;
         ignore.add(indexReg);
         masm.PopRegsInMaskIgnore(save, ignore);
 
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -71,16 +71,92 @@ MacroAssembler::call(const CallSiteDesc&
 
 void
 MacroAssembler::call(const CallSiteDesc& desc, Label* label)
 {
     call(label);
     append(desc, currentOffset(), framePushed());
 }
 
+// ===============================================================
+// ABI function calls.
+
+void
+MacroAssembler::passABIArg(Register reg)
+{
+    passABIArg(MoveOperand(reg), MoveOp::GENERAL);
+}
+
+void
+MacroAssembler::passABIArg(FloatRegister reg, MoveOp::Type type)
+{
+    passABIArg(MoveOperand(reg), type);
+}
+
+template <typename T> void
+MacroAssembler::callWithABI(const T& fun, MoveOp::Type result)
+{
+    profilerPreCall();
+    callWithABINoProfiler(fun, result);
+    profilerPostReturn();
+}
+
+void
+MacroAssembler::appendSignatureType(MoveOp::Type type)
+{
+#ifdef JS_SIMULATOR
+    signature_ <<= ArgType_Shift;
+    switch (type) {
+      case MoveOp::GENERAL: signature_ |= ArgType_General; break;
+      case MoveOp::DOUBLE:  signature_ |= ArgType_Double;  break;
+      case MoveOp::FLOAT32: signature_ |= ArgType_Float32; break;
+      default: MOZ_CRASH("Invalid argument type");
+    }
+#endif
+}
+
+ABIFunctionType
+MacroAssembler::signature() const
+{
+#ifdef JS_SIMULATOR
+#ifdef DEBUG
+    switch (signature_) {
+      case Args_General0:
+      case Args_General1:
+      case Args_General2:
+      case Args_General3:
+      case Args_General4:
+      case Args_General5:
+      case Args_General6:
+      case Args_General7:
+      case Args_General8:
+      case Args_Double_None:
+      case Args_Int_Double:
+      case Args_Float32_Float32:
+      case Args_Double_Double:
+      case Args_Double_Int:
+      case Args_Double_DoubleInt:
+      case Args_Double_DoubleDouble:
+      case Args_Double_IntDouble:
+      case Args_Int_IntDouble:
+      case Args_Double_DoubleDoubleDouble:
+      case Args_Double_DoubleDoubleDoubleDouble:
+        break;
+      default:
+        MOZ_CRASH("Unexpected type");
+    }
+#endif // DEBUG
+
+    return ABIFunctionType(signature_);
+#else
+    // No simulator enabled.
+    MOZ_CRASH("Only available for making calls within a simulator.");
+#endif
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssembler::PushStubCode()
 {
     exitCodePatch_ = PushWithPatch(ImmWord(-1));
 }
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1481,17 +1481,17 @@ MacroAssembler::initGCThing(Register obj
     }
 
 #ifdef JS_GC_TRACE
     RegisterSet regs = RegisterSet::Volatile();
     PushRegsInMask(regs);
     regs.takeUnchecked(obj);
     Register temp = regs.takeAnyGeneral();
 
-    setupUnalignedABICall(2, temp);
+    setupUnalignedABICall(temp);
     passABIArg(obj);
     movePtr(ImmGCPtr(templateObj->type()), temp);
     passABIArg(temp);
     callWithABI(JS_FUNC_TO_DATA_PTR(void*, js::gc::TraceCreateObject));
 
     PopRegsInMask(RegisterSet::Volatile());
 #endif
 }
@@ -1619,17 +1619,17 @@ MacroAssembler::generateBailoutTail(Regi
     JS_STATIC_ASSERT(BAILOUT_RETURN_OVERRECURSED == 2);
 
     branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_OK), &baseline);
     branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_FATAL_ERROR), exceptionLabel());
 
     // Fall-through: overrecursed.
     {
         loadJSContext(ReturnReg);
-        setupUnalignedABICall(1, scratch);
+        setupUnalignedABICall(scratch);
         passABIArg(ReturnReg);
         callWithABI(JS_FUNC_TO_DATA_PTR(void*, BailoutReportOverRecursed));
         jump(exceptionLabel());
     }
 
     bind(&baseline);
     {
         // Prepare a register set for use in this case.
@@ -1683,17 +1683,17 @@ MacroAssembler::generateBailoutTail(Regi
         {
             // Save needed values onto stack temporarily.
             pushValue(Address(bailoutInfo, offsetof(BaselineBailoutInfo, valueR0)));
             push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr)));
             push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)));
             push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, monitorStub)));
 
             // Call a stub to free allocated memory and create arguments objects.
-            setupUnalignedABICall(1, temp);
+            setupUnalignedABICall(temp);
             passABIArg(bailoutInfo);
             callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBailoutToBaseline));
             branchTest32(Zero, ReturnReg, ReturnReg, exceptionLabel());
 
             // Restore values where they need to be and resume execution.
             AllocatableGeneralRegisterSet enterMonRegs(GeneralRegisterSet::All());
             enterMonRegs.take(R0);
             enterMonRegs.take(ICStubReg);
@@ -1721,17 +1721,17 @@ MacroAssembler::generateBailoutTail(Regi
         {
             // Save needed values onto stack temporarily.
             pushValue(Address(bailoutInfo, offsetof(BaselineBailoutInfo, valueR0)));
             pushValue(Address(bailoutInfo, offsetof(BaselineBailoutInfo, valueR1)));
             push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr)));
             push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)));
 
             // Call a stub to free allocated memory and create arguments objects.
-            setupUnalignedABICall(1, temp);
+            setupUnalignedABICall(temp);
             passABIArg(bailoutInfo);
             callWithABI(JS_FUNC_TO_DATA_PTR(void*, FinishBailoutToBaseline));
             branchTest32(Zero, ReturnReg, ReturnReg, exceptionLabel());
 
             // Restore values where they need to be and resume execution.
             AllocatableGeneralRegisterSet enterRegs(GeneralRegisterSet::All());
             enterRegs.take(R0);
             enterRegs.take(R1);
@@ -1796,17 +1796,17 @@ MacroAssembler::assumeUnreachable(const 
 {
 #ifdef DEBUG
     if (!IsCompilingAsmJS()) {
         AllocatableRegisterSet regs(RegisterSet::Volatile());
         LiveRegisterSet save(regs.asLiveSet());
         PushRegsInMask(save);
         Register temp = regs.takeAnyGeneral();
 
-        setupUnalignedABICall(1, temp);
+        setupUnalignedABICall(temp);
         movePtr(ImmPtr(output), temp);
         passABIArg(temp);
         callWithABI(JS_FUNC_TO_DATA_PTR(void*, AssumeUnreachable_));
 
         PopRegsInMask(save);
     }
 #endif
 
@@ -1839,17 +1839,17 @@ void
 MacroAssembler::printf(const char* output)
 {
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     LiveRegisterSet save(regs.asLiveSet());
     PushRegsInMask(save);
 
     Register temp = regs.takeAnyGeneral();
 
-    setupUnalignedABICall(1, temp);
+    setupUnalignedABICall(temp);
     movePtr(ImmPtr(output), temp);
     passABIArg(temp);
     callWithABI(JS_FUNC_TO_DATA_PTR(void*, Printf0_));
 
     PopRegsInMask(save);
 }
 
 static void
@@ -1865,17 +1865,17 @@ MacroAssembler::printf(const char* outpu
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     LiveRegisterSet save(regs.asLiveSet());
     PushRegsInMask(save);
 
     regs.takeUnchecked(value);
 
     Register temp = regs.takeAnyGeneral();
 
-    setupUnalignedABICall(2, temp);
+    setupUnalignedABICall(temp);
     movePtr(ImmPtr(output), temp);
     passABIArg(temp);
     passABIArg(value);
     callWithABI(JS_FUNC_TO_DATA_PTR(void*, Printf1_));
 
     PopRegsInMask(save);
 }
 
@@ -1888,17 +1888,17 @@ MacroAssembler::tracelogStartId(Register
 
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     LiveRegisterSet save(regs.asLiveSet());
     PushRegsInMask(save);
     regs.takeUnchecked(logger);
 
     Register temp = regs.takeAnyGeneral();
 
-    setupUnalignedABICall(2, temp);
+    setupUnalignedABICall(temp);
     passABIArg(logger);
     move32(Imm32(textId), temp);
     passABIArg(temp);
     callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStartEventPrivate));
 
     PopRegsInMask(save);
 }
 
@@ -1908,17 +1908,17 @@ MacroAssembler::tracelogStartId(Register
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     LiveRegisterSet save(regs.asLiveSet());
     PushRegsInMask(save);
     regs.takeUnchecked(logger);
     regs.takeUnchecked(textId);
 
     Register temp = regs.takeAnyGeneral();
 
-    setupUnalignedABICall(2, temp);
+    setupUnalignedABICall(temp);
     passABIArg(logger);
     passABIArg(textId);
     callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStartEventPrivate));
 
     PopRegsInMask(save);
 }
 
 void
@@ -1929,17 +1929,17 @@ MacroAssembler::tracelogStartEvent(Regis
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     LiveRegisterSet save(regs.asLiveSet());
     PushRegsInMask(save);
     regs.takeUnchecked(logger);
     regs.takeUnchecked(event);
 
     Register temp = regs.takeAnyGeneral();
 
-    setupUnalignedABICall(2, temp);
+    setupUnalignedABICall(temp);
     passABIArg(logger);
     passABIArg(event);
     callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogFunc));
 
     PopRegsInMask(save);
 }
 
 void
@@ -1950,17 +1950,17 @@ MacroAssembler::tracelogStopId(Register 
 
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     LiveRegisterSet save(regs.asLiveSet());
     PushRegsInMask(save);
     regs.takeUnchecked(logger);
 
     Register temp = regs.takeAnyGeneral();
 
-    setupUnalignedABICall(2, temp);
+    setupUnalignedABICall(temp);
     passABIArg(logger);
     move32(Imm32(textId), temp);
     passABIArg(temp);
 
     callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStopEventPrivate));
 
     PopRegsInMask(save);
 }
@@ -1971,17 +1971,17 @@ MacroAssembler::tracelogStopId(Register 
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     LiveRegisterSet save(regs.asLiveSet());
     PushRegsInMask(save);
     regs.takeUnchecked(logger);
     regs.takeUnchecked(textId);
 
     Register temp = regs.takeAnyGeneral();
 
-    setupUnalignedABICall(2, temp);
+    setupUnalignedABICall(temp);
     passABIArg(logger);
     passABIArg(textId);
     callWithABI(JS_FUNC_TO_DATA_PTR(void*, TraceLogStopEventPrivate));
 
     PopRegsInMask(save);
 }
 #endif
 
@@ -2543,16 +2543,19 @@ MacroAssembler::alignJitStackBasedOnNArg
 }
 
 // ===============================================================
 
 MacroAssembler::MacroAssembler(JSContext* cx, IonScript* ion,
                                JSScript* script, jsbytecode* pc)
   : emitProfilingInstrumentation_(false),
     framePushed_(0)
+#ifdef DEBUG
+  , inCall_(false)
+#endif
 {
     constructRoot(cx);
     jitContext_.emplace(cx, (js::jit::TempAllocator*)nullptr);
     alloc_.emplace(cx);
     moveResolver_.setAllocator(*jitContext_->temp);
 #if defined(JS_CODEGEN_ARM)
     initWithAllocator();
     m_buffer.id = GetJitContext()->getNextAssemblerId();
@@ -2759,9 +2762,113 @@ MacroAssembler::freeStack(uint32_t amoun
 }
 
 void
 MacroAssembler::freeStack(Register amount)
 {
     addToStackPtr(amount);
 }
 
+// ===============================================================
+// ABI function calls.
+
+void
+MacroAssembler::setupABICall()
+{
+#ifdef DEBUG
+    MOZ_ASSERT(!inCall_);
+    inCall_ = true;
+#endif
+
+#ifdef JS_SIMULATOR
+    signature_ = 0;
+#endif
+
+    // Reinitialize the ABIArg generator.
+    abiArgs_ = ABIArgGenerator();
+
+#if defined(JS_CODEGEN_ARM)
+    // On ARM, we need to know what ABI we are using, either in the
+    // simulator, or based on the configure flags.
+#if defined(JS_SIMULATOR_ARM)
+    abiArgs_.setUseHardFp(UseHardFpABI());
+#elif defined(JS_CODEGEN_ARM_HARDFP)
+    abiArgs_.setUseHardFp(true);
+#else
+    abiArgs_.setUseHardFp(false);
+#endif
+#endif
+
+#if defined(JS_CODEGEN_MIPS32)
+    // On MIPS, the system ABI use general registers pairs to encode double
+    // arguments, after one or 2 integer-like arguments. Unfortunately, the
+    // Lowering phase is not capable to express it at the moment. So we enforce
+    // the system ABI here.
+    abiArgs_.enforceO32ABI();
+#endif
+}
+
+void
+MacroAssembler::setupAlignedABICall()
+{
+    setupABICall();
+    dynamicAlignment_ = false;
+    assertStackAlignment(ABIStackAlignment);
+
+#if defined(JS_CODEGEN_ARM64)
+    MOZ_CRASH("Not supported on arm64");
+#endif
+}
+
+void
+MacroAssembler::passABIArg(const MoveOperand& from, MoveOp::Type type)
+{
+    MOZ_ASSERT(inCall_);
+    appendSignatureType(type);
+
+    ABIArg arg;
+    switch (type) {
+      case MoveOp::FLOAT32:
+        arg = abiArgs_.next(MIRType_Float32);
+        break;
+      case MoveOp::DOUBLE:
+        arg = abiArgs_.next(MIRType_Double);
+        break;
+      case MoveOp::GENERAL:
+        arg = abiArgs_.next(MIRType_Pointer);
+        break;
+      default:
+        MOZ_CRASH("Unexpected argument type");
+    }
+
+    MoveOperand to(*this, arg);
+    if (from == to)
+        return;
+
+    if (!enoughMemory_)
+        return;
+    enoughMemory_ = moveResolver_.addMove(from, to, type);
+}
+
+void
+MacroAssembler::callWithABINoProfiler(void* fun, MoveOp::Type result)
+{
+    appendSignatureType(result);
+#ifdef JS_SIMULATOR
+    fun = Simulator::RedirectNativeFunction(fun, signature());
+#endif
+
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(ImmPtr(fun));
+    callWithABIPost(stackAdjust, result);
+}
+
+void
+MacroAssembler::callWithABINoProfiler(AsmJSImmPtr imm, MoveOp::Type result)
+{
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust, /* callFromAsmJS = */ true);
+    call(imm);
+    callWithABIPost(stackAdjust, result);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -327,16 +327,19 @@ class MacroAssembler : public MacroAssem
 
     // Labels for handling exceptions and failures.
     NonAssertingLabel failureLabel_;
 
   public:
     MacroAssembler()
       : emitProfilingInstrumentation_(false),
         framePushed_(0)
+#ifdef DEBUG
+      , inCall_(false)
+#endif
     {
         JitContext* jcx = GetJitContext();
         JSContext* cx = jcx->cx;
         if (cx)
             constructRoot(cx);
 
         if (!jcx->temp) {
             MOZ_ASSERT(cx);
@@ -359,16 +362,19 @@ class MacroAssembler : public MacroAssem
     explicit MacroAssembler(JSContext* cx, IonScript* ion = nullptr,
                             JSScript* script = nullptr, jsbytecode* pc = nullptr);
 
     // asm.js compilation handles its own JitContext-pushing
     struct AsmJSToken {};
     explicit MacroAssembler(AsmJSToken)
       : emitProfilingInstrumentation_(false),
         framePushed_(0)
+#ifdef DEBUG
+      , inCall_(false)
+#endif
     {
 #if defined(JS_CODEGEN_ARM)
         initWithAllocator();
         m_buffer.id = 0;
 #elif defined(JS_CODEGEN_ARM64)
         initWithAllocator();
         armbuffer_.id = 0;
 #endif
@@ -468,16 +474,89 @@ class MacroAssembler : public MacroAssem
     void call(ImmPtr imm) PER_SHARED_ARCH;
     void call(AsmJSImmPtr imm) PER_SHARED_ARCH;
     // Call a target JitCode, which must be traceable, and may be movable.
     void call(JitCode* c) PER_SHARED_ARCH;
 
     inline void call(const CallSiteDesc& desc, const Register reg);
     inline void call(const CallSiteDesc& desc, Label* label);
 
+  public:
+    // ===============================================================
+    // ABI function calls.
+
+    // Setup a call to C/C++ code, given the assumption that the framePushed
+    // accruately define the state of the stack, and that the top of the stack
+    // was properly aligned. Note that this only supports cdecl.
+    void setupAlignedABICall(); // CRASH_ON(arm64)
+
+    // Setup an ABI call for when the alignment is not known. This may need a
+    // scratch register.
+    void setupUnalignedABICall(Register scratch) PER_ARCH;
+
+    // Arguments must be assigned to a C/C++ call in order. They are moved
+    // in parallel immediately before performing the call. This process may
+    // temporarily use more stack, in which case esp-relative addresses will be
+    // automatically adjusted. It is extremely important that esp-relative
+    // addresses are computed *after* setupABICall(). Furthermore, no
+    // operations should be emitted while setting arguments.
+    void passABIArg(const MoveOperand& from, MoveOp::Type type);
+    inline void passABIArg(Register reg);
+    inline void passABIArg(FloatRegister reg, MoveOp::Type type);
+
+    template <typename T>
+    inline void callWithABI(const T& fun, MoveOp::Type result = MoveOp::GENERAL);
+
+  private:
+    // Reinitialize the variables which have to be cleared before making a call
+    // with callWithABI.
+    void setupABICall();
+
+    // Reserve the stack and resolve the arguments move.
+    void callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS = false) PER_ARCH;
+
+    // Emits a call to a C/C++ function, resolving all argument moves.
+    void callWithABINoProfiler(void* fun, MoveOp::Type result);
+    void callWithABINoProfiler(AsmJSImmPtr imm, MoveOp::Type result);
+    void callWithABINoProfiler(Register fun, MoveOp::Type result) PER_ARCH;
+    void callWithABINoProfiler(const Address& fun, MoveOp::Type result) PER_ARCH;
+
+    // Restore the stack to its state before the setup function call.
+    void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result) PER_ARCH;
+
+    // Create the signature to be able to decode the arguments of a native
+    // function, when calling a function within the simulator.
+    inline void appendSignatureType(MoveOp::Type type);
+    inline ABIFunctionType signature() const;
+
+    // Private variables used to handle moves between registers given as
+    // arguments to passABIArg and the list of ABI registers expected for the
+    // signature of the function.
+    MoveResolver moveResolver_;
+
+    // Architecture specific implementation which specify how registers & stack
+    // offsets are used for calling a function.
+    ABIArgGenerator abiArgs_;
+
+#ifdef DEBUG
+    // Flag use to assert that we use ABI function in the right context.
+    bool inCall_;
+#endif
+
+    // If set by setupUnalignedABICall then callWithABI will pop the stack
+    // register which is on the stack.
+    bool dynamicAlignment_;
+
+#ifdef JS_SIMULATOR
+    // The signature is used to accumulate all types of arguments which are used
+    // by the caller. This is used by the simulators to decode the arguments
+    // properly, and cast the function pointer to the right type.
+    uint32_t signature_;
+#endif
+
     //}}} check_macroassembler_style
   public:
 
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source>
     void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss);
 
@@ -997,23 +1076,16 @@ class MacroAssembler : public MacroAssem
 
     // These functions exist as small wrappers around sites where execution can
     // leave the currently running stream of instructions. They exist so that
     // instrumentation may be put in place around them if necessary and the
     // instrumentation is enabled. For the functions that return a uint32_t,
     // they are returning the offset of the assembler just after the call has
     // been made so that a safepoint can be made at that location.
 
-    template <typename T>
-    void callWithABI(const T& fun, MoveOp::Type result = MoveOp::GENERAL) {
-        profilerPreCall();
-        MacroAssemblerSpecific::callWithABI(fun, result);
-        profilerPostReturn();
-    }
-
     // see above comment for what is returned
     uint32_t callJit(Register callee) {
         profilerPreCall();
         MacroAssemblerSpecific::callJit(callee);
         uint32_t ret = currentOffset();
         profilerPostReturn();
         return ret;
     }
--- a/js/src/jit/MoveResolver.cpp
+++ b/js/src/jit/MoveResolver.cpp
@@ -1,20 +1,49 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jit/MoveResolver.h"
+
+#include "jit/MacroAssembler.h"
 #include "jit/RegisterSets.h"
 
 using namespace js;
 using namespace js::jit;
 
+MoveOperand::MoveOperand(MacroAssembler& masm, const ABIArg& arg)
+{
+    switch (arg.kind()) {
+      case ABIArg::GPR:
+        kind_ = REG;
+        code_ = arg.gpr().code();
+        break;
+#ifdef JS_CODEGEN_REGISTER_PAIR
+      case ABIArg::GPR_PAIR:
+        kind_ = REG_PAIR;
+        code_ = arg.evenGpr().code();
+        MOZ_ASSERT(code_ % 2 == 0);
+        MOZ_ASSERT(code_ + 1 == arg.oddGpr().code());
+        break;
+#endif
+      case ABIArg::FPU:
+        kind_ = FLOAT_REG;
+        code_ = arg.fpu().code();
+        break;
+      case ABIArg::Stack:
+        kind_ = MEMORY;
+        code_ = masm.getStackPointer().code();
+        disp_ = arg.offsetFromArgBase();
+        break;
+    }
+}
+
 MoveResolver::MoveResolver()
   : numCycles_(0), curCycles_(0)
 {
 }
 
 void
 MoveResolver::resetState()
 {
--- a/js/src/jit/MoveResolver.h
+++ b/js/src/jit/MoveResolver.h
@@ -9,24 +9,32 @@
 
 #include "jit/InlineList.h"
 #include "jit/JitAllocPolicy.h"
 #include "jit/Registers.h"
 
 namespace js {
 namespace jit {
 
+class MacroAssembler;
+
 // This is similar to Operand, but carries more information. We're also not
 // guaranteed that Operand looks like this on all ISAs.
 class MoveOperand
 {
   public:
     enum Kind {
         // A register in the "integer", aka "general purpose", class.
         REG,
+#ifdef JS_CODEGEN_REGISTER_PAIR
+        // Two consecutive "integer" register (aka "general purpose"). The even
+        // register contains the lower part, the odd register has the high bits
+        // of the content.
+        REG_PAIR,
+#endif
         // A register in the "float" register class.
         FLOAT_REG,
         // A memory region.
         MEMORY,
         // The address of a memory region.
         EFFECTIVE_ADDRESS
     };
 
@@ -48,40 +56,56 @@ class MoveOperand
         disp_(disp)
     {
         MOZ_ASSERT(isMemoryOrEffectiveAddress());
 
         // With a zero offset, this is a plain reg-to-reg move.
         if (disp == 0 && kind_ == EFFECTIVE_ADDRESS)
             kind_ = REG;
     }
+    MoveOperand(MacroAssembler& masm, const ABIArg& arg);
     MoveOperand(const MoveOperand& other)
       : kind_(other.kind_),
         code_(other.code_),
         disp_(other.disp_)
     { }
     bool isFloatReg() const {
         return kind_ == FLOAT_REG;
     }
     bool isGeneralReg() const {
         return kind_ == REG;
     }
+    bool isGeneralRegPair() const {
+#ifdef JS_CODEGEN_REGISTER_PAIR
+        return kind_ == REG_PAIR;
+#else
+        return false;
+#endif
+    }
     bool isMemory() const {
         return kind_ == MEMORY;
     }
     bool isEffectiveAddress() const {
         return kind_ == EFFECTIVE_ADDRESS;
     }
     bool isMemoryOrEffectiveAddress() const {
         return isMemory() || isEffectiveAddress();
     }
     Register reg() const {
         MOZ_ASSERT(isGeneralReg());
         return Register::FromCode(code_);
     }
+    Register evenReg() const {
+        MOZ_ASSERT(isGeneralRegPair());
+        return Register::FromCode(code_);
+    }
+    Register oddReg() const {
+        MOZ_ASSERT(isGeneralRegPair());
+        return Register::FromCode(code_ + 1);
+    }
     FloatRegister floatReg() const {
         MOZ_ASSERT(isFloatReg());
         return FloatRegister::FromCode(code_);
     }
     Register base() const {
         MOZ_ASSERT(isMemoryOrEffectiveAddress());
         return Register::FromCode(code_);
     }
@@ -96,16 +120,40 @@ class MoveOperand
         // only appear in controlled circumstances in the trampoline code
         // which ensures these cases never come up.
 
         MOZ_ASSERT_IF(isMemoryOrEffectiveAddress() && other.isGeneralReg(),
                       base() != other.reg());
         MOZ_ASSERT_IF(other.isMemoryOrEffectiveAddress() && isGeneralReg(),
                       other.base() != reg());
 
+        // Check if one of the operand is a registe rpair, in which case, we
+        // have to check any other register, or register pair.
+        if (isGeneralRegPair() || other.isGeneralRegPair()) {
+            if (isGeneralRegPair() && other.isGeneralRegPair()) {
+                // Assume that register pairs are aligned on even registers.
+                MOZ_ASSERT(!evenReg().aliases(other.oddReg()));
+                MOZ_ASSERT(!oddReg().aliases(other.evenReg()));
+                // Pair of registers are composed of consecutive registers, thus
+                // if the first registers are aliased, then the second registers
+                // are aliased too.
+                MOZ_ASSERT(evenReg().aliases(other.evenReg()) == oddReg().aliases(other.oddReg()));
+                return evenReg().aliases(other.evenReg());
+            } else if (other.isGeneralReg()) {
+                MOZ_ASSERT(isGeneralRegPair());
+                return evenReg().aliases(other.reg()) ||
+                       oddReg().aliases(other.reg());
+            } else if (isGeneralReg()) {
+                MOZ_ASSERT(other.isGeneralRegPair());
+                return other.evenReg().aliases(reg()) ||
+                       other.oddReg().aliases(reg());
+            }
+            return false;
+        }
+
         if (kind_ != other.kind_)
             return false;
         if (kind_ == FLOAT_REG)
             return floatReg().aliases(other.floatReg());
         if (code_ != other.code_)
             return false;
         if (isMemoryOrEffectiveAddress())
             return disp_ == other.disp_;
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -1215,34 +1215,69 @@ class AnyRegisterIterator
             return AnyRegister(*geniter_);
         return AnyRegister(*floatiter_);
     }
 };
 
 class ABIArg
 {
   public:
-    enum Kind { GPR, FPU, Stack };
+    enum Kind {
+        GPR,
+#ifdef JS_CODEGEN_REGISTER_PAIR
+        GPR_PAIR,
+#endif
+        FPU,
+        Stack
+    };
 
   private:
     Kind kind_;
     union {
         Register::Code gpr_;
         FloatRegister::Code fpu_;
         uint32_t offset_;
     } u;
 
   public:
     ABIArg() : kind_(Kind(-1)) { u.offset_ = -1; }
     explicit ABIArg(Register gpr) : kind_(GPR) { u.gpr_ = gpr.code(); }
+    explicit ABIArg(Register gprLow, Register gprHigh)
+    {
+#if defined(JS_CODEGEN_REGISTER_PAIR)
+        kind_ = GPR_PAIR;
+#else
+        MOZ_CRASH("Unsupported type of ABI argument.");
+#endif
+        u.gpr_ = gprLow.code();
+        MOZ_ASSERT(u.gpr_ % 2 == 0);
+        MOZ_ASSERT(u.gpr_ + 1 == gprHigh.code());
+    }
     explicit ABIArg(FloatRegister fpu) : kind_(FPU) { u.fpu_ = fpu.code(); }
     explicit ABIArg(uint32_t offset) : kind_(Stack) { u.offset_ = offset; }
 
     Kind kind() const { return kind_; }
-    Register gpr() const { MOZ_ASSERT(kind() == GPR); return Register::FromCode(u.gpr_); }
+#ifdef JS_CODEGEN_REGISTER_PAIR
+    bool isGeneralRegPair() const { return kind_ == GPR_PAIR; }
+#else
+    bool isGeneralRegPair() const { return false; }
+#endif
+
+    Register gpr() const {
+        MOZ_ASSERT(kind() == GPR);
+        return Register::FromCode(u.gpr_);
+    }
+    Register evenGpr() const {
+        MOZ_ASSERT(isGeneralRegPair());
+        return Register::FromCode(u.gpr_);
+    }
+    Register oddGpr() const {
+        MOZ_ASSERT(isGeneralRegPair());
+        return Register::FromCode(u.gpr_ + 1);
+    }
     FloatRegister fpu() const { MOZ_ASSERT(kind() == FPU); return FloatRegister::FromCode(u.fpu_); }
     uint32_t offsetFromArgBase() const { MOZ_ASSERT(kind() == Stack); return u.offset_; }
 
     bool argInRegister() const { return kind() != Stack; }
     AnyRegister reg() const { return kind_ == GPR ? AnyRegister(gpr()) : AnyRegister(fpu()); }
 };
 
 // Get the set of registers which should be saved by a block of code which
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -13,16 +13,18 @@
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/SharedICHelpers.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 
+#include "jit/MacroAssembler-inl.h"
+
 namespace js {
 namespace jit {
 
 #ifdef DEBUG
 void
 FallbackICSpew(JSContext* cx, ICFallbackStub* stub, const char* fmt, ...)
 {
     if (JitSpewEnabled(JitSpew_BaselineICFallback)) {
@@ -802,17 +804,17 @@ ICStubCompiler::emitPostWriteBarrierSlot
     masm.branchValueIsNurseryObject(Assembler::NotEqual, val, scratch, &skipBarrier);
 
     // void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
     saveRegs.add(ICTailCallReg);
 #endif
     saveRegs.set() = GeneralRegisterSet::Intersect(saveRegs.set(), GeneralRegisterSet::Volatile());
     masm.PushRegsInMask(saveRegs);
-    masm.setupUnalignedABICall(2, scratch);
+    masm.setupUnalignedABICall(scratch);
     masm.movePtr(ImmPtr(cx->runtime()), scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(obj);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
     masm.PopRegsInMask(saveRegs);
 
     masm.bind(&skipBarrier);
     return true;
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -634,16 +634,20 @@ static inline bool UseHardFpABI()
 #if defined(JS_CODEGEN_ARM_HARDFP)
     return true;
 #else
     return false;
 #endif
 }
 #endif
 
+// In order to handle SoftFp ABI calls, we need to be able to express that we
+// have ABIArg which are represented by pair of general purpose registers.
+#define JS_CODEGEN_REGISTER_PAIR 1
+
 // See the comments above AsmJSMappedSize in AsmJSValidate.h for more info.
 // TODO: Implement this for ARM. Note that it requires Codegen to respect the
 // offset field of AsmJSHeapAccess.
 static const size_t AsmJSCheckedImmediateRange = 0;
 static const size_t AsmJSImmediateRange = 0;
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -19,27 +19,77 @@
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::CountLeadingZeroes32;
 
 void dbg_break() {}
 
-// Note this is used for inter-AsmJS calls and may pass arguments and results in
-// floating point registers even if the system ABI does not.
+// The ABIArgGenerator is used for making system ABI calls and for inter-AsmJS
+// calls. The system ABI can either be SoftFp or HardFp, and inter-AsmJS calls
+// are always HardFp calls. The initialization defaults to HardFp, and the ABI
+// choice is made before any system ABI calls with the method "setUseHardFp".
 ABIArgGenerator::ABIArgGenerator()
   : intRegIndex_(0),
     floatRegIndex_(0),
     stackOffset_(0),
-    current_()
+    current_(),
+    useHardFp_(true)
 { }
 
+// See the "Parameter Passing" section of the "Procedure Call Standard for the
+// ARM Architecture" documentation.
+ABIArg
+ABIArgGenerator::softNext(MIRType type)
+{
+    switch (type) {
+      case MIRType_Int32:
+      case MIRType_Pointer:
+        if (intRegIndex_ == NumIntArgRegs) {
+            current_ = ABIArg(stackOffset_);
+            stackOffset_ += sizeof(uint32_t);
+            break;
+        }
+        current_ = ABIArg(Register::FromCode(intRegIndex_));
+        intRegIndex_++;
+        break;
+      case MIRType_Float32:
+        if (intRegIndex_ == NumIntArgRegs) {
+            current_ = ABIArg(stackOffset_);
+            stackOffset_ += sizeof(uint32_t);
+            break;
+        }
+        current_ = ABIArg(Register::FromCode(intRegIndex_));
+        intRegIndex_++;
+        break;
+      case MIRType_Double:
+        // Make sure to use an even register index. Increase to next even number
+        // when odd.
+        intRegIndex_ = (intRegIndex_ + 1) & ~1;
+        if (intRegIndex_ == NumIntArgRegs) {
+            // Align the stack on 8 bytes.
+            static const int align = sizeof(double) - 1;
+            stackOffset_ = (stackOffset_ + align) & ~align;
+            current_ = ABIArg(stackOffset_);
+            stackOffset_ += sizeof(double);
+            break;
+        }
+        current_ = ABIArg(Register::FromCode(intRegIndex_), Register::FromCode(intRegIndex_ + 1));
+        intRegIndex_ += 2;
+        break;
+      default:
+        MOZ_CRASH("Unexpected argument type");
+    }
+
+    return current_;
+}
+
 ABIArg
-ABIArgGenerator::next(MIRType type)
+ABIArgGenerator::hardNext(MIRType type)
 {
     switch (type) {
       case MIRType_Int32:
       case MIRType_Pointer:
         if (intRegIndex_ == NumIntArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint32_t);
             break;
@@ -54,35 +104,45 @@ ABIArgGenerator::next(MIRType type)
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
         current_ = ABIArg(VFPRegister(floatRegIndex_, VFPRegister::Single));
         floatRegIndex_++;
         break;
       case MIRType_Double:
-        // Bump the number of used registers up to the next multiple of two.
+        // Double register are composed of 2 float registers, thus we have to
+        // skip any float register which cannot be used in a pair of float
+        // registers in which a double value can be stored.
         floatRegIndex_ = (floatRegIndex_ + 1) & ~1;
         if (floatRegIndex_ == NumFloatArgRegs) {
             static const int align = sizeof(double) - 1;
             stackOffset_ = (stackOffset_ + align) & ~align;
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
         current_ = ABIArg(VFPRegister(floatRegIndex_ >> 1, VFPRegister::Double));
-        floatRegIndex_+=2;
+        floatRegIndex_ += 2;
         break;
       default:
         MOZ_CRASH("Unexpected argument type");
     }
 
     return current_;
 }
 
+ABIArg
+ABIArgGenerator::next(MIRType type)
+{
+    if (useHardFp_)
+        return hardNext(type);
+    return softNext(type);
+}
+
 const Register ABIArgGenerator::NonArgReturnReg0 = r4;
 const Register ABIArgGenerator::NonArgReturnReg1 = r5;
 const Register ABIArgGenerator::NonReturn_VolatileReg0 = r2;
 const Register ABIArgGenerator::NonReturn_VolatileReg1 = r3;
 
 // Encode a standard register when it is being used as src1, the dest, and an
 // extra register. These should never be called with an InvalidReg.
 uint32_t
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -67,19 +67,33 @@ static const uint32_t NumCallTempNonArgR
 
 class ABIArgGenerator
 {
     unsigned intRegIndex_;
     unsigned floatRegIndex_;
     uint32_t stackOffset_;
     ABIArg current_;
 
+    // ARM can either use HardFp (use float registers for float arguments), or
+    // SoftFp (use general registers for float arguments) ABI.  We keep this
+    // switch as a runtime switch because AsmJS always use the HardFp back-end
+    // while the calls to native functions have to use the one provided by the
+    // system.
+    bool useHardFp_;
+
+    ABIArg softNext(MIRType argType);
+    ABIArg hardNext(MIRType argType);
+
   public:
     ABIArgGenerator();
 
+    void setUseHardFp(bool useHardFp) {
+        MOZ_ASSERT(intRegIndex_ == 0 && floatRegIndex_ == 0);
+        useHardFp_ = useHardFp;
+    }
     ABIArg next(MIRType argType);
     ABIArg& current() { return current_; }
     uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
 
     static const Register NonArgReturnReg0;
     static const Register NonArgReturnReg1;
     static const Register NonReturn_VolatileReg0;
     static const Register NonReturn_VolatileReg1;
--- a/js/src/jit/arm/BaselineIC-arm.cpp
+++ b/js/src/jit/arm/BaselineIC-arm.cpp
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/BaselineCompiler.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Linker.h"
 #include "jit/SharedICHelpers.h"
 
+#include "jit/MacroAssembler-inl.h"
+
 using namespace js;
 using namespace js::jit;
 
 namespace js {
 namespace jit {
 
 // ICCompare_Int32
 
@@ -132,17 +134,17 @@ ICBinaryArith_Int32::Compiler::generateS
         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);
 
-        masm.setupAlignedABICall(2);
+        masm.setupAlignedABICall();
         masm.passABIArg(R0.payloadReg());
         masm.passABIArg(R1.payloadReg());
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
 
         // idivmod returns the quotient in r0, and the remainder in r1.
         if (op_ == JSOP_DIV) {
             // Result is a double if the remainder != 0.
             masm.branch32(Assembler::NotEqual, r1, Imm32(0), &revertRegister);
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -568,17 +568,17 @@ CodeGeneratorARM::visitSoftDivI(LSoftDiv
     Register lhs = ToRegister(ins->lhs());
     Register rhs = ToRegister(ins->rhs());
     Register output = ToRegister(ins->output());
     MDiv* mir = ins->mir();
 
     Label done;
     divICommon(mir, lhs, rhs, output, ins->snapshot(), done);
 
-    masm.setupAlignedABICall(2);
+    masm.setupAlignedABICall();
     masm.passABIArg(lhs);
     masm.passABIArg(rhs);
     if (gen->compilingAsmJS())
         masm.callWithABI(AsmJSImm_aeabi_idivmod);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
 
     // idivmod returns the quotient in r0, and the remainder in r1.
@@ -729,17 +729,17 @@ CodeGeneratorARM::visitSoftModI(LSoftMod
         } else {
             MOZ_ASSERT(mir->fallible());
             bailoutIf(Assembler::Equal, ins->snapshot());
         }
     }
 
     modICommon(mir, lhs, rhs, output, ins->snapshot(), done);
 
-    masm.setupAlignedABICall(2);
+    masm.setupAlignedABICall();
     masm.passABIArg(lhs);
     masm.passABIArg(rhs);
     if (gen->compilingAsmJS())
         masm.callWithABI(AsmJSImm_aeabi_idivmod);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
 
     // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0
@@ -1911,17 +1911,17 @@ CodeGeneratorARM::visitAsmJSCompareExcha
     const MAsmJSCompareExchangeHeap* mir = ins->mir();
     Scalar::Type viewType = mir->accessType();
     Register ptr = ToRegister(ins->ptr());
     Register oldval = ToRegister(ins->oldval());
     Register newval = ToRegister(ins->newval());
 
     MOZ_ASSERT(ToRegister(ins->output()) == ReturnReg);
 
-    masm.setupAlignedABICall(4);
+    masm.setupAlignedABICall();
     masm.ma_mov(Imm32(viewType), ScratchRegister);
     masm.passABIArg(ScratchRegister);
     masm.passABIArg(ptr);
     masm.passABIArg(oldval);
     masm.passABIArg(newval);
 
     masm.callWithABI(AsmJSImm_AtomicCmpXchg);
 }
@@ -1964,17 +1964,17 @@ CodeGeneratorARM::visitAsmJSAtomicExchan
 {
     const MAsmJSAtomicExchangeHeap* mir = ins->mir();
     Scalar::Type viewType = mir->accessType();
     Register ptr = ToRegister(ins->ptr());
     Register value = ToRegister(ins->value());
 
     MOZ_ASSERT(ToRegister(ins->output()) == ReturnReg);
 
-    masm.setupAlignedABICall(3);
+    masm.setupAlignedABICall();
     masm.ma_mov(Imm32(viewType), ScratchRegister);
     masm.passABIArg(ScratchRegister);
     masm.passABIArg(ptr);
     masm.passABIArg(value);
 
     masm.callWithABI(AsmJSImm_AtomicXchg);
 }
 
@@ -2061,17 +2061,17 @@ CodeGeneratorARM::visitAsmJSAtomicBinopH
 void
 CodeGeneratorARM::visitAsmJSAtomicBinopCallout(LAsmJSAtomicBinopCallout* ins)
 {
     const MAsmJSAtomicBinopHeap* mir = ins->mir();
     Scalar::Type viewType = mir->accessType();
     Register ptr = ToRegister(ins->ptr());
     Register value = ToRegister(ins->value());
 
-    masm.setupAlignedABICall(3);
+    masm.setupAlignedABICall();
     masm.ma_mov(Imm32(viewType), ScratchRegister);
     masm.passABIArg(ScratchRegister);
     masm.passABIArg(ptr);
     masm.passABIArg(value);
 
     switch (mir->operation()) {
       case AtomicFetchAddOp:
         masm.callWithABI(AsmJSImm_AtomicFetchAdd);
@@ -2188,17 +2188,17 @@ CodeGeneratorARM::visitSoftUDivOrMod(LSo
 
     Label done;
     MDiv* div = ins->mir()->isDiv() ? ins->mir()->toDiv() : nullptr;
     MMod* mod = !div ? ins->mir()->toMod() : nullptr;
 
     generateUDivModZeroCheck(rhs, output, &done, ins->snapshot(), div);
     generateUDivModZeroCheck(rhs, output, &done, ins->snapshot(), mod);
 
-    masm.setupAlignedABICall(2);
+    masm.setupAlignedABICall();
     masm.passABIArg(lhs);
     masm.passABIArg(rhs);
     if (gen->compilingAsmJS())
         masm.callWithABI(AsmJSImm_aeabi_uidivmod);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_uidivmod));
 
     // uidivmod returns the quotient in r0, and the remainder in r1.
@@ -2331,14 +2331,14 @@ CodeGeneratorARM::visitMemoryBarrier(LMe
 void
 CodeGeneratorARM::visitRandom(LRandom* ins)
 {
     Register temp = ToRegister(ins->temp());
     Register temp2 = ToRegister(ins->temp2());
 
     masm.loadJSContext(temp);
 
-    masm.setupUnalignedABICall(1, temp2);
+    masm.setupUnalignedABICall(temp2);
     masm.passABIArg(temp);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
 
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 }
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1698,16 +1698,22 @@ MacroAssemblerARM::ma_vxfer(FloatRegiste
 
 void
 MacroAssemblerARM::ma_vxfer(FloatRegister src, Register dest1, Register dest2, Condition cc)
 {
     as_vxfer(dest1, dest2, VFPRegister(src), FloatToCore, cc);
 }
 
 void
+MacroAssemblerARM::ma_vxfer(Register src, FloatRegister dest, Condition cc)
+{
+    as_vxfer(src, InvalidReg, VFPRegister(dest).singleOverlay(), CoreToFloat, cc);
+}
+
+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)
 {
@@ -3740,434 +3746,34 @@ MacroAssemblerARMCompat::ensureDouble(co
 
 void
 MacroAssemblerARMCompat::breakpoint(Condition cc)
 {
     ma_ldr(DTRAddr(r12, DtrRegImmShift(r12, LSL, 0, IsDown)), r12, Offset, cc);
 }
 
 void
-MacroAssemblerARMCompat::setupABICall(uint32_t args)
-{
-    MOZ_ASSERT(!inCall_);
-    inCall_ = true;
-    args_ = args;
-    passedArgs_ = 0;
-    passedArgTypes_ = 0;
-    usedIntSlots_ = 0;
-#if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_SIMULATOR_ARM)
-    usedFloatSlots_ = 0;
-    usedFloat32_ = false;
-    padding_ = 0;
-#endif
-    floatArgsInGPR[0] = MoveOperand();
-    floatArgsInGPR[1] = MoveOperand();
-    floatArgsInGPR[2] = MoveOperand();
-    floatArgsInGPR[3] = MoveOperand();
-    floatArgsInGPRValid[0] = false;
-    floatArgsInGPRValid[1] = false;
-    floatArgsInGPRValid[2] = false;
-    floatArgsInGPRValid[3] = false;
-}
-
-void
-MacroAssemblerARMCompat::setupAlignedABICall(uint32_t args)
-{
-    setupABICall(args);
-
-    dynamicAlignment_ = false;
-}
-
-void
-MacroAssemblerARMCompat::setupUnalignedABICall(uint32_t args, Register scratch)
-{
-    setupABICall(args);
-    dynamicAlignment_ = true;
-
-    ma_mov(sp, scratch);
-
-    // Force sp to be aligned.
-    ma_and(Imm32(~(ABIStackAlignment - 1)), sp, sp);
-    ma_push(scratch);
-}
-
-#if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_SIMULATOR_ARM)
-void
-MacroAssemblerARMCompat::passHardFpABIArg(const MoveOperand& from, MoveOp::Type type)
-{
-    MoveOperand to;
-    ++passedArgs_;
-    if (!enoughMemory_)
-        return;
-    switch (type) {
-      case MoveOp::FLOAT32: {
-        FloatRegister fr;
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Float32;
-        if (GetFloat32ArgReg(usedIntSlots_, usedFloatSlots_, &fr)) {
-            if (from.isFloatReg() && from.floatReg() == fr) {
-                // Nothing to do; the value is in the right register already.
-                usedFloatSlots_++;
-                passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Float32;
-                return;
-            }
-            to = MoveOperand(fr);
-        } else {
-            // If (and only if) the integer registers have started spilling, do
-            // we need to take the register's alignment into account.
-            uint32_t disp = GetFloat32ArgStackDisp(usedIntSlots_, usedFloatSlots_, &padding_);
-            to = MoveOperand(sp, disp);
-        }
-        usedFloatSlots_++;
-        break;
-      }
-
-      case MoveOp::DOUBLE: {
-          FloatRegister fr;
-          passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Double;
-          usedFloatSlots_ = (usedFloatSlots_ + 1) & -2;
-          if (GetDoubleArgReg(usedIntSlots_, usedFloatSlots_, &fr)) {
-              if (from.isFloatReg() && from.floatReg() == fr) {
-                  // Nothing to do; the value is in the right register already.
-                  usedFloatSlots_ += 2;
-                  return;
-              }
-              to = MoveOperand(fr);
-          } else {
-              // If (and only if) the integer registers have started spilling, do we
-              // need to take the register's alignment into account
-              uint32_t disp = GetDoubleArgStackDisp(usedIntSlots_, usedFloatSlots_, &padding_);
-              to = MoveOperand(sp, disp);
-          }
-          usedFloatSlots_+=2;
-          break;
-      }
-      case MoveOp::GENERAL: {
-        Register r;
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_General;
-        if (GetIntArgReg(usedIntSlots_, usedFloatSlots_, &r)) {
-            if (from.isGeneralReg() && from.reg() == r) {
-                // Nothing to do; the value is in the right register already.
-                usedIntSlots_++;
-                return;
-            }
-            to = MoveOperand(r);
-        } else {
-            uint32_t disp = GetIntArgStackDisp(usedIntSlots_, usedFloatSlots_, &padding_);
-            to = MoveOperand(sp, disp);
-        }
-        usedIntSlots_++;
-        break;
-      }
-      default:
-        MOZ_CRASH("Unexpected argument type");
-    }
-
-    enoughMemory_ = moveResolver_.addMove(from, to, type);
-}
-#endif
-
-#if !defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_SIMULATOR_ARM)
-void
-MacroAssemblerARMCompat::passSoftFpABIArg(const MoveOperand& from, MoveOp::Type type)
-{
-    MoveOperand to;
-    uint32_t increment = 1;
-    bool useResolver = true;
-    ++passedArgs_;
-    switch (type) {
-      case MoveOp::DOUBLE:
-        // Double arguments need to be rounded up to the nearest doubleword
-        // boundary, even if it is in a register!
-        usedIntSlots_ = (usedIntSlots_ + 1) & ~1;
-        increment = 2;
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Double;
-        break;
-      case MoveOp::FLOAT32:
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Float32;
-        break;
-      case MoveOp::GENERAL:
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_General;
-        break;
-      default:
-        MOZ_CRASH("Unexpected argument type");
-    }
-
-    Register destReg;
-    MoveOperand dest;
-    if (GetIntArgReg(usedIntSlots_, 0, &destReg)) {
-        if (type == MoveOp::DOUBLE || type == MoveOp::FLOAT32) {
-            floatArgsInGPR[destReg.code()] = from;
-            floatArgsInGPRValid[destReg.code()] = true;
-            useResolver = false;
-        } else if (from.isGeneralReg() && from.reg() == destReg) {
-            // No need to move anything.
-            useResolver = false;
-        } else {
-            dest = MoveOperand(destReg);
-        }
-    } else {
-        uint32_t disp = GetArgStackDisp(usedIntSlots_);
-        dest = MoveOperand(sp, disp);
-    }
-
-    if (useResolver)
-        enoughMemory_ = enoughMemory_ && moveResolver_.addMove(from, dest, type);
-    usedIntSlots_ += increment;
-}
-#endif
-
-void
-MacroAssemblerARMCompat::passABIArg(const MoveOperand& from, MoveOp::Type type)
-{
-#if defined(JS_SIMULATOR_ARM)
-    if (UseHardFpABI())
-        MacroAssemblerARMCompat::passHardFpABIArg(from, type);
-    else
-        MacroAssemblerARMCompat::passSoftFpABIArg(from, type);
-#elif defined(JS_CODEGEN_ARM_HARDFP)
-    MacroAssemblerARMCompat::passHardFpABIArg(from, type);
-#else
-    MacroAssemblerARMCompat::passSoftFpABIArg(from, type);
-#endif
-}
-
-void
-MacroAssemblerARMCompat::passABIArg(Register reg)
-{
-    passABIArg(MoveOperand(reg), MoveOp::GENERAL);
-}
-
-void
-MacroAssemblerARMCompat::passABIArg(FloatRegister freg, MoveOp::Type type)
-{
-    passABIArg(MoveOperand(freg), type);
-}
-
-void MacroAssemblerARMCompat::checkStackAlignment()
-{
-#ifdef DEBUG
-    ma_tst(sp, Imm32(ABIStackAlignment - 1));
-    breakpoint(NonZero);
-#endif
-}
-
-void
-MacroAssemblerARMCompat::callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS)
-{
-    MOZ_ASSERT(inCall_);
-
-    *stackAdjust = ((usedIntSlots_ > NumIntArgRegs) ? usedIntSlots_ - NumIntArgRegs : 0) * sizeof(intptr_t);
-#if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_SIMULATOR_ARM)
-    if (UseHardFpABI())
-        *stackAdjust += 2*((usedFloatSlots_ > NumFloatArgRegs) ? usedFloatSlots_ - NumFloatArgRegs : 0) * sizeof(intptr_t);
-#endif
-    uint32_t alignmentAtPrologue = callFromAsmJS ? sizeof(AsmJSFrame) : 0;
-
-    if (!dynamicAlignment_) {
-        *stackAdjust += ComputeByteAlignment(asMasm().framePushed() + *stackAdjust + alignmentAtPrologue,
-                                             ABIStackAlignment);
-    } else {
-        // sizeof(intptr_t) accounts for the saved stack pointer pushed by
-        // setupUnalignedABICall.
-        *stackAdjust += ComputeByteAlignment(*stackAdjust + sizeof(intptr_t), ABIStackAlignment);
-    }
-
-    asMasm().reserveStack(*stackAdjust);
-
-    // Position all arguments.
-    {
-        enoughMemory_ = enoughMemory_ && moveResolver_.resolve();
-        if (!enoughMemory_)
-            return;
-
-        MoveEmitter emitter(asMasm());
-        emitter.emit(moveResolver_);
-        emitter.finish();
-    }
-    for (int i = 0; i < 4; i++) {
-        if (floatArgsInGPRValid[i]) {
-            MoveOperand from = floatArgsInGPR[i];
-            Register to0 = Register::FromCode(i);
-            Register to1;
-
-            if (!from.isFloatReg() || from.floatReg().isDouble()) {
-                // Doubles need to be moved into a pair of aligned registers
-                // whether they come from the stack, or VFP registers.
-                to1 = Register::FromCode(i + 1);
-                MOZ_ASSERT(i % 2 == 0);
-            }
-
-            if (from.isFloatReg()) {
-                if (from.floatReg().isDouble())
-                    ma_vxfer(from.floatReg(), to0, to1);
-                else
-                    ma_vxfer(from.floatReg(), to0);
-            } else {
-                MOZ_ASSERT(from.isMemory());
-                // 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.
-                ma_ldrd(EDtrAddr(from.base(), EDtrOffImm(from.disp())), to0, to1);
-            }
-        }
-    }
-    checkStackAlignment();
-
-    // Save the lr register if we need to preserve it.
-    if (secondScratchReg_ != lr)
-        ma_mov(lr, secondScratchReg_);
-}
-
-void
-MacroAssemblerARMCompat::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
-{
-    if (secondScratchReg_ != lr)
-        ma_mov(secondScratchReg_, lr);
-
-    switch (result) {
-      case MoveOp::DOUBLE:
-        if (!UseHardFpABI()) {
-            // Move double from r0/r1 to ReturnFloatReg.
-            as_vxfer(r0, r1, ReturnDoubleReg, CoreToFloat);
-            break;
-        }
-      case MoveOp::FLOAT32:
-        if (!UseHardFpABI()) {
-            // Move float32 from r0 to ReturnFloatReg.
-            as_vxfer(r0, InvalidReg, ReturnFloat32Reg.singleOverlay(), CoreToFloat);
-            break;
-        }
-      case MoveOp::GENERAL:
-        break;
-
-      default:
-        MOZ_CRASH("unexpected callWithABI result");
-    }
-
-    asMasm().freeStack(stackAdjust);
-
-    if (dynamicAlignment_) {
-        // While the x86 supports pop esp, on ARM that isn't well defined, so
-        // just do it manually.
-        as_dtr(IsLoad, 32, Offset, sp, DTRAddr(sp, DtrOffImm(0)));
-    }
-
-    MOZ_ASSERT(inCall_);
-    inCall_ = false;
-}
-
-#if defined(DEBUG) && defined(JS_SIMULATOR_ARM)
-static void
-AssertValidABIFunctionType(uint32_t passedArgTypes)
-{
-    switch (passedArgTypes) {
-      case Args_General0:
-      case Args_General1:
-      case Args_General2:
-      case Args_General3:
-      case Args_General4:
-      case Args_General5:
-      case Args_General6:
-      case Args_General7:
-      case Args_General8:
-      case Args_Double_None:
-      case Args_Int_Double:
-      case Args_Float32_Float32:
-      case Args_Double_Double:
-      case Args_Double_Int:
-      case Args_Double_DoubleInt:
-      case Args_Double_DoubleDouble:
-      case Args_Double_IntDouble:
-      case Args_Int_IntDouble:
-      case Args_Double_DoubleDoubleDouble:
-      case Args_Double_DoubleDoubleDoubleDouble:
-        break;
-      default:
-        MOZ_CRASH("Unexpected type");
-    }
-}
-#endif
-
-void
-MacroAssemblerARMCompat::callWithABI(void* fun, MoveOp::Type result)
-{
-#ifdef JS_SIMULATOR_ARM
-    MOZ_ASSERT(passedArgs_ <= 15);
-    passedArgTypes_ <<= ArgType_Shift;
-    switch (result) {
-      case MoveOp::GENERAL: passedArgTypes_ |= ArgType_General; break;
-      case MoveOp::DOUBLE:  passedArgTypes_ |= ArgType_Double;  break;
-      case MoveOp::FLOAT32: passedArgTypes_ |= ArgType_Float32; break;
-      default: MOZ_CRASH("Invalid return type");
-    }
-#ifdef DEBUG
-    AssertValidABIFunctionType(passedArgTypes_);
-#endif
-    ABIFunctionType type = ABIFunctionType(passedArgTypes_);
-    fun = Simulator::RedirectNativeFunction(fun, type);
-#endif
-
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    ma_call(ImmPtr(fun));
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerARMCompat::callWithABI(AsmJSImmPtr imm, MoveOp::Type result)
-{
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust, /* callFromAsmJS = */ true);
-    asMasm().call(imm);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerARMCompat::callWithABI(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);
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(r12);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerARMCompat::callWithABI(Register fun, MoveOp::Type result)
-{
-    // Load the callee in r12, as above.
-    ma_mov(fun, r12);
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(r12);
-    callWithABIPost(stackAdjust, result);
+MacroAssemblerARMCompat::checkStackAlignment()
+{
+    asMasm().assertStackAlignment(ABIStackAlignment);
 }
 
 void
 MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler)
 {
     // Reserve space for exception information.
     int size = (sizeof(ResumeFromException) + 7) & ~7;
 
     ma_sub(Imm32(size), sp);
     ma_mov(sp, r0);
 
     // Call the handler.
-    setupUnalignedABICall(1, r1);
-    passABIArg(r0);
-    callWithABI(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);
@@ -5369,9 +4975,127 @@ MacroAssembler::call(JitCode* c)
         rs = L_MOVWT;
     else
         rs = L_LDR;
 
     ma_movPatchable(ImmPtr(c->raw()), ScratchRegister, Always, rs);
     ma_callJitHalfPush(ScratchRegister);
 }
 
+
+// ===============================================================
+// ABI function calls.
+
+void
+MacroAssembler::setupUnalignedABICall(Register scratch)
+{
+    setupABICall();
+    dynamicAlignment_ = true;
+
+    ma_mov(sp, scratch);
+    // Force sp to be aligned.
+    ma_and(Imm32(~(ABIStackAlignment - 1)), sp, sp);
+    ma_push(scratch);
+}
+
+void
+MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS)
+{
+    MOZ_ASSERT(inCall_);
+    uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
+
+    if (dynamicAlignment_) {
+        // sizeof(intptr_t) accounts for the saved stack pointer pushed by
+        // setupUnalignedABICall.
+        stackForCall += ComputeByteAlignment(stackForCall + sizeof(intptr_t),
+                                             ABIStackAlignment);
+    } else {
+        uint32_t alignmentAtPrologue = callFromAsmJS ? sizeof(AsmJSFrame) : 0;
+        stackForCall += ComputeByteAlignment(stackForCall + framePushed() + alignmentAtPrologue,
+                                             ABIStackAlignment);
+    }
+
+    *stackAdjust = stackForCall;
+    reserveStack(stackForCall);
+
+    // Position all arguments.
+    {
+        enoughMemory_ = enoughMemory_ && moveResolver_.resolve();
+        if (!enoughMemory_)
+            return;
+
+        MoveEmitter emitter(*this);
+        emitter.emit(moveResolver_);
+        emitter.finish();
+    }
+
+    assertStackAlignment(ABIStackAlignment);
+
+    // Save the lr register if we need to preserve it.
+    if (secondScratchReg_ != lr)
+        ma_mov(lr, secondScratchReg_);
+}
+
+void
+MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
+{
+    if (secondScratchReg_ != lr)
+        ma_mov(secondScratchReg_, lr);
+
+    switch (result) {
+      case MoveOp::DOUBLE:
+        if (!UseHardFpABI()) {
+            // Move double from r0/r1 to ReturnFloatReg.
+            ma_vxfer(r0, r1, ReturnDoubleReg);
+            break;
+        }
+      case MoveOp::FLOAT32:
+        if (!UseHardFpABI()) {
+            // Move float32 from r0 to ReturnFloatReg.
+            ma_vxfer(r0, ReturnFloat32Reg.singleOverlay());
+            break;
+        }
+      case MoveOp::GENERAL:
+        break;
+
+      default:
+        MOZ_CRASH("unexpected callWithABI result");
+    }
+
+    freeStack(stackAdjust);
+
+    if (dynamicAlignment_) {
+        // While the x86 supports pop esp, on ARM that isn't well defined, so
+        // just do it manually.
+        as_dtr(IsLoad, 32, Offset, sp, DTRAddr(sp, DtrOffImm(0)));
+    }
+
+#ifdef DEBUG
+    MOZ_ASSERT(inCall_);
+    inCall_ = false;
+#endif
+}
+
+void
+MacroAssembler::callWithABINoProfiler(Register fun, MoveOp::Type result)
+{
+    // Load the callee in r12, as above.
+    ma_mov(fun, r12);
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(r12);
+    callWithABIPost(stackAdjust, result);
+}
+
+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);
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(r12);
+    callWithABIPost(stackAdjust, result);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -396,19 +396,24 @@ class MacroAssemblerARM : public Assembl
     void ma_vcvt_F32_I32(FloatRegister src, FloatRegister dest, Condition cc = Always);
     void ma_vcvt_F32_U32(FloatRegister src, FloatRegister dest, Condition cc = Always);
 
     // Source is I32, dest is F32:
     void ma_vcvt_I32_F32(FloatRegister src, FloatRegister dest, Condition cc = Always);
     void ma_vcvt_U32_F32(FloatRegister src, FloatRegister dest, Condition cc = Always);
 
 
+    // Transfer (do not coerce) a float into a gpr.
     void ma_vxfer(VFPRegister src, Register dest, Condition cc = Always);
+    // 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_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, int32_t shift = defaultShift, Condition cc = Always);
@@ -488,58 +493,18 @@ class MacroAssembler;
 
 class MacroAssemblerARMCompat : public MacroAssemblerARM
 {
   private:
     // Perform a downcast. Should be removed by Bug 996602.
     MacroAssembler& asMasm();
     const MacroAssembler& asMasm() const;
 
-  private:
-    bool inCall_;
-    // Number of bytes the stack is adjusted inside a call to C. Calls to C may
-    // not be nested.
-    uint32_t args_;
-    // The actual number of arguments that were passed, used to assert that the
-    // initial number of arguments declared was correct.
-    uint32_t passedArgs_;
-    uint32_t passedArgTypes_;
-
-    // ARM treats arguments as a vector in registers/memory, that looks like:
-    // { r0, r1, r2, r3, [sp], [sp,+4], [sp,+8] ... }
-    // usedIntSlots_ keeps track of how many of these have been used. It bears a
-    // passing resemblance to passedArgs_, but a single argument can effectively
-    // use between one and three slots depending on its size and alignment
-    // requirements.
-    uint32_t usedIntSlots_;
-#if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_SIMULATOR_ARM)
-    uint32_t usedFloatSlots_;
-    bool usedFloat32_;
-    uint32_t padding_;
-#endif
-    bool dynamicAlignment_;
-
-    // Used to work around the move resolver's lack of support for moving into
-    // register pairs, which the softfp ABI needs.
-    mozilla::Array<MoveOperand, 4> floatArgsInGPR;
-    mozilla::Array<bool, 4> floatArgsInGPRValid;
-
-    // Compute space needed for the function call and set the properties of the
-    // callee. It returns the space which has to be allocated for calling the
-    // function.
-    //
-    // arg            Number of arguments of the function.
-    void setupABICall(uint32_t arg);
-
-  protected:
-    MoveResolver moveResolver_;
-
   public:
     MacroAssemblerARMCompat()
-      : inCall_(false)
     { }
 
   public:
 
     // Jumps + other functions that should be called from non-arm specific
     // code. Basically, an x86 front end on top of the ARM code.
     void j(Condition code , Label* dest)
     {
@@ -1747,57 +1712,20 @@ class MacroAssemblerARMCompat : public M
         emitSet(cond, dest);
     }
 
     void testUndefinedSet(Condition cond, const ValueOperand& value, Register dest) {
         cond = testUndefined(cond, value);
         emitSet(cond, dest);
     }
 
-    // Setup a call to C/C++ code, given the number of general arguments it
-    // takes. Note that this only supports cdecl.
-    //
-    // In order for alignment to work correctly, the MacroAssembler must have a
-    // consistent view of the stack displacement. It is okay to call "push"
-    // manually, however, if the stack alignment were to change, the macro
-    // assembler should be notified before starting a call.
-    void setupAlignedABICall(uint32_t args);
-
-    // Sets up an ABI call for when the alignment is not known. This may need a
-    // scratch register.
-    void setupUnalignedABICall(uint32_t args, Register scratch);
-
-    // Arguments must be assigned in a left-to-right order. This process may
-    // temporarily use more stack, in which case esp-relative addresses will be
-    // automatically adjusted. It is extremely important that esp-relative
-    // addresses are computed *after* setupABICall(). Furthermore, no operations
-    // should be emitted while setting arguments.
-    void passABIArg(const MoveOperand& from, MoveOp::Type type);
-    void passABIArg(Register reg);
-    void passABIArg(FloatRegister reg, MoveOp::Type type);
-    void passABIArg(const ValueOperand& regs);
-
-  private:
-    void passHardFpABIArg(const MoveOperand& from, MoveOp::Type type);
-    void passSoftFpABIArg(const MoveOperand& from, MoveOp::Type type);
-
   protected:
     bool buildOOLFakeExitFrame(void* fakeReturnAddr);
 
-  private:
-    void callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS = false);
-    void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result);
-
   public:
-    // Emits a call to a C/C++ function, resolving all argument moves.
-    void callWithABI(void* fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(AsmJSImmPtr imm, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(const Address& fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
-
     CodeOffsetLabel labelForPatch() {
         return CodeOffsetLabel(nextOffset().getOffset());
     }
 
     void computeEffectiveAddress(const Address& address, Register dest) {
         ma_add(address.base, Imm32(address.offset), dest, LeaveCC);
     }
     void computeEffectiveAddress(const BaseIndex& address, Register dest) {
--- a/js/src/jit/arm/MoveEmitter-arm.cpp
+++ b/js/src/jit/arm/MoveEmitter-arm.cpp
@@ -108,28 +108,37 @@ MoveEmitterARM::breakCycle(const MoveOpe
       case MoveOp::FLOAT32:
         if (to.isMemory()) {
             VFPRegister temp = ScratchFloat32Reg;
             masm.ma_vldr(toAddress(to), temp);
             // 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));
+        } 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));
         } 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));
         }
         break;
       case MoveOp::DOUBLE:
         if (to.isMemory()) {
             FloatRegister temp = ScratchDoubleReg;
             masm.ma_vldr(toAddress(to), temp);
             masm.ma_vstr(temp, cycleSlot(slotId, 0));
+        } else if (to.isGeneralRegPair()) {
+            FloatRegister reg = ScratchDoubleReg;
+            masm.ma_vxfer(to.evenReg(), to.oddReg(), reg);
+            masm.ma_vstr(reg, cycleSlot(slotId, 0));
         } else {
             masm.ma_vstr(to.floatReg().doubleOverlay(), cycleSlot(slotId, 0));
         }
         break;
       case MoveOp::INT32:
       case MoveOp::GENERAL:
         // an non-vfp value
         if (to.isMemory()) {
@@ -161,16 +170,24 @@ MoveEmitterARM::completeCycle(const Move
     // saved value of B, to A.
     switch (type) {
       case MoveOp::FLOAT32:
       case MoveOp::DOUBLE:
         if (to.isMemory()) {
             FloatRegister temp = ScratchDoubleReg;
             masm.ma_vldr(cycleSlot(slotId, 0), temp);
             masm.ma_vstr(temp, toAddress(to));
+        } else if (to.isGeneralReg()) {
+            MOZ_ASSERT(type == MoveOp::FLOAT32);
+            masm.ma_ldr(toAddress(from), to.reg());
+        } else if (to.isGeneralRegPair()) {
+            MOZ_ASSERT(type == MoveOp::DOUBLE);
+            FloatRegister reg = ScratchDoubleReg;
+            masm.ma_vldr(toAddress(from), reg);
+            masm.ma_vxfer(reg, 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());
         }
         break;
       case MoveOp::INT32:
@@ -191,16 +208,20 @@ MoveEmitterARM::completeCycle(const Move
       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());
+
     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_) {
@@ -231,42 +252,88 @@ MoveEmitterARM::emitMove(const MoveOpera
         MOZ_ASSERT(to.base() != reg);
         masm.ma_str(reg, toAddress(to));
     }
 }
 
 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());
+
     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));
+    } else if (from.isGeneralReg()) {
+        if (to.isFloatReg())
+            masm.ma_vxfer(from.reg(), to.floatReg());
+        else if (to.isGeneralReg())
+            masm.ma_mov(from.reg(), to.reg());
+        else
+            masm.ma_str(from.reg(), toAddress(to));
     } else if (to.isFloatReg()) {
         masm.ma_vldr(toAddress(from), VFPRegister(to.floatReg()).singleOverlay());
+    } else if (to.isGeneralReg()) {
+        masm.ma_ldr(toAddress(from), to.reg());
     } 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));
     }
 }
 
 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());
+
     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));
+    } 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));
+        }
     } else if (to.isFloatReg()) {
         masm.ma_vldr(toAddress(from), to.floatReg());
+    } 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());
         FloatRegister reg = ScratchDoubleReg;
         masm.ma_vldr(toAddress(from), reg);
         masm.ma_vstr(reg, toAddress(to));
     }
 }
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -287,17 +287,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.push(scratch);
         masm.push(Imm32(0)); // Fake return address.
         // No GC things to mark on the stack, push a bare token.
         masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
 
         masm.push(framePtr); // BaselineFrame
         masm.push(r0); // jitcode
 
-        masm.setupUnalignedABICall(3, scratch);
+        masm.setupUnalignedABICall(scratch);
         masm.passABIArg(r11); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
 
         Register jitcode = regs.takeAny();
         masm.pop(jitcode);
         masm.pop(framePtr);
@@ -418,17 +418,17 @@ JitRuntime::generateInvalidator(JSContex
 
     masm.ma_mov(sp, r0);
     const int sizeOfRetval = sizeof(size_t)*2;
     masm.reserveStack(sizeOfRetval);
     masm.mov(sp, r1);
     const int sizeOfBailoutInfo = sizeof(void*)*2;
     masm.reserveStack(sizeOfBailoutInfo);
     masm.mov(sp, r2);
-    masm.setupAlignedABICall(3);
+    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);
     // Remove the return address, the IonScript, the register state
@@ -641,17 +641,17 @@ GenerateBailoutThunk(JSContext* cx, Macr
     PushBailoutFrame(masm, frameClass, r0);
 
     // SP % 8 == 4
     // STEP 1c: Call the bailout function, giving a pointer to the
     //          structure we just blitted onto the stack.
     const int sizeOfBailoutInfo = sizeof(void*)*2;
     masm.reserveStack(sizeOfBailoutInfo);
     masm.mov(sp, r1);
-    masm.setupAlignedABICall(2);
+    masm.setupAlignedABICall();
 
     // Decrement sp by another 4, so we keep alignment. Not Anymore! Pushing
     // 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);
 
@@ -812,17 +812,17 @@ JitRuntime::generateVMWrapper(JSContext*
         masm.ma_mov(sp, outReg);
         break;
 
       default:
         MOZ_ASSERT(f.outParam == Type_Void);
         break;
     }
 
-    masm.setupUnalignedABICall(f.argc(), regs.getAny());
+    masm.setupUnalignedABICall(regs.getAny());
     masm.passABIArg(cxreg);
 
     size_t argDisp = 0;
 
     // Copy any arguments.
     for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
         MoveOperand from;
         switch (f.argProperties(explicitArg)) {
@@ -937,17 +937,17 @@ JitRuntime::generatePreBarrier(JSContext
                                  FloatRegisterSet());
     }
     save.add(lr);
     masm.PushRegsInMask(save);
 
     MOZ_ASSERT(PreBarrierReg == r1);
     masm.movePtr(ImmPtr(cx->runtime()), r0);
 
-    masm.setupUnalignedABICall(2, r2);
+    masm.setupUnalignedABICall(r2);
     masm.passABIArg(r0);
     masm.passABIArg(r1);
     masm.callWithABI(IonMarkFunction(type));
     save.take(AnyRegister(lr));
     save.add(pc);
     masm.PopRegsInMask(save);
 
     Linker linker(masm);
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -7,16 +7,18 @@
 #include "jit/arm64/MacroAssembler-arm64.h"
 
 #include "jit/arm64/MoveEmitter-arm64.h"
 #include "jit/arm64/SharedICRegisters-arm64.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/MacroAssembler.h"
 
+#include "jit/MacroAssembler-inl.h"
+
 namespace js {
 namespace jit {
 
 void
 MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
 {
     ARMRegister dest(output, 32);
     Fcvtns(dest, ARMFPRegister(input, 64));
@@ -175,19 +177,19 @@ MacroAssemblerCompat::handleFailureWithH
     int64_t size = (sizeof(ResumeFromException) + 7) & ~7;
     Sub(GetStackPointer64(), GetStackPointer64(), Operand(size));
     if (!GetStackPointer64().Is(sp))
         Mov(sp, GetStackPointer64());
 
     Mov(x0, GetStackPointer64());
 
     // Call the handler.
-    setupUnalignedABICall(1, r1);
-    passABIArg(r0);
-    callWithABI(handler);
+    asMasm().setupUnalignedABICall(r1);
+    asMasm().passABIArg(r0);
+    asMasm().callWithABI(handler);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
     MOZ_ASSERT(GetStackPointer64().Is(x28)); // Lets the code below be a little cleaner.
@@ -248,257 +250,16 @@ MacroAssemblerCompat::handleFailureWithH
     bind(&bailout);
     Ldr(x2, MemOperand(GetStackPointer64(), offsetof(ResumeFromException, bailoutInfo)));
     Ldr(x1, MemOperand(GetStackPointer64(), offsetof(ResumeFromException, target)));
     Mov(x0, BAILOUT_RETURN_OK);
     Br(x1);
 }
 
 void
-MacroAssemblerCompat::setupABICall(uint32_t args)
-{
-    MOZ_ASSERT(!inCall_);
-    inCall_ = true;
-
-    args_ = args;
-    usedOutParam_ = false;
-    passedIntArgs_ = 0;
-    passedFloatArgs_ = 0;
-    passedArgTypes_ = 0;
-    stackForCall_ = ShadowStackSpace;
-}
-
-void
-MacroAssemblerCompat::setupUnalignedABICall(uint32_t args, Register scratch)
-{
-    setupABICall(args);
-    dynamicAlignment_ = true;
-
-    int64_t alignment = ~(int64_t(ABIStackAlignment) - 1);
-    ARMRegister scratch64(scratch, 64);
-
-    // Always save LR -- Baseline ICs assume that LR isn't modified.
-    push(lr);
-
-    // Unhandled for sp -- needs slightly different logic.
-    MOZ_ASSERT(!GetStackPointer64().Is(sp));
-
-    // Remember the stack address on entry.
-    Mov(scratch64, GetStackPointer64());
-
-    // Make alignment, including the effective push of the previous sp.
-    Sub(GetStackPointer64(), GetStackPointer64(), Operand(8));
-    And(GetStackPointer64(), GetStackPointer64(), Operand(alignment));
-
-    // If the PseudoStackPointer is used, sp must be <= psp before a write is valid.
-    syncStackPtr();
-
-    // Store previous sp to the top of the stack, aligned.
-    Str(scratch64, MemOperand(GetStackPointer64(), 0));
-}
-
-void
-MacroAssemblerCompat::passABIArg(const MoveOperand& from, MoveOp::Type type)
-{
-    if (!enoughMemory_)
-        return;
-
-    Register activeSP = Register::FromCode(GetStackPointer64().code());
-    if (type == MoveOp::GENERAL) {
-        Register dest;
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_General;
-        if (GetIntArgReg(passedIntArgs_++, passedFloatArgs_, &dest)) {
-            if (!from.isGeneralReg() || from.reg() != dest)
-                enoughMemory_ = moveResolver_.addMove(from, MoveOperand(dest), type);
-            return;
-        }
-
-        enoughMemory_ = moveResolver_.addMove(from, MoveOperand(activeSP, stackForCall_), type);
-        stackForCall_ += sizeof(int64_t);
-        return;
-    }
-
-    MOZ_ASSERT(type == MoveOp::FLOAT32 || type == MoveOp::DOUBLE);
-    if (type == MoveOp::FLOAT32)
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Float32;
-    else
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Double;
-
-    FloatRegister fdest;
-    if (GetFloatArgReg(passedIntArgs_, passedFloatArgs_++, &fdest)) {
-        if (!from.isFloatReg() || from.floatReg() != fdest)
-            enoughMemory_ = moveResolver_.addMove(from, MoveOperand(fdest), type);
-        return;
-    }
-
-    enoughMemory_ = moveResolver_.addMove(from, MoveOperand(activeSP, stackForCall_), type);
-    switch (type) {
-      case MoveOp::FLOAT32: stackForCall_ += sizeof(float);  break;
-      case MoveOp::DOUBLE:  stackForCall_ += sizeof(double); break;
-      default: MOZ_CRASH("Unexpected float register class argument type");
-    }
-}
-
-void
-MacroAssemblerCompat::passABIArg(Register reg)
-{
-    passABIArg(MoveOperand(reg), MoveOp::GENERAL);
-}
-
-void
-MacroAssemblerCompat::passABIArg(FloatRegister reg, MoveOp::Type type)
-{
-    passABIArg(MoveOperand(reg), type);
-}
-void
-MacroAssemblerCompat::passABIOutParam(Register reg)
-{
-    if (!enoughMemory_)
-        return;
-    MOZ_ASSERT(!usedOutParam_);
-    usedOutParam_ = true;
-    if (reg == r8)
-        return;
-    enoughMemory_ = moveResolver_.addMove(MoveOperand(reg), MoveOperand(r8), MoveOp::GENERAL);
-
-}
-
-void
-MacroAssemblerCompat::callWithABIPre(uint32_t* stackAdjust)
-{
-    *stackAdjust = stackForCall_;
-    // ARM64 /really/ wants the stack to always be aligned.  Since we're already tracking it
-    // getting it aligned for an abi call is pretty easy.
-    *stackAdjust += ComputeByteAlignment(*stackAdjust, StackAlignment);
-    asMasm().reserveStack(*stackAdjust);
-    {
-        moveResolver_.resolve();
-        MoveEmitter emitter(asMasm());
-        emitter.emit(moveResolver_);
-        emitter.finish();
-    }
-
-    // Call boundaries communicate stack via sp.
-    syncStackPtr();
-}
-
-void
-MacroAssemblerCompat::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
-{
-    // Call boundaries communicate stack via sp.
-    if (!GetStackPointer64().Is(sp))
-        Mov(GetStackPointer64(), sp);
-
-    inCall_ = false;
-    asMasm().freeStack(stackAdjust);
-
-    // Restore the stack pointer from entry.
-    if (dynamicAlignment_)
-        Ldr(GetStackPointer64(), MemOperand(GetStackPointer64(), 0));
-
-    // Restore LR.
-    pop(lr);
-
-    // TODO: This one shouldn't be necessary -- check that callers
-    // aren't enforcing the ABI themselves!
-    syncStackPtr();
-
-    // If the ABI's return regs are where ION is expecting them, then
-    // no other work needs to be done.
-}
-
-#if defined(DEBUG) && defined(JS_SIMULATOR_ARM64)
-static void
-AssertValidABIFunctionType(uint32_t passedArgTypes)
-{
-    switch (passedArgTypes) {
-      case Args_General0:
-      case Args_General1:
-      case Args_General2:
-      case Args_General3:
-      case Args_General4:
-      case Args_General5:
-      case Args_General6:
-      case Args_General7:
-      case Args_General8:
-      case Args_Double_None:
-      case Args_Int_Double:
-      case Args_Float32_Float32:
-      case Args_Double_Double:
-      case Args_Double_Int:
-      case Args_Double_DoubleInt:
-      case Args_Double_DoubleDouble:
-      case Args_Double_DoubleDoubleDouble:
-      case Args_Double_DoubleDoubleDoubleDouble:
-      case Args_Double_IntDouble:
-      case Args_Int_IntDouble:
-        break;
-      default:
-        MOZ_CRASH("Unexpected type");
-    }
-}
-#endif // DEBUG && JS_SIMULATOR_ARM64
-
-void
-MacroAssemblerCompat::callWithABI(void* fun, MoveOp::Type result)
-{
-#ifdef JS_SIMULATOR_ARM64
-    MOZ_ASSERT(passedIntArgs_ + passedFloatArgs_ <= 15);
-    passedArgTypes_ <<= ArgType_Shift;
-    switch (result) {
-      case MoveOp::GENERAL: passedArgTypes_ |= ArgType_General; break;
-      case MoveOp::DOUBLE:  passedArgTypes_ |= ArgType_Double;  break;
-      case MoveOp::FLOAT32: passedArgTypes_ |= ArgType_Float32; break;
-      default: MOZ_CRASH("Invalid return type");
-    }
-# ifdef DEBUG
-    AssertValidABIFunctionType(passedArgTypes_);
-# endif
-    ABIFunctionType type = ABIFunctionType(passedArgTypes_);
-    fun = vixl::Simulator::RedirectNativeFunction(fun, type);
-#endif // JS_SIMULATOR_ARM64
-
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(ImmPtr(fun));
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerCompat::callWithABI(Register fun, MoveOp::Type result)
-{
-    movePtr(fun, ip0);
-
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(ip0);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerCompat::callWithABI(AsmJSImmPtr imm, MoveOp::Type result)
-{
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(imm);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerCompat::callWithABI(Address fun, MoveOp::Type result)
-{
-    loadPtr(fun, ip0);
-
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(ip0);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
 MacroAssemblerCompat::branchPtrInNurseryRange(Condition cond, Register ptr, Register temp,
                                               Label* label)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     MOZ_ASSERT(ptr != temp);
     MOZ_ASSERT(ptr != ScratchReg && ptr != ScratchReg2); // Both may be used internally.
     MOZ_ASSERT(temp != ScratchReg && temp != ScratchReg2);
 
@@ -749,12 +510,122 @@ MacroAssembler::call(JitCode* c)
     vixl::UseScratchRegisterScope temps(this);
     const ARMRegister scratch64 = temps.AcquireX();
     syncStackPtr();
     BufferOffset off = immPool64(scratch64, uint64_t(c->raw()));
     addPendingJump(off, ImmPtr(c->raw()), Relocation::JITCODE);
     blr(scratch64);
 }
 
+// ===============================================================
+// ABI function calls.
+
+void
+MacroAssembler::setupUnalignedABICall(Register scratch)
+{
+    setupABICall();
+    dynamicAlignment_ = true;
+
+    int64_t alignment = ~(int64_t(ABIStackAlignment) - 1);
+    ARMRegister scratch64(scratch, 64);
+
+    // Always save LR -- Baseline ICs assume that LR isn't modified.
+    push(lr);
+
+    // Unhandled for sp -- needs slightly different logic.
+    MOZ_ASSERT(!GetStackPointer64().Is(sp));
+
+    // Remember the stack address on entry.
+    Mov(scratch64, GetStackPointer64());
+
+    // Make alignment, including the effective push of the previous sp.
+    Sub(GetStackPointer64(), GetStackPointer64(), Operand(8));
+    And(GetStackPointer64(), GetStackPointer64(), Operand(alignment));
+
+    // If the PseudoStackPointer is used, sp must be <= psp before a write is valid.
+    syncStackPtr();
+
+    // Store previous sp to the top of the stack, aligned.
+    Str(scratch64, MemOperand(GetStackPointer64(), 0));
+}
+
+void
+MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS)
+{
+    MOZ_ASSERT(inCall_);
+    uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
+
+    // ARM64 /really/ wants the stack to always be aligned.  Since we're already tracking it
+    // getting it aligned for an abi call is pretty easy.
+    MOZ_ASSERT(dynamicAlignment_);
+    stackForCall += ComputeByteAlignment(stackForCall, StackAlignment);
+    *stackAdjust = stackForCall;
+    reserveStack(*stackAdjust);
+    {
+        moveResolver_.resolve();
+        MoveEmitter emitter(*this);
+        emitter.emit(moveResolver_);
+        emitter.finish();
+    }
+
+    // Call boundaries communicate stack via sp.
+    syncStackPtr();
+}
+
+void
+MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
+{
+    // Call boundaries communicate stack via sp.
+    if (!GetStackPointer64().Is(sp))
+        Mov(GetStackPointer64(), sp);
+
+    freeStack(stackAdjust);
+
+    // Restore the stack pointer from entry.
+    if (dynamicAlignment_)
+        Ldr(GetStackPointer64(), MemOperand(GetStackPointer64(), 0));
+
+    // Restore LR.
+    pop(lr);
+
+    // TODO: This one shouldn't be necessary -- check that callers
+    // aren't enforcing the ABI themselves!
+    syncStackPtr();
+
+    // If the ABI's return regs are where ION is expecting them, then
+    // no other work needs to be done.
+
+#ifdef DEBUG
+    MOZ_ASSERT(inCall_);
+    inCall_ = false;
+#endif
+}
+
+void
+MacroAssembler::callWithABINoProfiler(Register fun, MoveOp::Type result)
+{
+    vixl::UseScratchRegisterScope temps(this);
+    const Register scratch = temps.AcquireX().asUnsized();
+    movePtr(fun, scratch);
+
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(scratch);
+    callWithABIPost(stackAdjust, result);
+}
+
+void
+MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
+{
+    vixl::UseScratchRegisterScope temps(this);
+    const Register scratch = temps.AcquireX().asUnsized();
+    loadPtr(fun, scratch);
+
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(scratch);
+    callWithABIPost(stackAdjust, result);
+}
+
 //}}} check_macroassembler_style
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -54,39 +54,20 @@ class MacroAssemblerCompat : public vixl
     // Restrict to only VIXL-internal functions.
     vixl::MacroAssembler& asVIXL();
     const MacroAssembler& asVIXL() const;
 
   protected:
     bool enoughMemory_;
     uint32_t framePushed_;
 
-    // TODO: Can this be moved out of the MacroAssembler and into some shared code?
-    // TODO: All the code seems to be arch-independent, and it's weird to have this here.
-    bool inCall_;
-    bool usedOutParam_;
-    uint32_t args_;
-    uint32_t passedIntArgs_;
-    uint32_t passedFloatArgs_;
-    uint32_t passedArgTypes_;
-    uint32_t stackForCall_;
-    bool dynamicAlignment_;
-
     MacroAssemblerCompat()
       : vixl::MacroAssembler(),
         enoughMemory_(true),
-        framePushed_(0),
-        inCall_(false),
-        usedOutParam_(false),
-        args_(0),
-        passedIntArgs_(0),
-        passedFloatArgs_(0),
-        passedArgTypes_(0),
-        stackForCall_(0),
-        dynamicAlignment_(false)
+        framePushed_(0)
     { }
 
   protected:
     MoveResolver moveResolver_;
 
   public:
     bool oom() const {
         return Assembler::oom() || !enoughMemory_;
@@ -2645,57 +2626,17 @@ class MacroAssemblerCompat : public vixl
         ARMRegister base64(address.base, 64);
         ARMRegister index64(address.index, 64);
 
         Add(dest64, base64, Operand(index64, vixl::LSL, address.scale));
         if (address.offset)
             Add(dest64, dest64, Operand(address.offset));
     }
 
-  private:
-    void setupABICall(uint32_t args);
-
   public:
-    // Setup a call to C/C++ code, given the number of general arguments it
-    // takes. Note that this only supports cdecl.
-    //
-    // In order for alignment to work correctly, the MacroAssembler must have a
-    // consistent view of the stack displacement. It is okay to call "push"
-    // manually, however, if the stack alignment were to change, the macro
-    // assembler should be notified before starting a call.
-    void setupAlignedABICall(uint32_t args) {
-        MOZ_CRASH("setupAlignedABICall");
-    }
-
-    // Sets up an ABI call for when the alignment is not known. This may need a
-    // scratch register.
-    void setupUnalignedABICall(uint32_t args, Register scratch);
-
-    // Arguments must be assigned to a C/C++ call in order. They are moved
-    // in parallel immediately before performing the call. This process may
-    // temporarily use more stack, in which case sp-relative addresses will be
-    // automatically adjusted. It is extremely important that sp-relative
-    // addresses are computed *after* setupABICall(). Furthermore, no
-    // operations should be emitted while setting arguments.
-    void passABIArg(const MoveOperand& from, MoveOp::Type type);
-    void passABIArg(Register reg);
-    void passABIArg(FloatRegister reg, MoveOp::Type type);
-    void passABIOutParam(Register reg);
-
-  private:
-    void callWithABIPre(uint32_t* stackAdjust);
-    void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result);
-
-  public:
-    // Emits a call to a C/C++ function, resolving all argument moves.
-    void callWithABI(void* fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(AsmJSImmPtr imm, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(Address fun, MoveOp::Type result = MoveOp::GENERAL);
-
     CodeOffsetLabel labelForPatch() {
         return CodeOffsetLabel(nextOffset().getOffset());
     }
 
     void handleFailureWithHandlerTail(void* handler);
 
     // FIXME: This is the same on all platforms. Can be common code?
     void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
--- a/js/src/jit/arm64/Trampoline-arm64.cpp
+++ b/js/src/jit/arm64/Trampoline-arm64.cpp
@@ -9,16 +9,18 @@
 #include "jit/JitFrames.h"
 #include "jit/Linker.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/arm64/SharedICHelpers-arm64.h"
 #include "jit/VMFunctions.h"
 
+#include "jit/MacroAssembler-inl.h"
+
 using namespace js;
 using namespace js::jit;
 
 // All registers to save and restore. This includes the stack pointer, since we
 // use the ability to reference register values on the stack by index.
 static const LiveRegisterSet AllRegs =
     LiveRegisterSet(GeneralRegisterSet(Registers::AllMask & ~(1 << 31 | 1 << 30 | 1 << 29| 1 << 28)),
                 FloatRegisterSet(FloatRegisters::AllMask));
@@ -187,17 +189,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.makeFrameDescriptor(r19, JitFrame_BaselineJS);
         masm.asVIXL().Push(x19, xzr); // Push xzr for a fake return address.
         // No GC things to mark: push a bare token.
         masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
 
         masm.push(BaselineFrameReg, reg_code);
 
         // Initialize the frame, including filling in the slots.
-        masm.setupUnalignedABICall(3, r19);
+        masm.setupUnalignedABICall(r19);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame.
         masm.passABIArg(reg_osrFrame); // InterpreterFrame.
         masm.passABIArg(reg_osrNStack);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
 
         masm.pop(r19, BaselineFrameReg);
         MOZ_ASSERT(r19 != ReturnReg);
 
@@ -288,17 +290,17 @@ JitRuntime::generateInvalidator(JSContex
 
     masm.PushRegsInMask(AllRegs);
     masm.moveStackPtrTo(r0);
 
     masm.Sub(x1, masm.GetStackPointer64(), Operand(sizeof(size_t)));
     masm.Sub(x2, masm.GetStackPointer64(), Operand(sizeof(size_t) + sizeof(void*)));
     masm.moveToStackPtr(r2);
 
-    masm.setupUnalignedABICall(3, r10);
+    masm.setupUnalignedABICall(r10);
     masm.passABIArg(r0);
     masm.passABIArg(r1);
     masm.passABIArg(r2);
 
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout));
 
     masm.pop(r2, r1);
 
@@ -489,17 +491,17 @@ GenerateBailoutThunk(JSContext* cx, Macr
     // SP % 8 == 4
     // STEP 1c: Call the bailout function, giving a pointer to the
     //          structure we just blitted onto the stack.
     // Make space for the BaselineBailoutInfo* outparam.
     const int sizeOfBailoutInfo = sizeof(void*) * 2;
     masm.reserveStack(sizeOfBailoutInfo);
     masm.moveStackPtrTo(r1);
 
-    masm.setupUnalignedABICall(2, r2);
+    masm.setupUnalignedABICall(r2);
     masm.passABIArg(r0);
     masm.passABIArg(r1);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout));
 
     masm.Ldr(x2, MemOperand(masm.GetStackPointer64(), 0));
     masm.addToStackPtr(Imm32(sizeOfBailoutInfo));
 
     static const uint32_t BailoutDataSize = sizeof(void*) * Registers::Total +
@@ -629,17 +631,17 @@ JitRuntime::generateVMWrapper(JSContext*
         masm.moveStackPtrTo(outReg);
         break;
 
       default:
         MOZ_ASSERT(f.outParam == Type_Void);
         break;
     }
 
-    masm.setupUnalignedABICall(f.argc(), regs.getAny());
+    masm.setupUnalignedABICall(regs.getAny());
     masm.passABIArg(reg_cx);
 
     size_t argDisp = 0;
 
     // Copy arguments.
     for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
         MoveOperand from;
         switch (f.argProperties(explicitArg)) {
@@ -756,17 +758,17 @@ JitRuntime::generatePreBarrier(JSContext
     // Also preserve the return address.
     regs.add(lr);
 
     masm.PushRegsInMask(regs);
 
     MOZ_ASSERT(PreBarrierReg == r1);
     masm.movePtr(ImmPtr(cx->runtime()), r3);
 
-    masm.setupUnalignedABICall(2, r0);
+    masm.setupUnalignedABICall(r0);
     masm.passABIArg(r3);
     masm.passABIArg(PreBarrierReg);
     masm.callWithABI(IonMarkFunction(type));
 
     // Pop the volatile regs and restore LR.
     masm.PopRegsInMask(regs);
 
     masm.abiret();
--- a/js/src/jit/mips32/Architecture-mips32.h
+++ b/js/src/jit/mips32/Architecture-mips32.h
@@ -529,16 +529,22 @@ hasUnaliasedDouble() {
 // On MIPS, fn-double aliases both fn-float32 and fn+1-float32, so if you need
 // to convert a float32 to a double as a temporary, you need a temporary
 // double register.
 inline bool
 hasMultiAlias() {
     return true;
 }
 
+// In order to handle functions such as int(*)(int, double) where the first
+// argument is a general purpose register, and the second argument is a floating
+// point register, we have to store the double content into 2 general purpose
+// registers, namely a2 and a3.
+#define JS_CODEGEN_REGISTER_PAIR 1
+
 // See the comments above AsmJSMappedSize in AsmJSValidate.h for more info.
 // TODO: Implement this for MIPS. Note that it requires Codegen to respect the
 // offset field of AsmJSHeapAccess.
 static const size_t AsmJSCheckedImmediateRange = 0;
 static const size_t AsmJSImmediateRange = 0;
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/mips32/Assembler-mips32.cpp
+++ b/js/src/jit/mips32/Assembler-mips32.cpp
@@ -18,59 +18,74 @@
 
 using mozilla::DebugOnly;
 
 using namespace js;
 using namespace js::jit;
 
 ABIArgGenerator::ABIArgGenerator()
   : usedArgSlots_(0),
-    firstArgFloat(false),
+    firstArgFloatSize_(0),
+    useGPRForFloats_(false),
     current_()
 {}
 
 ABIArg
 ABIArgGenerator::next(MIRType type)
 {
-    FloatRegister::RegType regType;
+    Register destReg;
     switch (type) {
       case MIRType_Int32:
       case MIRType_Pointer:
-        Register destReg;
         if (GetIntArgReg(usedArgSlots_, &destReg))
             current_ = ABIArg(destReg);
         else
             current_ = ABIArg(usedArgSlots_ * sizeof(intptr_t));
         usedArgSlots_++;
         break;
       case MIRType_Float32:
-      case MIRType_Double:
-        regType = (type == MIRType_Double ? FloatRegister::Double : FloatRegister::Single);
         if (!usedArgSlots_) {
-            current_ = ABIArg(FloatRegister(FloatRegisters::f12, regType));
-            usedArgSlots_ += 2;
-            firstArgFloat = true;
-        } else if (usedArgSlots_ <= 2) {
-            // NOTE: We will use f14 always. This is not compatible with
-            // system ABI. We will have to introduce some infrastructure
-            // changes if we have to use system ABI here.
-            current_ = ABIArg(FloatRegister(FloatRegisters::f14, regType));
+            current_ = ABIArg(f12.asSingle());
+            firstArgFloatSize_ = 1;
+        } else if (usedArgSlots_ == firstArgFloatSize_) {
+            current_ = ABIArg(f14.asSingle());
+        } else if (useGPRForFloats_ && GetIntArgReg(usedArgSlots_, &destReg)) {
+            current_ = ABIArg(destReg);
+        } else {
+            if (usedArgSlots_ < NumIntArgRegs)
+                usedArgSlots_ = NumIntArgRegs;
+            current_ = ABIArg(usedArgSlots_ * sizeof(intptr_t));
+        }
+        usedArgSlots_++;
+        break;
+      case MIRType_Double:
+        if (!usedArgSlots_) {
+            current_ = ABIArg(f12);
+            usedArgSlots_ = 2;
+            firstArgFloatSize_ = 2;
+        } else if (usedArgSlots_ == firstArgFloatSize_) {
+            current_ = ABIArg(f14);
+            usedArgSlots_ = 4;
+        } else if (useGPRForFloats_ && usedArgSlots_ <= 2) {
+            current_ = ABIArg(a2, a3);
             usedArgSlots_ = 4;
         } else {
+            if (usedArgSlots_ < NumIntArgRegs)
+                usedArgSlots_ = NumIntArgRegs;
             usedArgSlots_ += usedArgSlots_ % 2;
             current_ = ABIArg(usedArgSlots_ * sizeof(intptr_t));
             usedArgSlots_ += 2;
         }
         break;
       default:
         MOZ_CRASH("Unexpected argument type");
     }
     return current_;
+}
 
-}
 const Register ABIArgGenerator::NonArgReturnReg0 = t0;
 const Register ABIArgGenerator::NonArgReturnReg1 = t1;
 const Register ABIArgGenerator::NonArg_VolatileReg = v0;
 const Register ABIArgGenerator::NonReturn_VolatileReg0 = a0;
 const Register ABIArgGenerator::NonReturn_VolatileReg1 = a1;
 
 // Encode a standard register when it is being used as rd, the rs, and
 // an extra register(rt). These should never be called with an InvalidReg.
--- a/js/src/jit/mips32/Assembler-mips32.h
+++ b/js/src/jit/mips32/Assembler-mips32.h
@@ -75,24 +75,31 @@ static MOZ_CONSTEXPR_VAR Register IntArg
 static MOZ_CONSTEXPR_VAR Register GlobalReg = s6; // used by Odin
 static MOZ_CONSTEXPR_VAR Register HeapReg = s7; // used by Odin
 static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { t0, t1, t2, t3, t4 };
 static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs);
 
 class ABIArgGenerator
 {
     unsigned usedArgSlots_;
-    bool firstArgFloat;
+    unsigned firstArgFloatSize_;
+    // Note: This is not compliant with the system ABI.  The Lowering phase
+    // expects to lower an MAsmJSParameter to only one register.
+    bool useGPRForFloats_;
     ABIArg current_;
 
   public:
     ABIArgGenerator();
     ABIArg next(MIRType argType);
     ABIArg& current() { return current_; }
 
+    void enforceO32ABI() {
+        useGPRForFloats_ = true;
+    }
+
     uint32_t stackBytesConsumedSoFar() const {
         if (usedArgSlots_ <= 4)
             return ShadowStackSpace;
 
         return usedArgSlots_ * sizeof(intptr_t);
     }
 
     static const Register NonArgReturnReg0;
--- a/js/src/jit/mips32/CodeGenerator-mips32.cpp
+++ b/js/src/jit/mips32/CodeGenerator-mips32.cpp
@@ -2115,14 +2115,14 @@ CodeGeneratorMIPS::visitNegF(LNegF* ins)
 void
 CodeGeneratorMIPS::visitRandom(LRandom* ins)
 {
     Register temp = ToRegister(ins->temp());
     Register temp2 = ToRegister(ins->temp2());
 
     masm.loadJSContext(temp);
 
-    masm.setupUnalignedABICall(1, temp2);
+    masm.setupUnalignedABICall(temp2);
     masm.passABIArg(temp);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
 
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 }
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -3144,143 +3144,16 @@ MacroAssemblerMIPSCompat::ensureDouble(c
 
     bind(&isDouble);
     unboxDouble(source, dest);
 
     bind(&done);
 }
 
 void
-MacroAssemblerMIPSCompat::setupABICall(uint32_t args)
-{
-    MOZ_ASSERT(!inCall_);
-    inCall_ = true;
-    args_ = args;
-    passedArgs_ = 0;
-    passedArgTypes_ = 0;
-
-    usedArgSlots_ = 0;
-    firstArgType = MoveOp::GENERAL;
-}
-
-void
-MacroAssemblerMIPSCompat::setupAlignedABICall(uint32_t args)
-{
-    setupABICall(args);
-
-    dynamicAlignment_ = false;
-}
-
-void
-MacroAssemblerMIPSCompat::setupUnalignedABICall(uint32_t args, Register scratch)
-{
-    setupABICall(args);
-    dynamicAlignment_ = true;
-
-    ma_move(scratch, StackPointer);
-
-    // Force sp to be aligned
-    ma_subu(StackPointer, StackPointer, Imm32(sizeof(uint32_t)));
-    ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
-    as_sw(scratch, StackPointer, 0);
-}
-
-void
-MacroAssemblerMIPSCompat::passABIArg(const MoveOperand& from, MoveOp::Type type)
-{
-    ++passedArgs_;
-    if (!enoughMemory_)
-        return;
-    switch (type) {
-      case MoveOp::FLOAT32:
-        if (!usedArgSlots_) {
-            if (from.floatReg() != f12)
-                enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f12), type);
-            firstArgType = MoveOp::FLOAT32;
-        } else if ((usedArgSlots_ == 1 && firstArgType == MoveOp::FLOAT32) ||
-                  (usedArgSlots_ == 2 && firstArgType == MoveOp::DOUBLE)) {
-            if (from.floatReg() != f14)
-                enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f14), type);
-        } else {
-            Register destReg;
-            if (GetIntArgReg(usedArgSlots_, &destReg)) {
-                if (from.isGeneralReg() && from.reg() == destReg) {
-                    // Nothing to do. Value is in the right register already
-                } else {
-                    enoughMemory_ = moveResolver_.addMove(from, MoveOperand(destReg), type);
-                }
-            } else {
-                uint32_t disp = GetArgStackDisp(usedArgSlots_);
-                enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), type);
-            }
-        }
-        usedArgSlots_++;
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Float32;
-        break;
-      case MoveOp::DOUBLE:
-        if (!usedArgSlots_) {
-            if (from.floatReg() != f12)
-                enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f12), type);
-            usedArgSlots_ = 2;
-            firstArgType = MoveOp::DOUBLE;
-        } else if (usedArgSlots_ <= 2) {
-            if ((usedArgSlots_ == 1 && firstArgType == MoveOp::FLOAT32) ||
-               (usedArgSlots_ == 2 && firstArgType == MoveOp::DOUBLE)) {
-                if (from.floatReg() != f14)
-                    enoughMemory_ = moveResolver_.addMove(from, MoveOperand(f14), type);
-            } else {
-                // Create two moves so that cycles are found. Move emitter
-                // will have special case to handle this.
-                enoughMemory_ = moveResolver_.addMove(from, MoveOperand(a2), type);
-                enoughMemory_ = moveResolver_.addMove(from, MoveOperand(a3), type);
-            }
-            usedArgSlots_ = 4;
-        } else {
-            // Align if necessary
-            usedArgSlots_ += usedArgSlots_ % 2;
-
-            uint32_t disp = GetArgStackDisp(usedArgSlots_);
-            enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), type);
-            usedArgSlots_ += 2;
-        }
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Double;
-        break;
-      case MoveOp::GENERAL:
-        Register destReg;
-        if (GetIntArgReg(usedArgSlots_, &destReg)) {
-            if (from.isGeneralReg() && from.reg() == destReg) {
-                // Nothing to do. Value is in the right register already
-            } else {
-                enoughMemory_ = moveResolver_.addMove(from, MoveOperand(destReg), type);
-            }
-        } else {
-            uint32_t disp = GetArgStackDisp(usedArgSlots_);
-            enoughMemory_ = moveResolver_.addMove(from, MoveOperand(sp, disp), type);
-        }
-        usedArgSlots_++;
-        passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_General;
-        break;
-      default:
-        MOZ_CRASH("Unexpected argument type");
-    }
-}
-
-void
-MacroAssemblerMIPSCompat::passABIArg(Register reg)
-{
-    passABIArg(MoveOperand(reg), MoveOp::GENERAL);
-}
-
-void
-MacroAssemblerMIPSCompat::passABIArg(FloatRegister freg, MoveOp::Type type)
-{
-    passABIArg(MoveOperand(freg), type);
-}
-
-void
 MacroAssemblerMIPSCompat::checkStackAlignment()
 {
 #ifdef DEBUG
     Label aligned;
     as_andi(ScratchRegister, sp, ABIStackAlignment - 1);
     ma_b(ScratchRegister, zero, &aligned, Equal, ShortJump);
     as_break(BREAK_STACK_UNALIGNED);
     bind(&aligned);
@@ -3318,179 +3191,27 @@ MacroAssembler::alignFrameForICArguments
 void
 MacroAssembler::restoreFrameAlignmentForICArguments(AfterICSaveLive& aic)
 {
     if (aic.alignmentPadding != 0)
         freeStack(aic.alignmentPadding);
 }
 
 void
-MacroAssemblerMIPSCompat::callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS)
-{
-    MOZ_ASSERT(inCall_);
-
-    // Reserve place for $ra.
-    *stackAdjust = sizeof(intptr_t);
-
-    *stackAdjust += usedArgSlots_ > NumIntArgRegs ?
-                    usedArgSlots_ * sizeof(intptr_t) :
-                    NumIntArgRegs * sizeof(intptr_t);
-
-    uint32_t alignmentAtPrologue = callFromAsmJS ? sizeof(AsmJSFrame) : 0;
-
-    if (dynamicAlignment_) {
-        *stackAdjust += ComputeByteAlignment(*stackAdjust, ABIStackAlignment);
-    } else {
-        *stackAdjust += ComputeByteAlignment(asMasm().framePushed() + alignmentAtPrologue + *stackAdjust,
-                                             ABIStackAlignment);
-    }
-
-    asMasm().reserveStack(*stackAdjust);
-
-    // Save $ra because call is going to clobber it. Restore it in
-    // callWithABIPost. NOTE: This is needed for calls from BaselineIC.
-    // Maybe we can do this differently.
-    ma_sw(ra, Address(StackPointer, *stackAdjust - sizeof(intptr_t)));
-
-    // Position all arguments.
-    {
-        enoughMemory_ = enoughMemory_ && moveResolver_.resolve();
-        if (!enoughMemory_)
-            return;
-
-        MoveEmitter emitter(asMasm());
-        emitter.emit(moveResolver_);
-        emitter.finish();
-    }
-
-    checkStackAlignment();
-}
-
-void
-MacroAssemblerMIPSCompat::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
-{
-    // Restore ra value (as stored in callWithABIPre()).
-    ma_lw(ra, Address(StackPointer, stackAdjust - sizeof(intptr_t)));
-
-    if (dynamicAlignment_) {
-        // Restore sp value from stack (as stored in setupUnalignedABICall()).
-        ma_lw(StackPointer, Address(StackPointer, stackAdjust));
-        // Use adjustFrame instead of freeStack because we already restored sp.
-        asMasm().adjustFrame(-stackAdjust);
-    } else {
-        asMasm().freeStack(stackAdjust);
-    }
-
-    MOZ_ASSERT(inCall_);
-    inCall_ = false;
-}
-
-#if defined(DEBUG) && defined(JS_SIMULATOR_MIPS32)
-static void
-AssertValidABIFunctionType(uint32_t passedArgTypes)
-{
-    switch (passedArgTypes) {
-      case Args_General0:
-      case Args_General1:
-      case Args_General2:
-      case Args_General3:
-      case Args_General4:
-      case Args_General5:
-      case Args_General6:
-      case Args_General7:
-      case Args_General8:
-      case Args_Double_None:
-      case Args_Int_Double:
-      case Args_Float32_Float32:
-      case Args_Double_Double:
-      case Args_Double_Int:
-      case Args_Double_DoubleInt:
-      case Args_Double_DoubleDouble:
-      case Args_Double_IntDouble:
-      case Args_Int_IntDouble:
-      case Args_Double_DoubleDoubleDouble:
-      case Args_Double_DoubleDoubleDoubleDouble:
-        break;
-      default:
-        MOZ_CRASH("Unexpected type");
-    }
-}
-#endif
-
-void
-MacroAssemblerMIPSCompat::callWithABI(void* fun, MoveOp::Type result)
-{
-#ifdef JS_SIMULATOR_MIPS32
-    MOZ_ASSERT(passedArgs_ <= 15);
-    passedArgTypes_ <<= ArgType_Shift;
-    switch (result) {
-      case MoveOp::GENERAL: passedArgTypes_ |= ArgType_General; break;
-      case MoveOp::DOUBLE:  passedArgTypes_ |= ArgType_Double;  break;
-      case MoveOp::FLOAT32: passedArgTypes_ |= ArgType_Float32; break;
-      default: MOZ_CRASH("Invalid return type");
-    }
-#ifdef DEBUG
-    AssertValidABIFunctionType(passedArgTypes_);
-#endif
-    ABIFunctionType type = ABIFunctionType(passedArgTypes_);
-    fun = Simulator::RedirectNativeFunction(fun, type);
-#endif
-
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    ma_call(ImmPtr(fun));
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerMIPSCompat::callWithABI(AsmJSImmPtr imm, MoveOp::Type result)
-{
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust, /* callFromAsmJS = */ true);
-    asMasm().call(imm);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerMIPSCompat::callWithABI(const Address& fun, MoveOp::Type result)
-{
-    // Load the callee in t9, no instruction between the lw 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_lw(t9, Address(fun.base, fun.offset));
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(t9);
-    callWithABIPost(stackAdjust, result);
-
-}
-
-void
-MacroAssemblerMIPSCompat::callWithABI(Register fun, MoveOp::Type result)
-{
-    // Load the callee in t9, as above.
-    ma_move(t9, fun);
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(t9);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
 MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void* handler)
 {
     // Reserve space for exception information.
     int size = (sizeof(ResumeFromException) + ABIStackAlignment) & ~(ABIStackAlignment - 1);
     ma_subu(StackPointer, StackPointer, Imm32(size));
     ma_move(a0, StackPointer); // Use a0 since it is a first function argument
 
     // Call the handler.
-    setupUnalignedABICall(1, a1);
-    passABIArg(a0);
-    callWithABI(handler);
+    asMasm().setupUnalignedABICall(a1);
+    asMasm().passABIArg(a0);
+    asMasm().callWithABI(handler);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
     // Already clobbered a0, so use it...
@@ -3819,9 +3540,110 @@ void
 MacroAssembler::call(JitCode* c)
 {
     BufferOffset bo = m_buffer.nextOffset();
     addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
     ma_liPatchable(ScratchRegister, Imm32((uint32_t)c->raw()));
     ma_callJitHalfPush(ScratchRegister);
 }
 
+// ===============================================================
+// ABI function calls.
+
+void
+MacroAssembler::setupUnalignedABICall(Register scratch)
+{
+    setupABICall();
+    dynamicAlignment_ = true;
+
+    ma_move(scratch, StackPointer);
+
+    // Force sp to be aligned
+    ma_subu(StackPointer, StackPointer, Imm32(sizeof(uint32_t)));
+    ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
+    as_sw(scratch, StackPointer, 0);
+}
+
+void
+MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS)
+{
+    MOZ_ASSERT(inCall_);
+    uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
+
+    // Reserve place for $ra.
+    stackForCall += sizeof(intptr_t);
+
+    if (dynamicAlignment_) {
+        stackForCall += ComputeByteAlignment(stackForCall, ABIStackAlignment);
+    } else {
+        uint32_t alignmentAtPrologue = callFromAsmJS ? sizeof(AsmJSFrame) : 0;
+        stackForCall += ComputeByteAlignment(stackForCall + framePushed() + alignmentAtPrologue,
+                                             ABIStackAlignment);
+    }
+
+    *stackAdjust = stackForCall;
+    reserveStack(stackForCall);
+
+    // Save $ra because call is going to clobber it. Restore it in
+    // callWithABIPost. NOTE: This is needed for calls from BaselineIC.
+    // Maybe we can do this differently.
+    ma_sw(ra, Address(StackPointer, stackForCall - sizeof(intptr_t)));
+
+    // Position all arguments.
+    {
+        enoughMemory_ = enoughMemory_ && moveResolver_.resolve();
+        if (!enoughMemory_)
+            return;
+
+        MoveEmitter emitter(*this);
+        emitter.emit(moveResolver_);
+        emitter.finish();
+    }
+
+    assertStackAlignment(ABIStackAlignment);
+}
+
+void
+MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
+{
+    // Restore ra value (as stored in callWithABIPre()).
+    ma_lw(ra, Address(StackPointer, stackAdjust - sizeof(intptr_t)));
+
+    if (dynamicAlignment_) {
+        // Restore sp value from stack (as stored in setupUnalignedABICall()).
+        ma_lw(StackPointer, Address(StackPointer, stackAdjust));
+        // Use adjustFrame instead of freeStack because we already restored sp.
+        adjustFrame(-stackAdjust);
+    } else {
+        freeStack(stackAdjust);
+    }
+
+#ifdef DEBUG
+    MOZ_ASSERT(inCall_);
+    inCall_ = false;
+#endif
+}
+
+void
+MacroAssembler::callWithABINoProfiler(Register fun, MoveOp::Type result)
+{
+    // Load the callee in t9, no instruction between the lw 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_move(t9, fun);
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(t9);
+    callWithABIPost(stackAdjust, result);
+}
+
+void
+MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
+{
+    // Load the callee in t9, as above.
+    ma_lw(t9, Address(fun.base, fun.offset));
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(t9);
+    callWithABIPost(stackAdjust, result);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/mips32/MacroAssembler-mips32.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32.h
@@ -347,44 +347,18 @@ class MacroAssembler;
 
 class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
 {
   private:
     // Perform a downcast. Should be removed by Bug 996602.
     MacroAssembler& asMasm();
     const MacroAssembler& asMasm() const;
 
-  private:
-    // Number of bytes the stack is adjusted inside a call to C. Calls to C may
-    // not be nested.
-    bool inCall_;
-    uint32_t args_;
-    // The actual number of arguments that were passed, used to assert that
-    // the initial number of arguments declared was correct.
-    uint32_t passedArgs_;
-    uint32_t passedArgTypes_;
-
-    uint32_t usedArgSlots_;
-    MoveOp::Type firstArgType;
-
-    bool dynamicAlignment_;
-
-    // Compute space needed for the function call and set the properties of the
-    // callee.  It returns the space which has to be allocated for calling the
-    // function.
-    //
-    // arg            Number of arguments of the function.
-    void setupABICall(uint32_t arg);
-
-  protected:
-    MoveResolver moveResolver_;
-
   public:
     MacroAssemblerMIPSCompat()
-      : inCall_(false)
     { }
 
   public:
     using MacroAssemblerMIPS::call;
 
     void j(Label* dest) {
         ma_b(dest);
     }
@@ -1413,53 +1387,20 @@ public:
     }
 
     template <typename T1, typename T2>
     void cmp32Set(Assembler::Condition cond, T1 lhs, T2 rhs, Register dest)
     {
         ma_cmp_set(dest, lhs, rhs, cond);
     }
 
-    // Setup a call to C/C++ code, given the number of general arguments it
-    // takes. Note that this only supports cdecl.
-    //
-    // In order for alignment to work correctly, the MacroAssembler must have a
-    // consistent view of the stack displacement. It is okay to call "push"
-    // manually, however, if the stack alignment were to change, the macro
-    // assembler should be notified before starting a call.
-    void setupAlignedABICall(uint32_t args);
-
-    // Sets up an ABI call for when the alignment is not known. This may need a
-    // scratch register.
-    void setupUnalignedABICall(uint32_t args, Register scratch);
-
-    // Arguments must be assigned in a left-to-right order. This process may
-    // temporarily use more stack, in which case sp-relative addresses will be
-    // automatically adjusted. It is extremely important that sp-relative
-    // addresses are computed *after* setupABICall(). Furthermore, no
-    // operations should be emitted while setting arguments.
-    void passABIArg(const MoveOperand& from, MoveOp::Type type);
-    void passABIArg(Register reg);
-    void passABIArg(FloatRegister reg, MoveOp::Type type);
-    void passABIArg(const ValueOperand& regs);
-
   protected:
     bool buildOOLFakeExitFrame(void* fakeReturnAddr);
 
-  private:
-    void callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS = false);
-    void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result);
-
   public:
-    // Emits a call to a C/C++ function, resolving all argument moves.
-    void callWithABI(void* fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(AsmJSImmPtr imm, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(const Address& fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
-
     CodeOffsetLabel labelForPatch() {
         return CodeOffsetLabel(nextOffset().getOffset());
     }
 
     void memIntToValue(Address Source, Address Dest) {
         load32(Source, SecondScratchReg);
         storeValue(JSVAL_TYPE_INT32, SecondScratchReg, Dest);
     }
--- a/js/src/jit/mips32/MoveEmitter-mips32.cpp
+++ b/js/src/jit/mips32/MoveEmitter-mips32.cpp
@@ -250,51 +250,40 @@ MoveEmitterMIPS::emitDoubleMove(const Mo
 {
     // Ensure that we can use ScratchDoubleReg in memory move.
     MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchDoubleReg);
     MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchDoubleReg);
 
     if (from.isFloatReg()) {
         if (to.isFloatReg()) {
             masm.moveDouble(from.floatReg(), to.floatReg());
-        } else if (to.isGeneralReg()) {
+        } else if (to.isGeneralRegPair()) {
             // Used for passing double parameter in a2,a3 register pair.
             // Two moves are added for one double parameter by
-            // MacroAssemblerMIPSCompat::passABIArg
-            if(to.reg() == a2)
-                masm.moveFromDoubleLo(from.floatReg(), a2);
-            else if(to.reg() == a3)
-                masm.moveFromDoubleHi(from.floatReg(), a3);
-            else
-                MOZ_CRASH("Invalid emitDoubleMove arguments.");
+            // MacroAssembler::passABIArg
+            MOZ_ASSERT(to.evenReg() == a2 && to.oddReg() == a3,
+                       "Invalid emitDoubleMove arguments.");
+            masm.moveFromDoubleLo(from.floatReg(), a2);
+            masm.moveFromDoubleHi(from.floatReg(), a3);
         } else {
             MOZ_ASSERT(to.isMemory());
             masm.storeDouble(from.floatReg(), getAdjustedAddress(to));
         }
     } else if (to.isFloatReg()) {
         MOZ_ASSERT(from.isMemory());
         masm.loadDouble(getAdjustedAddress(from), to.floatReg());
-    } else if (to.isGeneralReg()) {
+    } else if (to.isGeneralRegPair()) {
         // Used for passing double parameter in a2,a3 register pair.
         // Two moves are added for one double parameter by
-        // MacroAssemblerMIPSCompat::passABIArg
-        if (from.isMemory()) {
-            if(to.reg() == a2)
-                masm.loadPtr(getAdjustedAddress(from), a2);
-            else if(to.reg() == a3)
-                masm.loadPtr(Address(from.base(), getAdjustedOffset(from) + sizeof(uint32_t)), a3);
-            else
-                MOZ_CRASH("Invalid emitDoubleMove arguments.");
-        } else {
-            // Used for moving a double parameter from the same source. See Bug 1123874.
-            if(to.reg() == a2 || to.reg() == a3)
-                masm.ma_move(to.reg(), from.reg());
-            else
-                MOZ_CRASH("Invalid emitDoubleMove arguments.");
-        }
+        // MacroAssembler::passABIArg
+        MOZ_ASSERT(from.isMemory());
+        MOZ_ASSERT(to.evenReg() == a2 && to.oddReg() == a3,
+                   "Invalid emitDoubleMove arguments.");
+        masm.loadPtr(getAdjustedAddress(from), a2);
+        masm.loadPtr(Address(from.base(), getAdjustedOffset(from) + sizeof(uint32_t)), a3);
     } else {
         MOZ_ASSERT(from.isMemory());
         MOZ_ASSERT(to.isMemory());
         masm.loadDouble(getAdjustedAddress(from), ScratchDoubleReg);
         masm.storeDouble(ScratchDoubleReg, getAdjustedAddress(to));
     }
 }
 
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -242,17 +242,17 @@ JitRuntime::generateEnterJIT(JSContext* 
 
         // No GC things to mark, push a bare token.
         masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
 
         masm.reserveStack(2 * sizeof(uintptr_t));
         masm.storePtr(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame
         masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode
 
-        masm.setupUnalignedABICall(3, scratch);
+        masm.setupUnalignedABICall(scratch);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
 
         regs.add(OsrFrameReg);
         regs.add(scratch);
         regs.add(numStackValues);
@@ -369,17 +369,17 @@ JitRuntime::generateInvalidator(JSContex
 
     // Reserve place for return value and BailoutInfo pointer
     masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
     // Pass pointer to return value.
     masm.ma_addu(a1, StackPointer, Imm32(sizeof(uintptr_t)));
     // Pass pointer to BailoutInfo
     masm.movePtr(StackPointer, a2);
 
-    masm.setupAlignedABICall(3);
+    masm.setupAlignedABICall();
     masm.passABIArg(a0);
     masm.passABIArg(a1);
     masm.passABIArg(a2);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout));
 
     masm.loadPtr(Address(StackPointer, 0), a2);
     masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), a1);
     // Remove the return address, the IonScript, the register state
@@ -605,17 +605,17 @@ GenerateBailoutThunk(JSContext* cx, Macr
 {
     PushBailoutFrame(masm, frameClass, a0);
 
     // Put pointer to BailoutInfo
     masm.subPtr(Imm32(bailoutInfoOutParamSize), StackPointer);
     masm.storePtr(ImmPtr(nullptr), Address(StackPointer, 0));
     masm.movePtr(StackPointer, a1);
 
-    masm.setupAlignedABICall(2);
+    masm.setupAlignedABICall();
     masm.passABIArg(a0);
     masm.passABIArg(a1);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout));
 
     // Get BailoutInfo pointer
     masm.loadPtr(Address(StackPointer, 0), a2);
 
     // Remove both the bailout frame and the topmost Ion frame's stack.
@@ -761,17 +761,17 @@ JitRuntime::generateVMWrapper(JSContext*
     }
     // Reserve stack for double sized args that are copied to be aligned.
     outParamOffset += f.doubleByRefArgs() * sizeof(double);
 
     Register doubleArgs = t0;
     masm.reserveStack(outParamOffset);
     masm.movePtr(StackPointer, doubleArgs);
 
-    masm.setupAlignedABICall(f.argc());
+    masm.setupAlignedABICall();
     masm.passABIArg(cxreg);
 
     size_t argDisp = 0;
     size_t doubleArgDisp = 0;
 
     // Copy any arguments.
     for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
         MoveOperand from;
@@ -905,17 +905,17 @@ JitRuntime::generatePreBarrier(JSContext
         save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                            FloatRegisterSet());
     }
     masm.PushRegsInMask(save);
 
     MOZ_ASSERT(PreBarrierReg == a1);
     masm.movePtr(ImmPtr(cx->runtime()), a0);
 
-    masm.setupUnalignedABICall(2, a2);
+    masm.setupUnalignedABICall(a2);
     masm.passABIArg(a0);
     masm.passABIArg(a1);
     masm.callWithABI(IonMarkFunction(type));
 
     masm.PopRegsInMask(save);
     masm.ret();
 
     Linker linker(masm);
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -190,23 +190,18 @@ class MacroAssemblerNone : public Assemb
     void checkStackAlignment() { MOZ_CRASH(); }
     uint32_t currentOffset() { MOZ_CRASH(); }
     uint32_t actualOffset(uint32_t) { MOZ_CRASH(); }
     uint32_t labelOffsetToPatchOffset(uint32_t) { MOZ_CRASH(); }
     CodeOffsetLabel labelForPatch() { MOZ_CRASH(); }
 
     template <typename T> void call(T) { MOZ_CRASH(); }
     template <typename T, typename S> void call(T, S) { MOZ_CRASH(); }
-    template <typename T> void callWithABI(T, MoveOp::Type v = MoveOp::GENERAL) { MOZ_CRASH(); }
     void callAndPushReturnAddress(Label* label) { MOZ_CRASH(); }
 
-    void setupAlignedABICall(uint32_t) { MOZ_CRASH(); }
-    void setupUnalignedABICall(uint32_t, Register) { MOZ_CRASH(); }
-    template <typename T> void passABIArg(T, MoveOp::Type v = MoveOp::GENERAL) { MOZ_CRASH(); }
-
     void callWithExitFrame(Label*) { MOZ_CRASH(); }
     void callWithExitFrame(JitCode*) { MOZ_CRASH(); }
     void callWithExitFrame(JitCode*, Register) { MOZ_CRASH(); }
 
     void callJit(Register callee) { MOZ_CRASH(); }
     void callJitFromAsmJS(Register callee) { MOZ_CRASH(); }
 
     void nop() { MOZ_CRASH(); }
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -1446,17 +1446,17 @@ CodeGeneratorShared::visitOutOfLineTrunc
     if (ool->needFloat32Conversion()) {
         MOZ_ASSERT(src.isSingle());
         masm.push(src);
         masm.convertFloat32ToDouble(src, src);
         src = src.asDouble();
     }
 #endif
 
-    masm.setupUnalignedABICall(1, dest);
+    masm.setupUnalignedABICall(dest);
     masm.passABIArg(src, MoveOp::DOUBLE);
     if (gen->compilingAsmJS())
         masm.callWithABI(AsmJSImm_ToInt32);
     else
         masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
     masm.storeCallResult(dest);
 
 #if !defined(JS_CODEGEN_ARM) && !defined(JS_CODEGEN_ARM64)
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -6,16 +6,17 @@
 
 #include "jit/x64/CodeGenerator-x64.h"
 
 #include "jit/IonCaches.h"
 #include "jit/MIR.h"
 
 #include "jsscriptinlines.h"
 
+#include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 CodeGeneratorX64::CodeGeneratorX64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
   : CodeGeneratorX86Shared(gen, graph, masm)
 {
@@ -975,16 +976,16 @@ CodeGeneratorX64::visitOutOfLineRandom(O
     regs.add(ReturnFloat32Reg);
     regs.add(ReturnDoubleReg);
     regs.add(ReturnInt32x4Reg);
     regs.add(ReturnFloat32x4Reg);
     saveVolatile(regs);
 
     masm.loadJSContext(temp);
 
-    masm.setupUnalignedABICall(1, temp2);
+    masm.setupUnalignedABICall(temp2);
     masm.passABIArg(temp);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
 
     restoreVolatile(regs);
 
     masm.jump(ool->rejoin());
 }
--- a/js/src/jit/x64/MacroAssembler-x64.cpp
+++ b/js/src/jit/x64/MacroAssembler-x64.cpp
@@ -179,232 +179,26 @@ MacroAssemblerX64::finish()
           default: MOZ_CRASH("unexpected SimdConstant type");
         }
     }
 
     MacroAssemblerX86Shared::finish();
 }
 
 void
-MacroAssemblerX64::setupABICall(uint32_t args)
-{
-    MOZ_ASSERT(!inCall_);
-    inCall_ = true;
-
-    args_ = args;
-    passedIntArgs_ = 0;
-    passedFloatArgs_ = 0;
-    stackForCall_ = ShadowStackSpace;
-}
-
-void
-MacroAssemblerX64::setupAlignedABICall(uint32_t args)
-{
-    setupABICall(args);
-    dynamicAlignment_ = false;
-}
-
-void
-MacroAssemblerX64::setupUnalignedABICall(uint32_t args, Register scratch)
-{
-    setupABICall(args);
-    dynamicAlignment_ = true;
-
-    movq(rsp, scratch);
-    andq(Imm32(~(ABIStackAlignment - 1)), rsp);
-    push(scratch);
-}
-
-void
-MacroAssemblerX64::passABIArg(const MoveOperand& from, MoveOp::Type type)
-{
-    MoveOperand to;
-    switch (type) {
-      case MoveOp::FLOAT32:
-      case MoveOp::DOUBLE: {
-        FloatRegister dest;
-        if (GetFloatArgReg(passedIntArgs_, passedFloatArgs_++, &dest)) {
-            // Convert to the right type of register.
-            if (type == MoveOp::FLOAT32)
-                dest = dest.asSingle();
-            if (from.isFloatReg() && from.floatReg() == dest) {
-                // Nothing to do; the value is in the right register already
-                return;
-            }
-            to = MoveOperand(dest);
-        } else {
-            to = MoveOperand(StackPointer, stackForCall_);
-            switch (type) {
-              case MoveOp::FLOAT32: stackForCall_ += sizeof(float);  break;
-              case MoveOp::DOUBLE:  stackForCall_ += sizeof(double); break;
-              default: MOZ_CRASH("Unexpected float register class argument type");
-            }
-        }
-        break;
-      }
-      case MoveOp::GENERAL: {
-        Register dest;
-        if (GetIntArgReg(passedIntArgs_++, passedFloatArgs_, &dest)) {
-            if (from.isGeneralReg() && from.reg() == dest) {
-                // Nothing to do; the value is in the right register already
-                return;
-            }
-            to = MoveOperand(dest);
-        } else {
-            to = MoveOperand(StackPointer, stackForCall_);
-            stackForCall_ += sizeof(int64_t);
-        }
-        break;
-      }
-      default:
-        MOZ_CRASH("Unexpected argument type");
-    }
-
-    enoughMemory_ = moveResolver_.addMove(from, to, type);
-}
-
-void
-MacroAssemblerX64::passABIArg(Register reg)
-{
-    passABIArg(MoveOperand(reg), MoveOp::GENERAL);
-}
-
-void
-MacroAssemblerX64::passABIArg(FloatRegister reg, MoveOp::Type type)
-{
-    passABIArg(MoveOperand(reg), type);
-}
-
-void
-MacroAssemblerX64::callWithABIPre(uint32_t* stackAdjust)
-{
-    MOZ_ASSERT(inCall_);
-    MOZ_ASSERT(args_ == passedIntArgs_ + passedFloatArgs_);
-
-    if (dynamicAlignment_) {
-        *stackAdjust = stackForCall_
-                     + ComputeByteAlignment(stackForCall_ + sizeof(intptr_t),
-                                            ABIStackAlignment);
-    } else {
-        *stackAdjust = stackForCall_
-                     + ComputeByteAlignment(stackForCall_ + asMasm().framePushed(),
-                                            ABIStackAlignment);
-    }
-
-    asMasm().reserveStack(*stackAdjust);
-
-    // Position all arguments.
-    {
-        enoughMemory_ &= moveResolver_.resolve();
-        if (!enoughMemory_)
-            return;
-
-        MoveEmitter emitter(asMasm());
-        emitter.emit(moveResolver_);
-        emitter.finish();
-    }
-
-#ifdef DEBUG
-    {
-        Label good;
-        testPtr(rsp, Imm32(ABIStackAlignment - 1));
-        j(Equal, &good);
-        breakpoint();
-        bind(&good);
-    }
-#endif
-}
-
-void
-MacroAssemblerX64::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
-{
-    asMasm().freeStack(stackAdjust);
-    if (dynamicAlignment_)
-        pop(rsp);
-
-    MOZ_ASSERT(inCall_);
-    inCall_ = false;
-}
-
-void
-MacroAssemblerX64::callWithABI(void* fun, MoveOp::Type result)
-{
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(ImmPtr(fun));
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerX64::callWithABI(AsmJSImmPtr imm, MoveOp::Type result)
-{
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(imm);
-    callWithABIPost(stackAdjust, result);
-}
-
-static bool
-IsIntArgReg(Register reg)
-{
-    for (uint32_t i = 0; i < NumIntArgRegs; i++) {
-        if (IntArgRegs[i] == reg)
-            return true;
-    }
-
-    return false;
-}
-
-void
-MacroAssemblerX64::callWithABI(Address fun, MoveOp::Type result)
-{
-    if (IsIntArgReg(fun.base)) {
-        // Callee register may be clobbered for an argument. Move the callee to
-        // r10, a volatile, non-argument register.
-        moveResolver_.addMove(MoveOperand(fun.base), MoveOperand(r10), MoveOp::GENERAL);
-        fun.base = r10;
-    }
-
-    MOZ_ASSERT(!IsIntArgReg(fun.base));
-
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(fun);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerX64::callWithABI(Register fun, MoveOp::Type result)
-{
-    if (IsIntArgReg(fun)) {
-        // Callee register may be clobbered for an argument. Move the callee to
-        // r10, a volatile, non-argument register.
-        moveResolver_.addMove(MoveOperand(fun), MoveOperand(r10), MoveOp::GENERAL);
-        fun = r10;
-    }
-
-    MOZ_ASSERT(!IsIntArgReg(fun));
-
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(fun);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
 MacroAssemblerX64::handleFailureWithHandlerTail(void* handler)
 {
     // Reserve space for exception information.
     subq(Imm32(sizeof(ResumeFromException)), rsp);
     movq(rsp, rax);
 
     // Call the handler.
-    setupUnalignedABICall(1, rcx);
-    passABIArg(rax);
-    callWithABI(handler);
+    asMasm().setupUnalignedABICall(rcx);
+    asMasm().passABIArg(rax);
+    asMasm().callWithABI(handler);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
     loadPtr(Address(rsp, offsetof(ResumeFromException, kind)), rax);
@@ -603,9 +397,120 @@ MacroAssembler::reserveStack(uint32_t am
             store32(Imm32(0), Address(StackPointer, 0));
             amountLeft -= 4096;
         }
         subq(Imm32(amountLeft), StackPointer);
     }
     framePushed_ += amount;
 }
 
+
+// ===============================================================
+// ABI function calls.
+
+void
+MacroAssembler::setupUnalignedABICall(Register scratch)
+{
+    setupABICall();
+    dynamicAlignment_ = true;
+
+    movq(rsp, scratch);
+    andq(Imm32(~(ABIStackAlignment - 1)), rsp);
+    push(scratch);
+}
+
+void
+MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS)
+{
+    MOZ_ASSERT(inCall_);
+    uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
+
+    if (dynamicAlignment_) {
+        // sizeof(intptr_t) accounts for the saved stack pointer pushed by
+        // setupUnalignedABICall.
+        stackForCall += ComputeByteAlignment(stackForCall + sizeof(intptr_t),
+                                             ABIStackAlignment);
+    } else {
+        static_assert(sizeof(AsmJSFrame) % ABIStackAlignment == 0,
+                      "AsmJSFrame should be part of the stack alignment.");
+        stackForCall += ComputeByteAlignment(stackForCall + framePushed(),
+                                             ABIStackAlignment);
+    }
+
+    *stackAdjust = stackForCall;
+    reserveStack(stackForCall);
+
+    // Position all arguments.
+    {
+        enoughMemory_ &= moveResolver_.resolve();
+        if (!enoughMemory_)
+            return;
+
+        MoveEmitter emitter(*this);
+        emitter.emit(moveResolver_);
+        emitter.finish();
+    }
+
+    assertStackAlignment(ABIStackAlignment);
+}
+
+void
+MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
+{
+    freeStack(stackAdjust);
+    if (dynamicAlignment_)
+        pop(rsp);
+
+#ifdef DEBUG
+    MOZ_ASSERT(inCall_);
+    inCall_ = false;
+#endif
+}
+
+static bool
+IsIntArgReg(Register reg)
+{
+    for (uint32_t i = 0; i < NumIntArgRegs; i++) {
+        if (IntArgRegs[i] == reg)
+            return true;
+    }
+
+    return false;
+}
+
+void
+MacroAssembler::callWithABINoProfiler(Register fun, MoveOp::Type result)
+{
+    if (IsIntArgReg(fun)) {
+        // Callee register may be clobbered for an argument. Move the callee to
+        // r10, a volatile, non-argument register.
+        moveResolver_.addMove(MoveOperand(fun), MoveOperand(r10), MoveOp::GENERAL);
+        fun = r10;
+    }
+
+    MOZ_ASSERT(!IsIntArgReg(fun));
+
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(fun);
+    callWithABIPost(stackAdjust, result);
+}
+
+void
+MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
+{
+    Address safeFun = fun;
+    if (IsIntArgReg(safeFun.base)) {
+        // Callee register may be clobbered for an argument. Move the callee to
+        // r10, a volatile, non-argument register.
+        moveResolver_.addMove(MoveOperand(fun.base), MoveOperand(r10), MoveOp::GENERAL);
+        safeFun.base = r10;
+    }
+
+    MOZ_ASSERT(!IsIntArgReg(safeFun.base));
+
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(safeFun);
+    callWithABIPost(stackAdjust, result);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -35,25 +35,16 @@ struct ImmTag : public Imm32
 class MacroAssemblerX64 : public MacroAssemblerX86Shared
 {
   private:
     // Perform a downcast. Should be removed by Bug 996602.
     MacroAssembler& asMasm();
     const MacroAssembler& asMasm() const;
 
   private:
-    // Number of bytes the stack is adjusted inside a call to C. Calls to C may
-    // not be nested.
-    bool inCall_;
-    uint32_t args_;
-    uint32_t passedIntArgs_;
-    uint32_t passedFloatArgs_;
-    uint32_t stackForCall_;
-    bool dynamicAlignment_;
-
     // These use SystemAllocPolicy since asm.js releases memory after each
     // function is compiled, and these need to live until after all functions
     // are compiled.
     struct Double {
         double value;
         NonAssertingLabel uses;
         explicit Double(double value) : value(value) {}
     };
@@ -78,30 +69,24 @@ class MacroAssemblerX64 : public MacroAs
 
         explicit SimdData(const SimdConstant& v) : value(v) {}
         SimdConstant::Type type() { return value.type(); }
     };
     Vector<SimdData, 0, SystemAllocPolicy> simds_;
     typedef HashMap<SimdConstant, size_t, SimdConstant, SystemAllocPolicy> SimdMap;
     SimdMap simdMap_;
 
-    void setupABICall(uint32_t arg);
-
-  protected:
-    MoveResolver moveResolver_;
-
   public:
     using MacroAssemblerX86Shared::callWithExitFrame;
     using MacroAssemblerX86Shared::branch32;
     using MacroAssemblerX86Shared::branchTest32;
     using MacroAssemblerX86Shared::load32;
     using MacroAssemblerX86Shared::store32;
 
     MacroAssemblerX64()
-      : inCall_(false)
     {
     }
 
     // The buffer is about to be linked, make sure any constant pools or excess
     // bookkeeping has been flushed to the instruction stream.
     void finish();
 
     /////////////////////////////////////////////////////////////////
@@ -1359,50 +1344,17 @@ class MacroAssemblerX64 : public MacroAs
         jump(&done);
 
         bind(&isDouble);
         unboxDouble(source, dest);
 
         bind(&done);
     }
 
-    // Setup a call to C/C++ code, given the number of general arguments it
-    // takes. Note that this only supports cdecl.
-    //
-    // In order for alignment to work correctly, the MacroAssembler must have a
-    // consistent view of the stack displacement. It is okay to call "push"
-    // manually, however, if the stack alignment were to change, the macro
-    // assembler should be notified before starting a call.
-    void setupAlignedABICall(uint32_t args);
-
-    // Sets up an ABI call for when the alignment is not known. This may need a
-    // scratch register.
-    void setupUnalignedABICall(uint32_t args, Register scratch);
-
-    // Arguments must be assigned to a C/C++ call in order. They are moved
-    // in parallel immediately before performing the call. This process may
-    // temporarily use more stack, in which case esp-relative addresses will be
-    // automatically adjusted. It is extremely important that esp-relative
-    // addresses are computed *after* setupABICall(). Furthermore, no
-    // operations should be emitted while setting arguments.
-    void passABIArg(const MoveOperand& from, MoveOp::Type type);
-    void passABIArg(Register reg);
-    void passABIArg(FloatRegister reg, MoveOp::Type type);
-
-  private:
-    void callWithABIPre(uint32_t* stackAdjust);
-    void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result);
-
   public:
-    // Emits a call to a C/C++ function, resolving all argument moves.
-    void callWithABI(void* fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(AsmJSImmPtr imm, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(Address fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
-
     void handleFailureWithHandlerTail(void* handler);
 
     void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
         shlq(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
         orq(Imm32(type), frameSizeReg);
     }
 
     void callWithExitFrame(JitCode* target, Register dynStack);
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -228,17 +228,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         // No GC things to mark, push a bare token.
         masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
 
         regs.add(valuesSize);
 
         masm.push(framePtr);
         masm.push(reg_code);
 
-        masm.setupUnalignedABICall(3, scratch);
+        masm.setupUnalignedABICall(scratch);
         masm.passABIArg(framePtr); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
 
         masm.pop(reg_code);
         masm.pop(framePtr);
 
@@ -357,17 +357,17 @@ JitRuntime::generateInvalidator(JSContex
     // Make space for InvalidationBailout's frameSize outparam.
     masm.reserveStack(sizeof(size_t));
     masm.movq(rsp, rbx);
 
     // Make space for InvalidationBailout's bailoutInfo outparam.
     masm.reserveStack(sizeof(void*));
     masm.movq(rsp, r9);
 
-    masm.setupUnalignedABICall(3, rdx);
+    masm.setupUnalignedABICall(rdx);
     masm.passABIArg(rax);
     masm.passABIArg(rbx);
     masm.passABIArg(r9);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout));
 
     masm.pop(r9); // Get the bailoutInfo outparam.
     masm.pop(rbx); // Get the frameSize outparam.
 
@@ -583,17 +583,17 @@ GenerateBailoutThunk(JSContext* cx, Macr
 {
     PushBailoutFrame(masm, r8);
 
     // Make space for Bailout's bailoutInfo outparam.
     masm.reserveStack(sizeof(void*));
     masm.movq(rsp, r9);
 
     // Call the bailout function.
-    masm.setupUnalignedABICall(2, rax);
+    masm.setupUnalignedABICall(rax);
     masm.passABIArg(r8);
     masm.passABIArg(r9);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout));
 
     masm.pop(r9); // Get the bailoutInfo outparam.
 
     // Stack is:
     //     [frame]
@@ -709,17 +709,17 @@ JitRuntime::generateVMWrapper(JSContext*
         masm.movq(esp, outReg);
         break;
 
       default:
         MOZ_ASSERT(f.outParam == Type_Void);
         break;
     }
 
-    masm.setupUnalignedABICall(f.argc(), regs.getAny());
+    masm.setupUnalignedABICall(regs.getAny());
     masm.passABIArg(cxreg);
 
     size_t argDisp = 0;
 
     // Copy arguments.
     for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
         MoveOperand from;
         switch (f.argProperties(explicitArg)) {
@@ -826,17 +826,17 @@ JitRuntime::generatePreBarrier(JSContext
     LiveRegisterSet regs =
         LiveRegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                              FloatRegisterSet(FloatRegisters::VolatileMask));
     masm.PushRegsInMask(regs);
 
     MOZ_ASSERT(PreBarrierReg == rdx);
     masm.mov(ImmPtr(cx->runtime()), rcx);
 
-    masm.setupUnalignedABICall(2, rax);
+    masm.setupUnalignedABICall(rax);
     masm.passABIArg(rcx);
     masm.passABIArg(rdx);
     masm.callWithABI(IonMarkFunction(type));
 
     masm.PopRegsInMask(regs);
     masm.ret();
 
     Linker linker(masm);
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -15,16 +15,17 @@
 #include "jit/IonCaches.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "js/Conversions.h"
 #include "vm/Shape.h"
 
 #include "jsscriptinlines.h"
 
+#include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::BitwiseCast;
 using mozilla::DebugOnly;
 using mozilla::FloatingPoint;
@@ -1026,17 +1027,17 @@ CodeGeneratorX86::visitOutOfLineTruncate
         masm.j(Assembler::Parity, &fail);
         masm.j(Assembler::Equal, ool->rejoin());
     }
 
     masm.bind(&fail);
     {
         saveVolatile(output);
 
-        masm.setupUnalignedABICall(1, output);
+        masm.setupUnalignedABICall(output);
         masm.passABIArg(input, MoveOp::DOUBLE);
         if (gen->compilingAsmJS())
             masm.callWithABI(AsmJSImm_ToInt32);
         else
             masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
         masm.storeCallResult(output);
 
         restoreVolatile(output);
@@ -1116,17 +1117,17 @@ CodeGeneratorX86::visitOutOfLineTruncate
         masm.j(Assembler::Equal, ool->rejoin());
     }
 
     masm.bind(&fail);
     {
         saveVolatile(output);
 
         masm.push(input);
-        masm.setupUnalignedABICall(1, output);
+        masm.setupUnalignedABICall(output);
         masm.vcvtss2sd(input, input, input);
         masm.passABIArg(input.asDouble(), MoveOp::DOUBLE);
 
         if (gen->compilingAsmJS())
             masm.callWithABI(AsmJSImm_ToInt32);
         else
             masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
 
@@ -1142,14 +1143,14 @@ CodeGeneratorX86::visitOutOfLineTruncate
 void
 CodeGeneratorX86::visitRandom(LRandom* ins)
 {
     Register temp = ToRegister(ins->temp());
     Register temp2 = ToRegister(ins->temp2());
 
     masm.loadJSContext(temp);
 
-    masm.setupUnalignedABICall(1, temp2);
+    masm.setupUnalignedABICall(temp2);
     masm.passABIArg(temp);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
 
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 }
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -200,181 +200,26 @@ MacroAssemblerX86::finish()
         }
         addCodeLabel(cl);
         if (!enoughMemory_)
             return;
     }
 }
 
 void
-MacroAssemblerX86::setupABICall(uint32_t args)
-{
-    MOZ_ASSERT(!inCall_);
-    inCall_ = true;
-
-    args_ = args;
-    passedArgs_ = 0;
-    stackForCall_ = 0;
-}
-
-void
-MacroAssemblerX86::setupAlignedABICall(uint32_t args)
-{
-    setupABICall(args);
-    dynamicAlignment_ = false;
-}
-
-void
-MacroAssemblerX86::setupUnalignedABICall(uint32_t args, Register scratch)
-{
-    setupABICall(args);
-    dynamicAlignment_ = true;
-
-    movl(esp, scratch);
-    andl(Imm32(~(ABIStackAlignment - 1)), esp);
-    push(scratch);
-}
-
-void
-MacroAssemblerX86::passABIArg(const MoveOperand& from, MoveOp::Type type)
-{
-    ++passedArgs_;
-    MoveOperand to = MoveOperand(StackPointer, stackForCall_);
-    switch (type) {
-      case MoveOp::FLOAT32: stackForCall_ += sizeof(float); break;
-      case MoveOp::DOUBLE:  stackForCall_ += sizeof(double); break;
-      case MoveOp::INT32:   stackForCall_ += sizeof(int32_t); break;
-      case MoveOp::GENERAL: stackForCall_ += sizeof(intptr_t); break;
-      default: MOZ_CRASH("Unexpected argument type");
-    }
-    enoughMemory_ &= moveResolver_.addMove(from, to, type);
-}
-
-void
-MacroAssemblerX86::passABIArg(Register reg)
-{
-    passABIArg(MoveOperand(reg), MoveOp::GENERAL);
-}
-
-void
-MacroAssemblerX86::passABIArg(FloatRegister reg, MoveOp::Type type)
-{
-    passABIArg(MoveOperand(reg), type);
-}
-
-void
-MacroAssemblerX86::callWithABIPre(uint32_t* stackAdjust)
-{
-    MOZ_ASSERT(inCall_);
-    MOZ_ASSERT(args_ == passedArgs_);
-
-    if (dynamicAlignment_) {
-        *stackAdjust = stackForCall_
-                     + ComputeByteAlignment(stackForCall_ + sizeof(intptr_t),
-                                            ABIStackAlignment);
-    } else {
-        *stackAdjust = stackForCall_
-                     + ComputeByteAlignment(stackForCall_ + asMasm().framePushed(),
-                                            ABIStackAlignment);
-    }
-
-    asMasm().reserveStack(*stackAdjust);
-
-    // Position all arguments.
-    {
-        enoughMemory_ &= moveResolver_.resolve();
-        if (!enoughMemory_)
-            return;
-
-        MoveEmitter emitter(asMasm());
-        emitter.emit(moveResolver_);
-        emitter.finish();
-    }
-
-#ifdef DEBUG
-    {
-        // Check call alignment.
-        Label good;
-        test32(esp, Imm32(ABIStackAlignment - 1));
-        j(Equal, &good);
-        breakpoint();
-        bind(&good);
-    }
-#endif
-}
-
-void
-MacroAssemblerX86::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
-{
-    asMasm().freeStack(stackAdjust);
-    if (result == MoveOp::DOUBLE) {
-        asMasm().reserveStack(sizeof(double));
-        fstp(Operand(esp, 0));
-        loadDouble(Operand(esp, 0), ReturnDoubleReg);
-        asMasm().freeStack(sizeof(double));
-    } else if (result == MoveOp::FLOAT32) {
-        asMasm().reserveStack(sizeof(float));
-        fstp32(Operand(esp, 0));
-        loadFloat32(Operand(esp, 0), ReturnFloat32Reg);
-        asMasm().freeStack(sizeof(float));
-    }
-    if (dynamicAlignment_)
-        pop(esp);
-
-    MOZ_ASSERT(inCall_);
-    inCall_ = false;
-}
-
-void
-MacroAssemblerX86::callWithABI(void* fun, MoveOp::Type result)
-{
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(ImmPtr(fun));
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerX86::callWithABI(AsmJSImmPtr fun, MoveOp::Type result)
-{
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(fun);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerX86::callWithABI(const Address& fun, MoveOp::Type result)
-{
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(fun);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
-MacroAssemblerX86::callWithABI(Register fun, MoveOp::Type result)
-{
-    uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
-    asMasm().call(fun);
-    callWithABIPost(stackAdjust, result);
-}
-
-void
 MacroAssemblerX86::handleFailureWithHandlerTail(void* handler)
 {
     // Reserve space for exception information.
     subl(Imm32(sizeof(ResumeFromException)), esp);
     movl(esp, eax);
 
     // Call the handler.
-    setupUnalignedABICall(1, ecx);
-    passABIArg(eax);
-    callWithABI(handler);
+    asMasm().setupUnalignedABICall(ecx);
+    asMasm().passABIArg(eax);
+    asMasm().callWithABI(handler);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
     loadPtr(Address(esp, offsetof(ResumeFromException, kind)), eax);
@@ -584,9 +429,99 @@ MacroAssembler::reserveStack(uint32_t am
             store32(Imm32(0), Address(StackPointer, 0));
             amountLeft -= 4096;
         }
         subl(Imm32(amountLeft), StackPointer);
     }
     framePushed_ += amount;
 }
 
+// ===============================================================
+// ABI function calls.
+
+void
+MacroAssembler::setupUnalignedABICall(Register scratch)
+{
+    setupABICall();
+    dynamicAlignment_ = true;
+
+    movl(esp, scratch);
+    andl(Imm32(~(ABIStackAlignment - 1)), esp);
+    push(scratch);
+}
+
+void
+MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS)
+{
+    MOZ_ASSERT(inCall_);
+    uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar();
+
+    if (dynamicAlignment_) {
+        // sizeof(intptr_t) accounts for the saved stack pointer pushed by
+        // setupUnalignedABICall.
+        stackForCall += ComputeByteAlignment(stackForCall + sizeof(intptr_t),
+                                             ABIStackAlignment);
+    } else {
+        uint32_t alignmentAtPrologue = callFromAsmJS ? sizeof(AsmJSFrame) : 0;
+        stackForCall += ComputeByteAlignment(stackForCall + framePushed() + alignmentAtPrologue,
+                                             ABIStackAlignment);
+    }
+
+    *stackAdjust = stackForCall;
+    reserveStack(stackForCall);
+
+    // Position all arguments.
+    {
+        enoughMemory_ &= moveResolver_.resolve();
+        if (!enoughMemory_)
+            return;
+
+        MoveEmitter emitter(*this);
+        emitter.emit(moveResolver_);
+        emitter.finish();
+    }
+
+    assertStackAlignment(ABIStackAlignment);
+}
+
+void
+MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
+{
+    freeStack(stackAdjust);
+    if (result == MoveOp::DOUBLE) {
+        reserveStack(sizeof(double));
+        fstp(Operand(esp, 0));
+        loadDouble(Operand(esp, 0), ReturnDoubleReg);
+        freeStack(sizeof(double));
+    } else if (result == MoveOp::FLOAT32) {
+        reserveStack(sizeof(float));
+        fstp32(Operand(esp, 0));
+        loadFloat32(Operand(esp, 0), ReturnFloat32Reg);
+        freeStack(sizeof(float));
+    }
+    if (dynamicAlignment_)
+        pop(esp);
+
+#ifdef DEBUG
+    MOZ_ASSERT(inCall_);
+    inCall_ = false;
+#endif
+}
+
+void
+MacroAssembler::callWithABINoProfiler(Register fun, MoveOp::Type result)
+{
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(fun);
+    callWithABIPost(stackAdjust, result);
+}
+
+void
+MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
+{
+    uint32_t stackAdjust;
+    callWithABIPre(&stackAdjust);
+    call(fun);
+    callWithABIPost(stackAdjust, result);
+}
+
 //}}} check_macroassembler_style
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -19,24 +19,16 @@ namespace jit {
 class MacroAssemblerX86 : public MacroAssemblerX86Shared
 {
   private:
     // Perform a downcast. Should be removed by Bug 996602.
     MacroAssembler& asMasm();
     const MacroAssembler& asMasm() const;
 
   private:
-    // Number of bytes the stack is adjusted inside a call to C. Calls to C may
-    // not be nested.
-    bool inCall_;
-    uint32_t args_;
-    uint32_t passedArgs_;
-    uint32_t stackForCall_;
-    bool dynamicAlignment_;
-
     struct Double {
         double value;
         AbsoluteLabel uses;
         Double(double value) : value(value) {}
     };
     Vector<Double, 0, SystemAllocPolicy> doubles_;
     struct Float {
         float value;
@@ -94,17 +86,16 @@ class MacroAssemblerX86 : public MacroAs
     using MacroAssemblerX86Shared::callWithExitFrame;
     using MacroAssemblerX86Shared::branch32;
     using MacroAssemblerX86Shared::branchTest32;
     using MacroAssemblerX86Shared::load32;
     using MacroAssemblerX86Shared::store32;
     using MacroAssemblerX86Shared::call;
 
     MacroAssemblerX86()
-      : inCall_(false)
     {
     }
 
     // The buffer is about to be linked, make sure any constant pools or excess
     // bookkeeping has been flushed to the instruction stream.
     void finish();
 
     /////////////////////////////////////////////////////////////////
@@ -1136,50 +1127,17 @@ class MacroAssemblerX86 : public MacroAs
         jump(&done);
 
         bind(&isDouble);
         unboxDouble(source, dest);
 
         bind(&done);
     }
 
-    // Setup a call to C/C++ code, given the number of general arguments it
-    // takes. Note that this only supports cdecl.
-    //
-    // In order for alignment to work correctly, the MacroAssembler must have a
-    // consistent view of the stack displacement. It is okay to call "push"
-    // manually, however, if the stack alignment were to change, the macro
-    // assembler should be notified before starting a call.
-    void setupAlignedABICall(uint32_t args);
-
-    // Sets up an ABI call for when the alignment is not known. This may need a
-    // scratch register.
-    void setupUnalignedABICall(uint32_t args, Register scratch);
-
-    // Arguments must be assigned to a C/C++ call in order. They are moved
-    // in parallel immediately before performing the call. This process may
-    // temporarily use more stack, in which case esp-relative addresses will be
-    // automatically adjusted. It is extremely important that esp-relative
-    // addresses are computed *after* setupABICall(). Furthermore, no
-    // operations should be emitted while setting arguments.
-    void passABIArg(const MoveOperand& from, MoveOp::Type type);
-    void passABIArg(Register reg);
-    void passABIArg(FloatRegister reg, MoveOp::Type type);
-
-  private:
-    void callWithABIPre(uint32_t* stackAdjust);
-    void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result);
-
   public:
-    // Emits a call to a C/C++ function, resolving all argument moves.
-    void callWithABI(void* fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(AsmJSImmPtr fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(const Address& fun, MoveOp::Type result = MoveOp::GENERAL);
-    void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
-
     // Used from within an Exit frame to handle a pending exception.
     void handleFailureWithHandlerTail(void* handler);
 
     void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
         shll(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
         orl(Imm32(type), frameSizeReg);
     }
 
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -15,16 +15,18 @@
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 #include "jit/x86/SharedICHelpers-x86.h"
 
 #include "jsscriptinlines.h"
 
+#include "jit/MacroAssembler-inl.h"
+
 using namespace js;
 using namespace js::jit;
 
 // All registers to save and restore. This includes the stack pointer, since we
 // use the ability to reference register values on the stack by index.
 static const LiveRegisterSet AllRegs =
   LiveRegisterSet(GeneralRegisterSet(Registers::AllMask),
                        FloatRegisterSet(FloatRegisters::AllMask));
@@ -217,17 +219,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.push(scratch); // Fake return address.
         masm.push(Imm32(0));
         // No GC things to mark on the stack, push a bare token.
         masm.enterFakeExitFrame(ExitFrameLayout::BareToken());
 
         masm.push(framePtr);
         masm.push(jitcode);
 
-        masm.setupUnalignedABICall(3, scratch);
+        masm.setupUnalignedABICall(scratch);
         masm.passABIArg(framePtr); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
 
         masm.pop(jitcode);
         masm.pop(framePtr);
 
@@ -348,17 +350,17 @@ JitRuntime::generateInvalidator(JSContex
     // Make space for InvalidationBailout's frameSize outparam.
     masm.reserveStack(sizeof(size_t));
     masm.movl(esp, ebx);
 
     // Make space for InvalidationBailout's bailoutInfo outparam.
     masm.reserveStack(sizeof(void*));
     masm.movl(esp, ecx);
 
-    masm.setupUnalignedABICall(3, edx);
+    masm.setupUnalignedABICall(edx);
     masm.passABIArg(eax);
     masm.passABIArg(ebx);
     masm.passABIArg(ecx);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout));
 
     masm.pop(ecx); // Get bailoutInfo outparam.
     masm.pop(ebx); // Get the frameSize outparam.
 
@@ -580,17 +582,17 @@ GenerateBailoutThunk(JSContext* cx, Macr
 {
     PushBailoutFrame(masm, frameClass, eax);
 
     // Make space for Bailout's baioutInfo outparam.
     masm.reserveStack(sizeof(void*));
     masm.movl(esp, ebx);
 
     // Call the bailout function. This will correct the size of the bailout.
-    masm.setupUnalignedABICall(2, ecx);
+    masm.setupUnalignedABICall(ecx);
     masm.passABIArg(eax);
     masm.passABIArg(ebx);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout));
 
     masm.pop(ecx); // Get bailoutInfo outparam.
 
     // Common size of stuff we've pushed.
     static const uint32_t BailoutDataSize = 0
@@ -728,17 +730,17 @@ JitRuntime::generateVMWrapper(JSContext*
         masm.movl(esp, outReg);
         break;
 
       default:
         MOZ_ASSERT(f.outParam == Type_Void);
         break;
     }
 
-    masm.setupUnalignedABICall(f.argc(), regs.getAny());
+    masm.setupUnalignedABICall(regs.getAny());
     masm.passABIArg(cxreg);
 
     size_t argDisp = 0;
 
     // Copy arguments.
     for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
         MoveOperand from;
         switch (f.argProperties(explicitArg)) {
@@ -852,17 +854,17 @@ JitRuntime::generatePreBarrier(JSContext
         save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
                                  FloatRegisterSet());
     }
     masm.PushRegsInMask(save);
 
     MOZ_ASSERT(PreBarrierReg == edx);
     masm.movl(ImmPtr(cx->runtime()), ecx);
 
-    masm.setupUnalignedABICall(2, eax);
+    masm.setupUnalignedABICall(eax);
     masm.passABIArg(ecx);
     masm.passABIArg(edx);
     masm.callWithABI(IonMarkFunction(type));
 
     masm.PopRegsInMask(save);
     masm.ret();
 
     Linker linker(masm);
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -8,16 +8,17 @@
 
 #include "jit/BaselineIC.h"
 #include "jit/JitCommon.h"
 #include "jit/Linker.h"
 
 #include "jsobjinlines.h"
 
 #include "gc/Nursery-inl.h"
+#include "jit/MacroAssembler-inl.h"
 #include "vm/Shape-inl.h"
 
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::PodCopy;
 using mozilla::UniquePtr;
 
 using namespace js;
@@ -155,17 +156,17 @@ UnboxedLayout::makeConstructorCode(JSCon
 
     LiveGeneralRegisterSet liveVolatileRegisters;
     liveVolatileRegisters.add(propertiesReg);
     if (object.volatile_())
         liveVolatileRegisters.add(object);
     masm.PushRegsInMask(liveVolatileRegisters);
 
     masm.mov(ImmPtr(cx->runtime()), scratch1);
-    masm.setupUnalignedABICall(2, scratch2);
+    masm.setupUnalignedABICall(scratch2);
     masm.passABIArg(scratch1);
     masm.passABIArg(object);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
 
     masm.PopRegsInMask(liveVolatileRegisters);
 
     masm.bind(&allocated);
 
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -25,26 +25,29 @@ XDRBuffer::freeBuffer()
 }
 
 bool
 XDRBuffer::grow(size_t n)
 {
     MOZ_ASSERT(n > size_t(limit - cursor));
 
     const size_t MIN_CAPACITY = 8192;
+    const size_t MAX_CAPACITY = size_t(INT32_MAX) + 1;
     size_t offset = cursor - base;
-    size_t newCapacity = mozilla::RoundUpPow2(offset + n);
-    if (newCapacity < MIN_CAPACITY)
-        newCapacity = MIN_CAPACITY;
-    if (isUint32Overflow(newCapacity)) {
+    MOZ_ASSERT(offset <= MAX_CAPACITY);
+    if (n > MAX_CAPACITY - offset) {
         js::gc::AutoSuppressGC suppressGC(cx());
         JS_ReportErrorNumber(cx(), GetErrorMessage, nullptr, JSMSG_TOO_BIG_TO_ENCODE);
         return false;
     }
+    size_t newCapacity = mozilla::RoundUpPow2(offset + n);
+    if (newCapacity < MIN_CAPACITY)
+        newCapacity = MIN_CAPACITY;
 
+    MOZ_ASSERT(newCapacity <= MAX_CAPACITY);
     void* data = js_realloc(base, newCapacity);
     if (!data) {
         ReportOutOfMemory(cx());
         return false;
     }
     base = static_cast<uint8_t*>(data);
     cursor = base + offset;
     limit = base + newCapacity;
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -80,20 +80,16 @@ class XDRBuffer {
             if (!grow(n))
                 return nullptr;
         }
         uint8_t* ptr = cursor;
         cursor += n;
         return ptr;
     }
 
-    static bool isUint32Overflow(size_t n) {
-        return size_t(-1) > size_t(UINT32_MAX) && n > size_t(UINT32_MAX);
-    }
-
     void freeBuffer();
 
   private:
     bool grow(size_t n);
 
     JSContext*  const context;
     uint8_t*    base;
     uint8_t*    cursor;
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -122,18 +122,19 @@ IsKeyboardRTL()
   if (bidiKeyboard) {
     bidiKeyboard->IsLangRTL(&isKeyboardRTL);
   }
   return isKeyboardRTL;
 }
 
 nsCaret::nsCaret()
 : mOverrideOffset(0)
+, mBlinkCount(-1)
+, mHideCount(0)
 , mIsBlinkOn(false)
-, mBlinkCount(-1)
 , mVisible(false)
 , mReadOnly(false)
 , mShowDuringSelection(false)
 , mIgnoreUserModify(true)
 {
 }
 
 nsCaret::~nsCaret()
@@ -261,17 +262,17 @@ void nsCaret::SetVisible(bool inMakeVisi
   mVisible = inMakeVisible;
   mIgnoreUserModify = mVisible;
   ResetBlinking();
   SchedulePaint();
 }
 
 bool nsCaret::IsVisible()
 {
-  if (!mVisible) {
+  if (!mVisible || mHideCount) {
     return false;
   }
 
   if (!mShowDuringSelection &&
       !(sSelectionCaretEnabled && sSelectionCaretsAffectCaret)) {
     Selection* selection = GetSelectionInternal();
     if (!selection) {
       return false;
@@ -298,16 +299,35 @@ bool nsCaret::IsVisible()
 
   if (IsMenuPopupHidingCaret()) {
     return false;
   }
 
   return true;
 }
 
+void nsCaret::AddForceHide()
+{
+  MOZ_ASSERT(mHideCount < UINT32_MAX);
+  if (++mHideCount > 1) {
+    return;
+  }
+  ResetBlinking();
+  SchedulePaint();
+}
+
+void nsCaret::RemoveForceHide()
+{
+  if (!mHideCount || --mHideCount) {
+    return;
+  }
+  ResetBlinking();
+  SchedulePaint();
+}
+
 void nsCaret::SetCaretReadOnly(bool inMakeReadonly)
 {
   mReadOnly = inMakeReadonly;
   ResetBlinking();
   SchedulePaint();
 }
 
 /* static */ nsRect
@@ -617,17 +637,17 @@ nsCaret::NotifySelectionChanged(nsIDOMDo
 
   return NS_OK;
 }
 
 void nsCaret::ResetBlinking()
 {
   mIsBlinkOn = true;
 
-  if (mReadOnly || !mVisible) {
+  if (mReadOnly || !mVisible || mHideCount) {
     StopBlinking();
     return;
   }
 
   if (mBlinkTimer) {
     mBlinkTimer->Cancel();
   } else {
     nsresult  err;
--- a/layout/base/nsCaret.h
+++ b/layout/base/nsCaret.h
@@ -73,16 +73,31 @@ class nsCaret final : public nsISelectio
     /** IsVisible will get the visibility of the caret.
      *  This returns false if the caret is hidden because it was set
      *  to not be visible, or because the selection is not collapsed, or
      *  because an open popup is hiding the caret.
      *  It does not take account of blinking or the caret being hidden
      *  because we're in non-editable/disabled content.
      */
     bool IsVisible();
+    /**
+     * AddForceHide() increases mHideCount and hide the caret even if
+     * SetVisible(true) has been or will be called.  This is useful when the
+     * caller wants to hide caret temporarily and it needs to cancel later.
+     * Especially, in the latter case, it's too difficult to decide if the
+     * caret should be actually visible or not because caret visible state
+     * is set from a lot of event handlers.  So, it's very stateful.
+     */
+    void AddForceHide();
+    /**
+     * RemoveForceHide() decreases mHideCount if it's over 0.
+     * If the value becomes 0, this may show the caret if SetVisible(true)
+     * has been called.
+     */
+    void RemoveForceHide();
     /** SetCaretReadOnly set the appearance of the caret
      *  @param inMakeReadonly true to show the caret in a 'read only' state,
      *         false to show the caret in normal, editing state
      */
     void SetCaretReadOnly(bool inMakeReadonly);
     /**
      * @param aVisibility true if the caret should be visible even when the
      * selection is not collapsed.
@@ -196,28 +211,33 @@ protected:
      * focus node instead.
      */
     nsCOMPtr<nsINode>     mOverrideContent;
     /**
      * The character offset to draw the caret at.
      * Ignored if mOverrideContent is null.
      */
     int32_t               mOverrideOffset;
-
-    /**
-     * mIsBlinkOn is true when we're in a blink cycle where the caret is on.
-     */
-    bool                  mIsBlinkOn;
     /**
      * mBlinkCount is used to control the number of times to blink the caret
      * before stopping the blink. This is reset each time we reset the
      * blinking.
      */
     int32_t               mBlinkCount;
     /**
+     * mHideCount is not 0, it means that somebody doesn't want the caret
+     * to be visible.  See AddForceHide() and RemoveForceHide().
+     */
+    uint32_t              mHideCount;
+
+    /**
+     * mIsBlinkOn is true when we're in a blink cycle where the caret is on.
+     */
+    bool                  mIsBlinkOn;
+    /**
      * mIsVisible is true when SetVisible was last called with 'true'.
      */
     bool                  mVisible;
     /**
      * mReadOnly is true when the caret is set to "read only" mode (i.e.,
      * it doesn't blink).
      */
     bool                  mReadOnly;
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -72,16 +72,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mTracingEnabled(true)
   , mTimingEnabled(false)
   , mAllowSpdy(true)
   , mAllowAltSvc(true)
   , mResponseTimeoutEnabled(true)
   , mAllRedirectsSameOrigin(true)
   , mAllRedirectsPassTimingAllowCheck(true)
   , mForceNoIntercept(false)
+  , mResponseCouldBeSynthesized(false)
   , mSuspendCount(0)
   , mProxyResolveFlags(0)
   , mProxyURI(nullptr)
   , mContentDispositionHint(UINT32_MAX)
   , mHttpHandler(gHttpHandler)
   , mReferrerPolicy(REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE)
   , mRedirectCount(0)
   , mForcePending(false)
@@ -1430,24 +1431,24 @@ HttpBaseChannel::SetRedirectionLimit(uin
 
 nsresult
 HttpBaseChannel::OverrideSecurityInfo(nsISupports* aSecurityInfo)
 {
   MOZ_ASSERT(!mSecurityInfo,
              "This can only be called when we don't have a security info object already");
   MOZ_RELEASE_ASSERT(aSecurityInfo,
                      "This can only be called with a valid security info object");
-  MOZ_ASSERT(ShouldIntercept(),
+  MOZ_ASSERT(mResponseCouldBeSynthesized,
              "This can only be called on channels that can be intercepted");
   if (mSecurityInfo) {
     LOG(("HttpBaseChannel::OverrideSecurityInfo mSecurityInfo is null! "
          "[this=%p]\n", this));
     return NS_ERROR_UNEXPECTED;
   }
-  if (!ShouldIntercept()) {
+  if (!mResponseCouldBeSynthesized) {
     LOG(("HttpBaseChannel::OverrideSecurityInfo channel cannot be intercepted! "
          "[this=%p]\n", this));
     return NS_ERROR_UNEXPECTED;
   }
 
   mSecurityInfo = aSecurityInfo;
   return NS_OK;
 }
@@ -1459,17 +1460,17 @@ HttpBaseChannel::OverrideURI(nsIURI* aRe
              "This can only happen if the LOAD_REPLACE flag is set");
   MOZ_ASSERT(ShouldIntercept(),
              "This can only be called on channels that can be intercepted");
   if (!(mLoadFlags & LOAD_REPLACE)) {
     LOG(("HttpBaseChannel::OverrideURI LOAD_REPLACE flag not set! [this=%p]\n",
          this));
     return NS_ERROR_UNEXPECTED;
   }
-  if (!ShouldIntercept()) {
+  if (!mResponseCouldBeSynthesized) {
     LOG(("HttpBaseChannel::OverrideURI channel cannot be intercepted! "
          "[this=%p]\n", this));
     return NS_ERROR_UNEXPECTED;
   }
 
   mURI = aRedirectedURI;
   return NS_OK;
 }
@@ -1964,16 +1965,17 @@ HttpBaseChannel::GetLastModifiedTime(PRT
   *lastModifiedTime = lastMod;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::ForceNoIntercept()
 {
   mForceNoIntercept = true;
+  mResponseCouldBeSynthesized = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetCorsIncludeCredentials(bool* aInclude)
 {
   *aInclude = mCorsIncludeCredentials;
   return NS_OK;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -376,16 +376,19 @@ protected:
 
   // Is 1 if no redirects have occured or if all redirects
   // pass the Resource Timing timing-allow-check
   uint32_t                          mAllRedirectsPassTimingAllowCheck : 1;
 
   // True if this channel should skip any interception checks
   uint32_t                          mForceNoIntercept           : 1;
 
+  // True if this channel was intercepted and could receive a synthesized response.
+  uint32_t                          mResponseCouldBeSynthesized : 1;
+
   // Current suspension depth for this channel object
   uint32_t                          mSuspendCount;
 
   nsCOMPtr<nsIURI>                  mAPIRedirectToURI;
   nsAutoPtr<nsTArray<nsCString> >   mRedirectedCachekeys;
 
   uint32_t                          mProxyResolveFlags;
   nsCOMPtr<nsIURI>                  mProxyURI;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1542,16 +1542,18 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
     // We may have been canceled already, either by on-modify-request
     // listeners or by load group observers; in that case, don't create IPDL
     // connection. See nsHttpChannel::AsyncOpen().
     AsyncAbort(mStatus);
     return NS_OK;
   }
 
   if (ShouldIntercept()) {
+    mResponseCouldBeSynthesized = true;
+
     nsCOMPtr<nsINetworkInterceptController> controller;
     GetCallback(controller);
 
     mInterceptListener = new InterceptStreamListener(this, mListenerContext);
 
     nsRefPtr<InterceptedChannelContent> intercepted =
         new InterceptedChannelContent(this, controller, mInterceptListener);
     intercepted->NotifyController();
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -4978,16 +4978,17 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     rv = NS_CheckPortSafety(mURI);
     if (NS_FAILED(rv)) {
         ReleaseListeners();
         return rv;
     }
 
     if (ShouldIntercept()) {
         mInterceptCache = MAYBE_INTERCEPT;
+        mResponseCouldBeSynthesized = true;
     }
 
     // Remember the cookie header that was set, if any
     const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie);
     if (cookieHeader) {
         mUserSetCookieHeader = cookieHeader;
     }
 
new file mode 100644
--- /dev/null
+++ b/python/PyECC/MANIFEST.in
@@ -0,0 +1,1 @@
+include README.md
new file mode 100644
--- /dev/null
+++ b/python/PyECC/README.md
@@ -0,0 +1,29 @@
+ecc
+===
+
+Pure Python implementation of an elliptic curve cryptosystem based on FIPS 186-3
+
+License
+=======
+
+The MIT License (MIT)
+
+Copyright (c) 2010-2015 Toni Mattis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/Key.py
@@ -0,0 +1,320 @@
+#   ====================================================================
+#
+#       ELLIPTIC CURVE KEY ENCAPSULATION
+#       Version 2011-01-26
+#
+#       Copyright (c) 2010 - 2011 | Toni Mattis
+#
+#   ====================================================================
+
+"""
+== Elliptic Curve Key Encapsulation ==
+
+Keypairs
+--------
+Keypairs are generated using: Key.generate(bits)
+
+The number of bits is tied to the NIST-proposed elliptic curves
+and has to be 192, 224, 256, 384 or 521 (not 512!).
+The result is a Key object containing public and private key.
+
+private() is a method for checking whether the Key object is a
+pure public key or also includes the private part.
+
+
+Exchange
+--------
+Public keys have to be exported using the export()-Method without
+passing an argument. The result is a string which can be safely
+transmitted.
+
+Using Key.decode(<encoded key>) the receiver obtains a new
+public Key object of the sender.
+
+
+Storage
+-------
+For storing a key, export(True) exports both private and public
+key as a string. Make sure this information is properly encrypted
+when stored.
+
+Key.decode(<encoded key>) obtains the full Key object from the
+encoded keypair.
+
+
+Public Keys
+-----------
+A public Key object can perform the following cryptographic
+operations:
+
+*   validate()      Checks key integrity, i.e. after loading the
+                    key from a file. Returns True if the key is
+                    valid. Invalid keys should be discarded.
+
+*   fingerprint()   Returns the public key fingerprint used to
+                    identify the key. Optional arguments:
+                    1. as_hex - True, if output should be formatted
+                        as hexadecimal number (default: True).
+                    2. hashfunc - The official name of the hash
+                        function being used (default: 'sha1')
+                        For supported hash functions see below.
+
+*   keyid()         Returns a (mostly) unique Key ID, which is
+                    shorter than the fingerprint. The result
+                    is an integer of max. 64 bits.
+
+*   verify()        Verifies whether the given data (argument 1)
+                    matches the signature (argument 2) issued
+                    by the owner of this key. A falsification
+                    can have multiple causes:
+                    
+                    - Data, public key or signature were altered
+                      during transmission/storage.
+                    - The siganture was not issued by the owner
+                      of this key but may be valid with another
+                      key.
+                    - The signature was issued for different data.
+                    - The signature was issued using a different
+                      hash function. Another hash function may work.
+                      
+                    Optionally, the name of a hash algorithm
+                    can be provided. For hash names see below.
+
+* encrypt()         Encrypts a packet of data destined for the owner
+                    of this key*. After encryption only the holder
+                    of this Key's private part is able to decrypt
+                    the message.
+
+Private Keys / Keypairs
+-----------------------
+
+If the key object is private, then it is a keypair consisting of
+a public and a private key. Therefore all Public key operations
+are supported.
+
+Additional functions:
+
+* sign()            Signs given data using this private key. The
+                    result is a signature which can be passed as
+                    argument to the verify() function in addition
+                    to the data being verified.
+
+                    As additional argument the name of the hash
+                    function can be provided (defaults to 'sha256').
+                    For hash names see below.
+
+* auth_encrypt()    Performs authenticated encryption of data
+                    (argument 1) for the holder of the key provided
+                    as second argument. Only the receiver whose
+                    public key is given is able to derypt and verify
+                    the message. The message will be implicitly
+                    signed using the own private key. *
+
+* decrypt()         Decrypts a message which has been encrypted
+                    using the public key of this keypair*. If
+                    decryption yields random data, this can have
+                    multiple causes:
+                    - You were not the intended receiver, a different
+                      private key may be able to decrypt it.
+                    - The message was altered.
+                    - Your private key is damaged.
+
+* auth_decrypt()    Decrypts a message while verifying whether
+                    it has been authentically issued by the holder
+                    of the given key (argument 2). When
+                    authentication failed, a
+                    SecurityViolationException is thrown. Reasons
+                    for this to happen are those mentioned with
+                    decrypt() and verify(). *
+
+*) The encryption used here depends on the "eccrypt" module imported
+by this module. Default implementation should use RABBIT as cipher
+and do the asymmetric part using an optimized El-Gamal scheme.
+        
+            
+
+Hash functions
+--------------
+The following hash functions can be passed at the moment:
+
+name     | hash size              | security level
+         | (bits, bytes, hex digits)
+---------+------------------------+----------------
+'sha1'      160 / 20 / 40           medium
+'sha224'    224 / 28 / 56           medium-strong
+'sha256'    256 / 32 / 64           strong
+'sha384'    384 / 48 / 96           very strong
+'sha512'    512 / 64 / 128          very strong
+
+'md5'       128 / 16 / 32           weak (not recommended!)
+
+
+Curves
+------
+According to FIPS 186-3, Appendix D.1.2 there are 5 elliptic
+curves recommended. All of those are strong, but those with
+a higher bit number even stronger.
+
+192 and 224 bits are sufficient for most purposes.
+256 bits offer an additional magnitude of security.
+    (i.e. for classified / strongly confidential data)
+384 and 521 bits provide exceptionally strong security. According
+    to current research they most probably keep this level for
+    decades in the future.
+
+FIPS also recommends curves over polynomial fields but actually
+only prime fields are implemented here. (Because 2^521-1 is a mersenne
+prime having great security characteristics, 521 bits are preferred
+over a constructed 512 bit field.)
+"""
+
+from encoding import *
+from eccrypt import *
+import ecdsa
+import hashlib
+from SecurityViolationException import *
+
+class Key:
+
+    # --- KEY SETUP ------------------------------------------------------------
+
+    def __init__(self, public_key, private_key = None):
+        '''Create a Key(pair) from numeric keys.'''
+        self._pub = public_key
+        self._priv = private_key
+        self._fingerprint = {}
+        self._id = None
+
+    @staticmethod
+    def generate(bits):
+        '''Generate a new ECDSA keypair'''
+        return Key(*ecdsa.keypair(bits))
+
+    # --- BINARY REPRESENTATION ------------------------------------------------
+
+    def encode(self, include_private = False):
+        '''Returns a strict binary representation of this Key'''
+        e = Encoder().int(self.keyid(), 8)
+        e.int(self._pub[0], 2).point(self._pub[1], 2)
+        if include_private and self._priv:
+            e.long(self._priv[1], 2)
+        else:
+            e.long(0, 2)
+        return e.out()
+
+    def compress(self):
+        '''Returns a compact public key representation'''
+        
+
+    @staticmethod
+    def decode(s):
+        '''Constructs a new Key object from its binary representation'''
+        kid, ksize, pub, priv = Decoder(s).int(8).int(2).point(2).long(2).out()
+        k = Key((ksize, pub), (ksize, priv) if priv else None)
+        if kid == k.keyid():
+            return k
+        else:
+            raise ValueError, "Invalid Key ID"
+
+    # --- IDENTIFICATION AND VALIDATION ----------------------------------------
+
+    def private(self):
+        '''Checks whether Key object contains private key'''
+        return bool(self._priv)
+
+    def validate(self):
+        '''Checks key validity'''
+        if ecdsa.validate_public_key(self._pub):
+            if self._priv:          # ? validate and match private key
+                return ecdsa.validate_private_key(self._priv) and \
+                       ecdsa.match_keys(self._pub, self._priv)
+            else:
+                return True         # : everything valid
+        else:
+            return False
+
+    def fingerprint(self, as_hex = True, hashfunc = 'sha1'):
+        '''Get the public key fingerprint'''
+        if hashfunc in self._fingerprint:
+            return self._fingerprint[hashfunc] if not as_hex else \
+                   self._fingerprint[hashfunc].encode("hex")
+        else:
+            h = hashlib.new(hashfunc, enc_point(self._pub[1]))
+            d = h.digest()
+            self._fingerprint[hashfunc] = d
+            return d.encode("hex") if as_hex else d
+
+    def keyid(self):
+        '''Get a short, unique identifier'''
+        if not self._id:
+            self._id = dec_long(self.fingerprint(False, 'sha1')[:8])
+        return self._id
+
+    # --- DIGITAL SIGNATURES ---------------------------------------------------
+
+    def sign(self, data, hashfunc = 'sha256'):
+        '''Sign data using the specified hash function'''
+        if self._priv:
+            h = dec_long(hashlib.new(hashfunc, data).digest())
+            s = ecdsa.sign(h, self._priv)
+            return enc_point(s)
+        else:
+            raise AttributeError, "Private key needed for signing."
+
+    def verify(self, data, sig, hashfunc = 'sha256'):
+        '''Verify the signature of data using the specified hash function'''
+        h = dec_long(hashlib.new(hashfunc, data).digest())
+        s = dec_point(sig)
+        return ecdsa.verify(h, s, self._pub)
+
+    # --- HYBRID ENCRYPTION ----------------------------------------------------
+
+    def encrypt(self, data):
+        '''Encrypt a message using this public key'''
+        ctext, mkey = encrypt(data, self._pub)
+        return Encoder().point(mkey).str(ctext, 4).out()
+
+    def decrypt(self, data):
+        '''Decrypt an encrypted message using this private key'''
+        mkey, ctext = Decoder(data).point().str(4).out()
+        return decrypt(ctext, mkey, self._priv)
+        
+    # --- AUTHENTICATED ENCRYPTION ---------------------------------------------
+
+    def auth_encrypt(self, data, receiver):
+        '''Sign and encrypt a message'''
+        sgn = self.sign(data)
+        ctext, mkey = encrypt(data, receiver._pub)
+        return Encoder().point(mkey).str(ctext, 4).str(sgn, 2).out()
+
+    def auth_decrypt(self, data, source):
+        '''Decrypt and verify a message'''
+        mkey, ctext, sgn = Decoder(data).point().str(4).str(2).out()
+        text = decrypt(ctext, mkey, self._priv)
+        if source.verify(text, sgn):
+            return text
+        else:
+            raise SecurityViolationException, "Invalid Signature"
+
+
+if __name__ == "__main__":
+
+    import time
+
+    def test_overhead():
+        print "sender", "receiver", "+bytes", "+enctime", "+dectime"
+        for s in [192, 224, 256, 384, 521]:
+            sender = Key.generate(s)
+            for r in [192, 224, 256, 384, 521]:
+                receiver = Key.generate(r)
+                t = time.time()
+                e = sender.auth_encrypt("", receiver)
+                t1 = time.time() - t
+                t = time.time()
+                receiver.auth_decrypt(e, sender)
+                t2 = time.time() - t
+                print s, r, len(e), t1, t2
+
+                
+                
+    
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/Rabbit.py
@@ -0,0 +1,270 @@
+# ------------------------------------------------------------------------------
+#
+#   R A B B I T   Stream Cipher
+#   by M. Boesgaard, M. Vesterager, E. Zenner (specified in RFC 4503)
+#
+#
+#   Pure Python Implementation by Toni Mattis
+#
+# ------------------------------------------------------------------------------
+
+
+WORDSIZE = 0x100000000
+
+rot08 = lambda x: ((x <<  8) & 0xFFFFFFFF) | (x >> 24)
+rot16 = lambda x: ((x << 16) & 0xFFFFFFFF) | (x >> 16)
+
+def _nsf(u, v):
+    '''Internal non-linear state transition'''
+    s = (u + v) % WORDSIZE
+    s = s * s
+    return (s ^ (s >> 32)) % WORDSIZE
+
+class Rabbit:
+
+    def __init__(self, key, iv = None):
+        '''Initialize Rabbit cipher using a 128 bit integer/string'''
+        
+        if isinstance(key, str):
+            # interpret key string in big endian byte order
+            if len(key) < 16:
+                key = '\x00' * (16 - len(key)) + key
+            # if len(key) > 16 bytes only the first 16 will be considered
+            k = [ord(key[i + 1]) | (ord(key[i]) << 8)
+                 for i in xrange(14, -1, -2)]
+        else:
+            # k[0] = least significant 16 bits
+            # k[7] = most significant 16 bits
+            k = [(key >> i) & 0xFFFF for i in xrange(0, 128, 16)]
+            
+        # State and counter initialization
+        x = [(k[(j + 5) % 8] << 16) | k[(j + 4) % 8] if j & 1 else
+             (k[(j + 1) % 8] << 16) | k[j] for j in xrange(8)]
+        c = [(k[j] << 16) | k[(j + 1) % 8] if j & 1 else
+             (k[(j + 4) % 8] << 16) | k[(j + 5) % 8] for j in xrange(8)]
+        
+        self.x = x
+        self.c = c
+        self.b = 0
+        self._buf = 0           # output buffer
+        self._buf_bytes = 0     # fill level of buffer
+        
+        self.next()
+        self.next()
+        self.next()
+        self.next()
+
+        for j in xrange(8):
+            c[j] ^= x[(j + 4) % 8]
+        
+        self.start_x = self.x[:]    # backup initial key for IV/reset
+        self.start_c = self.c[:]
+        self.start_b = self.b
+
+        if iv != None:
+            self.set_iv(iv)
+
+    def reset(self, iv = None):
+        '''Reset the cipher and optionally set a new IV (int64 / string).'''
+        
+        self.c = self.start_c[:]
+        self.x = self.start_x[:]
+        self.b = self.start_b
+        self._buf = 0
+        self._buf_bytes = 0
+        if iv != None:
+            self.set_iv(iv)
+
+    def set_iv(self, iv):
+        '''Set a new IV (64 bit integer / bytestring).'''
+
+        if isinstance(iv, str):
+            i = 0
+            for c in iv:
+                i = (i << 8) | ord(c)
+            iv = i
+
+        c = self.c
+        i0 = iv & 0xFFFFFFFF
+        i2 = iv >> 32
+        i1 = ((i0 >> 16) | (i2 & 0xFFFF0000)) % WORDSIZE
+        i3 = ((i2 << 16) | (i0 & 0x0000FFFF)) % WORDSIZE
+        
+        c[0] ^= i0
+        c[1] ^= i1
+        c[2] ^= i2
+        c[3] ^= i3
+        c[4] ^= i0
+        c[5] ^= i1
+        c[6] ^= i2
+        c[7] ^= i3
+
+        self.next()
+        self.next()
+        self.next()
+        self.next()
+        
+
+    def next(self):
+        '''Proceed to the next internal state'''
+        
+        c = self.c
+        x = self.x
+        b = self.b
+
+        t = c[0] + 0x4D34D34D + b
+        c[0] = t % WORDSIZE
+        t = c[1] + 0xD34D34D3 + t // WORDSIZE
+        c[1] = t % WORDSIZE
+        t = c[2] + 0x34D34D34 + t // WORDSIZE
+        c[2] = t % WORDSIZE
+        t = c[3] + 0x4D34D34D + t // WORDSIZE
+        c[3] = t % WORDSIZE
+        t = c[4] + 0xD34D34D3 + t // WORDSIZE
+        c[4] = t % WORDSIZE
+        t = c[5] + 0x34D34D34 + t // WORDSIZE
+        c[5] = t % WORDSIZE
+        t = c[6] + 0x4D34D34D + t // WORDSIZE
+        c[6] = t % WORDSIZE
+        t = c[7] + 0xD34D34D3 + t // WORDSIZE
+        c[7] = t % WORDSIZE
+        b = t // WORDSIZE
+        
+        g = [_nsf(x[j], c[j]) for j in xrange(8)]
+        
+        x[0] = (g[0] + rot16(g[7]) + rot16(g[6])) % WORDSIZE
+        x[1] = (g[1] + rot08(g[0]) + g[7]) % WORDSIZE
+        x[2] = (g[2] + rot16(g[1]) + rot16(g[0])) % WORDSIZE
+        x[3] = (g[3] + rot08(g[2]) + g[1]) % WORDSIZE
+        x[4] = (g[4] + rot16(g[3]) + rot16(g[2])) % WORDSIZE
+        x[5] = (g[5] + rot08(g[4]) + g[3]) % WORDSIZE
+        x[6] = (g[6] + rot16(g[5]) + rot16(g[4])) % WORDSIZE
+        x[7] = (g[7] + rot08(g[6]) + g[5]) % WORDSIZE
+        
+        self.b = b
+        return self
+
+
+    def derive(self):
+        '''Derive a 128 bit integer from the internal state'''
+        
+        x = self.x
+        return ((x[0] & 0xFFFF) ^ (x[5] >> 16)) | \
+               (((x[0] >> 16) ^ (x[3] & 0xFFFF)) << 16)| \
+               (((x[2] & 0xFFFF) ^ (x[7] >> 16)) << 32)| \
+               (((x[2] >> 16) ^ (x[5] & 0xFFFF)) << 48)| \
+               (((x[4] & 0xFFFF) ^ (x[1] >> 16)) << 64)| \
+               (((x[4] >> 16) ^ (x[7] & 0xFFFF)) << 80)| \
+               (((x[6] & 0xFFFF) ^ (x[3] >> 16)) << 96)| \
+               (((x[6] >> 16) ^ (x[1] & 0xFFFF)) << 112)
+
+    
+    def keystream(self, n):
+        '''Generate a keystream of n bytes'''
+        
+        res = ""
+        b = self._buf
+        j = self._buf_bytes
+        next = self.next
+        derive = self.derive
+        
+        for i in xrange(n):
+            if not j:
+                j = 16
+                next()
+                b = derive()
+            res += chr(b & 0xFF)
+            j -= 1
+            b >>= 1
+
+        self._buf = b
+        self._buf_bytes = j
+        return res
+
+
+    def encrypt(self, data):
+        '''Encrypt/Decrypt data of arbitrary length.'''
+        
+        res = ""
+        b = self._buf
+        j = self._buf_bytes
+        next = self.next
+        derive = self.derive
+
+        for c in data:
+            if not j:   # empty buffer => fetch next 128 bits
+                j = 16
+                next()
+                b = derive()
+            res += chr(ord(c) ^ (b & 0xFF))
+            j -= 1
+            b >>= 1
+        self._buf = b
+        self._buf_bytes = j
+        return res
+
+    decrypt = encrypt
+        
+    
+
+if __name__ == "__main__":
+
+    import time
+
+    # --- Official Test Vectors ---
+    
+    # RFC 4503 Appendix A.1 - Testing without IV Setup
+
+    r = Rabbit(0)
+    assert r.next().derive() == 0xB15754F036A5D6ECF56B45261C4AF702
+    assert r.next().derive() == 0x88E8D815C59C0C397B696C4789C68AA7
+    assert r.next().derive() == 0xF416A1C3700CD451DA68D1881673D696
+
+    r = Rabbit(0x912813292E3D36FE3BFC62F1DC51C3AC)
+    assert r.next().derive() == 0x3D2DF3C83EF627A1E97FC38487E2519C
+    assert r.next().derive() == 0xF576CD61F4405B8896BF53AA8554FC19
+    assert r.next().derive() == 0xE5547473FBDB43508AE53B20204D4C5E
+
+    r = Rabbit(0x8395741587E0C733E9E9AB01C09B0043)
+    assert r.next().derive() == 0x0CB10DCDA041CDAC32EB5CFD02D0609B 
+    assert r.next().derive() == 0x95FC9FCA0F17015A7B7092114CFF3EAD
+    assert r.next().derive() == 0x9649E5DE8BFC7F3F924147AD3A947428
+
+    # RFC 4503 Appendix A.2 - Testing with IV Setup
+
+    r = Rabbit(0, 0)
+    assert r.next().derive() == 0xC6A7275EF85495D87CCD5D376705B7ED
+    assert r.next().derive() == 0x5F29A6AC04F5EFD47B8F293270DC4A8D
+    assert r.next().derive() == 0x2ADE822B29DE6C1EE52BDB8A47BF8F66
+
+    r = Rabbit(0, 0xC373F575C1267E59)
+    assert r.next().derive() == 0x1FCD4EB9580012E2E0DCCC9222017D6D
+    assert r.next().derive() == 0xA75F4E10D12125017B2499FFED936F2E
+    assert r.next().derive() == 0xEBC112C393E738392356BDD012029BA7
+
+    r = Rabbit(0, 0xA6EB561AD2F41727)
+    assert r.next().derive() == 0x445AD8C805858DBF70B6AF23A151104D
+    assert r.next().derive() == 0x96C8F27947F42C5BAEAE67C6ACC35B03
+    assert r.next().derive() == 0x9FCBFC895FA71C17313DF034F01551CB
+
+
+    # --- Performance Tests ---
+
+    def test_gen(n = 1048576):
+        '''Measure time for generating n bytes => (total, bytes per second)'''
+        
+        r = Rabbit(0)
+        t = time.time()
+        r.keystream(n)
+        t = time.time() - t
+        return t, n / t
+
+    def test_enc(n = 1048576):
+        '''Measure time for encrypting n bytes => (total, bytes per second)'''
+        
+        r = Rabbit(0)
+        x = 'x' * n
+        t = time.time()
+        r.encrypt(x)
+        t = time.time() - t
+        return t, n / t
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/SecurityViolationException.py
@@ -0,0 +1,2 @@
+class SecurityViolationException(Exception):
+    pass
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/curves.py
@@ -0,0 +1,81 @@
+#
+#   Predefined Elliptic Curves
+#   for use in signing and key exchange
+#
+'''
+Predefined elliptic curves for use in signing and key exchange.
+This Module implements FIPS approved standard curves P-192, P-224, P-256,
+P-384 and P-521 along with two weak non-standard curves of field size 128
+and 160 bits.
+
+The weak curves cannot be used for signing but provide a faster way to
+obfuscate non-critical transmissions.
+'''
+
+# FIPS approved elliptic curves over prime fields 
+# (see FIPS 186-3, Appendix D.1.2)
+DOMAINS = {
+    # Bits : (p, order of E(GF(P)), parameter b, base point x, base point y)
+    192 : (0xfffffffffffffffffffffffffffffffeffffffffffffffffL,
+           0xffffffffffffffffffffffff99def836146bc9b1b4d22831L,
+           0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1L,
+           0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012L,
+           0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811L),
+    
+    224 : (0xffffffffffffffffffffffffffffffff000000000000000000000001L,
+           0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3dL,
+           0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4L,
+           0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21L,
+           0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34L),
+
+    256 : (0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffL,
+           0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551L,
+           0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604bL,
+           0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296L,
+           0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5L),
+
+    384 : (0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffffL,
+           0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973L,
+           0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aefL,
+           0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7L,
+           0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5fL),
+
+    521 : (0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffL,
+           0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409L,
+           0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00L,
+           0x0c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66L,
+           0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650L)
+    }
+
+
+# Additional non-standard curves for low security but high performance
+# (not intended for use in signing, hence the missing group order)
+
+DOMAINS.update({
+    128 : (0xffffffffffffffffffffffffffffff61L,
+           None,
+           0xd83d3eb8266a89927d73d5fe263d5f23L,
+           0xa94d2d8531f7af8bde367def12b98eadL,
+           0x9f44e1d671beb68fd2df7f877ab13fa6L),
+    
+    160 : (0xffffffffffffffffffffffffffffffffffffffd1L,
+           None,
+           0x94bfe70deef7b94742c089ca4db3ca27fbe1f754L,
+           0xcc6562c2969ac57524b8d0f300d1f598c908c121L,
+           0x952ddde80a252683dd7ba90fb5919899b5af69f5L)
+    })     
+
+CURVE_P = 3     # global parameter of all curves (for efficiency reasons)
+
+           
+def get_curve(bits):
+    '''Get a known curve of the given size => (bits, prime, order, p, q, point).
+    Order may be None if unknown.'''
+    if bits in DOMAINS:
+        p, n, b, x, y = DOMAINS[bits]
+        return bits, p, n, CURVE_P, p - b, (x, y)
+    else:
+        raise KeyError, "Key size not implemented: %s" % bits
+ 
+def implemented_keys(must_sign = False):
+    return [k for k in DOMAINS if not must_sign or DOMAINS[k][1]]
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/eccrypt.py
@@ -0,0 +1,65 @@
+#   Elliptic Curve Hybrid Encryption Scheme
+#
+#   COPYRIGHT (c) 2010 by Toni Mattis <solaris@live.de>
+#
+
+from curves import get_curve
+from elliptic import mulp
+from encoding import enc_long
+from random import SystemRandom
+from Rabbit import Rabbit
+
+# important for cryptographically secure random numbers:
+random = SystemRandom()
+
+# Encryption Algorithm:
+# ---------------------
+# Input: Message M, public key Q
+#
+# 0. retrieve the group from which Q was generated.
+# 1. generate random number k between 1 and the group order.
+# 2. compute KG = k * G (where G is the base point of the group).
+# 3. compute SG = k * Q (where Q is the public key of the receiver).
+# 4. symmetrically encrypt M to M' using SG's x-coordinate as key.
+#
+# Return: Ciphertext M', temporary key KG
+
+
+def encrypt(message, qk, encrypter = Rabbit):
+    '''Encrypt a message using public key qk => (ciphertext, temp. pubkey)'''
+    bits, q = qk
+    try:
+        bits, cn, n, cp, cq, g = get_curve(bits)
+        if not n:
+            raise ValueError, "Key size %s not suitable for encryption" % bits
+    except KeyError:
+        raise ValueError, "Key size %s not implemented" % bits
+    
+    k = random.randint(1, n - 1)        # temporary private key k
+    kg = mulp(cp, cq, cn, g, k)         # temporary public key k*G
+    sg = mulp(cp, cq, cn, q, k)         # shared secret k*Q = k*d*G
+
+    return encrypter(enc_long(sg[0])).encrypt(message), kg
+
+# Decryption Algorithm:
+# ---------------------
+# Input: Ciphertext M', temporary key KG, private key d
+#
+# 0. retrieve the group from which d and KG were generated.
+# 1. compute SG = q * KG.
+# 2. symmetrically decrypt M' to M using SG's x-coordinate as key.
+#
+# Return: M
+
+def decrypt(message, kg, dk, decrypter = Rabbit):
+    '''Decrypt a message using temp. public key kg and private key dk'''
+    bits, d = dk
+    try:
+        bits, cn, n, cp, cq, g = get_curve(bits)
+    except KeyError:
+        raise ValueError, "Key size %s not implemented" % bits
+
+    sg = mulp(cp, cq, cn, kg, d)        # shared secret d*(k*G) = k*d*G
+    return decrypter(enc_long(sg[0])).decrypt(message)
+    
+    
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/ecdsa.py
@@ -0,0 +1,153 @@
+#
+#   Elliptic Curve Digital Signature Algorithm (ECDSA)
+#
+#   COPYRIGHT (c) 2010 by Toni Mattis <solaris@live.de>
+#
+
+from elliptic import inv, mulf, mulp, muladdp, element
+from curves import get_curve, implemented_keys
+from os import urandom
+
+import hashlib
+
+def randkey(bits, n):
+    '''Generate a random number (mod n) having the specified bit length'''
+    rb = urandom(bits / 8 + 8)  # + 64 bits as recommended in FIPS 186-3
+    c = 0
+    for r in rb:
+        c = (c << 8) | ord(r)
+    return (c % (n - 1)) + 1
+
+def keypair(bits):
+    '''Generate a new keypair (qk, dk) with dk = private and qk = public key'''
+    try:
+        bits, cn, n, cp, cq, g = get_curve(bits)
+    except KeyError:
+        raise ValueError, "Key size %s not implemented" % bits
+    if n > 0:
+        d = randkey(bits, n)
+        q = mulp(cp, cq, cn, g, d)
+        return (bits, q), (bits, d)
+    else:
+        raise ValueError, "Key size %s not suitable for signing" % bits
+
+def supported_keys():
+    '''Return a list of all key sizes implemented for signing'''
+    return implemented_keys(True)
+
+def validate_public_key(qk):
+    '''Check whether public key qk is valid'''
+    bits, q = qk
+    x, y = q
+    bits, cn, n, cp, cq, g = get_curve(bits)
+    return q and 0 < x < cn and 0 < y < cn and \
+        element(q, cp, cq, cn) and (mulp(cp, cq, cn, q, n) == None)
+
+def validate_private_key(dk):
+    '''Check whether private key dk is valid'''
+    bits, d = dk
+    bits, cn, n, cp, cq, g = get_curve(bits)
+    return 0 < d < cn
+
+def match_keys(qk, dk):
+    '''Check whether dk is the private key belonging to qk'''
+    bits, d = dk
+    bitz, q = qk
+    if bits == bitz:
+        bits, cn, n, cp, cq, g = get_curve(bits)
+        return mulp(cp, cq, cn, g, d) == q
+    else:
+        return False
+
+def truncate(h, hmax):
+    '''Truncate a hash to the bit size of hmax'''
+    while h > hmax:
+        h >>= 1
+    return h
+
+def sign(h, dk):
+    '''Sign the numeric value h using private key dk'''
+    bits, d = dk
+    bits, cn, n, cp, cq, g = get_curve(bits)
+    h = truncate(h, cn)
+    r = s = 0
+    while r == 0 or s == 0:
+        k = randkey(bits, cn)
+        kinv = inv(k, n)
+        kg = mulp(cp, cq, cn, g, k)
+        r = kg[0] % n
+        if r == 0:
+            continue
+        s = (kinv * (h + r * d)) % n
+    return r, s
+
+def verify(h, sig, qk):
+    '''Verify that 'sig' is a valid signature of h using public key qk'''
+    bits, q = qk
+    try:
+        bits, cn, n, cp, cq, g = get_curve(bits)
+    except KeyError:
+        return False
+    h = truncate(h, cn)
+    r, s = sig
+    if 0 < r < n and 0 < s < n:
+        w = inv(s, n)
+        u1 = (h * w) % n
+        u2 = (r * w) % n
+        x, y = muladdp(cp, cq, cn, g, u1, q, u2)
+        return r % n == x % n
+    return False
+
+def hash_sign(s, dk, hashfunc = 'sha256'):
+    h = int(hashlib.new(hashfunc, s).hexdigest(), 16)
+    return (hashfunc,) + sign(h, dk)
+
+def hash_verify(s, sig, qk):
+    h = int(hashlib.new(sig[0], s).hexdigest(), 16)
+    return verify(h, sig[1:], qk)
+
+
+if __name__ == "__main__":
+
+    import time
+
+    testh1 = 0x0123456789ABCDEF
+    testh2 = 0x0123456789ABCDEE
+
+    for k in supported_keys():
+        qk, dk = keypair(k)
+        s1 = sign(testh1, dk)
+        s2 = sign(testh1, (dk[0], dk[1] ^ 1))
+        s3 = (s1[0], s1[1] ^ 1)
+        qk2 = (qk[0], (qk[1][0] ^ 1, qk[1][1]))
+        
+        assert verify(testh1, s1, qk)       # everything ok -> must succeed
+        assert not verify(testh2, s1, qk)   # modified hash       -> must fail
+        assert not verify(testh1, s2, qk)   # different priv. key -> must fail
+        assert not verify(testh1, s3, qk)   # modified signature  -> must fail
+        assert not verify(testh1, s1, qk2)  # different publ. key -> must fail
+
+
+    def test_perf(bits, rounds = 50):
+        '''-> (key generations, signatures, verifications) / second'''
+        h = 0x0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
+        d = get_curve(bits)
+        
+        t = time.time()
+        for i in xrange(rounds):
+            qk, dk = keypair(bits)
+        tgen = time.time() - t
+        
+        t = time.time()
+        for i in xrange(rounds):
+            s = sign(0, dk)
+        tsign = time.time() - t
+
+        t = time.time()
+        for i in xrange(rounds):
+            verify(0, s, qk)
+        tver = time.time() - t
+
+        return rounds / tgen, rounds / tsign, rounds / tver
+
+    
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/elliptic.py
@@ -0,0 +1,381 @@
+
+# --- ELLIPTIC CURVE MATH ------------------------------------------------------
+#
+#   curve definition:   y^2 = x^3 - p*x - q
+#   over finite field:  Z/nZ* (prime residue classes modulo a prime number n)
+#
+#
+#   COPYRIGHT (c) 2010 by Toni Mattis <solaris@live.de>
+# ------------------------------------------------------------------------------
+
+'''
+Module for elliptic curve arithmetic over a prime field GF(n).
+E(GF(n)) takes the form y**2 == x**3 - p*x - q (mod n) for a prime n.
+
+0. Structures used by this module
+
+    PARAMETERS and SCALARS are non-negative (long) integers.
+
+    A POINT (x, y), usually denoted p1, p2, ...
+    is a pair of (long) integers where 0 <= x < n and 0 <= y < n
+
+    A POINT in PROJECTIVE COORDINATES, usually denoted jp1, jp2, ...
+    takes the form (X, Y, Z, Z**2, Z**3) where x = X / Z**2
+    and y = Y / z**3. This form is called Jacobian coordinates.
+
+    The NEUTRAL element "0" or "O" is represented by None
+    in both coordinate systems.
+
+1. Basic Functions
+
+    euclid()            Is the Extended Euclidean Algorithm.
+    inv()               Computes the multiplicative inversion modulo n.
+    curve_q()           Finds the curve parameter q (mod n)
+                        when p and a point are given.
+    element()           Tests whether a point (x, y) is on the curve.
+
+2. Point transformations
+
+    to_projective()     Converts a point (x, y) to projective coordinates.
+    from_projective()   Converts a point from projective coordinates
+                        to (x, y) using the transformation described above.
+    neg()               Computes the inverse point -P in both coordinate
+                        systems.
+
+3. Slow point arithmetic
+
+    These algorithms make use of basic geometry and modular arithmetic
+    thus being suitable for small numbers and academic study.
+
+    add()               Computes the sum of two (x, y)-points
+    mul()               Perform scalar multiplication using "double & add"
+
+4. Fast point arithmetic
+
+    These algorithms make use of projective coordinates, signed binary
+    expansion and a JSP-like approach (joint sparse form).
+
+    The following functions consume and return projective coordinates:
+
+    addf()              Optimized point addition.
+    doublef()           Optimized point doubling.
+    mulf()              Highly optimized scalar multiplication.
+    muladdf()           Highly optimized addition of two products.
+    
+    The following functions use the optimized ones above but consume
+    and output (x, y)-coordinates for a more convenient usage:
+
+    mulp()              Encapsulates mulf()
+    muladdp()           Encapsulates muladdf()
+
+    For single additions add() is generally faster than an encapsulation of
+    addf() which would involve expensive coordinate transformations.
+    Hence there is no addp() and doublep().
+'''
+
+# BASIC MATH -------------------------------------------------------------------
+
+def euclid(a, b):
+    '''Solve x*a + y*b = ggt(a, b) and return (x, y, ggt(a, b))'''
+    # Non-recursive approach hence suitable for large numbers
+    x = yy = 0
+    y = xx = 1
+    while b:
+        q = a // b
+        a, b = b, a % b
+        x, xx = xx - q * x, x
+        y, yy = yy - q * y, y
+    return xx, yy, a
+
+def inv(a, n):
+    '''Perform inversion 1/a modulo n. a and n should be COPRIME.'''
+    # coprimality is not checked here in favour of performance
+    i = euclid(a, n)[0]
+    while i < 0:
+        i += n
+    return i
+
+def curve_q(x, y, p, n):
+    '''Find curve parameter q mod n having point (x, y) and parameter p'''
+    return ((x * x - p) * x - y * y) % n
+
+def element(point, p, q, n):
+    '''Test, whether the given point is on the curve (p, q, n)'''
+    if point:
+        x, y = point
+        return (x * x * x - p * x - q) % n == (y * y) % n
+    else:
+        return True
+
+def to_projective(p):
+    '''Transform point p given as (x, y) to projective coordinates'''
+    if p:
+        return (p[0], p[1], 1, 1, 1)
+    else:
+        return None     # Identity point (0)
+
+def from_projective(jp, n):
+    '''Transform a point from projective coordinates to (x, y) mod n'''
+    if jp:
+        return (jp[0] * inv(jp[3], n)) % n, (jp[1] * inv(jp[4], n)) % n
+    else:
+        return None     # Identity point (0)
+
+def neg(p, n):
+    '''Compute the inverse point to p in any coordinate system'''
+    return (p[0], (n - p[1]) % n) + p[2:] if p else None
+
+
+# POINT ADDITION ---------------------------------------------------------------
+
+# addition of points in y**2 = x**3 - p*x - q over <Z/nZ*; +>
+def add(p, q, n, p1, p2):
+    '''Add points p1 and p2 over curve (p, q, n)'''
+    if p1 and p2:
+        x1, y1 = p1
+        x2, y2 = p2
+        if (x1 - x2) % n:
+            s = ((y1 - y2) * inv(x1 - x2, n)) % n   # slope
+            x = (s * s - x1 - x2) % n               # intersection with curve
+            return (x, n - (y1 + s * (x - x1)) % n)
+        else:
+            if (y1 + y2) % n:       # slope s calculated by derivation
+                s = ((3 * x1 * x1 - p) * inv(2 * y1, n)) % n
+                x = (s * s - 2 * x1) % n            # intersection with curve
+                return (x, n - (y1 + s * (x - x1)) % n)
+            else:
+                return None
+    else:   # either p1 is not none -> ret. p1, otherwiese p2, which may be
+        return p1 if p1 else p2     # none too.
+
+
+# faster addition: redundancy in projective coordinates eliminates
+# expensive inversions mod n.
+def addf(p, q, n, jp1, jp2):
+    '''Add jp1 and jp2 in projective (jacobian) coordinates.'''
+    if jp1 and jp2:
+        
+        x1, y1, z1, z1s, z1c = jp1
+        x2, y2, z2, z2s, z2c = jp2
+
+        s1 = (y1 * z2c) % n
+        s2 = (y2 * z1c) % n
+
+        u1 = (x1 * z2s) % n
+        u2 = (x2 * z1s) % n
+
+        if (u1 - u2) % n:
+
+            h = (u2 - u1) % n
+            r = (s2 - s1) % n
+
+            hs = (h * h) % n
+            hc = (hs * h) % n
+
+            x3 = (-hc - 2 * u1 * hs + r * r) % n
+            y3 = (-s1 * hc + r * (u1 * hs - x3)) % n
+            z3 = (z1 * z2 * h) % n
+            
+            z3s = (z3 * z3) % n
+            z3c = (z3s * z3) % n
+    
+            return (x3, y3, z3, z3s, z3c)
+        
+        else:
+            if (s1 + s2) % n:
+                return doublef(p, q, n, jp1)
+            else:
+                return None
+    else:
+        return jp1 if jp1 else jp2
+
+# explicit point doubling using redundant coordinates
+def doublef(p, q, n, jp):
+    '''Double jp in projective (jacobian) coordinates'''
+    if not jp:
+        return None
+    x1, y1, z1, z1p2, z1p3 = jp
+    
+    y1p2 = (y1 * y1) % n
+    a = (4 * x1 * y1p2) % n
+    b = (3 * x1 * x1 - p * z1p3 * z1) % n
+    x3 = (b * b - 2 * a) % n
+    y3 = (b * (a - x3) - 8 * y1p2 * y1p2) % n
+    z3 = (2 * y1 * z1) % n
+    z3p2 = (z3 * z3) % n
+    
+    return x3, y3, z3, z3p2, (z3p2 * z3) % n
+
+
+# SCALAR MULTIPLICATION --------------------------------------------------------
+
+# scalar multiplication p1 * c = p1 + p1 + ... + p1 (c times) in O(log(n))
+def mul(p, q, n, p1, c):
+    '''multiply point p1 by scalar c over curve (p, q, n)'''
+    res = None
+    while c > 0:
+        if c & 1:
+            res = add(p, q, n, res, p1)
+        c >>= 1                     # c = c / 2
+        p1 = add(p, q, n, p1, p1)   # p1 = p1 * 2
+    return res
+
+
+# this method allows _signed_bin() to choose between 1 and -1. It will select
+# the sign which leaves the higher number of zeroes in the binary
+# representation (the higher GDB).
+def _gbd(n):
+    '''Compute second greatest base-2 divisor'''
+    i = 1
+    if n <= 0: return 0
+    while not n % i:
+        i <<= 1
+    return i >> 2
+
+
+# This method transforms n into a binary representation having signed bits.
+# A signed binary expansion contains more zero-bits hence reducing the number
+# of additions required by a multiplication algorithm.
+#
+# Example:  15 ( 0b1111 ) can be written as 16 - 1, resulting in (1,0,0,0,-1)
+#           and saving 2 additions. Subtraction can be performed as
+#           efficiently as addition.
+def _signed_bin(n):
+    '''Transform n into an optimized signed binary representation'''
+    r = []
+    while n > 1:
+        if n & 1:
+            cp = _gbd(n + 1) 
+            cn = _gbd(n - 1)
+            if cp > cn:         # -1 leaves more zeroes -> subtract -1 (= +1)
+                r.append(-1)
+                n += 1
+            else:               # +1 leaves more zeroes -> subtract +1 (= -1)
+                r.append(+1)
+                n -= 1
+        else:
+            r.append(0)         # be glad about one more zero
+        n >>= 1
+    r.append(n)
+    return r[::-1]
+
+
+# This multiplication algorithm combines signed binary expansion and
+# fast addition using projective coordinates resulting in 5 to 10 times
+# faster multiplication.
+def mulf(p, q, n, jp1, c):
+    '''Multiply point jp1 by c in projective coordinates'''
+    sb = _signed_bin(c)
+    res = None
+    jp0 = neg(jp1, n)  # additive inverse of jp1 to be used fot bit -1
+    for s in sb:
+        res = doublef(p, q, n, res)
+        if s:
+            res = addf(p, q, n, res, jp1) if s > 0 else \
+                  addf(p, q, n, res, jp0)
+    return res
+
+# Encapsulates mulf() in order to enable flat coordinates (x, y)
+def mulp(p, q, n, p1, c):
+    '''Multiply point p by c using fast multiplication'''
+    return from_projective(mulf(p, q, n, to_projective(p1), c), n)
+
+
+# Sum of two products using Shamir's trick and signed binary expansion
+def muladdf(p, q, n, jp1, c1, jp2, c2):
+    '''Efficiently compute c1 * jp1 + c2 * jp2 in projective coordinates'''
+    s1 = _signed_bin(c1)
+    s2 = _signed_bin(c2)
+    diff = len(s2) - len(s1)
+    if diff > 0:
+        s1 = [0] * diff + s1
+    elif diff < 0:
+        s2 = [0] * -diff + s2
+
+    jp1p2 = addf(p, q, n, jp1, jp2)
+    jp1n2 = addf(p, q, n, jp1, neg(jp2, n))
+
+    precomp = ((None,           jp2,            neg(jp2, n)),
+               (jp1,            jp1p2,          jp1n2),
+               (neg(jp1, n),    neg(jp1n2, n),  neg(jp1p2, n)))
+    res = None
+
+    for i, j in zip(s1, s2):
+        res = doublef(p, q, n, res)
+        if i or j:
+            res = addf(p, q, n, res, precomp[i][j])
+    return res
+
+# Encapsulate muladdf()
+def muladdp(p, q, n, p1, c1, p2, c2):
+    '''Efficiently compute c1 * p1 + c2 * p2 in (x, y)-coordinates'''
+    return from_projective(muladdf(p, q, n,
+                                   to_projective(p1), c1,
+                                   to_projective(p2), c2), n)
+
+# POINT COMPRESSION ------------------------------------------------------------
+
+# Compute the square root modulo n
+
+
+# Determine the sign-bit of a point allowing to reconstruct y-coordinates
+# when x and the sign-bit are given:
+def sign_bit(p1):
+    '''Return the signedness of a point p1'''
+    return p1[1] % 2 if p1 else 0
+
+# Reconstruct the y-coordinate when curve parameters, x and the sign-bit of
+# the y coordinate are given:
+def y_from_x(x, p, q, n, sign):
+    '''Return the y coordinate over curve (p, q, n) for given (x, sign)'''
+
+    # optimized form of (x**3 - p*x - q) % n
+    a = (((x * x) % n - p) * x - q) % n
+    
+    
+
+if __name__ == "__main__":
+    import rsa
+    import time
+
+    t = time.time()
+    n = rsa.get_prime(256/8, 20)
+    tp = time.time() - t
+    p = rsa.random.randint(1, n)
+    p1 = (rsa.random.randint(1, n), rsa.random.randint(1, n))
+    q = curve_q(p1[0], p1[1], p, n)
+    r1 = rsa.random.randint(1,n)
+    r2 = rsa.random.randint(1,n)
+    q1 = mulp(p, q, n, p1, r1)
+    q2 = mulp(p, q, n, p1, r2)
+    s1 = mulp(p, q, n, q1, r2)
+    s2 = mulp(p, q, n, q2, r1)
+    s1 == s2
+    tt = time.time() - t
+
+    def test(tcount, bits = 256):
+        n = rsa.get_prime(bits/8, 20)
+        p = rsa.random.randint(1, n)
+        p1 = (rsa.random.randint(1, n), rsa.random.randint(1, n))
+        q = curve_q(p1[0], p1[1], p, n)
+        p2 = mulp(p, q, n, p1, rsa.random.randint(1, n))
+
+        c1 = [rsa.random.randint(1, n) for i in xrange(tcount)]
+        c2 = [rsa.random.randint(1, n) for i in xrange(tcount)]
+        c = zip(c1, c2)
+
+        t = time.time()
+        for i, j in c:
+            from_projective(addf(p, q, n,
+                                 mulf(p, q, n, to_projective(p1), i),
+                                 mulf(p, q, n, to_projective(p2), j)), n)
+        t1 = time.time() - t
+        t = time.time()
+        for i, j in c:
+            muladdp(p, q, n, p1, i, p2, j)
+        t2 = time.time() - t
+
+        return tcount, t1, t2
+        
+
+        
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/encoding.py
@@ -0,0 +1,178 @@
+#
+#   Encodings and Formats for Elliptic Curve Cryptography
+#
+
+import StringIO
+
+# Big-Endian Encoding
+
+def enc_long(n):
+    '''Encodes arbitrarily large number n to a sequence of bytes.
+    Big endian byte order is used.'''
+    s = ""
+    while n > 0:
+        s = chr(n & 0xFF) + s
+        n >>= 8
+    return s
+
+def enc_int(n):
+    '''Encodes an integer n to a 4-byte string.
+    Big endian byte order is used.'''
+    return chr((n >> 24) & 0xFF) + chr((n >> 16) & 0xFF) + \
+           chr((n >>  8) & 0xFF) + chr( n        & 0xFF)
+
+def enc_fixed_long(n, length):
+    return enc_long(n)[:length].rjust(length, '\x00')
+
+def dec_long(s):
+    '''Decodes s to its numeric representation.
+    Big endian byte order is used.'''
+    n = 0
+    for c in s:
+        n = (n << 8) | ord(c)
+    return n
+
+# dec_int not necessary,
+# dec_long does the same when provided with 4 bytes input.
+
+# Chunks
+
+def enc_chunks(*args):
+    '''Chain given string args or sub-chunks to a single chunk'''
+    return ''.join([enc_int(len(a)) + a for a in args])
+
+def dec_chunks(s):
+    '''Split a chunk into strings or sub-chunks'''
+    i = 0
+    result = []
+    while i < len(s):
+        size = dec_long(s[i : i + 4])
+        i += 4
+        result.append(s[i : i + size])
+        i += size
+    return result
+
+# Point and signature data
+
+def enc_point(p):
+    '''Encode a point p = (x, y)'''
+    x, y = p
+    sx = enc_long(x)
+    sy = enc_long(y)
+    diff = len(sx) - len(sy)
+    if diff > 0:
+        sy = '\x00' * diff + sy
+    elif diff < 0:
+        sx = '\x00' * -diff + sx
+    return sx + sy
+
+def dec_point(s):
+    '''Decode an even length string s to a point(x, y)'''
+    d = len(s) / 2
+    return (dec_long(s[:d]), dec_long(s[d:]))
+
+
+class Encoder:
+
+    def __init__(self):
+        self._io = StringIO.StringIO()
+
+    def int(self, n, size = 4):
+        self._io.write(enc_fixed_long(n, size))
+        return self
+
+    def long(self, n, pre = 2):
+        lstr = enc_long(n)
+        self._io.write(enc_fixed_long(len(lstr), pre) + lstr)
+        return self
+
+    def str(self, s, pre = 2):
+        self._io.write(enc_fixed_long(len(s), pre) + s)
+        return self
+
+    def point(self, p, pre = 2):
+        lstr = enc_point(p)
+        self._io.write(enc_fixed_long(len(lstr), pre) + lstr)
+        return self
+
+    def chunk(self, enc, pre = 2):
+        lstr = enc.out()
+        self._io.write(enc_fixed_long(len(lstr), pre) + lstr)
+        return self
+
+    def out(self):
+        return self._io.getvalue()
+
+class Decoder:
+
+    def __init__(self, data, offset = 0):
+        self._io = StringIO.StringIO(data)
+        self._io.seek(offset)
+        self._res = []
+        self._limit = None
+        self._parent = None
+
+    def _ret(self):
+##        if self._parent and self._io.tell() >= self._limit:
+##            return self.exit()
+##        else:
+##            return self
+        return self
+
+    def int(self, size = 4):
+        self._res.append(dec_long(self._io.read(size)))
+        return self._ret()
+        
+
+    def long(self, pre = 2):
+        llen = dec_long(self._io.read(pre))
+        self._res.append(dec_long(self._io.read(llen)))
+        return self._ret()
+
+    def str(self, pre = 2):
+        llen = dec_long(self._io.read(pre))
+        self._res.append(self._io.read(llen))
+        return self._ret()
+
+    def point(self, pre = 2):
+        llen = dec_long(self._io.read(pre))
+        self._res.append(dec_point(self._io.read(llen)))
+        return self._ret()
+
+    def enter(self, pre = 2):
+        llen = dec_long(self._io.read(pre))
+        subcoder = Decoder("")
+        subcoder._io = self._io
+        subcoder._parent = self
+        subcoder._limit = self._io.tell() + llen
+        return subcoder
+
+    def chunk(self, pre = 2):
+        llen = dec_long(self._io.read(pre))
+        self._res.append(Decoder(self._io.read(llen)))
+        return self._ret()
+
+    def exit(self):
+        if self._parent:
+            self._parent._io.seek(self._limit)
+            self._parent._res.append(self._res)
+            return self._parent
+        else:
+            raise RuntimeError, "Cannont exit top level Decoder"
+
+    def continues(self):
+        return (not self._limit) or (self._io.tell() < self._limit)
+
+    def out(self, exit_all = False):
+        if exit_all and self._parent:
+            return self.exit().out()
+        else:
+            r = self._res
+            self._res = []
+            return r
+
+    def only(self):
+        if self._res:
+            return self._res.pop(0)
+        else:
+            return RuntimeError, "Only what? (Empty decoder stack)"
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/performance.py
@@ -0,0 +1,50 @@
+from Key import Key
+import time
+from collections import OrderedDict
+
+def test_generation_perf(n = 100):
+    results = OrderedDict()
+    for bits in (192, 224, 256, 384, 521):
+        t = time.time()
+        for i in xrange(n):
+            k = Key.generate(bits)
+        t = time.time() - t
+        results[bits] = t
+    return results
+        
+def test_signing_perf(n = 100):
+    results = OrderedDict()
+    for bits in (192, 224, 256, 384, 521):
+        k = Key.generate(bits)
+        t = time.time()
+        for i in xrange(n):
+            k.sign("random string")
+        t = time.time() - t
+        results[bits] = t
+    return results
+
+def test_verification_perf(n = 100):
+    results = OrderedDict()
+    for bits in (192, 224, 256, 384, 521):
+        k = Key.generate(bits)
+        s = k.sign("random string")
+        t = time.time()
+        for i in xrange(n):
+            k.verify("random string", s)
+        t = time.time() - t
+        results[bits] = t
+    return results
+            
+def print_dict(title, d):
+    print title
+    print '-' * len(title)
+    for k, v in d.items():
+        print k, '\t', v
+    print
+
+n = 100
+print_dict("Key generation", test_generation_perf(n))
+print_dict("Signing", test_signing_perf(n))
+print_dict("Verifying", test_verification_perf(n))
+
+        
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/primes.py
@@ -0,0 +1,82 @@
+'''
+This module implements simple prime generation and primality testing.
+'''
+
+from random import SystemRandom
+random = SystemRandom()
+from os import urandom
+
+def exp(x, n, m):
+    '''Efficiently compute x ** n mod m'''
+    y = 1
+    z = x
+    while n > 0:
+        if n & 1:
+            y = (y * z) % m
+        z = (z * z) % m
+        n //= 2
+    return y
+
+
+# Miller-Rabin-Test
+
+def prime(n, k):
+    '''Checks whether n is probably prime (with probability 1 - 4**(-k)'''
+    
+    if n % 2 == 0:
+        return False
+    
+    d = n - 1
+    s = 0
+    
+    while d % 2 == 0:
+        s += 1
+        d /= 2
+        
+    for i in xrange(k):
+        
+        a = long(2 + random.randint(0, n - 4))
+        x = exp(a, d, n)
+        if (x == 1) or (x == n - 1):
+            continue
+        
+        for r in xrange(1, s):
+            x = (x * x) % n
+            
+            if x == 1:
+                return False
+            
+            if x == n - 1:
+                break
+            
+        else:
+            return False
+    return True
+
+
+# Generate and Test Algorithms
+
+def get_prime(size, accuracy):
+    '''Generate a pseudorandom prime number with the specified size (bytes).'''
+    
+    while 1:
+
+        # read some random data from the operating system
+        rstr = urandom(size - 1)
+        r = 128 | ord(urandom(1))   # MSB = 1 (not less than size)
+        for c in rstr:
+            r = (r << 8) | ord(c)
+        r |= 1                      # LSB = 1 (odd)
+
+        # test whether this results in a prime number
+        if prime(r, accuracy):
+            return r
+
+
+def get_prime_upto(n, accuracy):
+    '''Find largest prime less than n'''
+    n |= 1
+    while n > 0:
+        n -= 2
+        if prime(n, accuracy):
+            return n
new file mode 100644
--- /dev/null
+++ b/python/PyECC/ecc/shacrypt.py
@@ -0,0 +1,38 @@
+# ------------------------------------------------------------------------------
+#
+#   SHA-512-BASED FEISTEL CIPHER
+#   by Toni Mattis
+#
+#   Feistel Function:   SHA-512(Block || Key)
+#   Key Size:           Fully Dynamic
+#   Block Size:         1024 Bits
+#   Rounds:             User-Specified
+#
+# ------------------------------------------------------------------------------
+
+from hashlib import sha512
+
+BPOS = tuple(range(64))
+
+def enc_block(block, key, rounds = 16):
+    x = block[:64]
+    y = block[64:]
+    for i in xrange(rounds):
+        h = sha512(x + key).digest()
+        y = ''.join([chr(ord(y[k]) ^ ord(h[k])) for k in BPOS])
+        h = sha512(y + key).digest()
+        x = ''.join([chr(ord(x[k]) ^ ord(h[k])) for k in BPOS])
+    return x + y
+        
+def dec_block(block, key, rounds = 16):
+    x = block[:64]
+    y = block[64:]
+    for i in xrange(rounds):
+        h = sha512(y + key).digest()
+        x = ''.join([chr(ord(x[k]) ^ ord(h[k])) for k in BPOS])
+        h = sha512(x + key).digest()
+        y = ''.join([chr(ord(y[k]) ^ ord(h[k])) for k in BPOS])
+    return x + y
+
+
+    
new file mode 100644
--- /dev/null
+++ b/python/PyECC/setup.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2007 The Python-Twitter Developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# copied from https://github.com/bear/python-twitter/blob/master/setup.py
+#
+
+'''The setup and build script for the python-twitter library.'''
+
+__author__ = 'niccokunzmann@aol.com'
+__version__ = '0.0.1'
+
+
+# The base package metadata to be used by both distutils and setuptools
+METADATA = dict(
+    name = "ecc",
+    version = __version__,
+    packages = ['ecc'],
+    author='Toni Mattis',
+    author_email='solaris@live.de',
+    description='Pure Python implementation of an elliptic curve cryptosystem based on FIPS 186-3',
+    license='MIT',
+    url='https://github.com/niccokunzmann/ecc',
+    keywords='elliptic curve cryptosystem rabbit cipher',
+)
+
+# Extra package metadata to be used only if setuptools is installed
+SETUPTOOLS_METADATA = dict(
+    install_requires = [],
+    include_package_data = True,
+    classifiers = [
+        'Development Status :: 4 - Beta',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: MIT License',
+        'Topic :: Software Development :: Libraries :: Python Modules',
+        'Topic :: Communications',
+        'Topic :: Security :: Cryptography', 
+        'Topic :: Internet',
+    ],
+##    test_suite = 'distacc_test',
+)
+
+
+def Read(file):
+    return open(file).read()
+
+def BuildLongDescription():
+    return '\n'.join([Read('README.md'), ])
+
+def Main():
+    # Build the long_description from the README and CHANGES
+    METADATA['long_description'] = BuildLongDescription()
+
+    # Use setuptools if available, otherwise fallback and use distutils
+    try:
+        import setuptools
+        METADATA.update(SETUPTOOLS_METADATA)
+        setuptools.setup(**METADATA)
+    except ImportError:
+        import distutils.core
+        distutils.core.setup(**METADATA)
+
+
+if __name__ == '__main__':
+    Main()
--- a/security/manager/ssl/tests/unit/moz.build
+++ b/security/manager/ssl/tests/unit/moz.build
@@ -8,16 +8,17 @@ DIRS += ['tlsserver']
 TEST_DIRS += [
     'test_cert_eku',
     'test_cert_embedded_null',
     'test_cert_keyUsage',
     'test_cert_trust',
     'test_cert_version',
     'test_ev_certs',
     'test_intermediate_basic_usage_constraints',
+    'test_keysize',
     'test_keysize_ev',
     'test_pinning_dynamic',
     'test_ocsp_fetch_method',
     'test_ocsp_url',
     'test_validity',
 ]
 
 if not CONFIG['MOZ_NO_SMART_CARDS']:
--- a/security/manager/ssl/tests/unit/pycert.py
+++ b/security/manager/ssl/tests/unit/pycert.py
@@ -7,20 +7,21 @@
 """
 Reads a certificate specification from stdin or a file and outputs a
 signed x509 certificate with the desired properties.
 
 The input format is as follows:
 
 issuer:<string to use as the issuer common name>
 subject:<string to use as the subject common name>
-[version:<{1,2,3,4}>]
+[version:{1,2,3,4}]
 [validity:<YYYYMMDD-YYYYMMDD|duration in days>]
 [issuerKey:<key specification>]
 [subjectKey:<key specification>]
+[signature:{sha256WithRSAEncryption,ecdsaWithSHA256}]
 [extension:<extension name:<extension-specific data>>]
 [...]
 
 Known extensions are:
 basicConstraints:[cA],[pathLenConstraint]
 keyUsage:[digitalSignature,nonRepudiation,keyEncipherment,
           dataEncipherment,keyAgreement,keyCertSign,cRLSign]
 extKeyUsage:[serverAuth,clientAuth,codeSigning,emailProtection
@@ -28,31 +29,30 @@ extKeyUsage:[serverAuth,clientAuth,codeS
              OCSPSigning,timeStamping]
 subjectAlternativeName:[<dNSName>,...]
 authorityInformationAccess:<OCSP URI>
 certificatePolicies:<policy OID>
 
 Where:
   [] indicates an optional field or component of a field
   <> indicates a required component of a field
-  {} indicates choice among a set of values
-  [a,b,c] indicates a list of potential values, of which more than one
+  {} indicates a choice of exactly one value among a set of values
+  [a,b,c] indicates a list of potential values, of which zero or more
           may be used
 
 For instance, the version field is optional. However, if it is
 specified, it must have exactly one value from the set {1,2,3,4}.
 
-In the future it will be possible to specify other properties of the
-generated certificate (for example, the signature algorithm). For now,
-those fields have reasonable default values. By default one shared RSA
+Most fields have reasonable default values. By default one shared RSA
 key is used for all signatures and subject public key information
 fields. Using "issuerKey:<key specification>" or
-"subjectKey:<key specification>" causes a different RSA key be used for
+"subjectKey:<key specification>" causes a different key be used for
 signing or as the subject public key information field, respectively.
 See pykey.py for the list of available specifications.
+The signature algorithm is sha256WithRSAEncryption by default.
 
 The validity period may be specified as either concrete notBefore and
 notAfter values or as a validity period centered around 'now'. For the
 latter, this will result in a notBefore of 'now' - duration/2 and a
 notAfter of 'now' + duration/2.
 """
 
 from pyasn1.codec.der import decoder
@@ -144,18 +144,19 @@ def stringToAccessDescription(string):
 
 def stringToAlgorithmIdentifier(string):
     """Helper function that converts a description of an algorithm
     to a representation usable by the pyasn1 package"""
     algorithmIdentifier = rfc2459.AlgorithmIdentifier()
     algorithm = None
     if string == 'sha256WithRSAEncryption':
         algorithm = univ.ObjectIdentifier('1.2.840.113549.1.1.11')
-    # In the future, more algorithms will be supported.
-    if algorithm == None:
+    elif string == 'ecdsaWithSHA256':
+        algorithm = univ.ObjectIdentifier('1.2.840.10045.4.3.2')
+    else:
         raise UnknownAlgorithmTypeError(string)
     algorithmIdentifier.setComponentByName('algorithm', algorithm)
     return algorithmIdentifier
 
 def stringToCommonName(string):
     """Helper function for taking a string and building an x520 name
     representation usable by the pyasn1 package. Currently returns one
     RDN with one AVA consisting of a Common Name encoded as a
@@ -192,36 +193,38 @@ class Certificate:
         self.signature = 'sha256WithRSAEncryption'
         self.issuer = 'Default Issuer'
         actualNow = datetime.datetime.utcnow()
         self.now = datetime.datetime.strptime(str(actualNow.year), '%Y')
         aYearAndAWhile = datetime.timedelta(days=550)
         self.notBefore = self.now - aYearAndAWhile
         self.notAfter = self.now + aYearAndAWhile
         self.subject = 'Default Subject'
-        self.signatureAlgorithm = 'sha256WithRSAEncryption'
         self.extensions = None
-        self.subjectKey = pykey.RSAKey('default')
-        self.issuerKey = pykey.RSAKey('default')
+        self.subjectKey = pykey.keyFromSpecification('default')
+        self.issuerKey = pykey.keyFromSpecification('default')
         self.decodeParams(paramStream)
         self.serialNumber = self.generateSerialNumber()
 
     def generateSerialNumber(self):
         """Generates a serial number for this certificate based on its
         contents. Intended to be reproducible for compatibility with
         the build system on OS X (see the comment above main, later in
         this file)."""
         hasher = hashlib.sha256()
         hasher.update(str(self.versionValue))
         hasher.update(self.signature)
         hasher.update(self.issuer)
         hasher.update(str(self.notBefore))
         hasher.update(str(self.notAfter))
         hasher.update(self.subject)
-        hasher.update(self.signatureAlgorithm)
+        # Bug 1194419: This is duplicated so as to not have to
+        # re-generate the EV testing root certificates. At some point
+        # we should clean this up and re-generate them.
+        hasher.update(self.signature)
         if self.extensions:
             for extension in self.extensions:
                 hasher.update(str(extension))
         serialBytes = [ord(c) for c in hasher.digest()[:20]]
         # Ensure that the most significant bit isn't set (which would
         # indicate a negative number, which isn't valid for serial
         # numbers).
         serialBytes[0] &= 0x7f
@@ -250,16 +253,18 @@ class Certificate:
         elif param == 'validity':
             self.decodeValidity(value)
         elif param == 'extension':
             self.decodeExtension(value)
         elif param == 'issuerKey':
             self.setupKey('issuer', value)
         elif param == 'subjectKey':
             self.setupKey('subject', value)
+        elif param == 'signature':
+            self.signature = value
         else:
             raise UnknownParameterTypeError(param)
 
     def setVersion(self, version):
         intVersion = int(version)
         if intVersion >= 1 and intVersion <= 4:
             self.versionValue = intVersion - 1
         else:
@@ -290,19 +295,19 @@ class Certificate:
             self.addAuthorityInformationAccess(value)
         elif extensionType == 'certificatePolicies':
             self.addCertificatePolicies(value)
         else:
             raise UnknownExtensionTypeError(extensionType)
 
     def setupKey(self, subjectOrIssuer, value):
         if subjectOrIssuer == 'subject':
-            self.subjectKey = pykey.RSAKey(value)
+            self.subjectKey = pykey.keyFromSpecification(value)
         elif subjectOrIssuer == 'issuer':
-            self.issuerKey = pykey.RSAKey(value)
+            self.issuerKey = pykey.keyFromSpecification(value)
         else:
             raise UnknownKeyTargetError(subjectOrIssuer)
 
     def addExtension(self, extensionType, extensionValue):
         if not self.extensions:
             self.extensions = []
         encapsulated = univ.OctetString(encoder.encode(extensionValue))
         extension = rfc2459.Extension()
@@ -406,40 +411,38 @@ class Certificate:
         return datetimeToTime(self.notBefore)
 
     def getNotAfter(self):
         return datetimeToTime(self.notAfter)
 
     def getSubject(self):
         return stringToCommonName(self.subject)
 
-    def getSignatureAlgorithm(self):
-        return stringToAlgorithmIdentifier(self.signature)
-
     def toDER(self):
+        signatureOID = self.getSignature()
         tbsCertificate = rfc2459.TBSCertificate()
         tbsCertificate.setComponentByName('version', self.getVersion())
         tbsCertificate.setComponentByName('serialNumber', self.getSerialNumber())
-        tbsCertificate.setComponentByName('signature', self.getSignature())
+        tbsCertificate.setComponentByName('signature', signatureOID)
         tbsCertificate.setComponentByName('issuer', self.getIssuer())
         tbsCertificate.setComponentByName('validity', self.getValidity())
         tbsCertificate.setComponentByName('subject', self.getSubject())
         tbsCertificate.setComponentByName('subjectPublicKeyInfo',
                                           self.subjectKey.asSubjectPublicKeyInfo())
         if self.extensions:
             extensions = rfc2459.Extensions().subtype(
                 explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))
             count = 0
             for extension in self.extensions:
                 extensions.setComponentByPosition(count, extension)
                 count += 1
             tbsCertificate.setComponentByName('extensions', extensions)
         certificate = rfc2459.Certificate()
         certificate.setComponentByName('tbsCertificate', tbsCertificate)
-        certificate.setComponentByName('signatureAlgorithm', self.getSignatureAlgorithm())
+        certificate.setComponentByName('signatureAlgorithm', signatureOID)
         tbsDER = encoder.encode(tbsCertificate)
         certificate.setComponentByName('signatureValue', self.issuerKey.sign(tbsDER))
         return encoder.encode(certificate)
 
     def toPEM(self):
         output = '-----BEGIN CERTIFICATE-----'
         der = self.toDER()
         b64 = base64.b64encode(der)
--- a/security/manager/ssl/tests/unit/pykey.py
+++ b/security/manager/ssl/tests/unit/pykey.py
@@ -16,26 +16,34 @@ default: a 2048-bit RSA key
 alternate: a different 2048-bit RSA key
 ev: a 2048-bit RSA key that, when combined with the right pycert
     specification, results in a certificate that is enabled for
     extended validation in debug Firefox (see ExtendedValidation.cpp).
 evRSA2040: a 2040-bit RSA key that, when combined with the right pycert
            specification, results in a certificate that is enabled for
            extended validation in debug Firefox.
 rsa2040: a 2040-bit RSA key
-
-In the future it will be possible to specify other properties of the key
-(type, strength, signature algorithm, etc.).
+rsa1024: a 1024-bit RSA key
+rsa1016: a 1016-bit RSA key
+secp256k1: an ECC key on the curve secp256k1
+secp244r1: an ECC key on the curve secp244r1
+secp256r1: an ECC key on the curve secp256r1
+secp384r1: an ECC key on the curve secp384r1
+secp521r1: an ECC key on the curve secp521r1
 """
 
 from pyasn1.codec.der import encoder
 from pyasn1.type import univ, namedtype
 from pyasn1_modules import rfc2459
+from ecc import encoding
+from ecc import Key
+from ecc.ecdsa import randkey
 import base64
 import binascii
+import mock
 import rsa
 import sys
 
 def byteStringToHexifiedBitString(string):
     """Takes a string of bytes and returns a hex string representing
     those bytes for use with pyasn1.type.univ.BitString. It must be of
     the form "'<hex bytes>'H", where the trailing 'H' indicates to
     pyasn1 that the input is a hex string."""
@@ -76,16 +84,24 @@ class RSAPrivateKey(univ.Sequence):
         namedtype.NamedType('prime1', univ.Integer()),
         namedtype.NamedType('prime2', univ.Integer()),
         namedtype.NamedType('exponent1', univ.Integer()),
         namedtype.NamedType('exponent2', univ.Integer()),
         namedtype.NamedType('coefficient', univ.Integer()),
     )
 
 
+class ECPoint(univ.Sequence):
+    """Helper type for encoding a EC point"""
+    componentType = namedtype.NamedTypes(
+        namedtype.NamedType('x', univ.Integer()),
+        namedtype.NamedType('y', univ.Integer())
+    )
+
+
 class PrivateKeyInfo(univ.Sequence):
     """Helper type for encoding a PKCS #8 private key info"""
     componentType = namedtype.NamedTypes(
         namedtype.NamedType('version', univ.Integer()),
         namedtype.NamedType('privateKeyAlgorithm', rfc2459.AlgorithmIdentifier()),
         namedtype.NamedType('privateKey', univ.OctetString())
     )
 
@@ -301,66 +317,134 @@ class RSAKey:
     evRSA2040_coef = long(
         '040257c0d4a21c0b9843297c65652db66304fb263773d728b6abfa06d37a'
         'c0ca62c628023e09e37dc0a901e4ce1224180e2582a3aa4b6a1a7b98e2bd'
         '70077aec14ac8ab66a755c71e0fc102471f9bbc1b46a95aa0b645f2c38e7'
         '6450289619ea3f5e8ae61037bffcf8249f22aa4e76e2a01909f3feb290ce'
         '93edf57b10ebe796', 16)
 
     rsa2040_N = long(
-      '00bac0652fdfbc0055882ffbaeaceec88fa2d083c297dd5d40664dd3d90f'
-      '52f9aa02bd8a50fba16e0fd991878ef475f9b350d9f8e3eb2abd717ce327'
-      'b09788531f13df8e3e4e3b9d616bb8a41e5306eed2472163161051180127'
-      '6a4eb66f07331b5cbc8bcae7016a8f9b3d4f2ac4553c624cf5263bcb348e'
-      '8840de6612870960a792191b138fb217f765cec7bff8e94f16b39419bf75'
-      '04c59a7e4f79bd6d173e9c7bf3d9d2a4e73cc180b0590a73d584fb7fc9b5'
-      '4fa544607e53fc685c7a55fd44a81d4142b6af51ea6fa6cea52965a2e8c5'
-      'd84f3ca024d6fbb9b005b9651ce5d9f2ecf40ed404981a9ffc02636e311b'
-      '095c6332a0c87dc39271b5551481774b', 16)
+        '00bac0652fdfbc0055882ffbaeaceec88fa2d083c297dd5d40664dd3d90f'
+        '52f9aa02bd8a50fba16e0fd991878ef475f9b350d9f8e3eb2abd717ce327'
+        'b09788531f13df8e3e4e3b9d616bb8a41e5306eed2472163161051180127'
+        '6a4eb66f07331b5cbc8bcae7016a8f9b3d4f2ac4553c624cf5263bcb348e'
+        '8840de6612870960a792191b138fb217f765cec7bff8e94f16b39419bf75'
+        '04c59a7e4f79bd6d173e9c7bf3d9d2a4e73cc180b0590a73d584fb7fc9b5'
+        '4fa544607e53fc685c7a55fd44a81d4142b6af51ea6fa6cea52965a2e8c5'
+        'd84f3ca024d6fbb9b005b9651ce5d9f2ecf40ed404981a9ffc02636e311b'
+        '095c6332a0c87dc39271b5551481774b', 16)
     rsa2040_E = 65537L
     rsa2040_D = long(
-      '603db267df97555cbed86b8df355034af28f1eb7f3e7829d239bcc273a7c'
-      '7a69a10be8f21f1b6c4b02c6bae3731c3158b5bbff4605f57ab7b7b2a0cb'
-      'a2ec005a2db5b1ea6e0aceea5bc745dcd2d0e9d6b80d7eb0ea2bc08127bc'
-      'e35fa50c42cc411871ba591e23ba6a38484a33eff1347f907ee9a5a92a23'
-      '11bb0b435510020f78e3bb00099db4d1182928096505fcba84f3ca1238fd'
-      '1eba5eea1f391bbbcc5424b168063fc17e1ca6e1912ccba44f9d0292308a'
-      '1fedb80612529b39f59d0a3f8180b5ba201132197f93a5815ded938df8e7'
-      'd93c9b15766588f339bb59100afda494a7e452d7dd4c9a19ce2ec3a33a18'
-      'b20f0b4dade172bee19f26f0dcbe41', 16)
+        '603db267df97555cbed86b8df355034af28f1eb7f3e7829d239bcc273a7c'
+        '7a69a10be8f21f1b6c4b02c6bae3731c3158b5bbff4605f57ab7b7b2a0cb'
+        'a2ec005a2db5b1ea6e0aceea5bc745dcd2d0e9d6b80d7eb0ea2bc08127bc'
+        'e35fa50c42cc411871ba591e23ba6a38484a33eff1347f907ee9a5a92a23'
+        '11bb0b435510020f78e3bb00099db4d1182928096505fcba84f3ca1238fd'
+        '1eba5eea1f391bbbcc5424b168063fc17e1ca6e1912ccba44f9d0292308a'
+        '1fedb80612529b39f59d0a3f8180b5ba201132197f93a5815ded938df8e7'
+        'd93c9b15766588f339bb59100afda494a7e452d7dd4c9a19ce2ec3a33a18'
+        'b20f0b4dade172bee19f26f0dcbe41', 16)
     rsa2040_P = long(
-      '0ec3869cb92d406caddf7a319ab29448bc505a05913707873361fc5b986a'
-      '499fb65eeb815a7e37687d19f128087289d9bb8818e7bcca502c4900ad9a'
-      'ece1179be12ff3e467d606fc820ea8f07ac9ebffe2236e38168412028822'
-      '3e42dbe68dfd972a85a6447e51695f234da7911c67c9ab9531f33df3b994'
-      '32d4ee88c9a4efbb', 16)
+        '0ec3869cb92d406caddf7a319ab29448bc505a05913707873361fc5b986a'
+        '499fb65eeb815a7e37687d19f128087289d9bb8818e7bcca502c4900ad9a'
+        'ece1179be12ff3e467d606fc820ea8f07ac9ebffe2236e38168412028822'
+        '3e42dbe68dfd972a85a6447e51695f234da7911c67c9ab9531f33df3b994'
+        '32d4ee88c9a4efbb', 16)
     rsa2040_Q = long(
-      '0ca63934549e85feac8e0f5604303fd1849fe88af4b7f7e1213283bbc7a2'
-      'c2a509f9273c428c68de3db93e6145f1b400bd6d4a262614e9043ad362d4'
-      'eba4a6b995399c8934a399912199e841d8e8dbff0489f69e663796730b29'
-      '80530b31cb70695a21625ea2adccc09d930516fa872211a91e22dd89fd9e'
-      'b7da8574b72235b1', 16)
+        '0ca63934549e85feac8e0f5604303fd1849fe88af4b7f7e1213283bbc7a2'
+        'c2a509f9273c428c68de3db93e6145f1b400bd6d4a262614e9043ad362d4'
+        'eba4a6b995399c8934a399912199e841d8e8dbff0489f69e663796730b29'
+        '80530b31cb70695a21625ea2adccc09d930516fa872211a91e22dd89fd9e'
+        'b7da8574b72235b1', 16)
     rsa2040_exp1 = long(
-      '0d7d3a75e17f65f8a658a485c4095c10a4f66979e2b73bca9cf8ef21253e'
-      '1facac6d4791f58392ce8656f88f1240cc90c29653e3100c6d7a38ed44b1'
-      '63b339e5f3b6e38912126c69b3ceff2e5192426d9649b6ffca1abb75d2ba'
-      '2ed6d9a26aa383c5973d56216ff2edb90ccf887742a0f183ac92c94cf187'
-      '657645c7772d9ad7', 16)
+        '0d7d3a75e17f65f8a658a485c4095c10a4f66979e2b73bca9cf8ef21253e'
+        '1facac6d4791f58392ce8656f88f1240cc90c29653e3100c6d7a38ed44b1'
+        '63b339e5f3b6e38912126c69b3ceff2e5192426d9649b6ffca1abb75d2ba'
+        '2ed6d9a26aa383c5973d56216ff2edb90ccf887742a0f183ac92c94cf187'
+        '657645c7772d9ad7', 16)
     rsa2040_exp2 = long(
-      '03f550194c117f24bea285b209058032f42985ff55acebe88b16df9a3752'
-      '7b4e61dc91a68dbc9a645134528ce5f248bda2893c96cb7be79ee73996c7'
-      'c22577f6c2f790406f3472adb3b211b7e94494f32c5c6fcc0978839fe472'
-      '4c31b06318a2489567b4fca0337acb1b841227aaa5f6c74800a2306929f0'
-      '2ce038bad943df41', 16)
+        '03f550194c117f24bea285b209058032f42985ff55acebe88b16df9a3752'
+        '7b4e61dc91a68dbc9a645134528ce5f248bda2893c96cb7be79ee73996c7'
+        'c22577f6c2f790406f3472adb3b211b7e94494f32c5c6fcc0978839fe472'
+        '4c31b06318a2489567b4fca0337acb1b841227aaa5f6c74800a2306929f0'
+        '2ce038bad943df41', 16)
     rsa2040_coef = long(
-      '080a7dbfa8c2584814c71664c56eb62ce4caf16afe88d4499159d674774a'
-      '3a3ecddf1256c02fc91525c527692422d0aba94e5c41ee12dc71bb66f867'
-      '9fa17e096f28080851ba046eb31885c1414e8985ade599d907af17453d1c'
-      'caea2c0d06443f8367a6be154b125e390ee0d90f746f08801dd3f5367f59'
-      'fba2e5a67c05f375', 16)
+        '080a7dbfa8c2584814c71664c56eb62ce4caf16afe88d4499159d674774a'
+        '3a3ecddf1256c02fc91525c527692422d0aba94e5c41ee12dc71bb66f867'
+        '9fa17e096f28080851ba046eb31885c1414e8985ade599d907af17453d1c'
+        'caea2c0d06443f8367a6be154b125e390ee0d90f746f08801dd3f5367f59'
+        'fba2e5a67c05f375', 16)
+
+    rsa1024_N = long(
+        '00d3a97440101eba8c5df9503e6f935eb52ffeb3ebe9d0dc5cace26f973c'
+        'a94cbc0d9c31d66c0c013bce9c82d0d480328df05fb6bcd7990a5312ddae'
+        '6152ad6ee61c8c1bdd8663c68bd36224a9882ae78e89f556dfdbe6f51da6'
+        '112cbfc27c8a49336b41afdb75321b52b24a7344d1348e646351a551c757'
+        '1ccda0b8fe35f61a75', 16)
+    rsa1024_E = 65537L
+    rsa1024_D = long(
+        '5b6708e185548fc07ff062dba3792363e106ff9177d60ee3227162391024'
+        '1813f958a318f26db8b6a801646863ebbc69190d6c2f5e7723433e99666d'
+        '76b3987892cd568f1f18451e8dc05477c0607ee348380ebb7f4c98d0c036'
+        'a0260bc67b2dab46cbaa4ce87636d839d8fddcbae2da3e02e8009a21225d'
+        'd7e47aff2f82699d', 16)
+    rsa1024_P = long(
+        '00fcdee570323e8fc399dbfc63d8c1569546fb3cd6886c628668ab1e1d0f'
+        'ca71058febdf76d702970ad6579d80ac2f9521075e40ef8f3f39983bd819'
+        '07e898bad3', 16)
+    rsa1024_Q = long(
+        '00d64801c955b4eb75fbae230faa8b28c9cc5e258be63747ff5ac8d2af25'
+        '3e9f6d6ce03ea2eb13ae0eb32572feb848c32ca00743635374338fedacd8'
+        'c5885f7897', 16)
+    rsa1024_exp1 = long(
+        '76c0526d5b1b28368a75d5d42a01b9a086e20b9310241e2cd2d0b166a278'
+        'c694ff1e9d25d9193d47789b52bb0fa194de1af0b77c09007f12afdfeef9'
+        '58d108c3', 16)
+    rsa1024_exp2 = long(
+        '008a41898d8b14217c4d782cbd15ef95d0a660f45ed09a4884f4e170367b'
+        '946d2f20398b907896890e88fe17b54bd7febe133ebc7720c86fe0649cca'
+        '7ca121e05f', 16)
+    rsa1024_coef = long(
+        '22db133445f7442ea2a0f582031ee214ff5f661972986f172651d8d6b4ec'
+        '3163e99bff1c82fe58ec3d075c6d8f26f277020edb77c3ba821b9ba3ae18'
+        'ff8cb2cb', 16)
+
+    rsa1016_N = long(
+        '00d29bb12fb84fddcd29b3a519cb66c43b8d8f8be545ba79384ce663ed03'
+        'df75991600eb920790d2530cece544db99a71f05896a3ed207165534aa99'
+        '057e47c47e3bc81ada6fa1e12e37268b5046a55268f9dad7ccb485d81a2e'
+        '19d50d4f0b6854acaf6d7be69d9a083136e15afa8f53c1c8c84fc6077279'
+        'dd0e55d7369a5bdd', 16)
+    rsa1016_E = 65537L
+    rsa1016_D = long(
+        '3c4965070bf390c251d5a2c5277c5b5fd0bdee85cad7fe2b27982bb28511'
+        '4a507004036ae1cf8ae54b25e4db39215abd7e903f618c2d8b2f08cc6cd1'
+        '2dbccd72205e4945b6b3df389e5e43de0a148bb2c84e2431fdbe5920b044'
+        'bb272f45ecff0721b7dfb60397fc613a9ea35c22300530cae8f9159c534d'
+        'f3bf0910951901', 16)
+    rsa1016_P = long(
+        '0f9f17597c85b8051b9c69afb55ef576c996dbd09047d0ccde5b9d60ea5c'
+        '67fe4fac67be803f4b6ac5a3f050f76b966fb14f5cf105761e5ade6dd960'
+        'b183ba55', 16)
+    rsa1016_Q = long(
+        '0d7b637112ce61a55168c0f9c9386fb279ab40cba0d549336bba65277263'
+        'aac782611a2c81d9b635cf78c40018859e018c5e9006d12e3d2ee6f346e7'
+        '9fa43369', 16)
+    rsa1016_exp1 = long(
+        '09fd6c9a3ea6e91ae32070f9fc1c210ff9352f97be5d1eeb951bb39681e9'
+        'dc5b672a532221b3d8900c9a9d99b9d0a4e102dc450ca1b87b0b1389de65'
+        '16c0ae0d', 16)
+    rsa1016_exp2 = long(
+        '0141b832491b7dd4a83308920024c79cae64bd447df883bb4c5672a96bab'
+        '48b7123b34f26324452cdceb17f21e570e347cbe2fd4c2d8f9910eac2cb6'
+        'd895b8c9', 16)
+    rsa1016_coef = long(
+        '0458dd6aee18c88b2f9b81f1bc3075ae20dc1f9973d20724f20b06043d61'
+        '47c8789d4a07ae88bc82c8438c893e017b13947f62e0b18958a31eb664b1'
+        '9e64d3e0', 16)
 
     def __init__(self, specification):
         if specification == 'default':
             self.RSA_N = self.sharedRSA_N
             self.RSA_E = self.sharedRSA_E
             self.RSA_D = self.sharedRSA_D
             self.RSA_P = self.sharedRSA_P
             self.RSA_Q = self.sharedRSA_Q
@@ -398,16 +482,34 @@ class RSAKey:
             self.RSA_N = self.rsa2040_N
             self.RSA_E = self.rsa2040_E
             self.RSA_D = self.rsa2040_D
             self.RSA_P = self.rsa2040_P
             self.RSA_Q = self.rsa2040_Q
             self.RSA_exp1 = self.rsa2040_exp1
             self.RSA_exp2 = self.rsa2040_exp2
             self.RSA_coef = self.rsa2040_coef
+        elif specification == 'rsa1024':
+            self.RSA_N = self.rsa1024_N
+            self.RSA_E = self.rsa1024_E
+            self.RSA_D = self.rsa1024_D
+            self.RSA_P = self.rsa1024_P
+            self.RSA_Q = self.rsa1024_Q
+            self.RSA_exp1 = self.rsa1024_exp1
+            self.RSA_exp2 = self.rsa1024_exp2
+            self.RSA_coef = self.rsa1024_coef
+        elif specification == 'rsa1016':
+            self.RSA_N = self.rsa1016_N
+            self.RSA_E = self.rsa1016_E
+            self.RSA_D = self.rsa1016_D
+            self.RSA_P = self.rsa1016_P
+            self.RSA_Q = self.rsa1016_Q
+            self.RSA_exp1 = self.rsa1016_exp1
+            self.RSA_exp2 = self.rsa1016_exp2
+            self.RSA_coef = self.rsa1016_coef
         else:
             raise UnknownKeySpecificationError(specification)
 
     def toDER(self):
         privateKeyInfo = PrivateKeyInfo()
         privateKeyInfo.setComponentByName('version', 0)
         algorithmIdentifier = rfc2459.AlgorithmIdentifier()
         algorithmIdentifier.setComponentByName('algorithm', rfc2459.rsaEncryption)
@@ -456,19 +558,150 @@ class RSAKey:
         """Returns a hexified bit string representing a
         signature by this key over the specified data.
         Intended for use with pyasn1.type.univ.BitString"""
         rsaPrivateKey = rsa.PrivateKey(self.RSA_N, self.RSA_E, self.RSA_D, self.RSA_P, self.RSA_Q)
         signature = rsa.sign(data, rsaPrivateKey, 'SHA-256')
         return byteStringToHexifiedBitString(signature)
 
 
+ecPublicKey = univ.ObjectIdentifier('1.2.840.10045.2.1')
+secp256k1 = univ.ObjectIdentifier('1.3.132.0.10')
+secp224r1 = univ.ObjectIdentifier('1.3.132.0.33')
+secp256r1 = univ.ObjectIdentifier('1.2.840.10045.3.1.7')
+secp384r1 = univ.ObjectIdentifier('1.3.132.0.34')
+secp521r1 = univ.ObjectIdentifier('1.3.132.0.35')
+
+# ecc.curves.DOMAINS uses a 5-tuple to define curves.
+# The first number is p where F_p is the finite field of the curve.
+# The second number is the order n of the base point G.
+# The third number is the parameter b of the definition of the curve
+#   E: y^2 = x^3 + ax + b (a in this case is 0)
+# The fourth and fifth numbers constitute the base point G.
+secp256k1Params = (long('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16),
+                   long('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16),
+                   7,
+                   long('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 16),
+                   long('483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', 16))
+
+def longToEvenLengthHexString(val):
+    h = format(val, 'x')
+    if not len(h) % 2 == 0:
+        h = '0' + h
+    return h
+
+def notRandom(n):
+    return n * '\x04'
+
+class ECCKey:
+    secp256k1Encoded = str('08fd87b04fba98090100004035ee7c7289d8fef7a8'
+        '6afe5da66d8bc2ebb6a8543fd2fead089f45ce7acd0fa64382a9500c41dad'
+        '770ffd4b511bf4b492eb1238800c32c4f76c73a3f3294e7c5002067cebc20'
+        '8a5fa3df16ec2bb34acc59a42ab4abb0538575ca99b92b6a2149a04f')
+
+    secp224r1Encoded = str('0ee5587c4d18526f00e00038668d72cca6fd6a1b35'
+        '57b5366104d84408ecb637f08e8c86bbff82cc00e88f0066d7af63c3298ba'
+        '377348a1202b03b37fd6b1ff415aa311e001c04389459926c3296c242b83e'
+        '10a6cd2011c8fe2dae1b772ea5b21067')
+
+    secp256r1Encoded = str('cb872ac99cd31827010000404fbfbbbb61e0f8f9b1'
+        'a60a59ac8704e2ec050b423e3cf72e923f2c4f794b455c2a69d233456c36c'
+        '4119d0706e00eedc8d19390d7991b7b2d07a304eaa04aa6c000202191403d'
+        '5710bf15a265818cd42ed6fedf09add92d78b18e7a1e9feb95524702')
+
+    secp384r1Encoded = str('d3103f5ac81741e801800060a1687243362b5c7b18'
+        '89f379154615a1c73fb48dee863e022915db608e252de4b7132da8ce98e83'
+        '1534e6a9c0c0b09c8d639ade83206e5ba813473a11fa330e05da8c96e4383'
+        'fe27873da97103be2888cff002f05af71a1fddcc8374aa6ea9ce0030035c7'
+        'a1b10d9fafe837b64ad92f22f5ced0789186538669b5c6d872cec3d926122'
+        'b393772b57602ff31365efe1393246')
+
+    secp521r1Encoded = str('77f4b0ac81948ddc02090084014cdc9cacc4794109'
+        '6bc9cc66752ec27f597734fa66c62b792f88c519d6d37f0d16ea1c483a182'
+        '7a010b9128e3a08070ca33ef5f57835b7c1ba251f6cc3521dc42b01065345'
+        '1981b445d343eed3782a35d6cff0ff484f5a883d209f1b9042b726703568b'
+        '2f326e18b833bdd8aa0734392bcd19501e10d698a79f53e11e0a22bdd2aad'
+        '900042014f3284fa698dd9fe1118dd331851cdfaac5a3829278eb8994839d'
+        'e9471c940b858c69d2d05e8c01788a7d0b6e235aa5e783fc1bee807dcc386'
+        '5f920e12cf8f2d29')
+
+    def __init__(self, specification = None):
+        if specification == 'secp256k1':
+            self.key = Key.Key.decode(binascii.unhexlify(self.secp256k1Encoded))
+            self.keyOID = secp256k1
+        elif specification == 'secp224r1':
+            self.key = Key.Key.decode(binascii.unhexlify(self.secp224r1Encoded))
+            self.keyOID = secp224r1
+        elif specification == 'secp256r1':
+            self.key = Key.Key.decode(binascii.unhexlify(self.secp256r1Encoded))
+            self.keyOID = secp256r1
+        elif specification == 'secp384r1':
+            self.key = Key.Key.decode(binascii.unhexlify(self.secp384r1Encoded))
+            self.keyOID = secp384r1
+        elif specification == 'secp521r1':
+            self.key = Key.Key.decode(binascii.unhexlify(self.secp521r1Encoded))
+            self.keyOID = secp521r1
+        else:
+            raise UnknownKeySpecificationError(specification)
+
+    def asSubjectPublicKeyInfo(self):
+        """Returns a subject public key info representing
+        this key for use by pyasn1."""
+        algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+        algorithmIdentifier.setComponentByName('algorithm', ecPublicKey)
+        algorithmIdentifier.setComponentByName('parameters', self.keyOID)
+        spki = rfc2459.SubjectPublicKeyInfo()
+        spki.setComponentByName('algorithm', algorithmIdentifier)
+        # We need to extract the point that represents this key.
+        # The library encoding of the key is an 8-byte id, followed by 2
+        # bytes for the key length in bits, followed by the point on the
+        # curve (represented by two python longs). There appear to also
+        # be 2 bytes indicating the length of the point as encoded, but
+        # Decoder takes care of that.
+        encoded = self.key.encode()
+        _, _, points = encoding.Decoder(encoded).int(8).int(2).point(2).out()
+        # '04' indicates that the points are in uncompressed form.
+        hexifiedBitString = "'%s%s%s'H" % ('04', longToEvenLengthHexString(points[0]),
+                                           longToEvenLengthHexString(points[1]))
+        subjectPublicKey = univ.BitString(hexifiedBitString)
+        spki.setComponentByName('subjectPublicKey', subjectPublicKey)
+        return spki
+
+    def sign(self, data):
+        """Returns a hexified bit string representing a
+        signature by this key over the specified data.
+        Intended for use with pyasn1.type.univ.BitString"""
+        # There is some non-determinism in ECDSA signatures. Work around
+        # this by patching ecc.ecdsa.urandom to not be random.
+        with mock.patch('ecc.ecdsa.urandom', side_effect=notRandom):
+            # For some reason Key.sign returns an encoded point.
+            # Decode it so we can encode it as a BITSTRING consisting
+            # of a SEQUENCE of two INTEGERs.
+            # Also patch in secp256k1 if applicable.
+            if self.keyOID == secp256k1:
+                with mock.patch('ecc.curves.DOMAINS', {256: secp256k1Params}):
+                    x, y = encoding.dec_point(self.key.sign(data, 'sha256'))
+            else:
+                x, y = encoding.dec_point(self.key.sign(data, 'sha256'))
+            point = ECPoint()
+            point.setComponentByName('x', x)
+            point.setComponentByName('y', y)
+            return byteStringToHexifiedBitString(encoder.encode(point))
+
+
+def keyFromSpecification(specification):
+    """Pass in a specification, get the appropriate key back."""
+    if specification.startswith('secp'):
+        return ECCKey(specification)
+    else:
+        return RSAKey(specification)
+
 # The build harness will call this function with an output file-like
 # object and a path to a file containing a specification. This will
 # read the specification and output the key as ASCII-encoded PKCS #8.
 def main(output, inputPath):
     with open(inputPath) as configStream:
-        output.write(RSAKey(configStream.read().strip()).toPEM())
+        output.write(keyFromSpecification(configStream.read().strip()).toPEM())
 
 # When run as a standalone program, this will read a specification from
 # stdin and output the certificate as PEM to stdout.
 if __name__ == '__main__':
-    print RSAKey(sys.stdin.read()).toPEM()
+    print keyFromSpecification(sys.stdin.read()).toPEM()
--- a/security/manager/ssl/tests/unit/test_keysize.js
+++ b/security/manager/ssl/tests/unit/test_keysize.js
@@ -7,27 +7,16 @@
 // Checks that RSA certs with key sizes below 1024 bits are rejected.
 // Checks that ECC certs using curves other than the NIST P-256, P-384 or P-521
 // curves are rejected.
 
 do_get_profile(); // must be called before getting nsIX509CertDB
 const certdb = Cc["@mozilla.org/security/x509certdb;1"]
                  .getService(Ci.nsIX509CertDB);
 
-function certFromFile(filename) {
-  let der = readFile(do_get_file("test_keysize/" + filename, false));
-  return certdb.constructX509(der, der.length);
-}
-
-function loadCert(certName, trustString) {
-  let certFilename = certName + ".der";
-  addCertFromFile(certdb, "test_keysize/" + certFilename, trustString);
-  return certFromFile(certFilename);
-}
-
 /**
  * Tests a cert chain.
  *
  * @param {String} rootKeyType
  *        The key type of the root certificate, or the name of an elliptic
  *        curve, as output by the 'openssl ecparam -list_curves' command.
  * @param {Number} rootKeySize
  * @param {String} intKeyType
@@ -40,19 +29,19 @@ function checkChain(rootKeyType, rootKey
                     eeKeyType, eeKeySize, eeExpectedError) {
   let rootName = "root_" + rootKeyType + "_" + rootKeySize;
   let intName = "int_" + intKeyType + "_" + intKeySize;
   let eeName = "ee_" + eeKeyType + "_" + eeKeySize;
 
   let intFullName = intName + "-" + rootName;
   let eeFullName = eeName + "-" + intName + "-" + rootName;
 
-  loadCert(rootName, "CTu,CTu,CTu");
-  loadCert(intFullName, ",,");
-  let eeCert = certFromFile(eeFullName + ".der");
+  addCertFromFile(certdb, `test_keysize/${rootName}.pem`, "CTu,CTu,CTu");
+  addCertFromFile(certdb, `test_keysize/${intFullName}.pem`, ",,");
+  let eeCert = constructCertFromFile(`test_keysize/${eeFullName}.pem`);
 
   do_print("cert o=" + eeCert.organization);
   do_print("cert issuer o=" + eeCert.issuerOrganization);
   checkCertErrorGeneric(certdb, eeCert, eeExpectedError,
                         certificateUsageSSLServer);
 }
 
 /**
@@ -83,54 +72,54 @@ function checkRSAChains(inadequateKeySiz
   // Chain with an end entity cert that has an inadequate size for DV
   checkChain("rsa", adequateKeySize,
              "rsa", adequateKeySize,
              "rsa", inadequateKeySize,
              MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE);
 }
 
 function checkECCChains() {
-  checkChain("prime256v1", 256,
+  checkChain("secp256r1", 256,
              "secp384r1", 384,
              "secp521r1", 521,
              PRErrorCodeSuccess);
-  checkChain("prime256v1", 256,
+  checkChain("secp256r1", 256,
              "secp224r1", 224,
-             "prime256v1", 256,
+             "secp256r1", 256,
              SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
-  checkChain("prime256v1", 256,
-             "prime256v1", 256,
+  checkChain("secp256r1", 256,
+             "secp256r1", 256,
              "secp224r1", 224,
              SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
   checkChain("secp224r1", 224,
-             "prime256v1", 256,
-             "prime256v1", 256,
+             "secp256r1", 256,
+             "secp256r1", 256,
              SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
-  checkChain("prime256v1", 256,
-             "prime256v1", 256,
+  checkChain("secp256r1", 256,
+             "secp256r1", 256,
              "secp256k1", 256,
              SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
   checkChain("secp256k1", 256,
-             "prime256v1", 256,
-             "prime256v1", 256,
+             "secp256r1", 256,
+             "secp256r1", 256,
              SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
 }
 
 function checkCombinationChains() {
   checkChain("rsa", 2048,
-             "prime256v1", 256,
+             "secp256r1", 256,
              "secp384r1", 384,
              PRErrorCodeSuccess);
   checkChain("rsa", 2048,
-             "prime256v1", 256,
+             "secp256r1", 256,
              "secp224r1", 224,
              SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
-  checkChain("prime256v1", 256,
+  checkChain("secp256r1", 256,
              "rsa", 1016,
-             "prime256v1", 256,
+             "secp256r1", 256,
              MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE);
 }
 
 function run_test() {
   checkRSAChains(1016, 1024);
   checkECCChains();
   checkCombinationChains();
 
deleted file mode 100644
index ab85059359ffac31b43bd6d6cf8cc3c632039214..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index c5ebf5d4613edfc80748a7ec8ecbc677b5c31ef9..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index de4ed3320e1c4706fe8cb8302b8b56c29ad19c4c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 12de0969ec91bd2a2a537da8264e2dbd315be783..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 16681f8a598d35c726f4f9e72225529abee19915..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1016-int_rsa_1024-root_rsa_1024.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int_rsa_1024-root_rsa_1024
+subject:ee_rsa_1016-int_rsa_1024-root_rsa_1024
+issuerKey:rsa1024
+subjectKey:rsa1016
deleted file mode 100644
index 869b3ca8d67a0d6fb93b0f16075a87514b6d8d13..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1016-root_rsa_1024.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int_rsa_1016-root_rsa_1024
+subject:ee_rsa_1024-int_rsa_1016-root_rsa_1024
+issuerKey:rsa1016
+subjectKey:rsa1024
deleted file mode 100644
index db7dd02afa9282e4458c87fcda23ed72f73863e0..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1016.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int_rsa_1024-root_rsa_1016
+subject:ee_rsa_1024-int_rsa_1024-root_rsa_1016
+issuerKey:rsa1024
+subject:rsa1024
deleted file mode 100644
index 506c14088d3183ee26e201e4fece6c393fd13935..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_rsa_1024-int_rsa_1024-root_rsa_1024.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int_rsa_1024-root_rsa_1024
+subject:ee_rsa_1024-int_rsa_1024-root_rsa_1024
+issuerKey:rsa1024
+subjectKey:rsa1024
deleted file mode 100644
index 5981cb4b5ef3d6ee3518d8b450f39afdadaecb7e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index b32cbebec511a945583db568d5193d855f4b0917..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_rsa_2048.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_rsa_2048
+subject:ee_secp224r1_224-int_secp256r1_256-root_rsa_2048
+issuerKey:secp256r1
+subjectKey:secp224r1
+signature:ecdsaWithSHA256
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_secp256r1_256
+subject:ee_secp224r1_224-int_secp256r1_256-root_secp256r1_256
+issuerKey:secp256r1
+subjectKey:secp224r1
+signature:ecdsaWithSHA256
deleted file mode 100644
index 48fb614aa91b87bdec1126f3de1f3d97720e85e2..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_secp256r1_256
+subject:ee_secp256k1_256-int_secp256r1_256-root_secp256r1_256
+issuerKey:secp256r1
+subjectKey:secp256k1
+signature:ecdsaWithSHA256
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_rsa_1016-root_secp256r1_256.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int_rsa_1016-root_secp256r1_256
+subject:ee_secp256r1_256-int_rsa_1016-root_secp256r1_256
+issuerKey:rsa1016
+subjectKey:secp256r1
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp224r1_224-root_secp256r1_256
+subject:ee_secp256r1_256-int_secp224r1_224-root_secp256r1_256
+issuerKey:secp224r1
+subjectKey:secp256r1
+signature:ecdsaWithSHA256
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_secp224r1_224
+subject:ee_secp256r1_256-int_secp256r1_256-root_secp224r1_224
+issuerKey:secp256r1
+subjectKey:secp256r1
+signature:ecdsaWithSHA256
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_secp256k1_256
+subject:ee_secp256r1_256-int_secp256r1_256-root_secp256k1_256
+issuerKey:secp256r1
+subjectKey:secp256r1
+signature:ecdsaWithSHA256
deleted file mode 100644
index 87c06737c0a3affe002e1927430f4e20cbe05fa3..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp384r1_384-int_secp256r1_256-root_rsa_2048.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp256r1_256-root_rsa_2048
+subject:ee_secp384r1_384-int_secp256r1_256-root_rsa_2048
+issuerKey:secp256r1
+subjectKey:secp384r1
+signature:ecdsaWithSHA256
deleted file mode 100644
index b2e2d2528b21591d8c35f8b2ecd9ab06ba58d517..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int_secp384r1_384-root_secp256r1_256
+subject:ee_secp521r1_521-int_secp384r1_384-root_secp256r1_256
+issuerKey:secp384r1
+subjectKey:secp521r1
+signature:ecdsaWithSHA256
deleted file mode 100755
--- a/security/manager/ssl/tests/unit/test_keysize/generate.py
+++ /dev/null
@@ -1,189 +0,0 @@
-#!/usr/bin/env python
-
-# 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/.
-
-import tempfile, os, sys
-import random
-
-libpath = os.path.abspath('../psm_common_py')
-
-sys.path.append(libpath)
-
-import CertUtils
-
-srcdir = os.getcwd()
-db_dir = tempfile.mkdtemp()
-
-ca_ext_text = ('basicConstraints = critical, CA:TRUE\n' +
-               'keyUsage = keyCertSign, cRLSign\n')
-ee_ext_text = ''
-
-generated_certs = []
-
-def generate_cert(key_type, cert_name_prefix, cert_name_suffix, base_ext_text,
-                  signer_key_filename, signer_cert_filename, key_size):
-    """
-    Generates a certificate.
-    If an equivalent certificate has already been generated, it is reused.
-
-    Arguments:
-      key_type -- the type of key generated: potential values: 'rsa', or any of
-                  the curves found by 'openssl ecparam -list_curves'
-      cert_name_prefix -- prefix of the generated cert name
-      cert_name_suffix -- suffix of the generated cert name
-      base_ext_text -- the base text for the x509 extensions to be added to the
-                       certificate (extra extensions will be added if generating
-                       an EV cert)
-      signer_key_filename -- the filename of the key from which the cert will
-                             be signed. If an empty string is passed in the cert
-                             will be self signed (think CA roots).
-      signer_cert_filename -- the filename of the signer cert that will sign the
-                              certificate being generated. Ignored if an empty
-                              string is passed in for signer_key_filename.
-                              Must be in DER format.
-      key_size -- public key size for RSA certs
-
-    Output:
-      cert_name -- the resultant (nick)name of the certificate
-      key_filename -- the filename of the key file (PEM format)
-      cert_filename -- the filename of the certificate (DER format)
-    """
-    cert_name = cert_name_prefix + '_' + key_type + '_' + key_size
-
-    # If the suffix is not the empty string, add a hyphen for visual separation
-    if cert_name_suffix:
-        cert_name += '-' + cert_name_suffix
-
-    ev_ext_text = ''
-    subject_string = ('/CN=XPCShell Key Size Testing %s %s-bit' %
-                      (key_type, key_size))
-
-    # Use the organization field to store the cert nickname for easier debugging
-    subject_string += '/O=' + cert_name
-
-    # Don't regenerate a previously generated cert
-    for cert in generated_certs:
-        if cert_name == cert[0]:
-            return cert
-
-    [key_filename, cert_filename] = CertUtils.generate_cert_generic(
-        db_dir,
-        srcdir,
-        random.randint(100, 40000000),
-        key_type,
-        cert_name,
-        base_ext_text + ev_ext_text,
-        signer_key_filename,
-        signer_cert_filename,
-        subject_string,
-        key_size,
-        3 * 365 + 3 * 31) # 39 months
-    generated_certs.append([cert_name, key_filename, cert_filename])
-
-    return [cert_name, key_filename, cert_filename]
-
-def generate_cert_chain(root_key_type, root_key_size, int_key_type, int_key_size,
-                        ee_key_type, ee_key_size):
-    """
-    Generates a certificate chain.
-
-    Arguments:
-    (root|int|ee)_key_type -- the type of key generated: potential values: 'rsa',
-                              or any of the curves found by
-                              'openssl ecparam -list_curves'
-    (root|int|ee)_key_size -- public key size for the relevant cert
-    """
-    [root_nick, root_key_file, root_cert_file] = generate_cert(
-        root_key_type,
-        'root',
-        '',
-        ca_ext_text,
-        '',
-        '',
-        root_key_size)
-
-    [int_nick, int_key_file, int_cert_file] = generate_cert(
-        int_key_type,
-        'int',
-        root_nick,
-        ca_ext_text,
-        root_key_file,
-        root_cert_file,
-        int_key_size)
-
-    generate_cert(
-        ee_key_type,
-        'ee',
-        int_nick,
-        ee_ext_text,
-        int_key_file,
-        int_cert_file,
-        ee_key_size)
-
-def generate_rsa_chains(inadequate_key_size, adequate_key_size):
-    """
-    Generates various RSA chains with different combinations of adequately and
-    inadequately sized certs.
-
-    Arguments:
-      inadequate_key_size -- a string defining the inadequate public key size
-                             for the generated certs
-      adequate_key_size -- a string defining the adequate public key size for
-                           the generated certs
-    """
-    # Generate chain with certs that have adequate sizes
-    generate_cert_chain('rsa', adequate_key_size,
-                        'rsa', adequate_key_size,
-                        'rsa', adequate_key_size)
-
-    # Generate chain with a root cert that has an inadequate size
-    generate_cert_chain('rsa', inadequate_key_size,
-                        'rsa', adequate_key_size,
-                        'rsa', adequate_key_size)
-
-    # Generate chain with an intermediate cert that has an inadequate size
-    generate_cert_chain('rsa', adequate_key_size,
-                        'rsa', inadequate_key_size,
-                        'rsa', adequate_key_size)
-
-    # Generate chain with an end entity cert that has an inadequate size
-    generate_cert_chain('rsa', adequate_key_size,
-                        'rsa', adequate_key_size,
-                        'rsa', inadequate_key_size)
-
-def generate_ecc_chains():
-    generate_cert_chain('prime256v1', '256',
-                        'secp384r1', '384',
-                        'secp521r1', '521')
-    generate_cert_chain('prime256v1', '256',
-                        'secp224r1', '224',
-                        'prime256v1', '256')
-    generate_cert_chain('prime256v1', '256',
-                        'prime256v1', '256',
-                        'secp224r1', '224')
-    generate_cert_chain('secp224r1', '224',
-                        'prime256v1', '256',
-                        'prime256v1', '256')
-    generate_cert_chain('prime256v1', '256',
-                        'prime256v1', '256',
-                        'secp256k1', '256')
-    generate_cert_chain('secp256k1', '256',
-                        'prime256v1', '256',
-                        'prime256v1', '256')
-
-def generate_combination_chains():
-    generate_cert_chain('rsa', '2048',
-                        'prime256v1', '256',
-                        'secp384r1', '384')
-    generate_cert_chain('rsa', '2048',
-                        'prime256v1', '256',
-                        'secp224r1', '224')
-    generate_cert_chain('prime256v1', '256',
-                        'rsa', '1016',
-                        'prime256v1', '256')
-
-generate_rsa_chains('1016', '1024')
-generate_ecc_chains()
-generate_combination_chains()
deleted file mode 100644
index 383bf1964b7bdf202841fc7cc9025615a8cddb14..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 886d59144c29983be9cf69c9d2ca5775d68fdd31..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index c3b4716e68e89ec28f4cdc88f369fd6643609fa5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 74d2bc5c224dd599eb19bf6ac2965a42906473eb..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 7cb9357dddc73452085a18fe08a0ac0373ebbdb1..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index df26670e2463bcbf5299e678af909984e9604b4c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_rsa_1024.pem.certspec
@@ -0,0 +1,6 @@
+issuer:root_rsa_1024
+subject:int_rsa_1016-root_rsa_1024
+issuerKey:rsa1024
+subjectKey:rsa1016
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1016-root_secp256r1_256.pem.certspec
@@ -0,0 +1,7 @@
+issuer:root_secp256r1_256
+subject:int_rsa_1016-root_secp256r1_256
+issuerKey:secp256r1
+subjectKey:rsa1016
+signature:ecdsaWithSHA256
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
deleted file mode 100644
index 98e7d9d329a3e470cecb2e3c5e684e9900c3294a..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1016.pem.certspec
@@ -0,0 +1,6 @@
+issuer:root_rsa_1016
+subject:int_rsa_1024-root_rsa_1016
+issuerKey:rsa1016
+subjectKey:rsa1024
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
deleted file mode 100644
index b45a3a6ad207cf02f1903a015b12185d78e1409c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_keysize/int_rsa_1024-root_rsa_1024.pem.certspec
@@ -0,0 +1,6 @@
+issuer:root_rsa_1024
+subject:int_rsa_1024-root_rsa_1024
+issuerKey:rsa1024
+subjectKey:rsa1024
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
deleted file mode 100644
index a97924ceba6f318c9487479cb60069670e692eaf..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null