Merge inbound to m-c a=merge

This commit is contained in:
Wes Kocher 2014-11-21 16:35:57 -08:00
commit 1b666e0294
133 changed files with 4695 additions and 685 deletions

View File

@ -1555,6 +1555,9 @@ nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
if (aContext->IsList() &&
aContext->GetContent() == aContent->GetParent()) {
newAcc = new HTMLLIAccessible(aContent, document);
} else {
// Otherwise create a generic text accessible to avoid text jamming.
newAcc = new HyperTextAccessibleWrap(aContent, document);
}
break;
case eHTMLSelectListType:

View File

@ -34,10 +34,15 @@
accTree = { // div@role="list"
role: ROLE_LIST,
children: [
{ // li text leaf
role: ROLE_TEXT_LEAF,
name: "item1",
children: [ ]
{ // li
role: ROLE_PARAGRAPH,
children: [
{ // li text leaf
role: ROLE_TEXT_LEAF,
name: "item1",
children: [ ]
}
]
},
{ // li@role="listitem"
role: ROLE_LISTITEM,

View File

@ -56,6 +56,15 @@
] };
testAccessibleTree("tblfocusable_cnt", tree);
// Presentation list, expose generic accesisble for list items.
tree =
{ SECTION: [ // container
{ PARAGRAPH: [ // li generic accessible
{ TEXT_LEAF: [ ] } // li text
] }
] };
testAccessibleTree("list_cnt", tree);
// Has ARIA globals or referred by ARIA relationship.
tree =
{ SECTION: [ // container
@ -113,6 +122,12 @@
</table>
</div>
<div id="list_cnt">
<ul role="presentation">
<li>item</li>
</ul>
</div>
<div id="airaglobalprop_cnt">
<label role="presentation" aria-owns="ariaowned">has aria-owns</label>
<label role="presentation" id="ariaowned">referred by aria-owns</label>

View File

@ -80,15 +80,15 @@
ok(!isAccessible("presentation_ul"),
"presentational ul shouldn't be accessible");
ok(!isAccessible("item_in_presentation_ul"),
"li in presentational ul shouldn't be accessible");
ok(!isAccessible("styleditem_in_presentation_ul"),
"list styled span in presentational ul shouldn't be accessible");
ok(isAccessible("item_in_presentation_ul"),
"li in presentational ul should have generic accessible");
ok(isAccessible("styleditem_in_presentation_ul"),
"list styled span in presentational ul should have generic accessible");
ok(!isAccessible("presentation_ol"),
"presentational ol shouldn't be accessible");
ok(!isAccessible("item_in_presentation_ol"),
"li in presentational ol shouldn't be accessible");
ok(isAccessible("item_in_presentation_ol"),
"li in presentational ol should have generic accessible");
ok(!isAccessible("presentation_dl"),
"presentational dl shouldn't be accessible");

View File

@ -181,7 +181,6 @@ class Automation(object):
"log",
"runApp",
"Process",
"addCommonOptions",
"initializeProfile",
"DIST_BIN",
"DEFAULT_APP",
@ -385,15 +384,6 @@ class Automation(object):
proxy=proxy)
return profile
def addCommonOptions(self, parser):
"Adds command-line options which are common to mochitest and reftest."
parser.add_option("--setpref",
action = "append", type = "string",
default = [],
dest = "extraPrefs", metavar = "PREF=VALUE",
help = "defines an extra user preference")
def fillCertificateDB(self, profileDir, certPath, utilityPath, xrePath):
pwfilePath = os.path.join(profileDir, ".crtdbpw")
pwfile = open(pwfilePath, "w")

View File

@ -18,7 +18,6 @@ import mozinfo
__all__ = [
"ZipFileReader",
"addCommonOptions",
"dumpLeakLog",
"processLeakLog",
'systemMemory',
@ -140,30 +139,6 @@ def printstatus(status, name = ""):
# This is probably a can't-happen condition on Unix, but let's be defensive
print "TEST-INFO | %s: undecodable exit status %04x\n" % (name, status)
def addCommonOptions(parser, defaults={}):
parser.add_option("--xre-path",
action = "store", type = "string", dest = "xrePath",
# individual scripts will set a sane default
default = None,
help = "absolute path to directory containing XRE (probably xulrunner)")
if 'SYMBOLS_PATH' not in defaults:
defaults['SYMBOLS_PATH'] = None
parser.add_option("--symbols-path",
action = "store", type = "string", dest = "symbolsPath",
default = defaults['SYMBOLS_PATH'],
help = "absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols")
parser.add_option("--debugger",
action = "store", dest = "debugger",
help = "use the given debugger to launch the application")
parser.add_option("--debugger-args",
action = "store", dest = "debuggerArgs",
help = "pass the given args to the debugger _before_ "
"the application on the command line")
parser.add_option("--debugger-interactive",
action = "store_true", dest = "debuggerInteractive",
help = "prevents the test harness from redirecting "
"stdout and stderr for interactive debuggers")
def dumpLeakLog(leakLogFile, filter = False):
"""Process the leak log, without parsing it.

View File

@ -138,6 +138,7 @@
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/dom/WindowBinding.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/VRDevice.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -3073,7 +3074,7 @@ GetFullScreenError(nsIDocument* aDoc)
}
void
Element::MozRequestFullScreen()
Element::MozRequestFullScreen(const RequestFullscreenOptions& aOptions)
{
// Only grant full-screen requests if this is called from inside a trusted
// event handler (i.e. inside an event handler for a user initiated event).
@ -3097,7 +3098,12 @@ Element::MozRequestFullScreen()
return;
}
OwnerDoc()->AsyncRequestFullScreen(this);
FullScreenOptions opts;
if (aOptions.mVrDisplay) {
opts.mVRHMDDevice = aOptions.mVrDisplay->GetHMD();
}
OwnerDoc()->AsyncRequestFullScreen(this, opts);
return;
}

View File

@ -34,6 +34,7 @@
#include "mozilla/EventForwards.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/WindowBinding.h"
#include "mozilla/dom/ElementBinding.h"
#include "Units.h"
class nsIDOMEventListener;
@ -724,7 +725,7 @@ public:
nsIPresShell::SetCapturingContent(nullptr, 0);
}
}
void MozRequestFullScreen();
void MozRequestFullScreen(const RequestFullscreenOptions& aOptions);
void MozRequestPointerLock();
Attr* GetAttributeNode(const nsAString& aName);
already_AddRefed<Attr> SetAttributeNode(Attr& aNewAttr,
@ -1768,7 +1769,7 @@ NS_IMETHOD ReleaseCapture(void) MOZ_FINAL \
} \
NS_IMETHOD MozRequestFullScreen(void) MOZ_FINAL \
{ \
Element::MozRequestFullScreen(); \
Element::MozRequestFullScreen(mozilla::dom::RequestFullscreenOptions()); \
return NS_OK; \
} \
NS_IMETHOD MozRequestPointerLock(void) MOZ_FINAL \

View File

@ -40,6 +40,7 @@
#include "mozilla/dom/Telephony.h"
#include "mozilla/dom/Voicemail.h"
#include "mozilla/dom/TVManager.h"
#include "mozilla/dom/VRDevice.h"
#include "mozilla/Hal.h"
#include "nsISiteSpecificUserAgent.h"
#include "mozilla/ClearOnShutdown.h"
@ -1722,6 +1723,32 @@ Navigator::GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads,
}
#endif
already_AddRefed<Promise>
Navigator::GetVRDevices(ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
nsRefPtr<Promise> p = Promise::Create(go, aRv);
if (aRv.Failed()) {
return nullptr;
}
nsGlobalWindow* win = static_cast<nsGlobalWindow*>(mWindow.get());
nsTArray<nsRefPtr<VRDevice>> vrDevs;
if (!win->GetVRDevices(vrDevs)) {
p->MaybeReject(NS_ERROR_FAILURE);
} else {
p->MaybeResolve(vrDevs);
}
return p.forget();
}
//*****************************************************************************
// Navigator::nsIMozNavigatorNetwork
//*****************************************************************************

View File

@ -246,6 +246,7 @@ public:
#ifdef MOZ_GAMEPAD
void GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
#endif // MOZ_GAMEPAD
already_AddRefed<Promise> GetVRDevices(ErrorResult& aRv);
#ifdef MOZ_B2G_FM
FMRadio* GetMozFMRadio(ErrorResult& aRv);
#endif

View File

@ -10730,13 +10730,13 @@ nsIDocument::MozCancelFullScreen()
// Element::UnbindFromTree().
class nsSetWindowFullScreen : public nsRunnable {
public:
nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue)
: mDoc(aDoc), mValue(aValue) {}
nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue, gfx::VRHMDInfo* aHMD = nullptr)
: mDoc(aDoc), mValue(aValue), mHMD(aHMD) {}
NS_IMETHOD Run()
{
if (mDoc->GetWindow()) {
mDoc->GetWindow()->SetFullScreenInternal(mValue, false);
mDoc->GetWindow()->SetFullScreenInternal(mValue, false, mHMD);
}
return NS_OK;
}
@ -10744,6 +10744,7 @@ public:
private:
nsCOMPtr<nsIDocument> mDoc;
bool mValue;
nsRefPtr<gfx::VRHMDInfo> mHMD;
};
static nsIDocument*
@ -10763,7 +10764,7 @@ GetFullscreenRootDocument(nsIDocument* aDoc)
}
static void
SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
SetWindowFullScreen(nsIDocument* aDoc, bool aValue, gfx::VRHMDInfo *aVRHMD = nullptr)
{
// Maintain list of fullscreen root documents.
nsCOMPtr<nsIDocument> root = GetFullscreenRootDocument(aDoc);
@ -10773,7 +10774,7 @@ SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
FullscreenRoots::Remove(root);
}
if (!nsContentUtils::IsFullscreenApiContentOnly()) {
nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue));
nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue, aVRHMD));
}
}
@ -11093,12 +11094,13 @@ nsDocument::IsFullScreenDoc()
class nsCallRequestFullScreen : public nsRunnable
{
public:
explicit nsCallRequestFullScreen(Element* aElement)
explicit nsCallRequestFullScreen(Element* aElement, FullScreenOptions& aOptions)
: mElement(aElement),
mDoc(aElement->OwnerDoc()),
mWasCallerChrome(nsContentUtils::IsCallerChrome()),
mHadRequestPending(static_cast<nsDocument*>(mDoc.get())->
mAsyncFullscreenPending)
mAsyncFullscreenPending),
mOptions(aOptions)
{
static_cast<nsDocument*>(mDoc.get())->
mAsyncFullscreenPending = true;
@ -11110,6 +11112,7 @@ public:
mAsyncFullscreenPending = mHadRequestPending;
nsDocument* doc = static_cast<nsDocument*>(mDoc.get());
doc->RequestFullScreen(mElement,
mOptions,
mWasCallerChrome,
/* aNotifyOnOriginChange */ true);
return NS_OK;
@ -11119,10 +11122,12 @@ public:
nsCOMPtr<nsIDocument> mDoc;
bool mWasCallerChrome;
bool mHadRequestPending;
FullScreenOptions mOptions;
};
void
nsDocument::AsyncRequestFullScreen(Element* aElement)
nsDocument::AsyncRequestFullScreen(Element* aElement,
FullScreenOptions& aOptions)
{
NS_ASSERTION(aElement,
"Must pass non-null element to nsDocument::AsyncRequestFullScreen");
@ -11130,7 +11135,7 @@ nsDocument::AsyncRequestFullScreen(Element* aElement)
return;
}
// Request full-screen asynchronously.
nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement));
nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement, aOptions));
NS_DispatchToCurrentThread(event);
}
@ -11198,6 +11203,9 @@ nsDocument::CleanupFullscreenState()
Element* top = FullScreenStackTop();
NS_ASSERTION(top, "Should have a top when full-screen stack isn't empty");
if (top) {
// Remove any VR state properties
top->DeleteProperty(nsGkAtoms::vr_state);
EventStateManager::SetFullScreenState(top, false);
}
mFullScreenStack.Clear();
@ -11234,8 +11242,12 @@ nsDocument::FullScreenStackPop()
return;
}
// Remove styles from existing top element.
Element* top = FullScreenStackTop();
// Remove any VR state properties
top->DeleteProperty(nsGkAtoms::vr_state);
// Remove styles from existing top element.
EventStateManager::SetFullScreenState(top, false);
// Remove top element. Note the remaining top element in the stack
@ -11323,7 +11335,9 @@ nsresult nsDocument::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement,
// If the frame element is already the fullscreen element in this document,
// this has no effect.
nsCOMPtr<nsIContent> content(do_QueryInterface(aFrameElement));
FullScreenOptions opts;
RequestFullScreen(content->AsElement(),
opts,
/* aWasCallerChrome */ false,
/* aNotifyOnOriginChange */ false);
@ -11349,8 +11363,17 @@ nsresult nsDocument::RemoteFrameFullscreenReverted()
return NS_OK;
}
static void
ReleaseHMDInfoRef(void *, nsIAtom*, void *aPropertyValue, void *)
{
if (aPropertyValue) {
static_cast<gfx::VRHMDInfo*>(aPropertyValue)->Release();
}
}
void
nsDocument::RequestFullScreen(Element* aElement,
FullScreenOptions& aOptions,
bool aWasCallerChrome,
bool aNotifyOnOriginChange)
{
@ -11439,6 +11462,14 @@ nsDocument::RequestFullScreen(Element* aElement,
UnlockPointer();
}
// Process options -- in this case, just HMD
if (aOptions.mVRHMDDevice) {
nsRefPtr<gfx::VRHMDInfo> hmdRef = aOptions.mVRHMDDevice;
aElement->SetProperty(nsGkAtoms::vr_state, hmdRef.forget().take(),
ReleaseHMDInfoRef,
true);
}
// Set the full-screen element. This sets the full-screen style on the
// element, and the full-screen-ancestor styles on ancestors of the element
// in this document.
@ -11543,7 +11574,7 @@ nsDocument::RequestFullScreen(Element* aElement,
// modes. Also note that nsGlobalWindow::SetFullScreen() (which
// SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
// and does not operate on the a per-nsIDOMWindow basis.
SetWindowFullScreen(this, true);
SetWindowFullScreen(this, true, aOptions.mVRHMDDevice);
}
NS_IMETHODIMP

View File

@ -1146,7 +1146,8 @@ public:
virtual Element* FindImageMap(const nsAString& aNormalizedMapName) MOZ_OVERRIDE;
virtual Element* GetFullScreenElement() MOZ_OVERRIDE;
virtual void AsyncRequestFullScreen(Element* aElement) MOZ_OVERRIDE;
virtual void AsyncRequestFullScreen(Element* aElement,
mozilla::dom::FullScreenOptions& aOptions) MOZ_OVERRIDE;
virtual void RestorePreviousFullScreenState() MOZ_OVERRIDE;
virtual bool IsFullscreenLeaf() MOZ_OVERRIDE;
virtual bool IsFullScreenDoc() MOZ_OVERRIDE;
@ -1197,6 +1198,7 @@ public:
// need to send the notification with the origin of the document which
// originally requested fullscreen, not *this* document's origin.
void RequestFullScreen(Element* aElement,
mozilla::dom::FullScreenOptions& aOptions,
bool aWasCallerChrome,
bool aNotifyOnOriginChange);

View File

@ -2325,3 +2325,5 @@ GK_ATOM(onresume, "onresume")
GK_ATOM(onmark, "onmark")
GK_ATOM(onboundary, "onboundary")
#endif
GK_ATOM(vr_state, "vr-state")

View File

@ -190,6 +190,8 @@
#include "mozilla/dom/GamepadService.h"
#endif
#include "mozilla/dom/VRDevice.h"
#include "nsRefreshDriver.h"
#include "mozilla/dom/SelectionChangeEvent.h"
@ -1134,7 +1136,8 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
mCleanedUp(false),
mDialogAbuseCount(0),
mAreDialogsEnabled(true),
mCanSkipCCGeneration(0)
mCanSkipCCGeneration(0),
mVRDevicesInitialized(false)
{
nsLayoutStatics::AddRef();
@ -1771,6 +1774,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDevices)
// Traverse stuff from nsPIDOMWindow
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
@ -1832,6 +1837,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
#endif
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDevices)
// Unlink stuff from nsPIDOMWindow
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
@ -5923,7 +5930,7 @@ nsGlobalWindow::SetFullScreen(bool aFullScreen)
}
nsresult
nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust)
nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust, gfx::VRHMDInfo* aHMD)
{
MOZ_ASSERT(IsOuterWindow());
@ -5945,7 +5952,7 @@ nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust)
if (!window)
return NS_ERROR_FAILURE;
if (rootItem != mDocShell)
return window->SetFullScreenInternal(aFullScreen, aRequireTrust);
return window->SetFullScreenInternal(aFullScreen, aRequireTrust, aHMD);
// make sure we don't try to set full screen on a non-chrome window,
// which might happen in embedding world
@ -5980,8 +5987,13 @@ nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust)
// want the content to fill the entire client area of the emulator window.
if (!Preferences::GetBool("full-screen-api.ignore-widgets", false)) {
nsCOMPtr<nsIWidget> widget = GetMainWidget();
if (widget)
widget->MakeFullScreen(aFullScreen);
if (widget) {
nsCOMPtr<nsIScreen> screen;
if (aHMD) {
screen = aHMD->GetScreen();
}
widget->MakeFullScreen(aFullScreen, screen);
}
}
if (!mFullScreen) {
@ -13416,7 +13428,26 @@ nsGlobalWindow::SyncGamepadState()
mGamepads.EnumerateRead(EnumGamepadsForSync, nullptr);
}
}
#endif
#endif // MOZ_GAMEPAD
bool
nsGlobalWindow::GetVRDevices(nsTArray<nsRefPtr<mozilla::dom::VRDevice>>& aDevices)
{
FORWARD_TO_INNER(GetVRDevices, (aDevices), false);
if (!mVRDevicesInitialized) {
bool ok = mozilla::dom::VRDevice::CreateAllKnownVRDevices(ToSupports(this), mVRDevices);
if (!ok) {
mVRDevices.Clear();
return false;
}
}
mVRDevicesInitialized = true;
aDevices = mVRDevices;
return true;
}
// nsGlobalChromeWindow implementation
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)

View File

@ -104,6 +104,7 @@ class Console;
class External;
class Function;
class Gamepad;
class VRDevice;
class MediaQueryList;
class MozSelfSupport;
class Navigator;
@ -118,6 +119,9 @@ namespace indexedDB {
class IDBFactory;
} // namespace indexedDB
} // namespace dom
namespace gfx {
class VRHMDInfo;
} // namespace gfx
} // namespace mozilla
extern nsresult
@ -464,7 +468,8 @@ public:
virtual void RefreshCompartmentPrincipal();
// Outer windows only.
virtual nsresult SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust);
virtual nsresult SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust,
mozilla::gfx::VRHMDInfo *aHMD = nullptr);
bool FullScreen() const;
// Inner windows only.
@ -734,6 +739,8 @@ public:
void EnableGamepadUpdates();
void DisableGamepadUpdates();
// Get the VR devices for this window, initializing if necessary
bool GetVRDevices(nsTArray<nsRefPtr<mozilla::dom::VRDevice>>& aDevices);
#define EVENT(name_, id_, type_, struct_) \
mozilla::dom::EventHandlerNonNull* GetOn##name_() \
@ -1630,6 +1637,13 @@ protected:
// This is the CC generation the last time we called CanSkip.
uint32_t mCanSkipCCGeneration;
// Did VR get initialized for this window?
bool mVRDevicesInitialized;
// The VRDevies for this window
nsTArray<nsRefPtr<mozilla::dom::VRDevice>> mVRDevices;
// Any attached HMD when fullscreen
nsRefPtr<mozilla::gfx::VRHMDInfo> mVRHMDInfo;
friend class nsDOMScriptableHelper;
friend class nsDOMWindowUtils;
friend class PostMessageEvent;

View File

@ -27,6 +27,7 @@
#include "nsExpirationTracker.h"
#include "nsClassHashtable.h"
#include "prclist.h"
#include "gfxVR.h"
class imgIRequest;
class nsAString;
@ -135,6 +136,12 @@ template<typename> class Sequence;
template<typename, typename> class CallbackObjectHolder;
typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
struct FullScreenOptions {
FullScreenOptions() { }
nsRefPtr<gfx::VRHMDInfo> mVRHMDDevice;
};
} // namespace dom
} // namespace mozilla
@ -164,6 +171,7 @@ already_AddRefed<nsContentList>
NS_GetContentList(nsINode* aRootNode,
int32_t aMatchNameSpaceId,
const nsAString& aTagname);
//----------------------------------------------------------------------
// Document interface. This is implemented by all document objects in
@ -1064,7 +1072,8 @@ public:
* the <iframe> or <browser> that contains this document is also mode
* fullscreen. This happens recursively in all ancestor documents.
*/
virtual void AsyncRequestFullScreen(Element* aElement) = 0;
virtual void AsyncRequestFullScreen(Element* aElement,
mozilla::dom::FullScreenOptions& aOptions) = 0;
/**
* Called when a frame in a child process has entered fullscreen or when a

View File

@ -38,6 +38,9 @@ namespace dom {
class AudioContext;
class Element;
}
namespace gfx {
class VRHMDInfo;
}
}
// Popup control state enum. The values in this enum must go from most
@ -60,8 +63,8 @@ enum UIStateChangeType
};
#define NS_PIDOMWINDOW_IID \
{ 0x71412748, 0x6368, 0x4332, \
{ 0x82, 0x66, 0xff, 0xaa, 0x19, 0xda, 0x09, 0x7c } }
{ 0x19fb3019, 0x7b5d, 0x4235, \
{ 0xa9, 0x59, 0xa2, 0x31, 0xa2, 0xe7, 0x94, 0x79 } }
class nsPIDOMWindow : public nsIDOMWindowInternal
{
@ -460,9 +463,13 @@ public:
* otherwise exits fullscreen. If aRequireTrust is true, this method only
* changes window state in a context trusted for write.
*
* If aHMD is not null, the window is made full screen on the given VR HMD
* device instead of its currrent display.
*
* Outer windows only.
*/
virtual nsresult SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust) = 0;
virtual nsresult SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust,
mozilla::gfx::VRHMDInfo *aHMD = nullptr) = 0;
/**
* Call this to check whether some node (this window, its document,

View File

@ -1272,6 +1272,14 @@ DOMInterfaces = {
'wrapperCache': False,
}],
'VRFieldOfViewReadOnly': {
'concrete': False
},
'VRDevice': {
'concrete': False
},
'VTTCue': {
'nativeType': 'mozilla::dom::TextTrackCue'
},

View File

@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Embedded iframe</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="">
<p id="display"></p>
<h1>Top</h1>
<input id="input" style="display: block;">
<pre id="test">
<div id="content" style="height: 2000px;"></div>
<h1>Bottom</h1>
</body>
</html>

View File

@ -176,3 +176,6 @@ support-files =
bug989198_embedded.html
bug989198_helper.js
skip-if = buildapp == 'b2g' || e10s
[test_bug1096146.html]
support-files =
bug1096146_embedded.html

View File

@ -0,0 +1,170 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1096146
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1096146</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="runTests();">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1096146">Mozilla Bug 1096146</a>
<div id="content" style="display: none;">
</div>
<pre id="test">
<script type="application/javascript">
const kKeydownEvent = 0x1;
const kScrollEvent = 0x2;
var gCurrentTest = 0;
var kTests = [
{
description: "no preventDefault at 'mozbrowserbeforekeydown'",
expectedEvents: kKeydownEvent | kScrollEvent,
resultEvents: 0x0,
doPreventDefault: false
},
{
description: "do preventDefault at 'mozbrowserbeforekeydown'",
expectedEvents: 0x0,
resultEvents: 0x0,
doPreventDefault: true
}
]
function frameScript()
{
function handler(e) {
sendSyncMessage("forwardevent", { type: e.type });
}
addEventListener('keydown', handler);
addEventListener('scroll', handler);
}
function testDefaultAction()
{
synthesizeKey('VK_END', {}, document.getElementById("embedded").contentWindow);
setTimeout(function () {
is(kTests[gCurrentTest].expectedEvents,
kTests[gCurrentTest].resultEvents,
"verify result");
runTests();
}, 500);
}
function prepareTest()
{
var handler;
if (kTests[gCurrentTest].doPreventDefault) {
handler = preventDefaultHandler;
} else {
handler = noPreventDefaultHandler;
}
window.addEventListener("mozbrowserbeforekeydown", handler);
var iframe = document.createElement("iframe");
iframe.id = "embedded";
iframe.src = "bug1096146_embedded.html";
iframe.setAttribute("remote", "false");
SpecialPowers.wrap(iframe).mozbrowser = true;
iframe.addEventListener("mozbrowserloadend", function onloadend() {
iframe.removeEventListener("mozbrowserloadend", onloadend);
iframe.focus();
var mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
mm.addMessageListener("forwardevent", function(msg) {
var value = 0;
switch(msg.json.type) {
case "scroll":
value = kScrollEvent;
break;
case "keydown":
value = kKeydownEvent;
break;
default:
ok(false, "unexpected event");
}
kTests[gCurrentTest].resultEvents =
kTests[gCurrentTest].resultEvents | value;
});
mm.loadFrameScript("data:,(" + frameScript.toString() + ")();", false);
runTests();
return;
});
document.body.appendChild(iframe);
}
function preventDefaultHandler(evt)
{
ok(true, "receive " + evt.type + " and do preventDefault.");
evt.preventDefault();
}
function noPreventDefaultHandler(evt)
{
ok(true, "receive " + evt.type + ".");
}
function teardownHandler()
{
var handler;
if (kTests[gCurrentTest].doPreventDefault) {
handler = preventDefaultHandler;
} else {
handler = noPreventDefaultHandler;
}
window.removeEventListener("mozbrowserbeforekeydown", handler);
runTests();
}
var tests = [
function addPermissions() {
SpecialPowers.pushPermissions(
[{ type: "before-after-keyboard-event", allow: true, context: document },
{ type: "browser", allow: true, context: document }],
runTests);
},
function addPreferences() {
SpecialPowers.pushPrefEnv(
{ "set": [["dom.beforeAfterKeyboardEvent.enabled", true],
["dom.mozBrowserFramesEnabled", true],
["dom.ipc.tabs.disabled", false]] },
runTests);
},
prepareTest,
testDefaultAction,
teardownHandler,
function() {
gCurrentTest++;
runTests();
},
prepareTest,
testDefaultAction,
teardownHandler
];
function runTests()
{
if (!tests.length) {
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}
</script>
</pre>
</body>
</html>

View File

@ -5347,6 +5347,13 @@ public:
void
CloseOnOwningThread();
void
AssertInvalidatedOnMainThread() const
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInvalidatedOnMainThread);
}
NS_DECL_THREADSAFE_ISUPPORTS
private:
@ -9514,7 +9521,9 @@ QuotaClient::AbortTransactionsForStorage(nsIOfflineStorage* aStorage)
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aStorage);
MOZ_ASSERT(aStorage->GetClient() == this);
MOZ_ASSERT(aStorage->IsClosed());
static_cast<DatabaseOfflineStorage*>(aStorage)->
AssertInvalidatedOnMainThread();
// Nothing to do here, calling DatabaseOfflineStorage::Close() should have
// aborted any transactions already.
@ -9909,8 +9918,6 @@ DatabaseOfflineStorage::InvalidateOnMainThread()
nsCOMPtr<nsIEventTarget> owningThread = mOwningThread;
MOZ_ASSERT(owningThread);
CloseOnMainThread();
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(owningThread->Dispatch(runnable,
NS_DISPATCH_NORMAL)));
}

View File

@ -750,6 +750,39 @@ private:
}
};
class TabChild::DelayedDeleteRunnable MOZ_FINAL
: public nsRunnable
{
nsRefPtr<TabChild> mTabChild;
public:
DelayedDeleteRunnable(TabChild* aTabChild)
: mTabChild(aTabChild)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTabChild);
}
private:
~DelayedDeleteRunnable()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mTabChild);
}
NS_IMETHOD
Run()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mTabChild);
unused << PBrowserChild::Send__delete__(mTabChild);
mTabChild = nullptr;
return NS_OK;
}
};
StaticRefPtr<TabChild> sPreallocatedTab;
/*static*/
@ -2748,7 +2781,12 @@ TabChild::RecvDestroy()
// XXX what other code in ~TabChild() should we be running here?
DestroyWindow();
return Send__delete__(this);
// Bounce through the event loop once to allow any delayed teardown runnables
// that were just generated to have a chance to run.
nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(deleteRunnable)));
return true;
}
bool

View File

@ -583,6 +583,7 @@ private:
class CachedFileDescriptorInfo;
class CachedFileDescriptorCallbackRunnable;
class DelayedDeleteRunnable;
TextureFactoryIdentifier mTextureFactoryIdentifier;
nsCOMPtr<nsIWebNavigation> mWebNav;

View File

@ -93,7 +93,7 @@ public:
nsRefPtr<TrackChange> runnable =
new TrackChange(this, aID, aTrackOffset, aTrackEvents,
aQueuedMedia.GetType());
NS_DispatchToMainThread(runnable);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
}
}

View File

@ -1136,7 +1136,7 @@ MediaStreamGraphImpl::PlayVideo(MediaStream* aStream)
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(output, &VideoFrameContainer::Invalidate);
NS_DispatchToMainThread(event);
DispatchToMainThreadAfterStreamStateUpdate(event.forget());
}
if (!aStream->mNotifiedFinished) {
aStream->mLastPlayedVideoFrame = *frame;

View File

@ -81,8 +81,8 @@ TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) :
mappedTracksFinished.AppendElement(true);
mappedTracksWithMatchingInputTracks.AppendElement(false);
}
bool allFinished = true;
bool allHaveCurrentData = true;
bool allFinished = !mInputs.IsEmpty();
bool allHaveCurrentData = !mInputs.IsEmpty();
for (uint32_t i = 0; i < mInputs.Length(); ++i) {
MediaStream* stream = mInputs[i]->GetSource();
if (!stream->IsFinishedOnGraphThread()) {

View File

@ -105,6 +105,7 @@ DIRS += [
'xul',
'resourcestats',
'manifest',
'vr',
]
if CONFIG['OS_ARCH'] == 'WINNT':

View File

@ -441,6 +441,8 @@ var interfaceNamesInGlobalScope =
{name: "Headers", pref: "dom.fetch.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"History",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "HMDVRDevice", pref: "dom.vr.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"HTMLAllCollection",
// IMPORTANT: Do not change this list without review from a DOM peer!
@ -879,6 +881,8 @@ var interfaceNamesInGlobalScope =
"PopupBlockedEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "PopupBoxObject", xbl: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "PositionSensorVRDevice", pref: "dom.vr.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"ProcessingInstruction",
// IMPORTANT: Do not change this list without review from a DOM peer!
@ -1291,6 +1295,14 @@ var interfaceNamesInGlobalScope =
{name: "VideoPlaybackQuality", pref: "media.mediasource.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"VideoStreamTrack",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "VRDevice", pref: "dom.vr.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "VRPositionState", pref: "dom.vr.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "VRFieldOfView", pref: "dom.vr.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "VRFieldOfViewReadOnly", pref: "dom.vr.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"VTTCue",
// IMPORTANT: Do not change this list without review from a DOM peer!

322
dom/vr/VRDevice.cpp Normal file
View File

@ -0,0 +1,322 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsWrapperCache.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/VRDeviceBinding.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/VRDevice.h"
#include "gfxVR.h"
#include "nsIFrame.h"
using namespace mozilla::gfx;
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfView, mParent)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFieldOfView, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFieldOfView, Release)
already_AddRefed<VRFieldOfView>
VRFieldOfView::Constructor(const GlobalObject& aGlobal, const VRFieldOfViewInit& aParams,
ErrorResult& aRV)
{
nsRefPtr<VRFieldOfView> obj =
new VRFieldOfView(aGlobal.GetAsSupports(),
aParams.mUpDegrees, aParams.mRightDegrees,
aParams.mDownDegrees, aParams.mLeftDegrees);
return obj.forget();
}
already_AddRefed<VRFieldOfView>
VRFieldOfView::Constructor(const GlobalObject& aGlobal,
double aUpDegrees, double aRightDegrees,
double aDownDegrees, double aLeftDegrees,
ErrorResult& aRV)
{
nsRefPtr<VRFieldOfView> obj =
new VRFieldOfView(aGlobal.GetAsSupports(),
aUpDegrees, aRightDegrees,
aDownDegrees, aLeftDegrees);
return obj.forget();
}
JSObject*
VRFieldOfView::WrapObject(JSContext* aCx)
{
return VRFieldOfViewBinding::Wrap(aCx, this);
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRPositionState, mParent)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRPositionState, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRPositionState, Release)
VRPositionState::VRPositionState(nsISupports* aParent, const gfx::VRHMDSensorState& aState)
: mParent(aParent)
, mVRState(aState)
{
mTimeStamp = aState.timestamp;
if (aState.flags & gfx::VRHMDInfo::State_Position) {
mPosition = new DOMPoint(mParent, aState.position[0], aState.position[1], aState.position[2], 0.0);
}
if (aState.flags & gfx::VRHMDInfo::State_Orientation) {
mOrientation = new DOMPoint(mParent, aState.orientation[0], aState.orientation[1], aState.orientation[2], aState.orientation[3]);
}
}
DOMPoint*
VRPositionState::GetLinearVelocity()
{
if (!mLinearVelocity) {
mLinearVelocity = new DOMPoint(mParent, mVRState.linearVelocity[0], mVRState.linearVelocity[1], mVRState.linearVelocity[2], 0.0);
}
return mLinearVelocity;
}
DOMPoint*
VRPositionState::GetLinearAcceleration()
{
if (!mLinearAcceleration) {
mLinearAcceleration = new DOMPoint(mParent, mVRState.linearAcceleration[0], mVRState.linearAcceleration[1], mVRState.linearAcceleration[2], 0.0);
}
return mLinearAcceleration;
}
DOMPoint*
VRPositionState::GetAngularVelocity()
{
if (!mAngularVelocity) {
mAngularVelocity = new DOMPoint(mParent, mVRState.angularVelocity[0], mVRState.angularVelocity[1], mVRState.angularVelocity[2], 0.0);
}
return mAngularVelocity;
}
DOMPoint*
VRPositionState::GetAngularAcceleration()
{
if (!mAngularAcceleration) {
mAngularAcceleration = new DOMPoint(mParent, mVRState.angularAcceleration[0], mVRState.angularAcceleration[1], mVRState.angularAcceleration[2], 0.0);
}
return mAngularAcceleration;
}
JSObject*
VRPositionState::WrapObject(JSContext* aCx)
{
return VRPositionStateBinding::Wrap(aCx, this);
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(VRDevice)
NS_IMPL_CYCLE_COLLECTING_RELEASE(VRDevice)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(VRDevice)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRDevice, mParent)
/* virtual */ JSObject*
HMDVRDevice::WrapObject(JSContext* aCx)
{
return HMDVRDeviceBinding::Wrap(aCx, this);
}
/* virtual */ JSObject*
PositionSensorVRDevice::WrapObject(JSContext* aCx)
{
return PositionSensorVRDeviceBinding::Wrap(aCx, this);
}
static void
ReleaseHMDInfoRef(void *, nsIAtom*, void *aPropertyValue, void *)
{
if (aPropertyValue) {
static_cast<VRHMDInfo*>(aPropertyValue)->Release();
}
}
void
HMDVRDevice::XxxToggleElementVR(Element& aElement)
{
VRHMDInfo* hmdPtr = static_cast<VRHMDInfo*>(aElement.GetProperty(nsGkAtoms::vr_state));
if (hmdPtr) {
aElement.DeleteProperty(nsGkAtoms::vr_state);
return;
}
nsRefPtr<VRHMDInfo> hmdRef = mHMD;
aElement.SetProperty(nsGkAtoms::vr_state, hmdRef.forget().take(),
ReleaseHMDInfoRef,
true);
}
namespace {
gfx::VRHMDInfo::Eye
EyeToEye(const VREye& aEye)
{
return aEye == VREye::Left ? gfx::VRHMDInfo::Eye_Left : gfx::VRHMDInfo::Eye_Right;
}
class HMDInfoVRDevice : public HMDVRDevice
{
public:
HMDInfoVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD)
: HMDVRDevice(aParent, aHMD)
{
// XXX TODO use real names/IDs
mHWID.AppendPrintf("HMDInfo-0x%llx", aHMD);
mDeviceId.AssignLiteral("somedevid");
mDeviceName.AssignLiteral("HMD Device");
mValid = true;
}
virtual ~HMDInfoVRDevice() { }
/* If a field of view that is set to all 0's is passed in,
* the recommended field of view for that eye is used.
*/
virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV,
const VRFieldOfViewInit& aRightFOV,
double zNear, double zFar) MOZ_OVERRIDE
{
gfx::VRFieldOfView left = gfx::VRFieldOfView(aLeftFOV.mUpDegrees, aLeftFOV.mRightDegrees,
aLeftFOV.mDownDegrees, aLeftFOV.mLeftDegrees);
gfx::VRFieldOfView right = gfx::VRFieldOfView(aRightFOV.mUpDegrees, aRightFOV.mRightDegrees,
aRightFOV.mDownDegrees, aRightFOV.mLeftDegrees);
if (left.IsZero())
left = mHMD->GetRecommendedEyeFOV(VRHMDInfo::Eye_Left);
if (right.IsZero())
right = mHMD->GetRecommendedEyeFOV(VRHMDInfo::Eye_Right);
mHMD->SetFOV(left, right, zNear, zFar);
}
virtual already_AddRefed<DOMPoint> GetEyeTranslation(VREye aEye) MOZ_OVERRIDE
{
gfx::Point3D p = mHMD->GetEyeTranslation(EyeToEye(aEye));
nsRefPtr<DOMPoint> obj = new DOMPoint(mParent, p.x, p.y, p.z, 0.0);
return obj.forget();
}
virtual already_AddRefed<VRFieldOfView> GetCurrentEyeFieldOfView(VREye aEye) MOZ_OVERRIDE
{
return CopyFieldOfView(mHMD->GetEyeFOV(EyeToEye(aEye)));
}
virtual already_AddRefed<VRFieldOfView> GetRecommendedEyeFieldOfView(VREye aEye) MOZ_OVERRIDE
{
return CopyFieldOfView(mHMD->GetRecommendedEyeFOV(EyeToEye(aEye)));
}
virtual already_AddRefed<VRFieldOfView> GetMaximumEyeFieldOfView(VREye aEye) MOZ_OVERRIDE
{
return CopyFieldOfView(mHMD->GetMaximumEyeFOV(EyeToEye(aEye)));
}
virtual already_AddRefed<DOMRect> GetRecommendedEyeRenderRect(VREye aEye) MOZ_OVERRIDE
{
const IntSize& a(mHMD->SuggestedEyeResolution());
nsRefPtr<DOMRect> obj =
new DOMRect(mParent,
(aEye == VREye::Left) ? 0 : a.width, 0,
a.width, a.height);
return obj.forget();
}
protected:
already_AddRefed<VRFieldOfView>
CopyFieldOfView(const gfx::VRFieldOfView& aSrc)
{
nsRefPtr<VRFieldOfView> obj =
new VRFieldOfView(mParent, aSrc.upDegrees, aSrc.rightDegrees,
aSrc.downDegrees, aSrc.leftDegrees);
return obj.forget();
}
};
class HMDPositionVRDevice : public PositionSensorVRDevice
{
public:
HMDPositionVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD)
: PositionSensorVRDevice(aParent)
, mHMD(aHMD)
, mTracking(false)
{
// XXX TODO use real names/IDs
mHWID.AppendPrintf("HMDInfo-0x%llx", aHMD);
mDeviceId.AssignLiteral("somedevid");
mDeviceName.AssignLiteral("HMD Position Device");
mValid = true;
}
~HMDPositionVRDevice()
{
if (mTracking) {
mHMD->StopSensorTracking();
}
}
virtual already_AddRefed<VRPositionState> GetState(double timeOffset) MOZ_OVERRIDE
{
if (!mTracking) {
mHMD->StartSensorTracking();
mTracking = true;
}
gfx::VRHMDSensorState state = mHMD->GetSensorState(timeOffset);
nsRefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
return obj.forget();
}
virtual void ZeroSensor() MOZ_OVERRIDE
{
mHMD->ZeroSensor();
}
protected:
nsRefPtr<gfx::VRHMDInfo> mHMD;
bool mTracking;
};
} // namespace
bool
VRDevice::CreateAllKnownVRDevices(nsISupports *aParent, nsTArray<nsRefPtr<VRDevice>>& aDevices)
{
if (!gfx::VRHMDManagerOculus::Init()) {
NS_WARNING("Failed to initialize Oculus HMD Manager");
return false;
}
nsTArray<nsRefPtr<gfx::VRHMDInfo>> hmds;
gfx::VRHMDManagerOculus::GetOculusHMDs(hmds);
for (size_t i = 0; i < hmds.Length(); ++i) {
uint32_t sensorBits = hmds[i]->GetSupportedSensorStateBits();
aDevices.AppendElement(new HMDInfoVRDevice(aParent, hmds[i]));
if (sensorBits &
(gfx::VRHMDInfo::State_Position | gfx::VRHMDInfo::State_Orientation))
{
aDevices.AppendElement(new HMDPositionVRDevice(aParent, hmds[i]));
}
}
return true;
}
} // namespace dom
} // namespace mozilla

235
dom/vr/VRDevice.h Normal file
View File

@ -0,0 +1,235 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_VRDevice_h_
#define mozilla_dom_VRDevice_h_
#include <stdint.h>
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/dom/VRDeviceBinding.h"
#include "mozilla/dom/DOMPoint.h"
#include "mozilla/dom/DOMRect.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsWrapperCache.h"
#include "gfxVR.h"
namespace mozilla {
namespace dom {
class Element;
class VRFieldOfViewReadOnly : public nsWrapperCache
{
public:
VRFieldOfViewReadOnly(nsISupports* aParent,
double aUpDegrees, double aRightDegrees,
double aDownDegrees, double aLeftDegrees)
: mParent(aParent)
, mUpDegrees(aUpDegrees)
, mRightDegrees(aRightDegrees)
, mDownDegrees(aDownDegrees)
, mLeftDegrees(aLeftDegrees)
{
}
double UpDegrees() const { return mUpDegrees; }
double RightDegrees() const { return mRightDegrees; }
double DownDegrees() const { return mDownDegrees; }
double LeftDegrees() const { return mLeftDegrees; }
protected:
nsCOMPtr<nsISupports> mParent;
double mUpDegrees;
double mRightDegrees;
double mDownDegrees;
double mLeftDegrees;
};
class VRFieldOfView MOZ_FINAL : public VRFieldOfViewReadOnly
{
~VRFieldOfView() {}
public:
explicit VRFieldOfView(nsISupports* aParent,
double aUpDegrees = 0.0, double aRightDegrees = 0.0,
double aDownDegrees = 0.0, double aLeftDegrees = 0.0)
: VRFieldOfViewReadOnly(aParent, aUpDegrees, aRightDegrees, aDownDegrees, aLeftDegrees)
{}
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRFieldOfView)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRFieldOfView)
static already_AddRefed<VRFieldOfView>
Constructor(const GlobalObject& aGlobal, const VRFieldOfViewInit& aParams,
ErrorResult& aRv);
static already_AddRefed<VRFieldOfView>
Constructor(const GlobalObject& aGlobal,
double aUpDegrees, double aRightDegrees,
double aDownDegrees, double aLeftDegrees,
ErrorResult& aRv);
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
void SetUpDegrees(double aVal) { mUpDegrees = aVal; }
void SetRightDegrees(double aVal) { mRightDegrees = aVal; }
void SetDownDegrees(double aVal) { mDownDegrees = aVal; }
void SetLeftDegrees(double aVal) { mLeftDegrees = aVal; }
};
class VRPositionState MOZ_FINAL : public nsWrapperCache
{
~VRPositionState() {}
public:
explicit VRPositionState(nsISupports* aParent, const gfx::VRHMDSensorState& aState);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRPositionState)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRPositionState)
double TimeStamp() const { return mTimeStamp; }
bool HasPosition() const { return mPosition != nullptr; }
DOMPoint* GetPosition() const { return mPosition; }
bool HasOrientation() const { return mOrientation != nullptr; }
DOMPoint* GetOrientation() const { return mOrientation; }
// these are created lazily
DOMPoint* GetLinearVelocity();
DOMPoint* GetLinearAcceleration();
DOMPoint* GetAngularVelocity();
DOMPoint* GetAngularAcceleration();
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
protected:
nsCOMPtr<nsISupports> mParent;
double mTimeStamp;
gfx::VRHMDSensorState mVRState;
nsRefPtr<DOMPoint> mPosition;
nsRefPtr<DOMPoint> mLinearVelocity;
nsRefPtr<DOMPoint> mLinearAcceleration;
nsRefPtr<DOMPoint> mOrientation;
nsRefPtr<DOMPoint> mAngularVelocity;
nsRefPtr<DOMPoint> mAngularAcceleration;
};
class VRDevice : public nsISupports,
public nsWrapperCache
{
public:
// create new VRDevice objects for all known underlying gfx::vr devices
static bool CreateAllKnownVRDevices(nsISupports *aParent, nsTArray<nsRefPtr<VRDevice>>& aDevices);
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(VRDevice)
void GetHardwareUnitId(nsAString& aHWID) const { aHWID = mHWID; }
void GetDeviceId(nsAString& aDeviceId) const { aDeviceId = mDeviceId; }
void GetDeviceName(nsAString& aDeviceName) const { aDeviceName = mDeviceName; }
bool IsValid() { return mValid; }
virtual void Shutdown() { }
nsISupports* GetParentObject() const
{
return mParent;
}
enum VRDeviceType {
HMD,
PositionSensor
};
VRDeviceType GetType() const { return mType; }
protected:
VRDevice(nsISupports* aParent, VRDeviceType aType)
: mParent(aParent)
, mType(aType)
, mValid(false)
{
mHWID.AssignLiteral("uknown");
mDeviceId.AssignLiteral("unknown");
mDeviceName.AssignLiteral("unknown");
}
virtual ~VRDevice() {
Shutdown();
}
nsCOMPtr<nsISupports> mParent;
nsString mHWID;
nsString mDeviceId;
nsString mDeviceName;
VRDeviceType mType;
bool mValid;
};
class HMDVRDevice : public VRDevice
{
public:
virtual already_AddRefed<DOMPoint> GetEyeTranslation(VREye aEye) = 0;
virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV,
const VRFieldOfViewInit& aRightFOV,
double zNear, double zFar) = 0;
virtual already_AddRefed<VRFieldOfView> GetCurrentEyeFieldOfView(VREye aEye) = 0;
virtual already_AddRefed<VRFieldOfView> GetRecommendedEyeFieldOfView(VREye aEye) = 0;
virtual already_AddRefed<VRFieldOfView> GetMaximumEyeFieldOfView(VREye aEye) = 0;
virtual already_AddRefed<DOMRect> GetRecommendedEyeRenderRect(VREye aEye) = 0;
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
void XxxToggleElementVR(Element& aElement);
gfx::VRHMDInfo *GetHMD() { return mHMD.get(); }
protected:
HMDVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD)
: VRDevice(aParent, VRDevice::HMD)
, mHMD(aHMD)
{ }
virtual ~HMDVRDevice() { }
nsRefPtr<gfx::VRHMDInfo> mHMD;
};
class PositionSensorVRDevice : public VRDevice
{
public:
virtual already_AddRefed<VRPositionState> GetState(double timeOffset) = 0;
virtual void ZeroSensor() = 0;
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
protected:
PositionSensorVRDevice(nsISupports* aParent)
: VRDevice(aParent, VRDevice::PositionSensor)
{ }
virtual ~PositionSensorVRDevice() { }
};
} // namespace dom
} // namespace mozilla
#endif

22
dom/vr/moz.build Normal file
View File

@ -0,0 +1,22 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.dom += [
'VRDevice.h',
]
UNIFIED_SOURCES = [
'VRDevice.cpp',
]
FAIL_ON_WARNINGS = True
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/dom/base'
]

View File

@ -124,9 +124,11 @@ interface Element : Node {
* Requests that this element be made the full-screen element, as per the DOM
* full-screen api.
*
* The fsOptions parameter is non-standard.
*
* @see <https://wiki.mozilla.org/index.php?title=Gecko:FullScreenAPI>
*/
void mozRequestFullScreen();
void mozRequestFullScreen(optional RequestFullscreenOptions fsOptions);
/**
* Requests that this element be made the pointer-locked element, as per the DOM
@ -235,3 +237,10 @@ Element implements NonDocumentTypeChildNode;
Element implements ParentNode;
Element implements Animatable;
Element implements GeometryUtils;
// non-standard: allows passing options to Element.requestFullScreen
dictionary RequestFullscreenOptions {
// Which HMDVRDevice to go full screen on; also enables VR rendering.
// If null, normal fullscreen is entered.
HMDVRDevice? vrDisplay = null;
};

View File

@ -322,6 +322,11 @@ partial interface Navigator {
};
#endif // MOZ_GAMEPAD
partial interface Navigator {
[Throws, Pref="dom.vr.enabled"]
Promise<sequence<VRDevice>> getVRDevices();
};
#ifdef MOZ_B2G_BT
partial interface Navigator {
[Throws, CheckPermissions="bluetooth"]

128
dom/webidl/VRDevice.webidl Normal file
View File

@ -0,0 +1,128 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
enum VREye {
"left",
"right"
};
[Pref="dom.vr.enabled",
HeaderFile="mozilla/dom/VRDevice.h"]
interface VRFieldOfViewReadOnly {
readonly attribute double upDegrees;
readonly attribute double rightDegrees;
readonly attribute double downDegrees;
readonly attribute double leftDegrees;
};
[Pref="dom.vr.enabled",
HeaderFile="mozilla/dom/VRDevice.h",
Constructor(optional VRFieldOfViewInit fov),
Constructor(double upDegrees, double rightDegrees, double downDegrees, double leftDegrees)]
interface VRFieldOfView : VRFieldOfViewReadOnly {
inherit attribute double upDegrees;
inherit attribute double rightDegrees;
inherit attribute double downDegrees;
inherit attribute double leftDegrees;
};
dictionary VRFieldOfViewInit {
double upDegrees = 0.0;
double rightDegrees = 0.0;
double downDegrees = 0.0;
double leftDegrees = 0.0;
};
[Pref="dom.vr.enabled",
HeaderFile="mozilla/dom/VRDevice.h"]
interface VRPositionState {
readonly attribute double timeStamp;
readonly attribute boolean hasPosition;
readonly attribute DOMPoint? position;
readonly attribute DOMPoint? linearVelocity;
readonly attribute DOMPoint? linearAcceleration;
readonly attribute boolean hasOrientation;
// XXX should be DOMQuaternion as soon as we add that
readonly attribute DOMPoint? orientation;
readonly attribute DOMPoint? angularVelocity;
readonly attribute DOMPoint? angularAcceleration;
};
[Pref="dom.vr.enabled"]
interface VRDevice {
/**
* An identifier for the distinct hardware unit that this
* VR Device is a part of. All VRDevice/Sensors that come
* from the same hardware will have the same hardwareId
*/
[Pure] readonly attribute DOMString hardwareUnitId;
/**
* An identifier for this distinct sensor/device on a physical
* hardware device. This shouldn't change across browser
* restrats, allowing configuration data to be saved based on it.
*/
[Pure] readonly attribute DOMString deviceId;
/**
* a device name, a user-readable name identifying it
*/
[Pure] readonly attribute DOMString deviceName;
};
[Pref="dom.vr.enabled",
HeaderFile="mozilla/dom/VRDevice.h"]
interface HMDVRDevice : VRDevice {
/* The translation that should be applied to the view matrix for rendering each eye */
DOMPoint getEyeTranslation(VREye whichEye);
// the FOV that the HMD was configured with
VRFieldOfView getCurrentEyeFieldOfView(VREye whichEye);
// the recommended FOV, per eye.
VRFieldOfView getRecommendedEyeFieldOfView(VREye whichEye);
// the maximum FOV, per eye. Above this, rendering will look broken.
VRFieldOfView getMaximumEyeFieldOfView(VREye whichEye);
// Set a field of view. If either of the fields of view is null,
// or if their values are all zeros, then the recommended field of view
// for that eye will be used.
void setFieldOfView(optional VRFieldOfViewInit leftFOV,
optional VRFieldOfViewInit rightFOV,
optional double zNear = 0.01,
optional double zFar = 10000.0);
// return a recommended rect for this eye. Only useful for Canvas rendering,
// the x/y coordinates will be the location in the canvas where this eye should
// begin, and the width/height are the dimensions. Any canvas in the appropriate
// ratio will work.
DOMRect getRecommendedEyeRenderRect(VREye whichEye);
// hack for testing
void xxxToggleElementVR(Element element);
};
[Pref="dom.vr.enabled" ,
HeaderFile="mozilla/dom/VRDevice.h"]
interface PositionSensorVRDevice : VRDevice {
/*
* Return a VRPositionState dictionary containing the state of this position sensor,
* at an optional past time or predicted for a future time if timeOffset is != 0.
*
* The VRPositionState will contain the position, orientation, and velocity
* and acceleration of each of these properties. Use "hasPosition" and "hasOrientation"
* to check if the associated members are valid; if these are false, those members
* will be null.
*/
VRPositionState getState(optional double timeOffset = 0.0);
/* Zero this sensor, treating its current position and orientation
* as the "origin/zero" values.
*/
void zeroSensor();
};

View File

@ -528,6 +528,7 @@ WEBIDL_FILES = [
'VideoStreamTrack.webidl',
'VideoTrack.webidl',
'VideoTrackList.webidl',
'VRDevice.webidl',
'VTTCue.webidl',
'VTTRegion.webidl',
'WaveShaperNode.webidl',

View File

@ -377,6 +377,11 @@ public:
, _41(a41), _42(a42), _43(a43), _44(a44)
{}
Matrix4x4(const Matrix4x4& aOther)
{
memcpy(this, &aOther, sizeof(*this));
}
Float _11, _12, _13, _14;
Float _21, _22, _23, _24;
Float _31, _32, _33, _34;
@ -541,6 +546,11 @@ public:
aX, aY, aZ, 1.0f);
}
static Matrix4x4 Translation(const Point3D& aP)
{
return Translation(aP.x, aP.y, aP.z);
}
/**
* Apply a translation to this matrix.
*
@ -570,6 +580,10 @@ public:
return *this;
}
Matrix4x4 &PreTranslate(const Point3D& aPoint) {
return PreTranslate(aPoint.x, aPoint.y, aPoint.z);
}
/**
* Similar to PreTranslate, but the translation is applied -after- this
* matrix's existing transformation instead of before it.
@ -600,6 +614,10 @@ public:
return *this;
}
Matrix4x4 &PostTranslate(const Point3D& aPoint) {
return PostTranslate(aPoint.x, aPoint.y, aPoint.z);
}
static Matrix4x4 Scaling(Float aScaleX, Float aScaleY, float aScaleZ)
{
return Matrix4x4(aScaleX, 0.0f, 0.0f, 0.0f,
@ -673,6 +691,19 @@ public:
return *this;
}
Matrix4x4& Transpose() {
std::swap(_12, _21);
std::swap(_13, _31);
std::swap(_14, _41);
std::swap(_23, _32);
std::swap(_24, _42);
std::swap(_34, _43);
return *this;
}
bool operator==(const Matrix4x4& o) const
{
// XXX would be nice to memcmp here, but that breaks IEEE 754 semantics

View File

@ -150,6 +150,7 @@ MOZ_BEGIN_ENUM_CLASS(EffectTypes, uint8_t)
COMPONENT_ALPHA,
SOLID_COLOR,
RENDER_TARGET,
VR_DISTORTION,
MAX //sentinel for the count of all effect types
MOZ_END_ENUM_CLASS(EffectTypes)

View File

@ -69,3 +69,10 @@ EffectColorMatrix::PrintInfo(std::stringstream& aStream, const char* aPrefix)
AppendToString(aStream, mColorMatrix, " [matrix=", "]");
}
void
EffectVRDistortion::PrintInfo(std::stringstream& aStream, const char* aPrefix)
{
aStream << aPrefix;
aStream << nsPrintfCString("EffectVRDistortion (0x%p) [hmd=%p] [render-target=%p] [texture=%p]",
this, mHMD.get(), mRenderTarget.get(), mTexture).get();
}

View File

@ -18,6 +18,7 @@
#include "mozilla/mozalloc.h" // for operator delete, etc
#include "nscore.h" // for nsACString
#include "mozilla/EnumeratedArray.h"
#include "gfxVR.h"
namespace mozilla {
namespace layers {
@ -96,6 +97,36 @@ struct EffectMask : public Effect
gfx::Matrix4x4 mMaskTransform;
};
struct EffectVRDistortion : public Effect
{
EffectVRDistortion(gfx::VRHMDInfo* aHMD,
CompositingRenderTarget* aRenderTarget)
: Effect(EffectTypes::VR_DISTORTION)
, mHMD(aHMD)
, mRenderTarget(aRenderTarget)
, mTexture(aRenderTarget)
{}
EffectVRDistortion(gfx::VRHMDInfo* aHMD,
TextureSource* aTexture)
: Effect(EffectTypes::VR_DISTORTION)
, mHMD(aHMD)
, mRenderTarget(nullptr)
, mTexture(aTexture)
{}
virtual const char* Name() { return "EffectVRDistortion"; }
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
nsRefPtr<gfx::VRHMDInfo> mHMD;
RefPtr<CompositingRenderTarget> mRenderTarget;
TextureSource* mTexture;
// The viewport for each eye in the source and
// destination textures.
gfx::IntRect mViewports[2];
};
struct EffectBlendMode : public Effect
{
explicit EffectBlendMode(gfx::CompositionOp aBlendMode)

View File

@ -13,6 +13,7 @@
#include "mozilla/Endian.h"
#include "TexturePoolOGL.h"
#include "mozilla/layers/CompositorOGL.h"
#include "mozilla/layers/CompositorParent.h"
#include "mozilla/layers/LayerManagerComposite.h"
#include "mozilla/layers/TextureHostOGL.h"
@ -333,7 +334,7 @@ public:
static void CreateServerSocket()
{
// Create Web Server Socket (which has to be on the main thread)
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
if (!sWebSocketManager) {
sWebSocketManager = new LayerScopeWebSocketManager();
}
@ -651,6 +652,16 @@ protected:
NS_IMPL_ISUPPORTS(DebugDataSender, nsIRunnable);
class CreateServerSocketRunnable : public nsRunnable
{
public:
NS_IMETHOD Run() {
WebSocketHelper::CreateServerSocket();
return NS_OK;
}
};
/*
* LayerScope SendXXX Structure
* 1. SendLayer
@ -915,15 +926,19 @@ LayerScope::Init()
return;
}
// Note: The server socket has to be created on the main thread
WebSocketHelper::CreateServerSocket();
}
void
LayerScope::DeInit()
{
// Destroy Web Server Socket
WebSocketHelper::DestroyServerSocket();
if (NS_IsMainThread()) {
WebSocketHelper::CreateServerSocket();
} else {
// Dispatch creation to main thread, and make sure we
// dispatch this only once after booting
static bool dispatched = false;
if (dispatched) {
return;
}
DebugOnly<nsresult> rv = NS_DispatchToMainThread(new CreateServerSocketRunnable());
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to dispatch WebSocket Creation to main thread");
dispatched = true;
}
}
void
@ -965,7 +980,14 @@ LayerScope::SendLayerDump(UniquePtr<Packet> aPacket)
bool
LayerScope::CheckSendable()
{
// Only compositor threads check LayerScope status
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
if (!gfxPrefs::LayerScopeEnabled()) {
return false;
}
if (!WebSocketHelper::GetSocketManager()) {
Init();
return false;
}
if (!WebSocketHelper::GetSocketManager()->IsConnected()) {

View File

@ -24,8 +24,6 @@ class LayerComposite;
class LayerScope {
public:
static void Init();
static void DeInit();
static void SendEffectChain(gl::GLContext* aGLContext,
const EffectChain& aEffectChain,
int aWidth,
@ -36,6 +34,9 @@ public:
static void SendLayerDump(UniquePtr<layerscope::Packet> aPacket);
static bool CheckSendable();
static void CleanLayer();
private:
static void Init();
};
// Perform BeginFrame and EndFrame automatically

View File

@ -994,7 +994,8 @@ void
ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
{
aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale,
mInheritedXScale, mInheritedYScale);
mInheritedXScale, mInheritedYScale,
reinterpret_cast<uint64_t>(mHMDInfo.get()));
}
bool
@ -1658,6 +1659,9 @@ ContainerLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
if (1.0 != mPreXScale || 1.0 != mPreYScale) {
aStream << nsPrintfCString(" [preScale=%g, %g]", mPreXScale, mPreYScale).get();
}
if (mHMDInfo) {
aStream << nsPrintfCString(" [hmd=%p]", mHMDInfo.get()).get();
}
}
void

View File

@ -45,6 +45,7 @@
#include "nscore.h" // for nsACString, nsAString
#include "prlog.h" // for PRLogModuleInfo
#include "gfx2DGlue.h"
#include "gfxVR.h"
class gfxContext;
@ -1893,6 +1894,12 @@ public:
mChildrenChanged = aVal;
}
/**
* VR
*/
void SetVRHMDInfo(gfx::VRHMDInfo* aHMD) { mHMDInfo = aHMD; }
gfx::VRHMDInfo* GetVRHMDInfo() { return mHMDInfo; }
protected:
friend class ReadbackProcessor;
@ -1938,6 +1945,7 @@ protected:
// This is updated by ComputeDifferences. This will be true if we need to invalidate
// the intermediate surface.
bool mChildrenChanged;
nsRefPtr<gfx::VRHMDInfo> mHMDInfo;
};
/**

View File

@ -19,6 +19,7 @@
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
#include "nsPoint.h" // for nsIntPoint
#include "nsString.h" // for nsAutoCString
#include "gfxVR.h"
namespace mozilla {
namespace layers {

View File

@ -133,6 +133,105 @@ struct PreparedLayer
RenderTargetIntRect mClipRect;
};
template<class ContainerT> void
ContainerRenderVR(ContainerT* aContainer,
LayerManagerComposite* aManager,
const nsIntRect& aClipRect,
gfx::VRHMDInfo* aHMD)
{
RefPtr<CompositingRenderTarget> surface;
Compositor* compositor = aManager->GetCompositor();
RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
nsIntRect visibleRect = aContainer->GetEffectiveVisibleRegion().GetBounds();
float opacity = aContainer->GetEffectiveOpacity();
gfx::IntRect surfaceRect = gfx::IntRect(visibleRect.x, visibleRect.y,
visibleRect.width, visibleRect.height);
// we're about to create a framebuffer backed by textures to use as an intermediate
// surface. What to do if its size (as given by framebufferRect) would exceed the
// maximum texture size supported by the GL? The present code chooses the compromise
// of just clamping the framebuffer's size to the max supported size.
// This gives us a lower resolution rendering of the intermediate surface (children layers).
// See bug 827170 for a discussion.
int32_t maxTextureSize = compositor->GetMaxTextureSize();
surfaceRect.width = std::min(maxTextureSize, surfaceRect.width);
surfaceRect.height = std::min(maxTextureSize, surfaceRect.height);
// use NONE here, because we draw black to clear below
surface = compositor->CreateRenderTarget(surfaceRect, INIT_MODE_NONE);
if (!surface) {
return;
}
compositor->SetRenderTarget(surface);
nsAutoTArray<Layer*, 12> children;
aContainer->SortChildrenBy3DZOrder(children);
/**
* Render this container's contents.
*/
nsIntRect surfaceClipRect(0, 0, surfaceRect.width, surfaceRect.height);
RenderTargetIntRect rtClipRect(0, 0, surfaceRect.width, surfaceRect.height);
for (uint32_t i = 0; i < children.Length(); i++) {
LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->ImplData());
Layer* layer = layerToRender->GetLayer();
if (layer->GetEffectiveVisibleRegion().IsEmpty() &&
!layer->AsContainerLayer()) {
continue;
}
RenderTargetIntRect clipRect = layer->CalculateScissorRect(rtClipRect);
if (clipRect.IsEmpty()) {
continue;
}
layerToRender->Prepare(rtClipRect);
layerToRender->RenderLayer(surfaceClipRect);
}
// Unbind the current surface and rebind the previous one.
#ifdef MOZ_DUMP_PAINTING
if (gfxUtils::sDumpPainting) {
RefPtr<gfx::DataSourceSurface> surf = surface->Dump(aManager->GetCompositor());
if (surf) {
WriteSnapshotToDumpFile(aContainer, surf);
}
}
#endif
compositor->SetRenderTarget(previousTarget);
gfx::Rect rect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height);
gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
// The VR geometry may not cover the entire area; we need to fill with a solid color
// first.
// XXX should DrawQuad handle this on its own? Is there a time where we wouldn't want
// to do this? (e.g. something like Cardboard would not require distortion so will fill
// the entire rect)
EffectChain solidEffect(aContainer);
solidEffect.mPrimaryEffect = new EffectSolidColor(Color(0.0, 0.0, 0.0, 1.0));
aManager->GetCompositor()->DrawQuad(rect, clipRect, solidEffect, opacity,
aContainer->GetEffectiveTransform());
// draw the temporary surface with VR distortion to the original destination
EffectChain vrEffect(aContainer);
vrEffect.mPrimaryEffect = new EffectVRDistortion(aHMD, surface);
// XXX we shouldn't use visibleRect here -- the VR distortion needs to know the
// full rect, not just the visible one. Luckily, right now, VR distortion is only
// rendered when the element is fullscreen, so the visibleRect will be right anyway.
aManager->GetCompositor()->DrawQuad(rect, clipRect, vrEffect, opacity,
aContainer->GetEffectiveTransform());
}
/* all of the prepared data that we need in RenderLayer() */
struct PreparedData
{
@ -150,6 +249,15 @@ ContainerPrepare(ContainerT* aContainer,
aContainer->mPrepared = MakeUnique<PreparedData>();
aContainer->mPrepared->mNeedsSurfaceCopy = false;
gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo();
if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) {
// we're not going to do anything here; instead, we'll do it all in ContainerRender.
// XXX fix this; we can win with the same optimizations. Specifically, we
// want to render thebes layers only once and then composite the intermeidate surfaces
// with different transforms twice.
return;
}
/**
* Determine which layers to draw.
*/
@ -366,6 +474,14 @@ ContainerRender(ContainerT* aContainer,
const nsIntRect& aClipRect)
{
MOZ_ASSERT(aContainer->mPrepared);
gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo();
if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) {
ContainerRenderVR(aContainer, aManager, aClipRect, hmdInfo);
aContainer->mPrepared = nullptr;
return;
}
if (aContainer->UseIntermediateSurface()) {
RefPtr<CompositingRenderTarget> surface;

View File

@ -48,6 +48,10 @@ typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
using mozilla::layout::RenderFrameParent;
namespace mozilla {
namespace gfx {
class VRHMDInfo;
}
namespace layers {
class PGrallocBufferParent;
@ -378,6 +382,15 @@ LayerTransactionParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
specific.get_ContainerLayerAttributes();
containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale());
containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale());
if (attrs.hmdInfo()) {
if (!IsSameProcess()) {
NS_WARNING("VR layers currently not supported with cross-process compositing");
return false;
}
containerLayer->SetVRHMDInfo(reinterpret_cast<mozilla::gfx::VRHMDInfo*>(attrs.hmdInfo()));
}
break;
}
case Specific::TColorLayerAttributes: {

View File

@ -227,6 +227,10 @@ struct ContainerLayerAttributes {
float preYScale;
float inheritedXScale;
float inheritedYScale;
// This is a bare pointer; LayerTransactionParent::RecvUpdate prevents this
// from being used when !IsSameProcess(), but we should make this truly
// cross process at some point by passing the HMDConfig
uint64_t hmdInfo;
};
struct ColorLayerAttributes { LayerColor color; nsIntRect bounds; };
struct CanvasLayerAttributes { GraphicsFilterType filter; nsIntRect bounds; };

View File

@ -16,6 +16,7 @@
#include "gfxPlatform.h"
#include "gfxPrefs.h"
#include "gfxTextRun.h"
#include "gfxVR.h"
#ifdef XP_WIN
#include <process.h>
@ -386,6 +387,10 @@ gfxPlatform::gfxPlatform()
InitBackendPrefs(canvasMask, BackendType::CAIRO,
contentMask, BackendType::CAIRO);
mTotalSystemMemory = mozilla::hal::GetTotalSystemMemory();
// give ovr_Initialize a chance to be called very early on; we don't
// care if it succeeds or not
VRHMDManagerOculus::PlatformInit();
}
gfxPlatform*
@ -677,6 +682,9 @@ gfxPlatform::~gfxPlatform()
mScreenReferenceSurface = nullptr;
mScreenReferenceDrawTarget = nullptr;
// Clean up any VR stuff
VRHMDManagerOculus::Destroy();
// The cairo folks think we should only clean up in debug builds,
// but we're generally in the habit of trying to shut down as
// cleanly as possible even in production code, so call this

View File

@ -326,6 +326,9 @@ private:
DECL_GFX_PREF(Once, "webgl.angle.force-d3d11", WebGLANGLEForceD3D11, bool, false);
DECL_GFX_PREF(Once, "layers.stereo-video.enabled", StereoVideoEnabled, bool, false);
DECL_GFX_PREF(Once, "dom.vr.enabled", VREnabled, bool, false);
DECL_GFX_PREF(Once, "dom.vr.add-test-devices", VRAddTestDevices, int32_t, 1);
public:
// Manage the singleton:
static gfxPrefs& GetSingleton()

645
gfx/thebes/gfxVR.cpp Normal file
View File

@ -0,0 +1,645 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <math.h>
#include "prlink.h"
#include "prmem.h"
#include "prenv.h"
#include "gfxPrefs.h"
#include "gfxVR.h"
#include "ovr_capi_dynamic.h"
#include "nsServiceManagerUtils.h"
#include "nsIScreenManager.h"
#ifdef XP_WIN
#include "gfxWindowsPlatform.h" // for gfxWindowsPlatform::GetDPIScale
#endif
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
namespace {
#ifdef OVR_CAPI_LIMITED_MOZILLA
static pfn_ovr_Initialize ovr_Initialize = nullptr;
static pfn_ovr_Shutdown ovr_Shutdown = nullptr;
static pfn_ovrHmd_Detect ovrHmd_Detect = nullptr;
static pfn_ovrHmd_Create ovrHmd_Create = nullptr;
static pfn_ovrHmd_Destroy ovrHmd_Destroy = nullptr;
static pfn_ovrHmd_CreateDebug ovrHmd_CreateDebug = nullptr;
static pfn_ovrHmd_GetLastError ovrHmd_GetLastError = nullptr;
static pfn_ovrHmd_AttachToWindow ovrHmd_AttachToWindow = nullptr;
static pfn_ovrHmd_GetEnabledCaps ovrHmd_GetEnabledCaps = nullptr;
static pfn_ovrHmd_SetEnabledCaps ovrHmd_SetEnabledCaps = nullptr;
static pfn_ovrHmd_ConfigureTracking ovrHmd_ConfigureTracking = nullptr;
static pfn_ovrHmd_RecenterPose ovrHmd_RecenterPose = nullptr;
static pfn_ovrHmd_GetTrackingState ovrHmd_GetTrackingState = nullptr;
static pfn_ovrHmd_GetFovTextureSize ovrHmd_GetFovTextureSize = nullptr;
static pfn_ovrHmd_GetRenderDesc ovrHmd_GetRenderDesc = nullptr;
static pfn_ovrHmd_CreateDistortionMesh ovrHmd_CreateDistortionMesh = nullptr;
static pfn_ovrHmd_DestroyDistortionMesh ovrHmd_DestroyDistortionMesh = nullptr;
static pfn_ovrHmd_GetRenderScaleAndOffset ovrHmd_GetRenderScaleAndOffset = nullptr;
static pfn_ovrHmd_GetFrameTiming ovrHmd_GetFrameTiming = nullptr;
static pfn_ovrHmd_BeginFrameTiming ovrHmd_BeginFrameTiming = nullptr;
static pfn_ovrHmd_EndFrameTiming ovrHmd_EndFrameTiming = nullptr;
static pfn_ovrHmd_ResetFrameTiming ovrHmd_ResetFrameTiming = nullptr;
static pfn_ovrHmd_GetEyePoses ovrHmd_GetEyePoses = nullptr;
static pfn_ovrHmd_GetHmdPosePerEye ovrHmd_GetHmdPosePerEye = nullptr;
static pfn_ovrHmd_GetEyeTimewarpMatrices ovrHmd_GetEyeTimewarpMatrices = nullptr;
static pfn_ovrMatrix4f_Projection ovrMatrix4f_Projection = nullptr;
static pfn_ovrMatrix4f_OrthoSubProjection ovrMatrix4f_OrthoSubProjection = nullptr;
static pfn_ovr_GetTimeInSeconds ovr_GetTimeInSeconds = nullptr;
#if defined(XP_WIN)
# ifdef HAVE_64BIT_BUILD
# define OVR_LIB_NAME "libovr64.dll"
# else
# define OVR_LIB_NAME "libovr.dll"
# endif
#elif defined(XP_MACOSX)
# define OVR_LIB_NAME "libovr.dylib"
#else
# define OVR_LIB_NAME 0
#endif
static bool
InitializeOculusCAPI()
{
static PRLibrary *ovrlib = nullptr;
if (!ovrlib) {
const char *libName = OVR_LIB_NAME;
if (PR_GetEnv("OVR_LIB_NAME")) {
libName = PR_GetEnv("OVR_LIB_NAME");
}
if (!libName) {
printf_stderr("Don't know how to find Oculus VR library; missing OVR_LIB_NAME\n");
return false;
}
ovrlib = PR_LoadLibrary(libName);
if (!ovrlib) {
// Not found? Try harder. Needed mainly on OSX/etc. where
// the binary location is not in the search path.
const char *xulName = "libxul.so";
#if defined(XP_MACOSX)
xulName = "XUL";
#endif
char *xulpath = PR_GetLibraryFilePathname(xulName, (PRFuncPtr) &InitializeOculusCAPI);
if (xulpath) {
char *xuldir = strrchr(xulpath, '/');
if (xuldir) {
*xuldir = 0;
xuldir = xulpath;
char *ovrpath = PR_GetLibraryName(xuldir, libName);
ovrlib = PR_LoadLibrary(ovrpath);
PR_Free(ovrpath);
}
PR_Free(xulpath);
}
}
if (!ovrlib) {
printf_stderr("Failed to load Oculus VR library, tried '%s'\n", libName);
return false;
}
}
// was it already initialized?
if (ovr_Initialize)
return true;
#define REQUIRE_FUNCTION(_x) do { \
*(void **)&_x = (void *) PR_FindSymbol(ovrlib, #_x); \
if (!_x) { printf_stderr(#_x " symbol missing\n"); goto fail; } \
} while (0)
REQUIRE_FUNCTION(ovr_Initialize);
REQUIRE_FUNCTION(ovr_Shutdown);
REQUIRE_FUNCTION(ovrHmd_Detect);
REQUIRE_FUNCTION(ovrHmd_Create);
REQUIRE_FUNCTION(ovrHmd_Destroy);
REQUIRE_FUNCTION(ovrHmd_CreateDebug);
REQUIRE_FUNCTION(ovrHmd_GetLastError);
REQUIRE_FUNCTION(ovrHmd_AttachToWindow);
REQUIRE_FUNCTION(ovrHmd_GetEnabledCaps);
REQUIRE_FUNCTION(ovrHmd_SetEnabledCaps);
REQUIRE_FUNCTION(ovrHmd_ConfigureTracking);
REQUIRE_FUNCTION(ovrHmd_RecenterPose);
REQUIRE_FUNCTION(ovrHmd_GetTrackingState);
REQUIRE_FUNCTION(ovrHmd_GetFovTextureSize);
REQUIRE_FUNCTION(ovrHmd_GetRenderDesc);
REQUIRE_FUNCTION(ovrHmd_CreateDistortionMesh);
REQUIRE_FUNCTION(ovrHmd_DestroyDistortionMesh);
REQUIRE_FUNCTION(ovrHmd_GetRenderScaleAndOffset);
REQUIRE_FUNCTION(ovrHmd_GetFrameTiming);
REQUIRE_FUNCTION(ovrHmd_BeginFrameTiming);
REQUIRE_FUNCTION(ovrHmd_EndFrameTiming);
REQUIRE_FUNCTION(ovrHmd_ResetFrameTiming);
REQUIRE_FUNCTION(ovrHmd_GetEyePoses);
REQUIRE_FUNCTION(ovrHmd_GetHmdPosePerEye);
REQUIRE_FUNCTION(ovrHmd_GetEyeTimewarpMatrices);
REQUIRE_FUNCTION(ovrMatrix4f_Projection);
REQUIRE_FUNCTION(ovrMatrix4f_OrthoSubProjection);
REQUIRE_FUNCTION(ovr_GetTimeInSeconds);
#undef REQUIRE_FUNCTION
return true;
fail:
ovr_Initialize = nullptr;
return false;
}
#else
// we're statically linked; it's available
static bool InitializeOculusCAPI()
{
return true;
}
#endif
} // anonymous namespace
using namespace mozilla::gfx;
// Dummy nsIScreen implementation, for when we just need to specify a size
class FakeScreen : public nsIScreen
{
public:
FakeScreen(const IntRect& aScreenRect)
: mScreenRect(aScreenRect)
{ }
NS_DECL_ISUPPORTS
NS_IMETHOD GetRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) {
*l = mScreenRect.x;
*t = mScreenRect.y;
*w = mScreenRect.width;
*h = mScreenRect.height;
return NS_OK;
}
NS_IMETHOD GetAvailRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) {
return GetRect(l, t, w, h);
}
NS_IMETHOD GetRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) {
return GetRect(l, t, w, h);
}
NS_IMETHOD GetAvailRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) {
return GetAvailRect(l, t, w, h);
}
NS_IMETHOD GetId(uint32_t* aId) { *aId = (uint32_t)-1; return NS_OK; }
NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth) { *aPixelDepth = 24; return NS_OK; }
NS_IMETHOD GetColorDepth(int32_t* aColorDepth) { *aColorDepth = 24; return NS_OK; }
NS_IMETHOD LockMinimumBrightness(uint32_t aBrightness) { return NS_ERROR_NOT_AVAILABLE; }
NS_IMETHOD UnlockMinimumBrightness(uint32_t aBrightness) { return NS_ERROR_NOT_AVAILABLE; }
NS_IMETHOD GetRotation(uint32_t* aRotation) {
*aRotation = nsIScreen::ROTATION_0_DEG;
return NS_OK;
}
NS_IMETHOD SetRotation(uint32_t aRotation) { return NS_ERROR_NOT_AVAILABLE; }
NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) {
*aContentsScaleFactor = 1.0;
return NS_OK;
}
protected:
virtual ~FakeScreen() {}
IntRect mScreenRect;
};
NS_IMPL_ISUPPORTS(FakeScreen, nsIScreen)
class HMDInfoOculus : public VRHMDInfo {
friend class VRHMDManagerOculusImpl;
public:
HMDInfoOculus(ovrHmd aHMD);
bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
double zNear, double zFar) MOZ_OVERRIDE;
bool StartSensorTracking() MOZ_OVERRIDE;
VRHMDSensorState GetSensorState(double timeOffset) MOZ_OVERRIDE;
void StopSensorTracking() MOZ_OVERRIDE;
void ZeroSensor() MOZ_OVERRIDE;
void FillDistortionConstants(uint32_t whichEye,
const IntSize& textureSize, const IntRect& eyeViewport,
const Size& destViewport, const Rect& destRect,
VRDistortionConstants& values) MOZ_OVERRIDE;
void Destroy();
protected:
virtual ~HMDInfoOculus() {
Destroy();
MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus, VRHMDInfo);
}
ovrHmd mHMD;
ovrFovPort mFOVPort[2];
uint32_t mStartCount;
};
static ovrFovPort
ToFovPort(const VRFieldOfView& aFOV)
{
ovrFovPort fovPort;
fovPort.LeftTan = tan(aFOV.leftDegrees * M_PI / 180.0);
fovPort.RightTan = tan(aFOV.rightDegrees * M_PI / 180.0);
fovPort.UpTan = tan(aFOV.upDegrees * M_PI / 180.0);
fovPort.DownTan = tan(aFOV.downDegrees * M_PI / 180.0);
return fovPort;
}
static VRFieldOfView
FromFovPort(const ovrFovPort& aFOV)
{
VRFieldOfView fovInfo;
fovInfo.leftDegrees = atan(aFOV.LeftTan) * 180.0 / M_PI;
fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI;
fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI;
fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI;
return fovInfo;
}
HMDInfoOculus::HMDInfoOculus(ovrHmd aHMD)
: VRHMDInfo(VRHMDType::Oculus)
, mHMD(aHMD)
, mStartCount(0)
{
MOZ_COUNT_CTOR_INHERITED(HMDInfoOculus, VRHMDInfo);
mSupportedSensorBits = 0;
if (mHMD->TrackingCaps & ovrTrackingCap_Orientation)
mSupportedSensorBits |= State_Orientation;
if (mHMD->TrackingCaps & ovrTrackingCap_Position)
mSupportedSensorBits |= State_Position;
mRecommendedEyeFOV[Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]);
mRecommendedEyeFOV[Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]);
mMaximumEyeFOV[Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]);
mMaximumEyeFOV[Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]);
SetFOV(mRecommendedEyeFOV[Eye_Left], mRecommendedEyeFOV[Eye_Right], 0.01, 10000.0);
nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
if (screenmgr) {
screenmgr->ScreenForRect(mHMD->WindowsPos.x, mHMD->WindowsPos.y,
mHMD->Resolution.w, mHMD->Resolution.h,
getter_AddRefs(mScreen));
}
}
void
HMDInfoOculus::Destroy()
{
if (mHMD) {
ovrHmd_Destroy(mHMD);
mHMD = nullptr;
}
}
bool
HMDInfoOculus::SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
double zNear, double zFar)
{
float pixelsPerDisplayPixel = 1.0;
ovrSizei texSize[2];
uint32_t caps = ovrDistortionCap_Chromatic | ovrDistortionCap_Vignette; // XXX TODO add TimeWarp
// get eye parameters and create the mesh
for (uint32_t eye = 0; eye < NumEyes; eye++) {
mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight;
mFOVPort[eye] = ToFovPort(mEyeFOV[eye]);
ovrEyeRenderDesc renderDesc = ovrHmd_GetRenderDesc(mHMD, (ovrEyeType) eye, mFOVPort[eye]);
// these values are negated so that content can add the adjustment to its camera position,
// instead of subtracting
mEyeTranslation[eye] = Point3D(-renderDesc.ViewAdjust.x, -renderDesc.ViewAdjust.y, -renderDesc.ViewAdjust.z);
// note that we are using a right-handed coordinate system here, to match CSS
ovrMatrix4f projMatrix = ovrMatrix4f_Projection(mFOVPort[eye], zNear, zFar, true);
// XXX this is gross, we really need better methods on Matrix4x4
memcpy(&mEyeProjectionMatrix[eye], projMatrix.M, sizeof(ovrMatrix4f));
mEyeProjectionMatrix[eye].Transpose();
texSize[eye] = ovrHmd_GetFovTextureSize(mHMD, (ovrEyeType) eye, mFOVPort[eye], pixelsPerDisplayPixel);
ovrDistortionMesh mesh;
bool ok = ovrHmd_CreateDistortionMesh(mHMD, (ovrEyeType) eye, mFOVPort[eye], caps, &mesh);
if (!ok)
return false;
mDistortionMesh[eye].mVertices.SetLength(mesh.VertexCount);
mDistortionMesh[eye].mIndices.SetLength(mesh.IndexCount);
ovrDistortionVertex *srcv = mesh.pVertexData;
VRDistortionVertex *destv = mDistortionMesh[eye].mVertices.Elements();
memset(destv, 0, mesh.VertexCount * sizeof(VRDistortionVertex));
for (uint32_t i = 0; i < mesh.VertexCount; ++i) {
destv[i].pos[0] = srcv[i].ScreenPosNDC.x;
destv[i].pos[1] = srcv[i].ScreenPosNDC.y;
destv[i].texR[0] = srcv[i].TanEyeAnglesR.x;
destv[i].texR[1] = srcv[i].TanEyeAnglesR.y;
destv[i].texG[0] = srcv[i].TanEyeAnglesG.x;
destv[i].texG[1] = srcv[i].TanEyeAnglesG.y;
destv[i].texB[0] = srcv[i].TanEyeAnglesB.x;
destv[i].texB[1] = srcv[i].TanEyeAnglesB.y;
destv[i].genericAttribs[0] = srcv[i].VignetteFactor;
destv[i].genericAttribs[1] = srcv[i].TimeWarpFactor;
}
memcpy(mDistortionMesh[eye].mIndices.Elements(), mesh.pIndexData, mesh.IndexCount * sizeof(uint16_t));
ovrHmd_DestroyDistortionMesh(&mesh);
}
// take the max of both for eye resolution
mEyeResolution.width = std::max(texSize[Eye_Left].w, texSize[Eye_Right].w);
mEyeResolution.height = std::max(texSize[Eye_Left].h, texSize[Eye_Right].h);
mConfiguration.hmdType = mType;
mConfiguration.value = 0;
mConfiguration.fov[0] = aFOVLeft;
mConfiguration.fov[1] = aFOVRight;
return true;
//* need to call this during rendering each frame I think? */
//ovrHmd_GetRenderScaleAndOffset(fovPort, texSize, renderViewport, uvScaleOffsetOut);
}
void
HMDInfoOculus::FillDistortionConstants(uint32_t whichEye,
const IntSize& textureSize,
const IntRect& eyeViewport,
const Size& destViewport,
const Rect& destRect,
VRDistortionConstants& values)
{
ovrSizei texSize = { textureSize.width, textureSize.height };
ovrRecti eyePort = { { eyeViewport.x, eyeViewport.y }, { eyeViewport.width, eyeViewport.height } };
ovrVector2f scaleOut[2];
ovrHmd_GetRenderScaleAndOffset(mFOVPort[whichEye], texSize, eyePort, scaleOut);
values.eyeToSourceScaleAndOffset[0] = scaleOut[0].x;
values.eyeToSourceScaleAndOffset[1] = scaleOut[0].y;
values.eyeToSourceScaleAndOffset[2] = scaleOut[1].x;
values.eyeToSourceScaleAndOffset[3] = scaleOut[1].y;
// These values are in clip space [-1..1] range, but we're providing
// scaling in the 0..2 space for sanity.
// this is the destRect in clip space
float x0 = destRect.x / destViewport.width * 2.0 - 1.0;
float x1 = (destRect.x + destRect.width) / destViewport.width * 2.0 - 1.0;
float y0 = destRect.y / destViewport.height * 2.0 - 1.0;
float y1 = (destRect.y + destRect.height) / destViewport.height * 2.0 - 1.0;
// offset
values.destinationScaleAndOffset[0] = (x0+x1) / 2.0;
values.destinationScaleAndOffset[1] = (y0+y1) / 2.0;
// scale
values.destinationScaleAndOffset[2] = destRect.width / destViewport.width;
values.destinationScaleAndOffset[3] = destRect.height / destViewport.height;
}
bool
HMDInfoOculus::StartSensorTracking()
{
if (mStartCount == 0) {
bool ok = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0);
if (!ok)
return false;
}
mStartCount++;
return true;
}
void
HMDInfoOculus::StopSensorTracking()
{
if (--mStartCount == 0) {
ovrHmd_ConfigureTracking(mHMD, 0, 0);
}
}
void
HMDInfoOculus::ZeroSensor()
{
ovrHmd_RecenterPose(mHMD);
}
VRHMDSensorState
HMDInfoOculus::GetSensorState(double timeOffset)
{
VRHMDSensorState result;
result.Clear();
// XXX this is the wrong time base for timeOffset; we need to figure out how to synchronize
// the Oculus time base and the browser one.
ovrTrackingState state = ovrHmd_GetTrackingState(mHMD, ovr_GetTimeInSeconds() + timeOffset);
ovrPoseStatef& pose(state.HeadPose);
result.timestamp = pose.TimeInSeconds;
if (state.StatusFlags & ovrStatus_OrientationTracked) {
result.flags |= State_Orientation;
result.orientation[0] = pose.ThePose.Orientation.x;
result.orientation[1] = pose.ThePose.Orientation.y;
result.orientation[2] = pose.ThePose.Orientation.z;
result.orientation[3] = pose.ThePose.Orientation.w;
result.angularVelocity[0] = pose.AngularVelocity.x;
result.angularVelocity[1] = pose.AngularVelocity.y;
result.angularVelocity[2] = pose.AngularVelocity.z;
result.angularAcceleration[0] = pose.AngularAcceleration.x;
result.angularAcceleration[1] = pose.AngularAcceleration.y;
result.angularAcceleration[2] = pose.AngularAcceleration.z;
}
if (state.StatusFlags & ovrStatus_PositionTracked) {
result.flags |= State_Position;
result.position[0] = pose.ThePose.Position.x;
result.position[1] = pose.ThePose.Position.y;
result.position[2] = pose.ThePose.Position.z;
result.linearVelocity[0] = pose.LinearVelocity.x;
result.linearVelocity[1] = pose.LinearVelocity.y;
result.linearVelocity[2] = pose.LinearVelocity.z;
result.linearAcceleration[0] = pose.LinearAcceleration.x;
result.linearAcceleration[1] = pose.LinearAcceleration.y;
result.linearAcceleration[2] = pose.LinearAcceleration.z;
}
return result;
}
namespace mozilla {
namespace gfx {
class VRHMDManagerOculusImpl {
public:
VRHMDManagerOculusImpl() : mOculusInitialized(false), mOculusPlatformInitialized(false)
{ }
bool PlatformInit();
bool Init();
void Destroy();
void GetOculusHMDs(nsTArray<nsRefPtr<VRHMDInfo> >& aHMDResult);
protected:
nsTArray<nsRefPtr<HMDInfoOculus>> mOculusHMDs;
bool mOculusInitialized;
bool mOculusPlatformInitialized;
};
} // namespace gfx
} // namespace mozilla
VRHMDManagerOculusImpl *VRHMDManagerOculus::mImpl = nullptr;
// These just forward to the Impl class, to have a non-static container for various
// objects.
bool
VRHMDManagerOculus::PlatformInit()
{
if (!mImpl) {
mImpl = new VRHMDManagerOculusImpl;
}
return mImpl->PlatformInit();
}
bool
VRHMDManagerOculus::Init()
{
if (!mImpl) {
mImpl = new VRHMDManagerOculusImpl;
}
return mImpl->Init();
}
void
VRHMDManagerOculus::GetOculusHMDs(nsTArray<nsRefPtr<VRHMDInfo>>& aHMDResult)
{
if (!mImpl) {
mImpl = new VRHMDManagerOculusImpl;
}
mImpl->GetOculusHMDs(aHMDResult);
}
void
VRHMDManagerOculus::Destroy()
{
if (!mImpl)
return;
mImpl->Destroy();
delete mImpl;
mImpl = nullptr;
}
bool
VRHMDManagerOculusImpl::PlatformInit()
{
if (mOculusPlatformInitialized)
return true;
if (!gfxPrefs::VREnabled())
return false;
if (!InitializeOculusCAPI())
return false;
bool ok = ovr_Initialize();
if (!ok)
return false;
mOculusPlatformInitialized = true;
return true;
}
bool
VRHMDManagerOculusImpl::Init()
{
if (mOculusInitialized)
return true;
if (!PlatformInit())
return false;
int count = ovrHmd_Detect();
for (int i = 0; i < count; ++i) {
ovrHmd hmd = ovrHmd_Create(i);
nsRefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd);
mOculusHMDs.AppendElement(oc);
}
// VRAddTestDevices == 1: add test device only if no real devices present
// VRAddTestDevices == 2: add test device always
if ((count == 0 && gfxPrefs::VRAddTestDevices() == 1) ||
(gfxPrefs::VRAddTestDevices() == 2))
{
ovrHmd hmd = ovrHmd_CreateDebug(ovrHmd_DK2);
nsRefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd);
mOculusHMDs.AppendElement(oc);
}
mOculusInitialized = true;
return true;
}
void
VRHMDManagerOculusImpl::Destroy()
{
if (!mOculusInitialized)
return;
for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
mOculusHMDs[i]->Destroy();
}
mOculusHMDs.Clear();
ovr_Shutdown();
mOculusInitialized = false;
}
void
VRHMDManagerOculusImpl::GetOculusHMDs(nsTArray<nsRefPtr<VRHMDInfo>>& aHMDResult)
{
Init();
for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
aHMDResult.AppendElement(mOculusHMDs[i]);
}
}

217
gfx/thebes/gfxVR.h Normal file
View File

@ -0,0 +1,217 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_VR_H
#define GFX_VR_H
#include "nsTArray.h"
#include "nsIScreen.h"
#include "nsCOMPtr.h"
#include "nsRefPtr.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/EnumeratedArray.h"
namespace mozilla {
namespace gfx {
MOZ_BEGIN_ENUM_CLASS(VRHMDType, uint16_t)
Oculus,
NumHMDTypes
MOZ_END_ENUM_CLASS(VRHMDType)
struct VRFieldOfView {
static VRFieldOfView FromCSSPerspectiveInfo(double aPerspectiveDistance,
const Point& aPerspectiveOrigin,
const Point& aTransformOrigin,
const Rect& aContentRectangle)
{
/**/
return VRFieldOfView();
}
VRFieldOfView() {}
VRFieldOfView(double up, double right, double down, double left)
: upDegrees(up), rightDegrees(right), downDegrees(down), leftDegrees(left)
{}
bool operator==(const VRFieldOfView& other) const {
return other.upDegrees == upDegrees &&
other.downDegrees == downDegrees &&
other.rightDegrees == rightDegrees &&
other.leftDegrees == leftDegrees;
}
bool operator!=(const VRFieldOfView& other) const {
return !(*this == other);
}
bool IsZero() const {
return upDegrees == 0.0 ||
rightDegrees == 0.0 ||
downDegrees == 0.0 ||
leftDegrees == 0.0;
}
double upDegrees;
double rightDegrees;
double downDegrees;
double leftDegrees;
};
// 12 floats per vertex. Position, tex coordinates
// for each channel, and 4 generic attributes
struct VRDistortionConstants {
float eyeToSourceScaleAndOffset[4];
float destinationScaleAndOffset[4];
};
struct VRDistortionVertex {
float pos[2];
float texR[2];
float texG[2];
float texB[2];
float genericAttribs[4];
};
struct VRDistortionMesh {
nsTArray<VRDistortionVertex> mVertices;
nsTArray<uint16_t> mIndices;
};
struct VRHMDSensorState {
double timestamp;
uint32_t flags;
float orientation[4];
float position[3];
float angularVelocity[3];
float angularAcceleration[3];
float linearVelocity[3];
float linearAcceleration[3];
void Clear() {
memset(this, 0, sizeof(VRHMDSensorState));
}
};
/* A pure data struct that can be used to see if
* the configuration of one HMDInfo matches another; for rendering purposes,
* really asking "can the rendering details of this one be used for the other"
*/
struct VRHMDConfiguration {
VRHMDConfiguration() : hmdType(VRHMDType::NumHMDTypes) {}
bool operator==(const VRHMDConfiguration& other) const {
return hmdType == other.hmdType &&
value == other.value &&
fov[0] == other.fov[0] &&
fov[1] == other.fov[1];
}
bool operator!=(const VRHMDConfiguration& other) const {
return hmdType != other.hmdType ||
value != other.value ||
fov[0] != other.fov[0] ||
fov[1] != other.fov[1];
}
bool IsValid() const {
return hmdType != VRHMDType::NumHMDTypes;
}
VRHMDType hmdType;
uint32_t value;
VRFieldOfView fov[2];
};
class VRHMDInfo {
public:
enum Eye {
Eye_Left,
Eye_Right,
NumEyes
};
enum StateValidFlags {
State_Position = 1 << 1,
State_Orientation = 1 << 2
};
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRHMDInfo)
VRHMDType GetType() const { return mType; }
virtual const VRFieldOfView& GetRecommendedEyeFOV(uint32_t whichEye) { return mRecommendedEyeFOV[whichEye]; }
virtual const VRFieldOfView& GetMaximumEyeFOV(uint32_t whichEye) { return mMaximumEyeFOV[whichEye]; }
const VRHMDConfiguration& GetConfiguration() const { return mConfiguration; }
/* set the FOV for this HMD unit; this triggers a computation of all the remaining bits. Returns false if it fails */
virtual bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
double zNear, double zFar) = 0;
const VRFieldOfView& GetEyeFOV(uint32_t whichEye) { return mEyeFOV[whichEye]; }
/* Suggested resolution for rendering a single eye.
* Assumption is that left/right rendering will be 2x of this size.
* XXX fix this for vertical displays
*/
const IntSize& SuggestedEyeResolution() const { return mEyeResolution; }
const Point3D& GetEyeTranslation(uint32_t whichEye) const { return mEyeTranslation[whichEye]; }
const Matrix4x4& GetEyeProjectionMatrix(uint32_t whichEye) const { return mEyeProjectionMatrix[whichEye]; }
virtual uint32_t GetSupportedSensorStateBits() { return mSupportedSensorBits; }
virtual bool StartSensorTracking() = 0;
virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0) = 0;
virtual void StopSensorTracking() = 0;
virtual void ZeroSensor() = 0;
virtual void FillDistortionConstants(uint32_t whichEye,
const IntSize& textureSize, // the full size of the texture
const IntRect& eyeViewport, // the viewport within the texture for the current eye
const Size& destViewport, // the size of the destination viewport
const Rect& destRect, // the rectangle within the dest viewport that this should be rendered
VRDistortionConstants& values) = 0;
virtual const VRDistortionMesh& GetDistortionMesh(uint32_t whichEye) const { return mDistortionMesh[whichEye]; }
// The nsIScreen that represents this device
virtual nsIScreen* GetScreen() { return mScreen; }
protected:
VRHMDInfo(VRHMDType aType) : mType(aType) { MOZ_COUNT_CTOR(VRHMDInfo); }
virtual ~VRHMDInfo() { MOZ_COUNT_DTOR(VRHMDInfo); }
VRHMDType mType;
VRHMDConfiguration mConfiguration;
VRFieldOfView mEyeFOV[NumEyes];
IntSize mEyeResolution;
Point3D mEyeTranslation[NumEyes];
Matrix4x4 mEyeProjectionMatrix[NumEyes];
VRDistortionMesh mDistortionMesh[NumEyes];
uint32_t mSupportedSensorBits;
VRFieldOfView mRecommendedEyeFOV[NumEyes];
VRFieldOfView mMaximumEyeFOV[NumEyes];
nsCOMPtr<nsIScreen> mScreen;
};
class VRHMDManagerOculusImpl;
class VRHMDManagerOculus {
static VRHMDManagerOculusImpl *mImpl;
public:
static bool PlatformInit();
static bool Init();
static void Destroy();
static void GetOculusHMDs(nsTArray<nsRefPtr<VRHMDInfo> >& aHMDResult);
};
} // namespace gfx
} // namespace mozilla
#endif /* GFX_VR_H */

View File

@ -87,16 +87,18 @@ DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget)
if (aDrawTarget.GetBackendType() == BackendType::CAIRO) {
cairo_surface_t *surf = (cairo_surface_t*)
aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
cairo_surface_type_t surfaceType = cairo_surface_get_type(surf);
if (surfaceType == CAIRO_SURFACE_TYPE_WIN32 ||
surfaceType == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
mDC = cairo_win32_surface_get_dc(surf);
mNeedsRelease = false;
SaveDC(mDC);
cairo_t* ctx = (cairo_t*)
aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT);
cairo_scaled_font_t* scaled = cairo_get_scaled_font(ctx);
cairo_win32_scaled_font_select_font(scaled, mDC);
if (surf) {
cairo_surface_type_t surfaceType = cairo_surface_get_type(surf);
if (surfaceType == CAIRO_SURFACE_TYPE_WIN32 ||
surfaceType == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
mDC = cairo_win32_surface_get_dc(surf);
mNeedsRelease = false;
SaveDC(mDC);
cairo_t* ctx = (cairo_t*)
aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT);
cairo_scaled_font_t* scaled = cairo_get_scaled_font(ctx);
cairo_win32_scaled_font_select_font(scaled, mDC);
}
}
if (!mDC) {
mDC = GetDC(nullptr);

View File

@ -49,6 +49,7 @@ EXPORTS += [
'gfxTypes.h',
'gfxUserFontSet.h',
'gfxUtils.h',
'gfxVR.h',
'GraphicsFilter.h',
'RoundedRect.h',
]
@ -243,6 +244,7 @@ UNIFIED_SOURCES += [
'gfxTextRun.cpp',
'gfxUserFontSet.cpp',
'gfxUtils.cpp',
'gfxVR.cpp',
'nsUnicodeRange.cpp',
]

View File

@ -0,0 +1,228 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* This file contains just the needed struct definitions for
* interacting with the Oculus VR C API, without needing to #include
* OVR_CAPI.h directly. Note that it uses the same type names as the
* CAPI, and cannot be #included at the same time as OVR_CAPI.h. It
* does not include the entire C API, just want's needed.
*/
#ifdef OVR_CAPI_h
#warning OVR_CAPI.h included before ovr_capi_dynamic.h, skpping this
#define mozilla_ovr_capi_dynamic_h_
#endif
#ifndef mozilla_ovr_capi_dynamic_h_
#define mozilla_ovr_capi_dynamic_h_
#define OVR_CAPI_LIMITED_MOZILLA 1
#ifdef __cplusplus
extern "C" {
#endif
typedef char ovrBool;
typedef struct { int x, y; } ovrVector2i;
typedef struct { int w, h; } ovrSizei;
typedef struct { ovrVector2i Pos; ovrSizei Size; } ovrRecti;
typedef struct { float x, y, z, w; } ovrQuatf;
typedef struct { float x, y; } ovrVector2f;
typedef struct { float x, y, z; } ovrVector3f;
typedef struct { float M[4][4]; } ovrMatrix4f;
typedef struct {
ovrQuatf Orientation;
ovrVector3f Position;
} ovrPosef;
typedef struct {
ovrPosef ThePose;
ovrVector3f AngularVelocity;
ovrVector3f LinearVelocity;
ovrVector3f AngularAcceleration;
ovrVector3f LinearAcceleration;
double TimeInSeconds;
} ovrPoseStatef;
typedef struct {
float UpTan;
float DownTan;
float LeftTan;
float RightTan;
} ovrFovPort;
typedef enum {
ovrHmd_None = 0,
ovrHmd_DK1 = 3,
ovrHmd_DKHD = 4,
ovrHmd_DK2 = 6,
ovrHmd_Other
} ovrHmdType;
typedef enum {
ovrHmdCap_Present = 0x0001,
ovrHmdCap_Available = 0x0002,
ovrHmdCap_Captured = 0x0004,
ovrHmdCap_ExtendDesktop = 0x0008,
ovrHmdCap_DisplayOff = 0x0040,
ovrHmdCap_LowPersistence = 0x0080,
ovrHmdCap_DynamicPrediction = 0x0200,
ovrHmdCap_NoVSync = 0x1000,
ovrHmdCap_NoMirrorToWindow = 0x2000
} ovrHmdCapBits;
typedef enum
{
ovrTrackingCap_Orientation = 0x0010,
ovrTrackingCap_MagYawCorrection = 0x0020,
ovrTrackingCap_Position = 0x0040,
ovrTrackingCap_Idle = 0x0100
} ovrTrackingCaps;
typedef enum {
ovrDistortionCap_Chromatic = 0x01,
ovrDistortionCap_TimeWarp = 0x02,
ovrDistortionCap_Vignette = 0x08,
ovrDistortionCap_NoRestore = 0x10,
ovrDistortionCap_FlipInput = 0x20,
ovrDistortionCap_SRGB = 0x40,
ovrDistortionCap_Overdrive = 0x80,
ovrDistortionCap_ProfileNoTimewarpSpinWaits = 0x10000
} ovrDistortionCaps;
typedef enum {
ovrEye_Left = 0,
ovrEye_Right = 1,
ovrEye_Count = 2
} ovrEyeType;
typedef struct ovrHmdDesc_ {
void* Handle;
ovrHmdType Type;
const char* ProductName;
const char* Manufacturer;
short VendorId;
short ProductId;
char SerialNumber[24];
short FirmwareMajor;
short FirmwareMinor;
float CameraFrustumHFovInRadians;
float CameraFrustumVFovInRadians;
float CameraFrustumNearZInMeters;
float CameraFrustumFarZInMeters;
unsigned int HmdCaps;
unsigned int TrackingCaps;
unsigned int DistortionCaps;
ovrFovPort DefaultEyeFov[ovrEye_Count];
ovrFovPort MaxEyeFov[ovrEye_Count];
ovrEyeType EyeRenderOrder[ovrEye_Count];
ovrSizei Resolution;
ovrVector2i WindowsPos;
const char* DisplayDeviceName;
int DisplayId;
} ovrHmdDesc;
typedef const ovrHmdDesc* ovrHmd;
typedef enum {
ovrStatus_OrientationTracked = 0x0001,
ovrStatus_PositionTracked = 0x0002,
ovrStatus_CameraPoseTracked = 0x0004,
ovrStatus_PositionConnected = 0x0020,
ovrStatus_HmdConnected = 0x0080
} ovrStatusBits;
typedef struct ovrSensorData_ {
ovrVector3f Accelerometer;
ovrVector3f Gyro;
ovrVector3f Magnetometer;
float Temperature;
float TimeInSeconds;
} ovrSensorData;
typedef struct ovrTrackingState_ {
ovrPoseStatef HeadPose;
ovrPosef CameraPose;
ovrPosef LeveledCameraPose;
ovrSensorData RawSensorData;
unsigned int StatusFlags;
double LastVisionProcessingTime;
double LastVisionFrameLatency;
uint32_t LastCameraFrameCounter;
} ovrTrackingState;
typedef struct ovrFrameTiming_ {
float DeltaSeconds;
double ThisFrameSeconds;
double TimewarpPointSeconds;
double NextFrameSeconds;
double ScanoutMidpointSeconds;
double EyeScanoutSeconds[2];
} ovrFrameTiming;
typedef struct ovrEyeRenderDesc_ {
ovrEyeType Eye;
ovrFovPort Fov;
ovrRecti DistortedViewport;
ovrVector2f PixelsPerTanAngleAtCenter;
ovrVector3f ViewAdjust;
} ovrEyeRenderDesc;
typedef struct ovrDistortionVertex_ {
ovrVector2f ScreenPosNDC;
float TimeWarpFactor;
float VignetteFactor;
ovrVector2f TanEyeAnglesR;
ovrVector2f TanEyeAnglesG;
ovrVector2f TanEyeAnglesB;
} ovrDistortionVertex;
typedef struct ovrDistortionMesh_ {
ovrDistortionVertex* pVertexData;
unsigned short* pIndexData;
unsigned int VertexCount;
unsigned int IndexCount;
} ovrDistortionMesh;
typedef ovrBool (*pfn_ovr_Initialize)();
typedef void (*pfn_ovr_Shutdown)();
typedef int (*pfn_ovrHmd_Detect)();
typedef ovrHmd (*pfn_ovrHmd_Create)(int index);
typedef void (*pfn_ovrHmd_Destroy)(ovrHmd hmd);
typedef ovrHmd (*pfn_ovrHmd_CreateDebug)(ovrHmdType type);
typedef const char* (*pfn_ovrHmd_GetLastError)(ovrHmd hmd);
typedef ovrBool (*pfn_ovrHmd_AttachToWindow)(ovrHmd hmd, void* window, const ovrRecti* destMirrorRect, const ovrRecti* sourceRenderTargetRect);
typedef unsigned int (*pfn_ovrHmd_GetEnabledCaps)(ovrHmd hmd);
typedef void (*pfn_ovrHmd_SetEnabledCaps)(ovrHmd hmd, unsigned int hmdCaps);
typedef ovrBool (*pfn_ovrHmd_ConfigureTracking)(ovrHmd hmd, unsigned int supportedTrackingCaps, unsigned int requiredTrackingCaps);
typedef void (*pfn_ovrHmd_RecenterPose)(ovrHmd hmd);
typedef ovrTrackingState (*pfn_ovrHmd_GetTrackingState)(ovrHmd hmd, double absTime);
typedef ovrSizei (*pfn_ovrHmd_GetFovTextureSize)(ovrHmd hmd, ovrEyeType eye, ovrFovPort fov, float pixelsPerDisplayPixel);
typedef ovrEyeRenderDesc (*pfn_ovrHmd_GetRenderDesc)(ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov);
typedef ovrBool (*pfn_ovrHmd_CreateDistortionMesh)(ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov, unsigned int distortionCaps, ovrDistortionMesh *meshData);
typedef void (*pfn_ovrHmd_DestroyDistortionMesh)(ovrDistortionMesh* meshData);
typedef void (*pfn_ovrHmd_GetRenderScaleAndOffset)(ovrFovPort fov, ovrSizei textureSize, ovrRecti renderViewport, ovrVector2f uvScaleOffsetOut[2]);
typedef ovrFrameTiming (*pfn_ovrHmd_GetFrameTiming)(ovrHmd hmd, unsigned int frameIndex);
typedef ovrFrameTiming (*pfn_ovrHmd_BeginFrameTiming)(ovrHmd hmd, unsigned int frameIndex);
typedef void (*pfn_ovrHmd_EndFrameTiming)(ovrHmd hmd);
typedef void (*pfn_ovrHmd_ResetFrameTiming)(ovrHmd hmd, unsigned int frameIndex, bool vsync);
typedef void (*pfn_ovrHmd_GetEyePoses)(ovrHmd hmd, unsigned int frameIndex, ovrVector3f hmdToEyeViewOffset[2], ovrPosef outEyePoses[2], ovrTrackingState* outHmdTrackingState);
typedef ovrPosef (*pfn_ovrHmd_GetHmdPosePerEye)(ovrHmd hmd, ovrEyeType eye);
typedef void (*pfn_ovrHmd_GetEyeTimewarpMatrices)(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2]);
typedef ovrMatrix4f (*pfn_ovrMatrix4f_Projection) (ovrFovPort fov, float znear, float zfar, ovrBool rightHanded );
typedef ovrMatrix4f (*pfn_ovrMatrix4f_OrthoSubProjection) (ovrFovPort fov, ovrVector2f orthoScale, float orthoDistance, float eyeViewAdjustX);
typedef double (*pfn_ovr_GetTimeInSeconds)();
#ifdef __cplusplus
}
#endif
#endif /* mozilla_ovr_capi_dynamic_h_ */

View File

@ -754,6 +754,29 @@ AsmJSModule::staticallyLink(ExclusiveContext *cx)
MOZ_ASSERT(isStaticallyLinked());
}
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
static inline size_t
ViewTypeByteSize(AsmJSHeapAccess::ViewType vt)
{
switch (vt) {
case AsmJSHeapAccess::Int8:
case AsmJSHeapAccess::Uint8:
case AsmJSHeapAccess::Uint8Clamped:
case AsmJSHeapAccess::Int16:
case AsmJSHeapAccess::Uint16:
case AsmJSHeapAccess::Int32:
case AsmJSHeapAccess::Uint32:
case AsmJSHeapAccess::Float32:
case AsmJSHeapAccess::Float64:
return 1 << TypedArrayShift(Scalar::Type(vt));
case AsmJSHeapAccess::Float32x4:
case AsmJSHeapAccess::Int32x4:
return 16;
}
MOZ_CRASH("unexpected view type");
}
#endif // JS_CODEGEN_X86 || JS_CODEGEN_X64
void
AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx)
{
@ -767,11 +790,17 @@ AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx
#if defined(JS_CODEGEN_X86)
uint8_t *heapOffset = heap->dataPointer();
void *heapLength = (void*)heap->byteLength();
for (unsigned i = 0; i < heapAccesses_.length(); i++) {
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
if (access.hasLengthCheck())
X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
if (access.hasLengthCheck()) {
// An access is out-of-bounds iff
// 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.)
AsmJSHeapAccess::ViewType vt = access.viewType();
X86Assembler::setPointer(access.patchLengthAt(code_),
(void*)(heap->byteLength() + 1 - ViewTypeByteSize(vt)));
}
void *addr = access.patchOffsetAt(code_);
uint32_t disp = reinterpret_cast<uint32_t>(X86Assembler::getPointer(addr));
MOZ_ASSERT(disp <= INT32_MAX);
@ -788,8 +817,11 @@ AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx
int32_t heapLength = int32_t(intptr_t(heap->byteLength()));
for (size_t i = 0; i < heapAccesses_.length(); i++) {
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
if (access.hasLengthCheck())
X86Assembler::setInt32(access.patchLengthAt(code_), heapLength);
if (access.hasLengthCheck()) {
// See comment above for x86 codegen.
X86Assembler::setInt32(access.patchLengthAt(code_),
heapLength + 1 - ViewTypeByteSize(access.viewType()));
}
}
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
uint32_t heapLength = heap->byteLength();

View File

@ -331,45 +331,72 @@ ContextToPC(CONTEXT *context)
#if defined(JS_CODEGEN_X64)
template <class T>
static void
SetXMMRegToNaN(bool isFloat32, T *xmm_reg)
SetXMMRegToNaN(AsmJSHeapAccess::ViewType viewType, T *xmm_reg)
{
if (isFloat32) {
switch (viewType) {
case AsmJSHeapAccess::Float32: {
JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float));
float *floats = reinterpret_cast<float*>(xmm_reg);
floats[0] = GenericNaN();
floats[1] = 0;
floats[2] = 0;
floats[3] = 0;
} else {
break;
}
case AsmJSHeapAccess::Float64: {
JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double));
double *dbls = reinterpret_cast<double*>(xmm_reg);
dbls[0] = GenericNaN();
dbls[1] = 0;
break;
}
case AsmJSHeapAccess::Float32x4: {
JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float));
float *floats = reinterpret_cast<float*>(xmm_reg);
for (unsigned i = 0; i < 4; i++)
floats[i] = GenericNaN();
break;
}
case AsmJSHeapAccess::Int32x4: {
JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(int32_t));
int32_t *ints = reinterpret_cast<int32_t*>(xmm_reg);
for (unsigned i = 0; i < 4; i++)
ints[i] = 0;
break;
}
case AsmJSHeapAccess::Int8:
case AsmJSHeapAccess::Uint8:
case AsmJSHeapAccess::Int16:
case AsmJSHeapAccess::Uint16:
case AsmJSHeapAccess::Int32:
case AsmJSHeapAccess::Uint32:
case AsmJSHeapAccess::Uint8Clamped:
MOZ_CRASH("unexpected type in SetXMMRegToNaN");
}
}
# if !defined(XP_MACOSX)
static void
SetRegisterToCoercedUndefined(CONTEXT *context, bool isFloat32, AnyRegister reg)
SetRegisterToCoercedUndefined(CONTEXT *context, AsmJSHeapAccess::ViewType viewType, AnyRegister reg)
{
if (reg.isFloat()) {
switch (reg.fpu().code()) {
case X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 0)); break;
case X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 1)); break;
case X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 2)); break;
case X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 3)); break;
case X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 4)); break;
case X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 5)); break;
case X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 6)); break;
case X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 7)); break;
case X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 8)); break;
case X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 9)); break;
case X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 10)); break;
case X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 11)); break;
case X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 12)); break;
case X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 13)); break;
case X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 14)); break;
case X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &XMM_sig(context, 15)); break;
case X86Registers::xmm0: SetXMMRegToNaN(viewType, &XMM_sig(context, 0)); break;
case X86Registers::xmm1: SetXMMRegToNaN(viewType, &XMM_sig(context, 1)); break;
case X86Registers::xmm2: SetXMMRegToNaN(viewType, &XMM_sig(context, 2)); break;
case X86Registers::xmm3: SetXMMRegToNaN(viewType, &XMM_sig(context, 3)); break;
case X86Registers::xmm4: SetXMMRegToNaN(viewType, &XMM_sig(context, 4)); break;
case X86Registers::xmm5: SetXMMRegToNaN(viewType, &XMM_sig(context, 5)); break;
case X86Registers::xmm6: SetXMMRegToNaN(viewType, &XMM_sig(context, 6)); break;
case X86Registers::xmm7: SetXMMRegToNaN(viewType, &XMM_sig(context, 7)); break;
case X86Registers::xmm8: SetXMMRegToNaN(viewType, &XMM_sig(context, 8)); break;
case X86Registers::xmm9: SetXMMRegToNaN(viewType, &XMM_sig(context, 9)); break;
case X86Registers::xmm10: SetXMMRegToNaN(viewType, &XMM_sig(context, 10)); break;
case X86Registers::xmm11: SetXMMRegToNaN(viewType, &XMM_sig(context, 11)); break;
case X86Registers::xmm12: SetXMMRegToNaN(viewType, &XMM_sig(context, 12)); break;
case X86Registers::xmm13: SetXMMRegToNaN(viewType, &XMM_sig(context, 13)); break;
case X86Registers::xmm14: SetXMMRegToNaN(viewType, &XMM_sig(context, 14)); break;
case X86Registers::xmm15: SetXMMRegToNaN(viewType, &XMM_sig(context, 15)); break;
default: MOZ_CRASH();
}
} else {
@ -455,7 +482,7 @@ HandleFault(PEXCEPTION_POINTERS exception)
// 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->isFloat32Load(), heapAccess->loadedReg());
SetRegisterToCoercedUndefined(context, heapAccess->viewType(), heapAccess->loadedReg());
*ppc += heapAccess->opLength();
return true;
@ -505,24 +532,24 @@ SetRegisterToCoercedUndefined(mach_port_t rtThread, x86_thread_state64_t &state,
if (kret != KERN_SUCCESS)
return false;
bool f32 = heapAccess.isFloat32Load();
AsmJSHeapAccess::ViewType viewType = heapAccess.viewType();
switch (heapAccess.loadedReg().fpu().code()) {
case X86Registers::xmm0: SetXMMRegToNaN(f32, &fstate.__fpu_xmm0); break;
case X86Registers::xmm1: SetXMMRegToNaN(f32, &fstate.__fpu_xmm1); break;
case X86Registers::xmm2: SetXMMRegToNaN(f32, &fstate.__fpu_xmm2); break;
case X86Registers::xmm3: SetXMMRegToNaN(f32, &fstate.__fpu_xmm3); break;
case X86Registers::xmm4: SetXMMRegToNaN(f32, &fstate.__fpu_xmm4); break;
case X86Registers::xmm5: SetXMMRegToNaN(f32, &fstate.__fpu_xmm5); break;
case X86Registers::xmm6: SetXMMRegToNaN(f32, &fstate.__fpu_xmm6); break;
case X86Registers::xmm7: SetXMMRegToNaN(f32, &fstate.__fpu_xmm7); break;
case X86Registers::xmm8: SetXMMRegToNaN(f32, &fstate.__fpu_xmm8); break;
case X86Registers::xmm9: SetXMMRegToNaN(f32, &fstate.__fpu_xmm9); break;
case X86Registers::xmm10: SetXMMRegToNaN(f32, &fstate.__fpu_xmm10); break;
case X86Registers::xmm11: SetXMMRegToNaN(f32, &fstate.__fpu_xmm11); break;
case X86Registers::xmm12: SetXMMRegToNaN(f32, &fstate.__fpu_xmm12); break;
case X86Registers::xmm13: SetXMMRegToNaN(f32, &fstate.__fpu_xmm13); break;
case X86Registers::xmm14: SetXMMRegToNaN(f32, &fstate.__fpu_xmm14); break;
case X86Registers::xmm15: SetXMMRegToNaN(f32, &fstate.__fpu_xmm15); break;
case X86Registers::xmm0: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm0); break;
case X86Registers::xmm1: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm1); break;
case X86Registers::xmm2: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm2); break;
case X86Registers::xmm3: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm3); break;
case X86Registers::xmm4: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm4); break;
case X86Registers::xmm5: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm5); break;
case X86Registers::xmm6: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm6); break;
case X86Registers::xmm7: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm7); break;
case X86Registers::xmm8: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm8); break;
case X86Registers::xmm9: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm9); break;
case X86Registers::xmm10: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm10); break;
case X86Registers::xmm11: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm11); break;
case X86Registers::xmm12: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm12); break;
case X86Registers::xmm13: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm13); break;
case X86Registers::xmm14: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm14); break;
case X86Registers::xmm15: SetXMMRegToNaN(viewType, &fstate.__fpu_xmm15); break;
default: MOZ_CRASH();
}
@ -847,7 +874,7 @@ HandleFault(int signum, siginfo_t *info, void *ctx)
// 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->isFloat32Load(), heapAccess->loadedReg());
SetRegisterToCoercedUndefined(context, heapAccess->viewType(), heapAccess->loadedReg());
*ppc += heapAccess->opLength();
return true;

View File

@ -566,13 +566,13 @@ class Type
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type");
}
Type simdToScalarType() const {
AsmJSSimdType simdType() const {
MOZ_ASSERT(isSimd());
switch (which_) {
case Int32x4:
return Signed;
return AsmJSSimdType_int32x4;
case Float32x4:
return Float;
return AsmJSSimdType_float32x4;
// Scalar types
case Double:
case DoubleLit:
@ -588,32 +588,7 @@ class Type
case Void:
break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid SIMD Type");
}
Type simdToCoercedScalarType() const {
MOZ_ASSERT(isSimd());
switch (which_) {
case Int32x4:
return Intish;
case Float32x4:
return Floatish;
// Scalar types
case Double:
case DoubleLit:
case MaybeDouble:
case Float:
case MaybeFloat:
case Floatish:
case Fixnum:
case Int:
case Signed:
case Unsigned:
case Intish:
case Void:
break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid SIMD Type");
MOZ_CRASH("not a SIMD Type");
}
const char *toChars() const {
@ -2781,7 +2756,7 @@ class FunctionCompiler
curBlock_->setSlot(info().localSlot(local.slot), def);
}
MDefinition *loadHeap(Scalar::Type vt, MDefinition *ptr, NeedsBoundsCheck chk)
MDefinition *loadHeap(AsmJSHeapAccess::ViewType vt, MDefinition *ptr, NeedsBoundsCheck chk)
{
if (inDeadCode())
return nullptr;
@ -2792,7 +2767,8 @@ class FunctionCompiler
return load;
}
void storeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
void storeHeap(AsmJSHeapAccess::ViewType vt, MDefinition *ptr, MDefinition *v,
NeedsBoundsCheck chk)
{
if (inDeadCode())
return;
@ -2810,7 +2786,7 @@ class FunctionCompiler
curBlock_->add(ins);
}
MDefinition *atomicLoadHeap(Scalar::Type vt, MDefinition *ptr, NeedsBoundsCheck chk)
MDefinition *atomicLoadHeap(AsmJSHeapAccess::ViewType vt, MDefinition *ptr, NeedsBoundsCheck chk)
{
if (inDeadCode())
return nullptr;
@ -2822,7 +2798,8 @@ class FunctionCompiler
return load;
}
void atomicStoreHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
void atomicStoreHeap(AsmJSHeapAccess::ViewType vt, MDefinition *ptr, MDefinition *v,
NeedsBoundsCheck chk)
{
if (inDeadCode())
return;
@ -2833,7 +2810,8 @@ class FunctionCompiler
curBlock_->add(store);
}
MDefinition *atomicCompareExchangeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *oldv, MDefinition *newv, NeedsBoundsCheck chk)
MDefinition *atomicCompareExchangeHeap(AsmJSHeapAccess::ViewType vt, MDefinition *ptr,
MDefinition *oldv, MDefinition *newv, NeedsBoundsCheck chk)
{
if (inDeadCode())
return nullptr;
@ -2846,7 +2824,8 @@ class FunctionCompiler
return cas;
}
MDefinition *atomicBinopHeap(js::jit::AtomicOp op, Scalar::Type vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
MDefinition *atomicBinopHeap(js::jit::AtomicOp op, AsmJSHeapAccess::ViewType vt,
MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
{
if (inDeadCode())
return nullptr;
@ -4474,7 +4453,7 @@ CheckLoadArray(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *ty
if (!CheckArrayAccess(f, ElemBase(elem), ElemIndex(elem), &viewType, &pointerDef, &needsBoundsCheck))
return false;
*def = f.loadHeap(viewType, pointerDef, needsBoundsCheck);
*def = f.loadHeap(AsmJSHeapAccess::ViewType(viewType), pointerDef, needsBoundsCheck);
*type = TypedArrayLoadType(viewType);
return true;
}
@ -4515,7 +4494,11 @@ CheckDotAccess(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *ty
else
return f.fail(base, "dot access field must be a lane name (x, y, z, w) or signMask");
*type = baseType.simdToScalarType();
switch (baseType.simdType()) {
case AsmJSSimdType_int32x4: *type = Type::Signed; break;
case AsmJSSimdType_float32x4: *type = Type::Float; break;
}
*def = f.extractSimdElement(lane, baseDef, type->toMIRType());
return true;
}
@ -4564,7 +4547,7 @@ CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition
MOZ_CRASH("Unexpected view type");
}
f.storeHeap(viewType, pointerDef, rhsDef, needsBoundsCheck);
f.storeHeap(AsmJSHeapAccess::ViewType(viewType), pointerDef, rhsDef, needsBoundsCheck);
*def = rhsDef;
*type = rhsType;
@ -4832,7 +4815,7 @@ CheckAtomicsLoad(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type *
if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType, &pointerDef, &needsBoundsCheck))
return false;
*def = f.atomicLoadHeap(viewType, pointerDef, needsBoundsCheck);
*def = f.atomicLoadHeap(AsmJSHeapAccess::ViewType(viewType), pointerDef, needsBoundsCheck);
*type = Type::Signed;
return true;
}
@ -4861,7 +4844,7 @@ CheckAtomicsStore(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type
if (!rhsType.isIntish())
return f.failf(arrayArg, "%s is not a subtype of intish", rhsType.toChars());
f.atomicStoreHeap(viewType, pointerDef, rhsDef, needsBoundsCheck);
f.atomicStoreHeap(AsmJSHeapAccess::ViewType(viewType), pointerDef, rhsDef, needsBoundsCheck);
*def = rhsDef;
*type = Type::Signed;
@ -4892,7 +4875,8 @@ CheckAtomicsBinop(FunctionCompiler &f, ParseNode *call, MDefinition **def, Type
if (!valueArgType.isIntish())
return f.failf(valueArg, "%s is not a subtype of intish", valueArgType.toChars());
*def = f.atomicBinopHeap(op, viewType, pointerDef, valueArgDef, needsBoundsCheck);
*def = f.atomicBinopHeap(op, AsmJSHeapAccess::ViewType(viewType), pointerDef, valueArgDef,
needsBoundsCheck);
*type = Type::Signed;
return true;
}
@ -4930,7 +4914,8 @@ CheckAtomicsCompareExchange(FunctionCompiler &f, ParseNode *call, MDefinition **
if (!newValueArgType.isIntish())
return f.failf(newValueArg, "%s is not a subtype of intish", newValueArgType.toChars());
*def = f.atomicCompareExchangeHeap(viewType, pointerDef, oldValueArgDef, newValueArgDef, needsBoundsCheck);
*def = f.atomicCompareExchangeHeap(AsmJSHeapAccess::ViewType(viewType), pointerDef,
oldValueArgDef, newValueArgDef, needsBoundsCheck);
*type = Type::Signed;
return true;
}
@ -5365,7 +5350,7 @@ class CheckArgIsSubtypeOf
Type formalType_;
public:
explicit CheckArgIsSubtypeOf(Type t) : formalType_(t) {}
explicit CheckArgIsSubtypeOf(AsmJSSimdType t) : formalType_(t) {}
bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType,
MDefinition **def) const
@ -5378,14 +5363,26 @@ class CheckArgIsSubtypeOf
}
};
static inline Type
SimdToCoercedScalarType(AsmJSSimdType t)
{
switch (t) {
case AsmJSSimdType_int32x4:
return Type::Intish;
case AsmJSSimdType_float32x4:
return Type::Floatish;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
}
class CheckSimdScalarArgs
{
Type simdType_;
AsmJSSimdType simdType_;
Type formalType_;
public:
CheckSimdScalarArgs(Type simdType)
: simdType_(simdType), formalType_(simdType.simdToCoercedScalarType())
CheckSimdScalarArgs(AsmJSSimdType simdType)
: simdType_(simdType), formalType_(SimdToCoercedScalarType(simdType))
{}
bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType,
@ -5394,10 +5391,10 @@ class CheckSimdScalarArgs
if (!(actualType <= formalType_)) {
// As a special case, accept doublelit arguments to float32x4 ops by
// re-emitting them as float32 constants.
if (!simdType_.isFloat32x4() || !actualType.isDoubleLit()) {
if (simdType_ != AsmJSSimdType_float32x4 || !actualType.isDoubleLit()) {
return f.failf(arg, "%s is not a subtype of %s%s",
actualType.toChars(), formalType_.toChars(),
simdType_.isFloat32x4() ? " or doublelit" : "");
simdType_ == AsmJSSimdType_float32x4 ? " or doublelit" : "");
}
AsmJSNumLit doubleLit = ExtractNumericLiteral(f.m(), arg);
@ -5413,7 +5410,7 @@ class CheckSimdSelectArgs
Type formalType_;
public:
explicit CheckSimdSelectArgs(Type t) : formalType_(t) {}
explicit CheckSimdSelectArgs(AsmJSSimdType t) : formalType_(t) {}
bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType,
MDefinition **def) const
@ -5435,10 +5432,10 @@ class CheckSimdSelectArgs
class CheckSimdVectorScalarArgs
{
Type formalType_;
AsmJSSimdType formalSimdType_;
public:
explicit CheckSimdVectorScalarArgs(Type t) : formalType_(t) {}
explicit CheckSimdVectorScalarArgs(AsmJSSimdType t) : formalSimdType_(t) {}
bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType,
MDefinition **def) const
@ -5446,15 +5443,15 @@ class CheckSimdVectorScalarArgs
MOZ_ASSERT(argIndex < 2);
if (argIndex == 0) {
// First argument is the vector
if (!(actualType <= formalType_)) {
if (!(actualType <= Type(formalSimdType_))) {
return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
formalType_.toChars());
Type(formalSimdType_).toChars());
}
return true;
}
// Second argument is the scalar
Type coercedFormalType = formalType_.simdToCoercedScalarType();
Type coercedFormalType = SimdToCoercedScalarType(formalSimdType_);
if (!(actualType <= coercedFormalType)) {
return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
coercedFormalType.toChars());
@ -5466,82 +5463,87 @@ class CheckSimdVectorScalarArgs
} // anonymous namespace
static inline bool
CheckSimdUnary(FunctionCompiler &f, ParseNode *call, Type retType, MSimdUnaryArith::Operation op,
MDefinition **def, Type *type)
CheckSimdUnary(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType,
MSimdUnaryArith::Operation op, MDefinition **def, Type *type)
{
DefinitionVector defs;
if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(retType), &defs))
if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(opType), &defs))
return false;
*def = f.unarySimd(defs[0], op, retType.toMIRType());
*type = retType;
*type = opType;
*def = f.unarySimd(defs[0], op, type->toMIRType());
return true;
}
template<class OpEnum>
static inline bool
CheckSimdBinary(FunctionCompiler &f, ParseNode *call, Type retType, OpEnum op, MDefinition **def,
Type *type)
CheckSimdBinary(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, OpEnum op,
MDefinition **def, Type *type)
{
DefinitionVector argDefs;
if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(retType), &argDefs))
if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType), &argDefs))
return false;
*def = f.binarySimd(argDefs[0], argDefs[1], op, retType.toMIRType());
*type = retType;
*type = opType;
*def = f.binarySimd(argDefs[0], argDefs[1], op, type->toMIRType());
return true;
}
template<>
inline bool
CheckSimdBinary<MSimdBinaryComp::Operation>(FunctionCompiler &f, ParseNode *call, Type retType,
MSimdBinaryComp::Operation op, MDefinition **def,
Type *type)
CheckSimdBinary<MSimdBinaryComp::Operation>(FunctionCompiler &f, ParseNode *call,
AsmJSSimdType opType, MSimdBinaryComp::Operation op,
MDefinition **def, Type *type)
{
DefinitionVector argDefs;
if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(retType), &argDefs))
if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType), &argDefs))
return false;
*def = f.binarySimd<MSimdBinaryComp>(argDefs[0], argDefs[1], op);
*type = Type::Int32x4;
*def = f.binarySimd<MSimdBinaryComp>(argDefs[0], argDefs[1], op);
return true;
}
template<>
inline bool
CheckSimdBinary<MSimdShift::Operation>(FunctionCompiler &f, ParseNode *call, Type retType,
CheckSimdBinary<MSimdShift::Operation>(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType,
MSimdShift::Operation op, MDefinition **def, Type *type)
{
DefinitionVector argDefs;
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(retType), &argDefs))
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(opType), &argDefs))
return false;
*def = f.binarySimd<MSimdShift>(argDefs[0], argDefs[1], op);
*type = Type::Int32x4;
*def = f.binarySimd<MSimdShift>(argDefs[0], argDefs[1], op);
return true;
}
static bool
CheckSimdWith(FunctionCompiler &f, ParseNode *call, Type retType, SimdLane lane, MDefinition **def,
Type *type)
CheckSimdWith(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, SimdLane lane,
MDefinition **def, Type *type)
{
DefinitionVector defs;
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(retType), &defs))
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(opType), &defs))
return false;
*def = f.insertElementSimd(defs[0], defs[1], lane, retType.toMIRType());
*type = retType;
*type = opType;
*def = f.insertElementSimd(defs[0], defs[1], lane, type->toMIRType());
return true;
}
namespace {
// Include CheckSimdCast in unnamed namespace to avoid MSVC name lookup bug (due to the use of Type).
template<class T>
static bool
CheckSimdCast(FunctionCompiler &f, ParseNode *call, Type fromType, Type toType, MDefinition **def,
Type *type)
CheckSimdCast(FunctionCompiler &f, ParseNode *call, AsmJSSimdType fromType, AsmJSSimdType toType,
MDefinition **def, Type *type)
{
DefinitionVector defs;
if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(fromType), &defs))
return false;
*def = f.convertSimd<T>(defs[0], fromType.toMIRType(), toType.toMIRType());
*type = toType;
*def = f.convertSimd<T>(defs[0], Type(fromType).toMIRType(), type->toMIRType());
return true;
}
}
static bool
CheckSimdShuffleSelectors(FunctionCompiler &f, ParseNode *lane, int32_t lanes[4], uint32_t maxLane)
{
@ -5557,12 +5559,14 @@ CheckSimdShuffleSelectors(FunctionCompiler &f, ParseNode *lane, int32_t lanes[4]
}
static bool
CheckSimdSwizzle(FunctionCompiler &f, ParseNode *call, Type retType, MDefinition **def, Type *type)
CheckSimdSwizzle(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefinition **def,
Type *type)
{
unsigned numArgs = CallArgListLength(call);
if (numArgs != 5)
return f.failf(call, "expected 5 arguments to SIMD swizzle, got %u", numArgs);
Type retType = opType;
ParseNode *vec = CallArgList(call);
MDefinition *vecDef;
Type vecType;
@ -5581,12 +5585,14 @@ CheckSimdSwizzle(FunctionCompiler &f, ParseNode *call, Type retType, MDefinition
}
static bool
CheckSimdShuffle(FunctionCompiler &f, ParseNode *call, Type retType, MDefinition **def, Type *type)
CheckSimdShuffle(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefinition **def,
Type *type)
{
unsigned numArgs = CallArgListLength(call);
if (numArgs != 6)
return f.failf(call, "expected 6 arguments to SIMD shuffle, got %u", numArgs);
Type retType = opType;
ParseNode *arg = CallArgList(call);
MDefinition *vecs[2];
for (unsigned i = 0; i < 2; i++, arg = NextNode(arg)) {
@ -5607,107 +5613,216 @@ CheckSimdShuffle(FunctionCompiler &f, ParseNode *call, Type retType, MDefinition
return true;
}
static bool
CheckSimdLoadStoreArgs(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType,
AsmJSHeapAccess::ViewType *viewType, MDefinition **index,
NeedsBoundsCheck *needsBoundsCheck)
{
ParseNode *view = CallArgList(call);
if (!view->isKind(PNK_NAME))
return f.fail(view, "expected Uint8Array view as SIMD.*.store first argument");
const ModuleCompiler::Global *global = f.lookupGlobal(view->name());
if (!global ||
global->which() != ModuleCompiler::Global::ArrayView ||
global->viewType() != Scalar::Uint8)
{
return f.fail(view, "expected Uint8Array view as SIMD.*.store first argument");
}
*needsBoundsCheck = NEEDS_BOUNDS_CHECK;
switch (opType) {
case AsmJSSimdType_int32x4: *viewType = AsmJSHeapAccess::Int32x4; break;
case AsmJSSimdType_float32x4: *viewType = AsmJSHeapAccess::Float32x4; break;
}
ParseNode *indexExpr = NextNode(view);
uint32_t indexLit;
if (IsLiteralOrConstInt(f, indexExpr, &indexLit)) {
if (indexLit > INT32_MAX)
return f.fail(indexExpr, "constant index out of range");
if (!f.m().tryRequireHeapLengthToBeAtLeast(indexLit + Simd128DataSize)) {
return f.failf(indexExpr, "constant index outside heap size range declared by the "
"change-heap function (0x%x - 0x%x)",
f.m().minHeapLength(), f.m().module().maxHeapLength());
}
*needsBoundsCheck = NO_BOUNDS_CHECK;
*index = f.constant(Int32Value(indexLit), Type::Int);
return true;
}
f.enterHeapExpression();
Type indexType;
if (!CheckExpr(f, indexExpr, index, &indexType))
return false;
if (!indexType.isIntish())
return f.failf(indexExpr, "%s is not a subtype of intish", indexType.toChars());
f.leaveHeapExpression();
return true;
}
static bool
CheckSimdLoad(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefinition **def, Type *type)
{
unsigned numArgs = CallArgListLength(call);
if (numArgs != 2)
return f.failf(call, "expected 2 arguments to SIMD load, got %u", numArgs);
AsmJSHeapAccess::ViewType viewType;
MDefinition *index;
NeedsBoundsCheck needsBoundsCheck;
if (!CheckSimdLoadStoreArgs(f, call, opType, &viewType, &index, &needsBoundsCheck))
return false;
*def = f.loadHeap(viewType, index, needsBoundsCheck);
*type = opType;
return true;
}
static bool
CheckSimdStore(FunctionCompiler &f, ParseNode *call, AsmJSSimdType opType, MDefinition **def, Type *type)
{
unsigned numArgs = CallArgListLength(call);
if (numArgs != 3)
return f.failf(call, "expected 3 arguments to SIMD load, got %u", numArgs);
AsmJSHeapAccess::ViewType viewType;
MDefinition *index;
NeedsBoundsCheck needsBoundsCheck;
if (!CheckSimdLoadStoreArgs(f, call, opType, &viewType, &index, &needsBoundsCheck))
return false;
Type retType = opType;
ParseNode *vecExpr = NextNode(NextNode(CallArgList(call)));
MDefinition *vec;
Type vecType;
if (!CheckExpr(f, vecExpr, &vec, &vecType))
return false;
if (!(vecType <= retType))
return f.failf(vecExpr, "%s is not a subtype of %s", vecType.toChars(), retType.toChars());
f.storeHeap(viewType, index, vec, needsBoundsCheck);
*def = vec;
*type = vecType;
return true;
}
static bool
CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
MDefinition **def, Type *type)
{
MOZ_ASSERT(global->isSimdOperation());
Type retType = global->simdOperationType();
AsmJSSimdType opType = global->simdOperationType();
switch (global->simdOperation()) {
case AsmJSSimdOperation_add:
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Add, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Add, def, type);
case AsmJSSimdOperation_sub:
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Sub, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Sub, def, type);
case AsmJSSimdOperation_mul:
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Mul, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Mul, def, type);
case AsmJSSimdOperation_div:
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Div, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Div, def, type);
case AsmJSSimdOperation_max:
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Max, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Max, def, type);
case AsmJSSimdOperation_min:
return CheckSimdBinary(f, call, retType, MSimdBinaryArith::Min, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Min, def, type);
case AsmJSSimdOperation_maxNum:
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::MaxNum, def, type);
case AsmJSSimdOperation_minNum:
return CheckSimdBinary(f, call, opType, MSimdBinaryArith::MinNum, def, type);
case AsmJSSimdOperation_lessThan:
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::lessThan, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThan, def, type);
case AsmJSSimdOperation_lessThanOrEqual:
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::lessThanOrEqual, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThanOrEqual, def, type);
case AsmJSSimdOperation_equal:
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::equal, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::equal, def, type);
case AsmJSSimdOperation_notEqual:
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::notEqual, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::notEqual, def, type);
case AsmJSSimdOperation_greaterThan:
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::greaterThan, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::greaterThan, def, type);
case AsmJSSimdOperation_greaterThanOrEqual:
return CheckSimdBinary(f, call, retType, MSimdBinaryComp::greaterThanOrEqual, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryComp::greaterThanOrEqual, def, type);
case AsmJSSimdOperation_and:
return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::and_, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryBitwise::and_, def, type);
case AsmJSSimdOperation_or:
return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::or_, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryBitwise::or_, def, type);
case AsmJSSimdOperation_xor:
return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::xor_, def, type);
return CheckSimdBinary(f, call, opType, MSimdBinaryBitwise::xor_, def, type);
case AsmJSSimdOperation_withX:
return CheckSimdWith(f, call, retType, SimdLane::LaneX, def, type);
return CheckSimdWith(f, call, opType, SimdLane::LaneX, def, type);
case AsmJSSimdOperation_withY:
return CheckSimdWith(f, call, retType, SimdLane::LaneY, def, type);
return CheckSimdWith(f, call, opType, SimdLane::LaneY, def, type);
case AsmJSSimdOperation_withZ:
return CheckSimdWith(f, call, retType, SimdLane::LaneZ, def, type);
return CheckSimdWith(f, call, opType, SimdLane::LaneZ, def, type);
case AsmJSSimdOperation_withW:
return CheckSimdWith(f, call, retType, SimdLane::LaneW, def, type);
return CheckSimdWith(f, call, opType, SimdLane::LaneW, def, type);
case AsmJSSimdOperation_fromInt32x4:
return CheckSimdCast<MSimdConvert>(f, call, Type::Int32x4, retType, def, type);
return CheckSimdCast<MSimdConvert>(f, call, AsmJSSimdType_int32x4, opType, def, type);
case AsmJSSimdOperation_fromInt32x4Bits:
return CheckSimdCast<MSimdReinterpretCast>(f, call, Type::Int32x4, retType, def, type);
return CheckSimdCast<MSimdReinterpretCast>(f, call, AsmJSSimdType_int32x4, opType, def, type);
case AsmJSSimdOperation_fromFloat32x4:
return CheckSimdCast<MSimdConvert>(f, call, Type::Float32x4, retType, def, type);
return CheckSimdCast<MSimdConvert>(f, call, AsmJSSimdType_float32x4, opType, def, type);
case AsmJSSimdOperation_fromFloat32x4Bits:
return CheckSimdCast<MSimdReinterpretCast>(f, call, Type::Float32x4, retType, def, type);
return CheckSimdCast<MSimdReinterpretCast>(f, call, AsmJSSimdType_float32x4, opType, def, type);
case AsmJSSimdOperation_shiftLeft:
return CheckSimdBinary(f, call, Type::Int32x4, MSimdShift::lsh, def, type);
return CheckSimdBinary(f, call, opType, MSimdShift::lsh, def, type);
case AsmJSSimdOperation_shiftRight:
return CheckSimdBinary(f, call, Type::Int32x4, MSimdShift::rsh, def, type);
return CheckSimdBinary(f, call, opType, MSimdShift::rsh, def, type);
case AsmJSSimdOperation_shiftRightLogical:
return CheckSimdBinary(f, call, Type::Int32x4, MSimdShift::ursh, def, type);
return CheckSimdBinary(f, call, opType, MSimdShift::ursh, def, type);
case AsmJSSimdOperation_abs:
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::abs, def, type);
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::abs, def, type);
case AsmJSSimdOperation_neg:
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::neg, def, type);
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::neg, def, type);
case AsmJSSimdOperation_not:
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::not_, def, type);
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::not_, def, type);
case AsmJSSimdOperation_sqrt:
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::sqrt, def, type);
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::sqrt, def, type);
case AsmJSSimdOperation_reciprocal:
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::reciprocal, def, type);
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::reciprocal, def, type);
case AsmJSSimdOperation_reciprocalSqrt:
return CheckSimdUnary(f, call, retType, MSimdUnaryArith::reciprocalSqrt, def, type);
return CheckSimdUnary(f, call, opType, MSimdUnaryArith::reciprocalSqrt, def, type);
case AsmJSSimdOperation_swizzle:
return CheckSimdSwizzle(f, call, retType, def, type);
return CheckSimdSwizzle(f, call, opType, def, type);
case AsmJSSimdOperation_shuffle:
return CheckSimdShuffle(f, call, retType, def, type);
return CheckSimdShuffle(f, call, opType, def, type);
case AsmJSSimdOperation_load:
return CheckSimdLoad(f, call, opType, def, type);
case AsmJSSimdOperation_store:
return CheckSimdStore(f, call, opType, def, type);
case AsmJSSimdOperation_splat: {
DefinitionVector defs;
if (!CheckSimdCallArgs(f, call, 1, CheckSimdScalarArgs(retType), &defs))
if (!CheckSimdCallArgs(f, call, 1, CheckSimdScalarArgs(opType), &defs))
return false;
*def = f.splatSimd(defs[0], retType.toMIRType());
*type = retType;
*type = opType;
*def = f.splatSimd(defs[0], type->toMIRType());
return true;
}
case AsmJSSimdOperation_select: {
DefinitionVector defs;
if (!CheckSimdCallArgs(f, call, 3, CheckSimdSelectArgs(retType), &defs))
if (!CheckSimdCallArgs(f, call, 3, CheckSimdSelectArgs(opType), &defs))
return false;
*type = opType;
*def = f.ternarySimd(defs[0], defs[1], defs[2], MSimdTernaryBitwise::select,
retType.toMIRType());
*type = retType;
type->toMIRType());
return true;
}
}
@ -5726,16 +5841,15 @@ CheckSimdCtorCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Gl
return CheckCoercionArg(f, argNode, coercion, def, type);
AsmJSSimdType simdType = global->simdCtorType();
Type retType = simdType;
unsigned length = SimdTypeToLength(simdType);
DefinitionVector defs;
if (!CheckSimdCallArgs(f, call, length, CheckSimdScalarArgs(retType), &defs))
if (!CheckSimdCallArgs(f, call, length, CheckSimdScalarArgs(simdType), &defs))
return false;
// This code will need to be generalized when we handle float64x2
MOZ_ASSERT(length == 4);
*def = f.constructSimd<MSimdValueX4>(defs[0], defs[1], defs[2], defs[3], retType.toMIRType());
*type = retType;
*type = simdType;
*def = f.constructSimd<MSimdValueX4>(defs[0], defs[1], defs[2], defs[3], type->toMIRType());
return true;
}

View File

@ -57,7 +57,9 @@ const size_t AsmJSPageSize = 4096;
// On x64, the internal ArrayBuffer data array is inflated to 4GiB (only the
// byteLength portion of which is accessible) so that out-of-bounds accesses
// (made using a uint32 index) are guaranteed to raise a SIGSEGV.
static const size_t AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL;
// Unaligned accesses and mask optimizations might also try to access a few
// bytes after this limit, so just inflate it by AsmJSPageSize.
static const size_t AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL + AsmJSPageSize;
#endif
// From the asm.js spec Linking section:

View File

@ -508,11 +508,19 @@ struct Mul {
};
template<typename T>
struct Minimum {
static inline T apply(T l, T r) { return l < r ? l : r; }
static inline T apply(T l, T r) { return math_min_impl(l, r); }
};
template<typename T>
struct MinNum {
static inline T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_min_impl(l, r)); }
};
template<typename T>
struct Maximum {
static inline T apply(T l, T r) { return l > r ? l : r; }
static inline T apply(T l, T r) { return math_max_impl(l, r); }
};
template<typename T>
struct MaxNum {
static inline T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_max_impl(l, r)); }
};
template<typename T>
struct LessThan {
@ -971,6 +979,84 @@ Float32x4Select(JSContext *cx, unsigned argc, Value *vp)
return StoreResult<Float32x4>(cx, args, result);
}
template<class VElem, unsigned NumElem>
static bool
TypedArrayDataPtrFromArgs(JSContext *cx, const CallArgs &args, VElem **data)
{
if (!args[0].isObject())
return ErrorBadArgs(cx);
JSObject &argobj = args[0].toObject();
if (!argobj.is<TypedArrayObject>())
return ErrorBadArgs(cx);
Rooted<TypedArrayObject*> typedArray(cx, &argobj.as<TypedArrayObject>());
int32_t index;
if (!ToInt32(cx, args[1], &index))
return false;
int32_t byteStart = index * typedArray->bytesPerElement();
if (byteStart < 0 || (uint32_t(byteStart) + NumElem * sizeof(VElem)) > typedArray->byteLength())
return ErrorBadArgs(cx);
*data = reinterpret_cast<VElem*>(static_cast<char*>(typedArray->viewData()) + byteStart);
return true;
}
template<class V, unsigned NumElem>
static bool
Load(JSContext *cx, unsigned argc, Value *vp)
{
typedef typename V::Elem Elem;
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 2)
return ErrorBadArgs(cx);
Elem *typedArrayData = nullptr;
if (!TypedArrayDataPtrFromArgs<Elem, NumElem>(cx, args, &typedArrayData))
return false;
Rooted<TypeDescr*> typeDescr(cx, &V::GetTypeDescr(*cx->global()));
MOZ_ASSERT(typeDescr);
Rooted<TypedObject *> result(cx, OutlineTypedObject::createZeroed(cx, typeDescr, 0));
if (!result)
return false;
Elem *dest = reinterpret_cast<Elem*>(result->typedMem());
for (unsigned i = 0; i < NumElem; i++)
dest[i] = typedArrayData[i];
args.rval().setObject(*result);
return true;
}
template<class V, unsigned NumElem>
static bool
Store(JSContext *cx, unsigned argc, Value *vp)
{
typedef typename V::Elem Elem;
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 3)
return ErrorBadArgs(cx);
Elem *typedArrayData = nullptr;
if (!TypedArrayDataPtrFromArgs<Elem, NumElem>(cx, args, &typedArrayData))
return false;
if (!IsVectorObject<V>(args[2]))
return ErrorBadArgs(cx);
Elem *src = TypedObjectMemory<Elem*>(args[2]);
for (unsigned i = 0; i < NumElem; i++)
typedArrayData[i] = src[i];
args.rval().setObject(args[2].toObject());
return true;
}
#define DEFINE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags) \
bool \
js::simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp) \

View File

@ -41,12 +41,22 @@
V(greaterThanOrEqual, (CompareFunc<Float32x4, GreaterThanOrEqual>), 2, 0) \
V(lessThan, (CompareFunc<Float32x4, LessThan>), 2, 0) \
V(lessThanOrEqual, (CompareFunc<Float32x4, LessThanOrEqual>), 2, 0) \
V(load, (Load<Float32x4, 4>), 2, 0) \
V(loadXYZ, (Load<Float32x4, 3>), 2, 0) \
V(loadXY, (Load<Float32x4, 2>), 2, 0) \
V(loadX, (Load<Float32x4, 1>), 2, 0) \
V(max, (BinaryFunc<Float32x4, Maximum, Float32x4>), 2, 0) \
V(maxNum, (BinaryFunc<Float32x4, MaxNum, Float32x4>), 2, 0) \
V(min, (BinaryFunc<Float32x4, Minimum, Float32x4>), 2, 0) \
V(minNum, (BinaryFunc<Float32x4, MinNum, Float32x4>), 2, 0) \
V(mul, (BinaryFunc<Float32x4, Mul, Float32x4>), 2, 0) \
V(notEqual, (CompareFunc<Float32x4, NotEqual>), 2, 0) \
V(or, (CoercedBinaryFunc<Float32x4, Int32x4, Or, Float32x4>), 2, 0) \
V(scale, (FuncWith<Float32x4, Scale>), 2, 0) \
V(store, (Store<Float32x4, 4>), 3, 0) \
V(storeXYZ, (Store<Float32x4, 3>), 3, 0) \
V(storeXY, (Store<Float32x4, 2>), 3, 0) \
V(storeX, (Store<Float32x4, 1>), 3, 0) \
V(sub, (BinaryFunc<Float32x4, Sub, Float32x4>), 2, 0) \
V(withX, (FuncWith<Float32x4, WithX>), 2, 0) \
V(withY, (FuncWith<Float32x4, WithY>), 2, 0) \
@ -85,12 +95,20 @@
V(equal, (CompareFunc<Int32x4, Equal>), 2, 0) \
V(greaterThan, (CompareFunc<Int32x4, GreaterThan>), 2, 0) \
V(lessThan, (CompareFunc<Int32x4, LessThan>), 2, 0) \
V(load, (Load<Int32x4, 4>), 2, 0) \
V(loadXYZ, (Load<Int32x4, 3>), 2, 0) \
V(loadXY, (Load<Int32x4, 2>), 2, 0) \
V(loadX, (Load<Int32x4, 1>), 2, 0) \
V(mul, (BinaryFunc<Int32x4, Mul, Int32x4>), 2, 0) \
V(or, (BinaryFunc<Int32x4, Or, Int32x4>), 2, 0) \
V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2, 0) \
V(shiftLeft, (Int32x4BinaryScalar<ShiftLeft>), 2, 0) \
V(shiftRight, (Int32x4BinaryScalar<ShiftRight>), 2, 0) \
V(shiftRightLogical, (Int32x4BinaryScalar<ShiftRightLogical>), 2, 0) \
V(store, (Store<Int32x4, 4>), 3, 0) \
V(storeXYZ, (Store<Int32x4, 3>), 3, 0) \
V(storeXY, (Store<Int32x4, 2>), 3, 0) \
V(storeX, (Store<Int32x4, 1>), 3, 0) \
V(withFlagX, (FuncWith<Int32x4, WithFlagX>), 2, 0) \
V(withFlagY, (FuncWith<Int32x4, WithFlagY>), 2, 0) \
V(withFlagZ, (FuncWith<Int32x4, WithFlagZ>), 2, 0) \
@ -136,6 +154,8 @@
_(div) \
_(max) \
_(min) \
_(maxNum) \
_(minNum) \
_(lessThanOrEqual) \
_(notEqual) \
_(greaterThanOrEqual)
@ -157,7 +177,9 @@
_(withZ) \
_(withW) \
_(not) \
_(neg)
_(neg) \
_(load) \
_(store)
#define FORALL_SIMD_OP(_) \
FOREACH_INT32X4_SIMD_OP(_) \
FOREACH_FLOAT32X4_SIMD_OP(_) \

View File

@ -421,7 +421,7 @@ function unsafeVariableAddressTaken(suppressed, variable)
function computePrintedLines(functionName)
{
assert(!system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile));
assert(!os.system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile));
var lines = snarf(tmpfile).split('\n');
for (var body of functionBodies)

View File

@ -444,6 +444,18 @@ CheckF4(F32M, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [4,6,15,8]);
CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4m(x,y)', [Math.fround(13.37) * 4,6,15,8]);
CheckF4(F32M, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4(f4m(x,y))', [Math.fround(13.37) * 4,6,15,8]);
var f32x4 = SIMD.float32x4(0, NaN, -0, NaN);
var another = SIMD.float32x4(NaN, -1, -0, NaN);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32M + "function f(x, y) {x=f4(x); y=f4(y); x=f4m(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, 0, NaN]);
CheckF4(F32D, 'var x=f4(1,2,3,4); x=f4d(x,x)', [1,1,1,1]);
CheckF4(F32D, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4d(x,y)', [1/4,2/3,3/5,2]);
CheckF4(F32D, 'var x=f4(13.37,1,1,4); var y=f4(4,0,-0.,2); x=f4d(x,y)', [Math.fround(13.37) / 4,+Infinity,-Infinity,2]);
var f32x4 = SIMD.float32x4(0, 0, -0, NaN);
var another = SIMD.float32x4(0, -0, 0, 0);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32D + "function f(x,y) {x=f4(x); y=f4(y); x=f4d(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, NaN, NaN]);
// Unary arithmetic operators
function CheckUnaryF4(op, checkFunc, assertFunc) {
var _ = asmLink(asmCompile('glob', USE_ASM + F32 + 'var op=f4.' + op + '; function f(x){x=f4(x); return f4(op(x)); } return f'), this);
@ -513,31 +525,36 @@ assertAsmTypeFail('glob', USE_ASM + I32 + "var f4d=i4.max; function f() {} retur
const F32MIN = 'var min = f4.min;'
const F32MAX = 'var max = f4.max;'
// TODO amend tests once float32x4.min/max is fully specified, for NaN and -0
// vs 0. See also comment in js::jit::CodeGeneratorX86Shared::visitSimdBinaryArithFx4.
// In these tests, we assume x86/x64, so min(+0, -0) === +0, as specified in
// the Intel developer manual. See also bug 1068028.
CheckF4(F32MIN, 'var x=f4(1,2,3,4); x=min(x,x)', [1,2,3,4]);
CheckF4(F32MIN, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=min(x,y)', [4,2,3,2]);
CheckF4(F32MIN + FROUND + 'var Infinity = glob.Infinity;', 'var x=f4(0,0,0,0); var y=f4(2310,3,5,0); x=f4(f32(+Infinity),f32(-Infinity),f32(3),f32(-0.)); x=min(x,y)', [2310,-Infinity,3,0]);
CheckF4(F32MIN + FROUND + 'var Infinity = glob.Infinity;', 'var x=f4(0,0,0,0); var y=f4(2310,3,5,0); x=f4(f32(+Infinity),f32(-Infinity),f32(3),f32(-0.)); x=min(x,y)', [2310,-Infinity,3,-0]);
CheckF4(F32MIN, 'var x=f4(0,0,-0,-0); var y=f4(0,-0,0,-0); x=min(x,y)', [0,-0,-0,-0]);
CheckF4(F32MIN + FROUND + 'var NaN = glob.NaN;', 'var x=f4(0,0,0,0); var y=f4(0,0,0,0); var n=f32(0); n=f32(NaN); x=f4(n,0.,n,0.); y=f4(n,n,0.,0.); x=min(x,y)', [NaN, NaN, NaN, 0]);
CheckF4(F32MAX, 'var x=f4(1,2,3,4); x=max(x,x)', [1,2,3,4]);
CheckF4(F32MAX, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=max(x,y)', [13.37, 3, 5, 4]);
CheckF4(F32MAX + FROUND + 'var Infinity = glob.Infinity;', 'var x=f4(0,0,0,0); var y=f4(2310,3,5,0); x=f4(f32(+Infinity),f32(-Infinity),f32(3),f32(-0.)); x=max(x,y)', [+Infinity,3,5,0]);
// Test NaN
var f32x4 = SIMD.float32x4(0, NaN, -0, NaN);
var another = SIMD.float32x4(NaN, -1, -0, NaN);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32M + "function f(x, y) {x=f4(x); y=f4(y); x=f4m(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, 0, NaN]);
CheckF4(F32MAX, 'var x=f4(0,0,-0,-0); var y=f4(0,-0,0,-0); x=max(x,y)', [0,0,0,-0]);
CheckF4(F32MAX + FROUND + 'var NaN = glob.NaN;', 'var x=f4(0,0,0,0); var y=f4(0,0,0,0); var n=f32(0); n=f32(NaN); x=f4(n,0.,n,0.); y=f4(n,n,0.,0.); x=max(x,y)', [NaN, NaN, NaN, 0]);
CheckF4(F32D, 'var x=f4(1,2,3,4); x=f4d(x,x)', [1,1,1,1]);
CheckF4(F32D, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4d(x,y)', [1/4,2/3,3/5,2]);
CheckF4(F32D, 'var x=f4(13.37,1,1,4); var y=f4(4,0,-0.,2); x=f4d(x,y)', [Math.fround(13.37) / 4,+Infinity,-Infinity,2]);
const F32MINNUM = 'var min = f4.minNum;'
const F32MAXNUM = 'var max = f4.maxNum;'
// Test NaN
var f32x4 = SIMD.float32x4(0, 0, -0, NaN);
var another = SIMD.float32x4(0, -0, 0, 0);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32D + "function f(x,y) {x=f4(x); y=f4(y); x=f4d(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, NaN, NaN]);
CheckF4(F32MINNUM, 'var x=f4(1,2,3,4); x=min(x,x)', [1,2,3,4]);
CheckF4(F32MINNUM, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=min(x,y)', [4,2,3,2]);
CheckF4(F32MINNUM + FROUND + 'var Infinity = glob.Infinity;', 'var x=f4(0,0,0,0); var y=f4(2310,3,5,0); x=f4(f32(+Infinity),f32(-Infinity),f32(3),f32(-0.)); x=min(x,y)', [2310,-Infinity,3,-0]);
CheckF4(F32MINNUM, 'var x=f4(0,0,-0,-0); var y=f4(0,-0,0,-0); x=min(x,y)', [0,-0,-0,-0]);
CheckF4(F32MINNUM + FROUND + 'var NaN = glob.NaN;', 'var x=f4(0,0,0,0); var y=f4(0,0,0,0); var n=f32(0); n=f32(NaN); x=f4(n,0.,n,0.); y=f4(n,n,0.,0.); x=min(x,y)', [NaN, 0, 0, 0]);
CheckF4(F32MAXNUM, 'var x=f4(1,2,3,4); x=max(x,x)', [1,2,3,4]);
CheckF4(F32MAXNUM, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=max(x,y)', [13.37, 3, 5, 4]);
CheckF4(F32MAXNUM + FROUND + 'var Infinity = glob.Infinity;', 'var x=f4(0,0,0,0); var y=f4(2310,3,5,0); x=f4(f32(+Infinity),f32(-Infinity),f32(3),f32(-0.)); x=max(x,y)', [+Infinity,3,5,0]);
CheckF4(F32MAXNUM, 'var x=f4(0,0,-0,-0); var y=f4(0,-0,0,-0); x=max(x,y)', [0,0,0,-0]);
CheckF4(F32MAXNUM + FROUND + 'var NaN = glob.NaN;', 'var x=f4(0,0,0,0); var y=f4(0,0,0,0); var n=f32(0); n=f32(NaN); x=f4(n,0.,n,0.); y=f4(n,n,0.,0.); x=max(x,y)', [NaN, 0, 0, 0]);
// With
const WXF = 'var w = f4.withX;';
@ -945,6 +962,224 @@ 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);
// 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

View File

@ -927,6 +927,48 @@ function rregexp_m_literal_replace(i) {
return i;
}
var uceFault_string_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_replace'))
function rstring_replace(i) {
var re = /str\d+9/;
assertEq(re.lastIndex == 0, true);
var res = "str00123456789".replace(re, "abc");
if (uceFault_string_replace(i) || uceFault_string_replace(i)) {
assertEq(res, "abc");
}
assertEq(re.lastIndex == 0, true);
return i;
}
var uceFault_string_replace_y = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_replace_y'))
function rstring_replace_y(i) {
var re = /str\d+9/y;
assertEq(re.lastIndex == 0, true);
var res = "str00123456789".replace(re, "abc");
if (uceFault_string_replace_y(i) || uceFault_string_replace_y(i)) {
assertEq(res, "abc");
}
assertEq(re.lastIndex == 0, true);
return i;
}
var uceFault_string_replace_g = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_replace_g'))
function rstring_replace_g(i) {
var re = /str\d+9/g;
assertEq(re.lastIndex == 0, true);
var res = "str00123456789str00123456789".replace(re, "abc");
if (uceFault_string_replace_g(i) || uceFault_string_replace_g(i)) {
assertEq(res, "abcabc");
}
assertEq(re.lastIndex == 0, true);
return i;
}
var uceFault_typeof = eval(uneval(uceFault).replace('uceFault', 'uceFault_typeof'))
function rtypeof(i) {
var inputs = [ {}, [], 1, true, undefined, function(){}, null ];
@ -1098,6 +1140,9 @@ for (i = 0; i < 100; i++) {
rregexp_i_literal_replace(i);
rregexp_m_replace(i);
rregexp_m_literal_replace(i);
rstring_replace(i);
rstring_replace_y(i);
rstring_replace_g(i);
rtypeof(i);
rtodouble_value(i);
rtodouble_number(i);

View File

@ -366,19 +366,21 @@ class LSimdBinaryCompFx4 : public LSimdBinaryComp
};
// Binary SIMD arithmetic operation between two SIMD operands
class LSimdBinaryArith : public LInstructionHelper<1, 2, 0>
template<size_t Temps>
class LSimdBinaryArith : public LInstructionHelper<1, 2, Temps>
{
public:
LSimdBinaryArith() {}
const LAllocation *lhs() {
return getOperand(0);
return this->getOperand(0);
}
const LAllocation *rhs() {
return getOperand(1);
return this->getOperand(1);
}
MSimdBinaryArith::Operation operation() const {
return mir_->toSimdBinaryArith()->operation();
return this->mir_->toSimdBinaryArith()->operation();
}
const char *extraName() const {
return MSimdBinaryArith::OperationName(operation());
@ -386,19 +388,23 @@ class LSimdBinaryArith : public LInstructionHelper<1, 2, 0>
};
// Binary SIMD arithmetic operation between two Int32x4 operands
class LSimdBinaryArithIx4 : public LSimdBinaryArith
class LSimdBinaryArithIx4 : public LSimdBinaryArith<0>
{
public:
LIR_HEADER(SimdBinaryArithIx4);
LSimdBinaryArithIx4() : LSimdBinaryArith() {}
LSimdBinaryArithIx4() : LSimdBinaryArith<0>() {}
};
// Binary SIMD arithmetic operation between two Float32x4 operands
class LSimdBinaryArithFx4 : public LSimdBinaryArith
class LSimdBinaryArithFx4 : public LSimdBinaryArith<1>
{
public:
LIR_HEADER(SimdBinaryArithFx4);
LSimdBinaryArithFx4() : LSimdBinaryArith() {}
LSimdBinaryArithFx4() : LSimdBinaryArith<1>() {}
const LDefinition *temp() {
return getTemp(0);
}
};
// Unary SIMD arithmetic operation on a SIMD operand

View File

@ -4072,20 +4072,23 @@ LIRGenerator::visitSimdBinaryArith(MSimdBinaryArith *ins)
MDefinition *lhs = ins->lhs();
MDefinition *rhs = ins->rhs();
if (ins->isCommutative())
ReorderCommutative(&lhs, &rhs, ins);
if (ins->type() == MIRType_Int32x4) {
LSimdBinaryArithIx4 *add = new(alloc()) LSimdBinaryArithIx4();
return lowerForFPU(add, ins, lhs, rhs);
}
if (ins->type() == MIRType_Int32x4)
return lowerForFPU(new(alloc()) LSimdBinaryArithIx4(), ins, lhs, rhs);
if (ins->type() == MIRType_Float32x4) {
LSimdBinaryArithFx4 *add = new(alloc()) LSimdBinaryArithFx4();
return lowerForFPU(add, ins, lhs, rhs);
}
MOZ_ASSERT(ins->type() == MIRType_Float32x4, "unknown simd type on binary arith operation");
MOZ_CRASH("Unknown SIMD kind when adding values");
LSimdBinaryArithFx4 *lir = new(alloc()) LSimdBinaryArithFx4();
bool needsTemp = ins->operation() == MSimdBinaryArith::Max ||
ins->operation() == MSimdBinaryArith::MinNum ||
ins->operation() == MSimdBinaryArith::MaxNum;
lir->setTemp(0, needsTemp ? temp(LDefinition::FLOAT32X4) : LDefinition::BogusTemp());
return lowerForFPU(lir, ins, lhs, rhs);
}
bool

View File

@ -1857,17 +1857,21 @@ class MSimdBinaryArith : public MBinaryInstruction
Mul,
Div,
Min,
Max
Max,
MinNum,
MaxNum
};
static const char* OperationName(Operation op) {
switch (op) {
case Add: return "Add";
case Sub: return "Sub";
case Mul: return "Mul";
case Div: return "Div";
case Min: return "Min";
case Max: return "Max";
case Add: return "Add";
case Sub: return "Sub";
case Mul: return "Mul";
case Div: return "Div";
case Min: return "Min";
case Max: return "Max";
case MinNum: return "MinNum";
case MaxNum: return "MaxNum";
}
MOZ_CRASH("unexpected operation");
}
@ -6950,6 +6954,13 @@ class MStringReplace
AliasSet getAliasSet() const {
return AliasSet::None();
}
bool writeRecoverData(CompactBufferWriter &writer) const;
bool canRecoverOnBailout() const {
if (pattern()->isRegExp())
return !pattern()->toRegExp()->source()->global();
return false;
}
};
class MSubstr
@ -12092,15 +12103,19 @@ class MAsmJSNeg : public MUnaryInstruction
class MAsmJSHeapAccess
{
Scalar::Type viewType_;
protected:
typedef AsmJSHeapAccess::ViewType ViewType;
private:
ViewType viewType_;
bool needsBoundsCheck_;
public:
MAsmJSHeapAccess(Scalar::Type vt, bool needsBoundsCheck)
MAsmJSHeapAccess(ViewType vt, bool needsBoundsCheck)
: viewType_(vt), needsBoundsCheck_(needsBoundsCheck)
{}
Scalar::Type viewType() const { return viewType_; }
ViewType viewType() const { return viewType_; }
bool needsBoundsCheck() const { return needsBoundsCheck_; }
void removeBoundsCheck() { needsBoundsCheck_ = false; }
};
@ -12110,7 +12125,7 @@ class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
MemoryBarrierBits barrierBefore_;
MemoryBarrierBits barrierAfter_;
MAsmJSLoadHeap(Scalar::Type vt, MDefinition *ptr, bool needsBoundsCheck,
MAsmJSLoadHeap(ViewType vt, MDefinition *ptr, bool needsBoundsCheck,
MemoryBarrierBits before, MemoryBarrierBits after)
: MUnaryInstruction(ptr),
MAsmJSHeapAccess(vt, needsBoundsCheck),
@ -12121,18 +12136,37 @@ class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
setGuard(); // Not removable
else
setMovable();
if (vt == Scalar::Float32)
setResultType(MIRType_Float32);
else if (vt == Scalar::Float64)
setResultType(MIRType_Double);
else
switch (vt) {
case AsmJSHeapAccess::Int8:
case AsmJSHeapAccess::Uint8:
case AsmJSHeapAccess::Int16:
case AsmJSHeapAccess::Uint16:
case AsmJSHeapAccess::Int32:
case AsmJSHeapAccess::Uint32:
setResultType(MIRType_Int32);
break;
case AsmJSHeapAccess::Float32:
setResultType(MIRType_Float32);
break;
case AsmJSHeapAccess::Float64:
setResultType(MIRType_Double);
break;
case AsmJSHeapAccess::Float32x4:
setResultType(MIRType_Float32x4);
break;
case AsmJSHeapAccess::Int32x4:
setResultType(MIRType_Int32x4);
break;
case AsmJSHeapAccess::Uint8Clamped:
MOZ_CRASH("unexpected uint8clamped load heap in asm.js");
}
}
public:
INSTRUCTION_HEADER(AsmJSLoadHeap);
static MAsmJSLoadHeap *New(TempAllocator &alloc, Scalar::Type vt,
static MAsmJSLoadHeap *New(TempAllocator &alloc, ViewType vt,
MDefinition *ptr, bool needsBoundsCheck,
MemoryBarrierBits barrierBefore = MembarNobits,
MemoryBarrierBits barrierAfter = MembarNobits)
@ -12156,7 +12190,7 @@ class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
MemoryBarrierBits barrierBefore_;
MemoryBarrierBits barrierAfter_;
MAsmJSStoreHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck,
MAsmJSStoreHeap(ViewType vt, MDefinition *ptr, MDefinition *v, bool needsBoundsCheck,
MemoryBarrierBits before, MemoryBarrierBits after)
: MBinaryInstruction(ptr, v),
MAsmJSHeapAccess(vt, needsBoundsCheck),
@ -12170,7 +12204,7 @@ class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
public:
INSTRUCTION_HEADER(AsmJSStoreHeap);
static MAsmJSStoreHeap *New(TempAllocator &alloc, Scalar::Type vt,
static MAsmJSStoreHeap *New(TempAllocator &alloc, ViewType vt,
MDefinition *ptr, MDefinition *v, bool needsBoundsCheck,
MemoryBarrierBits barrierBefore = MembarNobits,
MemoryBarrierBits barrierAfter = MembarNobits)
@ -12191,7 +12225,7 @@ class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
class MAsmJSCompareExchangeHeap : public MTernaryInstruction, public MAsmJSHeapAccess
{
MAsmJSCompareExchangeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *oldv, MDefinition *newv,
MAsmJSCompareExchangeHeap(ViewType vt, MDefinition *ptr, MDefinition *oldv, MDefinition *newv,
bool needsBoundsCheck)
: MTernaryInstruction(ptr, oldv, newv),
MAsmJSHeapAccess(vt, needsBoundsCheck)
@ -12203,7 +12237,7 @@ class MAsmJSCompareExchangeHeap : public MTernaryInstruction, public MAsmJSHeapA
public:
INSTRUCTION_HEADER(AsmJSCompareExchangeHeap);
static MAsmJSCompareExchangeHeap *New(TempAllocator &alloc, Scalar::Type vt,
static MAsmJSCompareExchangeHeap *New(TempAllocator &alloc, ViewType vt,
MDefinition *ptr, MDefinition *oldv,
MDefinition *newv, bool needsBoundsCheck)
{
@ -12223,7 +12257,7 @@ class MAsmJSAtomicBinopHeap : public MBinaryInstruction, public MAsmJSHeapAccess
{
AtomicOp op_;
MAsmJSAtomicBinopHeap(AtomicOp op, Scalar::Type vt, MDefinition *ptr, MDefinition *v,
MAsmJSAtomicBinopHeap(AtomicOp op, ViewType vt, MDefinition *ptr, MDefinition *v,
bool needsBoundsCheck)
: MBinaryInstruction(ptr, v),
MAsmJSHeapAccess(vt, needsBoundsCheck),
@ -12236,7 +12270,7 @@ class MAsmJSAtomicBinopHeap : public MBinaryInstruction, public MAsmJSHeapAccess
public:
INSTRUCTION_HEADER(AsmJSAtomicBinopHeap);
static MAsmJSAtomicBinopHeap *New(TempAllocator &alloc, AtomicOp op, Scalar::Type vt,
static MAsmJSAtomicBinopHeap *New(TempAllocator &alloc, AtomicOp op, ViewType vt,
MDefinition *ptr, MDefinition *v, bool needsBoundsCheck)
{
return new(alloc) MAsmJSAtomicBinopHeap(op, vt, ptr, v, needsBoundsCheck);

View File

@ -1258,3 +1258,28 @@ RArrayState::recover(JSContext *cx, SnapshotIterator &iter) const
iter.storeInstructionResult(result);
return true;
}
bool
MStringReplace::writeRecoverData(CompactBufferWriter &writer) const
{
MOZ_ASSERT(canRecoverOnBailout());
writer.writeUnsigned(uint32_t(RInstruction::Recover_StringReplace));
return true;
}
RStringReplace::RStringReplace(CompactBufferReader &reader)
{ }
bool RStringReplace::recover(JSContext *cx, SnapshotIterator &iter) const
{
RootedString string(cx, iter.read().toString());
RootedString pattern(cx, iter.read().toString());
RootedString replace(cx, iter.read().toString());
RootedValue result(cx);
if (!js::str_replace_string_raw(cx, string, pattern, replace, &result))
return false;
iter.storeInstructionResult(result);
return true;
}

View File

@ -51,6 +51,7 @@ namespace jit {
_(RegExpExec) \
_(RegExpTest) \
_(RegExpReplace) \
_(StringReplace) \
_(TypeOf) \
_(ToDouble) \
_(ToFloat32) \
@ -518,6 +519,18 @@ class RRegExpReplace MOZ_FINAL : public RInstruction
bool recover(JSContext *cx, SnapshotIterator &iter) const;
};
class RStringReplace MOZ_FINAL : public RInstruction
{
public:
RINSTRUCTION_HEADER_(StringReplace)
virtual uint32_t numOperands() const {
return 3;
}
bool recover(JSContext *cx, SnapshotIterator &iter) const;
};
class RTypeOf MOZ_FINAL : public RInstruction
{
public:

View File

@ -2028,7 +2028,10 @@ bool
CodeGeneratorARM::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
{
MAsmJSCompareExchangeHeap *mir = ins->mir();
Scalar::Type vt = mir->viewType();
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
Scalar::Type vt = Scalar::Type(mir->viewType());
const LAllocation *ptr = ins->ptr();
Register ptrReg = ToRegister(ptr);
BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
@ -2063,7 +2066,10 @@ bool
CodeGeneratorARM::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins)
{
MAsmJSAtomicBinopHeap *mir = ins->mir();
Scalar::Type vt = mir->viewType();
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
Scalar::Type vt = Scalar::Type(mir->viewType());
const LAllocation *ptr = ins->ptr();
Register ptrReg = ToRegister(ptr);
Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());

View File

@ -199,8 +199,9 @@ LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
}
template<size_t Temps>
bool
LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
{
ins->setOperand(0, useRegisterAtStart(lhs));
ins->setOperand(1, useRegisterAtStart(rhs));
@ -208,6 +209,11 @@ LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
template bool LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs);
template bool LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 2, 1> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs);
bool
LIRGeneratorARM::lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
MDefinition *lhs, MDefinition *rhs)
@ -640,9 +646,7 @@ LIRGeneratorARM::visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArra
bool
LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins)
{
MOZ_ASSERT(ins->viewType() != Scalar::Uint8Clamped);
MOZ_ASSERT(ins->viewType() != Scalar::Float32);
MOZ_ASSERT(ins->viewType() != Scalar::Float64);
MOZ_ASSERT(ins->viewType() < AsmJSHeapAccess::Float32);
MDefinition *ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);
@ -658,9 +662,7 @@ LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap *ins)
bool
LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap *ins)
{
MOZ_ASSERT(ins->viewType() != Scalar::Uint8Clamped);
MOZ_ASSERT(ins->viewType() != Scalar::Float32);
MOZ_ASSERT(ins->viewType() != Scalar::Float64);
MOZ_ASSERT(ins->viewType() < AsmJSHeapAccess::Float32);
MDefinition *ptr = ins->ptr();
MOZ_ASSERT(ptr->type() == MIRType_Int32);

View File

@ -56,7 +56,8 @@ class LIRGeneratorARM : public LIRGeneratorShared
bool lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
MDefinition *src);
bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
template<size_t Temps>
bool lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs);
bool lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir,

View File

@ -201,8 +201,9 @@ LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir
LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
template<size_t Temps>
bool
LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs)
{
ins->setOperand(0, useRegister(lhs));
@ -211,6 +212,11 @@ LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir
LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
template bool LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs);
template bool LIRGeneratorMIPS::lowerForFPU(LInstructionHelper<1, 2, 1> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs);
bool
LIRGeneratorMIPS::lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
MDefinition *lhs, MDefinition *rhs)

View File

@ -56,7 +56,8 @@ class LIRGeneratorMIPS : public LIRGeneratorShared
bool lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
MDefinition *src);
bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
template<size_t Temps>
bool lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs);
bool lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir,

View File

@ -731,11 +731,27 @@ static const unsigned AsmJSNaN32GlobalDataOffset = 2 * sizeof(void*) + sizeof(do
// #ifdefery.
class AsmJSHeapAccess
{
public:
enum ViewType {
Int8 = Scalar::Int8,
Uint8 = Scalar::Uint8,
Int16 = Scalar::Int16,
Uint16 = Scalar::Uint16,
Int32 = Scalar::Int32,
Uint32 = Scalar::Uint32,
Float32 = Scalar::Float32,
Float64 = Scalar::Float64,
Uint8Clamped = Scalar::Uint8Clamped,
Float32x4,
Int32x4
};
private:
uint32_t offset_;
#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 isFloat32Load_;
ViewType viewType_;
AnyRegister::Code loadedReg_ : 8;
#endif
@ -748,19 +764,20 @@ class AsmJSHeapAccess
// If 'cmp' equals 'offset' or if it is not supplied then the
// cmpDelta_ is zero indicating that there is no length to patch.
AsmJSHeapAccess(uint32_t offset, uint32_t after, Scalar::Type vt,
AsmJSHeapAccess(uint32_t offset, uint32_t after, ViewType viewType,
AnyRegister loadedReg, uint32_t cmp = NoLengthCheck)
: offset_(offset),
cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp),
opLength_(after - offset),
isFloat32Load_(vt == Scalar::Float32),
viewType_(viewType),
loadedReg_(loadedReg.code())
{}
AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = NoLengthCheck)
AsmJSHeapAccess(uint32_t offset, uint8_t after, ViewType viewType,
uint32_t cmp = NoLengthCheck)
: offset_(offset),
cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp),
opLength_(after - offset),
isFloat32Load_(false),
viewType_(viewType),
loadedReg_(UINT8_MAX)
{}
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
@ -779,7 +796,7 @@ class AsmJSHeapAccess
void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); }
unsigned opLength() const { return opLength_; }
bool isLoad() const { return loadedReg_ != UINT8_MAX; }
bool isFloat32Load() const { return isFloat32Load_; }
ViewType viewType() const { return viewType_; }
AnyRegister loadedReg() const { return AnyRegister::FromCode(loadedReg_); }
#endif
};

View File

@ -475,6 +475,9 @@ class AssemblerX86Shared : public AssemblerShared
case Operand::MEM_SCALE:
masm.movaps_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
break;
case Operand::FPREG:
masm.movaps_rr(src.fpu(), dest.code());
break;
default:
MOZ_CRASH("unexpected operand kind");
}
@ -1688,6 +1691,21 @@ class AssemblerX86Shared : public AssemblerShared
MOZ_CRASH("unexpected operand kind");
}
}
void cmpeqps(const Operand &src, FloatRegister dest) {
cmpps(src, dest, X86Assembler::ConditionCmp_EQ);
}
void cmpltps(const Operand &src, FloatRegister dest) {
cmpps(src, dest, X86Assembler::ConditionCmp_LT);
}
void cmpleps(const Operand &src, FloatRegister dest) {
cmpps(src, dest, X86Assembler::ConditionCmp_LE);
}
void cmpunordps(const Operand &src, FloatRegister dest) {
cmpps(src, dest, X86Assembler::ConditionCmp_UNORD);
}
void cmpneqps(const Operand &src, FloatRegister dest) {
cmpps(src, dest, X86Assembler::ConditionCmp_NEQ);
}
void rcpps(const Operand &src, FloatRegister dest) {
MOZ_ASSERT(HasSSE2());
switch (src.kind()) {
@ -1889,6 +1907,7 @@ class AssemblerX86Shared : public AssemblerShared
}
}
void andnps(const Operand &src, FloatRegister dest) {
// Negates bits of dest and then applies AND
MOZ_ASSERT(HasSSE2());
switch (src.kind()) {
case Operand::FPREG:

View File

@ -197,6 +197,18 @@ public:
ConditionNC = ConditionAE
} Condition;
// Conditions for CMP instructions (CMPSS, CMPSD, CMPPS, CMPPD, etc).
typedef enum {
ConditionCmp_EQ = 0x0,
ConditionCmp_LT = 0x1,
ConditionCmp_LE = 0x2,
ConditionCmp_UNORD = 0x3,
ConditionCmp_NEQ = 0x4,
ConditionCmp_NLT = 0x5,
ConditionCmp_NLE = 0x6,
ConditionCmp_ORD = 0x7,
} ConditionCmp;
static const char* nameCC(Condition cc)
{
static const char* const names[16]
@ -3227,6 +3239,19 @@ public:
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
}
void movups_mr(const void* address, XMMRegisterID dst)
{
spew("movups %p, %s", address, nameFPReg(dst));
m_formatter.twoByteOp(OP2_MOVPS_VpsWps, (RegisterID)dst, address);
}
void movdqu_mr(const void* address, XMMRegisterID dst)
{
spew("movdqu %p, %s", address, nameFPReg(dst));
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp(OP2_MOVDQ_VdqWdq, (RegisterID)dst, address);
}
void movsd_rm(XMMRegisterID src, const void* address)
{
spew("movsd %s, %p", nameFPReg(src), address);
@ -3251,6 +3276,19 @@ public:
void movaps_rm(XMMRegisterID src, const void* address)
{
spew("movaps %s, %p", nameFPReg(src), address);
m_formatter.twoByteOp(OP2_MOVAPS_WsdVsd, (RegisterID)src, address);
}
void movdqu_rm(XMMRegisterID src, const void* address)
{
spew("movdqu %s, %p", nameFPReg(src), address);
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp(OP2_MOVDQ_WdqVdq, (RegisterID)src, address);
}
void movups_rm(XMMRegisterID src, const void* address)
{
spew("movups %s, %p", nameFPReg(src), address);
m_formatter.twoByteOp(OP2_MOVPS_WpsVps, (RegisterID)src, address);
}
#ifdef JS_CODEGEN_X64
@ -3334,6 +3372,12 @@ public:
nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
m_formatter.twoByteOp(OP2_MOVPS_WpsVps, (RegisterID)src, base, offset);
}
void movups_rm_disp32(XMMRegisterID src, int offset, RegisterID base)
{
spew("movups %s, %s0x%x(%s)",
nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
m_formatter.twoByteOp_disp32(OP2_MOVPS_WpsVps, (RegisterID)src, base, offset);
}
void movups_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
spew("movups %s, %d(%s,%s,%d)",
@ -3346,6 +3390,12 @@ public:
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
m_formatter.twoByteOp(OP2_MOVPS_VpsWps, (RegisterID)dst, base, offset);
}
void movups_mr_disp32(int offset, RegisterID base, XMMRegisterID dst)
{
spew("movups %s0x%x(%s), %s",
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
m_formatter.twoByteOp_disp32(OP2_MOVPS_VpsWps, (RegisterID)dst, base, offset);
}
void movups_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
{
spew("movups %d(%s,%s,%d), %s",
@ -3398,6 +3448,14 @@ public:
m_formatter.twoByteOp(OP2_MOVDQ_WdqVdq, (RegisterID)src, base, offset);
}
void movdqu_rm_disp32(XMMRegisterID src, int offset, RegisterID base)
{
spew("movdqu %s, %s0x%x(%s)",
nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp_disp32(OP2_MOVDQ_WdqVdq, (RegisterID)src, base, offset);
}
void movdqu_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
{
spew("movdqu %s, %d(%s,%s,%d)",
@ -3414,6 +3472,14 @@ public:
m_formatter.twoByteOp(OP2_MOVDQ_VdqWdq, (RegisterID)dst, base, offset);
}
void movdqu_mr_disp32(int offset, RegisterID base, XMMRegisterID dst)
{
spew("movdqu %s0x%x(%s), %s",
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
m_formatter.prefix(PRE_SSE_F3);
m_formatter.twoByteOp_disp32(OP2_MOVDQ_VdqWdq, (RegisterID)dst, base, offset);
}
void movdqu_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
{
spew("movdqu %d(%s,%s,%d), %s",

View File

@ -346,14 +346,29 @@ CodeGeneratorX86Shared::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins)
bool
CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
{
if (ool->dest().isFloat()) {
if (ool->isFloat32Load())
masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
else
masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
} else {
switch (ool->viewType()) {
case AsmJSHeapAccess::Float32:
masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
break;
case AsmJSHeapAccess::Float64:
masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
break;
case AsmJSHeapAccess::Float32x4:
masm.loadConstantFloat32x4(SimdConstant::SplatX4(float(GenericNaN())), ool->dest().fpu());
break;
case AsmJSHeapAccess::Int32x4:
masm.loadConstantInt32x4(SimdConstant::SplatX4(0), ool->dest().fpu());
break;
case AsmJSHeapAccess::Int8:
case AsmJSHeapAccess::Uint8:
case AsmJSHeapAccess::Int16:
case AsmJSHeapAccess::Uint16:
case AsmJSHeapAccess::Int32:
case AsmJSHeapAccess::Uint32:
case AsmJSHeapAccess::Uint8Clamped:
Register destReg = ool->dest().gpr();
masm.mov(ImmWord(0), destReg);
break;
}
masm.jmp(ool->rejoin());
return true;
@ -2705,16 +2720,16 @@ CodeGeneratorX86Shared::visitSimdBinaryCompFx4(LSimdBinaryCompFx4 *ins)
MSimdBinaryComp::Operation op = ins->operation();
switch (op) {
case MSimdBinaryComp::equal:
masm.cmpps(rhs, lhs, 0x0);
masm.cmpeqps(rhs, lhs);
return true;
case MSimdBinaryComp::lessThan:
masm.cmpps(rhs, lhs, 0x1);
masm.cmpltps(rhs, lhs);
return true;
case MSimdBinaryComp::lessThanOrEqual:
masm.cmpps(rhs, lhs, 0x2);
masm.cmpleps(rhs, lhs);
return true;
case MSimdBinaryComp::notEqual:
masm.cmpps(rhs, lhs, 0x4);
masm.cmpneqps(rhs, lhs);
return true;
case MSimdBinaryComp::greaterThanOrEqual:
case MSimdBinaryComp::greaterThan:
@ -2754,6 +2769,9 @@ CodeGeneratorX86Shared::visitSimdBinaryArithIx4(LSimdBinaryArithIx4 *ins)
// we can do max with a single instruction only if we have SSE4.1
// using the PMINSD instruction.
break;
case MSimdBinaryArith::MinNum:
case MSimdBinaryArith::MaxNum:
break;
}
MOZ_CRASH("unexpected SIMD op");
}
@ -2779,19 +2797,75 @@ CodeGeneratorX86Shared::visitSimdBinaryArithFx4(LSimdBinaryArithFx4 *ins)
case MSimdBinaryArith::Div:
masm.packedDivFloat32(rhs, lhs);
return true;
case MSimdBinaryArith::Max:
// TODO: standardization of float32x4.min/max needs to define the
// semantics for particular values: if both input operands are -0 or 0,
// or one operand is NaN, the return value will be the "second operand"
// in the instruction.
// See also bug 1068028, which is about fixing semantics once the
// specification is stable.
case MSimdBinaryArith::Max: {
masm.movaps(lhs, ScratchSimdReg);
masm.cmpunordps(rhs, ScratchSimdReg);
FloatRegister tmp = ToFloatRegister(ins->temp());
masm.movaps(rhs, tmp);
masm.maxps(Operand(lhs), tmp);
masm.maxps(rhs, lhs);
masm.andps(tmp, lhs);
masm.orps(ScratchSimdReg, lhs); // or in the all-ones NaNs
return true;
case MSimdBinaryArith::Min:
// See comment above.
}
case MSimdBinaryArith::Min: {
FloatRegister rhsCopy = ScratchSimdReg;
masm.movaps(rhs, rhsCopy);
masm.minps(Operand(lhs), rhsCopy);
masm.minps(rhs, lhs);
masm.orps(rhsCopy, lhs); // NaN or'd with arbitrary bits is NaN
return true;
}
case MSimdBinaryArith::MinNum: {
FloatRegister tmp = ToFloatRegister(ins->temp());
masm.loadConstantInt32x4(SimdConstant::SplatX4(int32_t(0x80000000)), ScratchSimdReg);
masm.movdqa(ScratchSimdReg, tmp);
FloatRegister mask = ScratchSimdReg;
masm.pcmpeqd(Operand(lhs), mask);
masm.andps(tmp, mask);
masm.movaps(lhs, tmp);
masm.minps(rhs, tmp);
masm.orps(mask, tmp);
masm.movaps(rhs, mask);
masm.cmpneqps(Operand(mask), mask);
// Emulates blendv
masm.andps(Operand(mask), lhs);
masm.andnps(Operand(tmp), mask);
masm.orps(Operand(mask), lhs);
return true;
}
case MSimdBinaryArith::MaxNum: {
FloatRegister mask = ScratchSimdReg;
masm.loadConstantInt32x4(SimdConstant::SplatX4(0), mask);
masm.pcmpeqd(Operand(lhs), mask);
FloatRegister tmp = ToFloatRegister(ins->temp());
masm.loadConstantInt32x4(SimdConstant::SplatX4(int32_t(0x80000000)), tmp);
masm.andps(tmp, mask);
masm.movaps(lhs, tmp);
masm.maxps(rhs, tmp);
masm.andnps(Operand(tmp), mask);
// Ensure tmp always contains the temporary result
mask = tmp;
tmp = ScratchSimdReg;
masm.movaps(rhs, mask);
masm.cmpneqps(Operand(mask), mask);
// Emulates blendv
masm.andps(Operand(mask), lhs);
masm.andnps(Operand(tmp), mask);
masm.orps(Operand(mask), lhs);
return true;
}
}
MOZ_CRASH("unexpected SIMD op");
}
@ -2942,8 +3016,6 @@ CodeGeneratorX86Shared::visitSimdSelect(LSimdSelect *ins)
FloatRegister onFalse = ToFloatRegister(ins->rhs());
MOZ_ASSERT(onTrue == ToFloatRegister(ins->output()));
// The onFalse argument is not destroyed but due to limitations of the
// register allocator its life ends at the start of the operation.
masm.bitwiseAndX4(Operand(mask), onTrue);
masm.bitwiseAndNotX4(Operand(onFalse), mask);
masm.bitwiseOrX4(Operand(mask), onTrue);

View File

@ -38,14 +38,14 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
class OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase<CodeGeneratorX86Shared>
{
AnyRegister dest_;
bool isFloat32Load_;
AsmJSHeapAccess::ViewType viewType_;
public:
OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, bool isFloat32Load)
: dest_(dest), isFloat32Load_(isFloat32Load)
OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, AsmJSHeapAccess::ViewType viewType)
: dest_(dest), viewType_(viewType)
{}
AnyRegister dest() const { return dest_; }
bool isFloat32Load() const { return isFloat32Load_; }
AsmJSHeapAccess::ViewType viewType() const { return viewType_; }
bool accept(CodeGeneratorX86Shared *codegen) {
return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this);
}

View File

@ -100,14 +100,20 @@ LIRGeneratorX86Shared::lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition
return defineReuseInput(ins, mir, 0);
}
template<size_t Temps>
bool
LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
{
ins->setOperand(0, useRegisterAtStart(lhs));
ins->setOperand(1, lhs != rhs ? use(rhs) : useAtStart(rhs));
return defineReuseInput(ins, mir, 0);
}
template bool LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs);
template bool LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, 1> *ins, MDefinition *mir,
MDefinition *lhs, MDefinition *rhs);
bool
LIRGeneratorX86Shared::lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir, MDefinition *lhs, MDefinition *rhs)
{

View File

@ -31,7 +31,8 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared
bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, MDefinition *input);
bool lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
MDefinition *rhs);
bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
template<size_t Temps>
bool lowerForFPU(LInstructionHelper<1, 2, Temps> *ins, MDefinition *mir, MDefinition *lhs,
MDefinition *rhs);
bool lowerForCompIx4(LSimdBinaryCompIx4 *ins, MSimdBinaryComp *mir,
MDefinition *lhs, MDefinition *rhs);

View File

@ -844,9 +844,15 @@ class MacroAssemblerX86Shared : public Assembler
void loadUnalignedInt32x4(const Address &src, FloatRegister dest) {
movdqu(Operand(src), dest);
}
void loadUnalignedInt32x4(const Operand &src, FloatRegister dest) {
movdqu(src, dest);
}
void storeUnalignedInt32x4(FloatRegister src, const Address &dest) {
movdqu(src, Operand(dest));
}
void storeUnalignedInt32x4(FloatRegister src, const Operand &dest) {
movdqu(src, dest);
}
void packedEqualInt32x4(const Operand &src, FloatRegister dest) {
pcmpeqd(src, dest);
}
@ -907,9 +913,15 @@ class MacroAssemblerX86Shared : public Assembler
void loadUnalignedFloat32x4(const Address &src, FloatRegister dest) {
movups(Operand(src), dest);
}
void loadUnalignedFloat32x4(const Operand &src, FloatRegister dest) {
movups(src, dest);
}
void storeUnalignedFloat32x4(FloatRegister src, const Address &dest) {
movups(src, Operand(dest));
}
void storeUnalignedFloat32x4(FloatRegister src, const Operand &dest) {
movups(src, dest);
}
void packedAddFloat32(const Operand &src, FloatRegister dest) {
addps(src, dest);
}

View File

@ -269,7 +269,7 @@ bool
CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
{
MAsmJSLoadHeap *mir = ins->mir();
Scalar::Type vt = mir->viewType();
AsmJSHeapAccess::ViewType vt = mir->viewType();
const LAllocation *ptr = ins->ptr();
const LDefinition *out = ins->output();
Operand srcAddr(HeapReg);
@ -286,8 +286,7 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr;
uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
if (mir->needsBoundsCheck()) {
bool isFloat32Load = vt == Scalar::Float32;
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt);
if (!addOutOfLineCode(ool, ins->mir()))
return false;
@ -298,15 +297,17 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
uint32_t before = masm.size();
switch (vt) {
case Scalar::Int8: masm.movsbl(srcAddr, ToRegister(out)); break;
case Scalar::Uint8: masm.movzbl(srcAddr, ToRegister(out)); break;
case Scalar::Int16: masm.movswl(srcAddr, ToRegister(out)); break;
case Scalar::Uint16: masm.movzwl(srcAddr, ToRegister(out)); break;
case Scalar::Int32:
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;
default: MOZ_CRASH("unexpected array type");
case AsmJSHeapAccess::Int8: masm.movsbl(srcAddr, ToRegister(out)); break;
case AsmJSHeapAccess::Uint8: masm.movzbl(srcAddr, ToRegister(out)); break;
case AsmJSHeapAccess::Int16: masm.movswl(srcAddr, ToRegister(out)); break;
case AsmJSHeapAccess::Uint16: masm.movzwl(srcAddr, ToRegister(out)); break;
case AsmJSHeapAccess::Int32:
case AsmJSHeapAccess::Uint32: masm.movl(srcAddr, ToRegister(out)); break;
case AsmJSHeapAccess::Float32: masm.loadFloat32(srcAddr, ToFloatRegister(out)); break;
case AsmJSHeapAccess::Float64: masm.loadDouble(srcAddr, ToFloatRegister(out)); break;
case AsmJSHeapAccess::Float32x4: masm.loadUnalignedFloat32x4(srcAddr, ToFloatRegister(out)); break;
case AsmJSHeapAccess::Int32x4: masm.loadUnalignedInt32x4(srcAddr, ToFloatRegister(out)); break;
case AsmJSHeapAccess::Uint8Clamped: MOZ_CRASH("unexpected array type");
}
uint32_t after = masm.size();
if (ool)
@ -320,7 +321,7 @@ bool
CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
{
MAsmJSStoreHeap *mir = ins->mir();
Scalar::Type vt = mir->viewType();
AsmJSHeapAccess::ViewType vt = mir->viewType();
const LAllocation *ptr = ins->ptr();
Operand dstAddr(HeapReg);
@ -344,32 +345,38 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
uint32_t before = masm.size();
if (ins->value()->isConstant()) {
switch (vt) {
case Scalar::Int8:
case Scalar::Uint8: masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
case Scalar::Int16:
case Scalar::Uint16: masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
case Scalar::Int32:
case Scalar::Uint32: masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
default: MOZ_CRASH("unexpected array type");
case AsmJSHeapAccess::Int8:
case AsmJSHeapAccess::Uint8: masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
case AsmJSHeapAccess::Int16:
case AsmJSHeapAccess::Uint16: masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
case AsmJSHeapAccess::Int32:
case AsmJSHeapAccess::Uint32: masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
case AsmJSHeapAccess::Float32:
case AsmJSHeapAccess::Float64:
case AsmJSHeapAccess::Float32x4:
case AsmJSHeapAccess::Int32x4:
case AsmJSHeapAccess::Uint8Clamped: MOZ_CRASH("unexpected array type");
}
} else {
switch (vt) {
case Scalar::Int8:
case Scalar::Uint8: masm.movb(ToRegister(ins->value()), dstAddr); break;
case Scalar::Int16:
case Scalar::Uint16: masm.movw(ToRegister(ins->value()), dstAddr); break;
case Scalar::Int32:
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;
default: MOZ_CRASH("unexpected array type");
case AsmJSHeapAccess::Int8:
case AsmJSHeapAccess::Uint8: masm.movb(ToRegister(ins->value()), dstAddr); break;
case AsmJSHeapAccess::Int16:
case AsmJSHeapAccess::Uint16: masm.movw(ToRegister(ins->value()), dstAddr); break;
case AsmJSHeapAccess::Int32:
case AsmJSHeapAccess::Uint32: masm.movl(ToRegister(ins->value()), dstAddr); break;
case AsmJSHeapAccess::Float32: masm.storeFloat32(ToFloatRegister(ins->value()), dstAddr); break;
case AsmJSHeapAccess::Float64: masm.storeDouble(ToFloatRegister(ins->value()), dstAddr); break;
case AsmJSHeapAccess::Float32x4: masm.storeUnalignedFloat32x4(ToFloatRegister(ins->value()), dstAddr); break;
case AsmJSHeapAccess::Int32x4: masm.storeUnalignedInt32x4(ToFloatRegister(ins->value()), dstAddr); break;
case AsmJSHeapAccess::Uint8Clamped: MOZ_CRASH("unexpected array type");
}
}
uint32_t after = masm.size();
if (rejoin.used())
masm.bind(&rejoin);
memoryBarrier(ins->mir()->barrierAfter());
masm.append(AsmJSHeapAccess(before, after, maybeCmpOffset));
masm.append(AsmJSHeapAccess(before, after, vt, maybeCmpOffset));
return true;
}
@ -377,7 +384,10 @@ bool
CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
{
MAsmJSCompareExchangeHeap *mir = ins->mir();
Scalar::Type vt = mir->viewType();
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
Scalar::Type vt = Scalar::Type(mir->viewType());
const LAllocation *ptr = ins->ptr();
MOZ_ASSERT(ptr->isRegister());
@ -395,7 +405,7 @@ CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
masm.j(Assembler::LessThan, &goahead);
memoryBarrier(MembarFull);
Register out = ToRegister(ins->output());
masm.xorl(out,out);
masm.xorl(out, out);
masm.jmp(&rejoin);
masm.bind(&goahead);
}
@ -408,7 +418,7 @@ CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
uint32_t after = masm.size();
if (rejoin.used())
masm.bind(&rejoin);
masm.append(AsmJSHeapAccess(after, after, maybeCmpOffset));
masm.append(AsmJSHeapAccess(after, after, mir->viewType(), maybeCmpOffset));
return true;
}
@ -416,7 +426,10 @@ bool
CodeGeneratorX64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins)
{
MAsmJSAtomicBinopHeap *mir = ins->mir();
Scalar::Type vt = mir->viewType();
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
Scalar::Type vt = Scalar::Type(mir->viewType());
const LAllocation *ptr = ins->ptr();
Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
const LAllocation* value = ins->value();
@ -456,7 +469,7 @@ CodeGeneratorX64::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins)
uint32_t after = masm.size();
if (rejoin.used())
masm.bind(&rejoin);
masm.append(AsmJSHeapAccess(after, after, maybeCmpOffset));
masm.append(AsmJSHeapAccess(after, after, mir->viewType(), maybeCmpOffset));
return true;
}

View File

@ -163,19 +163,21 @@ LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
LAsmJSStoreHeap *lir;
switch (ins->viewType()) {
case Scalar::Int8:
case Scalar::Uint8:
case Scalar::Int16:
case Scalar::Uint16:
case Scalar::Int32:
case Scalar::Uint32:
case AsmJSHeapAccess::Int8:
case AsmJSHeapAccess::Uint8:
case AsmJSHeapAccess::Int16:
case AsmJSHeapAccess::Uint16:
case AsmJSHeapAccess::Int32:
case AsmJSHeapAccess::Uint32:
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterOrConstantAtStart(ins->value()));
break;
case Scalar::Float32:
case Scalar::Float64:
case AsmJSHeapAccess::Float32:
case AsmJSHeapAccess::Float64:
case AsmJSHeapAccess::Float32x4:
case AsmJSHeapAccess::Int32x4:
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value()));
break;
default:
case AsmJSHeapAccess::Uint8Clamped:
MOZ_CRASH("unexpected array type");
}

View File

@ -465,6 +465,16 @@ class Assembler : public AssemblerX86Shared
masm.movsd_mr_disp32(src.offset, src.base.code(), dest.code());
return CodeOffsetLabel(masm.currentOffset());
}
CodeOffsetLabel movupsWithPatch(Address src, FloatRegister dest) {
MOZ_ASSERT(HasSSE2());
masm.movups_mr_disp32(src.offset, src.base.code(), dest.code());
return CodeOffsetLabel(masm.currentOffset());
}
CodeOffsetLabel movdquWithPatch(Address src, FloatRegister dest) {
MOZ_ASSERT(HasSSE2());
masm.movdqu_mr_disp32(src.offset, src.base.code(), dest.code());
return CodeOffsetLabel(masm.currentOffset());
}
// Store to *(base + disp32) where disp32 can be patched.
CodeOffsetLabel movbWithPatch(Register src, Address dest) {
@ -489,6 +499,16 @@ class Assembler : public AssemblerX86Shared
masm.movsd_rm_disp32(src.code(), dest.offset, dest.base.code());
return CodeOffsetLabel(masm.currentOffset());
}
CodeOffsetLabel movupsWithPatch(FloatRegister src, Address dest) {
MOZ_ASSERT(HasSSE2());
masm.movups_rm_disp32(src.code(), dest.offset, dest.base.code());
return CodeOffsetLabel(masm.currentOffset());
}
CodeOffsetLabel movdquWithPatch(FloatRegister src, Address dest) {
MOZ_ASSERT(HasSSE2());
masm.movdqu_rm_disp32(src.code(), dest.offset, dest.base.code());
return CodeOffsetLabel(masm.currentOffset());
}
// Load from *(addr + index*scale) where addr can be patched.
CodeOffsetLabel movlWithPatch(PatchedAbsoluteAddress addr, Register index, Scale scale,
@ -534,11 +554,21 @@ class Assembler : public AssemblerX86Shared
masm.movdqa_mr(src.addr, dest.code());
return CodeOffsetLabel(masm.currentOffset());
}
CodeOffsetLabel movdquWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) {
MOZ_ASSERT(HasSSE2());
masm.movdqu_mr(src.addr, dest.code());
return CodeOffsetLabel(masm.currentOffset());
}
CodeOffsetLabel movapsWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) {
MOZ_ASSERT(HasSSE2());
masm.movaps_mr(src.addr, dest.code());
return CodeOffsetLabel(masm.currentOffset());
}
CodeOffsetLabel movupsWithPatch(PatchedAbsoluteAddress src, FloatRegister dest) {
MOZ_ASSERT(HasSSE2());
masm.movups_mr(src.addr, dest.code());
return CodeOffsetLabel(masm.currentOffset());
}
// Store to *dest where dest can be patched.
CodeOffsetLabel movbWithPatch(Register src, PatchedAbsoluteAddress dest) {
@ -573,6 +603,16 @@ class Assembler : public AssemblerX86Shared
masm.movaps_rm(src.code(), dest.addr);
return CodeOffsetLabel(masm.currentOffset());
}
CodeOffsetLabel movdquWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) {
MOZ_ASSERT(HasSSE2());
masm.movdqu_rm(src.code(), dest.addr);
return CodeOffsetLabel(masm.currentOffset());
}
CodeOffsetLabel movupsWithPatch(FloatRegister src, PatchedAbsoluteAddress dest) {
MOZ_ASSERT(HasSSE2());
masm.movups_rm(src.code(), dest.addr);
return CodeOffsetLabel(masm.currentOffset());
}
void loadAsmJSActivation(Register dest) {
CodeOffsetLabel label = movlWithPatch(PatchedAbsoluteAddress(), dest);

View File

@ -267,26 +267,27 @@ CodeGeneratorX86::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir)
template<typename T>
void
CodeGeneratorX86::loadViewTypeElement(Scalar::Type vt, const T &srcAddr,
CodeGeneratorX86::loadViewTypeElement(AsmJSHeapAccess::ViewType vt, const T &srcAddr,
const LDefinition *out)
{
switch (vt) {
case Scalar::Int8: masm.movsblWithPatch(srcAddr, ToRegister(out)); break;
case Scalar::Uint8Clamped:
case Scalar::Uint8: masm.movzblWithPatch(srcAddr, ToRegister(out)); break;
case Scalar::Int16: masm.movswlWithPatch(srcAddr, ToRegister(out)); break;
case Scalar::Uint16: masm.movzwlWithPatch(srcAddr, ToRegister(out)); break;
case Scalar::Int32:
case Scalar::Uint32: masm.movlWithPatch(srcAddr, ToRegister(out)); break;
case Scalar::Float32: masm.movssWithPatch(srcAddr, ToFloatRegister(out)); break;
case Scalar::Float64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break;
default: MOZ_CRASH("unexpected array type");
case AsmJSHeapAccess::Int8: masm.movsblWithPatch(srcAddr, ToRegister(out)); break;
case AsmJSHeapAccess::Uint8Clamped:
case AsmJSHeapAccess::Uint8: masm.movzblWithPatch(srcAddr, ToRegister(out)); break;
case AsmJSHeapAccess::Int16: masm.movswlWithPatch(srcAddr, ToRegister(out)); break;
case AsmJSHeapAccess::Uint16: masm.movzwlWithPatch(srcAddr, ToRegister(out)); break;
case AsmJSHeapAccess::Int32:
case AsmJSHeapAccess::Uint32: masm.movlWithPatch(srcAddr, ToRegister(out)); break;
case AsmJSHeapAccess::Float32: masm.movssWithPatch(srcAddr, ToFloatRegister(out)); break;
case AsmJSHeapAccess::Float64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break;
case AsmJSHeapAccess::Float32x4: masm.movupsWithPatch(srcAddr, ToFloatRegister(out)); break;
case AsmJSHeapAccess::Int32x4: masm.movdquWithPatch(srcAddr, ToFloatRegister(out)); break;
}
}
template<typename T>
bool
CodeGeneratorX86::loadAndNoteViewTypeElement(Scalar::Type vt, const T &srcAddr,
CodeGeneratorX86::loadAndNoteViewTypeElement(AsmJSHeapAccess::ViewType vt, const T &srcAddr,
const LDefinition *out)
{
uint32_t before = masm.size();
@ -300,16 +301,15 @@ bool
CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
{
const MLoadTypedArrayElementStatic *mir = ins->mir();
Scalar::Type vt = mir->viewType();
MOZ_ASSERT_IF(vt == Scalar::Float32, mir->type() == MIRType_Float32);
AsmJSHeapAccess::ViewType vt = AsmJSHeapAccess::ViewType(mir->viewType());
MOZ_ASSERT_IF(vt == AsmJSHeapAccess::Float32, mir->type() == MIRType_Float32);
Register ptr = ToRegister(ins->ptr());
const LDefinition *out = ins->output();
OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr;
bool isFloat32Load = (vt == Scalar::Float32);
if (!mir->fallible()) {
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt);
if (!addOutOfLineCode(ool, ins->mir()))
return false;
}
@ -322,9 +322,9 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic
Address srcAddr(ptr, (int32_t) mir->base());
loadViewTypeElement(vt, srcAddr, out);
if (vt == Scalar::Float64)
if (vt == AsmJSHeapAccess::Float64)
masm.canonicalizeDouble(ToFloatRegister(out));
if (vt == Scalar::Float32)
if (vt == AsmJSHeapAccess::Float32)
masm.canonicalizeFloat(ToFloatRegister(out));
if (ool)
masm.bind(ool->rejoin());
@ -369,7 +369,7 @@ bool
CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
{
const MAsmJSLoadHeap *mir = ins->mir();
Scalar::Type vt = mir->viewType();
AsmJSHeapAccess::ViewType vt = mir->viewType();
const LAllocation *ptr = ins->ptr();
const LDefinition *out = ins->output();
@ -395,8 +395,7 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
return true;
}
bool isFloat32Load = vt == Scalar::Float32;
OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), vt);
if (!addOutOfLineCode(ool, mir))
return false;
@ -414,39 +413,40 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
template<typename T>
void
CodeGeneratorX86::storeViewTypeElement(Scalar::Type vt, const LAllocation *value,
CodeGeneratorX86::storeViewTypeElement(AsmJSHeapAccess::ViewType vt, const LAllocation *value,
const T &dstAddr)
{
switch (vt) {
case Scalar::Int8:
case Scalar::Uint8Clamped:
case Scalar::Uint8: masm.movbWithPatch(ToRegister(value), dstAddr); break;
case Scalar::Int16:
case Scalar::Uint16: masm.movwWithPatch(ToRegister(value), dstAddr); break;
case Scalar::Int32:
case Scalar::Uint32: masm.movlWithPatch(ToRegister(value), dstAddr); break;
case Scalar::Float32: masm.movssWithPatch(ToFloatRegister(value), dstAddr); break;
case Scalar::Float64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break;
default: MOZ_CRASH("unexpected array type");
case AsmJSHeapAccess::Int8:
case AsmJSHeapAccess::Uint8Clamped:
case AsmJSHeapAccess::Uint8: masm.movbWithPatch(ToRegister(value), dstAddr); break;
case AsmJSHeapAccess::Int16:
case AsmJSHeapAccess::Uint16: masm.movwWithPatch(ToRegister(value), dstAddr); break;
case AsmJSHeapAccess::Int32:
case AsmJSHeapAccess::Uint32: masm.movlWithPatch(ToRegister(value), dstAddr); break;
case AsmJSHeapAccess::Float32: masm.movssWithPatch(ToFloatRegister(value), dstAddr); break;
case AsmJSHeapAccess::Float64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break;
case AsmJSHeapAccess::Float32x4: masm.movupsWithPatch(ToFloatRegister(value), dstAddr); break;
case AsmJSHeapAccess::Int32x4: masm.movdquWithPatch(ToFloatRegister(value), dstAddr); break;
}
}
template<typename T>
void
CodeGeneratorX86::storeAndNoteViewTypeElement(Scalar::Type vt, const LAllocation *value,
const T &dstAddr)
CodeGeneratorX86::storeAndNoteViewTypeElement(AsmJSHeapAccess::ViewType vt,
const LAllocation *value, const T &dstAddr)
{
uint32_t before = masm.size();
storeViewTypeElement(vt, value, dstAddr);
uint32_t after = masm.size();
masm.append(AsmJSHeapAccess(before, after));
masm.append(AsmJSHeapAccess(before, after, vt));
}
bool
CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
{
MStoreTypedArrayElementStatic *mir = ins->mir();
Scalar::Type vt = mir->viewType();
AsmJSHeapAccess::ViewType vt = AsmJSHeapAccess::ViewType(mir->viewType());
Register ptr = ToRegister(ins->ptr());
const LAllocation *value = ins->value();
@ -465,7 +465,7 @@ bool
CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
{
MAsmJSStoreHeap *mir = ins->mir();
Scalar::Type vt = mir->viewType();
AsmJSHeapAccess::ViewType vt = mir->viewType();
const LAllocation *value = ins->value();
const LAllocation *ptr = ins->ptr();
@ -500,7 +500,7 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
uint32_t after = masm.size();
masm.bind(&rejoin);
memoryBarrier(ins->mir()->barrierAfter());
masm.append(AsmJSHeapAccess(before, after, cmp.offset()));
masm.append(AsmJSHeapAccess(before, after, vt, cmp.offset()));
return true;
}
@ -508,7 +508,10 @@ bool
CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
{
MAsmJSCompareExchangeHeap *mir = ins->mir();
Scalar::Type vt = mir->viewType();
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
Scalar::Type vt = Scalar::Type(mir->viewType());
const LAllocation *ptr = ins->ptr();
Register oldval = ToRegister(ins->oldValue());
Register newval = ToRegister(ins->newValue());
@ -536,7 +539,7 @@ CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap *ins)
uint32_t before = masm.size();
masm.addl_wide(Imm32(0), ptrReg);
uint32_t after = masm.size();
masm.append(AsmJSHeapAccess(before, after, maybeCmpOffset));
masm.append(AsmJSHeapAccess(before, after, mir->viewType(), maybeCmpOffset));
Address memAddr(ToRegister(ptr), 0);
masm.compareExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
@ -555,7 +558,10 @@ bool
CodeGeneratorX86::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins)
{
MAsmJSAtomicBinopHeap *mir = ins->mir();
Scalar::Type vt = mir->viewType();
MOZ_ASSERT(mir->viewType() <= AsmJSHeapAccess::Uint32);
Scalar::Type vt = Scalar::Type(mir->viewType());
const LAllocation *ptr = ins->ptr();
Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
const LAllocation* value = ins->value();
@ -584,7 +590,7 @@ CodeGeneratorX86::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap *ins)
uint32_t before = masm.size();
masm.addl_wide(Imm32(0), ptrReg);
uint32_t after = masm.size();
masm.append(AsmJSHeapAccess(before, after, maybeCmpOffset));
masm.append(AsmJSHeapAccess(before, after, mir->viewType(), maybeCmpOffset));
Address memAddr(ptrReg, 0);
if (value->isConstant()) {

View File

@ -29,17 +29,18 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
ValueOperand ToTempValue(LInstruction *ins, size_t pos);
template<typename T>
bool loadAndNoteViewTypeElement(Scalar::Type vt, const T &srcAddr,
bool loadAndNoteViewTypeElement(AsmJSHeapAccess::ViewType vt, const T &srcAddr,
const LDefinition *out);
template<typename T>
void loadViewTypeElement(AsmJSHeapAccess::ViewType vt, const T &srcAddr,
const LDefinition *out);
template<typename T>
void loadViewTypeElement(Scalar::Type vt, const T &srcAddr,
const LDefinition *out);
template<typename T>
void storeAndNoteViewTypeElement(Scalar::Type vt, const LAllocation *value,
void storeAndNoteViewTypeElement(AsmJSHeapAccess::ViewType vt, const LAllocation *value,
const T &dstAddr);
template<typename T>
void storeViewTypeElement(Scalar::Type vt, const LAllocation *value,
void storeViewTypeElement(AsmJSHeapAccess::ViewType vt, const LAllocation *value,
const T &dstAddr);
void memoryBarrier(MemoryBarrierBits barrier);
public:

View File

@ -243,34 +243,38 @@ LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
MOZ_ASSERT(ptrValue >= 0);
LAllocation ptrAlloc = LAllocation(ptr->toConstant()->vp());
switch (ins->viewType()) {
case Scalar::Int8: case Scalar::Uint8:
case AsmJSHeapAccess::Int8: case AsmJSHeapAccess::Uint8:
// See comment below.
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useFixed(ins->value(), eax));
break;
case Scalar::Int16: case Scalar::Uint16:
case Scalar::Int32: case Scalar::Uint32:
case Scalar::Float32: case Scalar::Float64:
case AsmJSHeapAccess::Int16: case AsmJSHeapAccess::Uint16:
case AsmJSHeapAccess::Int32: case AsmJSHeapAccess::Uint32:
case AsmJSHeapAccess::Float32: case AsmJSHeapAccess::Float64:
case AsmJSHeapAccess::Float32x4: case AsmJSHeapAccess::Int32x4:
// See comment below.
lir = new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value()));
break;
default: MOZ_CRASH("unexpected array type");
case AsmJSHeapAccess::Uint8Clamped:
MOZ_CRASH("unexpected array type");
}
return add(lir, ins);
}
switch (ins->viewType()) {
case Scalar::Int8: case Scalar::Uint8:
case AsmJSHeapAccess::Int8: case AsmJSHeapAccess::Uint8:
// See comment for LIRGeneratorX86::useByteOpRegister.
lir = new(alloc()) LAsmJSStoreHeap(useRegister(ins->ptr()), useFixed(ins->value(), eax));
break;
case Scalar::Int16: case Scalar::Uint16:
case Scalar::Int32: case Scalar::Uint32:
case Scalar::Float32: case Scalar::Float64:
case AsmJSHeapAccess::Int16: case AsmJSHeapAccess::Uint16:
case AsmJSHeapAccess::Int32: case AsmJSHeapAccess::Uint32:
case AsmJSHeapAccess::Float32: case AsmJSHeapAccess::Float64:
case AsmJSHeapAccess::Float32x4: case AsmJSHeapAccess::Int32x4:
// For now, don't allow constant values. The immediate operand
// affects instruction layout which affects patching.
lir = new(alloc()) LAsmJSStoreHeap(useRegisterAtStart(ptr), useRegisterAtStart(ins->value()));
break;
default: MOZ_CRASH("unexpected array type");
case AsmJSHeapAccess::Uint8Clamped:
MOZ_CRASH("unexpected array type");
}
return add(lir, ins);

View File

@ -3229,7 +3229,9 @@ static const JSFunctionSpec array_methods[] = {
JS_SELF_HOSTED_FN("keys", "ArrayKeys", 0,0),
/* ES7 additions */
#ifdef NIGHTLY_BUILD
JS_SELF_HOSTED_FN("includes", "ArrayIncludes", 2,0),
#endif
JS_FS_END
};

View File

@ -565,8 +565,8 @@ js::math_log(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static double
max_double(double x, double y)
double
js::math_max_impl(double x, double y)
{
// Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
if (x > y || IsNaN(x) || (x == y && IsNegative(y)))
@ -584,14 +584,14 @@ js::math_max(JSContext *cx, unsigned argc, Value *vp)
double x;
if (!ToNumber(cx, args[i], &x))
return false;
maxval = max_double(x, maxval);
maxval = math_max_impl(x, maxval);
}
args.rval().setNumber(maxval);
return true;
}
static double
min_double(double x, double y)
double
js::math_min_impl(double x, double y)
{
// Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
if (x < y || IsNaN(x) || (x == y && IsNegativeZero(x)))
@ -609,7 +609,7 @@ js::math_min(JSContext *cx, unsigned argc, Value *vp)
double x;
if (!ToNumber(cx, args[i], &x))
return false;
minval = min_double(x, minval);
minval = math_min_impl(x, minval);
}
args.rval().setNumber(minval);
return true;
@ -626,9 +626,9 @@ js::minmax_impl(JSContext *cx, bool max, HandleValue a, HandleValue b, MutableHa
return false;
if (max)
res.setNumber(max_double(x, y));
res.setNumber(math_max_impl(x, y));
else
res.setNumber(min_double(x, y));
res.setNumber(math_min_impl(x, y));
return true;
}

View File

@ -120,9 +120,15 @@ math_abs_handle(JSContext *cx, js::HandleValue v, js::MutableHandleValue r);
extern bool
math_abs(JSContext *cx, unsigned argc, js::Value *vp);
extern double
math_max_impl(double x, double y);
extern bool
math_max(JSContext *cx, unsigned argc, js::Value *vp);
extern double
math_min_impl(double x, double y);
extern bool
math_min(JSContext *cx, unsigned argc, js::Value *vp);

View File

@ -8,7 +8,18 @@
#include "shell/OSObject.h"
#include <errno.h>
#include <stdlib.h>
#ifdef XP_WIN
#include <process.h>
#include <string.h>
#else
#include <sys/wait.h>
#include <unistd.h>
#endif
// For JSFunctionSpecWithHelp
#include "jsfriendapi.h"
using namespace JS;
@ -38,9 +49,239 @@ os_getenv(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static const JSFunctionSpec os_functions[] = {
JS_FS("getenv", os_getenv, 1, 0),
JS_FS_END
static bool
os_getpid(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 0) {
JS_ReportError(cx, "os.getpid takes no arguments");
return false;
}
args.rval().setInt32(getpid());
return true;
}
// There are two possible definitions of strerror_r floating around. The GNU
// one returns a char* which may or may not be the buffer you passed in. The
// other one returns an integer status code, and always writes the result into
// the provided buffer.
static inline char *
strerror_message(int result, char *buffer)
{
return result == 0 ? buffer : nullptr;
}
static inline char *
strerror_message(char *result, char *buffer)
{
return result;
}
static void
ReportSysError(JSContext *cx, const char *prefix)
{
static char buffer[200];
#if defined(XP_WIN)
strerror_s(buffer, sizeof(buffer), errno);
const char *errstr = buffer;
#else
const char *errstr = strerror_message(strerror_r(errno, buffer, sizeof(buffer)), buffer);
#endif
if (!errstr)
errstr = "unknown error";
size_t nbytes = strlen(prefix) + strlen(errstr) + 3;
char *final = (char*) js_malloc(nbytes);
if (!final) {
JS_ReportOutOfMemory(cx);
return;
}
#ifdef XP_WIN
_snprintf(final, nbytes, "%s: %s", prefix, errstr);
#else
snprintf(final, nbytes, "%s: %s", prefix, errstr);
#endif
JS_ReportError(cx, final);
js_free(final);
}
static bool
os_system(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() == 0) {
JS_ReportError(cx, "os.system requires 1 argument");
return false;
}
JSString *str = JS::ToString(cx, args[0]);
if (!str)
return false;
JSAutoByteString command(cx, str);
if (!command)
return false;
int result = system(command.ptr());
if (result == -1) {
ReportSysError(cx, "system call failed");
return false;
}
args.rval().setInt32(result);
return true;
}
#ifndef XP_WIN
static bool
os_spawn(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() == 0) {
JS_ReportError(cx, "os.spawn requires 1 argument");
return false;
}
JSString *str = JS::ToString(cx, args[0]);
if (!str)
return false;
JSAutoByteString command(cx, str);
if (!command)
return false;
int32_t childPid = fork();
if (childPid) {
args.rval().setInt32(childPid);
return true;
}
if (childPid == -1) {
ReportSysError(cx, "fork failed");
return false;
}
// We are in the child
const char *cmd[] = {"sh", "-c", nullptr, nullptr};
cmd[2] = command.ptr();
execvp("sh", (char * const*)cmd);
exit(1);
}
static bool
os_kill(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
int32_t pid;
if (args.length() < 1) {
JS_ReportError(cx, "os.kill requires 1 argument");
return false;
}
if (!JS::ToInt32(cx, args[0], &pid))
return false;
// It is too easy to kill yourself accidentally with os.kill("goose").
if (pid == 0 && !args[0].isInt32()) {
JS_ReportError(cx, "os.kill requires numeric pid");
return false;
}
int signal = SIGINT;
if (args.length() > 1) {
if (!JS::ToInt32(cx, args[1], &signal))
return false;
}
int status = kill(pid, signal);
if (status == -1)
ReportSysError(cx, "kill failed");
args.rval().setUndefined();
return true;
}
static bool
os_waitpid(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
int32_t pid;
if (args.length() == 0) {
pid = -1;
} else {
if (!JS::ToInt32(cx, args[0], &pid))
return false;
}
bool nohang = false;
if (args.length() >= 2)
nohang = JS::ToBoolean(args[1]);
int status;
pid_t result = waitpid(pid, &status, nohang ? WNOHANG : 0);
if (result == -1) {
ReportSysError(cx, "os.waitpid failed");
return false;
}
RootedObject info(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
if (!info)
return false;
RootedValue v(cx);
if (result != 0) {
v.setInt32(result);
if (!JS_DefineProperty(cx, info, "pid", v, JSPROP_ENUMERATE))
return false;
}
if (WIFEXITED(status)) {
v.setInt32(WEXITSTATUS(status));
if (!JS_DefineProperty(cx, info, "exitStatus", v, JSPROP_ENUMERATE))
return false;
}
args.rval().setObject(*info);
return true;
}
#endif
static const JSFunctionSpecWithHelp os_functions[] = {
JS_FN_HELP("getenv", os_getenv, 1, 0,
"getenv(variable)",
" Get the value of an environment variable."),
JS_FN_HELP("getpid", os_getpid, 0, 0,
"getpid()",
" Return the current process id."),
JS_FN_HELP("system", os_system, 1, 0,
"system(command)",
" Execute command on the current host, returning result code or throwing an\n"
" exception on failure."),
#ifndef XP_WIN
JS_FN_HELP("spawn", os_spawn, 1, 0,
"spawn(command)",
" Start up a separate process running the given command. Returns the pid."),
JS_FN_HELP("kill", os_kill, 1, 0,
"kill(pid[, signal])",
" Send a signal to the given pid. The default signal is SIGINT. The signal\n"
" passed in must be numeric, if given."),
JS_FN_HELP("waitpid", os_waitpid, 1, 0,
"waitpid(pid[, nohang])",
" Calls waitpid(). 'nohang' is a boolean indicating whether to pass WNOHANG.\n"
" The return value is an object containing a 'pid' field, if a process was waitable\n"
" and an 'exitStatus' field if a pid exited."),
#endif
JS_FS_HELP_END
};
bool
@ -48,6 +289,6 @@ js::DefineOS(JSContext *cx, HandleObject global)
{
RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
return obj &&
JS_DefineFunctions(cx, obj, os_functions) &&
JS_DefineFunctionsWithHelp(cx, obj, os_functions) &&
JS_DefineProperty(cx, global, "os", obj, 0);
}

View File

@ -3575,30 +3575,6 @@ RedirectOutput(JSContext *cx, unsigned argc, jsval *vp)
return true;
}
static bool
System(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() == 0) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
"system");
return false;
}
JSString *str = JS::ToString(cx, args[0]);
if (!str)
return false;
JSAutoByteString command(cx, str);
if (!command)
return false;
int result = system(command.ptr());
args.rval().setInt32(result);
return true;
}
static int sArgc;
static char **sArgv;
@ -4565,10 +4541,6 @@ static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
" Redirect stdout and/or stderr to the named file. Pass undefined to avoid\n"
" redirecting. Filenames are relative to the current working directory."),
JS_FN_HELP("system", System, 1, 0,
"system(command)",
" Execute command on the current host, returning result code."),
JS_FN_HELP("nestedShell", NestedShell, 0, 0,
"nestedShell(shellArgs...)",
" Execute the given code in a new JS shell process, passing this nested shell\n"

View File

@ -0,0 +1,54 @@
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
var BUGNUMBER = 946042;
var float32x4 = SIMD.float32x4;
function testMaxFloat32(v, w) {
return testBinaryFunc(v, w, float32x4.max, (x, y) => Math.fround(Math.max(x, y)));
}
function testMinFloat32(v, w) {
return testBinaryFunc(v, w, float32x4.min, (x, y) => Math.fround(Math.min(x, y)));
}
function maxNum(x, y) {
if (x != x)
return y;
if (y != y)
return x;
return Math.max(x, y);
}
function minNum(x, y) {
if (x != x)
return y;
if (y != y)
return x;
return Math.min(x, y);
}
function testMaxNumFloat32(v, w) {
return testBinaryFunc(v, w, float32x4.maxNum, maxNum);
}
function testMinNumFloat32(v, w) {
return testBinaryFunc(v, w, float32x4.minNum, minNum);
}
function test() {
print(BUGNUMBER + ": " + summary);
for ([v, w] of [[float32x4(1, 20, 30, 4), float32x4(10, 2, 3, 40)],
[float32x4(9.999, 2.1234, 30.4443, 4), float32x4(10, 2.1233, 30.4444, 4.0001)],
[float32x4(NaN, -Infinity, +Infinity, -0), float32x4(13.37, 42.42, NaN, 0)]])
{
testMinFloat32(v, w);
testMaxFloat32(v, w);
testMinNumFloat32(v, w);
testMaxNumFloat32(v, w);
}
if (typeof reportCompare === "function")
reportCompare(true, true);
}
test();

View File

@ -1,35 +0,0 @@
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
var BUGNUMBER = 946042;
var float32x4 = SIMD.float32x4;
var int32x4 = SIMD.int32x4;
var summary = 'float32x4 max';
function test() {
print(BUGNUMBER + ": " + summary);
// FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
var a = float32x4(1, 20, 30, 4);
var b = float32x4(10, 2, 3, 40);
var c = SIMD.float32x4.max(a, b);
assertEq(c.x, 10);
assertEq(c.y, 20);
assertEq(c.z, 30);
assertEq(c.w, 40);
var d = float32x4(9.999, 2.1234, 30.4443, 4);
var e = float32x4(10, 2.1233, 30.4444, 4.0001);
var f = float32x4.max(d, e);
assertEq(f.x, 10);
assertEq(f.y, Math.fround(2.1234));
assertEq(f.z, Math.fround(30.4444));
assertEq(f.w, Math.fround(4.0001));
if (typeof reportCompare === "function")
reportCompare(true, true);
}
test();

View File

@ -1,35 +0,0 @@
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
var BUGNUMBER = 946042;
var float32x4 = SIMD.float32x4;
var int32x4 = SIMD.int32x4;
var summary = 'float32x4 min';
function test() {
print(BUGNUMBER + ": " + summary);
// FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
// FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
var a = float32x4(1, 20, 3, 40);
var b = float32x4(10, 2, 30, 4);
var c = SIMD.float32x4.min(a, b);
assertEq(c.x, 1);
assertEq(c.y, 2);
assertEq(c.z, 3);
assertEq(c.w, 4);
var d = float32x4(1.4321, 20.5567, 30.8999, 4.0002);
var e = float32x4(1.432, 20.5568, 30.8998, 4.0001);
var f = float32x4.min(d, e);
assertEq(f.x, Math.fround(1.432));
assertEq(f.y, Math.fround(20.5567));
assertEq(f.z, Math.fround(30.8998));
assertEq(f.w, Math.fround(4.0001));
if (typeof reportCompare === "function")
reportCompare(true, true);
}
test();

View File

@ -4,9 +4,6 @@ var int32x4 = SIMD.int32x4;
var summary = 'int32x4 select';
const INT32_MAX = Math.pow(2, 31) - 1;
const INT32_MIN = INT32_MAX + 1 | 0;
function test() {
print(BUGNUMBER + ": " + summary);

View File

@ -0,0 +1,154 @@
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
/*
* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/
*/
// Our float32array will have 16 elements
const SIZE_ARRAY = 16;
// 1 float32 == 4 bytes
const SIZE_BYTES = SIZE_ARRAY * 4;
function MakeComparator(kind, arr) {
var bpe = arr.BYTES_PER_ELEMENT;
var uint8 = (bpe != 1) ? new Uint8Array(arr.buffer) : arr;
// Size in bytes of a single element in the SIMD vector.
var sizeOfLaneElem;
// Typed array constructor corresponding to the SIMD kind.
var typedArrayCtor;
switch (kind) {
case 'int32x4':
sizeOfLaneElem = 4;
typedArrayCtor = Int32Array;
break;
case 'float32x4':
sizeOfLaneElem = 4;
typedArrayCtor = Float32Array;
break;
default:
assertEq(true, false, "unknown SIMD kind");
}
// Reads (numElemToRead * sizeOfLaneElem) bytes in arr, and reinterprets
// these bytes as a typed array equivalent to the typed SIMD vector.
var slice = function(start, numElemToRead) {
// Read enough bytes
var startBytes = start * bpe;
var endBytes = startBytes + numElemToRead * sizeOfLaneElem;
var asArray = Array.prototype.slice.call(uint8, startBytes, endBytes);
// If length is less than SIZE_BYTES bytes, fill with 0.
// This is needed for loadX, loadXY, loadXYZ which do only partial
// reads.
for (var i = asArray.length; i < SIZE_BYTES; i++) asArray[i] = 0;
assertEq(asArray.length, SIZE_BYTES);
return new typedArrayCtor(new Uint8Array(asArray).buffer);
}
return {
loadX: function(index) {
var v = SIMD[kind].loadX(arr, index);
assertEqX4(v, slice(index, 1));
},
loadXY: function(index) {
var v = SIMD[kind].loadXY(arr, index);
assertEqX4(v, slice(index, 2));
},
loadXYZ: function(index) {
var v = SIMD[kind].loadXYZ(arr, index);
assertEqX4(v, slice(index, 3));
},
load: function(index) {
var v = SIMD[kind].load(arr, index);
assertEqX4(v, slice(index, 4));
}
}
}
function testLoad(kind, TA) {
for (var i = SIZE_ARRAY; i--;)
TA[i] = i;
for (var ta of [
new Uint8Array(TA.buffer),
new Int8Array(TA.buffer),
new Uint16Array(TA.buffer),
new Int16Array(TA.buffer),
new Uint32Array(TA.buffer),
new Int32Array(TA.buffer),
new Float32Array(TA.buffer),
new Float64Array(TA.buffer)
])
{
// Invalid args
assertThrowsInstanceOf(() => SIMD[kind].load(), TypeError);
assertThrowsInstanceOf(() => SIMD[kind].load(ta), TypeError);
assertThrowsInstanceOf(() => SIMD[kind].load("hello", 0), TypeError);
assertThrowsInstanceOf(() => SIMD[kind].load(ta, -1), TypeError);
// Valid and invalid reads
var C = MakeComparator(kind, ta);
var bpe = ta.BYTES_PER_ELEMENT;
var lastValidArgLoadX = (SIZE_BYTES - 4) / bpe | 0;
var lastValidArgLoadXY = (SIZE_BYTES - 8) / bpe | 0;
var lastValidArgLoadXYZ = (SIZE_BYTES - 12) / bpe | 0;
var lastValidArgLoad = (SIZE_BYTES - 16) / bpe | 0;
C.load(0);
C.load(1);
C.load(2);
C.load(3);
C.load(lastValidArgLoad);
assertThrowsInstanceOf(() => SIMD[kind].load(ta, lastValidArgLoad + 1), TypeError);
C.loadX(0);
C.loadX(1);
C.loadX(2);
C.loadX(3);
C.loadX(lastValidArgLoadX);
assertThrowsInstanceOf(() => SIMD[kind].loadX(ta, lastValidArgLoadX + 1), TypeError);
C.loadXY(0);
C.loadXY(1);
C.loadXY(2);
C.loadXY(3);
C.loadXY(lastValidArgLoadXY);
assertThrowsInstanceOf(() => SIMD[kind].loadXY(ta, lastValidArgLoadXY + 1), TypeError);
C.loadXYZ(0);
C.loadXYZ(1);
C.loadXYZ(2);
C.loadXYZ(3);
C.loadXYZ(lastValidArgLoadXYZ);
assertThrowsInstanceOf(() => SIMD[kind].loadXYZ(ta, lastValidArgLoadXYZ + 1), TypeError);
}
// Test ToInt32 behavior
var v = SIMD[kind].load(TA, 12.5);
assertEqX4(v, [12, 13, 14, 15]);
var obj = {
valueOf: function() { return 12 }
}
var v = SIMD[kind].load(TA, obj);
assertEqX4(v, [12, 13, 14, 15]);
var obj = {
valueOf: function() { throw new TypeError("i ain't a number"); }
}
assertThrowsInstanceOf(() => SIMD[kind].load(TA, obj), TypeError);
}
testLoad('float32x4', new Float32Array(SIZE_ARRAY));
testLoad('int32x4', new Int32Array(SIZE_ARRAY));
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -0,0 +1,30 @@
function assertEqX4(v, arr) {
try {
assertEq(v.x, arr[0]);
assertEq(v.y, arr[1]);
assertEq(v.z, arr[2]);
assertEq(v.w, arr[3]);
} catch (e) {
print("stack trace:", e.stack);
throw e;
}
}
function simdToArray(v) {
return [v.x, v.y, v.z, v.w];
}
const INT32_MAX = Math.pow(2, 31) - 1;
const INT32_MIN = -Math.pow(2, 31);
assertEq(INT32_MAX + 1 | 0, INT32_MIN);
function testBinaryFunc(v, w, simdFunc, func) {
var varr = simdToArray(v);
var warr = simdToArray(w);
var observed = simdToArray(simdFunc(v, w));
var expected = varr.map(function(v, i) { return func(varr[i], warr[i]); });
for (var i = 0; i < observed.length; i++)
assertEq(observed[i], expected[i]);
}

View File

@ -0,0 +1,85 @@
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
/*
* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/
*/
// As SIMD.*.store is entirely symmetric to SIMD.*.load, this file just
// contains basic tests to store on one single TypedArray kind, while load is
// exhaustively tested. See load.js for more details.
const POISON = 42;
function reset(ta) {
for (var i = 0; i < ta.length; i++)
ta[i] = POISON + i;
}
function assertChanged(ta, from, expected) {
var i = 0;
for (; i < from; i++)
assertEq(ta[i], POISON + i);
for (; i < from + expected.length; i++)
assertEq(ta[i], expected[i - from]);
for (; i < ta.length; i++)
assertEq(ta[i], POISON + i);
}
function testStore(ta, kind, i, v) {
reset(ta);
SIMD[kind].storeX(ta, i, v);
assertChanged(ta, i, [v.x]);
reset(ta);
SIMD[kind].storeXY(ta, i, v);
assertChanged(ta, i, [v.x, v.y]);
reset(ta);
SIMD[kind].storeXYZ(ta, i, v);
assertChanged(ta, i, [v.x, v.y, v.z]);
reset(ta);
SIMD[kind].store(ta, i, v);
assertChanged(ta, i, [v.x, v.y, v.z, v.w]);
}
function testStoreInt32x4() {
var I32 = new Int32Array(16);
var v = SIMD.int32x4(0, 1, Math.pow(2,31) - 1, -Math.pow(2, 31));
testStore(I32, 'int32x4', 0, v);
testStore(I32, 'int32x4', 1, v);
testStore(I32, 'int32x4', 2, v);
testStore(I32, 'int32x4', 12, v);
assertThrowsInstanceOf(() => SIMD.int32x4.store(I32), TypeError);
assertThrowsInstanceOf(() => SIMD.int32x4.store(I32, 0), TypeError);
assertThrowsInstanceOf(() => SIMD.float32x4.store(I32, 0, v), TypeError);
}
function testStoreFloat32x4() {
var F32 = new Float32Array(16);
var v = SIMD.float32x4(1,2,3,4);
testStore(F32, 'float32x4', 0, v);
testStore(F32, 'float32x4', 1, v);
testStore(F32, 'float32x4', 2, v);
testStore(F32, 'float32x4', 12, v);
var v = SIMD.float32x4(NaN, -0, -Infinity, 5e-324);
testStore(F32, 'float32x4', 0, v);
testStore(F32, 'float32x4', 1, v);
testStore(F32, 'float32x4', 2, v);
testStore(F32, 'float32x4', 12, v);
assertThrowsInstanceOf(() => SIMD.float32x4.store(F32), TypeError);
assertThrowsInstanceOf(() => SIMD.float32x4.store(F32, 0), TypeError);
assertThrowsInstanceOf(() => SIMD.int32x4.store(F32, 0, v), TypeError);
}
testStoreInt32x4();
testStoreFloat32x4();
if (typeof reportCompare === "function")
reportCompare(true, true);

Some files were not shown because too many files have changed in this diff Show More