From 6ce384ea6b86d9284d7b3f481bcfb591fca50567 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Thu, 5 Feb 2015 15:48:44 -0600 Subject: [PATCH 01/39] Bug 1129575 - In the PPluginWidget Create handler fail via return result vs. abort the child process when there's no parent window available. r=roc --- dom/ipc/PPluginWidget.ipdl | 3 ++- dom/ipc/TabChild.cpp | 12 +++++++----- dom/ipc/TabChild.h | 2 +- dom/plugins/base/nsPluginInstanceOwner.cpp | 5 ++++- dom/plugins/ipc/PluginWidgetParent.cpp | 21 +++++++++++++-------- dom/plugins/ipc/PluginWidgetParent.h | 2 +- widget/PluginWidgetProxy.cpp | 5 ++++- 7 files changed, 32 insertions(+), 18 deletions(-) diff --git a/dom/ipc/PPluginWidget.ipdl b/dom/ipc/PPluginWidget.ipdl index 686dae7c3b00..7664132f104c 100644 --- a/dom/ipc/PPluginWidget.ipdl +++ b/dom/ipc/PPluginWidget.ipdl @@ -33,7 +33,8 @@ parent: async __delete__(); parent: - async Create(); + sync Create() returns (nsresult aResult); + async Destroy(); async SetFocus(bool aRaise); diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 0255fd3fff77..0faf4e387af6 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -3481,19 +3481,20 @@ TabChild::DeallocPPluginWidgetChild(mozilla::plugins::PPluginWidgetChild* aActor return true; } -already_AddRefed -TabChild::CreatePluginWidget(nsIWidget* aParent) +nsresult +TabChild::CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut) { + *aOut = nullptr; mozilla::plugins::PluginWidgetChild* child = static_cast(SendPPluginWidgetConstructor()); if (!child) { NS_ERROR("couldn't create PluginWidgetChild"); - return nullptr; + return NS_ERROR_UNEXPECTED; } nsCOMPtr pluginWidget = nsIWidget::CreatePluginProxyWidget(this, child); if (!pluginWidget) { NS_ERROR("couldn't create PluginWidgetProxy"); - return nullptr; + return NS_ERROR_UNEXPECTED; } nsWidgetInitData initData; @@ -3506,7 +3507,8 @@ TabChild::CreatePluginWidget(nsIWidget* aParent) if (NS_FAILED(rv)) { NS_WARNING("Creating native plugin widget on the chrome side failed."); } - return pluginWidget.forget(); + pluginWidget.forget(aOut); + return rv; } TabChildGlobal::TabChildGlobal(TabChildBase* aTabChild) diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index f0f1c285d2a7..d0a7b2beacb5 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -494,7 +494,7 @@ public: */ PPluginWidgetChild* AllocPPluginWidgetChild() MOZ_OVERRIDE; bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) MOZ_OVERRIDE; - already_AddRefed CreatePluginWidget(nsIWidget* aParent); + nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut); nsIntPoint GetChromeDisplacement() { return mChromeDisp; }; diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 340d985f6648..03ce582615be 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -2872,7 +2872,10 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void) dom::TabChild* tc = dom::TabChild::GetFrom(topWindow); if (tc) { // This returns a PluginWidgetProxy which remotes a number of calls. - mWidget = tc->CreatePluginWidget(parentWidget.get()); + rv = tc->CreatePluginWidget(parentWidget.get(), getter_AddRefs(mWidget)); + if (NS_FAILED(rv)) { + return rv; + } } } } diff --git a/dom/plugins/ipc/PluginWidgetParent.cpp b/dom/plugins/ipc/PluginWidgetParent.cpp index 600dfc7c673a..517371adc630 100644 --- a/dom/plugins/ipc/PluginWidgetParent.cpp +++ b/dom/plugins/ipc/PluginWidgetParent.cpp @@ -4,6 +4,7 @@ #include "PluginWidgetParent.h" #include "mozilla/dom/TabParent.h" +#include "mozilla/dom/ContentParent.h" #include "nsComponentManagerUtils.h" #include "nsWidgetsCID.h" #include "mozilla/DebugOnly.h" @@ -99,14 +100,12 @@ PluginWidgetParent::SendAsyncUpdate(nsIWidget* aWidget) // makes use of some of the utility functions as well. bool -PluginWidgetParent::RecvCreate() +PluginWidgetParent::RecvCreate(nsresult* aResult) { PWLOG("PluginWidgetParent::RecvCreate()\n"); - nsresult rv; - - mWidget = do_CreateInstance(kWidgetCID, &rv); - NS_ASSERTION(NS_SUCCEEDED(rv), "widget create failure"); + mWidget = do_CreateInstance(kWidgetCID, aResult); + NS_ASSERTION(NS_SUCCEEDED(*aResult), "widget create failure"); #if defined(MOZ_WIDGET_GTK) // We need this currently just for GTK in setting up a socket widget @@ -122,17 +121,23 @@ PluginWidgetParent::RecvCreate() // This returns the top level window widget nsCOMPtr parentWidget = GetTabParent()->GetWidget(); + // If this fails, bail. + if (!parentWidget) { + *aResult = NS_ERROR_NOT_AVAILABLE; + return true; + } nsWidgetInitData initData; initData.mWindowType = eWindowType_plugin_ipc_chrome; initData.mUnicode = false; initData.clipChildren = true; initData.clipSiblings = true; - rv = mWidget->Create(parentWidget.get(), nullptr, nsIntRect(0,0,0,0), - nullptr, &initData); - if (NS_FAILED(rv)) { + *aResult = mWidget->Create(parentWidget.get(), nullptr, nsIntRect(0,0,0,0), + nullptr, &initData); + if (NS_FAILED(*aResult)) { mWidget->Destroy(); mWidget = nullptr; + // This should never fail, abort. return false; } diff --git a/dom/plugins/ipc/PluginWidgetParent.h b/dom/plugins/ipc/PluginWidgetParent.h index 7808c6a97863..024b40d60a67 100644 --- a/dom/plugins/ipc/PluginWidgetParent.h +++ b/dom/plugins/ipc/PluginWidgetParent.h @@ -37,7 +37,7 @@ public: virtual ~PluginWidgetParent(); virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - virtual bool RecvCreate() MOZ_OVERRIDE; + virtual bool RecvCreate(nsresult* aResult) MOZ_OVERRIDE; virtual bool RecvDestroy() MOZ_OVERRIDE; virtual bool RecvSetFocus(const bool& aRaise) MOZ_OVERRIDE; virtual bool RecvGetNativePluginPort(uintptr_t* value) MOZ_OVERRIDE; diff --git a/widget/PluginWidgetProxy.cpp b/widget/PluginWidgetProxy.cpp index 3e585d152b2b..dda1d2809568 100644 --- a/widget/PluginWidgetProxy.cpp +++ b/widget/PluginWidgetProxy.cpp @@ -56,8 +56,11 @@ PluginWidgetProxy::Create(nsIWidget* aParent, ENSURE_CHANNEL; PWLOG("PluginWidgetProxy::Create()\n"); - if (!mActor->SendCreate()) { + nsresult rv = NS_ERROR_UNEXPECTED; + mActor->SendCreate(&rv); + if (NS_FAILED(rv)) { NS_WARNING("failed to create chrome widget, plugins won't paint."); + return rv; } BaseCreate(aParent, aRect, aContext, aInitData); From bf6b3c59f1262e6f7af5f34e8c84e1461a521dea Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Tue, 3 Feb 2015 14:07:45 -0500 Subject: [PATCH 02/39] Bug 1129064. Remove duplicate IsPlaceholderTile function. r=BenWa The method version is cheaper because it doesn't need to make a copy of the Tile. --- gfx/layers/TiledLayerBuffer.h | 22 ++++++++++------------ gfx/layers/client/TiledContentClient.h | 2 +- gfx/tests/gtest/TestTiledLayerBuffer.cpp | 4 ++++ 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/gfx/layers/TiledLayerBuffer.h b/gfx/layers/TiledLayerBuffer.h index 043875a517fc..a61e3ef7f20a 100644 --- a/gfx/layers/TiledLayerBuffer.h +++ b/gfx/layers/TiledLayerBuffer.h @@ -139,7 +139,7 @@ public: mRetainedWidth = 0; mRetainedHeight = 0; for (size_t i = 0; i < mRetainedTiles.Length(); i++) { - if (!IsPlaceholder(mRetainedTiles[i])) { + if (!mRetainedTiles[i].IsPlaceholderTile()) { AsDerived().ReleaseTile(mRetainedTiles[i]); } } @@ -205,8 +205,6 @@ protected: private: const Derived& AsDerived() const { return *static_cast(this); } Derived& AsDerived() { return *static_cast(this); } - - bool IsPlaceholder(Tile aTile) const { return aTile == AsDerived().GetPlaceholderTile(); } }; class ClientTiledLayerBuffer; @@ -301,7 +299,7 @@ TiledLayerBuffer::RemoveTile(int x, int y, Tile& aRemovedTile) { int index = x * mRetainedHeight + y; const Tile& tileToRemove = mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile()); - if (!IsPlaceholder(tileToRemove)) { + if (!tileToRemove.IsPlaceholderTile()) { aRemovedTile = tileToRemove; mRetainedTiles[index] = AsDerived().GetPlaceholderTile(); return true; @@ -393,8 +391,8 @@ TiledLayerBuffer::Update(const nsIntRegion& aNewValidRegion, int index = tileX * oldRetainedHeight + tileY; // The tile may have been removed, skip over it in this case. - if (IsPlaceholder(oldRetainedTiles. - SafeElementAt(index, AsDerived().GetPlaceholderTile()))) { + if (oldRetainedTiles. + SafeElementAt(index, AsDerived().GetPlaceholderTile()).IsPlaceholderTile()) { newRetainedTiles.AppendElement(AsDerived().GetPlaceholderTile()); } else { Tile tileWithPartialValidContent = oldRetainedTiles[index]; @@ -436,7 +434,7 @@ TiledLayerBuffer::Update(const nsIntRegion& aNewValidRegion, int oldTileCount = 0; for (size_t i = 0; i < oldRetainedTiles.Length(); i++) { Tile oldTile = oldRetainedTiles[i]; - if (IsPlaceholder(oldTile)) { + if (oldTile.IsPlaceholderTile()) { continue; } @@ -500,8 +498,8 @@ TiledLayerBuffer::Update(const nsIntRegion& aNewValidRegion, // If allocating a tile failed we can run into this assertion. // Rendering is going to be glitchy but we don't want to crash. NS_ASSERTION(!newValidRegion.Intersects(tileRect) || - !IsPlaceholder(newRetainedTiles. - SafeElementAt(index, AsDerived().GetPlaceholderTile())), + !newRetainedTiles. + SafeElementAt(index, AsDerived().GetPlaceholderTile()).IsPlaceholderTile(), "Unexpected placeholder tile"); #endif @@ -520,10 +518,10 @@ TiledLayerBuffer::Update(const nsIntRegion& aNewValidRegion, // Try to reuse a tile from the old retained tiles that had no partially // valid content. - while (IsPlaceholder(newTile) && oldRetainedTiles.Length() > 0) { + while (newTile.IsPlaceholderTile() && oldRetainedTiles.Length() > 0) { AsDerived().SwapTiles(newTile, oldRetainedTiles[oldRetainedTiles.Length()-1]); oldRetainedTiles.RemoveElementAt(oldRetainedTiles.Length()-1); - if (!IsPlaceholder(newTile)) { + if (!newTile.IsPlaceholderTile()) { oldTileCount--; } } @@ -534,7 +532,7 @@ TiledLayerBuffer::Update(const nsIntRegion& aNewValidRegion, nsIntPoint tileOrigin(tileStartX, tileStartY); newTile = AsDerived().ValidateTile(newTile, nsIntPoint(tileStartX, tileStartY), tileDrawRegion); - NS_ASSERTION(!IsPlaceholder(newTile), "Unexpected placeholder tile - failed to allocate?"); + NS_ASSERTION(!newTile.IsPlaceholderTile(), "Unexpected placeholder tile - failed to allocate?"); #ifdef GFX_TILEDLAYER_PREF_WARNINGS printf_stderr("Store Validate tile %i, %i -> %i\n", tileStartX, tileStartY, index); #endif diff --git a/gfx/layers/client/TiledContentClient.h b/gfx/layers/client/TiledContentClient.h index d86a9feebee3..547284519b92 100644 --- a/gfx/layers/client/TiledContentClient.h +++ b/gfx/layers/client/TiledContentClient.h @@ -182,7 +182,7 @@ struct TileClient mCompositableClient = aCompositableClient; } - bool IsPlaceholderTile() + bool IsPlaceholderTile() const { return mBackBuffer == nullptr && mFrontBuffer == nullptr; } diff --git a/gfx/tests/gtest/TestTiledLayerBuffer.cpp b/gfx/tests/gtest/TestTiledLayerBuffer.cpp index e024bbe5299d..a556165fdb4a 100644 --- a/gfx/tests/gtest/TestTiledLayerBuffer.cpp +++ b/gfx/tests/gtest/TestTiledLayerBuffer.cpp @@ -21,6 +21,10 @@ struct TestTiledLayerTile { bool operator!= (const TestTiledLayerTile& o) const { return value != o.value; } + + bool IsPlaceholderTile() const { + return value == -1; + } }; class TestTiledLayerBuffer : public TiledLayerBuffer From 9a2cccac4ce1b33d9fe8143ba67c119cbd736b09 Mon Sep 17 00:00:00 2001 From: Edwin Flores Date: Fri, 6 Feb 2015 11:04:47 +1300 Subject: [PATCH 03/39] Bug 1129722 - Add {Hold,Drop}JSObjects to MediaKeyStatusMap - r=jwwang,bz --- dom/media/eme/MediaKeyStatusMap.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dom/media/eme/MediaKeyStatusMap.cpp b/dom/media/eme/MediaKeyStatusMap.cpp index dcd9e3519b98..be367d48e3d1 100644 --- a/dom/media/eme/MediaKeyStatusMap.cpp +++ b/dom/media/eme/MediaKeyStatusMap.cpp @@ -47,10 +47,13 @@ MediaKeyStatusMap::MediaKeyStatusMap(JSContext* aCx, if (NS_WARN_IF(!mMap)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); } + + mozilla::HoldJSObjects(this); } MediaKeyStatusMap::~MediaKeyStatusMap() { + mozilla::DropJSObjects(this); } JSObject* From b472f52bedc1ea87e3d1d73e9b74c7a232fb2a69 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 5 Feb 2015 14:29:32 -0800 Subject: [PATCH 04/39] Bug 1126052 - Reenable test_SeekTwice.mp4. r=RyanVM --- dom/media/mediasource/test/mochitest.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini index fc3409a98efd..dfcd730e9589 100644 --- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -35,8 +35,7 @@ skip-if = (toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet o [test_SeekableBeforeEndOfStream.html] [test_SeekableBeforeEndOfStreamSplit.html] [test_SeekTwice_mp4.html] -#skip-if = os == 'linux' # No fmp4 support on linux -skip-if = true # Fails on most platforms and was pushed anyway. +skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ [test_SetModeThrows.html] [test_SplitAppendDelay.html] [test_SplitAppend.html] From ca2f09c9a898d91278b50a2eee9a02946c4fa281 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 26 Jan 2015 19:12:24 +0100 Subject: [PATCH 05/39] Bug 1113338: Move asm.js SIMD.load/store tests into their own file; r=luke --HG-- extra : rebase_source : e80632b2412514c4c5d0bc1d7365f782602db6a7 --- .../tests/asm.js/testSIMD-load-store.js | 259 ++++++++++++++++++ js/src/jit-test/tests/asm.js/testSIMD.js | 241 ---------------- 2 files changed, 259 insertions(+), 241 deletions(-) create mode 100644 js/src/jit-test/tests/asm.js/testSIMD-load-store.js diff --git a/js/src/jit-test/tests/asm.js/testSIMD-load-store.js b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js new file mode 100644 index 000000000000..cfd778490699 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js @@ -0,0 +1,259 @@ +load(libdir + "asm.js"); + +// Set to true to see more JS debugging spew +const DEBUG = false; + +if (!isSimdAvailable() || typeof SIMD === 'undefined') { + DEBUG && print("won't run tests as simd extensions aren't activated yet"); + quit(0); +} + +const INT32_MAX = Math.pow(2, 31) - 1; +const INT32_MIN = INT32_MAX + 1 | 0; + +function assertEqX4(real, expected, assertFunc) { + if (typeof assertFunc === 'undefined') + assertFunc = assertEq; + + assertFunc(real.x, expected[0]); + assertFunc(real.y, expected[1]); + assertFunc(real.z, expected[2]); + assertFunc(real.w, expected[3]); +} + +// Load / Store +var IMPORTS = USE_ASM + 'var H=new glob.Uint8Array(heap); var i4=glob.SIMD.int32x4; var load=i4.load; var store=i4.store;'; + +// Bad number of args +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load();} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3, 4, 5);} return f"); + +// Bad type of args +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3, 5);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, 5.0);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0.;load(H, i);} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var H2=new glob.Int32Array(heap); function f(){var i=0;load(H2, i)} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var H2=42; function f(){var i=0;load(H2, i)} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;load(H2, i)} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var f4=glob.SIMD.float32x4; function f(){var i=0;var vec=f4(1,2,3,4); store(H, i, vec)} return f"); + +// Bad coercions of returned values +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return load(H, i)|0;} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return +load(H, i);} return f"); + +// Literal index constants +var buf = new ArrayBuffer(BUF_MIN); +var asI32 = new Int32Array(buf); +asI32[(BUF_MIN >> 2) - 4] = 4; +asI32[(BUF_MIN >> 2) - 3] = 3; +asI32[(BUF_MIN >> 2) - 2] = 2; +asI32[(BUF_MIN >> 2) - 1] = 1; + +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1) + ");} return f"); +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 15) + ");} return f"); +asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 16) + ");} return f"); + +assertAsmLinkFail(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + (BUF_MIN - 15) + "));} return f"), this, {}, buf); +assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + (BUF_MIN - 16) + "));} return f"), this, {}, buf)(), [4, 3, 2, 1]); +assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + BUF_MIN + " - 16 | 0));} return f"), this, {}, buf)(), [4, 3, 2, 1]); + +var CONSTANT_INDEX = 42; +var CONSTANT_BYTE_INDEX = CONSTANT_INDEX << 2; + +var loadStoreCode = ` + "use asm"; + + var H = new glob.Uint8Array(heap); + + var i4 = glob.SIMD.int32x4; + var i4load = i4.load; + var i4store = i4.store; + + var f4 = glob.SIMD.float32x4; + var f4load = f4.load; + var f4store = f4.store; + + function f32l(i) { i=i|0; return f4(f4load(H, i|0)); } + function f32lcst() { return f4(f4load(H, ${CONSTANT_BYTE_INDEX})); } + function f32s(i, vec) { i=i|0; vec=f4(vec); f4store(H, i|0, vec); } + function f32scst(vec) { vec=f4(vec); f4store(H, ${CONSTANT_BYTE_INDEX}, vec); } + + function i32l(i) { i=i|0; return i4(i4load(H, i|0)); } + function i32lcst() { return i4(i4load(H, ${CONSTANT_BYTE_INDEX})); } + function i32s(i, vec) { i=i|0; vec=i4(vec); i4store(H, i|0, vec); } + function i32scst(vec) { vec=i4(vec); i4store(H, ${CONSTANT_BYTE_INDEX}, vec); } + + function f32lbndcheck(i) { + i=i|0; + if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX}; + if ((i|0) < 0) i = 0; + return f4(f4load(H, i|0)); + } + function f32sbndcheck(i, vec) { + i=i|0; + vec=f4(vec); + if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX}; + if ((i|0) < 0) i = 0; + return f4(f4store(H, i|0, vec)); + } + + return { + f32l: f32l, + f32lcst: f32lcst, + f32s: f32s, + f32scst: f32scst, + f32lbndcheck: f32lbndcheck, + f32sbndcheck: f32sbndcheck, + i32l: i32l, + i32lcst: i32lcst, + i32s: i32s, + i32scst: i32scst + } +`; + +const SIZE = 0x8000; + +var F32 = new Float32Array(SIZE); +var reset = function() { + for (var i = 0; i < SIZE; i++) + F32[i] = i + 1; +}; +reset(); + +var buf = F32.buffer; +var m = asmLink(asmCompile('glob', 'ffi', 'heap', loadStoreCode), this, null, buf); + +function slice(TA, i, n) { return Array.prototype.slice.call(TA, i, i + n); } + +// Float32x4.load +function f32l(n) { return m.f32l((n|0) << 2 | 0); }; + +// Correct accesses +assertEqX4(f32l(0), slice(F32, 0, 4)); +assertEqX4(f32l(1), slice(F32, 1, 4)); +assertEqX4(f32l(SIZE - 4), slice(F32, SIZE - 4, 4)); + +assertEqX4(m.f32lcst(), slice(F32, CONSTANT_INDEX, 4)); +assertEqX4(m.f32lbndcheck(CONSTANT_BYTE_INDEX), slice(F32, CONSTANT_INDEX, 4)); + +// OOB +var BatNaN = [NaN, NaN, NaN, NaN] // NaNNaNNaNNaN etc. +assertEqX4(f32l(-1), BatNaN); +assertEqX4(f32l(SIZE), BatNaN); +assertEqX4(f32l(SIZE - 1), BatNaN); +assertEqX4(f32l(SIZE - 2), BatNaN); +assertEqX4(f32l(SIZE - 3), BatNaN); + +var code = ` + "use asm"; + var f4 = glob.SIMD.float32x4; + var f4l = f4.load; + var u8 = new glob.Uint8Array(heap); + + function g(x) { + x = x|0; + // set a constraint on the size of the heap + var ptr = 0; + ptr = u8[0xFFFF] | 0; + // give a precise range to x + x = (x>>0) > 5 ? 5 : x; + x = (x>>0) < 0 ? 0 : x; + // ptr value gets a precise range but the bounds check shouldn't get + // eliminated. + return f4(f4l(u8, 0xFFFA + x | 0)); + } + + return g; +`; +assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', code), this, {}, new ArrayBuffer(0x10000))(0), BatNaN); + +// Float32x4.store +function f32s(n, v) { return m.f32s((n|0) << 2 | 0, v); }; + +var vec = SIMD.float32x4(5,6,7,8); +var vec2 = SIMD.float32x4(0,1,2,3); + +reset(); +f32s(0, vec); +assertEqX4(vec, slice(F32, 0, 4)); + +reset(); +f32s(0, vec2); +assertEqX4(vec2, slice(F32, 0, 4)); + +reset(); +f32s(4, vec); +assertEqX4(vec, slice(F32, 4, 4)); + +reset(); +m.f32scst(vec2); +assertEqX4(vec2, slice(F32, CONSTANT_INDEX, 4)); + +reset(); +m.f32sbndcheck(CONSTANT_BYTE_INDEX, vec); +assertEqX4(vec, slice(F32, CONSTANT_INDEX, 4)); + +// OOB +reset(); +f32s(SIZE - 3, vec); +f32s(SIZE - 2, vec); +f32s(SIZE - 1, vec); +f32s(SIZE, vec); +for (var i = 0; i < SIZE; i++) + assertEq(F32[i], i + 1); + +// Int32x4.load +var I32 = new Int32Array(buf); +reset = function () { + for (var i = 0; i < SIZE; i++) + I32[i] = i + 1; +}; +reset(); + +function i32(n) { return m.i32l((n|0) << 2 | 0); }; + +// Correct accesses +assertEqX4(i32(0), slice(I32, 0, 4)); +assertEqX4(i32(1), slice(I32, 1, 4)); +assertEqX4(i32(SIZE - 4), slice(I32, SIZE - 4, 4)); + +assertEqX4(m.i32lcst(), slice(I32, CONSTANT_INDEX, 4)); + +// OOB +assertEqX4(i32(-1), [0,0,0,0]); +assertEqX4(i32(SIZE), [0,0,0,0]); +assertEqX4(i32(SIZE - 1), [0,0,0,0]); +assertEqX4(i32(SIZE - 2), [0,0,0,0]); +assertEqX4(i32(SIZE - 3), [0,0,0,0]); + +// Int32x4.store +function i32s(n, v) { return m.i32s((n|0) << 2 | 0, v); }; + +var vec = SIMD.int32x4(5,6,7,8); +var vec2 = SIMD.int32x4(0,1,2,3); + +reset(); +i32s(0, vec); +assertEqX4(vec, slice(I32, 0, 4)); + +reset(); +i32s(0, vec2); +assertEqX4(vec2, slice(I32, 0, 4)); + +reset(); +i32s(4, vec); +assertEqX4(vec, slice(I32, 4, 4)); + +reset(); +m.i32scst(vec2); +assertEqX4(vec2, slice(I32, CONSTANT_INDEX, 4)); + +// OOB +reset(); +i32s(SIZE - 3, vec); +i32s(SIZE - 2, vec); +i32s(SIZE - 1, vec); +i32s(SIZE - 0, vec); +for (var i = 0; i < SIZE; i++) + assertEq(I32[i], i + 1); diff --git a/js/src/jit-test/tests/asm.js/testSIMD.js b/js/src/jit-test/tests/asm.js/testSIMD.js index 21ec8c1e4d8f..038f90e04ccb 100644 --- a/js/src/jit-test/tests/asm.js/testSIMD.js +++ b/js/src/jit-test/tests/asm.js/testSIMD.js @@ -1059,247 +1059,6 @@ assertAsmTypeFail('glob', 'ffi', USE_ASM + F32 + "var func=ffi.func; function f( assertAsmTypeFail('glob', 'ffi', USE_ASM + I32 + "var func=ffi.func; function f() {var x=i4(1,2,3,4); x=i4(func());} return f"); assertAsmTypeFail('glob', 'ffi', USE_ASM + F32 + "var func=ffi.func; function f() {var x=f4(1,2,3,4); x=f4(func());} return f"); -// Load / Store -(function testLoadStore() { - -var IMPORTS = USE_ASM + 'var H=new glob.Uint8Array(heap); var i4=glob.SIMD.int32x4; var load=i4.load; var store=i4.store;'; - -// Bad number of args -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load();} return f"); -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3);} return f"); -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3, 4, 5);} return f"); - -// Bad type of args -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3, 5);} return f"); -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, 5.0);} return f"); -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0.;load(H, i);} return f"); -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var H2=new glob.Int32Array(heap); function f(){var i=0;load(H2, i)} return f"); -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var H2=42; function f(){var i=0;load(H2, i)} return f"); -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;load(H2, i)} return f"); -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var f4=glob.SIMD.float32x4; function f(){var i=0;var vec=f4(1,2,3,4); store(H, i, vec)} return f"); - -// Bad coercions of returned values -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return load(H, i)|0;} return f"); -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return +load(H, i);} return f"); - -// Literal index constants -var buf = new ArrayBuffer(BUF_MIN); -var asI32 = new Int32Array(buf); -asI32[(BUF_MIN >> 2) - 4] = 4; -asI32[(BUF_MIN >> 2) - 3] = 3; -asI32[(BUF_MIN >> 2) - 2] = 2; -asI32[(BUF_MIN >> 2) - 1] = 1; - -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1) + ");} return f"); -assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 15) + ");} return f"); -asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 16) + ");} return f"); - -assertAsmLinkFail(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + (BUF_MIN - 15) + "));} return f"), this, {}, buf); -assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + (BUF_MIN - 16) + "));} return f"), this, {}, buf)(), [4, 3, 2, 1]); -assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return i4(load(H, " + BUF_MIN + " - 16 | 0));} return f"), this, {}, buf)(), [4, 3, 2, 1]); - -var CONSTANT_INDEX = 42; -var CONSTANT_BYTE_INDEX = CONSTANT_INDEX << 2; - -var loadStoreCode = ` - "use asm"; - - var H = new glob.Uint8Array(heap); - - var i4 = glob.SIMD.int32x4; - var i4load = i4.load; - var i4store = i4.store; - - var f4 = glob.SIMD.float32x4; - var f4load = f4.load; - var f4store = f4.store; - - function f32l(i) { i=i|0; return f4(f4load(H, i|0)); } - function f32lcst() { return f4(f4load(H, ${CONSTANT_BYTE_INDEX})); } - function f32s(i, vec) { i=i|0; vec=f4(vec); f4store(H, i|0, vec); } - function f32scst(vec) { vec=f4(vec); f4store(H, ${CONSTANT_BYTE_INDEX}, vec); } - - function i32l(i) { i=i|0; return i4(i4load(H, i|0)); } - function i32lcst() { return i4(i4load(H, ${CONSTANT_BYTE_INDEX})); } - function i32s(i, vec) { i=i|0; vec=i4(vec); i4store(H, i|0, vec); } - function i32scst(vec) { vec=i4(vec); i4store(H, ${CONSTANT_BYTE_INDEX}, vec); } - - function f32lbndcheck(i) { - i=i|0; - if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX}; - if ((i|0) < 0) i = 0; - return f4(f4load(H, i|0)); - } - function f32sbndcheck(i, vec) { - i=i|0; - vec=f4(vec); - if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX}; - if ((i|0) < 0) i = 0; - return f4(f4store(H, i|0, vec)); - } - - return { - f32l: f32l, - f32lcst: f32lcst, - f32s: f32s, - f32scst: f32scst, - f32lbndcheck: f32lbndcheck, - f32sbndcheck: f32sbndcheck, - i32l: i32l, - i32lcst: i32lcst, - i32s: i32s, - i32scst: i32scst - } -`; - -const SIZE = 0x8000; - -var F32 = new Float32Array(SIZE); -var reset = function() { - for (var i = 0; i < SIZE; i++) - F32[i] = i + 1; -}; -reset(); - -var buf = F32.buffer; -var m = asmLink(asmCompile('glob', 'ffi', 'heap', loadStoreCode), this, null, buf); - -function slice(TA, i, n) { return Array.prototype.slice.call(TA, i, i + n); } - -// Float32x4.load -function f32l(n) { return m.f32l((n|0) << 2 | 0); }; - -// Correct accesses -assertEqX4(f32l(0), slice(F32, 0, 4)); -assertEqX4(f32l(1), slice(F32, 1, 4)); -assertEqX4(f32l(SIZE - 4), slice(F32, SIZE - 4, 4)); - -assertEqX4(m.f32lcst(), slice(F32, CONSTANT_INDEX, 4)); -assertEqX4(m.f32lbndcheck(CONSTANT_BYTE_INDEX), slice(F32, CONSTANT_INDEX, 4)); - -// OOB -var BatNaN = [NaN, NaN, NaN, NaN] // NaNNaNNaNNaN etc. -assertEqX4(f32l(-1), BatNaN); -assertEqX4(f32l(SIZE), BatNaN); -assertEqX4(f32l(SIZE - 1), BatNaN); -assertEqX4(f32l(SIZE - 2), BatNaN); -assertEqX4(f32l(SIZE - 3), BatNaN); - -var code = ` - "use asm"; - var f4 = glob.SIMD.float32x4; - var f4l = f4.load; - var u8 = new glob.Uint8Array(heap); - - function g(x) { - x = x|0; - // set a constraint on the size of the heap - var ptr = 0; - ptr = u8[0xFFFF] | 0; - // give a precise range to x - x = (x>>0) > 5 ? 5 : x; - x = (x>>0) < 0 ? 0 : x; - // ptr value gets a precise range but the bounds check shouldn't get - // eliminated. - return f4(f4l(u8, 0xFFFA + x | 0)); - } - - return g; -`; -assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', code), this, {}, new ArrayBuffer(0x10000))(0), BatNaN); - -// Float32x4.store -function f32s(n, v) { return m.f32s((n|0) << 2 | 0, v); }; - -var vec = SIMD.float32x4(5,6,7,8); -var vec2 = SIMD.float32x4(0,1,2,3); - -reset(); -f32s(0, vec); -assertEqX4(vec, slice(F32, 0, 4)); - -reset(); -f32s(0, vec2); -assertEqX4(vec2, slice(F32, 0, 4)); - -reset(); -f32s(4, vec); -assertEqX4(vec, slice(F32, 4, 4)); - -reset(); -m.f32scst(vec2); -assertEqX4(vec2, slice(F32, CONSTANT_INDEX, 4)); - -reset(); -m.f32sbndcheck(CONSTANT_BYTE_INDEX, vec); -assertEqX4(vec, slice(F32, CONSTANT_INDEX, 4)); - -// OOB -reset(); -f32s(SIZE - 3, vec); -f32s(SIZE - 2, vec); -f32s(SIZE - 1, vec); -f32s(SIZE, vec); -for (var i = 0; i < SIZE; i++) - assertEq(F32[i], i + 1); - -// Int32x4.load -var I32 = new Int32Array(buf); -reset = function () { - for (var i = 0; i < SIZE; i++) - I32[i] = i + 1; -}; -reset(); - -function i32(n) { return m.i32l((n|0) << 2 | 0); }; - -// Correct accesses -assertEqX4(i32(0), slice(I32, 0, 4)); -assertEqX4(i32(1), slice(I32, 1, 4)); -assertEqX4(i32(SIZE - 4), slice(I32, SIZE - 4, 4)); - -assertEqX4(m.i32lcst(), slice(I32, CONSTANT_INDEX, 4)); - -// OOB -assertEqX4(i32(-1), [0,0,0,0]); -assertEqX4(i32(SIZE), [0,0,0,0]); -assertEqX4(i32(SIZE - 1), [0,0,0,0]); -assertEqX4(i32(SIZE - 2), [0,0,0,0]); -assertEqX4(i32(SIZE - 3), [0,0,0,0]); - -// Int32x4.store -function i32s(n, v) { return m.i32s((n|0) << 2 | 0, v); }; - -var vec = SIMD.int32x4(5,6,7,8); -var vec2 = SIMD.int32x4(0,1,2,3); - -reset(); -i32s(0, vec); -assertEqX4(vec, slice(I32, 0, 4)); - -reset(); -i32s(0, vec2); -assertEqX4(vec2, slice(I32, 0, 4)); - -reset(); -i32s(4, vec); -assertEqX4(vec, slice(I32, 4, 4)); - -reset(); -m.i32scst(vec2); -assertEqX4(vec2, slice(I32, CONSTANT_INDEX, 4)); - -// OOB -reset(); -i32s(SIZE - 3, vec); -i32s(SIZE - 2, vec); -i32s(SIZE - 1, vec); -i32s(SIZE - 0, vec); -for (var i = 0; i < SIZE; i++) - assertEq(I32[i], i + 1); - -})(); - // 3.3 Internal calls // asm.js -> asm.js // Retrieving values from asm.js From f6b43dea9ec65b6c3e968e2e444312f5e77a9982 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 28 Jan 2015 09:31:07 +0100 Subject: [PATCH 06/39] Bug 1113338: Throw on out-of-bounds SIMD heap accesses in asm.js; r=luke --HG-- extra : rebase_source : a645d89059764f5caa2fed6a156afa7e31b5589c --- js/src/asmjs/AsmJSModule.cpp | 13 ++- js/src/asmjs/AsmJSModule.h | 9 +- js/src/asmjs/AsmJSSignalHandlers.cpp | 82 +++++++++++-------- js/src/asmjs/AsmJSValidate.cpp | 35 +++++++- js/src/builtin/SIMD.cpp | 1 + js/src/jit-test/tests/asm.js/testProfiling.js | 14 ++++ .../tests/asm.js/testSIMD-load-store.js | 42 +++++----- js/src/jit/MIR.h | 20 +++-- js/src/jit/shared/Assembler-shared.h | 1 + .../jit/shared/CodeGenerator-x86-shared.cpp | 8 +- js/src/jit/x64/CodeGenerator-x64.cpp | 16 ++-- js/src/jit/x86/CodeGenerator-x86.cpp | 21 +++-- js/src/jsfriendapi.h | 22 +++++ 13 files changed, 200 insertions(+), 84 deletions(-) diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index e4d4f5951bbd..022d8d1d78e3 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -273,7 +273,7 @@ AsmJSModule::lookupHeapAccess(void *pc) const bool AsmJSModule::finish(ExclusiveContext *cx, TokenStream &tokenStream, MacroAssembler &masm, - const Label &interruptLabel) + const Label &interruptLabel, const Label &outOfBoundsLabel) { MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished()); @@ -315,6 +315,7 @@ AsmJSModule::finish(ExclusiveContext *cx, TokenStream &tokenStream, MacroAssembl // Copy over metadata, making sure to update all offsets on ARM. staticLinkData_.interruptExitOffset = masm.actualOffset(interruptLabel.offset()); + staticLinkData_.outOfBoundsExitOffset = masm.actualOffset(outOfBoundsLabel.offset()); // Heap-access metadata used for link-time patching and fault-handling. heapAccesses_ = masm.extractAsmJSHeapAccesses(); @@ -465,6 +466,13 @@ OnDetached() JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY); } +static void +OnOutOfBounds() +{ + JSContext *cx = JSRuntime::innermostAsmJSActivation()->cx(); + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX); +} + static bool AsmJSHandleExecutionInterrupt() { @@ -659,6 +667,8 @@ AddressOf(AsmJSImmKind kind, ExclusiveContext *cx) return RedirectCall(FuncCast(AsmJSReportOverRecursed), Args_General0); case AsmJSImm_OnDetached: return RedirectCall(FuncCast(OnDetached), Args_General0); + case AsmJSImm_OnOutOfBounds: + return RedirectCall(FuncCast(OnOutOfBounds), Args_General0); case AsmJSImm_HandleExecutionInterrupt: return RedirectCall(FuncCast(AsmJSHandleExecutionInterrupt), Args_General0); case AsmJSImm_InvokeFromAsmJS_Ignore: @@ -730,6 +740,7 @@ AsmJSModule::staticallyLink(ExclusiveContext *cx) // Process staticLinkData_ interruptExit_ = code_ + staticLinkData_.interruptExitOffset; + outOfBoundsExit_ = code_ + staticLinkData_.outOfBoundsExitOffset; for (size_t i = 0; i < staticLinkData_.relativeLinks.length(); i++) { RelativeLink link = staticLinkData_.relativeLinks[i]; diff --git a/js/src/asmjs/AsmJSModule.h b/js/src/asmjs/AsmJSModule.h index 54f64168dbcd..46a6d929b081 100644 --- a/js/src/asmjs/AsmJSModule.h +++ b/js/src/asmjs/AsmJSModule.h @@ -782,6 +782,7 @@ class AsmJSModule struct StaticLinkData { uint32_t interruptExitOffset; + uint32_t outOfBoundsExitOffset; RelativeLinkVector relativeLinks; AbsoluteLinkArray absoluteLinks; @@ -844,6 +845,7 @@ class AsmJSModule PropertyName * bufferArgumentName_; uint8_t * code_; uint8_t * interruptExit_; + uint8_t * outOfBoundsExit_; StaticLinkData staticLinkData_; HeapPtrArrayBufferObjectMaybeShared maybeHeap_; AsmJSModule ** prevLinked_; @@ -1284,7 +1286,8 @@ class AsmJSModule bool finish(ExclusiveContext *cx, frontend::TokenStream &tokenStream, jit::MacroAssembler &masm, - const jit::Label &interruptLabel); + const jit::Label &interruptLabel, + const jit::Label &outOfBoundsLabel); /*************************************************************************/ // These accessor functions can be used after finish(): @@ -1550,6 +1553,10 @@ class AsmJSModule MOZ_ASSERT(isDynamicallyLinked()); return interruptExit_; } + uint8_t *outOfBoundsExit() const { + MOZ_ASSERT(isDynamicallyLinked()); + return outOfBoundsExit_; + } uint8_t *maybeHeap() const { MOZ_ASSERT(isDynamicallyLinked()); return heapDatum(); diff --git a/js/src/asmjs/AsmJSSignalHandlers.cpp b/js/src/asmjs/AsmJSSignalHandlers.cpp index 35400435007c..ebb38b60b039 100644 --- a/js/src/asmjs/AsmJSSignalHandlers.cpp +++ b/js/src/asmjs/AsmJSSignalHandlers.cpp @@ -350,20 +350,9 @@ SetXMMRegToNaN(Scalar::Type viewType, T *xmm_reg) dbls[1] = 0; break; } - case Scalar::Float32x4: { - JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float)); - float *floats = reinterpret_cast(xmm_reg); - for (unsigned i = 0; i < 4; i++) - floats[i] = GenericNaN(); - break; - } - case Scalar::Int32x4: { - JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(int32_t)); - int32_t *ints = reinterpret_cast(xmm_reg); - for (unsigned i = 0; i < 4; i++) - ints[i] = 0; - break; - } + // Float32x4 and Int32x4 out of bounds are handled with the OutOfBounds stub. + case Scalar::Float32x4: + case Scalar::Int32x4: case Scalar::Int8: case Scalar::Uint8: case Scalar::Int16: @@ -423,6 +412,13 @@ SetRegisterToCoercedUndefined(CONTEXT *context, Scalar::Type viewType, AnyRegist } } # endif // !XP_MACOSX + +static void +RedirectToOutOfBoundsLabel(uint8_t **ppc, const AsmJSModule &module) +{ + MOZ_ASSERT(module.containsFunctionPC(*ppc)); + *ppc = module.outOfBoundsExit(); +} #endif // JS_CODEGEN_X64 #if defined(XP_WIN) @@ -493,11 +489,18 @@ HandleFault(PEXCEPTION_POINTERS exception) return false; // We now know that this is an out-of-bounds access made by an asm.js - // load/store that we should handle. If this is a load, assign the - // JS-defined result value to the destination register (ToInt32(undefined) - // or ToNumber(undefined), determined by the type of the destination - // register) and set the PC to the next op. Upon return from the handler, - // execution will resume at this next PC. + // load/store that we should handle. + + // SIMD out-of-bounds loads and stores just need to throw. + if (Scalar::isSimdType(heapAccess->type())) { + RedirectToOutOfBoundsLabel(ppc, module); + return true; + } + + // If this is a load, assign the JS-defined result value to the destination + // register (ToInt32(undefined) or ToNumber(undefined), determined by the + // type of the destination register) and set the PC to the next op. Upon + // return from the handler, execution will resume at this next PC. if (heapAccess->isLoad()) SetRegisterToCoercedUndefined(context, heapAccess->type(), heapAccess->loadedReg()); *ppc += heapAccess->opLength(); @@ -671,16 +674,22 @@ HandleMachException(JSRuntime *rt, const ExceptionRequest &request) return false; // We now know that this is an out-of-bounds access made by an asm.js - // load/store that we should handle. If this is a load, assign the - // JS-defined result value to the destination register (ToInt32(undefined) - // or ToNumber(undefined), determined by the type of the destination - // register) and set the PC to the next op. Upon return from the handler, - // execution will resume at this next PC. - if (heapAccess->isLoad()) { - if (!SetRegisterToCoercedUndefined(rtThread, state.uts.ts64, *heapAccess)) - return false; + // load/store that we should handle. + + if (Scalar::isSimdType(heapAccess->type())) { + // SIMD out-of-bounds loads and stores just need to throw. + RedirectToOutOfBoundsLabel(ppc, module); + } else { + // If this is a load, assign the JS-defined result value to the destination + // register (ToInt32(undefined) or ToNumber(undefined), determined by the + // type of the destination register) and set the PC to the next op. Upon + // return from the handler, execution will resume at this next PC. + if (heapAccess->isLoad()) { + if (!SetRegisterToCoercedUndefined(rtThread, state.uts.ts64, *heapAccess)) + return false; + } + *ppc += heapAccess->opLength(); } - *ppc += heapAccess->opLength(); // Update the thread state with the new pc. kret = thread_set_state(rtThread, x86_THREAD_STATE, (thread_state_t)&state, x86_THREAD_STATE_COUNT); @@ -885,11 +894,18 @@ HandleFault(int signum, siginfo_t *info, void *ctx) return false; // We now know that this is an out-of-bounds access made by an asm.js - // load/store that we should handle. If this is a load, assign the - // JS-defined result value to the destination register (ToInt32(undefined) - // or ToNumber(undefined), determined by the type of the destination - // register) and set the PC to the next op. Upon return from the handler, - // execution will resume at this next PC. + // load/store that we should handle. + + // SIMD out-of-bounds loads and stores just need to throw. + if (Scalar::isSimdType(heapAccess->type())) { + RedirectToOutOfBoundsLabel(ppc, module); + return true; + } + + // If this is a load, assign the JS-defined result value to the destination + // register (ToInt32(undefined) or ToNumber(undefined), determined by the + // type of the destination register) and set the PC to the next op. Upon + // return from the handler, execution will resume at this next PC. if (heapAccess->isLoad()) SetRegisterToCoercedUndefined(context, heapAccess->type(), heapAccess->loadedReg()); *ppc += heapAccess->opLength(); diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index 0c6972c68a19..14475153b75d 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -1296,6 +1296,7 @@ class MOZ_STACK_CLASS ModuleCompiler NonAssertingLabel asyncInterruptLabel_; NonAssertingLabel syncInterruptLabel_; NonAssertingLabel onDetachedLabel_; + NonAssertingLabel onOutOfBoundsLabel_; UniquePtr errorString_; uint32_t errorOffset_; @@ -1514,6 +1515,7 @@ class MOZ_STACK_CLASS ModuleCompiler Label &asyncInterruptLabel() { return asyncInterruptLabel_; } Label &syncInterruptLabel() { return syncInterruptLabel_; } Label &onDetachedLabel() { return onDetachedLabel_; } + Label &onOutOfBoundsLabel() { return onOutOfBoundsLabel_; } bool hasError() const { return errorString_ != nullptr; } const AsmJSModule &module() const { return *module_.get(); } bool usesSignalHandlersForInterrupt() const { return module_->usesSignalHandlersForInterrupt(); } @@ -1803,7 +1805,7 @@ class MOZ_STACK_CLASS ModuleCompiler canValidateChangeHeap_ = false; return ret; } - bool hasChangeHeap() { + bool hasChangeHeap() const { return hasChangeHeap_; } bool finishGeneratingFunction(Func &func, CodeGenerator &codegen, @@ -1960,7 +1962,7 @@ class MOZ_STACK_CLASS ModuleCompiler if (masm_.oom()) return false; - if (!module_->finish(cx_, tokenStream(), masm_, asyncInterruptLabel_)) + if (!module_->finish(cx_, tokenStream(), masm_, asyncInterruptLabel_, onOutOfBoundsLabel_)) return false; // Finally, convert all the function-pointer table elements into @@ -2763,7 +2765,8 @@ class FunctionCompiler return nullptr; bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); - MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), vt, ptr, needsBoundsCheck); + Label *outOfBoundsLabel = Scalar::isSimdType(vt) ? &m().onOutOfBoundsLabel() : nullptr; + MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), vt, ptr, needsBoundsCheck, outOfBoundsLabel); curBlock_->add(load); return load; } @@ -2774,7 +2777,8 @@ class FunctionCompiler return; bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); - MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), vt, ptr, v, needsBoundsCheck); + Label *outOfBoundsLabel = Scalar::isSimdType(vt) ? &m().onOutOfBoundsLabel() : nullptr; + MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), vt, ptr, v, needsBoundsCheck, outOfBoundsLabel); curBlock_->add(store); } @@ -2793,6 +2797,7 @@ class FunctionCompiler bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), vt, ptr, needsBoundsCheck, + /* outOfBoundsLabel = */ nullptr, MembarBeforeLoad, MembarAfterLoad); curBlock_->add(load); return load; @@ -2805,6 +2810,7 @@ class FunctionCompiler bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), vt, ptr, v, needsBoundsCheck, + /* outOfBoundsLabel = */ nullptr, MembarBeforeStore, MembarAfterStore); curBlock_->add(store); } @@ -8817,6 +8823,24 @@ GenerateOnDetachedLabelExit(ModuleCompiler &m, Label *throwLabel) return m.finishGeneratingInlineStub(&m.onDetachedLabel()) && !masm.oom(); } +static bool +GenerateOnOutOfBoundsLabelExit(ModuleCompiler &m, Label *throwLabel) +{ + MacroAssembler &masm = m.masm(); + masm.bind(&m.onOutOfBoundsLabel()); + + // sp can be anything at this point, so ensure it is aligned when calling + // into C++. We unconditionally jump to throw so don't worry about restoring sp. + masm.andPtr(Imm32(~(ABIStackAlignment - 1)), StackPointer); + + // OnOutOfBounds always throws. + masm.assertStackAlignment(ABIStackAlignment); + masm.call(AsmJSImmPtr(AsmJSImm_OnOutOfBounds)); + masm.jump(throwLabel); + + return m.finishGeneratingInlineStub(&m.onOutOfBoundsLabel()) && !masm.oom(); +} + static const RegisterSet AllRegsExceptSP = RegisterSet(GeneralRegisterSet(Registers::AllMask & ~(uint32_t(1) << Registers::StackPointer)), @@ -9045,6 +9069,9 @@ GenerateStubs(ModuleCompiler &m) if (m.onDetachedLabel().used() && !GenerateOnDetachedLabelExit(m, &throwLabel)) return false; + if (!GenerateOnOutOfBoundsLabelExit(m, &throwLabel)) + return false; + if (!GenerateAsyncInterruptExit(m, &throwLabel)) return false; if (m.syncInterruptLabel().used() && !GenerateSyncInterruptExit(m, &throwLabel)) diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index ca29d20c4bcd..ae1716c64635 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -1049,6 +1049,7 @@ TypedArrayDataPtrFromArgs(JSContext *cx, const CallArgs &args, VElem **data) int32_t byteStart = index * typedArray->bytesPerElement(); if (byteStart < 0 || (uint32_t(byteStart) + NumElem * sizeof(VElem)) > typedArray->byteLength()) { + // Keep in sync with AsmJS OnOutOfBounds function. JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX); return false; } diff --git a/js/src/jit-test/tests/asm.js/testProfiling.js b/js/src/jit-test/tests/asm.js/testProfiling.js index 78a957404914..ee62ee959c06 100644 --- a/js/src/jit-test/tests/asm.js/testProfiling.js +++ b/js/src/jit-test/tests/asm.js/testProfiling.js @@ -172,6 +172,20 @@ assertThrowsInstanceOf(f, InternalError); var stacks = disableSingleStepProfiling(); assertStackContainsSeq(stacks, ">,f,>,<,f,>,inline stub,f,>,<,f,>,inline stub,f,>"); + +if (isSimdAvailable() && typeof SIMD !== 'undefined') { + // SIMD out-of-bounds exit + var buf = new ArrayBuffer(0x10000); + var f = asmLink(asmCompile('g','ffi','buf', USE_ASM + 'var f4=g.SIMD.float32x4; var f4l=f4.load; var u8=new g.Uint8Array(buf); function f(i) { i=i|0; return f4l(u8, 0xFFFF + i | 0); } return f'), this, {}, buf); + enableSingleStepProfiling(); + assertThrowsInstanceOf(() => f(4), RangeError); + var stacks = disableSingleStepProfiling(); + // TODO check that expected is actually the correctly expected string, when + // SIMD is implemented on ARM. + assertStackContainsSeq(stacks, ">,f,>,inline stub,f,>"); +} + + // This takes forever to run. // Stack-overflow exit test //var limit = -1; diff --git a/js/src/jit-test/tests/asm.js/testSIMD-load-store.js b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js index cfd778490699..5a7ae98f0da9 100644 --- a/js/src/jit-test/tests/asm.js/testSIMD-load-store.js +++ b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js @@ -1,4 +1,6 @@ +// |jit-test| test-also-noasmjs load(libdir + "asm.js"); +load(libdir + "asserts.js"); // Set to true to see more JS debugging spew const DEBUG = false; @@ -138,12 +140,11 @@ assertEqX4(m.f32lcst(), slice(F32, CONSTANT_INDEX, 4)); assertEqX4(m.f32lbndcheck(CONSTANT_BYTE_INDEX), slice(F32, CONSTANT_INDEX, 4)); // OOB -var BatNaN = [NaN, NaN, NaN, NaN] // NaNNaNNaNNaN etc. -assertEqX4(f32l(-1), BatNaN); -assertEqX4(f32l(SIZE), BatNaN); -assertEqX4(f32l(SIZE - 1), BatNaN); -assertEqX4(f32l(SIZE - 2), BatNaN); -assertEqX4(f32l(SIZE - 3), BatNaN); +assertThrowsInstanceOf(() => f32l(-1), RangeError); +assertThrowsInstanceOf(() => f32l(SIZE), RangeError); +assertThrowsInstanceOf(() => f32l(SIZE - 1), RangeError); +assertThrowsInstanceOf(() => f32l(SIZE - 2), RangeError); +assertThrowsInstanceOf(() => f32l(SIZE - 3), RangeError); var code = ` "use asm"; @@ -166,7 +167,7 @@ var code = ` return g; `; -assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', code), this, {}, new ArrayBuffer(0x10000))(0), BatNaN); +assertThrowsInstanceOf(() =>asmLink(asmCompile('glob', 'ffi', 'heap', code), this, {}, new ArrayBuffer(0x10000))(0), RangeError); // Float32x4.store function f32s(n, v) { return m.f32s((n|0) << 2 | 0, v); }; @@ -196,10 +197,10 @@ assertEqX4(vec, slice(F32, CONSTANT_INDEX, 4)); // OOB reset(); -f32s(SIZE - 3, vec); -f32s(SIZE - 2, vec); -f32s(SIZE - 1, vec); -f32s(SIZE, vec); +assertThrowsInstanceOf(() => f32s(SIZE - 3, vec), RangeError); +assertThrowsInstanceOf(() => f32s(SIZE - 2, vec), RangeError); +assertThrowsInstanceOf(() => f32s(SIZE - 1, vec), RangeError); +assertThrowsInstanceOf(() => f32s(SIZE, vec), RangeError); for (var i = 0; i < SIZE; i++) assertEq(F32[i], i + 1); @@ -221,11 +222,11 @@ assertEqX4(i32(SIZE - 4), slice(I32, SIZE - 4, 4)); assertEqX4(m.i32lcst(), slice(I32, CONSTANT_INDEX, 4)); // OOB -assertEqX4(i32(-1), [0,0,0,0]); -assertEqX4(i32(SIZE), [0,0,0,0]); -assertEqX4(i32(SIZE - 1), [0,0,0,0]); -assertEqX4(i32(SIZE - 2), [0,0,0,0]); -assertEqX4(i32(SIZE - 3), [0,0,0,0]); +assertThrowsInstanceOf(() => i32(-1), RangeError); +assertThrowsInstanceOf(() => i32(SIZE), RangeError); +assertThrowsInstanceOf(() => i32(SIZE - 1), RangeError); +assertThrowsInstanceOf(() => i32(SIZE - 2), RangeError); +assertThrowsInstanceOf(() => i32(SIZE - 3), RangeError); // Int32x4.store function i32s(n, v) { return m.i32s((n|0) << 2 | 0, v); }; @@ -251,9 +252,10 @@ assertEqX4(vec2, slice(I32, CONSTANT_INDEX, 4)); // OOB reset(); -i32s(SIZE - 3, vec); -i32s(SIZE - 2, vec); -i32s(SIZE - 1, vec); -i32s(SIZE - 0, vec); +assertThrowsInstanceOf(() => i32s(SIZE - 3, vec), RangeError); +assertThrowsInstanceOf(() => i32s(SIZE - 2, vec), RangeError); +assertThrowsInstanceOf(() => i32s(SIZE - 1, vec), RangeError); +assertThrowsInstanceOf(() => i32s(SIZE - 0, vec), RangeError); for (var i = 0; i < SIZE; i++) assertEq(I32[i], i + 1); + diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 2a914d98adcf..c5e0ece4dc51 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -12149,15 +12149,17 @@ class MAsmJSHeapAccess { Scalar::Type viewType_; bool needsBoundsCheck_; + Label *outOfBoundsLabel_; public: - MAsmJSHeapAccess(Scalar::Type vt, bool needsBoundsCheck) - : viewType_(vt), needsBoundsCheck_(needsBoundsCheck) + MAsmJSHeapAccess(Scalar::Type vt, bool needsBoundsCheck, Label *outOfBoundsLabel = nullptr) + : viewType_(vt), needsBoundsCheck_(needsBoundsCheck), outOfBoundsLabel_(outOfBoundsLabel) {} Scalar::Type viewType() const { return viewType_; } bool needsBoundsCheck() const { return needsBoundsCheck_; } void removeBoundsCheck() { needsBoundsCheck_ = false; } + Label *outOfBoundsLabel() const { return outOfBoundsLabel_; } }; class MAsmJSLoadHeap @@ -12169,9 +12171,9 @@ class MAsmJSLoadHeap MemoryBarrierBits barrierAfter_; MAsmJSLoadHeap(Scalar::Type vt, MDefinition *ptr, bool needsBoundsCheck, - MemoryBarrierBits before, MemoryBarrierBits after) + Label *outOfBoundsLabel, MemoryBarrierBits before, MemoryBarrierBits after) : MUnaryInstruction(ptr), - MAsmJSHeapAccess(vt, needsBoundsCheck), + MAsmJSHeapAccess(vt, needsBoundsCheck, outOfBoundsLabel), barrierBefore_(before), barrierAfter_(after) { @@ -12212,10 +12214,11 @@ class MAsmJSLoadHeap static MAsmJSLoadHeap *New(TempAllocator &alloc, Scalar::Type vt, MDefinition *ptr, bool needsBoundsCheck, + Label *outOfBoundsLabel = nullptr, MemoryBarrierBits barrierBefore = MembarNobits, MemoryBarrierBits barrierAfter = MembarNobits) { - return new(alloc) MAsmJSLoadHeap(vt, ptr, needsBoundsCheck, barrierBefore, barrierAfter); + return new(alloc) MAsmJSLoadHeap(vt, ptr, needsBoundsCheck, outOfBoundsLabel, barrierBefore, barrierAfter); } MDefinition *ptr() const { return getOperand(0); } @@ -12238,9 +12241,9 @@ class MAsmJSStoreHeap MemoryBarrierBits barrierAfter_; MAsmJSStoreHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck, - MemoryBarrierBits before, MemoryBarrierBits after) + Label *outOfBoundsLabel, MemoryBarrierBits before, MemoryBarrierBits after) : MBinaryInstruction(ptr, v), - MAsmJSHeapAccess(vt, needsBoundsCheck), + MAsmJSHeapAccess(vt, needsBoundsCheck, outOfBoundsLabel), barrierBefore_(before), barrierAfter_(after) { @@ -12253,10 +12256,11 @@ class MAsmJSStoreHeap static MAsmJSStoreHeap *New(TempAllocator &alloc, Scalar::Type vt, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck, + Label *outOfBoundsLabel = nullptr, MemoryBarrierBits barrierBefore = MembarNobits, MemoryBarrierBits barrierAfter = MembarNobits) { - return new(alloc) MAsmJSStoreHeap(vt, ptr, v, needsBoundsCheck, + return new(alloc) MAsmJSStoreHeap(vt, ptr, v, needsBoundsCheck, outOfBoundsLabel, barrierBefore, barrierAfter); } diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index b9b29ba3f14e..661080da3297 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -860,6 +860,7 @@ enum AsmJSImmKind AsmJSImm_StackLimit, AsmJSImm_ReportOverRecursed, AsmJSImm_OnDetached, + AsmJSImm_OnOutOfBounds, AsmJSImm_HandleExecutionInterrupt, AsmJSImm_InvokeFromAsmJS_Ignore, AsmJSImm_InvokeFromAsmJS_ToInt32, diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.cpp b/js/src/jit/shared/CodeGenerator-x86-shared.cpp index fb38d51ae830..52d4528fb8d5 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -336,6 +336,8 @@ void CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool) { switch (ool->viewType()) { + case Scalar::Float32x4: + case Scalar::Int32x4: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); case Scalar::Float32: @@ -344,12 +346,6 @@ CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTyp case Scalar::Float64: masm.loadConstantDouble(GenericNaN(), ool->dest().fpu()); break; - case Scalar::Float32x4: - masm.loadConstantFloat32x4(SimdConstant::SplatX4(float(GenericNaN())), ool->dest().fpu()); - break; - case Scalar::Int32x4: - masm.loadConstantInt32x4(SimdConstant::SplatX4(0), ool->dest().fpu()); - break; case Scalar::Int8: case Scalar::Uint8: case Scalar::Int16: diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 96d4b8a4bb0d..3a9ebfd82e3e 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -275,11 +275,14 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr; uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck; if (mir->needsBoundsCheck()) { - ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt); - addOutOfLineCode(ool, ins->mir()); - CodeOffsetLabel cmp = masm.cmp32WithPatch(ToRegister(ptr), Imm32(0)); - masm.j(Assembler::AboveOrEqual, ool->entry()); + if (mir->outOfBoundsLabel()) { + masm.j(Assembler::AboveOrEqual, mir->outOfBoundsLabel()); // Throws RangeError + } else { + ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt); + addOutOfLineCode(ool, ins->mir()); + masm.j(Assembler::AboveOrEqual, ool->entry()); + } maybeCmpOffset = cmp.offset(); } @@ -328,7 +331,10 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck; if (mir->needsBoundsCheck()) { CodeOffsetLabel cmp = masm.cmp32WithPatch(ToRegister(ptr), Imm32(0)); - masm.j(Assembler::AboveOrEqual, &rejoin); + if (mir->outOfBoundsLabel()) + masm.j(Assembler::AboveOrEqual, mir->outOfBoundsLabel()); // Throws RangeError + else + masm.j(Assembler::AboveOrEqual, &rejoin); maybeCmpOffset = cmp.offset(); } diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 8b4f48e4d237..9b93da9c6aaf 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -384,16 +384,21 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) return; } - OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt); - addOutOfLineCode(ool, mir); - + OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr; CodeOffsetLabel cmp = masm.cmp32WithPatch(ptrReg, Imm32(0)); - masm.j(Assembler::AboveOrEqual, ool->entry()); + if (mir->outOfBoundsLabel()) { + masm.j(Assembler::AboveOrEqual, mir->outOfBoundsLabel()); // Throws RangeError + } else { + ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt); + addOutOfLineCode(ool, mir); + masm.j(Assembler::AboveOrEqual, ool->entry()); + } uint32_t before = masm.size(); load(vt, srcAddr, out); uint32_t after = masm.size(); - masm.bind(ool->rejoin()); + if (ool) + masm.bind(ool->rejoin()); memoryBarrier(ins->mir()->barrierAfter()); masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset())); } @@ -486,7 +491,11 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) CodeOffsetLabel cmp = masm.cmp32WithPatch(ptrReg, Imm32(0)); Label rejoin; - masm.j(Assembler::AboveOrEqual, &rejoin); + + if (mir->outOfBoundsLabel()) + masm.j(Assembler::AboveOrEqual, mir->outOfBoundsLabel()); // Throws RangeError + else + masm.j(Assembler::AboveOrEqual, &rejoin); uint32_t before = masm.size(); store(vt, value, dstAddr); diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 14b66eedb53e..2292aa363eb9 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1445,6 +1445,28 @@ byteSize(Type atype) } } +static inline bool +isSimdType(Type atype) { + switch (atype) { + case Int8: + case Uint8: + case Uint8Clamped: + case Int16: + case Uint16: + case Int32: + case Uint32: + case Float32: + case Float64: + return false; + case Int32x4: + case Float32x4: + return true; + case MaxTypedArrayViewType: + break; + } + MOZ_CRASH("invalid scalar type"); +} + } /* namespace Scalar */ } /* namespace js */ From de9ea0eeeb510e5b55d56e7bcdb484e94393f81f Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 26 Jan 2015 19:57:24 +0100 Subject: [PATCH 07/39] Bug 1113338: Rename viewType into accessType everywhere; r=luke --HG-- extra : rebase_source : 90af0e88aea7d95493ec1a00571f91bc37ffa9af --- js/src/asmjs/AsmJSValidate.cpp | 30 ++++++------ js/src/jit/MIR.cpp | 6 +-- js/src/jit/MIR.h | 59 ++++++++++++----------- js/src/jit/RangeAnalysis.cpp | 4 +- js/src/jit/TypePolicy.cpp | 2 +- js/src/jit/arm/CodeGenerator-arm.cpp | 8 +-- js/src/jit/arm/Lowering-arm.cpp | 4 +- js/src/jit/mips/CodeGenerator-mips.cpp | 4 +- js/src/jit/shared/Lowering-x86-shared.cpp | 4 +- js/src/jit/x64/CodeGenerator-x64.cpp | 12 ++--- js/src/jit/x64/Lowering-x64.cpp | 2 +- js/src/jit/x86/CodeGenerator-x86.cpp | 16 +++--- js/src/jit/x86/Lowering-x86.cpp | 6 +-- 13 files changed, 80 insertions(+), 77 deletions(-) diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index 14475153b75d..6d6a68d01219 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -2759,26 +2759,28 @@ class FunctionCompiler curBlock_->setSlot(info().localSlot(local.slot), def); } - MDefinition *loadHeap(Scalar::Type vt, MDefinition *ptr, NeedsBoundsCheck chk) + MDefinition *loadHeap(Scalar::Type accessType, MDefinition *ptr, NeedsBoundsCheck chk) { if (inDeadCode()) return nullptr; bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); - Label *outOfBoundsLabel = Scalar::isSimdType(vt) ? &m().onOutOfBoundsLabel() : nullptr; - MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), vt, ptr, needsBoundsCheck, outOfBoundsLabel); + Label *outOfBoundsLabel = Scalar::isSimdType(accessType) ? &m().onOutOfBoundsLabel() : nullptr; + MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck, + outOfBoundsLabel); curBlock_->add(load); return load; } - void storeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk) + void storeHeap(Scalar::Type accessType, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk) { if (inDeadCode()) return; bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); - Label *outOfBoundsLabel = Scalar::isSimdType(vt) ? &m().onOutOfBoundsLabel() : nullptr; - MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), vt, ptr, v, needsBoundsCheck, outOfBoundsLabel); + Label *outOfBoundsLabel = Scalar::isSimdType(accessType) ? &m().onOutOfBoundsLabel() : nullptr; + MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck, + outOfBoundsLabel); curBlock_->add(store); } @@ -2790,32 +2792,32 @@ class FunctionCompiler curBlock_->add(ins); } - MDefinition *atomicLoadHeap(Scalar::Type vt, MDefinition *ptr, NeedsBoundsCheck chk) + MDefinition *atomicLoadHeap(Scalar::Type accessType, MDefinition *ptr, NeedsBoundsCheck chk) { if (inDeadCode()) return nullptr; bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); - MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), vt, ptr, needsBoundsCheck, + MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck, /* outOfBoundsLabel = */ nullptr, MembarBeforeLoad, MembarAfterLoad); curBlock_->add(load); return load; } - void atomicStoreHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk) + void atomicStoreHeap(Scalar::Type accessType, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk) { if (inDeadCode()) return; bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); - MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), vt, ptr, v, needsBoundsCheck, + MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck, /* outOfBoundsLabel = */ nullptr, MembarBeforeStore, MembarAfterStore); curBlock_->add(store); } - MDefinition *atomicCompareExchangeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *oldv, + MDefinition *atomicCompareExchangeHeap(Scalar::Type accessType, MDefinition *ptr, MDefinition *oldv, MDefinition *newv, NeedsBoundsCheck chk) { if (inDeadCode()) @@ -2823,12 +2825,12 @@ class FunctionCompiler bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; MAsmJSCompareExchangeHeap *cas = - MAsmJSCompareExchangeHeap::New(alloc(), vt, ptr, oldv, newv, needsBoundsCheck); + MAsmJSCompareExchangeHeap::New(alloc(), accessType, ptr, oldv, newv, needsBoundsCheck); curBlock_->add(cas); return cas; } - MDefinition *atomicBinopHeap(js::jit::AtomicOp op, Scalar::Type vt, MDefinition *ptr, + MDefinition *atomicBinopHeap(js::jit::AtomicOp op, Scalar::Type accessType, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk) { if (inDeadCode()) @@ -2836,7 +2838,7 @@ class FunctionCompiler bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK; MAsmJSAtomicBinopHeap *binop = - MAsmJSAtomicBinopHeap::New(alloc(), op, vt, ptr, v, needsBoundsCheck); + MAsmJSAtomicBinopHeap::New(alloc(), op, accessType, ptr, v, needsBoundsCheck); curBlock_->add(binop); return binop; } diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 130f3e022d40..7973d19734fd 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -3794,7 +3794,7 @@ MAsmJSLoadHeap::mightAlias(const MDefinition *def) const { if (def->isAsmJSStoreHeap()) { const MAsmJSStoreHeap *store = def->toAsmJSStoreHeap(); - if (store->viewType() != viewType()) + if (store->accessType() != accessType()) return true; if (!ptr()->isConstant() || !store->ptr()->isConstant()) return true; @@ -3810,7 +3810,7 @@ MAsmJSLoadHeap::congruentTo(const MDefinition *ins) const if (!ins->isAsmJSLoadHeap()) return false; const MAsmJSLoadHeap *load = ins->toAsmJSLoadHeap(); - return load->viewType() == viewType() && congruentIfOperandsEqual(load); + return load->accessType() == accessType() && congruentIfOperandsEqual(load); } bool @@ -4066,7 +4066,7 @@ MLoadTypedArrayElementStatic::congruentTo(const MDefinition *ins) const return false; if (needsBoundsCheck() != other->needsBoundsCheck()) return false; - if (viewType() != other->viewType()) + if (accessType() != other->accessType()) return false; if (base() != other->base()) return false; diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index c5e0ece4dc51..4888ff67f8b7 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -8755,7 +8755,7 @@ class MLoadTypedArrayElementStatic : MUnaryInstruction(ptr), someTypedArray_(someTypedArray), offset_(offset), needsBoundsCheck_(needsBoundsCheck), fallible_(true) { - int type = viewType(); + int type = accessType(); if (type == Scalar::Float32) setResultType(MIRType_Float32); else if (type == Scalar::Float64) @@ -8783,7 +8783,7 @@ class MLoadTypedArrayElementStatic needsBoundsCheck); } - Scalar::Type viewType() const { + Scalar::Type accessType() const { return AnyTypedArrayType(someTypedArray_); } void *base() const; @@ -8810,7 +8810,7 @@ class MLoadTypedArrayElementStatic void computeRange(TempAllocator &alloc) MOZ_OVERRIDE; bool needTruncation(TruncateKind kind) MOZ_OVERRIDE; - bool canProduceFloat32() const MOZ_OVERRIDE { return viewType() == Scalar::Float32; } + bool canProduceFloat32() const MOZ_OVERRIDE { return accessType() == Scalar::Float32; } void collectRangeInfoPreTrunc() MOZ_OVERRIDE; }; @@ -8997,12 +8997,12 @@ class MStoreTypedArrayElementStatic : offset, needsBoundsCheck); } - Scalar::Type viewType() const { + Scalar::Type accessType() const { return AnyTypedArrayType(someTypedArray_); } bool isFloatArray() const { - return viewType() == Scalar::Float32 || - viewType() == Scalar::Float64; + return accessType() == Scalar::Float32 || + accessType() == Scalar::Float64; } void *base() const; @@ -9020,7 +9020,7 @@ class MStoreTypedArrayElementStatic : TruncateKind operandTruncateKind(size_t index) const MOZ_OVERRIDE; bool canConsumeFloat32(MUse *use) const MOZ_OVERRIDE { - return use == getUseFor(1) && viewType() == Scalar::Float32; + return use == getUseFor(1) && accessType() == Scalar::Float32; } void collectRangeInfoPreTrunc() MOZ_OVERRIDE; }; @@ -12147,16 +12147,16 @@ class MAsmJSNeg class MAsmJSHeapAccess { - Scalar::Type viewType_; + Scalar::Type accessType_; bool needsBoundsCheck_; Label *outOfBoundsLabel_; public: - MAsmJSHeapAccess(Scalar::Type vt, bool needsBoundsCheck, Label *outOfBoundsLabel = nullptr) - : viewType_(vt), needsBoundsCheck_(needsBoundsCheck), outOfBoundsLabel_(outOfBoundsLabel) + MAsmJSHeapAccess(Scalar::Type accessType, bool needsBoundsCheck, Label *outOfBoundsLabel = nullptr) + : accessType_(accessType), needsBoundsCheck_(needsBoundsCheck), outOfBoundsLabel_(outOfBoundsLabel) {} - Scalar::Type viewType() const { return viewType_; } + Scalar::Type accessType() const { return accessType_; } bool needsBoundsCheck() const { return needsBoundsCheck_; } void removeBoundsCheck() { needsBoundsCheck_ = false; } Label *outOfBoundsLabel() const { return outOfBoundsLabel_; } @@ -12170,10 +12170,10 @@ class MAsmJSLoadHeap MemoryBarrierBits barrierBefore_; MemoryBarrierBits barrierAfter_; - MAsmJSLoadHeap(Scalar::Type vt, MDefinition *ptr, bool needsBoundsCheck, + MAsmJSLoadHeap(Scalar::Type accessType, MDefinition *ptr, bool needsBoundsCheck, Label *outOfBoundsLabel, MemoryBarrierBits before, MemoryBarrierBits after) : MUnaryInstruction(ptr), - MAsmJSHeapAccess(vt, needsBoundsCheck, outOfBoundsLabel), + MAsmJSHeapAccess(accessType, needsBoundsCheck, outOfBoundsLabel), barrierBefore_(before), barrierAfter_(after) { @@ -12182,7 +12182,7 @@ class MAsmJSLoadHeap else setMovable(); - switch (vt) { + switch (accessType) { case Scalar::Int8: case Scalar::Uint8: case Scalar::Int16: @@ -12212,13 +12212,14 @@ class MAsmJSLoadHeap public: INSTRUCTION_HEADER(AsmJSLoadHeap) - static MAsmJSLoadHeap *New(TempAllocator &alloc, Scalar::Type vt, + static MAsmJSLoadHeap *New(TempAllocator &alloc, Scalar::Type accessType, MDefinition *ptr, bool needsBoundsCheck, Label *outOfBoundsLabel = nullptr, MemoryBarrierBits barrierBefore = MembarNobits, MemoryBarrierBits barrierAfter = MembarNobits) { - return new(alloc) MAsmJSLoadHeap(vt, ptr, needsBoundsCheck, outOfBoundsLabel, barrierBefore, barrierAfter); + return new(alloc) MAsmJSLoadHeap(accessType, ptr, needsBoundsCheck, outOfBoundsLabel, + barrierBefore, barrierAfter); } MDefinition *ptr() const { return getOperand(0); } @@ -12240,10 +12241,10 @@ class MAsmJSStoreHeap MemoryBarrierBits barrierBefore_; MemoryBarrierBits barrierAfter_; - MAsmJSStoreHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck, + MAsmJSStoreHeap(Scalar::Type accessType, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck, Label *outOfBoundsLabel, MemoryBarrierBits before, MemoryBarrierBits after) : MBinaryInstruction(ptr, v), - MAsmJSHeapAccess(vt, needsBoundsCheck, outOfBoundsLabel), + MAsmJSHeapAccess(accessType, needsBoundsCheck, outOfBoundsLabel), barrierBefore_(before), barrierAfter_(after) { @@ -12254,13 +12255,13 @@ class MAsmJSStoreHeap public: INSTRUCTION_HEADER(AsmJSStoreHeap) - static MAsmJSStoreHeap *New(TempAllocator &alloc, Scalar::Type vt, + static MAsmJSStoreHeap *New(TempAllocator &alloc, Scalar::Type accessType, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck, Label *outOfBoundsLabel = nullptr, MemoryBarrierBits barrierBefore = MembarNobits, MemoryBarrierBits barrierAfter = MembarNobits) { - return new(alloc) MAsmJSStoreHeap(vt, ptr, v, needsBoundsCheck, outOfBoundsLabel, + return new(alloc) MAsmJSStoreHeap(accessType, ptr, v, needsBoundsCheck, outOfBoundsLabel, barrierBefore, barrierAfter); } @@ -12279,10 +12280,10 @@ class MAsmJSCompareExchangeHeap public MAsmJSHeapAccess, public NoTypePolicy::Data { - MAsmJSCompareExchangeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *oldv, MDefinition *newv, - bool needsBoundsCheck) + MAsmJSCompareExchangeHeap(Scalar::Type accessType, MDefinition *ptr, MDefinition *oldv, + MDefinition *newv, bool needsBoundsCheck) : MTernaryInstruction(ptr, oldv, newv), - MAsmJSHeapAccess(vt, needsBoundsCheck) + MAsmJSHeapAccess(accessType, needsBoundsCheck) { setGuard(); // Not removable setResultType(MIRType_Int32); @@ -12291,11 +12292,11 @@ class MAsmJSCompareExchangeHeap public: INSTRUCTION_HEADER(AsmJSCompareExchangeHeap) - static MAsmJSCompareExchangeHeap *New(TempAllocator &alloc, Scalar::Type vt, + static MAsmJSCompareExchangeHeap *New(TempAllocator &alloc, Scalar::Type accessType, MDefinition *ptr, MDefinition *oldv, MDefinition *newv, bool needsBoundsCheck) { - return new(alloc) MAsmJSCompareExchangeHeap(vt, ptr, oldv, newv, needsBoundsCheck); + return new(alloc) MAsmJSCompareExchangeHeap(accessType, ptr, oldv, newv, needsBoundsCheck); } MDefinition *ptr() const { return getOperand(0); } @@ -12314,10 +12315,10 @@ class MAsmJSAtomicBinopHeap { AtomicOp op_; - MAsmJSAtomicBinopHeap(AtomicOp op, Scalar::Type vt, MDefinition *ptr, MDefinition *v, + MAsmJSAtomicBinopHeap(AtomicOp op, Scalar::Type accessType, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck) : MBinaryInstruction(ptr, v), - MAsmJSHeapAccess(vt, needsBoundsCheck), + MAsmJSHeapAccess(accessType, needsBoundsCheck), op_(op) { setGuard(); // Not removable @@ -12327,10 +12328,10 @@ class MAsmJSAtomicBinopHeap public: INSTRUCTION_HEADER(AsmJSAtomicBinopHeap) - static MAsmJSAtomicBinopHeap *New(TempAllocator &alloc, AtomicOp op, Scalar::Type vt, + static MAsmJSAtomicBinopHeap *New(TempAllocator &alloc, AtomicOp op, Scalar::Type accessType, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck) { - return new(alloc) MAsmJSAtomicBinopHeap(op, vt, ptr, v, needsBoundsCheck); + return new(alloc) MAsmJSAtomicBinopHeap(op, accessType, ptr, v, needsBoundsCheck); } AtomicOp operation() const { return op_; } diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index 1e11a9f12c63..9dbda50e7fc7 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -2210,7 +2210,7 @@ RangeAnalysis::analyze() if (iter->isAsmJSLoadHeap()) { MAsmJSLoadHeap *ins = iter->toAsmJSLoadHeap(); Range *range = ins->ptr()->range(); - uint32_t elemSize = TypedArrayElemSize(ins->viewType()); + uint32_t elemSize = TypedArrayElemSize(ins->accessType()); if (range && range->hasInt32LowerBound() && range->lower() >= 0 && range->hasInt32UpperBound() && uint32_t(range->upper()) + elemSize <= minHeapLength) { ins->removeBoundsCheck(); @@ -2218,7 +2218,7 @@ RangeAnalysis::analyze() } else if (iter->isAsmJSStoreHeap()) { MAsmJSStoreHeap *ins = iter->toAsmJSStoreHeap(); Range *range = ins->ptr()->range(); - uint32_t elemSize = TypedArrayElemSize(ins->viewType()); + uint32_t elemSize = TypedArrayElemSize(ins->accessType()); if (range && range->hasInt32LowerBound() && range->lower() >= 0 && range->hasInt32UpperBound() && uint32_t(range->upper()) + elemSize <= minHeapLength) { ins->removeBoundsCheck(); diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp index 31ae3666e6a4..07eb839fe4e3 100644 --- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -918,7 +918,7 @@ StoreTypedArrayElementStaticPolicy::adjustInputs(TempAllocator &alloc, MInstruct MStoreTypedArrayElementStatic *store = ins->toStoreTypedArrayElementStatic(); return ConvertToInt32Policy<0>::staticAdjustInputs(alloc, ins) && - StoreTypedArrayPolicy::adjustValueInput(alloc, ins, store->viewType(), store->value(), 1); + StoreTypedArrayPolicy::adjustValueInput(alloc, ins, store->accessType(), store->value(), 1); } bool diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index 230ea46983c6..50129cdb5583 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -1772,7 +1772,7 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) bool isSigned; int size; bool isFloat = false; - switch (mir->viewType()) { + switch (mir->accessType()) { case Scalar::Int8: isSigned = true; size = 8; break; case Scalar::Uint8: isSigned = false; size = 8; break; case Scalar::Int16: isSigned = true; size = 16; break; @@ -1852,7 +1852,7 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) bool isSigned; int size; bool isFloat = false; - switch (mir->viewType()) { + switch (mir->accessType()) { case Scalar::Int8: case Scalar::Uint8: isSigned = false; size = 8; break; case Scalar::Int16: @@ -1920,7 +1920,7 @@ void CodeGeneratorARM::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins) { MAsmJSCompareExchangeHeap *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); const LAllocation *ptr = ins->ptr(); Register ptrReg = ToRegister(ptr); BaseIndex srcAddr(HeapReg, ptrReg, TimesOne); @@ -1954,7 +1954,7 @@ void CodeGeneratorARM::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins) { MAsmJSAtomicBinopHeap *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); const LAllocation *ptr = ins->ptr(); Register ptrReg = ToRegister(ptr); Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp()); diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index 14f2c70d0331..920b3340b570 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -644,7 +644,7 @@ LIRGeneratorARM::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArra void LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins) { - MOZ_ASSERT(ins->viewType() < Scalar::Float32); + MOZ_ASSERT(ins->accessType() < Scalar::Float32); MDefinition *ptr = ins->ptr(); MOZ_ASSERT(ptr->type() == MIRType_Int32); @@ -660,7 +660,7 @@ LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins) void LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins) { - MOZ_ASSERT(ins->viewType() < Scalar::Float32); + MOZ_ASSERT(ins->accessType() < Scalar::Float32); MDefinition *ptr = ins->ptr(); MOZ_ASSERT(ptr->type() == MIRType_Int32); diff --git a/js/src/jit/mips/CodeGenerator-mips.cpp b/js/src/jit/mips/CodeGenerator-mips.cpp index aa964292667e..4a6b3ff776bc 100644 --- a/js/src/jit/mips/CodeGenerator-mips.cpp +++ b/js/src/jit/mips/CodeGenerator-mips.cpp @@ -1828,7 +1828,7 @@ CodeGeneratorMIPS::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) bool isSigned; int size; bool isFloat = false; - switch (mir->viewType()) { + switch (mir->accessType()) { case Scalar::Int8: isSigned = true; size = 8; break; case Scalar::Uint8: isSigned = false; size = 8; break; case Scalar::Int16: isSigned = true; size = 16; break; @@ -1916,7 +1916,7 @@ CodeGeneratorMIPS::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) bool isSigned; int size; bool isFloat = false; - switch (mir->viewType()) { + switch (mir->accessType()) { case Scalar::Int8: isSigned = true; size = 8; break; case Scalar::Uint8: isSigned = false; size = 8; break; case Scalar::Int16: isSigned = true; size = 16; break; diff --git a/js/src/jit/shared/Lowering-x86-shared.cpp b/js/src/jit/shared/Lowering-x86-shared.cpp index cda936c5701e..e7ad6549b538 100644 --- a/js/src/jit/shared/Lowering-x86-shared.cpp +++ b/js/src/jit/shared/Lowering-x86-shared.cpp @@ -510,7 +510,7 @@ LIRGeneratorX86Shared::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap * MOZ_ASSERT(ptr->type() == MIRType_Int32); bool byteArray = false; - switch (ins->viewType()) { + switch (ins->accessType()) { case Scalar::Int8: case Scalar::Uint8: byteArray = true; @@ -555,7 +555,7 @@ LIRGeneratorX86Shared::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins) MOZ_ASSERT(ptr->type() == MIRType_Int32); bool byteArray = false; - switch (ins->viewType()) { + switch (ins->accessType()) { case Scalar::Int8: case Scalar::Uint8: byteArray = true; diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 3a9ebfd82e3e..3fb65fa12a52 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -258,7 +258,7 @@ void CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) { MAsmJSLoadHeap *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); const LAllocation *ptr = ins->ptr(); const LDefinition *out = ins->output(); Operand srcAddr(HeapReg); @@ -314,7 +314,7 @@ void CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) { MAsmJSStoreHeap *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); const LAllocation *ptr = ins->ptr(); Operand dstAddr(HeapReg); @@ -384,7 +384,7 @@ void CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins) { MAsmJSCompareExchangeHeap *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); const LAllocation *ptr = ins->ptr(); MOZ_ASSERT(ptr->isRegister()); @@ -414,14 +414,14 @@ CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins) uint32_t after = masm.size(); if (rejoin.used()) masm.bind(&rejoin); - masm.append(AsmJSHeapAccess(after, after, mir->viewType(), maybeCmpOffset)); + masm.append(AsmJSHeapAccess(after, after, mir->accessType(), maybeCmpOffset)); } void CodeGeneratorX64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins) { MAsmJSAtomicBinopHeap *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); const LAllocation *ptr = ins->ptr(); Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp()); const LAllocation* value = ins->value(); @@ -460,7 +460,7 @@ CodeGeneratorX64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins) uint32_t after = masm.size(); if (rejoin.used()) masm.bind(&rejoin); - masm.append(AsmJSHeapAccess(after, after, mir->viewType(), maybeCmpOffset)); + masm.append(AsmJSHeapAccess(after, after, mir->accessType(), maybeCmpOffset)); } void diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp index 778014447d58..4145c0c88852 100644 --- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -172,7 +172,7 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) : useRegisterOrNonNegativeConstantAtStart(ptr); LAsmJSStoreHeap *lir = nullptr; // initialize to silence GCC warning - switch (ins->viewType()) { + switch (ins->accessType()) { case Scalar::Int8: case Scalar::Uint8: case Scalar::Int16: diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 9b93da9c6aaf..1334bdfca0a1 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -290,7 +290,7 @@ void CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins) { const MLoadTypedArrayElementStatic *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); MOZ_ASSERT_IF(vt == Scalar::Float32, mir->type() == MIRType_Float32); Register ptr = ToRegister(ins->ptr()); @@ -358,7 +358,7 @@ void CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) { const MAsmJSLoadHeap *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); const LAllocation *ptr = ins->ptr(); const LDefinition *out = ins->output(); @@ -438,7 +438,7 @@ void CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins) { MStoreTypedArrayElementStatic *mir = ins->mir(); - Scalar::Type vt = Scalar::Type(mir->viewType()); + Scalar::Type vt = mir->accessType(); Register ptr = ToRegister(ins->ptr()); const LAllocation *value = ins->value(); uint32_t offset = mir->offset(); @@ -463,7 +463,7 @@ void CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) { MAsmJSStoreHeap *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); const LAllocation *value = ins->value(); const LAllocation *ptr = ins->ptr(); @@ -509,7 +509,7 @@ void CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins) { MAsmJSCompareExchangeHeap *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); const LAllocation *ptr = ins->ptr(); Register oldval = ToRegister(ins->oldValue()); Register newval = ToRegister(ins->newValue()); @@ -537,7 +537,7 @@ CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins) uint32_t before = masm.size(); masm.addlWithPatch(Imm32(0), ptrReg); uint32_t after = masm.size(); - masm.append(AsmJSHeapAccess(before, after, mir->viewType(), maybeCmpOffset)); + masm.append(AsmJSHeapAccess(before, after, mir->accessType(), maybeCmpOffset)); Address memAddr(ToRegister(ptr), 0); masm.compareExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt, @@ -554,7 +554,7 @@ void CodeGeneratorX86::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins) { MAsmJSAtomicBinopHeap *mir = ins->mir(); - Scalar::Type vt = mir->viewType(); + Scalar::Type vt = mir->accessType(); const LAllocation *ptr = ins->ptr(); Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp()); const LAllocation* value = ins->value(); @@ -583,7 +583,7 @@ CodeGeneratorX86::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins) uint32_t before = masm.size(); masm.addlWithPatch(Imm32(0), ptrReg); uint32_t after = masm.size(); - masm.append(AsmJSHeapAccess(before, after, mir->viewType(), maybeCmpOffset)); + masm.append(AsmJSHeapAccess(before, after, mir->accessType(), maybeCmpOffset)); Address memAddr(ptrReg, 0); if (value->isConstant()) { diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index a304dae81435..057a896d8c01 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -218,7 +218,7 @@ LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) if (ptr->isConstant() && !ins->needsBoundsCheck()) { MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0); LAllocation ptrAlloc = LAllocation(ptr->toConstant()->vp()); - switch (ins->viewType()) { + switch (ins->accessType()) { case Scalar::Int8: case Scalar::Uint8: // See comment below. lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useFixed(ins->value(), eax)); @@ -238,7 +238,7 @@ LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) return; } - switch (ins->viewType()) { + switch (ins->accessType()) { case Scalar::Int8: case Scalar::Uint8: // See comment for LIRGeneratorX86::useByteOpRegister. lir = new(alloc()) LAsmJSStoreHeap(useRegister(ins->ptr()), useFixed(ins->value(), eax)); @@ -265,7 +265,7 @@ LIRGeneratorX86::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic // The code generated for StoreTypedArrayElementStatic is identical to that // for AsmJSStoreHeap, and the same concerns apply. LStoreTypedArrayElementStatic *lir; - switch (ins->viewType()) { + switch (ins->accessType()) { case Scalar::Int8: case Scalar::Uint8: case Scalar::Uint8Clamped: lir = new(alloc()) LStoreTypedArrayElementStatic(useRegister(ins->ptr()), From 26252ed8badcb0630c4df1d12b4b524ce14327d1 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 28 Jan 2015 13:30:32 +0100 Subject: [PATCH 08/39] Bug 1113338: Generalize AsmJS{Load,Store}Heap to handle partial loads; r=luke --HG-- extra : rebase_source : 14f1292bcaef32933e4a13136165973657cabeec --- js/src/asmjs/AsmJSValidate.cpp | 57 ++++++++++++++++++++++++++-------- js/src/jit/IonTypes.h | 24 ++++++++++++++ js/src/jit/MIR.h | 30 ++++++++++++------ 3 files changed, 88 insertions(+), 23 deletions(-) diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index 6d6a68d01219..6541d5441dcc 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -2765,9 +2765,23 @@ class FunctionCompiler return nullptr; bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); - Label *outOfBoundsLabel = Scalar::isSimdType(accessType) ? &m().onOutOfBoundsLabel() : nullptr; + MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD loads should use loadSimdHeap"); + MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck); + curBlock_->add(load); + return load; + } + + MDefinition *loadSimdHeap(Scalar::Type accessType, MDefinition *ptr, NeedsBoundsCheck chk, + unsigned numElems) + { + if (inDeadCode()) + return nullptr; + + bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); + MOZ_ASSERT(Scalar::isSimdType(accessType), "loadSimdHeap can only load from a SIMD view"); + Label *outOfBoundsLabel = &m().onOutOfBoundsLabel(); MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck, - outOfBoundsLabel); + outOfBoundsLabel, numElems); curBlock_->add(load); return load; } @@ -2778,9 +2792,22 @@ class FunctionCompiler return; bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); - Label *outOfBoundsLabel = Scalar::isSimdType(accessType) ? &m().onOutOfBoundsLabel() : nullptr; + MOZ_ASSERT(!Scalar::isSimdType(accessType), "SIMD stores should use loadSimdHeap"); + MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck); + curBlock_->add(store); + } + + void storeSimdHeap(Scalar::Type accessType, MDefinition *ptr, MDefinition *v, + NeedsBoundsCheck chk, unsigned numElems) + { + if (inDeadCode()) + return; + + bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); + MOZ_ASSERT(Scalar::isSimdType(accessType), "storeSimdHeap can only load from a SIMD view"); + Label *outOfBoundsLabel = &m().onOutOfBoundsLabel(); MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck, - outOfBoundsLabel); + outOfBoundsLabel, numElems); curBlock_->add(store); } @@ -2800,6 +2827,7 @@ class FunctionCompiler bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), accessType, ptr, needsBoundsCheck, /* outOfBoundsLabel = */ nullptr, + /* numElems */ 0, MembarBeforeLoad, MembarAfterLoad); curBlock_->add(load); return load; @@ -2813,6 +2841,7 @@ class FunctionCompiler bool needsBoundsCheck = chk == NEEDS_BOUNDS_CHECK && !m().usesSignalHandlersForOOB(); MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), accessType, ptr, v, needsBoundsCheck, /* outOfBoundsLabel = */ nullptr, + /* numElems = */ 0, MembarBeforeStore, MembarAfterStore); curBlock_->add(store); } @@ -5591,7 +5620,7 @@ CheckSimdShuffle(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDe static bool CheckSimdLoadStoreArgs(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, - Scalar::Type *viewType, MDefinition **index, + unsigned numElems, Scalar::Type *viewType, MDefinition **index, NeedsBoundsCheck *needsBoundsCheck) { ParseNode *view = CallArgList(call); @@ -5644,7 +5673,8 @@ CheckSimdLoadStoreArgs(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opTyp } static bool -CheckSimdLoad(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefinition **def, Type *type) +CheckSimdLoad(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, + unsigned numElems, MDefinition **def, Type *type) { unsigned numArgs = CallArgListLength(call); if (numArgs != 2) @@ -5653,16 +5683,17 @@ CheckSimdLoad(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefin Scalar::Type viewType; MDefinition *index; NeedsBoundsCheck needsBoundsCheck; - if (!CheckSimdLoadStoreArgs(f, call, opType, &viewType, &index, &needsBoundsCheck)) + if (!CheckSimdLoadStoreArgs(f, call, opType, numElems, &viewType, &index, &needsBoundsCheck)) return false; - *def = f.loadHeap(viewType, index, needsBoundsCheck); + *def = f.loadSimdHeap(viewType, index, needsBoundsCheck, numElems); *type = opType; return true; } static bool -CheckSimdStore(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefinition **def, Type *type) +CheckSimdStore(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, + unsigned numElems, MDefinition **def, Type *type) { unsigned numArgs = CallArgListLength(call); if (numArgs != 3) @@ -5671,7 +5702,7 @@ CheckSimdStore(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefi Scalar::Type viewType; MDefinition *index; NeedsBoundsCheck needsBoundsCheck; - if (!CheckSimdLoadStoreArgs(f, call, opType, &viewType, &index, &needsBoundsCheck)) + if (!CheckSimdLoadStoreArgs(f, call, opType, numElems, &viewType, &index, &needsBoundsCheck)) return false; Type retType = opType; @@ -5683,7 +5714,7 @@ CheckSimdStore(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefi if (!(vecType <= retType)) return f.failf(vecExpr, "%s is not a subtype of %s", vecType.toChars(), retType.toChars()); - f.storeHeap(viewType, index, vec, needsBoundsCheck); + f.storeSimdHeap(viewType, index, vec, needsBoundsCheck, numElems); *def = vec; *type = vecType; return true; @@ -5791,9 +5822,9 @@ CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompile return CheckSimdShuffle(f, call, opType, def, type); case AsmJSSimdOperation_load: - return CheckSimdLoad(f, call, opType, def, type); + return CheckSimdLoad(f, call, opType, 4, def, type); case AsmJSSimdOperation_store: - return CheckSimdStore(f, call, opType, def, type); + return CheckSimdStore(f, call, opType, 4, def, type); case AsmJSSimdOperation_bitselect: return CheckSimdSelect(f, call, opType, /*isElementWise */ false, def, type); diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index 5aaaa819d942..1b910be7969b 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -9,6 +9,7 @@ #include "mozilla/HashFunctions.h" +#include "jsfriendapi.h" #include "jstypes.h" #include "js/Value.h" @@ -552,6 +553,29 @@ SimdTypeToLength(MIRType type) return 1 << ((type >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK); } +static inline unsigned +ScalarTypeToLength(Scalar::Type type) +{ + switch (type) { + case Scalar::Int8: + case Scalar::Uint8: + case Scalar::Int16: + case Scalar::Uint16: + case Scalar::Int32: + case Scalar::Uint32: + case Scalar::Float32: + case Scalar::Float64: + case Scalar::Uint8Clamped: + return 1; + case Scalar::Float32x4: + case Scalar::Int32x4: + return 4; + case Scalar::MaxTypedArrayViewType: + break; + } + MOZ_CRASH("unexpected SIMD kind"); +} + static inline MIRType SimdTypeToScalarType(MIRType type) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 4888ff67f8b7..0f4385378343 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -12150,16 +12150,22 @@ class MAsmJSHeapAccess Scalar::Type accessType_; bool needsBoundsCheck_; Label *outOfBoundsLabel_; + unsigned numSimdElems_; public: - MAsmJSHeapAccess(Scalar::Type accessType, bool needsBoundsCheck, Label *outOfBoundsLabel = nullptr) - : accessType_(accessType), needsBoundsCheck_(needsBoundsCheck), outOfBoundsLabel_(outOfBoundsLabel) - {} + MAsmJSHeapAccess(Scalar::Type accessType, bool needsBoundsCheck, + Label *outOfBoundsLabel = nullptr, unsigned numSimdElems = 0) + : accessType_(accessType), needsBoundsCheck_(needsBoundsCheck), + outOfBoundsLabel_(outOfBoundsLabel), numSimdElems_(numSimdElems) + { + MOZ_ASSERT(numSimdElems <= ScalarTypeToLength(accessType)); + } Scalar::Type accessType() const { return accessType_; } bool needsBoundsCheck() const { return needsBoundsCheck_; } void removeBoundsCheck() { needsBoundsCheck_ = false; } Label *outOfBoundsLabel() const { return outOfBoundsLabel_; } + unsigned numSimdElems() const { return numSimdElems_; } }; class MAsmJSLoadHeap @@ -12171,9 +12177,10 @@ class MAsmJSLoadHeap MemoryBarrierBits barrierAfter_; MAsmJSLoadHeap(Scalar::Type accessType, MDefinition *ptr, bool needsBoundsCheck, - Label *outOfBoundsLabel, MemoryBarrierBits before, MemoryBarrierBits after) + Label *outOfBoundsLabel, unsigned numSimdElems, + MemoryBarrierBits before, MemoryBarrierBits after) : MUnaryInstruction(ptr), - MAsmJSHeapAccess(accessType, needsBoundsCheck, outOfBoundsLabel), + MAsmJSHeapAccess(accessType, needsBoundsCheck, outOfBoundsLabel, numSimdElems), barrierBefore_(before), barrierAfter_(after) { @@ -12205,7 +12212,7 @@ class MAsmJSLoadHeap break; case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: - MOZ_CRASH("unexpected uint8clamped load heap in asm.js"); + MOZ_CRASH("unexpected load heap in asm.js"); } } @@ -12215,11 +12222,12 @@ class MAsmJSLoadHeap static MAsmJSLoadHeap *New(TempAllocator &alloc, Scalar::Type accessType, MDefinition *ptr, bool needsBoundsCheck, Label *outOfBoundsLabel = nullptr, + unsigned numSimdElems = 0, MemoryBarrierBits barrierBefore = MembarNobits, MemoryBarrierBits barrierAfter = MembarNobits) { return new(alloc) MAsmJSLoadHeap(accessType, ptr, needsBoundsCheck, outOfBoundsLabel, - barrierBefore, barrierAfter); + numSimdElems, barrierBefore, barrierAfter); } MDefinition *ptr() const { return getOperand(0); } @@ -12242,9 +12250,10 @@ class MAsmJSStoreHeap MemoryBarrierBits barrierAfter_; MAsmJSStoreHeap(Scalar::Type accessType, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck, - Label *outOfBoundsLabel, MemoryBarrierBits before, MemoryBarrierBits after) + Label *outOfBoundsLabel, unsigned numSimdElems, + MemoryBarrierBits before, MemoryBarrierBits after) : MBinaryInstruction(ptr, v), - MAsmJSHeapAccess(accessType, needsBoundsCheck, outOfBoundsLabel), + MAsmJSHeapAccess(accessType, needsBoundsCheck, outOfBoundsLabel, numSimdElems), barrierBefore_(before), barrierAfter_(after) { @@ -12258,11 +12267,12 @@ class MAsmJSStoreHeap static MAsmJSStoreHeap *New(TempAllocator &alloc, Scalar::Type accessType, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck, Label *outOfBoundsLabel = nullptr, + unsigned numSimdElems = 0, MemoryBarrierBits barrierBefore = MembarNobits, MemoryBarrierBits barrierAfter = MembarNobits) { return new(alloc) MAsmJSStoreHeap(accessType, ptr, v, needsBoundsCheck, outOfBoundsLabel, - barrierBefore, barrierAfter); + numSimdElems, barrierBefore, barrierAfter); } MDefinition *ptr() const { return getOperand(0); } From 841e931d9ab8bf387642f5d1c5568db5028f99fb Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 5 Feb 2015 16:35:32 +0100 Subject: [PATCH 09/39] Bug 1113338: Add SIMD partial loads/stores in asm.js; r=sunfish,luke --HG-- extra : rebase_source : 64c55de279c05492afa5f8b1599aeae44a054068 --- js/src/asmjs/AsmJSModule.cpp | 14 +- js/src/asmjs/AsmJSSignalHandlers.cpp | 8 +- js/src/asmjs/AsmJSValidate.cpp | 12 + js/src/builtin/SIMD.h | 8 +- .../tests/asm.js/testSIMD-load-store.js | 262 ++++++++++++++++- js/src/jit/MIR.h | 2 +- js/src/jit/shared/Assembler-shared.h | 30 +- js/src/jit/shared/Assembler-x86-shared.h | 26 ++ js/src/jit/shared/BaseAssembler-x86-shared.h | 269 ++++++++++++----- js/src/jit/shared/Encoding-x86-shared.h | 2 + js/src/jit/x64/Assembler-x64.h | 30 ++ js/src/jit/x64/CodeGenerator-x64.cpp | 227 +++++++++++++- js/src/jit/x64/CodeGenerator-x64.h | 4 + js/src/jit/x64/Lowering-x64.cpp | 2 +- js/src/jit/x86/Assembler-x86.h | 20 ++ js/src/jit/x86/CodeGenerator-x86.cpp | 276 ++++++++++++++++-- js/src/jit/x86/CodeGenerator-x86.h | 11 + js/src/jsfriendapi.h | 21 ++ 18 files changed, 1086 insertions(+), 138 deletions(-) diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index 022d8d1d78e3..da05269127ba 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -774,6 +774,16 @@ AsmJSModule::staticallyLink(ExclusiveContext *cx) MOZ_ASSERT(isStaticallyLinked()); } +#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) +static size_t +ByteSizeOfHeapAccess(const jit::AsmJSHeapAccess access) +{ + Scalar::Type type = access.type(); + if (Scalar::isSimdType(type)) + return Scalar::scalarByteSize(type) * access.numSimdElems(); + return TypedArrayElemSize(type); +} +#endif void AsmJSModule::initHeap(Handle heap, JSContext *cx) { @@ -794,7 +804,7 @@ AsmJSModule::initHeap(Handle heap, JSContext *cx // ptr + data-type-byte-size > heapLength // i.e. ptr >= heapLength + 1 - data-type-byte-size // (Note that we need >= as this is what codegen uses.) - size_t scalarByteSize = TypedArrayElemSize(access.type()); + size_t scalarByteSize = ByteSizeOfHeapAccess(access); X86Encoding::SetPointer(access.patchLengthAt(code_), (void*)(heap->byteLength() + 1 - scalarByteSize)); } @@ -816,7 +826,7 @@ AsmJSModule::initHeap(Handle heap, JSContext *cx const jit::AsmJSHeapAccess &access = heapAccesses_[i]; if (access.hasLengthCheck()) { // See comment above for x86 codegen. - size_t scalarByteSize = TypedArrayElemSize(access.type()); + size_t scalarByteSize = ByteSizeOfHeapAccess(access); X86Encoding::SetInt32(access.patchLengthAt(code_), heapLength + 1 - scalarByteSize); } } diff --git a/js/src/asmjs/AsmJSSignalHandlers.cpp b/js/src/asmjs/AsmJSSignalHandlers.cpp index ebb38b60b039..570d9051676f 100644 --- a/js/src/asmjs/AsmJSSignalHandlers.cpp +++ b/js/src/asmjs/AsmJSSignalHandlers.cpp @@ -484,10 +484,6 @@ HandleFault(PEXCEPTION_POINTERS exception) if (!heapAccess) return false; - // Also not necessary, but, since we can, do. - if (heapAccess->isLoad() != !record->ExceptionInformation[0]) - return false; - // We now know that this is an out-of-bounds access made by an asm.js // load/store that we should handle. @@ -497,6 +493,10 @@ HandleFault(PEXCEPTION_POINTERS exception) return true; } + // Also not necessary, but, since we can, do. + if (heapAccess->isLoad() != !record->ExceptionInformation[0]) + return false; + // If this is a load, assign the JS-defined result value to the destination // register (ToInt32(undefined) or ToNumber(undefined), determined by the // type of the destination register) and set the PC to the next op. Upon diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index 6541d5441dcc..d29b8b1a723b 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -5823,8 +5823,20 @@ CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompile case AsmJSSimdOperation_load: return CheckSimdLoad(f, call, opType, 4, def, type); + case AsmJSSimdOperation_loadX: + return CheckSimdLoad(f, call, opType, 1, def, type); + case AsmJSSimdOperation_loadXY: + return CheckSimdLoad(f, call, opType, 2, def, type); + case AsmJSSimdOperation_loadXYZ: + return CheckSimdLoad(f, call, opType, 3, def, type); case AsmJSSimdOperation_store: return CheckSimdStore(f, call, opType, 4, def, type); + case AsmJSSimdOperation_storeX: + return CheckSimdStore(f, call, opType, 1, def, type); + case AsmJSSimdOperation_storeXY: + return CheckSimdStore(f, call, opType, 2, def, type); + case AsmJSSimdOperation_storeXYZ: + return CheckSimdStore(f, call, opType, 3, def, type); case AsmJSSimdOperation_bitselect: return CheckSimdSelect(f, call, opType, /*isElementWise */ false, def, type); diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index dc62df8940e7..4439f950a600 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -227,7 +227,13 @@ _(not) \ _(neg) \ _(load) \ - _(store) + _(loadX) \ + _(loadXY) \ + _(loadXYZ) \ + _(store) \ + _(storeX) \ + _(storeXY) \ + _(storeXYZ) #define FORALL_SIMD_OP(_) \ FOREACH_INT32X4_SIMD_OP(_) \ FOREACH_FLOAT32X4_SIMD_OP(_) \ diff --git a/js/src/jit-test/tests/asm.js/testSIMD-load-store.js b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js index 5a7ae98f0da9..1c97790327f6 100644 --- a/js/src/jit-test/tests/asm.js/testSIMD-load-store.js +++ b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js @@ -17,10 +17,15 @@ function assertEqX4(real, expected, assertFunc) { if (typeof assertFunc === 'undefined') assertFunc = assertEq; - assertFunc(real.x, expected[0]); - assertFunc(real.y, expected[1]); - assertFunc(real.z, expected[2]); - assertFunc(real.w, expected[3]); + try { + assertFunc(real.x, expected[0]); + assertFunc(real.y, expected[1]); + assertFunc(real.z, expected[2]); + assertFunc(real.w, expected[3]); + } catch (e) { + print("Stack: " + e.stack); + throw e; + } } // Load / Store @@ -46,12 +51,14 @@ assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return // Literal index constants var buf = new ArrayBuffer(BUF_MIN); +var SIZE_TA = BUF_MIN >> 2 var asI32 = new Int32Array(buf); -asI32[(BUF_MIN >> 2) - 4] = 4; -asI32[(BUF_MIN >> 2) - 3] = 3; -asI32[(BUF_MIN >> 2) - 2] = 2; -asI32[(BUF_MIN >> 2) - 1] = 1; +asI32[SIZE_TA - 4] = 4; +asI32[SIZE_TA - 3] = 3; +asI32[SIZE_TA - 2] = 2; +asI32[SIZE_TA - 1] = 1; +assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, -1);} return f"); assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1) + ");} return f"); assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 15) + ");} return f"); asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 16) + ");} return f"); @@ -259,3 +266,242 @@ assertThrowsInstanceOf(() => i32s(SIZE - 0, vec), RangeError); for (var i = 0; i < SIZE; i++) assertEq(I32[i], i + 1); +// Partial loads and stores +(function() { + +// Variable indexes +function MakeCodeFor(typeName) { + return ` + "use asm"; + var type = glob.SIMD.${typeName}; + + var lx = type.loadX; + var lxy = type.loadXY; + var lxyz = type.loadXYZ; + + var sx = type.storeX; + var sxy = type.storeXY; + var sxyz = type.storeXYZ; + + var u8 = new glob.Uint8Array(heap); + + function loadX(i) { i=i|0; return lx(u8, i); } + function loadXY(i) { i=i|0; return lxy(u8, i); } + function loadXYZ(i) { i=i|0; return lxyz(u8, i); } + + function loadCstX() { return lx(u8, 41 << 2); } + function loadCstXY() { return lxy(u8, 41 << 2); } + function loadCstXYZ() { return lxyz(u8, 41 << 2); } + + function storeX(i, x) { i=i|0; x=type(x); return sx(u8, i, x); } + function storeXY(i, x) { i=i|0; x=type(x); return sxy(u8, i, x); } + function storeXYZ(i, x) { i=i|0; x=type(x); return sxyz(u8, i, x); } + + function storeCstX(x) { x=type(x); return sx(u8, 41 << 2, x); } + function storeCstXY(x) { x=type(x); return sxy(u8, 41 << 2, x); } + function storeCstXYZ(x) { x=type(x); return sxyz(u8, 41 << 2, x); } + + return { + loadX: loadX, + loadXY: loadXY, + loadXYZ: loadXYZ, + loadCstX: loadCstX, + loadCstXY: loadCstXY, + loadCstXYZ: loadCstXYZ, + storeX: storeX, + storeXY: storeXY, + storeXYZ: storeXYZ, + storeCstX: storeCstX, + storeCstXY: storeCstXY, + storeCstXYZ: storeCstXYZ, + } +`; +} + +var SIZE = 0x10000; + +function TestPartialLoads(m, typedArray, x, y, z, w) { + // Fill array with predictable values + for (var i = 0; i < SIZE; i += 4) { + typedArray[i] = x(i); + typedArray[i + 1] = y(i); + typedArray[i + 2] = z(i); + typedArray[i + 3] = w(i); + } + + // Test correct loads + var i = 0, j = 0; // i in elems, j in bytes + assertEqX4(m.loadX(j), [x(i), 0, 0, 0]); + assertEqX4(m.loadXY(j), [x(i), y(i), 0, 0]); + assertEqX4(m.loadXYZ(j), [x(i), y(i), z(i), 0]); + + j += 4; + assertEqX4(m.loadX(j), [y(i), 0, 0, 0]); + assertEqX4(m.loadXY(j), [y(i), z(i), 0, 0]); + assertEqX4(m.loadXYZ(j), [y(i), z(i), w(i), 0]); + + j += 4; + assertEqX4(m.loadX(j), [z(i), 0, 0, 0]); + assertEqX4(m.loadXY(j), [z(i), w(i), 0, 0]); + assertEqX4(m.loadXYZ(j), [z(i), w(i), x(i+4), 0]); + + j += 4; + assertEqX4(m.loadX(j), [w(i), 0, 0, 0]); + assertEqX4(m.loadXY(j), [w(i), x(i+4), 0, 0]); + assertEqX4(m.loadXYZ(j), [w(i), x(i+4), y(i+4), 0]); + + j += 4; + i += 4; + assertEqX4(m.loadX(j), [x(i), 0, 0, 0]); + assertEqX4(m.loadXY(j), [x(i), y(i), 0, 0]); + assertEqX4(m.loadXYZ(j), [x(i), y(i), z(i), 0]); + + // Test loads with constant indexes (41) + assertEqX4(m.loadCstX(), [y(40), 0, 0, 0]); + assertEqX4(m.loadCstXY(), [y(40), z(40), 0, 0]); + assertEqX4(m.loadCstXYZ(), [y(40), z(40), w(40), 0]); + + // Test limit and OOB accesses + assertEqX4(m.loadX((SIZE - 1) << 2), [w(SIZE - 4), 0, 0, 0]); + assertThrowsInstanceOf(() => m.loadX(((SIZE - 1) << 2) + 1), RangeError); + + assertEqX4(m.loadXY((SIZE - 2) << 2), [z(SIZE - 4), w(SIZE - 4), 0, 0]); + assertThrowsInstanceOf(() => m.loadXY(((SIZE - 2) << 2) + 1), RangeError); + + assertEqX4(m.loadXYZ((SIZE - 3) << 2), [y(SIZE - 4), z(SIZE - 4), w(SIZE - 4), 0]); + assertThrowsInstanceOf(() => m.loadXYZ(((SIZE - 3) << 2) + 1), RangeError); +} + +// Partial stores +function TestPartialStores(m, typedArray, typeName, x, y, z, w) { + var val = SIMD[typeName](x, y, z, w); + + function Reset() { + for (var i = 0; i < SIZE; i++) + typedArray[i] = i + 1; + } + function CheckNotModified(low, high) { + for (var i = low; i < high; i++) + assertEq(typedArray[i], i + 1); + } + + function TestStoreX(i) { + m.storeX(i, val); + CheckNotModified(0, i >> 2); + assertEq(typedArray[i >> 2], x); + CheckNotModified((i >> 2) + 1, SIZE); + typedArray[i >> 2] = (i >> 2) + 1; + } + + function TestStoreXY(i) { + m.storeXY(i, val); + CheckNotModified(0, i >> 2); + assertEq(typedArray[i >> 2], x); + assertEq(typedArray[(i >> 2) + 1], y); + CheckNotModified((i >> 2) + 2, SIZE); + typedArray[i >> 2] = (i >> 2) + 1; + typedArray[(i >> 2) + 1] = (i >> 2) + 2; + } + + function TestStoreXYZ(i) { + m.storeXYZ(i, val); + CheckNotModified(0, i >> 2); + assertEq(typedArray[i >> 2], x); + assertEq(typedArray[(i >> 2) + 1], y); + assertEq(typedArray[(i >> 2) + 2], z); + CheckNotModified((i >> 2) + 3, SIZE); + typedArray[i >> 2] = (i >> 2) + 1; + typedArray[(i >> 2) + 1] = (i >> 2) + 2; + typedArray[(i >> 2) + 2] = (i >> 2) + 3; + } + + function TestOOBStore(f) { + assertThrowsInstanceOf(f, RangeError); + CheckNotModified(0, SIZE); + } + + Reset(); + + TestStoreX(0); + TestStoreX(1 << 2); + TestStoreX(2 << 2); + TestStoreX(3 << 2); + TestStoreX(1337 << 2); + + var i = (SIZE - 1) << 2; + TestStoreX(i); + TestOOBStore(() => m.storeX(i + 1, val)); + TestOOBStore(() => m.storeX(-1, val)); + + TestStoreXY(0); + TestStoreXY(1 << 2); + TestStoreXY(2 << 2); + TestStoreXY(3 << 2); + TestStoreXY(1337 << 2); + + var i = (SIZE - 2) << 2; + TestStoreXY(i); + TestOOBStore(() => m.storeXY(i + 1, val)); + TestOOBStore(() => m.storeXY(-1, val)); + + TestStoreXYZ(0); + TestStoreXYZ(1 << 2); + TestStoreXYZ(2 << 2); + TestStoreXYZ(3 << 2); + TestStoreXYZ(1337 << 2); + + var i = (SIZE - 3) << 2; + TestStoreXYZ(i); + TestOOBStore(() => m.storeXYZ(i + 1, val)); + TestOOBStore(() => m.storeXYZ(-1, val)); + TestOOBStore(() => m.storeXYZ(-9, val)); + + // Constant indexes (41) + m.storeCstX(val); + CheckNotModified(0, 41); + assertEq(typedArray[41], x); + CheckNotModified(42, SIZE); + typedArray[41] = 42; + + m.storeCstXY(val); + CheckNotModified(0, 41); + assertEq(typedArray[41], x); + assertEq(typedArray[42], y); + CheckNotModified(43, SIZE); + typedArray[41] = 42; + typedArray[42] = 43; + + m.storeCstXYZ(val); + CheckNotModified(0, 41); + assertEq(typedArray[41], x); + assertEq(typedArray[42], y); + assertEq(typedArray[43], z); + CheckNotModified(44, SIZE); + typedArray[41] = 42; + typedArray[42] = 43; + typedArray[43] = 44; +} + +var f32 = new Float32Array(SIZE); +var mfloat32x4 = asmLink(asmCompile('glob', 'ffi', 'heap', MakeCodeFor('float32x4')), this, null, f32.buffer); + +TestPartialLoads(mfloat32x4, f32, + (i) => i + 1, + (i) => Math.fround(13.37), + (i) => Math.fround(1/i), + (i) => Math.fround(Math.sqrt(0x2000 - i))); + +TestPartialStores(mfloat32x4, f32, 'float32x4', 42, -0, NaN, 0.1337); + +var i32 = new Int32Array(f32.buffer); +var mint32x4 = asmLink(asmCompile('glob', 'ffi', 'heap', MakeCodeFor('int32x4')), this, null, i32.buffer); + +TestPartialLoads(mint32x4, i32, + (i) => i + 1 | 0, + (i) => -i | 0, + (i) => i * 2 | 0, + (i) => 42); + +TestPartialStores(mint32x4, i32, 'int32x4', 42, -3, 13, 37); + +})(); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 0f4385378343..921d78c18955 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -12165,7 +12165,7 @@ class MAsmJSHeapAccess bool needsBoundsCheck() const { return needsBoundsCheck_; } void removeBoundsCheck() { needsBoundsCheck_ = false; } Label *outOfBoundsLabel() const { return outOfBoundsLabel_; } - unsigned numSimdElems() const { return numSimdElems_; } + unsigned numSimdElems() const { MOZ_ASSERT(Scalar::isSimdType(accessType_)); return numSimdElems_; } }; class MAsmJSLoadHeap diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index 661080da3297..f9ec8f3f272e 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -769,6 +769,7 @@ class AsmJSHeapAccess #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) uint8_t cmpDelta_; // the number of bytes from the cmp to the load/store instruction uint8_t opLength_; // the length of the load/store instruction + uint8_t numSimdElems_; // the number of SIMD lanes to load/store at once Scalar::Type type_; AnyRegister::Code loadedReg_ : 8; #endif @@ -787,16 +788,34 @@ class AsmJSHeapAccess : offset_(offset), cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp), opLength_(after - offset), + numSimdElems_(UINT8_MAX), type_(type), loadedReg_(loadedReg.code()) - {} + { + MOZ_ASSERT(!Scalar::isSimdType(type)); + } AsmJSHeapAccess(uint32_t offset, uint8_t after, Scalar::Type type, uint32_t cmp = NoLengthCheck) : offset_(offset), cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp), opLength_(after - offset), + numSimdElems_(UINT8_MAX), type_(type), loadedReg_(UINT8_MAX) - {} + { + MOZ_ASSERT(!Scalar::isSimdType(type)); + } + // SIMD loads / stores + AsmJSHeapAccess(uint32_t offset, uint32_t after, unsigned numSimdElems, Scalar::Type type, + uint32_t cmp = NoLengthCheck) + : offset_(offset), + cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp), + opLength_(after - offset), + numSimdElems_(numSimdElems), + type_(type), + loadedReg_(UINT8_MAX) + { + MOZ_ASSERT(Scalar::isSimdType(type)); + } #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) explicit AsmJSHeapAccess(uint32_t offset) : offset_(offset) @@ -808,11 +827,14 @@ class AsmJSHeapAccess #if defined(JS_CODEGEN_X86) void *patchOffsetAt(uint8_t *code) const { return code + (offset_ + opLength_); } #endif +#if defined(JS_CODEGEN_X64) + unsigned opLength() const { MOZ_ASSERT(!Scalar::isSimdType(type_)); return opLength_; } + bool isLoad() const { MOZ_ASSERT(!Scalar::isSimdType(type_)); return loadedReg_ != UINT8_MAX; } +#endif #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) bool hasLengthCheck() const { return cmpDelta_ > 0; } void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); } - unsigned opLength() const { return opLength_; } - bool isLoad() const { return loadedReg_ != UINT8_MAX; } + unsigned numSimdElems() const { MOZ_ASSERT(Scalar::isSimdType(type_)); return numSimdElems_; } Scalar::Type type() const { return type_; } AnyRegister loadedReg() const { return AnyRegister::FromCode(loadedReg_); } #endif diff --git a/js/src/jit/shared/Assembler-x86-shared.h b/js/src/jit/shared/Assembler-x86-shared.h index 5ecc591827d5..aa8a08745f55 100644 --- a/js/src/jit/shared/Assembler-x86-shared.h +++ b/js/src/jit/shared/Assembler-x86-shared.h @@ -1767,6 +1767,32 @@ class AssemblerX86Shared : public AssemblerShared MOZ_ASSERT(HasSSE2()); masm.vmovd_rr(src.code(), dest.code()); } + void vmovd(const Operand &src, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + switch (src.kind()) { + case Operand::MEM_REG_DISP: + masm.vmovd_mr(src.disp(), src.base(), dest.code()); + break; + case Operand::MEM_SCALE: + masm.vmovd_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } + void vmovd(FloatRegister src, const Operand &dest) { + MOZ_ASSERT(HasSSE2()); + switch (dest.kind()) { + case Operand::MEM_REG_DISP: + masm.vmovd_rm(src.code(), dest.disp(), dest.base()); + break; + case Operand::MEM_SCALE: + masm.vmovd_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } void vpaddd(const Operand &src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); switch (src1.kind()) { diff --git a/js/src/jit/shared/BaseAssembler-x86-shared.h b/js/src/jit/shared/BaseAssembler-x86-shared.h index 2bcd89ca293a..2ecdb72c04f4 100644 --- a/js/src/jit/shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/shared/BaseAssembler-x86-shared.h @@ -127,7 +127,7 @@ public: // Arithmetic operations: #ifdef JS_CODEGEN_X86 - void adcl_im(int32_t imm, const void* addr) + void adcl_im(int32_t imm, const void *addr) { spew("adcl %d, %p", imm, addr); if (CAN_SIGN_EXTEND_8_32(imm)) { @@ -208,7 +208,7 @@ public: m_formatter.oneByteOp64(OP_ADD_GvEv, offset, base, dst); } - void addq_mr(const void* addr, RegisterID dst) + void addq_mr(const void *addr, RegisterID dst) { spew("addq %p, %s", addr, GPReg64Name(dst)); m_formatter.oneByteOp64(OP_ADD_GvEv, addr, dst); @@ -241,7 +241,7 @@ public: } } - void addq_im(int32_t imm, const void* addr) + void addq_im(int32_t imm, const void *addr) { spew("addq $%d, %p", imm, addr); if (CAN_SIGN_EXTEND_8_32(imm)) { @@ -253,7 +253,7 @@ public: } } #endif - void addl_im(int32_t imm, const void* addr) + void addl_im(int32_t imm, const void *addr) { spew("addl $%d, %p", imm, addr); if (CAN_SIGN_EXTEND_8_32(imm)) { @@ -301,7 +301,7 @@ public: { twoByteOpSimd("vpaddd", VEX_PD, OP2_PADDD_VdqWdq, offset, base, src0, dst); } - void vpaddd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vpaddd_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpaddd", VEX_PD, OP2_PADDD_VdqWdq, address, src0, dst); } @@ -314,7 +314,7 @@ public: { twoByteOpSimd("vpsubd", VEX_PD, OP2_PSUBD_VdqWdq, offset, base, src0, dst); } - void vpsubd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vpsubd_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpsubd", VEX_PD, OP2_PSUBD_VdqWdq, address, src0, dst); } @@ -336,7 +336,7 @@ public: { threeByteOpSimd("vpmulld", VEX_PD, OP3_PMULLD_VdqWdq, ESCAPE_38, offset, base, src0, dst); } - void vpmulld_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vpmulld_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { threeByteOpSimd("vpmulld", VEX_PD, OP3_PMULLD_VdqWdq, ESCAPE_38, address, src0, dst); } @@ -349,7 +349,7 @@ public: { twoByteOpSimd("vaddps", VEX_PS, OP2_ADDPS_VpsWps, offset, base, src0, dst); } - void vaddps_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vaddps_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vaddps", VEX_PS, OP2_ADDPS_VpsWps, address, src0, dst); } @@ -362,7 +362,7 @@ public: { twoByteOpSimd("vsubps", VEX_PS, OP2_SUBPS_VpsWps, offset, base, src0, dst); } - void vsubps_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vsubps_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vsubps", VEX_PS, OP2_SUBPS_VpsWps, address, src0, dst); } @@ -375,7 +375,7 @@ public: { twoByteOpSimd("vmulps", VEX_PS, OP2_MULPS_VpsWps, offset, base, src0, dst); } - void vmulps_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vmulps_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vmulps", VEX_PS, OP2_MULPS_VpsWps, address, src0, dst); } @@ -388,7 +388,7 @@ public: { twoByteOpSimd("vdivps", VEX_PS, OP2_DIVPS_VpsWps, offset, base, src0, dst); } - void vdivps_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vdivps_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vdivps", VEX_PS, OP2_DIVPS_VpsWps, address, src0, dst); } @@ -401,7 +401,7 @@ public: { twoByteOpSimd("vmaxps", VEX_PS, OP2_MAXPS_VpsWps, offset, base, src0, dst); } - void vmaxps_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vmaxps_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vmaxps", VEX_PS, OP2_MAXPS_VpsWps, address, src0, dst); } @@ -414,7 +414,7 @@ public: { twoByteOpSimd("vminps", VEX_PS, OP2_MINPS_VpsWps, offset, base, src0, dst); } - void vminps_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vminps_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vminps", VEX_PS, OP2_MINPS_VpsWps, address, src0, dst); } @@ -495,7 +495,7 @@ public: m_formatter.oneByteOp64(OP_OR_GvEv, offset, base, dst); } - void orq_mr(const void* addr, RegisterID dst) + void orq_mr(const void *addr, RegisterID dst) { spew("orq %p, %s", addr, GPReg64Name(dst)); m_formatter.oneByteOp64(OP_OR_GvEv, addr, dst); @@ -516,7 +516,7 @@ public: } } #else - void andl_im(int32_t imm, const void* addr) + void andl_im(int32_t imm, const void *addr) { spew("andl $0x%x, %p", imm, addr); if (CAN_SIGN_EXTEND_8_32(imm)) { @@ -658,7 +658,7 @@ public: m_formatter.oneByteOp64(OP_GROUP3_Ev, dst, GROUP3_OP_NOT); } #else - void orl_im(int32_t imm, const void* addr) + void orl_im(int32_t imm, const void *addr) { spew("orl $0x%x, %p", imm, addr); if (CAN_SIGN_EXTEND_8_32(imm)) { @@ -735,7 +735,7 @@ public: m_formatter.oneByteOp64(OP_SUB_GvEv, offset, base, dst); } - void subq_mr(const void* addr, RegisterID dst) + void subq_mr(const void *addr, RegisterID dst) { spew("subq %p, %s", addr, GPReg64Name(dst)); m_formatter.oneByteOp64(OP_SUB_GvEv, addr, dst); @@ -756,7 +756,7 @@ public: } } #else - void subl_im(int32_t imm, const void* addr) + void subl_im(int32_t imm, const void *addr) { spew("subl $%d, %p", imm, addr); if (CAN_SIGN_EXTEND_8_32(imm)) { @@ -1249,7 +1249,7 @@ public: m_formatter.immediate32(rhs); } } - void cmpq_im(int32_t rhs, const void* addr) + void cmpq_im(int32_t rhs, const void *addr) { spew("cmpq $0x%" PRIx64 ", %p", int64_t(rhs), addr); if (CAN_SIGN_EXTEND_8_32(rhs)) { @@ -1260,25 +1260,25 @@ public: m_formatter.immediate32(rhs); } } - void cmpq_rm(RegisterID rhs, const void* addr) + void cmpq_rm(RegisterID rhs, const void *addr) { spew("cmpq %s, %p", GPReg64Name(rhs), addr); m_formatter.oneByteOp64(OP_CMP_EvGv, addr, rhs); } #endif - void cmpl_rm(RegisterID rhs, const void* addr) + void cmpl_rm(RegisterID rhs, const void *addr) { spew("cmpl %s, %p", GPReg32Name(rhs), addr); m_formatter.oneByteOp(OP_CMP_EvGv, addr, rhs); } - void cmpl_rm_disp32(RegisterID rhs, const void* addr) + void cmpl_rm_disp32(RegisterID rhs, const void *addr) { spew("cmpl %s, %p", GPReg32Name(rhs), addr); m_formatter.oneByteOp_disp32(OP_CMP_EvGv, addr, rhs); } - void cmpl_im(int32_t rhs, const void* addr) + void cmpl_im(int32_t rhs, const void *addr) { spew("cmpl $0x%x, %p", rhs, addr); if (CAN_SIGN_EXTEND_8_32(rhs)) { @@ -1526,7 +1526,7 @@ public: m_formatter.oneByteOp(OP_MOV_EvGv, offset, base, index, scale, src); } - void movw_rm(RegisterID src, const void* addr) + void movw_rm(RegisterID src, const void *addr) { spew("movw %s, %p", GPReg16Name(src), addr); m_formatter.prefix(PRE_OPERAND_SIZE); @@ -1551,7 +1551,7 @@ public: m_formatter.oneByteOp(OP_MOV_EvGv, offset, base, index, scale, src); } - void movl_mEAX(const void* addr) + void movl_mEAX(const void *addr) { #ifdef JS_CODEGEN_X64 if (IsAddressImmediate(addr)) { @@ -1585,7 +1585,7 @@ public: m_formatter.oneByteOp_disp32(OP_MOV_GvEv, offset, base, dst); } - void movl_mr(const void* base, RegisterID index, int scale, RegisterID dst) + void movl_mr(const void *base, RegisterID index, int scale, RegisterID dst) { int32_t disp = AddressImmediate(base); @@ -1599,7 +1599,7 @@ public: m_formatter.oneByteOp(OP_MOV_GvEv, offset, base, index, scale, dst); } - void movl_mr(const void* addr, RegisterID dst) + void movl_mr(const void *addr, RegisterID dst) { if (dst == rax #ifdef JS_CODEGEN_X64 @@ -1643,7 +1643,7 @@ public: m_formatter.immediate8(imm); } - void movb_im(int32_t imm, const void* addr) + void movb_im(int32_t imm, const void *addr) { spew("movb $%d, %p", imm, addr); m_formatter.oneByteOp_disp32(OP_GROUP11_EvIb, addr, GROUP11_MOV); @@ -1658,7 +1658,7 @@ public: m_formatter.immediate16(imm); } - void movw_im(int32_t imm, const void* addr) + void movw_im(int32_t imm, const void *addr) { spew("movw $%d, %p", imm, addr); m_formatter.prefix(PRE_OPERAND_SIZE); @@ -1688,7 +1688,7 @@ public: m_formatter.immediate32(imm); } - void movl_EAXm(const void* addr) + void movl_EAXm(const void *addr) { #ifdef JS_CODEGEN_X64 if (IsAddressImmediate(addr)) { @@ -1731,7 +1731,7 @@ public: m_formatter.oneByteOp64(OP_MOV_EvGv, offset, base, index, scale, src); } - void movq_rm(RegisterID src, const void* addr) + void movq_rm(RegisterID src, const void *addr) { if (src == rax && !IsAddressImmediate(addr)) { movq_EAXm(addr); @@ -1742,7 +1742,28 @@ public: m_formatter.oneByteOp64(OP_MOV_EvGv, addr, src); } - void movq_mEAX(const void* addr) + void movq_rm(XMMRegisterID src, int32_t offset, RegisterID base) + { + spew("movq %s, " MEM_ob, XMMRegName(src), ADDR_ob(offset, base)); + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVQ_EdVd, offset, base, src); + } + + void movq_rm(XMMRegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) + { + spew("movq %s, " MEM_obs, XMMRegName(src), ADDR_obs(offset, base, index, scale)); + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVQ_EdVd, offset, base, index, scale, src); + } + + void movq_rm(XMMRegisterID src, const void *addr) + { + spew("movq %s, %p", XMMRegName(src), addr); + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVQ_EdVd, addr, src); + } + + void movq_mEAX(const void *addr) { if (IsAddressImmediate(addr)) { movq_mr(addr, rax); @@ -1754,7 +1775,7 @@ public: m_formatter.immediate64(reinterpret_cast(addr)); } - void movq_EAXm(const void* addr) + void movq_EAXm(const void *addr) { if (IsAddressImmediate(addr)) { movq_rm(rax, addr); @@ -1784,7 +1805,7 @@ public: m_formatter.oneByteOp64(OP_MOV_GvEv, offset, base, index, scale, dst); } - void movq_mr(const void* addr, RegisterID dst) + void movq_mr(const void *addr, RegisterID dst) { if (dst == rax && !IsAddressImmediate(addr)) { movq_mEAX(addr); @@ -1795,6 +1816,27 @@ public: m_formatter.oneByteOp64(OP_MOV_GvEv, addr, dst); } + void movq_mr(int32_t offset, RegisterID base, XMMRegisterID dst) + { + spew("movq " MEM_ob ", %s", ADDR_ob(offset, base), XMMRegName(dst)); + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVQ_VdEd, offset, base, (RegisterID) dst); + } + + void movq_mr(int32_t offset, RegisterID base, RegisterID index, int32_t scale, XMMRegisterID dst) + { + spew("movq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), XMMRegName(dst)); + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVQ_VdEd, offset, base, index, scale, (RegisterID) dst); + } + + void movq_mr(const void *addr, XMMRegisterID dst) + { + spew("movq %p, %s", addr, XMMRegName(dst)); + m_formatter.prefix(PRE_SSE_66); + m_formatter.twoByteOp64(OP2_MOVQ_VdEd, addr, (RegisterID) dst); + } + void leaq_mr(int32_t offset, RegisterID base, RegisterID index, int scale, RegisterID dst) { spew("leaq " MEM_obs ", %s", ADDR_obs(offset, base, index, scale), GPReg64Name(dst)), @@ -1814,7 +1856,7 @@ public: m_formatter.oneByteOp64(OP_GROUP11_EvIz, offset, base, index, scale, GROUP11_MOV); m_formatter.immediate32(imm); } - void movq_i32m(int32_t imm, const void* addr) + void movq_i32m(int32_t imm, const void *addr) { spew("movq $%d, %p", imm, addr); m_formatter.oneByteOp64(OP_GROUP11_EvIz, addr, GROUP11_MOV); @@ -1873,7 +1915,7 @@ public: return label; } #endif - void movl_rm(RegisterID src, const void* addr) + void movl_rm(RegisterID src, const void *addr) { if (src == rax #ifdef JS_CODEGEN_X64 @@ -1888,7 +1930,7 @@ public: m_formatter.oneByteOp(OP_MOV_EvGv, addr, src); } - void movl_i32m(int32_t imm, const void* addr) + void movl_i32m(int32_t imm, const void *addr) { spew("movl $%d, %p", imm, addr); m_formatter.oneByteOp(OP_GROUP11_EvIz, addr, GROUP11_MOV); @@ -1913,7 +1955,7 @@ public: m_formatter.oneByteOp8(OP_MOV_EbGv, offset, base, index, scale, src); } - void movb_rm(RegisterID src, const void* addr) + void movb_rm(RegisterID src, const void *addr) { spew("movb %s, %p", GPReg8Name(src), addr); m_formatter.oneByteOp8(OP_MOV_EbGv, addr, src); @@ -1949,7 +1991,7 @@ public: m_formatter.twoByteOp(OP2_MOVZX_GvEb, offset, base, index, scale, dst); } - void movzbl_mr(const void* addr, RegisterID dst) + void movzbl_mr(const void *addr, RegisterID dst) { spew("movzbl %p, %s", addr, GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEb, addr, dst); @@ -1979,7 +2021,7 @@ public: m_formatter.twoByteOp(OP2_MOVSX_GvEb, offset, base, index, scale, dst); } - void movsbl_mr(const void* addr, RegisterID dst) + void movsbl_mr(const void *addr, RegisterID dst) { spew("movsbl %p, %s", addr, GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEb, addr, dst); @@ -2009,7 +2051,7 @@ public: m_formatter.twoByteOp(OP2_MOVZX_GvEw, offset, base, index, scale, dst); } - void movzwl_mr(const void* addr, RegisterID dst) + void movzwl_mr(const void *addr, RegisterID dst) { spew("movzwl %p, %s", addr, GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVZX_GvEw, addr, dst); @@ -2039,7 +2081,7 @@ public: m_formatter.twoByteOp(OP2_MOVSX_GvEw, offset, base, index, scale, dst); } - void movswl_mr(const void* addr, RegisterID dst) + void movswl_mr(const void *addr, RegisterID dst) { spew("movswl %p, %s", addr, GPReg32Name(dst)); m_formatter.twoByteOp(OP2_MOVSX_GvEw, addr, dst); @@ -2211,7 +2253,7 @@ public: { twoByteOpSimd("vpcmpeqd", VEX_PD, OP2_PCMPEQD_VdqWdq, offset, base, src0, dst); } - void vpcmpeqd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vpcmpeqd_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpcmpeqd", VEX_PD, OP2_PCMPEQD_VdqWdq, address, src0, dst); } @@ -2224,7 +2266,7 @@ public: { twoByteOpSimd("vpcmpgtd", VEX_PD, OP2_PCMPGTD_VdqWdq, offset, base, src0, dst); } - void vpcmpgtd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vpcmpgtd_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpcmpgtd", VEX_PD, OP2_PCMPGTD_VdqWdq, address, src0, dst); } @@ -2237,7 +2279,7 @@ public: { twoByteOpImmSimd("vcmpps", VEX_PS, OP2_CMPPS_VpsWps, order, offset, base, src0, dst); } - void vcmpps_mr(uint8_t order, const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vcmpps_mr(uint8_t order, const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpImmSimd("vcmpps", VEX_PS, OP2_CMPPS_VpsWps, order, address, src0, dst); } @@ -2248,7 +2290,7 @@ public: void vrcpps_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { twoByteOpSimd("vrcpps", VEX_PS, OP2_RCPPS_VpsWps, offset, base, invalid_xmm, dst); } - void vrcpps_mr(const void* address, XMMRegisterID dst) { + void vrcpps_mr(const void *address, XMMRegisterID dst) { twoByteOpSimd("vrcpps", VEX_PS, OP2_RCPPS_VpsWps, address, invalid_xmm, dst); } @@ -2258,7 +2300,7 @@ public: void vrsqrtps_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { twoByteOpSimd("vrsqrtps", VEX_PS, OP2_RSQRTPS_VpsWps, offset, base, invalid_xmm, dst); } - void vrsqrtps_mr(const void* address, XMMRegisterID dst) { + void vrsqrtps_mr(const void *address, XMMRegisterID dst) { twoByteOpSimd("vrsqrtps", VEX_PS, OP2_RSQRTPS_VpsWps, address, invalid_xmm, dst); } @@ -2268,7 +2310,7 @@ public: void vsqrtps_mr(int32_t offset, RegisterID base, XMMRegisterID dst) { twoByteOpSimd("vsqrtps", VEX_PS, OP2_SQRTPS_VpsWps, offset, base, invalid_xmm, dst); } - void vsqrtps_mr(const void* address, XMMRegisterID dst) { + void vsqrtps_mr(const void *address, XMMRegisterID dst) { twoByteOpSimd("vsqrtps", VEX_PS, OP2_SQRTPS_VpsWps, address, invalid_xmm, dst); } @@ -2292,11 +2334,11 @@ public: twoByteOpSimd("vaddss", VEX_SS, OP2_ADDSD_VsdWsd, offset, base, src0, dst); } - void vaddsd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vaddsd_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vaddsd", VEX_SD, OP2_ADDSD_VsdWsd, address, src0, dst); } - void vaddss_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vaddss_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vaddss", VEX_SS, OP2_ADDSD_VsdWsd, address, src0, dst); } @@ -2363,7 +2405,7 @@ public: } #ifdef JS_CODEGEN_X86 - void vcvtsi2sd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vcvtsi2sd_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vcvtsi2sd", VEX_SD, OP2_CVTSI2SD_VsdEd, address, src0, dst); } @@ -2474,7 +2516,7 @@ public: { twoByteOpImmSimd("vpshufd", VEX_PD, OP2_PSHUFD_VdqWdqIb, mask, offset, base, invalid_xmm, dst); } - void vpshufd_imr(uint32_t mask, const void* address, XMMRegisterID dst) + void vpshufd_imr(uint32_t mask, const void *address, XMMRegisterID dst) { twoByteOpImmSimd("vpshufd", VEX_PD, OP2_PSHUFD_VdqWdqIb, mask, address, invalid_xmm, dst); } @@ -2487,7 +2529,7 @@ public: { twoByteOpImmSimd("vshufps", VEX_PS, OP2_SHUFPS_VpsWpsIb, mask, offset, base, src0, dst); } - void vshufps_imr(uint32_t mask, const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vshufps_imr(uint32_t mask, const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpImmSimd("vshufps", VEX_PS, OP2_SHUFPS_VpsWpsIb, mask, address, src0, dst); } @@ -2577,6 +2619,46 @@ public: twoByteOpInt32Simd("vmovd", VEX_PD, OP2_MOVD_VdEd, src, invalid_xmm, dst); } + void vmovd_mr(int32_t offset, RegisterID base, XMMRegisterID dst) + { + twoByteOpSimd("vmovd", VEX_PD, OP2_MOVD_VdEd, offset, base, invalid_xmm, dst); + } + + void vmovd_mr(int32_t offset, RegisterID base, RegisterID index, int32_t scale, XMMRegisterID dst) + { + twoByteOpSimd("vmovd", VEX_PD, OP2_MOVD_VdEd, offset, base, index, scale, invalid_xmm, dst); + } + + void vmovd_mr_disp32(int32_t offset, RegisterID base, XMMRegisterID dst) + { + twoByteOpSimd_disp32("vmovd", VEX_PD, OP2_MOVD_VdEd, offset, base, invalid_xmm, dst); + } + + void vmovd_mr(const void *address, XMMRegisterID dst) + { + twoByteOpSimd("vmovd", VEX_PD, OP2_MOVD_VdEd, address, invalid_xmm, dst); + } + + void vmovd_rm(XMMRegisterID src, int32_t offset, RegisterID base) + { + twoByteOpSimd("vmovd", VEX_PD, OP2_MOVD_EdVd, offset, base, invalid_xmm, src); + } + + void vmovd_rm(XMMRegisterID src, int32_t offset, RegisterID base, RegisterID index, int scale) + { + twoByteOpSimd("vmovd", VEX_PD, OP2_MOVD_EdVd, offset, base, index, scale, invalid_xmm, src); + } + + void vmovd_rm_disp32(XMMRegisterID src, int32_t offset, RegisterID base) + { + twoByteOpSimd_disp32("vmovd", VEX_PD, OP2_MOVD_EdVd, offset, base, invalid_xmm, src); + } + + void vmovd_rm(XMMRegisterID src, const void *address) + { + twoByteOpSimd("vmovd", VEX_PD, OP2_MOVD_EdVd, address, invalid_xmm, src); + } + #ifdef JS_CODEGEN_X64 void vmovq_rr(XMMRegisterID src, RegisterID dst) { @@ -2664,52 +2746,52 @@ public: twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, src1, src0, dst); } - void vmovsd_mr(const void* address, XMMRegisterID dst) + void vmovsd_mr(const void *address, XMMRegisterID dst) { twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_VsdWsd, address, invalid_xmm, dst); } - void vmovss_mr(const void* address, XMMRegisterID dst) + void vmovss_mr(const void *address, XMMRegisterID dst) { twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_VsdWsd, address, invalid_xmm, dst); } - void vmovups_mr(const void* address, XMMRegisterID dst) + void vmovups_mr(const void *address, XMMRegisterID dst) { twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_VpsWps, address, invalid_xmm, dst); } - void vmovdqu_mr(const void* address, XMMRegisterID dst) + void vmovdqu_mr(const void *address, XMMRegisterID dst) { twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_VdqWdq, address, invalid_xmm, dst); } - void vmovsd_rm(XMMRegisterID src, const void* address) + void vmovsd_rm(XMMRegisterID src, const void *address) { twoByteOpSimd("vmovsd", VEX_SD, OP2_MOVSD_WsdVsd, address, invalid_xmm, src); } - void vmovss_rm(XMMRegisterID src, const void* address) + void vmovss_rm(XMMRegisterID src, const void *address) { twoByteOpSimd("vmovss", VEX_SS, OP2_MOVSD_WsdVsd, address, invalid_xmm, src); } - void vmovdqa_rm(XMMRegisterID src, const void* address) + void vmovdqa_rm(XMMRegisterID src, const void *address) { twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_WdqVdq, address, invalid_xmm, src); } - void vmovaps_rm(XMMRegisterID src, const void* address) + void vmovaps_rm(XMMRegisterID src, const void *address) { twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_WsdVsd, address, invalid_xmm, src); } - void vmovdqu_rm(XMMRegisterID src, const void* address) + void vmovdqu_rm(XMMRegisterID src, const void *address) { twoByteOpSimd("vmovdqu", VEX_SS, OP2_MOVDQ_WdqVdq, address, invalid_xmm, src); } - void vmovups_rm(XMMRegisterID src, const void* address) + void vmovups_rm(XMMRegisterID src, const void *address) { twoByteOpSimd("vmovups", VEX_PS, OP2_MOVPS_WpsVps, address, invalid_xmm, src); } @@ -2830,12 +2912,12 @@ public: return twoByteRipOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, invalid_xmm, dst); } #else - void vmovaps_mr(const void* address, XMMRegisterID dst) + void vmovaps_mr(const void *address, XMMRegisterID dst) { twoByteOpSimd("vmovaps", VEX_PS, OP2_MOVAPS_VsdWsd, address, invalid_xmm, dst); } - void vmovdqa_mr(const void* address, XMMRegisterID dst) + void vmovdqa_mr(const void *address, XMMRegisterID dst) { twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, address, invalid_xmm, dst); } @@ -3013,7 +3095,7 @@ public: twoByteOpSimd("vandps", VEX_PS, OP2_ANDPS_VpsWps, offset, base, src0, dst); } - void vandps_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vandps_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vandps", VEX_PS, OP2_ANDPS_VpsWps, address, src0, dst); } @@ -3028,7 +3110,7 @@ public: twoByteOpSimd("vandnps", VEX_PS, OP2_ANDNPS_VpsWps, offset, base, src0, dst); } - void vandnps_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vandnps_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vandnps", VEX_PS, OP2_ANDNPS_VpsWps, address, src0, dst); } @@ -3043,7 +3125,7 @@ public: twoByteOpSimd("vorps", VEX_PS, OP2_ORPS_VpsWps, offset, base, src0, dst); } - void vorps_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vorps_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vorps", VEX_PS, OP2_ORPS_VpsWps, address, src0, dst); } @@ -3058,7 +3140,7 @@ public: twoByteOpSimd("vxorps", VEX_PS, OP2_XORPS_VpsWps, offset, base, src0, dst); } - void vxorps_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) + void vxorps_mr(const void *address, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vxorps", VEX_PS, OP2_XORPS_VpsWps, address, src0, dst); } @@ -3339,7 +3421,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off SetRel32(code + from.offset(), code + to.offset()); } - void executableCopy(void* buffer) + void executableCopy(void *buffer) { memcpy(buffer, m_formatter.buffer(), size()); } @@ -3577,7 +3659,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off } void twoByteOpSimd(const char *name, VexOperandType ty, TwoByteOpcodeID opcode, - const void* address, XMMRegisterID src0, XMMRegisterID dst) + const void *address, XMMRegisterID src0, XMMRegisterID dst) { if (useLegacySSEEncoding(src0, dst)) { if (IsXMMReversedOperands(opcode)) @@ -4059,7 +4141,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off memoryModRM_disp32(offset, index, scale, reg); } - void oneByteOp(OneByteOpcodeID opcode, const void* address, int reg) + void oneByteOp(OneByteOpcodeID opcode, const void *address, int reg) { m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, 0); @@ -4067,7 +4149,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off memoryModRM_disp32(address, reg); } - void oneByteOp_disp32(OneByteOpcodeID opcode, const void* address, int reg) + void oneByteOp_disp32(OneByteOpcodeID opcode, const void *address, int reg) { m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, 0); @@ -4199,7 +4281,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off memoryModRM(offset, base, index, scale, reg); } - void twoByteOp(TwoByteOpcodeID opcode, const void* address, int reg) + void twoByteOp(TwoByteOpcodeID opcode, const void *address, int reg) { m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, 0); @@ -4209,7 +4291,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off } void twoByteOpVex(VexOperandType ty, TwoByteOpcodeID opcode, - const void* address, XMMRegisterID src0, int reg) + const void *address, XMMRegisterID src0, int reg) { int r = (reg >> 3), x = 0, b = 0; int m = 1; // 0x0F @@ -4266,7 +4348,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off memoryModRM(offset, base, reg); } - void threeByteOp(ThreeByteOpcodeID opcode, ThreeByteEscape escape, const void* address, int reg) + void threeByteOp(ThreeByteOpcodeID opcode, ThreeByteEscape escape, const void *address, int reg) { m_buffer.ensureSpace(MaxInstructionSize); emitRexIfNeeded(reg, 0, 0); @@ -4373,7 +4455,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off memoryModRM(offset, base, index, scale, reg); } - void oneByteOp64(OneByteOpcodeID opcode, const void* address, int reg) + void oneByteOp64(OneByteOpcodeID opcode, const void *address, int reg) { m_buffer.ensureSpace(MaxInstructionSize); emitRexW(reg, 0, 0); @@ -4390,6 +4472,33 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off registerModRM(rm, reg); } + void twoByteOp64(TwoByteOpcodeID opcode, int offset, RegisterID base, int reg) + { + m_buffer.ensureSpace(MaxInstructionSize); + emitRexW(reg, 0, base); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(offset, base, reg); + } + + void twoByteOp64(TwoByteOpcodeID opcode, int offset, RegisterID base, RegisterID index, int scale, int reg) + { + m_buffer.ensureSpace(MaxInstructionSize); + emitRexW(reg, index, base); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(offset, base, index, scale, reg); + } + + void twoByteOp64(TwoByteOpcodeID opcode, const void *address, int reg) + { + m_buffer.ensureSpace(MaxInstructionSize); + emitRexW(reg, 0, 0); + m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE); + m_buffer.putByteUnchecked(opcode); + memoryModRM(address, reg); + } + void twoByteOpVex64(VexOperandType ty, TwoByteOpcodeID opcode, RegisterID rm, XMMRegisterID src0, XMMRegisterID reg) { @@ -4466,7 +4575,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off memoryModRM(offset, base, index, scale, reg); } - void oneByteOp8(OneByteOpcodeID opcode, const void* address, RegisterID reg) + void oneByteOp8(OneByteOpcodeID opcode, const void *address, RegisterID reg) { m_buffer.ensureSpace(MaxInstructionSize); emitRexIf(byteRegRequiresRex(reg), reg, 0, 0); @@ -4810,7 +4919,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off m_buffer.putIntUnchecked(offset); } - void memoryModRM_disp32(const void* address, int reg) + void memoryModRM_disp32(const void *address, int reg) { int32_t disp = AddressImmediate(address); @@ -4824,7 +4933,7 @@ threeByteOpImmSimd("vblendps", VEX_PD, OP3_BLENDPS_VpsWpsIb, ESCAPE_3A, imm, off m_buffer.putIntUnchecked(disp); } - void memoryModRM(const void* address, int reg) + void memoryModRM(const void *address, int reg) { memoryModRM_disp32(address, reg); } diff --git a/js/src/jit/shared/Encoding-x86-shared.h b/js/src/jit/shared/Encoding-x86-shared.h index a513fbd4e0fb..a493ec46253c 100644 --- a/js/src/jit/shared/Encoding-x86-shared.h +++ b/js/src/jit/shared/Encoding-x86-shared.h @@ -167,6 +167,7 @@ enum TwoByteOpcodeID { OP2_XORPD_VpdWpd = 0x57, OP2_PCMPGTD_VdqWdq = 0x66, OP2_MOVD_VdEd = 0x6E, + OP2_MOVQ_VdEd = 0x6E, OP2_MOVDQ_VsdWsd = 0x6F, OP2_MOVDQ_VdqWdq = 0x6F, OP2_PSHUFD_VdqWdqIb = 0x70, @@ -177,6 +178,7 @@ enum TwoByteOpcodeID { OP2_PCMPEQW = 0x75, OP2_PCMPEQD_VdqWdq = 0x76, OP2_MOVD_EdVd = 0x7E, + OP2_MOVQ_EdVd = 0x7E, OP2_MOVDQ_WdqVdq = 0x7F, OP2_JCC_rel32 = 0x80, OP_SETCC = 0x90, diff --git a/js/src/jit/x64/Assembler-x64.h b/js/src/jit/x64/Assembler-x64.h index f9c0bf0194ee..e66330152dcd 100644 --- a/js/src/jit/x64/Assembler-x64.h +++ b/js/src/jit/x64/Assembler-x64.h @@ -361,6 +361,21 @@ class Assembler : public AssemblerX86Shared MOZ_CRASH("unexpected operand kind"); } } + void movq(const Operand &src, FloatRegister dest) { + switch (src.kind()) { + case Operand::MEM_REG_DISP: + masm.movq_mr(src.disp(), src.base(), dest.code()); + break; + case Operand::MEM_SCALE: + masm.movq_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code()); + break; + case Operand::MEM_ADDRESS32: + masm.movq_mr(src.address(), dest.code()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } void movq(Register src, const Operand &dest) { switch (dest.kind()) { case Operand::REG: @@ -379,6 +394,21 @@ class Assembler : public AssemblerX86Shared MOZ_CRASH("unexpected operand kind"); } } + void movq(FloatRegister src, const Operand &dest) { + switch (dest.kind()) { + case Operand::MEM_REG_DISP: + masm.movq_rm(src.code(), dest.disp(), dest.base()); + break; + case Operand::MEM_SCALE: + masm.movq_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale()); + break; + case Operand::MEM_ADDRESS32: + masm.movq_rm(src.code(), dest.address()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } void movq(Imm32 imm32, const Operand &dest) { switch (dest.kind()) { case Operand::REG: diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 3fb65fa12a52..e346c9f69327 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -254,6 +254,105 @@ CodeGeneratorX64::memoryBarrier(MemoryBarrierBits barrier) masm.storeLoadFence(); } +void +CodeGeneratorX64::loadSimd(Scalar::Type type, unsigned numElems, const Operand &srcAddr, + FloatRegister out) +{ + switch (type) { + case Scalar::Float32x4: { + switch (numElems) { + // In memory-to-register mode, movss zeroes out the high lanes. + case 1: masm.loadFloat32(srcAddr, out); break; + // See comment above, which also applies to movsd. + case 2: masm.loadDouble(srcAddr, out); break; + case 4: masm.loadUnalignedFloat32x4(srcAddr, out); break; + default: MOZ_CRASH("unexpected size for partial load"); + } + break; + } + case Scalar::Int32x4: { + switch (numElems) { + // In memory-to-register mode, movd zeroes out the high lanes. + case 1: masm.vmovd(srcAddr, out); break; + // See comment above, which also applies to movq. + case 2: masm.movq(srcAddr, out); break; + case 4: masm.loadUnalignedInt32x4(srcAddr, out); break; + default: MOZ_CRASH("unexpected size for partial load"); + } + break; + } + case Scalar::Int8: + case Scalar::Uint8: + case Scalar::Int16: + case Scalar::Uint16: + case Scalar::Int32: + case Scalar::Uint32: + case Scalar::Float32: + case Scalar::Float64: + case Scalar::Uint8Clamped: + case Scalar::MaxTypedArrayViewType: + MOZ_CRASH("should only handle SIMD types"); + } +} + +void +CodeGeneratorX64::emitSimdLoad(LAsmJSLoadHeap *ins) +{ + MAsmJSLoadHeap *mir = ins->mir(); + Scalar::Type type = mir->accessType(); + const LAllocation *ptr = ins->ptr(); + FloatRegister out = ToFloatRegister(ins->output()); + Operand srcAddr(HeapReg); + + if (ptr->isConstant()) { + int32_t ptrImm = ptr->toConstant()->toInt32(); + MOZ_ASSERT(ptrImm >= 0); + srcAddr = Operand(HeapReg, ptrImm); + } else { + srcAddr = Operand(HeapReg, ToRegister(ptr), TimesOne); + } + + uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck; + if (mir->needsBoundsCheck()) { + maybeCmpOffset = masm.cmp32WithPatch(ToRegister(ptr), Imm32(0)).offset(); + masm.j(Assembler::AboveOrEqual, mir->outOfBoundsLabel()); // Throws RangeError + } + + unsigned numElems = mir->numSimdElems(); + if (numElems == 3) { + MOZ_ASSERT(type == Scalar::Int32x4 || type == Scalar::Float32x4); + + Operand shiftedOffset(HeapReg); + if (ptr->isConstant()) + shiftedOffset = Operand(HeapReg, ptr->toConstant()->toInt32() + 2 * sizeof(float)); + else + shiftedOffset = Operand(HeapReg, ToRegister(ptr), TimesOne, 2 * sizeof(float)); + + // Load XY + uint32_t before = masm.size(); + loadSimd(type, 2, srcAddr, out); + uint32_t after = masm.size(); + // We're noting a load of 3 elements, so that the bounds check checks + // for 3 elements. + masm.append(AsmJSHeapAccess(before, after, 3, type, maybeCmpOffset)); + + // Load Z (W is zeroed) + before = after; + loadSimd(type, 1, shiftedOffset, ScratchSimdReg); + after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, 1, type)); + + // Move ZW atop XY + masm.vmovlhps(ScratchSimdReg, out, out); + return; + } + + uint32_t before = masm.size(); + loadSimd(type, numElems, srcAddr, out); + uint32_t after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, numElems, type, maybeCmpOffset)); +} + void CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) { @@ -263,6 +362,9 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) const LDefinition *out = ins->output(); Operand srcAddr(HeapReg); + if (Scalar::isSimdType(vt)) + return emitSimdLoad(ins); + if (ptr->isConstant()) { int32_t ptrImm = ptr->toConstant()->toInt32(); MOZ_ASSERT(ptrImm >= 0); @@ -276,13 +378,9 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck; if (mir->needsBoundsCheck()) { CodeOffsetLabel cmp = masm.cmp32WithPatch(ToRegister(ptr), Imm32(0)); - if (mir->outOfBoundsLabel()) { - masm.j(Assembler::AboveOrEqual, mir->outOfBoundsLabel()); // Throws RangeError - } else { - ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt); - addOutOfLineCode(ool, ins->mir()); - masm.j(Assembler::AboveOrEqual, ool->entry()); - } + ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt); + addOutOfLineCode(ool, ins->mir()); + masm.j(Assembler::AboveOrEqual, ool->entry()); maybeCmpOffset = cmp.offset(); } @@ -296,8 +394,8 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) case Scalar::Uint32: masm.movl(srcAddr, ToRegister(out)); break; case Scalar::Float32: masm.loadFloat32(srcAddr, ToFloatRegister(out)); break; case Scalar::Float64: masm.loadDouble(srcAddr, ToFloatRegister(out)); break; - case Scalar::Float32x4: masm.loadUnalignedFloat32x4(srcAddr, ToFloatRegister(out)); break; - case Scalar::Int32x4: masm.loadUnalignedInt32x4(srcAddr, ToFloatRegister(out)); break; + case Scalar::Float32x4: + case Scalar::Int32x4: MOZ_CRASH("SIMD loads should be handled in emitSimdLoad"); case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); @@ -310,6 +408,105 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), maybeCmpOffset)); } +void +CodeGeneratorX64::storeSimd(Scalar::Type type, unsigned numElems, FloatRegister in, + const Operand &dstAddr) +{ + switch (type) { + case Scalar::Float32x4: { + switch (numElems) { + // In memory-to-register mode, movss zeroes out the high lanes. + case 1: masm.storeFloat32(in, dstAddr); break; + // See comment above, which also applies to movsd. + case 2: masm.storeDouble(in, dstAddr); break; + case 4: masm.storeUnalignedFloat32x4(in, dstAddr); break; + default: MOZ_CRASH("unexpected size for partial load"); + } + break; + } + case Scalar::Int32x4: { + switch (numElems) { + // In memory-to-register mode, movd zeroes out the high lanes. + case 1: masm.vmovd(in, dstAddr); break; + // See comment above, which also applies to movq. + case 2: masm.movq(in, dstAddr); break; + case 4: masm.storeUnalignedInt32x4(in, dstAddr); break; + default: MOZ_CRASH("unexpected size for partial load"); + } + break; + } + case Scalar::Int8: + case Scalar::Uint8: + case Scalar::Int16: + case Scalar::Uint16: + case Scalar::Int32: + case Scalar::Uint32: + case Scalar::Float32: + case Scalar::Float64: + case Scalar::Uint8Clamped: + case Scalar::MaxTypedArrayViewType: + MOZ_CRASH("should only handle SIMD types"); + } +} + +void +CodeGeneratorX64::emitSimdStore(LAsmJSStoreHeap *ins) +{ + MAsmJSStoreHeap *mir = ins->mir(); + Scalar::Type type = mir->accessType(); + const LAllocation *ptr = ins->ptr(); + FloatRegister in = ToFloatRegister(ins->value()); + Operand dstAddr(HeapReg); + + if (ptr->isConstant()) { + int32_t ptrImm = ptr->toConstant()->toInt32(); + MOZ_ASSERT(ptrImm >= 0); + dstAddr = Operand(HeapReg, ptrImm); + } else { + dstAddr = Operand(HeapReg, ToRegister(ptr), TimesOne); + } + + uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck; + if (mir->needsBoundsCheck()) { + maybeCmpOffset = masm.cmp32WithPatch(ToRegister(ptr), Imm32(0)).offset(); + masm.j(Assembler::AboveOrEqual, mir->outOfBoundsLabel()); // Throws RangeError + } + + unsigned numElems = mir->numSimdElems(); + if (numElems == 3) { + MOZ_ASSERT(type == Scalar::Int32x4 || type == Scalar::Float32x4); + + Operand shiftedOffset(HeapReg); + if (ptr->isConstant()) + shiftedOffset = Operand(HeapReg, ptr->toConstant()->toInt32() + 2 * sizeof(float)); + else + shiftedOffset = Operand(HeapReg, ToRegister(ptr), TimesOne, 2 * sizeof(float)); + + // Store Z first: it would be observable to store XY first, in the + // case XY can be stored in bounds but Z can't (in this case, we'd throw + // without restoring the values previously stored before XY). + masm.vmovhlps(in, ScratchSimdReg, ScratchSimdReg); + uint32_t before = masm.size(); + storeSimd(type, 1, ScratchSimdReg, shiftedOffset); + uint32_t after = masm.size(); + // We're noting a store of 3 elements, so that the bounds check checks + // for 3 elements. + masm.append(AsmJSHeapAccess(before, after, 3, type, maybeCmpOffset)); + + // Store XY + before = after; + storeSimd(type, 2, in, dstAddr); + after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, 2, type)); + return; + } + + uint32_t before = masm.size(); + storeSimd(type, numElems, in, dstAddr); + uint32_t after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, numElems, type, maybeCmpOffset)); +} + void CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) { @@ -318,6 +515,9 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) const LAllocation *ptr = ins->ptr(); Operand dstAddr(HeapReg); + if (Scalar::isSimdType(vt)) + return emitSimdStore(ins); + if (ptr->isConstant()) { int32_t ptrImm = ptr->toConstant()->toInt32(); MOZ_ASSERT(ptrImm >= 0); @@ -331,10 +531,7 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck; if (mir->needsBoundsCheck()) { CodeOffsetLabel cmp = masm.cmp32WithPatch(ToRegister(ptr), Imm32(0)); - if (mir->outOfBoundsLabel()) - masm.j(Assembler::AboveOrEqual, mir->outOfBoundsLabel()); // Throws RangeError - else - masm.j(Assembler::AboveOrEqual, &rejoin); + masm.j(Assembler::AboveOrEqual, &rejoin); maybeCmpOffset = cmp.offset(); } @@ -365,8 +562,8 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) case Scalar::Uint32: masm.movl(ToRegister(ins->value()), dstAddr); break; case Scalar::Float32: masm.storeFloat32(ToFloatRegister(ins->value()), dstAddr); break; case Scalar::Float64: masm.storeDouble(ToFloatRegister(ins->value()), dstAddr); break; - case Scalar::Float32x4: masm.storeUnalignedFloat32x4(ToFloatRegister(ins->value()), dstAddr); break; - case Scalar::Int32x4: masm.storeUnalignedInt32x4(ToFloatRegister(ins->value()), dstAddr); break; + case Scalar::Float32x4: + case Scalar::Int32x4: MOZ_CRASH("SIMD stores must be handled in emitSimdStore"); case Scalar::Uint8Clamped: case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected array type"); diff --git a/js/src/jit/x64/CodeGenerator-x64.h b/js/src/jit/x64/CodeGenerator-x64.h index bf5654a03975..4dc14cfa5814 100644 --- a/js/src/jit/x64/CodeGenerator-x64.h +++ b/js/src/jit/x64/CodeGenerator-x64.h @@ -27,6 +27,10 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared Operand dest, MIRType slotType); void memoryBarrier(MemoryBarrierBits barrier); + void loadSimd(Scalar::Type type, unsigned numElems, const Operand &srcAddr, FloatRegister out); + void emitSimdLoad(LAsmJSLoadHeap *ins); + void storeSimd(Scalar::Type type, unsigned numElems, FloatRegister in, const Operand &dstAddr); + void emitSimdStore(LAsmJSStoreHeap *ins); public: CodeGeneratorX64(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm); diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp index 4145c0c88852..7c4db8b70987 100644 --- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -148,7 +148,7 @@ LIRGeneratorX64::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) // offset in the addressing mode would not wrap back into the protected area // reserved for the heap. For simplicity (and since we don't care about // getting maximum performance in these cases) only allow constant - // opererands when skipping bounds checks. + // operands when skipping bounds checks. LAllocation ptrAlloc = ins->needsBoundsCheck() ? useRegisterAtStart(ptr) : useRegisterOrNonNegativeConstantAtStart(ptr); diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index 27ce2e5eff64..62a99bd3f3cc 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -467,6 +467,11 @@ class Assembler : public AssemblerX86Shared masm.vmovss_mr_disp32(src.offset, src.base.code(), dest.code()); return CodeOffsetLabel(masm.currentOffset()); } + CodeOffsetLabel vmovdWithPatch(Address src, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + masm.vmovd_mr_disp32(src.offset, src.base.code(), dest.code()); + return CodeOffsetLabel(masm.currentOffset()); + } CodeOffsetLabel vmovsdWithPatch(Address src, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); masm.vmovsd_mr_disp32(src.offset, src.base.code(), dest.code()); @@ -496,6 +501,11 @@ class Assembler : public AssemblerX86Shared masm.movl_rm_disp32(src.code(), dest.offset, dest.base.code()); return CodeOffsetLabel(masm.currentOffset()); } + CodeOffsetLabel vmovdWithPatch(FloatRegister src, Address dest) { + MOZ_ASSERT(HasSSE2()); + masm.vmovd_rm_disp32(src.code(), dest.offset, dest.base.code()); + return CodeOffsetLabel(masm.currentOffset()); + } CodeOffsetLabel vmovssWithPatch(FloatRegister src, Address dest) { MOZ_ASSERT(HasSSE2()); masm.vmovss_rm_disp32(src.code(), dest.offset, dest.base.code()); @@ -551,6 +561,11 @@ class Assembler : public AssemblerX86Shared masm.vmovss_mr(src.addr, dest.code()); return CodeOffsetLabel(masm.currentOffset()); } + CodeOffsetLabel vmovdWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) { + MOZ_ASSERT(HasSSE2()); + masm.vmovd_mr(src.addr, dest.code()); + return CodeOffsetLabel(masm.currentOffset()); + } CodeOffsetLabel vmovsdWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); masm.vmovsd_mr(src.addr, dest.code()); @@ -595,6 +610,11 @@ class Assembler : public AssemblerX86Shared masm.vmovss_rm(src.code(), dest.addr); return CodeOffsetLabel(masm.currentOffset()); } + CodeOffsetLabel vmovdWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) { + MOZ_ASSERT(HasSSE2()); + masm.vmovd_rm(src.code(), dest.addr); + return CodeOffsetLabel(masm.currentOffset()); + } CodeOffsetLabel vmovsdWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) { MOZ_ASSERT(HasSSE2()); masm.vmovsd_rm(src.code(), dest.addr); diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 1334bdfca0a1..41ab5a3d9638 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -269,8 +269,8 @@ CodeGeneratorX86::load(Scalar::Type vt, const T &srcAddr, const LDefinition *out case Scalar::Uint32: masm.movlWithPatch(srcAddr, ToRegister(out)); break; case Scalar::Float32: masm.vmovssWithPatch(srcAddr, ToFloatRegister(out)); break; case Scalar::Float64: masm.vmovsdWithPatch(srcAddr, ToFloatRegister(out)); break; - case Scalar::Float32x4: masm.vmovupsWithPatch(srcAddr, ToFloatRegister(out)); break; - case Scalar::Int32x4: masm.vmovdquWithPatch(srcAddr, ToFloatRegister(out)); break; + case Scalar::Float32x4: + case Scalar::Int32x4: MOZ_CRASH("SIMD load should be handled in their own function"); case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected type"); } } @@ -354,14 +354,135 @@ CodeGeneratorX86::memoryBarrier(MemoryBarrierBits barrier) masm.storeLoadFence(); } +template +void +CodeGeneratorX86::loadSimd(Scalar::Type type, unsigned numElems, T srcAddr, FloatRegister out) +{ + switch (type) { + case Scalar::Float32x4: { + switch (numElems) { + // In memory-to-register mode, movss zeroes out the high lanes. + case 1: masm.vmovssWithPatch(srcAddr, out); break; + // See comment above, which also applies to movsd. + case 2: masm.vmovsdWithPatch(srcAddr, out); break; + case 4: masm.vmovupsWithPatch(srcAddr, out); break; + default: MOZ_CRASH("unexpected size for partial load"); + } + break; + } + case Scalar::Int32x4: { + switch (numElems) { + // In memory-to-register mode, movd zeroes out the high lanes. + case 1: masm.vmovdWithPatch(srcAddr, out); break; + // See comment above, which also applies to movsd. + // TODO memory-to-xmm movq is encodable on x86 as well + case 2: masm.vmovsdWithPatch(srcAddr, out); break; + case 4: masm.vmovdquWithPatch(srcAddr, out); break; + default: MOZ_CRASH("unexpected size for partial load"); + } + break; + } + case Scalar::Int8: + case Scalar::Uint8: + case Scalar::Int16: + case Scalar::Uint16: + case Scalar::Int32: + case Scalar::Uint32: + case Scalar::Float32: + case Scalar::Float64: + case Scalar::Uint8Clamped: + case Scalar::MaxTypedArrayViewType: + MOZ_CRASH("should only handle SIMD types"); + } +} + +void +CodeGeneratorX86::emitSimdLoad(Scalar::Type type, unsigned numElems, const LAllocation *ptr, + FloatRegister out, bool needsBoundsCheck /* = false */, + Label *oobLabel /* = nullptr */) +{ + if (ptr->isConstant()) { + MOZ_ASSERT(!needsBoundsCheck); + + if (numElems == 3) { + MOZ_ASSERT(type == Scalar::Int32x4 || type == Scalar::Float32x4); + + // Load XY + emitSimdLoad(type, 2, ptr, out); + + // Load Z (W is zeroed) + // This add won't overflow, as we've checked that we have at least + // room for loading 4 elements during asm.js validation. + PatchedAbsoluteAddress srcAddr((void *) (ptr->toConstant()->toInt32() + 2 * sizeof(float))); + uint32_t before = masm.size(); + loadSimd(type, 1, srcAddr, ScratchSimdReg); + uint32_t after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, 1, type)); + + // Move ZW atop XY + masm.vmovlhps(ScratchSimdReg, out, out); + return; + } + + PatchedAbsoluteAddress srcAddr((void *) ptr->toConstant()->toInt32()); + uint32_t before = masm.size(); + loadSimd(type, numElems, srcAddr, out); + uint32_t after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, numElems, type)); + return; + } + + Register ptrReg = ToRegister(ptr); + uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck; + if (needsBoundsCheck) { + maybeCmpOffset = masm.cmp32WithPatch(ptrReg, Imm32(0)).offset(); + masm.j(Assembler::AboveOrEqual, oobLabel); // Throws RangeError + } + + uint32_t before = masm.size(); + if (numElems == 3) { + MOZ_ASSERT(type == Scalar::Int32x4 || type == Scalar::Float32x4); + + // Load XY + Address addr(ptrReg, 0); + before = masm.size(); + loadSimd(type, 2, addr, out); + uint32_t after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, 3, type, maybeCmpOffset)); + + // Load Z (W is zeroed) + // This is still in bounds, as we've checked with a manual bounds check + // or we had enough space for sure when removing the bounds check. + Address shiftedAddr(ptrReg, 2 * sizeof(float)); + before = after; + loadSimd(type, 1, shiftedAddr, ScratchSimdReg); + after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, 1, type)); + + // Move ZW atop XY + masm.vmovlhps(ScratchSimdReg, out, out); + return; + } + + Address addr(ptrReg, 0); + loadSimd(type, numElems, addr, out); + uint32_t after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, numElems, type, maybeCmpOffset)); +} + void CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) { const MAsmJSLoadHeap *mir = ins->mir(); - Scalar::Type vt = mir->accessType(); + Scalar::Type accessType = mir->accessType(); const LAllocation *ptr = ins->ptr(); const LDefinition *out = ins->output(); + if (Scalar::isSimdType(accessType)) { + return emitSimdLoad(accessType, mir->numSimdElems(), ptr, ToFloatRegister(out), + mir->needsBoundsCheck(), mir->outOfBoundsLabel()); + } + memoryBarrier(ins->mir()->barrierBefore()); if (ptr->isConstant()) { @@ -370,7 +491,7 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) // immediate in the instruction. This displacement will fixed up when the // base address is known during dynamic linking (AsmJSModule::initHeap). PatchedAbsoluteAddress srcAddr((void *) ptr->toConstant()->toInt32()); - loadAndNoteViewTypeElement(vt, srcAddr, out); + loadAndNoteViewTypeElement(accessType, srcAddr, out); memoryBarrier(ins->mir()->barrierAfter()); return; } @@ -379,28 +500,24 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) Address srcAddr(ptrReg, 0); if (!mir->needsBoundsCheck()) { - loadAndNoteViewTypeElement(vt, srcAddr, out); + loadAndNoteViewTypeElement(accessType, srcAddr, out); memoryBarrier(ins->mir()->barrierAfter()); return; } - OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr; + OutOfLineLoadTypedArrayOutOfBounds *ool = + new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), accessType); CodeOffsetLabel cmp = masm.cmp32WithPatch(ptrReg, Imm32(0)); - if (mir->outOfBoundsLabel()) { - masm.j(Assembler::AboveOrEqual, mir->outOfBoundsLabel()); // Throws RangeError - } else { - ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt); - addOutOfLineCode(ool, mir); - masm.j(Assembler::AboveOrEqual, ool->entry()); - } + addOutOfLineCode(ool, mir); + masm.j(Assembler::AboveOrEqual, ool->entry()); uint32_t before = masm.size(); - load(vt, srcAddr, out); + load(accessType, srcAddr, out); uint32_t after = masm.size(); if (ool) masm.bind(ool->rejoin()); memoryBarrier(ins->mir()->barrierAfter()); - masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset())); + masm.append(AsmJSHeapAccess(before, after, accessType, ToAnyRegister(out), cmp.offset())); } template @@ -417,8 +534,8 @@ CodeGeneratorX86::store(Scalar::Type vt, const LAllocation *value, const T &dstA case Scalar::Uint32: masm.movlWithPatch(ToRegister(value), dstAddr); break; case Scalar::Float32: masm.vmovssWithPatch(ToFloatRegister(value), dstAddr); break; case Scalar::Float64: masm.vmovsdWithPatch(ToFloatRegister(value), dstAddr); break; - case Scalar::Float32x4: masm.vmovupsWithPatch(ToFloatRegister(value), dstAddr); break; - case Scalar::Int32x4: masm.vmovdquWithPatch(ToFloatRegister(value), dstAddr); break; + case Scalar::Float32x4: + case Scalar::Int32x4: MOZ_CRASH("SIMD stores should be handled in emitSimdStore"); case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected type"); } } @@ -459,6 +576,120 @@ CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStati masm.bind(&rejoin); } +template +void +CodeGeneratorX86::storeSimd(Scalar::Type type, unsigned numElems, FloatRegister in, T destAddr) +{ + switch (type) { + case Scalar::Float32x4: { + switch (numElems) { + // In memory-to-register mode, movss zeroes out the high lanes. + case 1: masm.vmovssWithPatch(in, destAddr); break; + // See comment above, which also applies to movsd. + case 2: masm.vmovsdWithPatch(in, destAddr); break; + case 4: masm.vmovupsWithPatch(in, destAddr); break; + default: MOZ_CRASH("unexpected size for partial load"); + } + break; + } + case Scalar::Int32x4: { + switch (numElems) { + // In memory-to-register mode, movd zeroes destAddr the high lanes. + case 1: masm.vmovdWithPatch(in, destAddr); break; + // See comment above, which also applies to movsd. + // Cross-domain penalty here, as movq isn't encodable on x86. + case 2: masm.vmovsdWithPatch(in, destAddr); break; + case 4: masm.vmovdquWithPatch(in, destAddr); break; + default: MOZ_CRASH("unexpected size for partial load"); + } + break; + } + case Scalar::Int8: + case Scalar::Uint8: + case Scalar::Int16: + case Scalar::Uint16: + case Scalar::Int32: + case Scalar::Uint32: + case Scalar::Float32: + case Scalar::Float64: + case Scalar::Uint8Clamped: + case Scalar::MaxTypedArrayViewType: + MOZ_CRASH("should only handle SIMD types"); + } +} + +void +CodeGeneratorX86::emitSimdStore(Scalar::Type type, unsigned numElems, FloatRegister in, + const LAllocation *ptr, bool needsBoundsCheck /* = false */, + Label *oobLabel /* = nullptr */) +{ + if (ptr->isConstant()) { + MOZ_ASSERT(!needsBoundsCheck); + + if (numElems == 3) { + MOZ_ASSERT(type == Scalar::Int32x4 || type == Scalar::Float32x4); + + // Store XY + emitSimdStore(type, 2, in, ptr); + + masm.vmovhlps(in, ScratchSimdReg, ScratchSimdReg); + + // Store Z + // This add won't overflow, as we've checked that we have at least + // room for loading 4 elements during asm.js validation. + PatchedAbsoluteAddress dstAddr((void *) (ptr->toConstant()->toInt32() + 2 * sizeof(float))); + uint32_t before = masm.size(); + storeSimd(type, 1, ScratchSimdReg, dstAddr); + uint32_t after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, 1, type)); + return; + } + + PatchedAbsoluteAddress dstAddr((void *) ptr->toConstant()->toInt32()); + uint32_t before = masm.size(); + storeSimd(type, numElems, in, dstAddr); + uint32_t after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, 3, type)); + return; + } + + Register ptrReg = ToRegister(ptr); + uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck; + if (needsBoundsCheck) { + maybeCmpOffset = masm.cmp32WithPatch(ptrReg, Imm32(0)).offset(); + masm.j(Assembler::AboveOrEqual, oobLabel); // Throws RangeError + } + + uint32_t before = masm.size(); + if (numElems == 3) { + MOZ_ASSERT(type == Scalar::Int32x4 || type == Scalar::Float32x4); + + // Store XY + Address addr(ptrReg, 0); + before = masm.size(); + storeSimd(type, 2, in, addr); + uint32_t after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, 3, type, maybeCmpOffset)); + + masm.vmovhlps(in, ScratchSimdReg, ScratchSimdReg); + + // Store Z (W is zeroed) + // This is still in bounds, as we've checked with a manual bounds check + // or we had enough space for sure when removing the bounds check. + Address shiftedAddr(ptrReg, 2 * sizeof(float)); + before = masm.size(); + storeSimd(type, 1, ScratchSimdReg, shiftedAddr); + after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, 1, type)); + return; + } + + Address addr(ptrReg, 0); + storeSimd(type, numElems, in, addr); + uint32_t after = masm.size(); + masm.append(AsmJSHeapAccess(before, after, numElems, type, maybeCmpOffset)); +} + void CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) { @@ -467,6 +698,11 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) const LAllocation *value = ins->value(); const LAllocation *ptr = ins->ptr(); + if (Scalar::isSimdType(vt)) { + return emitSimdStore(vt, mir->numSimdElems(), ToFloatRegister(value), ptr, + mir->needsBoundsCheck(), mir->outOfBoundsLabel()); + } + memoryBarrier(ins->mir()->barrierBefore()); if (ptr->isConstant()) { @@ -491,11 +727,7 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) CodeOffsetLabel cmp = masm.cmp32WithPatch(ptrReg, Imm32(0)); Label rejoin; - - if (mir->outOfBoundsLabel()) - masm.j(Assembler::AboveOrEqual, mir->outOfBoundsLabel()); // Throws RangeError - else - masm.j(Assembler::AboveOrEqual, &rejoin); + masm.j(Assembler::AboveOrEqual, &rejoin); uint32_t before = masm.size(); store(vt, value, dstAddr); diff --git a/js/src/jit/x86/CodeGenerator-x86.h b/js/src/jit/x86/CodeGenerator-x86.h index 368a889d8da6..7b0a3d81befe 100644 --- a/js/src/jit/x86/CodeGenerator-x86.h +++ b/js/src/jit/x86/CodeGenerator-x86.h @@ -37,6 +37,17 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared template void store(Scalar::Type vt, const LAllocation *value, const T &dstAddr); + template + void loadSimd(Scalar::Type type, unsigned numElems, T srcAddr, FloatRegister out); + void emitSimdLoad(Scalar::Type type, unsigned numElems, const LAllocation *ptr, + FloatRegister out, bool needsBoundsCheck = false, Label *oobLabel = nullptr); + + template + void storeSimd(Scalar::Type type, unsigned numElems, FloatRegister in, T destAddr); + void emitSimdStore(Scalar::Type type, unsigned numElems, FloatRegister in, + const LAllocation *ptr, bool needsBoundsCheck = false, + Label *oobLabel = nullptr); + void memoryBarrier(MemoryBarrierBits barrier); public: diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 2292aa363eb9..7638d2c9bf27 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1467,6 +1467,27 @@ isSimdType(Type atype) { MOZ_CRASH("invalid scalar type"); } +static inline size_t +scalarByteSize(Type atype) { + switch (atype) { + case Int32x4: + case Float32x4: + return 4; + case Int8: + case Uint8: + case Uint8Clamped: + case Int16: + case Uint16: + case Int32: + case Uint32: + case Float32: + case Float64: + case MaxTypedArrayViewType: + break; + } + MOZ_CRASH("invalid simd type"); +} + } /* namespace Scalar */ } /* namespace js */ From 010594b8f62371b3e5a301e74f4104856653101c Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Thu, 5 Feb 2015 14:42:38 -0800 Subject: [PATCH 10/39] Bug 1124610 - Call PostHasTransparency for corrupt images that we treat as usable. r=jrmuizel --- image/src/Decoder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/image/src/Decoder.cpp b/image/src/Decoder.cpp index d09cc43c3246..751273f43137 100644 --- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -266,6 +266,10 @@ Decoder::CompleteDecode() if (!HasDecoderError() && GetCompleteFrameCount() > 0) { // We're usable, so do exactly what we should have when the decoder // completed. + + // Not writing to the entire frame may have left us transparent. + PostHasTransparency(); + if (mInFrame) { PostFrameStop(); } From 26cde7af39a76e0594f6196255e6157673bd148b Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 5 Feb 2015 12:53:56 -0800 Subject: [PATCH 11/39] Bug 1087728: Make JS callers of ios.newChannel call ios.newChannel2 in dom/apps (r=fabrice) --- dom/apps/AppsUtils.jsm | 11 ++++++++-- dom/apps/OfflineCacheInstaller.jsm | 29 +++++++++++++++++---------- dom/apps/TrustedHostedAppsUtils.jsm | 29 +++++++++++++++++++++------ dom/apps/Webapps.jsm | 31 +++++++++++++++++++++++++---- 4 files changed, 78 insertions(+), 22 deletions(-) diff --git a/dom/apps/AppsUtils.jsm b/dom/apps/AppsUtils.jsm index 4dce2950d25d..de7b830cc468 100644 --- a/dom/apps/AppsUtils.jsm +++ b/dom/apps/AppsUtils.jsm @@ -673,10 +673,17 @@ this.AppsUtils = { let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); file.initWithPath(aPath); - let channel = NetUtil.newChannel(file); + let channel = NetUtil.newChannel2(file, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); channel.contentType = "application/json"; - NetUtil.asyncFetch(channel, function(aStream, aResult) { + NetUtil.asyncFetch2(channel, function(aStream, aResult) { if (!Components.isSuccessCode(aResult)) { deferred.resolve(null); diff --git a/dom/apps/OfflineCacheInstaller.jsm b/dom/apps/OfflineCacheInstaller.jsm index 358b38349144..06c9fea56679 100644 --- a/dom/apps/OfflineCacheInstaller.jsm +++ b/dom/apps/OfflineCacheInstaller.jsm @@ -37,13 +37,11 @@ function debug(aMsg) { } -function enableOfflineCacheForApp(origin, appId) { - let principal = Services.scriptSecurityManager.getAppCodebasePrincipal( - origin, appId, false); - Services.perms.addFromPrincipal(principal, 'offline-app', +function enableOfflineCacheForApp(aPrincipal) { + Services.perms.addFromPrincipal(aPrincipal, 'offline-app', Ci.nsIPermissionManager.ALLOW_ACTION); // Prevent cache from being evicted: - Services.perms.addFromPrincipal(principal, 'pin-app', + Services.perms.addFromPrincipal(aPrincipal, 'pin-app', Ci.nsIPermissionManager.ALLOW_ACTION); } @@ -80,10 +78,18 @@ function storeCache(applicationCache, url, file, itemType) { }); } -function readFile(aFile, aCallback) { - let channel = NetUtil.newChannel(aFile); +function readFile(aFile, aPrincipal, aCallback) { + + let channel = NetUtil.newChannel2(aFile, + null, + null, + null, // aLoadingNode + aPrincipal, + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); channel.contentType = "plain/text"; - NetUtil.asyncFetch(channel, function(aStream, aResult) { + NetUtil.asyncFetch2(channel, function(aStream, aResult) { if (!Components.isSuccessCode(aResult)) { Cu.reportError("OfflineCacheInstaller: Could not read file " + aFile.path); if (aCallback) @@ -211,7 +217,10 @@ function installCache(app) { if (!cacheManifest.exists()) return; - enableOfflineCacheForApp(app.origin, app.localId); + let principal = Services.scriptSecurityManager.getAppCodebasePrincipal( + app.origin, app.localId, false); + + enableOfflineCacheForApp(principal); // Get the url for the manifest. let appcacheURL = app.appcache_path; @@ -223,7 +232,7 @@ function installCache(app) { let applicationCache = applicationCacheService.createApplicationCache(groupID); applicationCache.activate(); - readFile(cacheManifest, function readAppCache(content) { + readFile(cacheManifest, principal, function readAppCache(content) { let entries = parseAppCache(app, cacheManifest.path, content); entries.urls.forEach(function processCachedFile(url) { diff --git a/dom/apps/TrustedHostedAppsUtils.jsm b/dom/apps/TrustedHostedAppsUtils.jsm index 37cfbbe078b4..7b740431ef65 100644 --- a/dom/apps/TrustedHostedAppsUtils.jsm +++ b/dom/apps/TrustedHostedAppsUtils.jsm @@ -200,7 +200,17 @@ this.TrustedHostedAppsUtils = { throw "CERTDB_ERROR"; } - let mRequestChannel = NetUtil.newChannel(aApp.manifestURL) + let principal = Services.scriptSecurityManager.getAppCodebasePrincipal( + aApp.origin, aApp.localId, false); + + let mRequestChannel = NetUtil.newChannel2(aApp.manifestURL, + null, + null, + null, // aLoadingNode + principal, + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER) .QueryInterface(Ci.nsIHttpChannel); mRequestChannel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; mRequestChannel.notificationCallbacks = @@ -222,7 +232,14 @@ this.TrustedHostedAppsUtils = { return; } - let sRequestChannel = NetUtil.newChannel(signatureURL) + let sRequestChannel = NetUtil.newChannel2(signatureURL, + null, + null, + null, // aLoadingNode + principal, + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER) .QueryInterface(Ci.nsIHttpChannel); sRequestChannel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; sRequestChannel.notificationCallbacks = @@ -239,12 +256,12 @@ this.TrustedHostedAppsUtils = { Promise.all([ new Promise((resolve, reject) => { - NetUtil.asyncFetch(mRequestChannel, - getAsyncFetchCallback(resolve, reject)); + NetUtil.asyncFetch2(mRequestChannel, + getAsyncFetchCallback(resolve, reject)); }), new Promise((resolve, reject) => { - NetUtil.asyncFetch(sRequestChannel, - getAsyncFetchCallback(resolve, reject)); + NetUtil.asyncFetch2(sRequestChannel, + getAsyncFetchCallback(resolve, reject)); }) ]).then(([aManifestStream, aSignatureStream]) => { this._verifySignedFile(aManifestStream, aSignatureStream, certDb) diff --git a/dom/apps/Webapps.jsm b/dom/apps/Webapps.jsm index 82f39505b3df..0f8bf0142a57 100755 --- a/dom/apps/Webapps.jsm +++ b/dom/apps/Webapps.jsm @@ -3429,11 +3429,29 @@ this.DOMApplicationRegistry = { aNewApp) { let requestChannel; + let appURI = NetUtil.newURI(aNewApp.origin, null, null); + let principal = Services.scriptSecurityManager.getAppCodebasePrincipal( + appURI, aNewApp.localId, false); + if (aIsLocalFileInstall) { - requestChannel = NetUtil.newChannel(aFullPackagePath) + requestChannel = NetUtil.newChannel2(aFullPackagePath, + null, + null, + null, // aLoadingNode + principal, + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER) .QueryInterface(Ci.nsIFileChannel); } else { - requestChannel = NetUtil.newChannel(aFullPackagePath) + requestChannel = NetUtil.newChannel2(aFullPackagePath, + null, + null, + null, // aLoadingNode + principal, + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER) .QueryInterface(Ci.nsIHttpChannel); requestChannel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; } @@ -3529,7 +3547,7 @@ this.DOMApplicationRegistry = { let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); file.initWithPath(aFilePath); - NetUtil.asyncFetch(file, function(inputStream, status) { + NetUtil.asyncFetch2(file, function(inputStream, status) { if (!Components.isSuccessCode(status)) { debug("Error reading " + aFilePath + ": " + e); deferred.reject(); @@ -3556,7 +3574,12 @@ this.DOMApplicationRegistry = { debug("File hash computed: " + hash); deferred.resolve(hash); - }); + }, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); return deferred.promise; }, From f882eb6afef10bbfbf958148dee430cb8b5dea69 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 5 Feb 2015 12:53:45 -0800 Subject: [PATCH 12/39] Bug 1087728: Make JS callers of ios.newChannel call ios.newChannel2 in dom/contacts (r=fabrice) --- dom/contacts/fallback/ContactDB.jsm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dom/contacts/fallback/ContactDB.jsm b/dom/contacts/fallback/ContactDB.jsm index 5715fd4811fa..5d3799b28ee2 100644 --- a/dom/contacts/fallback/ContactDB.jsm +++ b/dom/contacts/fallback/ContactDB.jsm @@ -124,7 +124,14 @@ ContactDB.prototype = { } } - let chan = jsm.NetUtil.newChannel(contactsFile); + let chan = jsm.NetUtil.newChannel2(contactsFile, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); let stream = chan.open(); // Obtain a converter to read from a UTF-8 encoded input stream. let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] From f243f49914a6a782eaeb8392199bab3e7a63de24 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 5 Feb 2015 12:53:32 -0800 Subject: [PATCH 13/39] Bug 1087728: Make JS callers of ios.newChannel call ios.newChannel2 in dom/settings (r=fabrice) --- dom/settings/SettingsDB.jsm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dom/settings/SettingsDB.jsm b/dom/settings/SettingsDB.jsm index f82b820ae29c..e30273352db0 100644 --- a/dom/settings/SettingsDB.jsm +++ b/dom/settings/SettingsDB.jsm @@ -80,7 +80,14 @@ SettingsDB.prototype = { } } - let chan = NetUtil.newChannel(settingsFile); + let chan = NetUtil.newChannel2(settingsFile, + null, + null, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); let stream = chan.open(); // Obtain a converter to read from a UTF-8 encoded input stream. let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"] From 84e003dae2e0aec51a61d7dd5f6ede580381c580 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Thu, 5 Feb 2015 12:53:14 -0800 Subject: [PATCH 14/39] Bug 1087728: Make JS callers of ios.newChannel call ios.newChannel2 in dom/system (r=fabrice) --- dom/system/gonk/NetworkService.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dom/system/gonk/NetworkService.js b/dom/system/gonk/NetworkService.js index 15c9b08c9ab4..0789cb1a2aad 100644 --- a/dom/system/gonk/NetworkService.js +++ b/dom/system/gonk/NetworkService.js @@ -131,7 +131,7 @@ NetworkService.prototype = { return; } - NetUtil.asyncFetch(file, function(inputStream, status) { + NetUtil.asyncFetch2(file, function(inputStream, status) { let rxBytes = 0, txBytes = 0, now = Date.now(); @@ -154,7 +154,12 @@ NetworkService.prototype = { // netd always return success even interface doesn't exist. callback.networkStatsAvailable(true, rxBytes, txBytes, now); - }); + }, + null, // aLoadingNode + Services.scriptSecurityManager.getSystemPrincipal(), + null, // aTriggeringPrincipal + Ci.nsILoadInfo.SEC_NORMAL, + Ci.nsIContentPolicy.TYPE_OTHER); }, setNetworkInterfaceAlarm: function(networkName, threshold, callback) { From f6570a6b84a174efdd9e58769cbef06f5488037e Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Thu, 5 Feb 2015 15:07:25 -0800 Subject: [PATCH 15/39] Bug 1130192 - Make WebGL2 toggle pref a default pref. - r=kamidphish --- modules/libpref/init/all.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 440ea445fa70..793599e37af4 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3864,6 +3864,7 @@ pref("webgl.max-warnings-per-context", 32); pref("webgl.enable-draft-extensions", false); pref("webgl.enable-privileged-extensions", false); pref("webgl.bypass-shader-validation", false); +pref("webgl.enable-prototype-webgl2", false); #ifdef XP_WIN pref("webgl.angle.try-d3d11", true); pref("webgl.angle.force-d3d11", false); From 892f49c42093169d55808e18ca0d2ae49de2f2c0 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Thu, 5 Feb 2015 20:44:25 -0500 Subject: [PATCH 16/39] bug 1102923 - test_spdy backend separate push body and err handlers r=test-only --- testing/xpcshell/moz-spdy/moz-spdy.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/testing/xpcshell/moz-spdy/moz-spdy.js b/testing/xpcshell/moz-spdy/moz-spdy.js index 6b5f5a43b07b..e7ece7051bda 100644 --- a/testing/xpcshell/moz-spdy/moz-spdy.js +++ b/testing/xpcshell/moz-spdy/moz-spdy.js @@ -132,26 +132,22 @@ function handleRequest(req, res) { res.setHeader("X-Received-Test-Header", val); } } else if (u.pathname == "/push") { - res.push('/push.js', + var stream = res.push('/push.js', { 'content-type': 'application/javascript', 'pushed' : 'yes', 'content-length' : 11, - 'X-Connection-Spdy': 'yes'}, - function(err, stream) { - if (err) return; - stream.end('// comments'); - }).on('error', function(){}); + 'X-Connection-Spdy': 'yes'}); + stream.on('error', function(){}); + stream.end('// comments'); content = '