import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1248863 - IonMonkey: MIPS: Fix MDefinition::constantValue re-factored. r=luke (c38ce4f8dd)
- Bug 1231024 - narrow the live range for values. r=jandem (bfe06e964d)
- Bug 1248007 part 1 - Refactor useBox and friends to work more like useRegister. r=nbp (8585828647)
- Bug 1248007 part 2 - Remove unused This operands from LCallDirectEval. r=nbp (ca16fc594e)
- Bug 1248598 part 3 - Enable i64 on x64 and various related changes. r=sunfish (75311df85c)
- Bug 1248863 - IonMonkey: MIPS32: Fix LIRGeneratorMIPS::visitBox. r=arai (2d6f64ed18)
- Bug 1244889 - Handle all SIMD types in js::SimdTypeToName. r=bbouvier (7e8952b52d)
- Bug 1244889 - Add support for Uint32x4 as an asm.js type. r=luke (cab8e0e725)
- Bug 1244889 - Fix Float32x4toUint32x4 for asm.js. r=bbouvier (3b34875729)
- Bug 1244889 - Disallow unsigned SIMD types for global variables. r=luke (cde605325d)
- Bug 1201934 - Remove SIMD shiftRight***ByScalar. r=sunfish (db2a308c6f)
- Bug 1246800 - Masked shift-by-scalar amounts. r=sunfish (58f335a1cf)
- Bug 1226017 - Drop support for b2g desktop in reftests, r=jgriffin (b71bfbea26)
- Bug 1231261 - Append plugins to extra profile files instead of overwriting. r=dbaron (cce158e2bf)
- Bug 1193223 - Add reftest support to mach test, r=chmanchester (44e12e1622)
- Bug 1087791 - Add |mach {reftest,crashtest,jstestbrowser}| for mobile/android. r=nalexander (711a6eb376)
- Bug 1087791 - Follow-up on 340c1df41b69 (pushed wrong version of patch); r=nalexander (d4e7b53834)
- Bug 1228636 - Add mach support for running reftests on mulet, r=jgriffin (25fa23feb4)
- Bug 1232792 - Convert JS callsites to use open2 within layout/ (r=sicking) (8ee4616658)
- Bug 1196831 - Add 'run-until-failure' and 'repeat' flags to reftest. r=jmaher (7792c9fa22)
- Bug 1215148 - Object-count based leak checking for Mochitest. r=jgriffin (afe3a307b2)
- Bug 1219371 - Add suppression for Aurora-only Windows leak. r=erahm (2e74e92da2)
- Bug 1218393 - Give a summary for object-count leak checking. r=jgriffin (b1bd4844e9)
- Bug 1219919 - Add suppressions for Windows-specific content process graphics leaks. r=erahm (a651f412b3)
- Bug 1140394 - Protect standard output from interleaving. r=ahal (0ef5d8f71f)
- Bug 1034290 - Use structured log output for test results in reftest, r=jmaher (3e93bd862b)
- Bug 1249787 - OdinMonkey: Add offset and align fields to the encoding of load and store. r=luke (822e9b01f9)
- Bug 1248203 - streamline h2 stream flow control buffer r=hurley (739a39a255)
- Bug 1221320 - XMLHttpRequest authentication should not require auth prompt dialog, r=honzab.moz (12d9bcc6d0)
- Bug 1158543 - Remove SpdyConnectTransaction::mRequestHead and make the base class mRequestHead protected; r=mcmanus (9678015004)
- Bug 844948 - Allow changing padding of themed button on OS X. r=mstange,heycam (56cbcffbfe)
- Bug 1248983 - Fix spelling for nsCocoaWindow.mm. r=jdm (e185ad5889)
- Bug 917505 - Add WEBGL_compressed_texture_es3 support. r=jgilbert r=smaug (bf4ef11229)
- Bug 1243072 - Make GfxTexturesReporter work again r=nical,jgilbert (6942d70ecf)
This commit is contained in:
2023-09-25 10:10:09 +08:00
parent e753cbebb0
commit 78ceb79db1
126 changed files with 8131 additions and 1461 deletions
+10 -4
View File
@@ -396,7 +396,7 @@ class Automation(object):
self.log.info("Can't trigger Breakpad, just killing process")
self.killPid(processPID)
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath):
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, outputHandler=None):
""" Look for timeout or crashes and return the status after the process terminates """
stackFixerFunction = None
didTimeout = False
@@ -437,7 +437,12 @@ class Automation(object):
while line != "" and not didTimeout:
if stackFixerFunction:
line = stackFixerFunction(line)
self.log.info(line.rstrip().decode("UTF-8", "ignore"))
if outputHandler is None:
self.log.info(line.rstrip().decode("UTF-8", "ignore"))
else:
outputHandler(line)
if "TEST-START" in line and "|" in line:
self.lastTestSeen = line.split("|")[1].strip()
if not debuggerInfo and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line:
@@ -531,7 +536,7 @@ class Automation(object):
debuggerInfo = None, symbolsPath = None,
timeout = -1, maxTime = None, onLaunch = None,
detectShutdownLeaks = False, screenshotOnFail=False, testPath=None, bisectChunk=None,
valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None):
valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None, outputHandler=None):
"""
Run the app, log the duration it took to execute, return the status code.
Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
@@ -580,7 +585,8 @@ class Automation(object):
# app is launched.
onLaunch()
status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath)
status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath,
outputHandler=outputHandler)
self.log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime))
# Do a final check for zombie child processes.
+19 -10
View File
@@ -205,23 +205,31 @@ class B2GRemoteAutomation(Automation):
return app, args
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime,
debuggerInfo, symbolsPath):
debuggerInfo, symbolsPath, outputHandler=None):
""" Wait for tests to finish (as evidenced by a signature string
in logcat), or for a given amount of time to elapse with no
output.
"""
timeout = timeout or 120
while True:
currentlog = proc.getStdoutLines(timeout)
if currentlog:
print currentlog
lines = proc.getStdoutLines(timeout)
if lines:
currentlog = '\n'.join(lines)
if outputHandler:
for line in lines:
outputHandler(line)
else:
print(currentlog)
# Match the test filepath from the last TEST-START line found in the new
# log content. These lines are in the form:
# ... INFO TEST-START | /filepath/we/wish/to/capture.html\n
testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", currentlog)
if testStartFilenames:
self.lastTestSeen = testStartFilenames[-1]
if hasattr(self, 'logFinish') and self.logFinish in currentlog:
if (outputHandler and outputHandler.suite_finished) or (
hasattr(self, 'logFinish') and self.logFinish in currentlog):
return 0
else:
self.log.info("TEST-UNEXPECTED-FAIL | %s | application timed "
@@ -431,11 +439,12 @@ class B2GRemoteAutomation(Automation):
break
# wait 'timeout' for any additional lines
try:
lines.append(self.queue.get(True, timeout))
except Queue.Empty:
pass
return '\n'.join(lines)
if not lines:
try:
lines.append(self.queue.get(True, timeout))
except Queue.Empty:
pass
return lines
def wait(self, timeout=None):
# this should never happen
+20 -18
View File
@@ -80,7 +80,7 @@ class RemoteAutomation(Automation):
return env
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath):
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, outputHandler=None):
""" Wait for tests to finish.
If maxTime seconds elapse or no output is detected for timeout
seconds, kill the process and fail the test.
@@ -264,20 +264,21 @@ class RemoteAutomation(Automation):
return pid
def read_stdout(self):
""" Fetch the full remote log file using devicemanager and return just
the new log entries since the last call (as a list of messages or lines).
"""
Fetch the full remote log file using devicemanager, process them and
return whether there were any new log entries since the last call.
"""
if not self.dm.fileExists(self.proc):
return []
return False
try:
newLogContent = self.dm.pullFile(self.proc, self.stdoutlen)
except DMError:
# we currently don't retry properly in the pullFile
# function in dmSUT, so an error here is not necessarily
# the end of the world
return []
return False
if not newLogContent:
return []
return False
self.stdoutlen += len(newLogContent)
@@ -286,26 +287,27 @@ class RemoteAutomation(Automation):
if testStartFilenames:
self.lastTestSeen = testStartFilenames[-1]
print newLogContent
return [newLogContent]
return True
self.logBuffer += newLogContent
lines = self.logBuffer.split('\n')
if not lines:
return
# We only keep the last (unfinished) line in the buffer
self.logBuffer = lines[-1]
del lines[-1]
messages = []
if lines:
# We only keep the last (unfinished) line in the buffer
self.logBuffer = lines[-1]
del lines[-1]
if not lines:
return False
for line in lines:
# This passes the line to the logger (to be logged or buffered)
# and returns a list of structured messages (dict)
parsed_messages = self.messageLogger.write(line)
for message in parsed_messages:
if message['action'] == 'test_start':
if isinstance(message, dict) and message.get('action') == 'test_start':
self.lastTestSeen = message['test']
messages += parsed_messages
return messages
return True
@property
def getLastTestSeen(self):
@@ -331,10 +333,10 @@ class RemoteAutomation(Automation):
# too long, only do it every 60 seconds
if (not slowLog) or (timer % 60 == 0):
startRead = datetime.datetime.now()
messages = self.read_stdout()
hasOutput = self.read_stdout()
if (datetime.datetime.now() - startRead) > datetime.timedelta(seconds=5):
slowLog = True
if messages:
if hasOutput:
noOutputTimer = 0
time.sleep(interval)
timer += interval
+43 -119
View File
@@ -179,72 +179,6 @@ static void AddLoadFlags(nsIRequest *request, nsLoadFlags newFlags)
request->SetLoadFlags(flags);
}
//-----------------------------------------------------------------------------
// XMLHttpRequestAuthPrompt
//-----------------------------------------------------------------------------
class XMLHttpRequestAuthPrompt : public nsIAuthPrompt
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIAUTHPROMPT
XMLHttpRequestAuthPrompt();
protected:
virtual ~XMLHttpRequestAuthPrompt();
};
NS_IMPL_ISUPPORTS(XMLHttpRequestAuthPrompt, nsIAuthPrompt)
XMLHttpRequestAuthPrompt::XMLHttpRequestAuthPrompt()
{
MOZ_COUNT_CTOR(XMLHttpRequestAuthPrompt);
}
XMLHttpRequestAuthPrompt::~XMLHttpRequestAuthPrompt()
{
MOZ_COUNT_DTOR(XMLHttpRequestAuthPrompt);
}
NS_IMETHODIMP
XMLHttpRequestAuthPrompt::Prompt(const char16_t* aDialogTitle,
const char16_t* aText,
const char16_t* aPasswordRealm,
uint32_t aSavePassword,
const char16_t* aDefaultText,
char16_t** aResult,
bool* aRetval)
{
*aRetval = false;
return NS_OK;
}
NS_IMETHODIMP
XMLHttpRequestAuthPrompt::PromptUsernameAndPassword(const char16_t* aDialogTitle,
const char16_t* aDialogText,
const char16_t* aPasswordRealm,
uint32_t aSavePassword,
char16_t** aUser,
char16_t** aPwd,
bool* aRetval)
{
*aRetval = false;
return NS_OK;
}
NS_IMETHODIMP
XMLHttpRequestAuthPrompt::PromptPassword(const char16_t* aDialogTitle,
const char16_t* aText,
const char16_t* aPasswordRealm,
uint32_t aSavePassword,
char16_t** aPwd,
bool* aRetval)
{
*aRetval = false;
return NS_OK;
}
/////////////////////////////////////////////
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREventTarget)
@@ -2930,6 +2864,10 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
mChannel->SetNotificationCallbacks(this);
if (internalHttpChannel) {
internalHttpChannel->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
}
// Start reading from the channel
// Because of bug 682305, we can't let listener be the XHR object itself
// because JS wouldn't be able to use it. So create a listener around 'this'.
@@ -3543,59 +3481,6 @@ nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
}
else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
nsCOMPtr<nsIURI> uri;
rv = mChannel->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
// Verify that it's ok to prompt for credentials here, per spec
// http://xhr.spec.whatwg.org/#the-send%28%29-method
bool showPrompt = true;
// If authentication fails, XMLHttpRequest origin and
// the request URL are same origin, ...
/* Disabled - bug: 799540
if (IsCrossSiteCORSRequest()) {
showPrompt = false;
}
*/
// ... Authorization is not in the list of author request headers, ...
if (showPrompt) {
for (uint32_t i = 0, len = mModifiedRequestHeaders.Length(); i < len; ++i) {
if (mModifiedRequestHeaders[i].header.
LowerCaseEqualsLiteral("authorization")) {
showPrompt = false;
break;
}
}
}
// ... request username is null, and request password is null,
if (showPrompt) {
nsCString username;
rv = uri->GetUsername(username);
NS_ENSURE_SUCCESS(rv, rv);
nsCString password;
rv = uri->GetPassword(password);
NS_ENSURE_SUCCESS(rv, rv);
if (!username.IsEmpty() || !password.IsEmpty()) {
showPrompt = false;
}
}
// ... user agents should prompt the end user for their username and password.
if (!showPrompt) {
RefPtr<XMLHttpRequestAuthPrompt> prompt = new XMLHttpRequestAuthPrompt();
if (!prompt)
return NS_ERROR_OUT_OF_MEMORY;
return prompt->QueryInterface(aIID, aResult);
}
nsCOMPtr<nsIPromptFactory> wwatch =
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
@@ -3743,6 +3628,45 @@ nsXMLHttpRequest::EnsureXPCOMifier()
return newRef.forget();
}
bool
nsXMLHttpRequest::ShouldBlockAuthPrompt()
{
// Verify that it's ok to prompt for credentials here, per spec
// http://xhr.spec.whatwg.org/#the-send%28%29-method
for (uint32_t i = 0, len = mModifiedRequestHeaders.Length(); i < len; ++i) {
if (mModifiedRequestHeaders[i].header.
LowerCaseEqualsLiteral("authorization")) {
return true;
}
}
nsCOMPtr<nsIURI> uri;
nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
// Also skip if a username and/or password is provided in the URI.
nsCString username;
rv = uri->GetUsername(username);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
nsCString password;
rv = uri->GetPassword(password);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
if (!username.IsEmpty() || !password.IsEmpty()) {
return true;
}
return false;
}
NS_IMPL_ISUPPORTS(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor)
NS_IMETHODIMP nsXMLHttpRequest::
+2
View File
@@ -799,6 +799,8 @@ protected:
void ResetResponse();
bool ShouldBlockAuthPrompt();
struct RequestHeader
{
nsCString header;
-1
View File
@@ -400,7 +400,6 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(clipboard unde
[test_bug276037-1.html]
[test_bug276037-2.xhtml]
[test_bug282547.html]
skip-if = e10s
[test_bug28293.html]
[test_bug28293.xhtml]
[test_bug298064.html]
+5
View File
@@ -1405,6 +1405,11 @@ DOMInterfaces = {
'headerFile': 'WebGLExtensions.h'
},
'WEBGL_compressed_texture_es3': {
'nativeType': 'mozilla::WebGLExtensionCompressedTextureES3',
'headerFile': 'WebGLExtensions.h'
},
'WEBGL_compressed_texture_pvrtc': {
'nativeType': 'mozilla::WebGLExtensionCompressedTexturePVRTC',
'headerFile': 'WebGLExtensions.h'
+1
View File
@@ -189,6 +189,7 @@ class WebGLContext
friend class WebGL2Context;
friend class WebGLContextUserData;
friend class WebGLExtensionCompressedTextureATC;
friend class WebGLExtensionCompressedTextureES3;
friend class WebGLExtensionCompressedTextureETC1;
friend class WebGLExtensionCompressedTexturePVRTC;
friend class WebGLExtensionCompressedTextureS3TC;
+6
View File
@@ -47,6 +47,7 @@ WebGLContext::GetExtensionString(WebGLExtensionID ext)
WEBGL_EXTENSION_IDENTIFIER(OES_vertex_array_object)
WEBGL_EXTENSION_IDENTIFIER(WEBGL_color_buffer_float)
WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_atc)
WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_es3)
WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_etc1)
WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_pvrtc)
WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_s3tc)
@@ -199,6 +200,8 @@ WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
switch (ext) {
case WebGLExtensionID::EXT_disjoint_timer_query:
return WebGLExtensionDisjointTimerQuery::IsSupported(this);
case WebGLExtensionID::WEBGL_compressed_texture_es3:
return gl->IsExtensionSupported(gl::GLContext::ARB_ES3_compatibility);
default:
// For warnings-as-errors.
@@ -377,6 +380,9 @@ WebGLContext::EnableExtension(WebGLExtensionID ext)
case WebGLExtensionID::WEBGL_compressed_texture_atc:
obj = new WebGLExtensionCompressedTextureATC(this);
break;
case WebGLExtensionID::WEBGL_compressed_texture_es3:
obj = new WebGLExtensionCompressedTextureES3(this);
break;
case WebGLExtensionID::WEBGL_compressed_texture_etc1:
obj = new WebGLExtensionCompressedTextureETC1(this);
break;
@@ -0,0 +1,55 @@
/* 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 "WebGLExtensions.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "WebGLContext.h"
#ifdef FOO
#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
#endif
namespace mozilla {
WebGLExtensionCompressedTextureES3::WebGLExtensionCompressedTextureES3(WebGLContext* webgl)
: WebGLExtensionBase(webgl)
{
RefPtr<WebGLContext> webgl_ = webgl; // Bug 1201275
const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
auto& fua = webgl_->mFormatUsage;
auto usage = fua->EditUsage(effFormat);
usage->isFilterable = true;
fua->AllowSizedTexFormat(sizedFormat, usage);
webgl_->mCompressedTextureFormats.AppendElement(sizedFormat);
};
#define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x
fnAdd(FOO(COMPRESSED_R11_EAC));
fnAdd(FOO(COMPRESSED_SIGNED_R11_EAC));
fnAdd(FOO(COMPRESSED_RG11_EAC));
fnAdd(FOO(COMPRESSED_SIGNED_RG11_EAC));
fnAdd(FOO(COMPRESSED_RGB8_ETC2));
fnAdd(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2));
fnAdd(FOO(COMPRESSED_RGBA8_ETC2_EAC));
// sRGB support is manadatory in GL 4.3 and GL ES 3.0, which are the only
// versions to support ETC2.
fnAdd(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC));
fnAdd(FOO(COMPRESSED_SRGB8_ETC2));
fnAdd(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2));
#undef FOO
}
WebGLExtensionCompressedTextureES3::~WebGLExtensionCompressedTextureES3()
{
}
IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionCompressedTextureES3, WEBGL_compressed_texture_es3)
} // namespace mozilla
+10
View File
@@ -71,6 +71,16 @@ public:
DECL_WEBGL_EXTENSION_GOOP
};
class WebGLExtensionCompressedTextureES3
: public WebGLExtensionBase
{
public:
explicit WebGLExtensionCompressedTextureES3(WebGLContext*);
virtual ~WebGLExtensionCompressedTextureES3();
DECL_WEBGL_EXTENSION_GOOP
};
class WebGLExtensionCompressedTextureETC1
: public WebGLExtensionBase
{
+1
View File
@@ -8,6 +8,7 @@
#include <map>
#include <set>
#include <string>
#include <vector>
#include "mozilla/LinkedList.h"
+1
View File
@@ -149,6 +149,7 @@ enum class WebGLExtensionID : uint8_t {
OES_vertex_array_object,
WEBGL_color_buffer_float,
WEBGL_compressed_texture_atc,
WEBGL_compressed_texture_es3,
WEBGL_compressed_texture_etc1,
WEBGL_compressed_texture_pvrtc,
WEBGL_compressed_texture_s3tc,
+1
View File
@@ -101,6 +101,7 @@ UNIFIED_SOURCES += [
'WebGLExtensionColorBufferFloat.cpp',
'WebGLExtensionColorBufferHalfFloat.cpp',
'WebGLExtensionCompressedTextureATC.cpp',
'WebGLExtensionCompressedTextureES3.cpp',
'WebGLExtensionCompressedTextureETC1.cpp',
'WebGLExtensionCompressedTexturePVRTC.cpp',
'WebGLExtensionCompressedTextureS3TC.cpp',
+2
View File
@@ -4,6 +4,7 @@ skip-if = ((os == 'linux') && (buildapp == 'b2g'))
support-files =
webgl-mochitest/driver-info.js
webgl-mochitest/es3-data.js
webgl-mochitest/webgl-util.js
[webgl-mochitest/test_backbuffer_channels.html]
@@ -33,6 +34,7 @@ skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests
# We haven't cleaned up the Try results yet, but let's get this on the books first.
[webgl-mochitest/test_webgl_conformance.html]
skip-if = buildapp == 'mulet' || toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
[webgl-mochitest/test_webgl_compressed_texture_es3.html]
[webgl-mochitest/test_webgl_disjoint_timer_query.html]
[webgl-mochitest/test_webgl_request_context.html]
skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,753 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script src="webgl-util.js"></script>
<script src="es3-data.js"></script>
<title>WebGL test: test WEBGL_compressed_texture_es3 extension</title>
<style>
img {
border: 1px solid black;
margin-right: 1em;
}
.testimages {
}
.testimages br {
clear: both;
}
.testimages > div {
float: left;
margin: 1em;
}
</style>
</head>
<body>
<div id="description"></div>
<canvas id="canvas" width="8" height="8"></canvas>
<div id="console"></div>
<script id="vshader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec2 texCoord0;
varying vec2 texCoord;
void main() {
gl_Position = vPosition;
texCoord = texCoord0;
}
</script>
<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D tex;
varying vec2 texCoord;
void main() {
gl_FragData[0] = texture2D(tex, texCoord);
}
</script>
<script id="fshader-r" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D tex;
varying vec2 texCoord;
void main() {
vec4 pixel = (texture2D(tex, texCoord));
pixel.r = (pixel.r + 1.0) / 2.0;
gl_FragData[0] = pixel;
}
</script>
<script id="fshader-rg" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D tex;
varying vec2 texCoord;
void main() {
vec4 pixel = (texture2D(tex, texCoord));
pixel.rg = (pixel.rg + 1.0) / 2.0;
gl_FragData[0] = pixel;
}
</script>
<script>
"use strict";
var ext = null;
var vao = null;
var gl = null;
var validFormats = {
COMPRESSED_R11_EAC : 0x9270,
COMPRESSED_SIGNED_R11_EAC : 0x9271,
COMPRESSED_RG11_EAC : 0x9272,
COMPRESSED_SIGNED_RG11_EAC : 0x9273,
COMPRESSED_RGB8_ETC2 : 0x9274,
COMPRESSED_SRGB8_ETC2 : 0x9275,
COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 : 0x9276,
COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 : 0x9277,
COMPRESSED_RGBA8_ETC2_EAC : 0x9278,
COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : 0x9279,
};
var name;
var supportedFormats;
function setupUnitQuad() {
var vertexObject = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
var vertexObject = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(1);
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0);
}
function runTest() {
gl = WebGLUtil.getWebGL("canvas", false, {antialias: false});
if (!gl) {
ok(false, "WebGL context does not exist");
} else {
ok(true, "WebGL context exists");
setupUnitQuad();
// Run tests with extension disabled
runTestDisabled();
// Query the extension and store globally so shouldBe can access it
ext = gl.getExtension("WEBGL_compressed_texture_es3");
if (!ext) {
ok(true, "No WEBGL_compressed_texture_es3 support -- this is legal");
runSupportedTest(false);
} else {
ok(true, "Successfully enabled WEBGL_compressed_texture_es3 extension");
runSupportedTest(true);
runTestExtension();
}
}
SimpleTest.finish();
}
function runSupportedTest(extensionEnabled) {
var supported = gl.getSupportedExtensions();
if (supported.indexOf("WEBGL_compressed_texture_es3") >= 0) {
if (extensionEnabled) {
ok(true, "WEBGL_compressed_texture_es3 listed as supported and getExtension succeeded");
} else {
ok(false, "WEBGL_compressed_texture_es3 listed as supported but getExtension failed");
}
} else {
if (extensionEnabled) {
ok(false, "WEBGL_compressed_texture_es3 not listed as supported but getExtension succeeded");
} else {
ok(true, "WEBGL_compressed_texture_es3 not listed as supported and getExtension failed -- this is legal");
}
}
}
function runTestDisabled() {
is(gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS).length, 0,
"Should be no compressed texture formats");
}
function formatExists(format, supportedFormats) {
for (var ii = 0; ii < supportedFormats.length; ++ii) {
if (format == supportedFormats[ii]) {
ok(true, "supported format " + formatToString(format) + " is exists");
return;
}
}
ok(false, "supported format " + formatToString(format) + " does not exist");
}
function formatToString(format) {
for (var p in ext) {
if (ext[p] == format) {
return p;
}
}
return "0x" + format.toString(16);
}
function runTestExtension() {
// check that all format enums exist.
for (name in validFormats) {
is(ext[name], validFormats[name], "format is match");
}
supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
// There should be exactly 10 formats
is(supportedFormats.length, 10, "Should be exactly 10 formats");
// check that all 10 formats exist
for (var name in validFormats.length) {
formatExists(validFormats[name], supportedFormats);
}
// Test each format
testETC2_RGB();
}
function testETC2_RGB() {
var tests = [
{
width: 4,
height: 4,
channels: 1,
data: img_4x4_r11_eac,
format: ext.COMPRESSED_R11_EAC
},
{
width: 4,
height: 4,
channels: 1,
data: img_4x4_signed_r11_eac,
format: ext.COMPRESSED_SIGNED_R11_EAC
},
{
width: 4,
height: 4,
channels: 2,
data: img_4x4_rg11_eac,
format: ext.COMPRESSED_RG11_EAC
},
{
width: 4,
height: 4,
channels: 2,
data: img_4x4_signed_rg11_eac,
format: ext.COMPRESSED_SIGNED_RG11_EAC
},
{
width: 4,
height: 4,
channels: 3,
data: img_4x4_rgb_etc2,
format: ext.COMPRESSED_RGB8_ETC2
},
{
width: 4,
height: 4,
channels: 3,
data: img_4x4_rgb_etc2,
format: ext.COMPRESSED_SRGB8_ETC2
},
{
width: 4,
height: 4,
channels: 4,
data: img_4x4_rgb_punchthrough_etc2,
format: ext.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
},
{
width: 4,
height: 4,
channels: 4,
data: img_4x4_rgb_punchthrough_etc2,
format: ext.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2
},
{
width: 4,
height: 4,
channels: 4,
data: img_4x4_rgba_etc2,
format: ext.COMPRESSED_RGBA8_ETC2_EAC
},
{
width: 4,
height: 4,
channels: 4,
data: img_4x4_rgba_etc2,
format: ext.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC
},
{
width: 8,
height: 8,
channels: 1,
data: img_8x8_r11_eac,
format: ext.COMPRESSED_R11_EAC
},
{
width: 8,
height: 8,
channels: 1,
data: img_8x8_signed_r11_eac,
format: ext.COMPRESSED_SIGNED_R11_EAC
},
{
width: 8,
height: 8,
channels: 2,
data: img_8x8_rg11_eac,
format: ext.COMPRESSED_RG11_EAC
},
{
width: 8,
height: 8,
channels: 2,
data: img_8x8_signed_rg11_eac,
format: ext.COMPRESSED_SIGNED_RG11_EAC
},
{
width: 8,
height: 8,
channels: 3,
data: img_8x8_rgb_etc2,
format: ext.COMPRESSED_RGB8_ETC2
},
{
width: 8,
height: 8,
channels: 3,
data: img_8x8_rgb_etc2,
format: ext.COMPRESSED_SRGB8_ETC2
},
{
width: 8,
height: 8,
channels: 4,
data: img_8x8_rgb_punchthrough_etc2,
format: ext.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
},
{
width: 8,
height: 8,
channels: 4,
data: img_8x8_rgb_punchthrough_etc2,
format: ext.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2
},
{
width: 8,
height: 8,
channels: 4,
data: img_8x8_rgba_etc2,
format: ext.COMPRESSED_RGBA8_ETC2_EAC
},
{
width: 8,
height: 8,
channels: 4,
data: img_8x8_rgba_etc2,
format: ext.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC
},
{
width: 32,
height: 32,
channels: 1,
data: img_32x32_r11_eac,
format: ext.COMPRESSED_R11_EAC
},
{
width: 32,
height: 32,
channels: 1,
data: img_32x32_signed_r11_eac,
format: ext.COMPRESSED_SIGNED_R11_EAC
},
{
width: 32,
height: 32,
channels: 2,
data: img_32x32_rg11_eac,
format: ext.COMPRESSED_RG11_EAC
},
{
width: 32,
height: 32,
channels: 2,
data: img_32x32_signed_rg11_eac,
format: ext.COMPRESSED_SIGNED_RG11_EAC
},
{
width: 32,
height: 32,
channels: 3,
data: img_32x32_rgb_etc2,
format: ext.COMPRESSED_RGB8_ETC2
},
{
width: 32,
height: 32,
channels: 3,
data: img_32x32_rgb_etc2,
format: ext.COMPRESSED_SRGB8_ETC2
},
{
width: 32,
height: 32,
channels: 4,
data: img_32x32_rgb_punchthrough_etc2,
format: ext.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
},
{
width: 32,
height: 32,
channels: 4,
data: img_32x32_rgb_punchthrough_etc2,
format: ext.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2
},
{
width: 32,
height: 32,
channels: 4,
data: img_32x32_rgba_etc2,
format: ext.COMPRESSED_RGBA8_ETC2_EAC
},
{
width: 32,
height: 32,
channels: 4,
data: img_32x32_rgba_etc2,
format: ext.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC
},
];
testETCTextures(tests);
}
function testETCTextures(tests) {
for (var ii = 0; ii < tests.length; ++ii) {
testETCTexture(tests[ii]);
}
}
/* Return the size of block in bytes */
function getBlockSize(format) {
switch (format) {
case ext.COMPRESSED_R11_EAC:
case ext.COMPRESSED_SIGNED_R11_EAC:
case ext.COMPRESSED_RGB8_ETC2:
case ext.COMPRESSED_SRGB8_ETC2:
case ext.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case ext.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
return 8;
case ext.COMPRESSED_RG11_EAC:
case ext.COMPRESSED_SIGNED_RG11_EAC:
case ext.COMPRESSED_RGBA8_ETC2_EAC:
case ext.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
return 16
}
}
function copyRect(data, srcX, srcY, dstX, dstY, width, height, stride) {
var bytesPerLine = width * 4;
var srcOffset = srcX * 4 + srcY * stride;
var dstOffset = dstX * 4 + dstY * stride;
for (var jj = height; jj > 0; --jj) {
for (var ii = 0; ii < bytesPerLine; ++ii) {
data[dstOffset + ii] = data[srcOffset + ii];
}
srcOffset += stride;
dstOffset += stride;
}
}
function testETCTexture(test) {
var data = new Uint8Array(test.data.compressed);
var width = test.width;
var height = test.height;
var format = test.format;
var uncompressedData = new Uint8Array(test.data.decompressed);
var glErrorShouldBe = (gl, glError, msg) => {
msg = msg || "";
var err = gl.getError();
var getGLErrorAsString = err => {
if (err === gl.NO_ERROR) {
return "NO_ERROR";
}
for (var name in gl) {
if (gl[name] === err) {
return name;
}
}
return err.toString();
}
if (err != glError) {
ok(false, "getError expected: " + getGLErrorAsString(glError) +
". Was " + getGLErrorAsString(err) + " : " + msg);
} else {
ok(true, "getError was expected value: " +
getGLErrorAsString(glError) + " : " + msg);
}
};
canvas.width = width;
canvas.height = height;
gl.viewport(0, 0, width, height);
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
gl.generateMipmap(gl.TEXTURE_2D);
glErrorShouldBe(gl, gl.INVALID_OPERATION, "trying to generate mipmaps from compressed texture");
if (format == ext.COMPRESSED_SIGNED_R11_EAC) {
var program = WebGLUtil.createProgramByIds(gl, 'vshader', 'fshader-r');
} else if (format == ext.COMPRESSED_SIGNED_RG11_EAC) {
var program = WebGLUtil.createProgramByIds(gl, 'vshader', 'fshader-rg');
} else {
var program = WebGLUtil.createProgramByIds(gl, 'vshader', 'fshader');
}
gl.bindAttribLocation(program, 0, 'vPosition');
gl.bindAttribLocation(program, 1, 'texCoord0');
gl.useProgram(program);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6);
compareRect(width, height, test.channels, width, height, uncompressedData, data, format);
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 1, data);
glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width + 4, height, 0, data);
glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height + 4, 0, data);
glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 4, height, 0, data);
glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 4, 0, data);
glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported");
if (width == 4) {
gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 1, height, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 2, height, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
}
if (height == 4) {
gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 1, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 2, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
}
// pick a wrong format that uses the same amount of data.
var wrongFormat;
switch (format) {
case ext.COMPRESSED_R11_EAC:
wrongFormat = ext.COMPRESSED_SIGNED_R11_EAC;
break;
case ext.COMPRESSED_SIGNED_R11_EAC:
wrongFormat = ext.COMPRESSED_R11_EAC;
break;
case ext.COMPRESSED_RG11_EAC:
wrongFormat = ext.COMPRESSED_SIGNED_RG11_EAC;
break;
case ext.COMPRESSED_SIGNED_RG11_EAC:
wrongFormat = ext.COMPRESSED_RG11_EAC;
break;
case ext.COMPRESSED_RGB8_ETC2:
wrongFormat = ext.COMPRESSED_SRGB8_ETC2;
break;
case ext.COMPRESSED_SRGB8_ETC2:
wrongFormat = ext.COMPRESSED_RGB8_ETC2;
break;
case ext.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
wrongFormat = ext.COMPRESSED_RGB8_ETC2;
break;
case ext.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
wrongFormat = ext.COMPRESSED_RGB8_ETC2;
break;
case ext.COMPRESSED_RGBA8_ETC2_EAC:
wrongFormat = ext.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
break;
case ext.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
wrongFormat = ext.COMPRESSED_RGBA8_ETC2_EAC;
break;
}
// Restore original texture.
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, wrongFormat, data);
glErrorShouldBe(gl, gl.INVALID_OPERATION, "format does not match");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width + 4, height, format, data);
glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height + 4, format, data);
glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 4, height, format, data);
glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 4, format, data);
glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data);
glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 1, height, format, data);
glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 2, height, format, data);
glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 1, format, data);
glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 2, format, data);
glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
var subData = new Uint8Array(data.buffer, 0, getBlockSize(format));
if (width == 8 && height == 8) {
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 1, 0, 4, 4, format, subData);
glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid offset");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 1, 4, 4, format, subData);
glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid offset");
}
if (width < 32 && height < 32) {
var stride = width * 4;
for (var yoff = 0; yoff < height; yoff += 4) {
for (var xoff = 0; xoff < width; xoff += 4) {
copyRect(uncompressedData, 0, 0, xoff, yoff, 4, 4, stride);
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, xoff, yoff, 4, 4, format, subData);
glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6);
compareRect(width, height, test.channels, width, height, uncompressedData, data, format);
}
}
}
}
function insertImg(element, caption, img) {
var div = document.createElement("div");
div.appendChild(img);
var label = document.createElement("div");
label.appendChild(document.createTextNode(caption));
div.appendChild(label);
element.appendChild(div);
}
function convertToSRGB(val) {
var norm = val / 255.0;
var res = 0;
if (norm <= 0.04045) {
res = norm / 12.92;
} else {
res = Math.pow(((norm + 0.055)/1.055), 2.4);
}
return res * 255.0;
}
function makeImage(imageWidth, imageHeight, dataWidth, data, alpha) {
var scale = 8;
var c = document.createElement("canvas");
c.width = imageWidth * scale;
c.height = imageHeight * scale;
var ctx = c.getContext("2d");
for (var yy = 0; yy < imageHeight; ++yy) {
for (var xx = 0; xx < imageWidth; ++xx) {
var offset = (yy * dataWidth + xx) * 4;
ctx.fillStyle = "rgba(" +
data[offset + 0] + "," +
data[offset + 1] + "," +
data[offset + 2] + "," +
(alpha ? data[offset + 3] / 255 : 1) + ")";
ctx.fillRect(xx * scale, yy * scale, scale, scale);
}
}
var img = document.createElement("img");
img.src = c.toDataURL();
return img;
}
function compareRect(actualWidth, actualHeight, actualChannels,
dataWidth, dataHeight, expectedData,
testData, testFormat)
{
var actual = new Uint8Array(actualWidth * actualHeight * 4);
gl.readPixels(
0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual);
var div = document.createElement("div");
div.className = "testimages";
var hasAlpha = actualChannels == 4;
var imgExpected = makeImage(actualWidth, actualHeight, dataWidth, expectedData, hasAlpha);
var imgActual = makeImage(actualWidth, actualHeight, actualWidth, actual, hasAlpha);
insertImg(div, "expected", imgExpected);
insertImg(div, "actual", imgActual);
div.appendChild(document.createElement('br'));
document.getElementById("console").appendChild(div);
var failed = false;
for (var yy = 0; yy < actualHeight; ++yy) {
for (var xx = 0; xx < actualWidth; ++xx) {
var actualOffset = (yy * actualWidth + xx) * 4;
var expectedOffset = (yy * dataWidth + xx) * 4;
var expected = expectedData.slice(expectedOffset, expectedOffset + 4);
var maxDiffPixel = 0;
switch (testFormat) {
case ext.COMPRESSED_SRGB8_ETC2:
case ext.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
case ext.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
// Alpha shouldn't do conversion.
for (var i = 0; i < 3; ++i) {
expected[i] = convertToSRGB(expected[i]);
}
//fallthrough
case ext.COMPRESSED_R11_EAC:
case ext.COMPRESSED_RG11_EAC:
case ext.COMPRESSED_SIGNED_R11_EAC:
case ext.COMPRESSED_SIGNED_RG11_EAC:
// Due to floating round error, we need fuzzy test here.
var maxDiffPixel = 1;
break;
default:
var maxDiffPixel = 0;
break;
}
for (var channel = 0; channel < actualChannels; ++channel) {
var diff = Math.abs(expected[channel] - actual[actualOffset + channel]);
if (diff > maxDiffPixel) {
failed = true;
var was = actual.slice(actualOffset, actualOffset + 4).join();
ok(false, 'at (' + xx + ', ' + yy +
') expected: ' + expected.join() + ' was ' + was);
break;
}
}
}
}
if (!failed) {
ok(true, "texture rendered correctly");
}
}
var prefArrArr = [
['webgl.enable-draft-extensions', true],
];
var prefEnv = {'set': prefArrArr};
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv(prefEnv, runTest);
</script>
</body>
</html>
+15
View File
@@ -827,6 +827,21 @@ interface WEBGL_compressed_texture_atc
const GLenum COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL = 0x87EE;
};
[NoInterfaceObject]
interface WEBGL_compressed_texture_es3
{
const GLenum COMPRESSED_R11_EAC = 0x9270;
const GLenum COMPRESSED_SIGNED_R11_EAC = 0x9271;
const GLenum COMPRESSED_RG11_EAC = 0x9272;
const GLenum COMPRESSED_SIGNED_RG11_EAC = 0x9273;
const GLenum COMPRESSED_RGB8_ETC2 = 0x9274;
const GLenum COMPRESSED_SRGB8_ETC2 = 0x9275;
const GLenum COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276;
const GLenum COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277;
const GLenum COMPRESSED_RGBA8_ETC2_EAC = 0x9278;
const GLenum COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279;
};
[NoInterfaceObject]
interface WEBGL_compressed_texture_etc1
{
+22 -1
View File
@@ -12,6 +12,7 @@
#include "mozilla/gfx/2D.h"
#include "ScopedGLHelpers.h"
#include "GLUploadHelpers.h"
#include "GfxTexturesReporter.h"
#include "TextureImageEGL.h"
#ifdef XP_MACOSX
@@ -95,6 +96,16 @@ gfx::IntRect TextureImage::GetSrcTileRect() {
return GetTileRect();
}
void
TextureImage::UpdateUploadSize(size_t amount)
{
if (mUploadSize > 0) {
GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryFreed, mUploadSize);
}
mUploadSize = amount;
GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryAllocated, mUploadSize);
}
BasicTextureImage::~BasicTextureImage()
{
GLContext *ctx = mGLContext;
@@ -162,16 +173,20 @@ BasicTextureImage::EndUpdate()
RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface();
bool relative = FinishedSurfaceUpdate();
size_t uploadSize;
mTextureFormat =
UploadSurfaceToTexture(mGLContext,
updateData,
mUpdateRegion,
mTexture,
&uploadSize,
mTextureState == Created,
mUpdateOffset,
relative);
FinishedSurfaceUpload();
if (uploadSize > 0) {
UpdateUploadSize(uploadSize);
}
mUpdateDrawTarget = nullptr;
mTextureState = Valid;
@@ -214,14 +229,19 @@ BasicTextureImage::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion
region = aRegion;
}
size_t uploadSize;
mTextureFormat =
UploadSurfaceToTexture(mGLContext,
aSurf,
region,
mTexture,
&uploadSize,
mTextureState == Created,
bounds.TopLeft() + IntPoint(aFrom.x, aFrom.y),
false);
if (uploadSize > 0) {
UpdateUploadSize(uploadSize);
}
mTextureState = Valid;
return true;
}
@@ -273,6 +293,7 @@ TextureImage::TextureImage(const gfx::IntSize& aSize,
, mTextureFormat(gfx::SurfaceFormat::UNKNOWN)
, mFilter(Filter::GOOD)
, mFlags(aFlags)
, mUploadSize(0)
{}
BasicTextureImage::BasicTextureImage(GLuint aTexture,
+6 -1
View File
@@ -203,6 +203,8 @@ public:
protected:
friend class GLContext;
void UpdateUploadSize(size_t amount);
/**
* After the ctor, the TextureImage is invalid. Implementations
* must allocate resources successfully before returning the new
@@ -214,7 +216,9 @@ protected:
Flags aFlags = NoFlags);
// Protected destructor, to discourage deletion outside of Release():
virtual ~TextureImage() {}
virtual ~TextureImage() {
UpdateUploadSize(0);
}
virtual gfx::IntRect GetSrcTileRect();
@@ -224,6 +228,7 @@ protected:
gfx::SurfaceFormat mTextureFormat;
gfx::Filter mFilter;
Flags mFlags;
size_t mUploadSize;
};
/**
+61 -3
View File
@@ -9,6 +9,8 @@
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Tools.h" // For BytesPerPixel
#include "nsRegion.h"
#include "GfxTexturesReporter.h"
#include "mozilla/gfx/Logging.h"
namespace mozilla {
@@ -379,6 +381,51 @@ TexImage2DHelper(GLContext *gl,
}
}
static uint32_t
GetBytesPerTexel(GLenum format, GLenum type)
{
// If there is no defined format or type, we're not taking up any memory
if (!format || !type) {
return 0;
}
if (format == LOCAL_GL_DEPTH_COMPONENT) {
if (type == LOCAL_GL_UNSIGNED_SHORT)
return 2;
else if (type == LOCAL_GL_UNSIGNED_INT)
return 4;
} else if (format == LOCAL_GL_DEPTH_STENCIL) {
if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
return 4;
}
if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT || type == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) {
uint32_t multiplier = type == LOCAL_GL_UNSIGNED_BYTE ? 1 : 4;
switch (format) {
case LOCAL_GL_ALPHA:
case LOCAL_GL_LUMINANCE:
return 1 * multiplier;
case LOCAL_GL_LUMINANCE_ALPHA:
return 2 * multiplier;
case LOCAL_GL_RGB:
return 3 * multiplier;
case LOCAL_GL_RGBA:
return 4 * multiplier;
default:
break;
}
} else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
{
return 2;
}
gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
MOZ_CRASH();
return 0;
}
SurfaceFormat
UploadImageDataToTexture(GLContext* gl,
unsigned char* aData,
@@ -386,6 +433,7 @@ UploadImageDataToTexture(GLContext* gl,
SurfaceFormat aFormat,
const nsIntRegion& aDstRegion,
GLuint& aTexture,
size_t* aOutUploadSize,
bool aOverwrite,
bool aPixelBuffer,
GLenum aTextureUnit,
@@ -505,6 +553,10 @@ UploadImageDataToTexture(GLContext* gl,
// Top left point of the region's bounding rectangle.
IntPoint topLeft = paintRegion.GetBounds().TopLeft();
if (aOutUploadSize) {
*aOutUploadSize = 0;
}
for (auto iter = paintRegion.RectIter(); !iter.Done(); iter.Next()) {
const IntRect& rect = iter.Get();
// The inital data pointer is at the top left point of the region's
@@ -544,6 +596,11 @@ UploadImageDataToTexture(GLContext* gl,
rectData);
}
if (aOutUploadSize && !textureInited) {
uint32_t texelSize = GetBytesPerTexel(internalFormat, type);
size_t numTexels = size_t(rect.width) * size_t(rect.height);
*aOutUploadSize += texelSize * numTexels;
}
}
return surfaceFormat;
@@ -551,9 +608,10 @@ UploadImageDataToTexture(GLContext* gl,
SurfaceFormat
UploadSurfaceToTexture(GLContext* gl,
DataSourceSurface *aSurface,
DataSourceSurface* aSurface,
const nsIntRegion& aDstRegion,
GLuint& aTexture,
size_t* aOutUploadSize,
bool aOverwrite,
const gfx::IntPoint& aSrcPoint,
bool aPixelBuffer,
@@ -565,8 +623,8 @@ UploadSurfaceToTexture(GLContext* gl,
SurfaceFormat format = aSurface->GetFormat();
data += DataOffset(aSrcPoint, stride, format);
return UploadImageDataToTexture(gl, data, stride, format,
aDstRegion, aTexture, aOverwrite,
aPixelBuffer, aTextureUnit,
aDstRegion, aTexture, aOutUploadSize,
aOverwrite, aPixelBuffer, aTextureUnit,
aTextureTarget);
}
+3
View File
@@ -40,6 +40,7 @@ class GLContext;
* \param aData Image data to upload.
* \param aDstRegion Region of texture to upload to.
* \param aTexture Texture to use, or 0 to have one created for you.
* \param aOutUploadSize if set, the number of bytes the texture requires will be returned here
* \param aOverwrite Over an existing texture with a new one.
* \param aSrcPoint Offset into aSrc where the region's bound's
* TopLeft() sits.
@@ -59,6 +60,7 @@ UploadImageDataToTexture(GLContext* gl,
gfx::SurfaceFormat aFormat,
const nsIntRegion& aDstRegion,
GLuint& aTexture,
size_t* aOutUploadSize = nullptr,
bool aOverwrite = false,
bool aPixelBuffer = false,
GLenum aTextureUnit = LOCAL_GL_TEXTURE0,
@@ -72,6 +74,7 @@ UploadSurfaceToTexture(GLContext* gl,
gfx::DataSourceSurface *aSurface,
const nsIntRegion& aDstRegion,
GLuint& aTexture,
size_t* aOutUploadSize = nullptr,
bool aOverwrite = false,
const gfx::IntPoint& aSrcPoint = gfx::IntPoint(0, 0),
bool aPixelBuffer = false,
+6 -69
View File
@@ -5,85 +5,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GfxTexturesReporter.h"
#include "GLDefs.h"
using namespace mozilla;
using namespace mozilla::gl;
NS_IMPL_ISUPPORTS(GfxTexturesReporter, nsIMemoryReporter)
Atomic<int32_t> GfxTexturesReporter::sAmount(0);
Atomic<int32_t> GfxTexturesReporter::sTileWasteAmount(0);
static uint32_t
GetBitsPerTexel(GLenum format, GLenum type)
{
// If there is no defined format or type, we're not taking up any memory
if (!format || !type) {
return 0;
}
if (format == LOCAL_GL_DEPTH_COMPONENT) {
if (type == LOCAL_GL_UNSIGNED_SHORT)
return 2*8;
else if (type == LOCAL_GL_UNSIGNED_INT)
return 4*8;
} else if (format == LOCAL_GL_DEPTH_STENCIL) {
if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
return 4*8;
}
if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT) {
uint32_t multiplier = type == LOCAL_GL_FLOAT ? 32 : 8;
switch (format) {
case LOCAL_GL_ALPHA:
case LOCAL_GL_LUMINANCE:
return 1 * multiplier;
case LOCAL_GL_LUMINANCE_ALPHA:
return 2 * multiplier;
case LOCAL_GL_RGB:
return 3 * multiplier;
case LOCAL_GL_RGBA:
return 4 * multiplier;
case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
return 2;
case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case LOCAL_GL_ATC_RGB:
case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
case LOCAL_GL_ETC1_RGB8_OES:
return 4;
case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
return 8;
default:
break;
}
} else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
{
return 2*8;
}
MOZ_ASSERT(false);
return 0;
}
Atomic<size_t> GfxTexturesReporter::sAmount(0);
Atomic<size_t> GfxTexturesReporter::sTileWasteAmount(0);
/* static */ void
GfxTexturesReporter::UpdateAmount(MemoryUse action, GLenum format,
GLenum type, int32_t tileWidth,
int32_t tileHeight)
GfxTexturesReporter::UpdateAmount(MemoryUse action, size_t amount)
{
int64_t bitsPerTexel = GetBitsPerTexel(format, type);
int64_t bytes = int64_t(tileWidth) * int64_t(tileHeight) * bitsPerTexel/8;
if (action == MemoryFreed) {
sAmount -= bytes;
MOZ_RELEASE_ASSERT(amount <= sAmount);
sAmount -= amount;
} else {
sAmount += bytes;
sAmount += amount;
}
}
+6 -7
View File
@@ -41,10 +41,9 @@ public:
// When memory is used/freed for tile textures, call this method to update
// the value reported by this memory reporter.
static void UpdateAmount(MemoryUse action, GLenum format, GLenum type,
int32_t tileWidth, int32_t tileHeight);
static void UpdateAmount(MemoryUse action, size_t amount);
static void UpdateWasteAmount(int32_t delta) {
static void UpdateWasteAmount(size_t delta) {
sTileWasteAmount += delta;
}
@@ -52,17 +51,17 @@ public:
nsISupports* aData, bool aAnonymize) override
{
MOZ_COLLECT_REPORT("gfx-tiles-waste", KIND_OTHER, UNITS_BYTES,
sTileWasteAmount,
int64_t(sTileWasteAmount),
"Memory lost due to tiles extending past content boundaries");
return MOZ_COLLECT_REPORT(
"gfx-textures", KIND_OTHER, UNITS_BYTES, sAmount,
"gfx-textures", KIND_OTHER, UNITS_BYTES, int64_t(sAmount),
"Memory used for storing GL textures.");
}
private:
static Atomic<int32_t> sAmount;
static Atomic<size_t> sAmount;
// Count the amount of memory lost to tile waste
static Atomic<int32_t> sTileWasteAmount;
static Atomic<size_t> sTileWasteAmount;
};
class GfxTextureWasteTracker {
+5
View File
@@ -207,14 +207,19 @@ TextureImageEGL::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion&
region = aRegion;
}
size_t uploadSize = 0;
mTextureFormat =
UploadSurfaceToTexture(mGLContext,
aSurf,
region,
mTexture,
&uploadSize,
mTextureState == Created,
bounds.TopLeft() + gfx::IntPoint(aFrom.x, aFrom.y),
false);
if (uploadSize > 0) {
UpdateUploadSize(uploadSize);
}
mTextureState = Valid;
return true;
+138 -96
View File
@@ -918,6 +918,10 @@ ParseVarOrConstStatement(AsmJSParser& parser, ParseNode** var)
// out of range: otherwise
// Lastly, a literal may be a float literal which is any double or integer
// literal coerced with Math.fround.
//
// This class distinguishes between signed and unsigned integer SIMD types like
// Int32x4 and Uint32x4, and so does Type below. The wasm ValType and ExprType
// enums, and the wasm::Val class do not.
class NumLit
{
public:
@@ -928,6 +932,7 @@ class NumLit
Double,
Float,
Int32x4,
Uint32x4,
Float32x4,
Bool32x4,
OutOfRangeInt = -1
@@ -982,7 +987,8 @@ class NumLit
}
bool isSimd() const {
return which_ == Int32x4 || which_ == Float32x4 || which_ == Bool32x4;
return which_ == Int32x4 || which_ == Uint32x4 || which_ == Float32x4 ||
which_ == Bool32x4;
}
const SimdConstant& simdValue() const {
@@ -1006,6 +1012,7 @@ class NumLit
case NumLit::Float:
return toFloat() == 0.f && !IsNegativeZero(toFloat());
case NumLit::Int32x4:
case NumLit::Uint32x4:
return simdValue() == SimdConstant::SplatX4(0);
case NumLit::Float32x4:
return simdValue() == SimdConstant::SplatX4(0.f);
@@ -1028,6 +1035,7 @@ class NumLit
case NumLit::Double:
return Val(toDouble());
case NumLit::Int32x4:
case NumLit::Uint32x4:
return Val(simdValue().asInt32x4());
case NumLit::Float32x4:
return Val(simdValue().asFloat32x4());
@@ -1063,6 +1071,7 @@ class Type
DoubleLit = NumLit::Double,
Float = NumLit::Float,
Int32x4 = NumLit::Int32x4,
Uint32x4 = NumLit::Uint32x4,
Float32x4 = NumLit::Float32x4,
Bool32x4 = NumLit::Bool32x4,
Double,
@@ -1083,6 +1092,7 @@ class Type
MOZ_IMPLICIT Type(SimdType type) {
switch (type) {
case SimdType::Int32x4: which_ = Int32x4; return;
case SimdType::Uint32x4: which_ = Uint32x4; return;
case SimdType::Float32x4: which_ = Float32x4; return;
case SimdType::Bool32x4: which_ = Bool32x4; return;
default: break;
@@ -1090,35 +1100,6 @@ class Type
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad SimdType");
}
static Type var(ValType t) {
switch (t) {
case ValType::I32: return Int;
case ValType::I64: MOZ_CRASH("no int64 in asm.js");
case ValType::F32: return Float;
case ValType::F64: return Double;
case ValType::I32x4: return Int32x4;
case ValType::F32x4: return Float32x4;
case ValType::B32x4: return Bool32x4;
case ValType::Limit: break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
}
static Type ret(ExprType t) {
switch (t) {
case ExprType::Void: return Type::Void;
case ExprType::I32: return Signed;
case ExprType::I64: MOZ_CRASH("no int64 in asm.js");
case ExprType::F32: return Float;
case ExprType::F64: return Double;
case ExprType::I32x4: return Int32x4;
case ExprType::F32x4: return Float32x4;
case ExprType::B32x4: return Bool32x4;
case ExprType::Limit: break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad type");
}
// Map an already canonicalized Type to the return type of a function call.
static Type ret(Type t) {
MOZ_ASSERT(t.isCanonical());
@@ -1156,6 +1137,7 @@ class Type
return Void;
case Int32x4:
case Uint32x4:
case Float32x4:
case Bool32x4:
return t;
@@ -1184,6 +1166,7 @@ class Type
case Double: return isDouble();
case Float: return isFloat();
case Int32x4: return isInt32x4();
case Uint32x4: return isUint32x4();
case Float32x4: return isFloat32x4();
case Bool32x4: return isBool32x4();
case MaybeDouble: return isMaybeDouble();
@@ -1253,6 +1236,10 @@ class Type
return which_ == Int32x4;
}
bool isUint32x4() const {
return which_ == Uint32x4;
}
bool isFloat32x4() const {
return which_ == Float32x4;
}
@@ -1262,17 +1249,27 @@ class Type
}
bool isSimd() const {
return isInt32x4() || isFloat32x4() || isBool32x4();
return isInt32x4() || isUint32x4() || isFloat32x4() || isBool32x4();
}
bool isUnsignedSimd() const {
return isUint32x4();
}
// Check if this is one of the valid types for a function argument.
bool isArgType() const {
return isInt() || isFloat() || isDouble() || isSimd();
return isInt() || isFloat() || isDouble() || (isSimd() && !isUnsignedSimd());
}
// Check if this is one of the valid types for a function return value.
bool isReturnType() const {
return isSigned() || isFloat() || isDouble() || isSimd() || isVoid();
return isSigned() || isFloat() || isDouble() || (isSimd() && !isUnsignedSimd()) ||
isVoid();
}
// Check if this is one of the valid types for a global variable.
bool isGlobalVarType() const {
return isArgType();
}
// Check if this is one of the canonical vartype representations of a
@@ -1301,6 +1298,7 @@ class Type
case Float: return ExprType::F32;
case Double: return ExprType::F64;
case Void: return ExprType::Void;
case Uint32x4:
case Int32x4: return ExprType::I32x4;
case Float32x4: return ExprType::F32x4;
case Bool32x4: return ExprType::B32x4;
@@ -1327,6 +1325,7 @@ class Type
case Unsigned: return "unsigned";
case Intish: return "intish";
case Int32x4: return "int32x4";
case Uint32x4: return "uint32x4";
case Float32x4: return "float32x4";
case Bool32x4: return "bool32x4";
case Void: return "void";
@@ -1830,11 +1829,13 @@ class MOZ_STACK_CLASS ModuleValidator
MOZ_ASSERT(n->isTenured());
module_->bufferArgumentName = n;
}
bool addGlobalVarInit(PropertyName* var, const NumLit& lit, bool isConst) {
bool addGlobalVarInit(PropertyName* var, const NumLit& lit, Type type, bool isConst)
{
MOZ_ASSERT(type.isGlobalVarType());
MOZ_ASSERT(type == Type::canonicalize(Type::lit(lit)));
uint32_t index;
Type litType = Type::lit(lit);
Type canonicalType = Type::canonicalize(litType);
if (!mg_.allocateGlobalVar(canonicalType.canonicalToValType(), isConst, &index))
if (!mg_.allocateGlobalVar(type.canonicalToValType(), isConst, &index))
return false;
Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
@@ -1842,7 +1843,7 @@ class MOZ_STACK_CLASS ModuleValidator
if (!global)
return false;
global->u.varOrConst.index_ = index;
global->u.varOrConst.type_ = (isConst ? litType : canonicalType).which();
global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : type).which();
if (isConst)
global->u.varOrConst.literalValue_ = lit;
if (!globalMap_.putNew(var, global))
@@ -1854,9 +1855,12 @@ class MOZ_STACK_CLASS ModuleValidator
g.pod.u.var.globalDataOffset_ = mg_.globalVar(index).globalDataOffset;
return module_->globals.append(g);
}
bool addGlobalVarImport(PropertyName* var, PropertyName* field, ValType type, bool isConst) {
bool addGlobalVarImport(PropertyName* var, PropertyName* field, Type type, bool isConst) {
MOZ_ASSERT(type.isGlobalVarType());
uint32_t index;
if (!mg_.allocateGlobalVar(type, isConst, &index))
ValType valType = type.canonicalToValType();
if (!mg_.allocateGlobalVar(valType, isConst, &index))
return false;
Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
@@ -1864,13 +1868,13 @@ class MOZ_STACK_CLASS ModuleValidator
if (!global)
return false;
global->u.varOrConst.index_ = index;
global->u.varOrConst.type_ = Type::var(type).which();
global->u.varOrConst.type_ = type.which();
if (!globalMap_.putNew(var, global))
return false;
AsmJSGlobal g(AsmJSGlobal::Variable, field);
g.pod.u.var.initKind_ = AsmJSGlobal::InitImport;
g.pod.u.var.u.importType_ = type;
g.pod.u.var.u.importType_ = valType;
g.pod.u.var.globalDataOffset_ = mg_.globalVar(index).globalDataOffset;
return module_->globals.append(g);
}
@@ -2369,6 +2373,7 @@ IsSimdLiteral(ModuleValidator& m, ParseNode* pn)
uint32_t _;
switch (type) {
case SimdType::Int32x4:
case SimdType::Uint32x4:
case SimdType::Bool32x4:
if (!IsLiteralInt(m, arg, &_))
return false;
@@ -2425,7 +2430,8 @@ ExtractSimdValue(ModuleValidator& m, ParseNode* pn)
ParseNode* arg = CallArgList(pn);
switch (type) {
case SimdType::Int32x4: {
case SimdType::Int32x4:
case SimdType::Uint32x4: {
MOZ_ASSERT(GetSimdLanes(type) == 4);
int32_t val[4];
for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) {
@@ -2434,7 +2440,8 @@ ExtractSimdValue(ModuleValidator& m, ParseNode* pn)
val[i] = int32_t(u32);
}
MOZ_ASSERT(arg== nullptr);
return NumLit(NumLit::Int32x4, SimdConstant::CreateX4(val));
NumLit::Which w = type == SimdType::Uint32x4 ? NumLit::Uint32x4 : NumLit::Int32x4;
return NumLit(w, SimdConstant::CreateX4(val));
}
case SimdType::Float32x4: {
MOZ_ASSERT(GetSimdLanes(type) == 4);
@@ -2524,6 +2531,7 @@ IsLiteralInt(NumLit lit, uint32_t* u32)
case NumLit::Float:
case NumLit::OutOfRangeInt:
case NumLit::Int32x4:
case NumLit::Uint32x4:
case NumLit::Float32x4:
case NumLit::Bool32x4:
return false;
@@ -2751,6 +2759,7 @@ class MOZ_STACK_CLASS FunctionValidator
return encoder().writeExpr(Expr::F64Const) &&
encoder().writeFixedF64(lit.toDouble());
case NumLit::Int32x4:
case NumLit::Uint32x4:
return encoder().writeExpr(Expr::I32x4Const) &&
encoder().writeFixedI32x4(lit.simdValue().asInt32x4());
case NumLit::Float32x4:
@@ -2901,7 +2910,11 @@ CheckGlobalVariableInitConstant(ModuleValidator& m, PropertyName* varName, Parse
if (!lit.valid())
return m.fail(initNode, "global initializer is out of representable integer range");
return m.addGlobalVarInit(varName, lit, isConst);
Type canonicalType = Type::canonicalize(Type::lit(lit));
if (!canonicalType.isGlobalVarType())
return m.fail(initNode, "global variable type not allowed");
return m.addGlobalVarInit(varName, lit, canonicalType, isConst);
}
static bool
@@ -2937,12 +2950,20 @@ CheckTypeAnnotation(ModuleValidator& m, ParseNode* coercionNode, Type* coerceTo,
}
static bool
CheckGlobalVariableImportExpr(ModuleValidator& m, PropertyName* varName, ValType coerceTo,
ParseNode* coercedExpr, bool isConst)
CheckGlobalVariableInitImport(ModuleValidator& m, PropertyName* varName, ParseNode* initNode,
bool isConst)
{
Type coerceTo;
ParseNode* coercedExpr;
if (!CheckTypeAnnotation(m, initNode, &coerceTo, &coercedExpr))
return false;
if (!coercedExpr->isKind(PNK_DOT))
return m.failName(coercedExpr, "invalid import expression for global '%s'", varName);
if (!coerceTo.isGlobalVarType())
return m.fail(initNode, "global variable type not allowed");
ParseNode* base = DotBase(coercedExpr);
PropertyName* field = DotMember(coercedExpr);
@@ -2955,17 +2976,6 @@ CheckGlobalVariableImportExpr(ModuleValidator& m, PropertyName* varName, ValType
return m.addGlobalVarImport(varName, field, coerceTo, isConst);
}
static bool
CheckGlobalVariableInitImport(ModuleValidator& m, PropertyName* varName, ParseNode* initNode,
bool isConst)
{
Type coerceTo;
ParseNode* coercedExpr;
if (!CheckTypeAnnotation(m, initNode, &coerceTo, &coercedExpr))
return false;
return CheckGlobalVariableImportExpr(m, varName, coerceTo.canonicalToValType(), coercedExpr, isConst);
}
static bool
IsArrayViewCtorName(ModuleValidator& m, PropertyName* name, Scalar::Type* type)
{
@@ -3051,24 +3061,6 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr)
return m.addArrayView(varName, type, field);
}
static bool
IsSimdTypeName(ModuleValidator& m, PropertyName* name, SimdType* type)
{
if (name == m.cx()->names().Int32x4) {
*type = SimdType::Int32x4;
return true;
}
if (name == m.cx()->names().Float32x4) {
*type = SimdType::Float32x4;
return true;
}
if (name == m.cx()->names().Bool32x4) {
*type = SimdType::Bool32x4;
return true;
}
return false;
}
static bool
IsSimdValidOperationType(SimdType type, SimdOperation op)
{
@@ -3076,27 +3068,39 @@ IsSimdValidOperationType(SimdType type, SimdOperation op)
switch(type) {
case SimdType::Int32x4:
switch (op) {
case SimdOperation::Constructor:
case SimdOperation::Fn_fromUint32x4Bits:
FORALL_INT32X4_ASMJS_OP(CASE) return true;
default: return false;
}
break;
case SimdType::Uint32x4:
switch (op) {
case SimdOperation::Constructor:
case SimdOperation::Fn_fromInt32x4Bits:
FORALL_INT32X4_ASMJS_OP(CASE) return true;
default: return false;
}
break;
case SimdType::Float32x4:
switch (op) {
case SimdOperation::Constructor:
FORALL_FLOAT32X4_ASMJS_OP(CASE) return true;
default: return false;
}
break;
case SimdType::Bool32x4:
switch (op) {
case SimdOperation::Constructor:
FORALL_BOOL_SIMD_OP(CASE) return true;
default: return false;
}
break;
default:
break;
// Unimplemented SIMD type.
return false;
}
#undef CASE
MOZ_CRASH("Unhandles SIMD type");
}
static bool
@@ -3140,8 +3144,17 @@ CheckGlobalSimdImport(ModuleValidator& m, ParseNode* initNode, PropertyName* var
// SIMD constructor, with the form glob.SIMD.[[type]]
SimdType simdType;
if (!IsSimdTypeName(m, field, &simdType))
if (!IsSimdTypeName(m.cx()->names(), field, &simdType))
return m.failName(initNode, "'%s' is not a standard SIMD type", field);
// IsSimdTypeName will return true for any SIMD type supported by the VM.
//
// Since we may not support all of those SIMD types in asm.js, use the
// asm.js-specific IsSimdValidOperationType() to check if this specific
// constructor is supported in asm.js.
if (!IsSimdValidOperationType(simdType, SimdOperation::Constructor))
return m.failName(initNode, "'%s' is not a supported SIMD type", field);
return m.addSimdCtor(varName, simdType, field);
}
@@ -3313,6 +3326,9 @@ CheckArgumentType(FunctionValidator& f, ParseNode* stmt, PropertyName* name, Typ
if (!CheckTypeAnnotation(f.m(), coercionNode, type, &coercedExpr))
return false;
if (!type->isArgType())
return f.failName(stmt, "invalid type for argument '%s'", name);
if (!IsUseOfName(coercedExpr, name))
return ArgFail(f, name, stmt);
@@ -3591,6 +3607,14 @@ static bool
CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
Scalar::Type* viewType, int32_t* mask)
{
// asm.js doesn't have constant offsets, so just encode a 0.
if (!f.encoder().writeVarU32(0))
return false;
size_t alignAt;
if (!f.encoder().writePatchableVarU8(&alignAt))
return false;
size_t prepareAt;
if (!f.encoder().writePatchableExpr(&prepareAt))
return false;
@@ -3598,6 +3622,9 @@ CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode*
if (!CheckArrayAccess(f, viewName, indexExpr, viewType, mask))
return false;
// asm.js only has naturally-aligned accesses.
f.encoder().patchVarU8(alignAt, TypedArrayElemSize(*viewType));
// Don't generate the mask op if there is no need for it which could happen for
// a shift of zero or a SIMD access.
if (*mask != NoMask) {
@@ -4244,14 +4271,14 @@ CheckSignatureAgainstExisting(ModuleValidator& m, ParseNode* usepn, const Sig& s
for (unsigned i = 0; i < sig.args().length(); i++) {
if (sig.arg(i) != existing.arg(i)) {
return m.failf(usepn, "incompatible type for argument %u: (%s here vs. %s before)",
i, Type::var(sig.arg(i)).toChars(), Type::var(existing.arg(i)).toChars());
return m.failf(usepn, "incompatible type for argument %u: (%s here vs. %s before)", i,
ToCString(sig.arg(i)), ToCString(existing.arg(i)));
}
}
if (sig.ret() != existing.ret()) {
return m.failf(usepn, "%s incompatible with previous return of type %s",
Type::ret(sig.ret()).toChars(), Type::ret(existing.ret()).toChars());
ToCString(sig.ret()), ToCString(existing.ret()));
}
MOZ_ASSERT(sig == existing);
@@ -4280,7 +4307,10 @@ static bool
CheckIsArgType(FunctionValidator& f, ParseNode* argNode, Type type)
{
if (!type.isArgType())
return f.failf(argNode, "%s is not a subtype of int, float or double", type.toChars());
return f.failf(argNode,
"%s is not a subtype of int, float, double, or an allowed SIMD type",
type.toChars());
return true;
}
@@ -4663,6 +4693,7 @@ SimdToCoercedScalarType(SimdType t)
{
switch (t) {
case SimdType::Int32x4:
case SimdType::Uint32x4:
case SimdType::Bool32x4:
return Type::Intish;
case SimdType::Float32x4:
@@ -4851,7 +4882,7 @@ CheckSimdBinaryShift(FunctionValidator& f, ParseNode* call, SimdType opType, Sim
return false;
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(opType)))
return false;
*type = Type::Int32x4;
*type = opType;
return true;
}
@@ -4863,7 +4894,7 @@ CheckSimdBinaryComp(FunctionValidator& f, ParseNode* call, SimdType opType, Simd
return false;
if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType)))
return false;
*type = Type::Bool32x4;
*type = GetBooleanSimdType(opType);
return true;
}
@@ -4886,6 +4917,7 @@ CheckSimdExtractLane(FunctionValidator& f, ParseNode* call, SimdType opType, Typ
return false;
switch (opType) {
case SimdType::Int32x4: *type = Type::Signed; break;
case SimdType::Uint32x4: *type = Type::Unsigned; break;
case SimdType::Float32x4: *type = Type::Float; break;
case SimdType::Bool32x4: *type = Type::Int; break;
default: MOZ_CRASH("unhandled simd type");
@@ -5166,6 +5198,9 @@ CheckSimdOperationCall(FunctionValidator& f, ParseNode* call, const ModuleValida
case SimdOperation::Fn_fromInt32x4:
case SimdOperation::Fn_fromInt32x4Bits:
return CheckSimdCast(f, call, SimdType::Int32x4, opType, op, type);
case SimdOperation::Fn_fromUint32x4:
case SimdOperation::Fn_fromUint32x4Bits:
return CheckSimdCast(f, call, SimdType::Uint32x4, opType, op, type);
case SimdOperation::Fn_fromFloat32x4:
case SimdOperation::Fn_fromFloat32x4Bits:
return CheckSimdCast(f, call, SimdType::Float32x4, opType, op, type);
@@ -5207,12 +5242,10 @@ CheckSimdOperationCall(FunctionValidator& f, ParseNode* call, const ModuleValida
case SimdOperation::Constructor:
MOZ_CRASH("constructors are handled in CheckSimdCtorCall");
case SimdOperation::Fn_fromUint32x4:
case SimdOperation::Fn_fromInt8x16Bits:
case SimdOperation::Fn_fromInt16x8Bits:
case SimdOperation::Fn_fromUint8x16Bits:
case SimdOperation::Fn_fromUint16x8Bits:
case SimdOperation::Fn_fromUint32x4Bits:
case SimdOperation::Fn_fromFloat64x2Bits:
MOZ_CRASH("NYI");
}
@@ -5593,12 +5626,8 @@ CheckConditional(FunctionValidator& f, ParseNode* ternary, Type* type)
*type = Type::Double;
} else if (thenType.isFloat() && elseType.isFloat()) {
*type = Type::Float;
} else if (elseType.isInt32x4() && thenType.isInt32x4()) {
*type = Type::Int32x4;
} else if (elseType.isFloat32x4() && thenType.isFloat32x4()) {
*type = Type::Float32x4;
} else if (elseType.isBool32x4() && thenType.isBool32x4()) {
*type = Type::Bool32x4;
} else if (thenType.isSimd() && elseType == thenType) {
*type = thenType;
} else {
return f.failf(ternary, "then/else branches of conditional must both produce int, float, "
"double or SIMD types, current types are %s and %s",
@@ -5626,6 +5655,7 @@ IsValidIntMultiplyConstant(ModuleValidator& m, ParseNode* expr)
case NumLit::Float:
case NumLit::OutOfRangeInt:
case NumLit::Int32x4:
case NumLit::Uint32x4:
case NumLit::Float32x4:
case NumLit::Bool32x4:
return false;
@@ -6167,6 +6197,7 @@ CheckCaseExpr(FunctionValidator& f, ParseNode* caseExpr, int32_t* value)
case NumLit::Double:
case NumLit::Float:
case NumLit::Int32x4:
case NumLit::Uint32x4:
case NumLit::Float32x4:
case NumLit::Bool32x4:
return f.fail(caseExpr, "switch case expression must be an integer literal");
@@ -6327,7 +6358,7 @@ CheckReturnType(FunctionValidator& f, ParseNode* usepn, Type ret)
if (f.returnedType() != ret.canonicalToExprType()) {
return f.failf(usepn, "%s incompatible with previous return of type %s",
Type::ret(ret).toChars(), Type::ret(f.returnedType()).toChars());
Type::ret(ret).toChars(), ToCString(f.returnedType()));
}
return true;
@@ -6840,6 +6871,7 @@ GetDataProperty(JSContext* cx, HandleValue objVal, HandlePropertyName field, Mut
static bool
HasPureCoercion(JSContext* cx, HandleValue v)
{
// Unsigned SIMD types are not allowed in function signatures.
if (IsVectorObject<Int32x4>(v) || IsVectorObject<Float32x4>(v) || IsVectorObject<Bool32x4>(v))
return true;
@@ -7040,7 +7072,7 @@ ValidateSimdType(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal
else
type = global.simdOperationType();
RootedPropertyName simdTypeName(cx, SimdTypeToName(cx, type));
RootedPropertyName simdTypeName(cx, SimdTypeToName(cx->names(), type));
if (!GetDataProperty(cx, v, simdTypeName, &v))
return false;
@@ -7080,12 +7112,21 @@ ValidateSimdOperation(JSContext* cx, const AsmJSGlobal& global, HandleValue glob
Native native = nullptr;
switch (global.simdOperationType()) {
#define SET_NATIVE_INT32X4(op) case SimdOperation::Fn_##op: native = simd_int32x4_##op; break;
#define SET_NATIVE_UINT32X4(op) case SimdOperation::Fn_##op: native = simd_uint32x4_##op; break;
#define SET_NATIVE_FLOAT32X4(op) case SimdOperation::Fn_##op: native = simd_float32x4_##op; break;
#define SET_NATIVE_BOOL32X4(op) case SimdOperation::Fn_##op: native = simd_bool32x4_##op; break;
#define FALLTHROUGH(op) case SimdOperation::Fn_##op:
case SimdType::Int32x4:
switch (global.simdOperation()) {
FORALL_INT32X4_ASMJS_OP(SET_NATIVE_INT32X4)
SET_NATIVE_INT32X4(fromUint32x4Bits)
default: MOZ_CRASH("shouldn't have been validated in the first place");
}
break;
case SimdType::Uint32x4:
switch (global.simdOperation()) {
FORALL_INT32X4_ASMJS_OP(SET_NATIVE_UINT32X4)
SET_NATIVE_UINT32X4(fromInt32x4Bits)
default: MOZ_CRASH("shouldn't have been validated in the first place");
}
break;
@@ -7103,8 +7144,9 @@ ValidateSimdOperation(JSContext* cx, const AsmJSGlobal& global, HandleValue glob
break;
default: MOZ_CRASH("unhandled simd type");
#undef FALLTHROUGH
#undef SET_NATIVE_FLOAT32X4
#undef SET_NATIVE_INT32X4
#undef SET_NATIVE_UINT32X4
#undef SET_NATIVE_FLOAT32X4
#undef SET_NATIVE_BOOL32X4
#undef SET_NATIVE
}
+10 -23
View File
@@ -83,23 +83,6 @@ class FunctionDecoder
}
};
static const char*
ToCString(ExprType type)
{
switch (type) {
case ExprType::Void: return "void";
case ExprType::I32: return "i32";
case ExprType::I64: return "i64";
case ExprType::F32: return "f32";
case ExprType::F64: return "f64";
case ExprType::I32x4: return "i32x4";
case ExprType::F32x4: return "f32x4";
case ExprType::B32x4: return "b32x4";
case ExprType::Limit:;
}
MOZ_CRASH("bad expression type");
}
static bool
CheckType(FunctionDecoder& f, ExprType actual, ExprType expected)
{
@@ -129,6 +112,10 @@ DecodeValType(JSContext* cx, Decoder& d, ValType *type)
case ValType::F64:
break;
case ValType::I64:
#ifndef JS_CPU_X64
return Fail(cx, d, "i64 NYI on this platform");
#endif
break;
case ValType::I32x4:
case ValType::F32x4:
case ValType::B32x4:
@@ -153,6 +140,10 @@ DecodeExprType(JSContext* cx, Decoder& d, ExprType *type)
case ExprType::Void:
break;
case ExprType::I64:
#ifndef JS_CPU_X64
return Fail(cx, d, "i64 NYI on this platform");
#endif
break;
case ExprType::I32x4:
case ExprType::F32x4:
case ExprType::B32x4:
@@ -376,10 +367,7 @@ DecodeLoadStoreAddress(FunctionDecoder &f)
if (!mozilla::IsPowerOfTwo(align))
return f.fail("memory access alignment must be a power of two");
if (!DecodeExpr(f, ExprType::I32))
return false;
return f.fail("NYI: wasm loads and stores");
return DecodeExpr(f, ExprType::I32);
}
static bool
@@ -423,8 +411,7 @@ DecodeExpr(FunctionDecoder& f, ExprType expected)
case Expr::I32Const:
return DecodeConstI32(f, expected);
case Expr::I64Const:
return f.fail("NYI: i64") &&
DecodeConstI64(f, expected);
return DecodeConstI64(f, expected);
case Expr::F32Const:
return DecodeConstF32(f, expected);
case Expr::F64Const:
+9
View File
@@ -476,6 +476,15 @@ class Encoder
return patchVarU32(offset, patchBits, UINT32_MAX);
}
MOZ_WARN_UNUSED_RESULT bool writePatchableVarU8(size_t* offset) {
*offset = bytecode_.length();
return writeU8(UINT8_MAX);
}
void patchVarU8(size_t offset, uint8_t patchBits) {
MOZ_ASSERT(patchBits < 0x80);
return patchU8(offset, patchBits);
}
MOZ_WARN_UNUSED_RESULT bool writePatchableExpr(size_t* offset) {
return writePatchableEnum<Expr>(offset);
}
+47 -21
View File
@@ -118,7 +118,8 @@ class FunctionCompiler
ins = MConstant::NewAsmJS(alloc(), Int32Value(0), MIRType_Int32);
break;
case ValType::I64:
MOZ_CRASH("int64");
ins = MConstant::NewInt64(alloc(), 0);
break;
case ValType::F32:
ins = MConstant::NewAsmJS(alloc(), Float32Value(0.f), MIRType_Float32);
break;
@@ -1421,10 +1422,22 @@ static bool EmitExpr(FunctionCompiler&, ExprType, MDefinition**, LabelVector* =
static bool EmitExprStmt(FunctionCompiler&, MDefinition**, LabelVector* = nullptr);
static bool
EmitLoadArray(FunctionCompiler& f, Scalar::Type scalarType, MDefinition** def)
EmitLoadStoreAddress(FunctionCompiler& f, uint32_t* offset, uint32_t* align, MDefinition** base)
{
*offset = f.readVarU32();
MOZ_ASSERT(*offset == 0, "Non-zero offsets not supported yet");
*align = f.readVarU32();
return EmitExpr(f, ExprType::I32, base);
}
static bool
EmitLoad(FunctionCompiler& f, Scalar::Type scalarType, MDefinition** def)
{
uint32_t offset, align;
MDefinition* ptr;
if (!EmitExpr(f, ExprType::I32, &ptr))
if (!EmitLoadStoreAddress(f, &offset, &align, &ptr))
return false;
*def = f.loadHeap(scalarType, ptr);
return true;
@@ -1433,8 +1446,9 @@ EmitLoadArray(FunctionCompiler& f, Scalar::Type scalarType, MDefinition** def)
static bool
EmitStore(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def)
{
uint32_t offset, align;
MDefinition* ptr;
if (!EmitExpr(f, ExprType::I32, &ptr))
if (!EmitLoadStoreAddress(f, &offset, &align, &ptr))
return false;
MDefinition* rhs = nullptr;
@@ -1465,8 +1479,9 @@ static bool
EmitStoreWithCoercion(FunctionCompiler& f, Scalar::Type rhsType, Scalar::Type viewType,
MDefinition **def)
{
uint32_t offset, align;
MDefinition* ptr;
if (!EmitExpr(f, ExprType::I32, &ptr))
if (!EmitLoadStoreAddress(f, &offset, &align, &ptr))
return false;
MDefinition* rhs = nullptr;
@@ -1536,9 +1551,12 @@ static bool
EmitAtomicsLoad(FunctionCompiler& f, MDefinition** def)
{
Scalar::Type viewType = Scalar::Type(f.readU8());
uint32_t offset, align;
MDefinition* index;
if (!EmitExpr(f, ExprType::I32, &index))
if (!EmitLoadStoreAddress(f, &offset, &align, &index))
return false;
*def = f.atomicLoadHeap(viewType, index);
return true;
}
@@ -1547,9 +1565,12 @@ static bool
EmitAtomicsStore(FunctionCompiler& f, MDefinition** def)
{
Scalar::Type viewType = Scalar::Type(f.readU8());
uint32_t offset, align;
MDefinition* index;
if (!EmitExpr(f, ExprType::I32, &index))
if (!EmitLoadStoreAddress(f, &offset, &align, &index))
return false;
MDefinition* value;
if (!EmitExpr(f, ExprType::I32, &value))
return false;
@@ -1563,9 +1584,12 @@ EmitAtomicsBinOp(FunctionCompiler& f, MDefinition** def)
{
Scalar::Type viewType = Scalar::Type(f.readU8());
js::jit::AtomicOp op = js::jit::AtomicOp(f.readU8());
uint32_t offset, align;
MDefinition* index;
if (!EmitExpr(f, ExprType::I32, &index))
if (!EmitLoadStoreAddress(f, &offset, &align, &index))
return false;
MDefinition* value;
if (!EmitExpr(f, ExprType::I32, &value))
return false;
@@ -1577,9 +1601,12 @@ static bool
EmitAtomicsCompareExchange(FunctionCompiler& f, MDefinition** def)
{
Scalar::Type viewType = Scalar::Type(f.readU8());
uint32_t offset, align;
MDefinition* index;
if (!EmitExpr(f, ExprType::I32, &index))
if (!EmitLoadStoreAddress(f, &offset, &align, &index))
return false;
MDefinition* oldValue;
if (!EmitExpr(f, ExprType::I32, &oldValue))
return false;
@@ -1594,9 +1621,12 @@ static bool
EmitAtomicsExchange(FunctionCompiler& f, MDefinition** def)
{
Scalar::Type viewType = Scalar::Type(f.readU8());
uint32_t offset, align;
MDefinition* index;
if (!EmitExpr(f, ExprType::I32, &index))
if (!EmitLoadStoreAddress(f, &offset, &align, &index))
return false;
MDefinition* value;
if (!EmitExpr(f, ExprType::I32, &value))
return false;
@@ -2338,10 +2368,6 @@ EmitSimdOp(FunctionCompiler& f, ExprType type, SimdOperation op, SimdSign sign,
return EmitSimdShift(f, type, MSimdShift::lsh, def);
case SimdOperation::Fn_shiftRightByScalar:
return EmitSimdShift(f, type, MSimdShift::rshForSign(sign), def);
case SimdOperation::Fn_shiftRightArithmeticByScalar:
return EmitSimdShift(f, type, MSimdShift::rsh, def);
case SimdOperation::Fn_shiftRightLogicalByScalar:
return EmitSimdShift(f, type, MSimdShift::ursh, def);
#define _CASE(OP) \
case SimdOperation::Fn_##OP: \
return EmitSimdBinaryComp(f, type, MSimdBinaryComp::OP, sign, def);
@@ -2774,15 +2800,15 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may
case Expr::I32BitNot:
return EmitBitwise<MBitNot>(f, def);
case Expr::I32LoadMem8S:
return EmitLoadArray(f, Scalar::Int8, def);
return EmitLoad(f, Scalar::Int8, def);
case Expr::I32LoadMem8U:
return EmitLoadArray(f, Scalar::Uint8, def);
return EmitLoad(f, Scalar::Uint8, def);
case Expr::I32LoadMem16S:
return EmitLoadArray(f, Scalar::Int16, def);
return EmitLoad(f, Scalar::Int16, def);
case Expr::I32LoadMem16U:
return EmitLoadArray(f, Scalar::Uint16, def);
return EmitLoad(f, Scalar::Uint16, def);
case Expr::I32LoadMem:
return EmitLoadArray(f, Scalar::Int32, def);
return EmitLoad(f, Scalar::Int32, def);
case Expr::I32StoreMem8:
return EmitStore(f, Scalar::Int8, def);
case Expr::I32StoreMem16:
@@ -2856,7 +2882,7 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may
case Expr::F32ConvertUI32:
return EmitUnary<MAsmJSUnsignedToFloat32>(f, ExprType::I32, def);
case Expr::F32LoadMem:
return EmitLoadArray(f, Scalar::Float32, def);
return EmitLoad(f, Scalar::Float32, def);
case Expr::F32StoreMem:
return EmitStore(f, Scalar::Float32, def);
case Expr::F32StoreMemF64:
@@ -2904,7 +2930,7 @@ EmitExpr(FunctionCompiler& f, ExprType type, MDefinition** def, LabelVector* may
case Expr::F64ConvertUI32:
return EmitUnary<MAsmJSUnsignedToDouble>(f, ExprType::I32, def);
case Expr::F64LoadMem:
return EmitLoadArray(f, Scalar::Float64, def);
return EmitLoad(f, Scalar::Float64, def);
case Expr::F64StoreMem:
return EmitStore(f, Scalar::Float64, def);
case Expr::F64StoreMemF32:
+24 -1
View File
@@ -74,7 +74,7 @@ ToMIRType(ValType vt)
{
switch (vt) {
case ValType::I32: return jit::MIRType_Int32;
case ValType::I64: MOZ_CRASH("NYI");
case ValType::I64: return jit::MIRType_Int64;
case ValType::F32: return jit::MIRType_Float32;
case ValType::F64: return jit::MIRType_Double;
case ValType::I32x4: return jit::MIRType_Int32x4;
@@ -194,6 +194,29 @@ ToMIRType(ExprType et)
return IsVoid(et) ? jit::MIRType_None : ToMIRType(ValType(et));
}
static inline const char*
ToCString(ExprType type)
{
switch (type) {
case ExprType::Void: return "void";
case ExprType::I32: return "i32";
case ExprType::I64: return "i64";
case ExprType::F32: return "f32";
case ExprType::F64: return "f64";
case ExprType::I32x4: return "i32x4";
case ExprType::F32x4: return "f32x4";
case ExprType::B32x4: return "b32x4";
case ExprType::Limit:;
}
MOZ_CRASH("bad expression type");
}
static inline const char*
ToCString(ValType type)
{
return ToCString(ToExprType(type));
}
// The Sig class represents a WebAssembly function signature which takes a list
// of value types and returns an expression type. The engine uses two in-memory
// representations of the argument Vector's memory (when elements do not fit
+39 -18
View File
@@ -39,19 +39,6 @@ using mozilla::NumberIsInt32;
static_assert(unsigned(SimdType::Count) == 12, "sync with TypedObjectConstants.h");
PropertyName*
js::SimdTypeToName(JSContext* cx, SimdType type)
{
switch (type) {
case SimdType::Int32x4: return cx->names().Int32x4;
case SimdType::Float32x4: return cx->names().Float32x4;
case SimdType::Bool32x4: return cx->names().Bool32x4;
default: break;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
}
static bool
CheckVectorObject(HandleValue v, SimdType expectedType)
{
@@ -107,6 +94,30 @@ js::SimdTypeToString(SimdType type)
return "<bad SimdType>";
}
PropertyName*
js::SimdTypeToName(const JSAtomState& atoms, SimdType type)
{
switch (type) {
#define CASE_(TypeName) case SimdType::TypeName: return atoms.TypeName;
FOR_EACH_SIMD(CASE_)
#undef CASE_
case SimdType::Count: break;
}
MOZ_CRASH("bad SIMD type");
}
bool
js::IsSimdTypeName(const JSAtomState& atoms, const PropertyName* name, SimdType* type)
{
#define CHECK_(TypeName) if (name == atoms.TypeName) { \
*type = SimdType::TypeName; \
return true; \
}
FOR_EACH_SIMD(CHECK_)
#undef CHECK_
return false;
}
static inline bool
ErrorBadArgs(JSContext* cx)
{
@@ -701,27 +712,37 @@ template<typename T>
struct Or {
static T apply(T l, T r) { return l | r; }
};
// For the following three operators, if the value v we're trying to shift is
// such that v << bits can't fit in the int32 range, then we have undefined
// behavior, according to C++11 [expr.shift]p2.
// behavior, according to C++11 [expr.shift]p2. However, left-shifting an
// unsigned type is well-defined.
//
// In C++, shifting by an amount outside the range [0;N-1] is undefined
// behavior. SIMD.js reduces the shift amount modulo the number of bits in a
// lane and has defined behavior for all shift amounts.
template<typename T>
struct ShiftLeft {
static T apply(T v, int32_t bits) {
return uint32_t(bits) >= sizeof(T) * 8 ? 0 : v << bits;
typedef typename mozilla::MakeUnsigned<T>::Type UnsignedT;
uint32_t maskedBits = uint32_t(bits) % (sizeof(T) * 8);
return UnsignedT(v) << maskedBits;
}
};
template<typename T>
struct ShiftRightArithmetic {
static T apply(T v, int32_t bits) {
typedef typename mozilla::MakeSigned<T>::Type SignedT;
uint32_t maxBits = sizeof(T) * 8;
return SignedT(v) >> (uint32_t(bits) >= maxBits ? maxBits - 1 : bits);
uint32_t maskedBits = uint32_t(bits) % (sizeof(T) * 8);
return SignedT(v) >> maskedBits;
}
};
template<typename T>
struct ShiftRightLogical {
static T apply(T v, int32_t bits) {
return uint32_t(bits) >= sizeof(T) * 8 ? 0 : uint32_t(v) >> bits;
typedef typename mozilla::MakeUnsigned<T>::Type UnsignedT;
uint32_t maskedBits = uint32_t(bits) % (sizeof(T) * 8);
return UnsignedT(v) >> maskedBits;
}
};
+7 -16
View File
@@ -250,8 +250,6 @@
V(subSaturate, (BinaryFunc<Int8x16, SubSaturate, Int8x16>), 2) \
V(shiftLeftByScalar, (BinaryScalar<Int8x16, ShiftLeft>), 2) \
V(shiftRightByScalar, (BinaryScalar<Int8x16, ShiftRightArithmetic>), 2) \
V(shiftRightArithmeticByScalar, (BinaryScalar<Int8x16, ShiftRightArithmetic>), 2) \
V(shiftRightLogicalByScalar, (BinaryScalar<Int8x16, ShiftRightLogical>), 2) \
V(xor, (BinaryFunc<Int8x16, Xor, Int8x16>), 2)
#define INT8X16_TERNARY_FUNCTION_LIST(V) \
@@ -301,8 +299,6 @@
V(subSaturate, (BinaryFunc<Uint8x16, SubSaturate, Uint8x16>), 2) \
V(shiftLeftByScalar, (BinaryScalar<Uint8x16, ShiftLeft>), 2) \
V(shiftRightByScalar, (BinaryScalar<Uint8x16, ShiftRightLogical>), 2) \
V(shiftRightArithmeticByScalar, (BinaryScalar<Uint8x16, ShiftRightArithmetic>), 2) \
V(shiftRightLogicalByScalar, (BinaryScalar<Uint8x16, ShiftRightLogical>), 2) \
V(xor, (BinaryFunc<Uint8x16, Xor, Uint8x16>), 2)
#define UINT8X16_TERNARY_FUNCTION_LIST(V) \
@@ -352,8 +348,6 @@
V(subSaturate, (BinaryFunc<Int16x8, SubSaturate, Int16x8>), 2) \
V(shiftLeftByScalar, (BinaryScalar<Int16x8, ShiftLeft>), 2) \
V(shiftRightByScalar, (BinaryScalar<Int16x8, ShiftRightArithmetic>), 2) \
V(shiftRightArithmeticByScalar, (BinaryScalar<Int16x8, ShiftRightArithmetic>), 2) \
V(shiftRightLogicalByScalar, (BinaryScalar<Int16x8, ShiftRightLogical>), 2) \
V(xor, (BinaryFunc<Int16x8, Xor, Int16x8>), 2)
#define INT16X8_TERNARY_FUNCTION_LIST(V) \
@@ -403,8 +397,6 @@
V(subSaturate, (BinaryFunc<Uint16x8, SubSaturate, Uint16x8>), 2) \
V(shiftLeftByScalar, (BinaryScalar<Uint16x8, ShiftLeft>), 2) \
V(shiftRightByScalar, (BinaryScalar<Uint16x8, ShiftRightLogical>), 2) \
V(shiftRightArithmeticByScalar, (BinaryScalar<Uint16x8, ShiftRightArithmetic>), 2) \
V(shiftRightLogicalByScalar, (BinaryScalar<Uint16x8, ShiftRightLogical>), 2) \
V(xor, (BinaryFunc<Uint16x8, Xor, Uint16x8>), 2)
#define UINT16X8_TERNARY_FUNCTION_LIST(V) \
@@ -456,8 +448,6 @@
V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2) \
V(shiftLeftByScalar, (BinaryScalar<Int32x4, ShiftLeft>), 2) \
V(shiftRightByScalar, (BinaryScalar<Int32x4, ShiftRightArithmetic>), 2) \
V(shiftRightArithmeticByScalar, (BinaryScalar<Int32x4, ShiftRightArithmetic>), 2) \
V(shiftRightLogicalByScalar, (BinaryScalar<Int32x4, ShiftRightLogical>), 2) \
V(xor, (BinaryFunc<Int32x4, Xor, Int32x4>), 2)
#define INT32X4_TERNARY_FUNCTION_LIST(V) \
@@ -512,8 +502,6 @@
V(sub, (BinaryFunc<Uint32x4, Sub, Uint32x4>), 2) \
V(shiftLeftByScalar, (BinaryScalar<Uint32x4, ShiftLeft>), 2) \
V(shiftRightByScalar, (BinaryScalar<Uint32x4, ShiftRightLogical>), 2) \
V(shiftRightArithmeticByScalar, (BinaryScalar<Uint32x4, ShiftRightArithmetic>), 2) \
V(shiftRightLogicalByScalar, (BinaryScalar<Uint32x4, ShiftRightLogical>), 2) \
V(xor, (BinaryFunc<Uint32x4, Xor, Uint32x4>), 2)
#define UINT32X4_TERNARY_FUNCTION_LIST(V) \
@@ -586,9 +574,7 @@
// Bitwise shifts defined on integer SIMD types.
#define FOREACH_SHIFT_SIMD_OP(_) \
_(shiftLeftByScalar) \
_(shiftRightByScalar) \
_(shiftRightArithmeticByScalar) \
_(shiftRightLogicalByScalar)
_(shiftRightByScalar)
// Unary arithmetic operators defined on numeric SIMD types.
#define FOREACH_NUMERIC_SIMD_UNOP(_) \
@@ -1103,7 +1089,12 @@ struct Bool64x2 {
}
};
PropertyName* SimdTypeToName(JSContext* cx, SimdType type);
// Get the well known name of the SIMD.* object corresponding to type.
PropertyName* SimdTypeToName(const JSAtomState& atoms, SimdType type);
// Check if name is the well known name of a SIMD type.
// Returns true and sets *type iff name is known.
bool IsSimdTypeName(const JSAtomState& atoms, const PropertyName* name, SimdType* type);
const char* SimdTypeToString(SimdType type);
+33 -22
View File
@@ -4,21 +4,25 @@ setJitCompilerOption("ion.warmup.trigger", 50);
function curry(f, arg) { return f.bind(null, arg); }
function binaryLsh(count, v) { if (count>>>0 >= 32) return 0; return (v << count) | 0; }
function binaryLsh(count, v) { count &= 31; return (v << count) | 0; }
function lsh(count) { return curry(binaryLsh, count); }
function binaryRsh(count, v) { if (count>>>0 >= 32) count = 31; return (v >> count) | 0; }
function binaryRsh(count, v) { count &= 31; return (v >> count) | 0; }
function rsh(count) { return curry(binaryRsh, count); }
function binaryUrsh(count, v) { if (count>>>0 >= 32) return 0; return (v >>> count) | 0; }
function binaryUlsh(count, v) { count &= 31; return (v << count) >>> 0; }
function ulsh(count) { return curry(binaryUlsh, count); }
function binaryUrsh(count, v) { count &= 31; return v >>> count; }
function ursh(count) { return curry(binaryUrsh, count); }
function f() {
var v = SIMD.Int32x4(1, 2, -3, 4);
var u = SIMD.Uint32x4(1, 0x55005500, -3, 0xaa00aa00);
var a = [1, 2, -3, 4];
var zeros = [0,0,0,0];
var b = [1, 0x55005500, -3, 0xaa00aa00];
var shifts = [-1, 0, 1, 31, 32];
var shifts = [-2, -1, 0, 1, 31, 32, 33];
var r;
for (var i = 0; i < 150; i++) {
@@ -29,33 +33,40 @@ function f() {
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 2), a.map(lsh(2)));
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 31), a.map(lsh(31)));
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 32), a.map(lsh(32)));
assertEqX4(SIMD.Int32x4.shiftRightArithmeticByScalar(v, -1), a.map(rsh(31)));
assertEqX4(SIMD.Int32x4.shiftRightArithmeticByScalar(v, 0), a.map(rsh(0)));
assertEqX4(SIMD.Int32x4.shiftRightArithmeticByScalar(v, 1), a.map(rsh(1)));
assertEqX4(SIMD.Int32x4.shiftRightArithmeticByScalar(v, 2), a.map(rsh(2)));
assertEqX4(SIMD.Int32x4.shiftRightArithmeticByScalar(v, 31), a.map(rsh(31)));
assertEqX4(SIMD.Int32x4.shiftRightArithmeticByScalar(v, 32), a.map(rsh(31)));
assertEqX4(SIMD.Int32x4.shiftRightLogicalByScalar(v, -1), a.map(ursh(-1)));
assertEqX4(SIMD.Int32x4.shiftRightLogicalByScalar(v, 0), a.map(ursh(0)));
assertEqX4(SIMD.Int32x4.shiftRightLogicalByScalar(v, 1), a.map(ursh(1)));
assertEqX4(SIMD.Int32x4.shiftRightLogicalByScalar(v, 2), a.map(ursh(2)));
assertEqX4(SIMD.Int32x4.shiftRightLogicalByScalar(v, 31), a.map(ursh(31)));
assertEqX4(SIMD.Int32x4.shiftRightLogicalByScalar(v, 32), a.map(ursh(32)));
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 33), a.map(lsh(33)));
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, -1), a.map(rsh(31)));
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 0), a.map(rsh(0)));
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 1), a.map(rsh(1)));
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 2), a.map(rsh(2)));
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 31), a.map(rsh(31)));
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 32), a.map(rsh(31)));
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 32), a.map(rsh(32)));
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 33), a.map(rsh(33)));
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, -1), b.map(ulsh(-1)));
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 0), b.map(ulsh(0)));
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 1), b.map(ulsh(1)));
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 2), b.map(ulsh(2)));
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 31), b.map(ulsh(31)));
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 32), b.map(ulsh(32)));
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 33), b.map(ulsh(33)));
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, -1), b.map(ursh(-1)));
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 0), b.map(ursh(0)));
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 1), b.map(ursh(1)));
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 2), b.map(ursh(2)));
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 31), b.map(ursh(31)));
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 32), b.map(ursh(32)));
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 33), b.map(ursh(33)));
// Non constant shift counts
var c = shifts[i % shifts.length];
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, c), a.map(lsh(c)));
assertEqX4(SIMD.Int32x4.shiftRightArithmeticByScalar(v, c), a.map(rsh(c)));
assertEqX4(SIMD.Int32x4.shiftRightLogicalByScalar(v, c), a.map(ursh(c)));
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, c), a.map(rsh(c)));
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, c), b.map(ulsh(c)));
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, c), b.map(ursh(c)));
}
return r;
}
+189 -15
View File
@@ -16,6 +16,15 @@ const CI32 = 'var ci4 = i4.check;'
const I32A = 'var i4a = i4.add;'
const I32S = 'var i4s = i4.sub;'
const I32M = 'var i4m = i4.mul;'
const I32U32 = 'var i4u4 = i4.fromUint32x4Bits;'
const U32 = 'var u4 = glob.SIMD.Uint32x4;'
const CU32 = 'var cu4 = u4.check;'
const U32A = 'var u4a = u4.add;'
const U32S = 'var u4s = u4.sub;'
const U32M = 'var u4m = u4.mul;'
const U32I32 = 'var u4i4 = u4.fromInt32x4Bits;'
const F32 = 'var f4 = glob.SIMD.Float32x4;'
const CF32 = 'var cf4 = f4.check;'
const F32A = 'var f4a = f4.add;'
@@ -27,6 +36,7 @@ const B32 = 'var b4 = glob.SIMD.Bool32x4;'
const CB32 = 'var cb4 = b4.check;'
const EXTI4 = 'var e = i4.extractLane;'
const EXTU4 = 'var e = u4.extractLane;'
const EXTF4 = 'var e = f4.extractLane;'
const EXTB4 = 'var e = b4.extractLane;'
@@ -36,6 +46,7 @@ const ALLB4 = 'var allt=b4.allTrue;'
const INT32_MAX = Math.pow(2, 31) - 1;
const INT32_MIN = INT32_MAX + 1 | 0;
const UINT32_MAX = Math.pow(2, 32) - 1;
const assertEqFFI = {assertEq:assertEq};
@@ -46,6 +57,15 @@ function CheckI4(header, code, expected) {
assertEqX4(observed, expected);
}
function CheckU4(header, code, expected) {
// code needs to contain a local called x.
header = USE_ASM + U32 + CU32 + EXTU4 + I32 + CI32 + I32U32 + header;
var observed = asmLink(asmCompile('glob', header + ';function f() {' + code + ';return ci4(i4u4(x))} return f'), this)();
// We can't return an unsigned SIMD type. Return Int32x4, convert to unsigned here.
observed = SIMD.Uint32x4.fromInt32x4Bits(observed)
assertEqX4(observed, expected);
}
function CheckF4(header, code, expected) {
// code needs to contain a local called x
header = USE_ASM + F32 + CF32 + EXTF4 + header;
@@ -176,6 +196,7 @@ assertAsmTypeFail('glob', USE_ASM + "function f() {var x=42; return x.signMask;}
assertAsmTypeFail('glob', USE_ASM + "function f() {var x=42.; return x.signMask;} return f");
assertAsmTypeFail('glob', USE_ASM + FROUND + "function f() {var x=f32(42.); return x.signMask;} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + 'function f() { var x=i4(1,2,3,4); return x.signMask | 0 } return f');
assertAsmTypeFail('glob', USE_ASM + U32 + 'function f() { var x=u4(1,2,3,4); return x.signMask | 0 } return f');
assertAsmTypeFail('glob', USE_ASM + F32 + FROUND + 'var Infinity = glob.Infinity; function f() { var x=f4(0,0,0,0); x=f4(f32(1), f32(-13.37), f32(42), f32(-Infinity)); return x.signMask | 0 } return f');
// Check lane extraction.
@@ -186,6 +207,11 @@ function CheckLanes(innerBody, type, expected) {
coerceBefore = '';
coerceAfter = '|0';
extractLane = 'ei';
} else if (type === SIMD.Uint32x4) {
// Coerce Uint32 lanes to double so they can be legally returned.
coerceBefore = '+';
coerceAfter = '';
extractLane = 'eu';
} else if (type === SIMD.Float32x4) {
coerceBefore = '+';
coerceAfter = '';
@@ -201,9 +227,11 @@ function CheckLanes(innerBody, type, expected) {
var lane = i;
var laneCheckCode = `"use asm";
var i4=glob.SIMD.Int32x4;
var u4=glob.SIMD.Uint32x4;
var f4=glob.SIMD.Float32x4;
var b4=glob.SIMD.Bool32x4;
var ei=i4.extractLane;
var eu=u4.extractLane;
var ef=f4.extractLane;
var eb=b4.extractLane;
function f() {${innerBody}; return ${coerceBefore}${extractLane}(x, ${lane})${coerceAfter} }
@@ -212,6 +240,7 @@ function CheckLanes(innerBody, type, expected) {
}
}
function CheckLanesI4(innerBody, expected) { return CheckLanes(innerBody, SIMD.Int32x4, expected); }
function CheckLanesU4(innerBody, expected) { return CheckLanes(innerBody, SIMD.Uint32x4, expected); }
function CheckLanesF4(innerBody, expected) { return CheckLanes(innerBody, SIMD.Float32x4, expected); }
function CheckLanesB4(innerBody, expected) { return CheckLanes(innerBody, SIMD.Bool32x4, expected); }
@@ -222,6 +251,13 @@ CheckLanesI4('var x=i4(1,2,3,4); var y=i4(5,6,7,8)', [1,2,3,4]);
CheckLanesI4('var a=1; var b=i4(9,8,7,6); var c=13.37; var x=i4(1,2,3,4); var y=i4(5,6,7,8)', [1,2,3,4]);
CheckLanesI4('var y=i4(5,6,7,8); var x=i4(1,2,3,4)', [1,2,3,4]);
CheckLanesU4('var x=u4(0,0,0,0);', [0,0,0,0]);
CheckLanesU4('var x=u4(1,2,3,4000000000);', [1,2,3,4000000000]);
CheckLanesU4('var x=u4(' + INT32_MIN + ',2,3,' + UINT32_MAX + ')', [INT32_MIN>>>0,2,3,UINT32_MAX]);
CheckLanesU4('var x=u4(1,2,3,4); var y=u4(5,6,7,8)', [1,2,3,4]);
CheckLanesU4('var a=1; var b=u4(9,8,7,6); var c=13.37; var x=u4(1,2,3,4); var y=u4(5,6,7,8)', [1,2,3,4]);
CheckLanesU4('var y=u4(5,6,7,8); var x=u4(1,2,3,4)', [1,2,3,4]);
CheckLanesF4('var x=f4(' + INT32_MAX + ', 2, 3, ' + INT32_MIN + ')', [INT32_MAX, 2, 3, INT32_MIN]);
CheckLanesF4('var x=f4(' + (INT32_MAX + 1) + ', 2, 3, 4)', [INT32_MAX + 1, 2, 3, 4]);
CheckLanesF4('var x=f4(1.3, 2.4, 3.5, 98.76)', [1.3, 2.4, 3.5, 98.76]);
@@ -253,6 +289,10 @@ CheckI4('', 'var x=i4(1,2,3,4); x=i4(5,6,7,8)', [5, 6, 7, 8]);
CheckI4('', 'var x=i4(1,2,3,4); var c=6; x=i4(5,c|0,7,8)', [5, 6, 7, 8]);
CheckI4('', 'var x=i4(8,7,6,5); x=i4(e(x,3)|0,e(x,2)|0,e(x,1)|0,e(x,0)|0)', [5, 6, 7, 8]);
CheckU4('', 'var x=u4(1,2,3,4); x=u4(5,6,7,4000000000)', [5, 6, 7, 4000000000]);
CheckU4('', 'var x=u4(1,2,3,4); var c=6; x=u4(5,c|0,7,8)', [5, 6, 7, 8]);
CheckU4('', 'var x=u4(8,7,6,5); x=u4(e(x,3)|0,e(x,2)|0,e(x,1)|0,e(x,0)|0)', [5, 6, 7, 8]);
assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4); var c=4; x=f4(1,2,3,c);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4); var c=4; x=f4(1.,2.,3.,c);} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4); var c=4.; x=f4(1,2,3,c);} return f");
@@ -281,6 +321,8 @@ assertAsmTypeFail('glob', USE_ASM + F32 + "function f() {var x=f4(1,2,3,4); var
assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "function f() {var x=f4(1,2,3,4); var y=i4(1,2,3,4); x=1?x:y;} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + I32 + "function f() {var x=f4(1,2,3,4); var y=i4(1,2,3,4); x=1?y:y;} return f");
assertAsmTypeFail('glob', USE_ASM + B32 + I32 + "function f() {var x=b4(1,2,3,4); var y=i4(1,2,3,4); x=1?y:y;} return f");
assertAsmTypeFail('glob', USE_ASM + U32 + I32 + "function f() {var x=u4(1,2,3,4); var y=i4(1,2,3,4); x=1?y:y;} return f");
assertAsmTypeFail('glob', USE_ASM + U32 + I32 + "function f() {var x=i4(1,2,3,4); var y=u4(1,2,3,4); x=1?y:y;} return f");
CheckF4('', 'var x=f4(1,2,3,4); var y=f4(4,3,2,1); x=3?y:x', [4, 3, 2, 1]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + "function f(x) {x=x|0; var v=f4(1,2,3,4); var w=f4(5,6,7,8); return cf4(x?w:v);} return f"), this)(1), [5,6,7,8]);
@@ -292,6 +334,10 @@ assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(x) {x=x
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(v) {v=ci4(v); var w=i4(5,6,7,8); return ci4(4?w:v);} return f"), this)(SIMD.Int32x4(1,2,3,4)), [5,6,7,8]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + "function f(v, x) {v=ci4(v); x=x|0; var w=i4(5,6,7,8); return ci4(x?w:v);} return f"), this)(SIMD.Int32x4(1,2,3,4), 0), [1,2,3,4]);
// Unsigned SIMD types can't be function arguments or return values.
assertAsmTypeFail('glob', USE_ASM + U32 + CU32 + "function f(x) {x=cu4(x);} return f");
assertAsmTypeFail('glob', USE_ASM + U32 + CU32 + "function f() {x=u4(0,0,0,0); return cu4(x);} return f");
// 1.3.4 Return values
assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {var x=1; return ci4(x)} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + "function f() {var x=1; return ci4(x + x)} return f");
@@ -395,11 +441,15 @@ assertAsmTypeFail('glob', USE_ASM + F32 + "var g=f4(1., 2., 3., 4.); var f32=glo
assertAsmTypeFail('glob', USE_ASM + F32 + I32 + CI32 + "var g=f4(1., 2., 3., 4.); function f() {var x=i4(1,2,3,4); x=ci4(g);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CF32 + "var g=i4(1,2,3,4); function f() {var x=f4(1.,2.,3.,4.); x=cf4(g);} return f");
assertAsmTypeFail('glob', USE_ASM + U32 + I32 + CI32 + "var g=u4(1,2,3,4); function f() {var x=i4(1,2,3,4); x=ci4(g);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "var g=0; function f() {var x=i4(1,2,3,4); x=g|0;} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "var g=0.; function f() {var x=i4(1,2,3,4); x=+g;} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + "var f32=glob.Math.fround; var g=f32(0.); function f() {var x=i4(1,2,3,4); x=f32(g);} return f");
// Unsigned SIMD globals are not allowed.
assertAsmTypeFail('glob', USE_ASM + U32 + "var g=u4(0,0,0,0); function f() {var x=u4(1,2,3,4); x=g;} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + "var g=0; function f() {var x=f4(0.,0.,0.,0.); x=g|0;} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + "var g=0.; function f() {var x=f4(0.,0.,0.,0.); x=+g;} return f");
assertAsmTypeFail('glob', USE_ASM + F32 + "var f32=glob.Math.fround; var g=f32(0.); function f() {var x=f4(0.,0.,0.,0.); x=f32(g);} return f");
@@ -447,6 +497,9 @@ assertEq(SIMD.Int32x4.extractLane(Int32x4, 3), 4);
for (var v of [1, {}, "totally legit SIMD variable", SIMD.Float32x4(1,2,3,4)])
assertCaught(asmCompile('glob', 'ffi', USE_ASM + I32 + CI32 + "var g=ci4(ffi.g); function f() {return ci4(g)} return f"), this, {g: v});
// Unsigned SIMD globals are not allowed.
assertAsmTypeFail('glob', 'ffi', USE_ASM + U32 + CU32 + "var g=cu4(ffi.g); function f() {} return f");
var Float32x4 = asmLink(asmCompile('glob', 'ffi', USE_ASM + F32 + CF32 + "var g=cf4(ffi.g); function f() {return cf4(g)} return f"), this, {g: SIMD.Float32x4(1,2,3,4)})();
assertEq(SIMD.Float32x4.extractLane(Float32x4, 0), 1);
assertEq(SIMD.Float32x4.extractLane(Float32x4, 1), 2);
@@ -492,6 +545,8 @@ assertAsmTypeFail('glob', USE_ASM + "var g = 3; var add = g.add; return {}");
assertAsmTypeFail('glob', USE_ASM + I32 + "var func = i4.doTheHarlemShake; return {}");
assertAsmTypeFail('glob', USE_ASM + I32 + "var div = i4.div; return {}");
assertAsmTypeFail('glob', USE_ASM + "var f32 = glob.Math.fround; var i4a = f32.add; return {}");
// Operation exists, but in a different type.
assertAsmTypeFail('glob', USE_ASM + I32 + "var func = i4.fromUint32x4; return {}");
// 2.2 Linking
assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + I32 + I32A + "function f() {} return f"), {});
@@ -519,6 +574,7 @@ assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4
assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); var y=4; x=i4a(x, y);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(0,0,0,0); var y=4; x=i4a(y, y);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(0,0,0,0); var y=4; y=i4a(x, x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + I32A + U32 + "function f() {var x=i4(0,0,0,0); var y=u4(1,2,3,4); y=i4a(x, y);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); x=i4a(x, y);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); y=i4a(x, y);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32A + "function f() {var x=i4(0,0,0,0); var y=f4(4,3,2,1); y=i4a(x, x);} return f");
@@ -535,6 +591,10 @@ CheckI4(I32A, 'var x=i4(1,2,3,4); x=i4a(x,x)', [2,4,6,8]);
CheckI4(I32A, 'var x=i4(' + INT32_MAX + ',2,3,4); var y=i4(1,1,0,3); x=i4a(x,y)', [INT32_MIN,3,3,7]);
CheckI4(I32A, 'var x=i4(' + INT32_MAX + ',2,3,4); var y=i4(1,1,0,3); x=ci4(i4a(x,y))', [INT32_MIN,3,3,7]);
CheckU4(U32A, 'var z=u4(1,2,3,4); var y=u4(0,1,0,3); var x=u4(0,0,0,0); x=u4a(z,y)', [1,3,3,7]);
CheckU4(U32A, 'var x=u4(2,3,4,5); var y=u4(0,1,0,3); x=u4a(x,y)', [2,4,4,8]);
CheckU4(U32A, 'var x=u4(1,2,3,4); x=u4a(x,x)', [2,4,6,8]);
CheckF4(F32A, 'var x=f4(1,2,3,4); x=f4a(x,x)', [2,4,6,8]);
CheckF4(F32A, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4a(x,y)', [5,5,8,6]);
CheckF4(F32A, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4a(x,y)', [Math.fround(13.37) + 4,5,8,6]);
@@ -547,6 +607,12 @@ CheckI4(I32S, 'var x=i4(1,2,3,4); x=i4s(x,x)', [0,0,0,0]);
CheckI4(I32S, 'var x=i4(' + INT32_MIN + ',2,3,4); var y=i4(1,1,0,3); x=i4s(x,y)', [INT32_MAX,1,3,1]);
CheckI4(I32S, 'var x=i4(' + INT32_MIN + ',2,3,4); var y=i4(1,1,0,3); x=ci4(i4s(x,y))', [INT32_MAX,1,3,1]);
CheckU4(U32S, 'var x=u4(1,2,3,4); var y=u4(-1,1,0,2); x=u4s(x,y)', [2,1,3,2]);
CheckU4(U32S, 'var x=u4(5,4,3,2); var y=u4(1,2,3,4); x=u4s(x,y)', [4,2,0,-2>>>0]);
CheckU4(U32S, 'var x=u4(1,2,3,4); x=u4s(x,x)', [0,0,0,0]);
CheckU4(U32S, 'var x=u4(' + INT32_MIN + ',2,3,4); var y=u4(1,1,0,3); x=u4s(x,y)', [INT32_MAX,1,3,1]);
CheckU4(U32S, 'var x=u4(' + INT32_MIN + ',2,3,4); var y=u4(1,1,0,3); x=cu4(u4s(x,y))', [INT32_MAX,1,3,1]);
CheckF4(F32S, 'var x=f4(1,2,3,4); x=f4s(x,x)', [0,0,0,0]);
CheckF4(F32S, 'var x=f4(1,2,3,4); var y=f4(4,3,5,2); x=f4s(x,y)', [-3,-1,-2,2]);
CheckF4(F32S, 'var x=f4(13.37,2,3,4); var y=f4(4,3,5,2); x=f4s(x,y)', [Math.fround(13.37) - 4,-1,-2,2]);
@@ -603,6 +669,16 @@ function CheckUnaryI4(op, checkFunc) {
}
}
function CheckUnaryU4(op, checkFunc) {
var _ = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32U32 + U32 + U32I32 +
'var op=u4.' + op + '; function f(x){x=ci4(x); return ci4(i4u4(op(u4i4(x)))); } return f'), this);
return function(input) {
var simd = SIMD.Int32x4(input[0], input[1], input[2], input[3]);
var res = SIMD.Uint32x4.fromInt32x4Bits(_(simd));
assertEqX4(res, input.map(checkFunc).map(function(x) { return x >>> 0 }));
}
}
function CheckUnaryB4(op, checkFunc) {
var _ = asmLink(asmCompile('glob', USE_ASM + B32 + CB32 + 'var op=b4.' + op + '; function f(x){x=cb4(x); return cb4(op(x)); } return f'), this);
return function(input) {
@@ -614,6 +690,9 @@ function CheckUnaryB4(op, checkFunc) {
CheckUnaryI4('neg', function(x) { return -x })([1, -2, INT32_MIN, INT32_MAX]);
CheckUnaryI4('not', function(x) { return ~x })([1, -2, INT32_MIN, INT32_MAX]);
CheckUnaryU4('neg', function(x) { return -x })([1, -2, INT32_MIN, INT32_MAX]);
CheckUnaryU4('not', function(x) { return ~x })([1, -2, INT32_MIN, INT32_MAX]);
var CheckNotB = CheckUnaryB4('not', function(x) { return !x });
CheckNotB([true, false, true, true]);
CheckNotB([true, true, true, true]);
@@ -714,6 +793,12 @@ CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 1, 42);', [1, 42, 3, 4]);
CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 2, 42);', [1, 2, 42, 4]);
CheckI4(RLI, 'var x = i4(1,2,3,4); x = r(x, 3, 42);', [1, 2, 3, 42]);
const RLU = 'var r = u4.replaceLane;';
CheckU4(RLU, 'var x = u4(1,2,3,4); x = r(x, 0, 42);', [42, 2, 3, 4]);
CheckU4(RLU, 'var x = u4(1,2,3,4); x = r(x, 1, 42);', [1, 42, 3, 4]);
CheckU4(RLU, 'var x = u4(1,2,3,4); x = r(x, 2, 42);', [1, 2, 42, 4]);
CheckU4(RLU, 'var x = u4(1,2,3,4); x = r(x, 3, 42);', [1, 2, 3, 42]);
const RLB = 'var r = b4.replaceLane;';
CheckB4(RLB, 'var x = b4(1,1,0,0); x = r(x, 0, 0);', [false, true, false, false]);
CheckB4(RLB, 'var x = b4(1,1,0,0); x = r(x, 1, 0);', [true, false, false, false]);
@@ -756,6 +841,37 @@ CheckB4(I32+GEI32, 'var x=b4(0,0,0,0); var a=i4(1,2,3,4); var b=i4(-1,1,0,2); x
CheckB4(I32+GEI32, 'var x=b4(0,0,0,0); var a=i4(-1,1,0,2); var b=i4(1,2,3,4); x=ge(a,b)', [F, F, F, F]);
CheckB4(I32+GEI32, 'var x=b4(0,0,0,0); var a=i4(1,0,3,4); var b=i4(1,1,7,0); x=ge(a,b)', [T, F, F, T]);
const EQU32 = 'var eq = u4.equal';
const NEU32 = 'var ne = u4.notEqual';
const LTU32 = 'var lt = u4.lessThan;';
const LEU32 = 'var le = u4.lessThanOrEqual';
const GTU32 = 'var gt = u4.greaterThan;';
const GEU32 = 'var ge = u4.greaterThanOrEqual';
CheckB4(U32+EQU32, 'var x=b4(0,0,0,0); var a=u4(1,2,3,4); var b=u4(-1,1,0,2); x=eq(a,b)', [F, F, F, F]);
CheckB4(U32+EQU32, 'var x=b4(0,0,0,0); var a=u4(-1,1,0,2); var b=u4(1,2,3,4); x=eq(a,b)', [F, F, F, F]);
CheckB4(U32+EQU32, 'var x=b4(0,0,0,0); var a=u4(1,0,3,4); var b=u4(1,1,7,0); x=eq(a,b)', [T, F, F, F]);
CheckB4(U32+NEU32, 'var x=b4(0,0,0,0); var a=u4(1,2,3,4); var b=u4(-1,1,0,2); x=ne(a,b)', [T, T, T, T]);
CheckB4(U32+NEU32, 'var x=b4(0,0,0,0); var a=u4(-1,1,0,2); var b=u4(1,2,3,4); x=ne(a,b)', [T, T, T, T]);
CheckB4(U32+NEU32, 'var x=b4(0,0,0,0); var a=u4(1,0,3,4); var b=u4(1,1,7,0); x=ne(a,b)', [F, T, T, T]);
CheckB4(U32+LTU32, 'var x=b4(0,0,0,0); var a=u4(1,2,3,4); var b=u4(-1,1,0,2); x=lt(a,b)', [T, F, F, F]);
CheckB4(U32+LTU32, 'var x=b4(0,0,0,0); var a=u4(-1,1,0,2); var b=u4(1,2,3,4); x=lt(a,b)', [F, T, T, T]);
CheckB4(U32+LTU32, 'var x=b4(0,0,0,0); var a=u4(1,0,3,4); var b=u4(1,1,7,0); x=lt(a,b)', [F, T, T, F]);
CheckB4(U32+LEU32, 'var x=b4(0,0,0,0); var a=u4(1,2,3,4); var b=u4(-1,1,0,2); x=le(a,b)', [T, F, F, F]);
CheckB4(U32+LEU32, 'var x=b4(0,0,0,0); var a=u4(-1,1,0,2); var b=u4(1,2,3,4); x=le(a,b)', [F, T, T, T]);
CheckB4(U32+LEU32, 'var x=b4(0,0,0,0); var a=u4(1,0,3,4); var b=u4(1,1,7,0); x=le(a,b)', [T, T, T, F]);
CheckB4(U32+GTU32, 'var x=b4(0,0,0,0); var a=u4(1,2,3,4); var b=u4(-1,1,0,2); x=gt(a,b)', [F, T, T, T]);
CheckB4(U32+GTU32, 'var x=b4(0,0,0,0); var a=u4(-1,1,0,2); var b=u4(1,2,3,4); x=gt(a,b)', [T, F, F, F]);
CheckB4(U32+GTU32, 'var x=b4(0,0,0,0); var a=u4(1,0,3,4); var b=u4(1,1,7,0); x=gt(a,b)', [F, F, F, T]);
CheckB4(U32+GEU32, 'var x=b4(0,0,0,0); var a=u4(1,2,3,4); var b=u4(-1,1,0,2); x=ge(a,b)', [F, T, T, T]);
CheckB4(U32+GEU32, 'var x=b4(0,0,0,0); var a=u4(-1,1,0,2); var b=u4(1,2,3,4); x=ge(a,b)', [T, F, F, F]);
CheckB4(U32+GEU32, 'var x=b4(0,0,0,0); var a=u4(1,0,3,4); var b=u4(1,1,7,0); x=ge(a,b)', [T, F, F, T]);
const LTF32 = 'var lt=f4.lessThan;';
const LEF32 = 'var le=f4.lessThanOrEqual;';
const GTF32 = 'var gt=f4.greaterThan;';
@@ -808,8 +924,13 @@ assertEq(f(SIMD.Int32x4(1,2,3,5)), 0);
// Conversions operators
const CVTIF = 'var cvt=f4.fromInt32x4;';
const CVTFI = 'var cvt=i4.fromFloat32x4;';
const CVTUF = 'var cvt=f4.fromUint32x4;';
const CVTFU = 'var cvt=u4.fromFloat32x4;';
assertAsmTypeFail('glob', USE_ASM + I32 + "var cvt=i4.fromInt32x4; return {}");
assertAsmTypeFail('glob', USE_ASM + I32 + "var cvt=i4.fromUint32x4; return {}");
assertAsmTypeFail('glob', USE_ASM + U32 + "var cvt=u4.fromInt32x4; return {}");
assertAsmTypeFail('glob', USE_ASM + U32 + "var cvt=u4.fromUint32x4; return {}");
assertAsmTypeFail('glob', USE_ASM + F32 + "var cvt=f4.fromFloat32x4; return {}");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=i4(1,2,3,4); x=cvt(x);} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=f4(1,2,3,4); x=cvt(x);} return f");
@@ -818,6 +939,11 @@ var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CF32 + CI32 + CVTIF + '
assertEqX4(f(SIMD.Int32x4(1,2,3,4)), [1, 2, 3, 4]);
assertEqX4(f(SIMD.Int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, Math.fround(INT32_MIN), Math.fround(INT32_MAX), -1]);
var f = asmLink(asmCompile('glob', USE_ASM + I32 + U32 + U32I32 + F32 + CF32 + CI32 + CVTUF +
'function f(x){x=ci4(x); var y=f4(0,0,0,0); y=cvt(u4i4(x)); return cf4(y);} return f'), this);
assertEqX4(f(SIMD.Int32x4(1,2,3,4)), [1, 2, 3, 4]);
assertEqX4(f(SIMD.Int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, Math.fround(INT32_MAX+1), Math.fround(INT32_MAX), Math.fround(UINT32_MAX)]);
var f = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + F32 + CF32 + CVTFI + 'function f(x){x=cf4(x); var y=i4(0,0,0,0); y=cvt(x); return ci4(y);} return f'), this);
assertEqX4(f(SIMD.Float32x4(1,2,3,4)), [1, 2, 3, 4]);
// Test that INT32_MIN (exactly representable as an float32) and the first
@@ -832,6 +958,20 @@ assertThrowsInstanceOf(() => f(SIMD.Float32x4(NaN, 0, 0, 0)), RangeError);
assertThrowsInstanceOf(() => f(SIMD.Float32x4(Infinity, 0, 0, 0)), RangeError);
assertThrowsInstanceOf(() => f(SIMD.Float32x4(-Infinity, 0, 0, 0)), RangeError);
var f = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + U32 + I32U32 + F32 + CF32 + CVTFU +
'function f(x){x=cf4(x); var y=u4(0,0,0,0); y=cvt(x); return ci4(i4u4(y));} return f'), this);
assertEqX4(f(SIMD.Float32x4(1,2,3,4)), [1, 2, 3, 4]);
// TODO: Test negative numbers > -1. They should truncate to 0. See https://github.com/tc39/ecmascript_simd/issues/315
assertEqX4(SIMD.Uint32x4.fromInt32x4Bits(f(SIMD.Float32x4(0xffffff00, INT32_MAX+1, -0, 0))),
[0xffffff00, INT32_MAX+1, 0, 0].map(Math.fround));
// Test boundaries: -1 or less.
assertThrowsInstanceOf(() => f(SIMD.Float32x4(-1, 0, 0, 0)), RangeError);
assertThrowsInstanceOf(() => f(SIMD.Float32x4(Math.pow(2, 32), 0, 0, 0)), RangeError);
// Special values
assertThrowsInstanceOf(() => f(SIMD.Float32x4(NaN, 0, 0, 0)), RangeError);
assertThrowsInstanceOf(() => f(SIMD.Float32x4(Infinity, 0, 0, 0)), RangeError);
assertThrowsInstanceOf(() => f(SIMD.Float32x4(-Infinity, 0, 0, 0)), RangeError);
// Cast operators
const CVTIFB = 'var cvt=f4.fromInt32x4Bits;';
const CVTFIB = 'var cvt=i4.fromFloat32x4Bits;';
@@ -886,6 +1026,14 @@ CheckI4(ANDI32, 'var x=i4(42,1337,-1,13); var y=i4(2, 4, 7, 15); x=andd(x,y)', [
CheckI4(ORI32, ' var x=i4(42,1337,-1,13); var y=i4(2, 4, 7, 15); x=orr(x,y)', [42 | 2, 1337 | 4, -1 | 7, 13 | 15]);
CheckI4(XORI32, 'var x=i4(42,1337,-1,13); var y=i4(2, 4, 7, 15); x=xorr(x,y)', [42 ^ 2, 1337 ^ 4, -1 ^ 7, 13 ^ 15]);
const ANDU32 = 'var andd=u4.and;';
const ORU32 = 'var orr=u4.or;';
const XORU32 = 'var xorr=u4.xor;';
CheckU4(ANDU32, 'var x=u4(42,1337,-1,13); var y=u4(2, 4, 7, 15); x=andd(x,y)', [42 & 2, 1337 & 4, (-1 & 7) >>> 0, 13 & 15]);
CheckU4(ORU32, ' var x=u4(42,1337,-1,13); var y=u4(2, 4, 7, 15); x=orr(x,y)', [42 | 2, 1337 | 4, (-1 | 7) >>> 0, 13 | 15]);
CheckU4(XORU32, 'var x=u4(42,1337,-1,13); var y=u4(2, 4, 7, 15); x=xorr(x,y)', [42 ^ 2, 1337 ^ 4, (-1 ^ 7) >>> 0, 13 ^ 15]);
const ANDB32 = 'var andd=b4.and;';
const ORB32 = 'var orr=b4.or;';
const XORB32 = 'var xorr=b4.xor;';
@@ -908,8 +1056,6 @@ assertAsmTypeFail('glob', USE_ASM + F32 + CF32 + NOTF32 + 'function f() {var x=f
// Logical ops
const LSHI = 'var lsh=i4.shiftLeftByScalar;'
const RSHI = 'var rsh=i4.shiftRightByScalar;'
const ARSHI = 'var arsh=i4.shiftRightArithmeticByScalar;'
const URSHI = 'var ursh=i4.shiftRightLogicalByScalar;'
assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + F32 + FROUND + LSHI + "function f() {var x=f4(1,2,3,4); return ci4(lsh(x,f32(42)));} return f");
assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + F32 + FROUND + LSHI + "function f() {var x=f4(1,2,3,4); return ci4(lsh(x,42));} return f");
@@ -919,33 +1065,49 @@ assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + FROUND + LSHI + "function f() {
var input = 'i4(0, 1, ' + INT32_MIN + ', ' + INT32_MAX + ')';
var vinput = [0, 1, INT32_MIN, INT32_MAX];
// TODO: What to do for masks > 31? Should we keep only the five low bits of
// the mask (JS) or not (x86)?
// Behave as x86 for now, fix when more broadly specified. See also bug 1068028
function Lsh(i) { if (i > 31) return () => 0; return function(x) { return (x << i) | 0 } }
function Rsh(i) { if (i > 31) return (x) => (x<0)?-1:0; return function(x) { return (x >> i) | 0 } }
function Arsh(i) { if (i > 31) return (x) => (x<0)?-1:0; return function(x) { return (x >> i) | 0 } }
function Ursh(i) { if (i > 31) return () => 0; return function(x) { return (x >>> i) | 0 } }
function Lsh(i) { return function(x) { return (x << (i & 31)) | 0 } }
function Rsh(i) { return function(x) { return (x >> (i & 31)) | 0 } }
var asmLsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + LSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(lsh(v, x+y))} return f;'), this)
var asmRsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + RSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(rsh(v, x+y))} return f;'), this)
var asmArsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + ARSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(arsh(v, x+y))} return f;'), this)
var asmUrsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + URSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(ursh(v, x+y))} return f;'), this)
for (var i = 1; i < 64; i++) {
CheckI4(LSHI, 'var x=' + input + '; x=lsh(x, ' + i + ')', vinput.map(Lsh(i)));
CheckI4(RSHI, 'var x=' + input + '; x=rsh(x, ' + i + ')', vinput.map(Rsh(i)));
CheckI4(ARSHI, 'var x=' + input + '; x=arsh(x, ' + i + ')', vinput.map(Arsh(i)));
CheckI4(URSHI, 'var x=' + input + '; x=ursh(x, ' + i + ')', vinput.map(Ursh(i)));
assertEqX4(asmLsh(i, 3), vinput.map(Lsh(i + 3)));
assertEqX4(asmRsh(i, 3), vinput.map(Rsh(i + 3)));
assertEqX4(asmArsh(i, 3), vinput.map(Arsh(i + 3)));
assertEqX4(asmUrsh(i, 3), vinput.map(Ursh(i + 3)));
}
// Same thing for Uint32x4.
const LSHU = 'var lsh=u4.shiftLeftByScalar;'
const RSHU = 'var rsh=u4.shiftRightByScalar;'
input = 'u4(0, 1, 0x80008000, ' + INT32_MAX + ')';
vinput = [0, 1, 0x80008000, INT32_MAX];
function uLsh(i) { return function(x) { return (x << (i & 31)) >>> 0 } }
function uRsh(i) { return function(x) { return (x >>> (i & 31)) } }
// Need to bitcast to Int32x4 before returning result.
asmLsh = asmLink(asmCompile('glob', USE_ASM + U32 + CU32 + LSHU + I32 + CI32 + I32U32 +
'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(i4u4(lsh(v, x+y)));} return f;'), this)
asmRsh = asmLink(asmCompile('glob', USE_ASM + U32 + CU32 + RSHU + I32 + CI32 + I32U32 +
'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(i4u4(rsh(v, x+y)));} return f;'), this)
for (var i = 1; i < 64; i++) {
// Constant shifts.
CheckU4(LSHU, 'var x=' + input + '; x=lsh(x, ' + i + ')', vinput.map(uLsh(i)));
CheckU4(RSHU, 'var x=' + input + '; x=rsh(x, ' + i + ')', vinput.map(uRsh(i)));
// Dynamically computed shifts. The asm function returns a Int32x4.
assertEqX4(SIMD.Uint32x4.fromInt32x4Bits(asmLsh(i, 3)), vinput.map(uLsh(i + 3)));
assertEqX4(SIMD.Uint32x4.fromInt32x4Bits(asmRsh(i, 3)), vinput.map(uRsh(i + 3)));
}
// Select
const I32SEL = 'var i4sel = i4.select;'
const U32SEL = 'var u4sel = u4.select;'
const F32SEL = 'var f4sel = f4.select;'
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + B32 + CI32 + I32SEL + "function f() {var x=f4(1,2,3,4); return ci4(i4sel(x,x,x));} return f");
@@ -975,8 +1137,14 @@ assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + F32 + CF32 + F32SEL
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + F32 + CF32 + F32SEL + "function f() {var m=b4(0,1,0,1); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + B32 + F32 + CF32 + F32SEL + "function f() {var m=b4(0,0,1,1); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return cf4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]);
CheckU4(B32 + U32SEL, "var m=b4(0,0,0,0); var x=u4(1,2,3,4); var y=u4(5,6,7,8); x=u4sel(m,x,y);", [5, 6, 7, 8]);
CheckU4(B32 + U32SEL, "var m=b4(1,1,1,1); var x=u4(1,2,3,4); var y=u4(5,6,7,8); x=u4sel(m,x,y);", [1, 2, 3, 4]);
CheckU4(B32 + U32SEL, "var m=b4(0,1,0,1); var x=u4(1,2,3,4); var y=u4(5,6,7,8); x=u4sel(m,x,y);", [5, 2, 7, 4]);
CheckU4(B32 + U32SEL, "var m=b4(0,0,1,1); var x=u4(1,2,3,4); var y=u4(5,6,7,8); x=u4sel(m,x,y);", [5, 6, 3, 4]);
// Splat
const I32SPLAT = 'var splat=i4.splat;'
const U32SPLAT = 'var splat=u4.splat;'
const F32SPLAT = 'var splat=f4.splat;'
const B32SPLAT = 'var splat=b4.splat;'
@@ -991,6 +1159,8 @@ assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + FROUND + "function f() {var
assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + I32SPLAT + 'function f(){return ci4(splat(42));} return f'), this)(), [42, 42, 42, 42]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + B32 + CB32 + B32SPLAT + 'function f(){return cb4(splat(42));} return f'), this)(), [true, true, true, true]);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + B32 + CB32 + B32SPLAT + 'function f(){return cb4(splat(0));} return f'), this)(), [false, false, false, false]);
CheckU4(B32 + U32SPLAT, "var x=u4(1,2,3,4); x=splat(0);", [0, 0, 0, 0]);
CheckU4(B32 + U32SPLAT, "var x=u4(1,2,3,4); x=splat(0xaabbccdd);", [0xaabbccdd, 0xaabbccdd, 0xaabbccdd, 0xaabbccdd]);
const l33t = Math.fround(13.37);
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + CF32 + F32SPLAT + FROUND + 'function f(){return cf4(splat(f32(1)));} return f'), this)(), [1, 1, 1, 1]);
@@ -1030,6 +1200,7 @@ var before = Date.now();
for (var i = 0; i < Math.pow(4, 4); i++) {
var lanes = [i & 3, (i >> 2) & 3, (i >> 4) & 3, (i >> 6) & 3];
CheckI4('var swizzle=i4.swizzle;', 'var x=i4(1,2,3,4); x=swizzle(x, ' + lanes.join(',') + ')', swizzle([1,2,3,4], lanes));
CheckU4('var swizzle=u4.swizzle;', 'var x=u4(1,2,3,4); x=swizzle(x, ' + lanes.join(',') + ')', swizzle([1,2,3,4], lanes));
CheckF4('var swizzle=f4.swizzle;', 'var x=f4(1,2,3,4); x=swizzle(x, ' + lanes.join(',') + ')', swizzle([1,2,3,4], lanes));
}
DEBUG && print('time for checking all swizzles:', Date.now() - before);
@@ -1102,6 +1273,7 @@ const LANE_SELECTORS = [
for (var lanes of LANE_SELECTORS) {
CheckI4('var shuffle=i4.shuffle;', 'var x=i4(1,2,3,4); var y=i4(5,6,7,8); x=shuffle(x, y, ' + lanes.join(',') + ')', shuffle([1,2,3,4], [5,6,7,8], lanes));
CheckU4('var shuffle=u4.shuffle;', 'var x=u4(1,2,3,4); var y=u4(5,6,7,8); x=shuffle(x, y, ' + lanes.join(',') + ')', shuffle([1,2,3,4], [5,6,7,8], lanes));
CheckF4('var shuffle=f4.shuffle;', 'var x=f4(1,2,3,4); var y=f4(5,6,7,8); x=shuffle(x, y, ' + lanes.join(',') + ')', shuffle([1,2,3,4], [5,6,7,8], lanes));
}
DEBUG && print('time for checking all shuffles:', Date.now() - before);
@@ -1121,11 +1293,13 @@ assertAsmTypeFail('glob', USE_ASM + I32 + "var pow=glob.Math.pow; function f() {
// 3.2. FFI calls
// Can't pass SIMD arguments to FFI
assertAsmTypeFail('glob', 'ffi', USE_ASM + I32 + "var func=ffi.func; function f() {var x=i4(1,2,3,4); func(x);} return f");
assertAsmTypeFail('glob', 'ffi', USE_ASM + U32 + "var func=ffi.func; function f() {var x=u4(1,2,3,4); func(x);} return f");
assertAsmTypeFail('glob', 'ffi', USE_ASM + F32 + "var func=ffi.func; function f() {var x=f4(1,2,3,4); func(x);} return f");
assertAsmTypeFail('glob', 'ffi', USE_ASM + B32 + "var func=ffi.func; function f() {var x=b4(1,2,3,4); func(x);} return f");
// Can't have FFI return SIMD values
assertAsmTypeFail('glob', 'ffi', USE_ASM + I32 + "var func=ffi.func; function f() {var x=i4(1,2,3,4); x=i4(func());} return f");
assertAsmTypeFail('glob', 'ffi', USE_ASM + U32 + "var func=ffi.func; function f() {var x=u4(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");
assertAsmTypeFail('glob', 'ffi', USE_ASM + B32 + "var func=ffi.func; function f() {var x=b4(1,2,3,4); x=b4(func());} return f");
+13 -12
View File
@@ -87,8 +87,11 @@ wasmEvalText('(module (func (result i32) (param i32) (i32.const 42)))');
wasmEvalText('(module (func (param f32)))');
wasmEvalText('(module (func (param f64)))');
assertErrorMessage(() => wasmEvalText('(module (func (param i64)))'), TypeError, /NYI/);
assertErrorMessage(() => wasmEvalText('(module (func (result i64)))'), TypeError, /NYI/);
var hasI64 = getBuildConfiguration().x64;
if (!hasI64) {
assertErrorMessage(() => wasmEvalText('(module (func (param i64)))'), TypeError, /NYI/);
assertErrorMessage(() => wasmEvalText('(module (func (result i64)))'), TypeError, /NYI/);
}
// ----------------------------------------------------------------------------
// imports
@@ -223,7 +226,8 @@ wasmEvalText('(module (func (local i32) (local f32) (set_local 1 (get_local 1)))
assertEq(wasmEvalText('(module (func (result i32) (local i32) (set_local 0 (i32.const 42))) (export "" 0))')(), 42);
assertEq(wasmEvalText('(module (func (result i32) (local i32) (set_local 0 (get_local 0))) (export "" 0))')(), 0);
assertErrorMessage(() => wasmEvalText('(module (func (local i64)))'), TypeError, /NYI/);
if (!hasI64)
assertErrorMessage(() => wasmEvalText('(module (func (local i64)))'), TypeError, /NYI/);
assertEq(wasmEvalText('(module (func (param $a i32) (result i32) (get_local $a)) (export "" 0))')(), 0);
assertEq(wasmEvalText('(module (func (param $a i32) (local $b i32) (result i32) (block (set_local $b (get_local $a)) (get_local $b))) (export "" 0))')(42), 42);
@@ -348,15 +352,12 @@ for (bad of [6, 7, 100, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1, Math
assertThrowsInstanceOf(() => i2v(bad, 0), RangeError);
}
// When the test below starts failing, remove it and uncomment the lines below!
assertErrorMessage(() => wasmEvalText('(module (func (param i64)))'), TypeError, /NYI/);
/*
assertErrorMessage(() => wasmEvalText('(module (func (param i64) (result i32) (i32.const 123)) (export "" 0))'), TypeError, /i64 argument/);
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i64) (i64.const 123)) (export "" 0))'), TypeError, /i64 return type/);
assertErrorMessage(() => wasmEvalText('(module (import "a" "" (param i64) (result i32)))'), TypeError, /i64 argument/);
assertErrorMessage(() => wasmEvalText('(module (import "a" "" (result i64)))'), TypeError, /i64 return type/);
*/
if (hasI64) {
assertErrorMessage(() => wasmEvalText('(module (func (param i64) (result i32) (i32.const 123)) (export "" 0))'), TypeError, /i64 argument/);
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result i64) (i64.const 123)) (export "" 0))'), TypeError, /i64 return type/);
assertErrorMessage(() => wasmEvalText('(module (import "a" "" (param i64) (result i32)))'), TypeError, /i64 argument/);
assertErrorMessage(() => wasmEvalText('(module (import "a" "" (result i64)))'), TypeError, /i64 return type/);
}
var {v2i, i2i, i2v} = wasmEvalText(`(module
(type $a (func (result i32)))
+11 -5
View File
@@ -2490,6 +2490,12 @@ CodeGenerator::visitInteger(LInteger* lir)
masm.move32(Imm32(lir->getValue()), ToRegister(lir->output()));
}
void
CodeGenerator::visitInteger64(LInteger64* lir)
{
masm.move64(Imm64(lir->getValue()), ToOutRegister64(lir));
}
void
CodeGenerator::visitPointer(LPointer* lir)
{
@@ -9312,23 +9318,23 @@ CodeGenerator::visitToIdV(LToIdV* lir)
Label notInt32;
FloatRegister temp = ToFloatRegister(lir->tempFloat());
const ValueOperand out = ToOutValue(lir);
ValueOperand index = ToValue(lir, LToIdV::Index);
ValueOperand input = ToValue(lir, LToIdV::Input);
OutOfLineCode* ool = oolCallVM(ToIdInfo, lir,
ArgList(ImmGCPtr(current->mir()->info().script()),
ImmPtr(lir->mir()->resumePoint()->pc()),
ToValue(lir, LToIdV::Index)),
ToValue(lir, LToIdV::Input)),
StoreValueTo(out));
Register tag = masm.splitTagForTest(index);
Register tag = masm.splitTagForTest(input);
masm.branchTestInt32(Assembler::NotEqual, tag, &notInt32);
masm.moveValue(index, out);
masm.moveValue(input, out);
masm.jump(ool->rejoin());
masm.bind(&notInt32);
masm.branchTestDouble(Assembler::NotEqual, tag, ool->entry());
masm.unboxDouble(index, temp);
masm.unboxDouble(input, temp);
masm.convertDoubleToInt32(temp, out.scratchReg(), ool->entry(), true);
masm.tagValue(JSVAL_TYPE_INT32, out.scratchReg(), out);
+1
View File
@@ -106,6 +106,7 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitValueToString(LValueToString* lir);
void visitValueToObjectOrNull(LValueToObjectOrNull* lir);
void visitInteger(LInteger* lir);
void visitInteger64(LInteger64* lir);
void visitRegExp(LRegExp* lir);
void visitRegExpMatcher(LRegExpMatcher* lir);
void visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool);
+57
View File
@@ -50,6 +50,8 @@ static const uint32_t PAYLOAD_INDEX = 1;
# error "Unknown!"
#endif
static const uint32_t INT64_PIECES = sizeof(int64_t) / sizeof(uintptr_t);
// Represents storage for an operand. For constants, the pointer is tagged
// with a single bit, and the untagged pointer is a pointer to a Value.
class LAllocation : public TempObject
@@ -291,6 +293,50 @@ class LUse : public LAllocation
static const uint32_t MAX_VIRTUAL_REGISTERS = LUse::VREG_MASK;
class LBoxAllocation
{
#ifdef JS_NUNBOX32
LAllocation type_;
LAllocation payload_;
#else
LAllocation value_;
#endif
public:
#ifdef JS_NUNBOX32
LBoxAllocation(LAllocation type, LAllocation payload) : type_(type), payload_(payload) {}
LAllocation type() const { return type_; }
LAllocation payload() const { return payload_; }
#else
explicit LBoxAllocation(LAllocation value) : value_(value) {}
LAllocation value() const { return value_; }
#endif
};
class LInt64Allocation
{
#if JS_BITS_PER_WORD == 32
LAllocation high_;
LAllocation low_;
#else
LAllocation value_;
#endif
public:
#if JS_BITS_PER_WORD == 32
LInt64Allocation(LAllocation high, LAllocation low) : high_(high), low_(low) {}
LAllocation high() const { return high_; }
LAllocation low() const { return low_; }
#else
explicit LInt64Allocation(LAllocation value) : value_(value) {}
LAllocation value() const { return value_; }
#endif
};
class LGeneralReg : public LAllocation
{
public:
@@ -567,6 +613,9 @@ class LDefinition
case MIRType_Elements:
return LDefinition::SLOTS;
case MIRType_Pointer:
#if JS_BITS_PER_WORD == 64
case MIRType_Int64:
#endif
return LDefinition::GENERAL;
case MIRType_Bool32x4:
case MIRType_Int32x4:
@@ -1050,6 +1099,14 @@ class LInstructionHelper : public details::LInstructionFixedDefsTempsHelper<Defs
void setOperand(size_t index, const LAllocation& a) final override {
operands_[index] = a;
}
void setBoxOperand(size_t index, const LBoxAllocation& alloc) {
#ifdef JS_NUNBOX32
operands_[index] = alloc.type();
operands_[index + 1] = alloc.payload();
#else
operands_[index] = alloc.value();
#endif
}
};
template<size_t Defs, size_t Temps>
+135 -184
View File
@@ -24,22 +24,22 @@ using namespace jit;
using mozilla::DebugOnly;
using JS::GenericNaN;
void
LIRGenerator::useBoxAtStart(LInstruction* lir, size_t n, MDefinition* mir, LUse::Policy policy)
{
return useBox(lir, n, mir, policy, true);
}
void
LIRGenerator::useBoxFixedAtStart(LInstruction* lir, size_t n, MDefinition* mir, ValueOperand op)
LBoxAllocation
LIRGenerator::useBoxFixedAtStart(MDefinition* mir, ValueOperand op)
{
#if defined(JS_NUNBOX32)
return useBoxFixed(lir, n, mir, op.typeReg(), op.payloadReg(), true);
return useBoxFixed(mir, op.typeReg(), op.payloadReg(), true);
#elif defined(JS_PUNBOX64)
return useBoxFixed(lir, n, mir, op.valueReg(), op.scratchReg(), true);
return useBoxFixed(mir, op.valueReg(), op.scratchReg(), true);
#endif
}
LBoxAllocation
LIRGenerator::useBoxAtStart(MDefinition* mir, LUse::Policy policy)
{
return useBox(mir, policy, /* useAtStart = */ true);
}
void
LIRGenerator::visitCloneLiteral(MCloneLiteral* ins)
{
@@ -155,7 +155,6 @@ LIRGenerator::visitTableSwitch(MTableSwitch* tableswitch)
// If we don't know the type.
if (opd->type() == MIRType_Value) {
LTableSwitchV* lir = newLTableSwitchV(tableswitch);
useBox(lir, LTableSwitchV::InputValue, opd);
add(lir);
return;
}
@@ -312,9 +311,9 @@ LIRGenerator::visitNewStringObject(MNewStringObject* ins)
void
LIRGenerator::visitInitElem(MInitElem* ins)
{
LInitElem* lir = new(alloc()) LInitElem(useRegisterAtStart(ins->getObject()));
useBoxAtStart(lir, LInitElem::IdIndex, ins->getId());
useBoxAtStart(lir, LInitElem::ValueIndex, ins->getValue());
LInitElem* lir = new(alloc()) LInitElem(useRegisterAtStart(ins->getObject()),
useBoxAtStart(ins->getId()),
useBoxAtStart(ins->getValue()));
add(lir, ins);
assignSafepoint(lir, ins);
}
@@ -324,8 +323,8 @@ LIRGenerator::visitInitElemGetterSetter(MInitElemGetterSetter* ins)
{
LInitElemGetterSetter* lir =
new(alloc()) LInitElemGetterSetter(useRegisterAtStart(ins->object()),
useBoxAtStart(ins->idValue()),
useRegisterAtStart(ins->value()));
useBoxAtStart(lir, LInitElemGetterSetter::IdIndex, ins->idValue());
add(lir, ins);
assignSafepoint(lir, ins);
}
@@ -333,8 +332,8 @@ LIRGenerator::visitInitElemGetterSetter(MInitElemGetterSetter* ins)
void
LIRGenerator::visitMutateProto(MMutateProto* ins)
{
LMutateProto* lir = new(alloc()) LMutateProto(useRegisterAtStart(ins->getObject()));
useBoxAtStart(lir, LMutateProto::ValueIndex, ins->getValue());
LMutateProto* lir = new(alloc()) LMutateProto(useRegisterAtStart(ins->getObject()),
useBoxAtStart(ins->getValue()));
add(lir, ins);
assignSafepoint(lir, ins);
}
@@ -342,8 +341,8 @@ LIRGenerator::visitMutateProto(MMutateProto* ins)
void
LIRGenerator::visitInitProp(MInitProp* ins)
{
LInitProp* lir = new(alloc()) LInitProp(useRegisterAtStart(ins->getObject()));
useBoxAtStart(lir, LInitProp::ValueIndex, ins->getValue());
LInitProp* lir = new(alloc()) LInitProp(useRegisterAtStart(ins->getObject()),
useBoxAtStart(ins->getValue()));
add(lir, ins);
assignSafepoint(lir, ins);
}
@@ -408,16 +407,16 @@ void
LIRGenerator::visitSetArgumentsObjectArg(MSetArgumentsObjectArg* ins)
{
LAllocation argsObj = useRegister(ins->getArgsObject());
LSetArgumentsObjectArg* lir = new(alloc()) LSetArgumentsObjectArg(argsObj, temp());
useBox(lir, LSetArgumentsObjectArg::ValueIndex, ins->getValue());
LSetArgumentsObjectArg* lir =
new(alloc()) LSetArgumentsObjectArg(argsObj, useBox(ins->getValue()), temp());
add(lir, ins);
}
void
LIRGenerator::visitReturnFromCtor(MReturnFromCtor* ins)
{
LReturnFromCtor* lir = new(alloc()) LReturnFromCtor(useRegister(ins->getObject()));
useBox(lir, LReturnFromCtor::ValueIndex, ins->getValue());
LReturnFromCtor* lir = new(alloc()) LReturnFromCtor(useBox(ins->getValue()),
useRegister(ins->getObject()));
define(lir, ins);
}
@@ -427,13 +426,10 @@ LIRGenerator::visitComputeThis(MComputeThis* ins)
MOZ_ASSERT(ins->type() == MIRType_Value);
MOZ_ASSERT(ins->input()->type() == MIRType_Value);
LComputeThis* lir = new(alloc()) LComputeThis();
// Don't use useBoxAtStart because ComputeThis has a safepoint and needs to
// have its inputs in different registers than its return value so that
// they aren't clobbered.
useBox(lir, LComputeThis::ValueIndex, ins->input());
LComputeThis* lir = new(alloc()) LComputeThis(useBox(ins->input()));
defineBox(lir, ins);
assignSafepoint(lir, ins);
}
@@ -472,8 +468,7 @@ LIRGenerator::lowerCallArguments(MCall* call)
// Values take a slow path.
if (arg->type() == MIRType_Value) {
LStackArgV* stack = new(alloc()) LStackArgV(argslot);
useBox(stack, 0, arg);
LStackArgV* stack = new(alloc()) LStackArgV(argslot, useBox(arg));
add(stack);
} else {
// Known types can move constant types and/or payloads.
@@ -554,12 +549,10 @@ LIRGenerator::visitApplyArgs(MApplyArgs* apply)
LApplyArgsGeneric* lir = new(alloc()) LApplyArgsGeneric(
useFixed(apply->getFunction(), CallTempReg3),
useFixed(apply->getArgc(), CallTempReg0),
useBoxFixed(apply->getThis(), CallTempReg4, CallTempReg5),
tempFixed(CallTempReg1), // object register
tempFixed(CallTempReg2)); // stack counter register
MDefinition* self = apply->getThis();
useBoxFixed(lir, LApplyArgsGeneric::ThisIndex, self, CallTempReg4, CallTempReg5);
// Bailout is only needed in the case of possible non-JSFunction callee.
if (!apply->getSingleTarget())
assignSnapshot(lir, Bailout_NonJSFunctionCallee);
@@ -582,15 +575,13 @@ LIRGenerator::visitApplyArray(MApplyArray* apply)
MOZ_ASSERT(CallTempReg2 != JSReturnReg_Data);
LApplyArrayGeneric* lir = new(alloc()) LApplyArrayGeneric(
useFixed(apply->getFunction(), CallTempReg3),
useFixed(apply->getElements(), CallTempReg0),
useFixedAtStart(apply->getFunction(), CallTempReg3),
useFixedAtStart(apply->getElements(), CallTempReg0),
useBoxFixed(apply->getThis(), CallTempReg4, CallTempReg5),
tempFixed(CallTempReg1), // object register
tempFixed(CallTempReg2)); // stack counter register
MDefinition* self = apply->getThis();
useBoxFixed(lir, LApplyArrayGeneric::ThisIndex, self, CallTempReg4, CallTempReg5);
// Bailout is needed in the case of possible non-JSFunction callee
// Bailout is needed in the case of possible non-JSFunction callee,
// too many values in the array, or empty space at the end of the
// array. I'm going to use NonJSFunctionCallee for the code even
// if that is not an adequate description.
@@ -682,9 +673,8 @@ LIRGenerator::visitCallDirectEval(MCallDirectEval* ins)
MDefinition* newTargetValue = ins->getNewTargetValue();
LInstruction* lir = new(alloc()) LCallDirectEval(useRegisterAtStart(scopeChain),
useRegisterAtStart(string));
useBoxAtStart(lir, LCallDirectEval::NewTarget, newTargetValue);
useRegisterAtStart(string),
useBoxAtStart(newTargetValue));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -733,8 +723,7 @@ LIRGenerator::visitTest(MTest* test)
temp1 = LDefinition::BogusTemp();
}
LTestVAndBranch* lir =
new(alloc()) LTestVAndBranch(ifTrue, ifFalse, tempDouble(), temp0, temp1);
useBox(lir, LTestVAndBranch::Input, opd);
new(alloc()) LTestVAndBranch(ifTrue, ifFalse, useBox(opd), tempDouble(), temp0, temp1);
add(lir, test);
return;
}
@@ -814,9 +803,8 @@ LIRGenerator::visitTest(MTest* test)
}
LIsNullOrLikeUndefinedAndBranchV* lir =
new(alloc()) LIsNullOrLikeUndefinedAndBranchV(comp, ifTrue, ifFalse,
new(alloc()) LIsNullOrLikeUndefinedAndBranchV(comp, ifTrue, ifFalse, useBox(left),
tmp, tmpToUnbox);
useBox(lir, LIsNullOrLikeUndefinedAndBranchV::Value, left);
add(lir, test);
return;
}
@@ -826,9 +814,9 @@ LIRGenerator::visitTest(MTest* test)
MOZ_ASSERT(left->type() == MIRType_Value);
MOZ_ASSERT(right->type() == MIRType_Boolean);
LAllocation rhs = useRegisterOrConstant(right);
LCompareBAndBranch* lir = new(alloc()) LCompareBAndBranch(comp, rhs, ifTrue, ifFalse);
useBox(lir, LCompareBAndBranch::Lhs, left);
LCompareBAndBranch* lir = new(alloc()) LCompareBAndBranch(comp, useBox(left),
useRegisterOrConstant(right),
ifTrue, ifFalse);
add(lir, test);
return;
}
@@ -874,9 +862,9 @@ LIRGenerator::visitTest(MTest* test)
// Compare values.
if (comp->compareType() == MCompare::Compare_Bitwise) {
LCompareBitwiseAndBranch* lir =
new(alloc()) LCompareBitwiseAndBranch(comp, ifTrue, ifFalse);
useBoxAtStart(lir, LCompareBitwiseAndBranch::LhsInput, left);
useBoxAtStart(lir, LCompareBitwiseAndBranch::RhsInput, right);
new(alloc()) LCompareBitwiseAndBranch(comp, ifTrue, ifFalse,
useBoxAtStart(left),
useBoxAtStart(right));
add(lir, test);
return;
}
@@ -898,8 +886,8 @@ LIRGenerator::visitTest(MTest* test)
MDefinition* input = opd->toIsObject()->input();
MOZ_ASSERT(input->type() == MIRType_Value);
LIsObjectAndBranch* lir = new(alloc()) LIsObjectAndBranch(ifTrue, ifFalse);
useBoxAtStart(lir, LIsObjectAndBranch::Input, input);
LIsObjectAndBranch* lir = new(alloc()) LIsObjectAndBranch(ifTrue, ifFalse,
useBoxAtStart(input));
add(lir, test);
return;
}
@@ -910,8 +898,8 @@ LIRGenerator::visitTest(MTest* test)
MDefinition* input = opd->toIsNoIter()->input();
MOZ_ASSERT(input->type() == MIRType_Value);
LIsNoIterAndBranch* lir = new(alloc()) LIsNoIterAndBranch(ifTrue, ifFalse);
useBox(lir, LIsNoIterAndBranch::Input, input);
LIsNoIterAndBranch* lir = new(alloc()) LIsNoIterAndBranch(ifTrue, ifFalse,
useBox(input));
add(lir, test);
return;
}
@@ -1000,8 +988,8 @@ LIRGenerator::visitCompare(MCompare* comp)
MOZ_ASSERT(left->type() == MIRType_Value);
MOZ_ASSERT(right->type() == MIRType_String);
LCompareStrictS* lir = new(alloc()) LCompareStrictS(useRegister(right), tempToUnbox());
useBox(lir, LCompareStrictS::Lhs, left);
LCompareStrictS* lir = new(alloc()) LCompareStrictS(useBox(left), useRegister(right),
tempToUnbox());
define(lir, comp);
assignSafepoint(lir, comp);
return;
@@ -1009,9 +997,7 @@ LIRGenerator::visitCompare(MCompare* comp)
// Unknown/unspecialized compare use a VM call.
if (comp->compareType() == MCompare::Compare_Unknown) {
LCompareVM* lir = new(alloc()) LCompareVM();
useBoxAtStart(lir, LCompareVM::LhsInput, left);
useBoxAtStart(lir, LCompareVM::RhsInput, right);
LCompareVM* lir = new(alloc()) LCompareVM(useBoxAtStart(left), useBoxAtStart(right));
defineReturn(lir, comp);
assignSafepoint(lir, comp);
return;
@@ -1048,8 +1034,8 @@ LIRGenerator::visitCompare(MCompare* comp)
tmpToUnbox = LDefinition::BogusTemp();
}
LIsNullOrLikeUndefinedV* lir = new(alloc()) LIsNullOrLikeUndefinedV(tmp, tmpToUnbox);
useBox(lir, LIsNullOrLikeUndefinedV::Value, left);
LIsNullOrLikeUndefinedV* lir = new(alloc()) LIsNullOrLikeUndefinedV(useBox(left),
tmp, tmpToUnbox);
define(lir, comp);
return;
}
@@ -1059,8 +1045,7 @@ LIRGenerator::visitCompare(MCompare* comp)
MOZ_ASSERT(left->type() == MIRType_Value);
MOZ_ASSERT(right->type() == MIRType_Boolean);
LCompareB* lir = new(alloc()) LCompareB(useRegisterOrConstant(right));
useBox(lir, LCompareB::Lhs, left);
LCompareB* lir = new(alloc()) LCompareB(useBox(left), useRegisterOrConstant(right));
define(lir, comp);
return;
}
@@ -1098,9 +1083,8 @@ LIRGenerator::visitCompare(MCompare* comp)
// Compare values.
if (comp->compareType() == MCompare::Compare_Bitwise) {
LCompareBitwise* lir = new(alloc()) LCompareBitwise();
useBoxAtStart(lir, LCompareBitwise::LhsInput, left);
useBoxAtStart(lir, LCompareBitwise::RhsInput, right);
LCompareBitwise* lir = new(alloc()) LCompareBitwise(useBoxAtStart(left),
useBoxAtStart(right));
define(lir, comp);
return;
}
@@ -1120,9 +1104,7 @@ LIRGenerator::lowerBitOp(JSOp op, MInstruction* ins)
return;
}
LBitOpV* lir = new(alloc()) LBitOpV(op);
useBoxAtStart(lir, LBitOpV::LhsInput, lhs);
useBoxAtStart(lir, LBitOpV::RhsInput, rhs);
LBitOpV* lir = new(alloc()) LBitOpV(op, useBoxAtStart(lhs), useBoxAtStart(rhs));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -1133,16 +1115,14 @@ LIRGenerator::visitTypeOf(MTypeOf* ins)
MDefinition* opd = ins->input();
MOZ_ASSERT(opd->type() == MIRType_Value);
LTypeOfV* lir = new(alloc()) LTypeOfV(tempToUnbox());
useBox(lir, LTypeOfV::Input, opd);
LTypeOfV* lir = new(alloc()) LTypeOfV(useBox(opd), tempToUnbox());
define(lir, ins);
}
void
LIRGenerator::visitToId(MToId* ins)
{
LToIdV* lir = new(alloc()) LToIdV(tempDouble());
useBox(lir, LToIdV::Index, ins->input());
LToIdV* lir = new(alloc()) LToIdV(useBox(ins->input()), tempDouble());
defineBox(lir, ins);
assignSafepoint(lir, ins);
}
@@ -1157,8 +1137,7 @@ LIRGenerator::visitBitNot(MBitNot* ins)
return;
}
LBitNotV* lir = new(alloc()) LBitNotV;
useBoxAtStart(lir, LBitNotV::Input, input);
LBitNotV* lir = new(alloc()) LBitNotV(useBoxAtStart(input));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -1242,9 +1221,7 @@ LIRGenerator::lowerShiftOp(JSOp op, MShiftInstruction* ins)
return;
}
LBitOpV* lir = new(alloc()) LBitOpV(op);
useBoxAtStart(lir, LBitOpV::LhsInput, lhs);
useBoxAtStart(lir, LBitOpV::RhsInput, rhs);
LBitOpV* lir = new(alloc()) LBitOpV(op, useBoxAtStart(lhs), useBoxAtStart(rhs));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -1676,9 +1653,7 @@ LIRGenerator::lowerBinaryV(JSOp op, MBinaryInstruction* ins)
MOZ_ASSERT(lhs->type() == MIRType_Value);
MOZ_ASSERT(rhs->type() == MIRType_Value);
LBinaryV* lir = new(alloc()) LBinaryV(op);
useBoxAtStart(lir, LBinaryV::LhsInput, lhs);
useBoxAtStart(lir, LBinaryV::RhsInput, rhs);
LBinaryV* lir = new(alloc()) LBinaryV(op, useBoxAtStart(lhs), useBoxAtStart(rhs));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -1797,8 +1772,7 @@ LIRGenerator::visitToDouble(MToDouble* convert)
switch (opd->type()) {
case MIRType_Value:
{
LValueToDouble* lir = new(alloc()) LValueToDouble();
useBox(lir, LValueToDouble::Input, opd);
LValueToDouble* lir = new(alloc()) LValueToDouble(useBox(opd));
assignSnapshot(lir, Bailout_NonPrimitiveInput);
define(lir, convert);
break;
@@ -1853,8 +1827,7 @@ LIRGenerator::visitToFloat32(MToFloat32* convert)
switch (opd->type()) {
case MIRType_Value:
{
LValueToFloat32* lir = new(alloc()) LValueToFloat32();
useBox(lir, LValueToFloat32::Input, opd);
LValueToFloat32* lir = new(alloc()) LValueToFloat32(useBox(opd));
assignSnapshot(lir, Bailout_NonPrimitiveInput);
define(lir, convert);
break;
@@ -1909,8 +1882,7 @@ LIRGenerator::visitToInt32(MToInt32* convert)
case MIRType_Value:
{
LValueToInt32* lir =
new(alloc()) LValueToInt32(tempDouble(), temp(), LValueToInt32::NORMAL);
useBox(lir, LValueToInt32::Input, opd);
new(alloc()) LValueToInt32(useBox(opd), tempDouble(), temp(), LValueToInt32::NORMAL);
assignSnapshot(lir, Bailout_NonPrimitiveInput);
define(lir, convert);
assignSafepoint(lir, convert);
@@ -1968,9 +1940,8 @@ LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate)
switch (opd->type()) {
case MIRType_Value:
{
LValueToInt32* lir = new(alloc()) LValueToInt32(tempDouble(), temp(),
LValueToInt32* lir = new(alloc()) LValueToInt32(useBox(opd), tempDouble(), temp(),
LValueToInt32::TRUNCATE);
useBox(lir, LValueToInt32::Input, opd);
assignSnapshot(lir, Bailout_NonPrimitiveInput);
define(lir, truncate);
assignSafepoint(lir, truncate);
@@ -2049,8 +2020,7 @@ LIRGenerator::visitToString(MToString* ins)
break;
case MIRType_Value: {
LValueToString* lir = new(alloc()) LValueToString(tempToUnbox());
useBox(lir, LValueToString::Input, opd);
LValueToString* lir = new(alloc()) LValueToString(useBox(opd), tempToUnbox());
if (ins->fallible())
assignSnapshot(lir, Bailout_NonPrimitiveInput);
define(lir, ins);
@@ -2069,8 +2039,7 @@ LIRGenerator::visitToObjectOrNull(MToObjectOrNull* ins)
{
MOZ_ASSERT(ins->input()->type() == MIRType_Value);
LValueToObjectOrNull* lir = new(alloc()) LValueToObjectOrNull();
useBox(lir, LValueToString::Input, ins->input());
LValueToObjectOrNull* lir = new(alloc()) LValueToObjectOrNull(useBox(ins->input()));
define(lir, ins);
assignSafepoint(lir, ins);
}
@@ -2220,11 +2189,8 @@ LIRGenerator::visitBinarySharedStub(MBinarySharedStub* ins)
MOZ_ASSERT(ins->type() == MIRType_Value);
MOZ_ASSERT(ins->type() == MIRType_Value);
LBinarySharedStub* lir = new(alloc()) LBinarySharedStub();
useBoxFixedAtStart(lir, LBinarySharedStub::LhsInput, lhs, R0);
useBoxFixedAtStart(lir, LBinarySharedStub::RhsInput, rhs, R1);
LBinarySharedStub* lir = new(alloc()) LBinarySharedStub(useBoxFixedAtStart(lhs, R0),
useBoxFixedAtStart(rhs, R1));
defineSharedStubReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -2235,10 +2201,7 @@ LIRGenerator::visitUnarySharedStub(MUnarySharedStub* ins)
MDefinition* input = ins->getOperand(0);
MOZ_ASSERT(ins->type() == MIRType_Value);
LUnarySharedStub* lir = new(alloc()) LUnarySharedStub();
useBoxFixedAtStart(lir, LUnarySharedStub::Input, input, R0);
LUnarySharedStub* lir = new(alloc()) LUnarySharedStub(useBoxFixedAtStart(input, R0));
defineSharedStubReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -2268,8 +2231,8 @@ LIRGenerator::visitLambdaArrow(MLambdaArrow* ins)
MOZ_ASSERT(ins->scopeChain()->type() == MIRType_Object);
MOZ_ASSERT(ins->newTargetDef()->type() == MIRType_Value);
LLambdaArrow* lir = new(alloc()) LLambdaArrow(useRegister(ins->scopeChain()));
useBox(lir, LLambdaArrow::NewTargetValue, ins->newTargetDef());
LLambdaArrow* lir = new(alloc()) LLambdaArrow(useRegister(ins->scopeChain()),
useBox(ins->newTargetDef()));
define(lir, ins);
assignSafepoint(lir, ins);
}
@@ -2377,8 +2340,7 @@ LIRGenerator::visitStoreSlot(MStoreSlot* ins)
switch (ins->value()->type()) {
case MIRType_Value:
lir = new(alloc()) LStoreSlotV(useRegister(ins->slots()));
useBox(lir, LStoreSlotV::Value, ins->value());
lir = new(alloc()) LStoreSlotV(useRegister(ins->slots()), useBox(ins->value()));
add(lir, ins);
break;
@@ -2429,8 +2391,7 @@ LIRGenerator::visitTypeBarrier(MTypeBarrier* ins)
// Handle typebarrier with Value as input.
if (inputType == MIRType_Value) {
LDefinition tmp = needTemp ? temp() : tempToUnbox();
LTypeBarrierV* barrier = new(alloc()) LTypeBarrierV(tmp);
useBox(barrier, LTypeBarrierV::Input, ins->input());
LTypeBarrierV* barrier = new(alloc()) LTypeBarrierV(useBox(ins->input()), tmp);
assignSnapshot(barrier, Bailout_TypeBarrierV);
add(barrier, ins);
redefine(ins, ins->input());
@@ -2471,8 +2432,7 @@ LIRGenerator::visitMonitorTypes(MMonitorTypes* ins)
bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
LDefinition tmp = needTemp ? temp() : tempToUnbox();
LMonitorTypes* lir = new(alloc()) LMonitorTypes(tmp);
useBox(lir, LMonitorTypes::Input, ins->input());
LMonitorTypes* lir = new(alloc()) LMonitorTypes(useBox(ins->input()), tmp);
assignSnapshot(lir, Bailout_MonitorTypes);
add(lir, ins);
}
@@ -2518,8 +2478,8 @@ LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier* ins)
new(alloc()) LPostWriteBarrierV(useConstantObject
? useOrConstant(ins->object())
: useRegister(ins->object()),
useBox(ins->value()),
tmp);
useBox(lir, LPostWriteBarrierV::Input, ins->value());
add(lir, ins);
assignSafepoint(lir, ins);
break;
@@ -2567,8 +2527,8 @@ LIRGenerator::visitPostWriteElementBarrier(MPostWriteElementBarrier* ins)
? useOrConstant(ins->object())
: useRegister(ins->object()),
useRegister(ins->index()),
useBox(ins->value()),
tmp);
useBox(lir, LPostWriteElementBarrierV::Input, ins->value());
add(lir, ins);
assignSafepoint(lir, ins);
break;
@@ -2756,8 +2716,7 @@ LIRGenerator::visitNot(MNot* ins)
temp1 = LDefinition::BogusTemp();
}
LNotV* lir = new(alloc()) LNotV(tempDouble(), temp0, temp1);
useBox(lir, LNotV::Input, op);
LNotV* lir = new(alloc()) LNotV(useBox(op), tempDouble(), temp0, temp1);
define(lir, ins);
break;
}
@@ -2912,10 +2871,9 @@ LIRGenerator::visitStoreElement(MStoreElement* ins)
switch (ins->value()->type()) {
case MIRType_Value:
{
LInstruction* lir = new(alloc()) LStoreElementV(elements, index);
LInstruction* lir = new(alloc()) LStoreElementV(elements, index, useBox(ins->value()));
if (ins->fallible())
assignSnapshot(lir, Bailout_Hole);
useBox(lir, LStoreElementV::Value, ins->value());
add(lir, ins);
break;
}
@@ -2950,8 +2908,8 @@ LIRGenerator::visitStoreElementHole(MStoreElementHole* ins)
LInstruction* lir;
switch (ins->value()->type()) {
case MIRType_Value:
lir = new(alloc()) LStoreElementHoleV(object, elements, index, tempDef);
useBox(lir, LStoreElementHoleV::Value, ins->value());
lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value()),
tempDef);
break;
default:
@@ -3049,8 +3007,7 @@ LIRGenerator::visitArrayPush(MArrayPush* ins)
switch (ins->value()->type()) {
case MIRType_Value:
{
LArrayPushV* lir = new(alloc()) LArrayPushV(object, temp());
useBox(lir, LArrayPushV::Value, ins->value());
LArrayPushV* lir = new(alloc()) LArrayPushV(object, useBox(ins->value()), temp());
define(lir, ins);
assignSafepoint(lir, ins);
break;
@@ -3192,8 +3149,7 @@ LIRGenerator::visitClampToUint8(MClampToUint8* ins)
case MIRType_Value:
{
LClampVToUint8* lir = new(alloc()) LClampVToUint8(tempDouble());
useBox(lir, LClampVToUint8::Input, in);
LClampVToUint8* lir = new(alloc()) LClampVToUint8(useBox(in), tempDouble());
assignSnapshot(lir, Bailout_NonPrimitiveInput);
define(lir, ins);
assignSafepoint(lir, ins);
@@ -3339,8 +3295,8 @@ LIRGenerator::visitStoreFixedSlot(MStoreFixedSlot* ins)
MOZ_ASSERT(ins->object()->type() == MIRType_Object);
if (ins->value()->type() == MIRType_Value) {
LStoreFixedSlotV* lir = new(alloc()) LStoreFixedSlotV(useRegister(ins->object()));
useBox(lir, LStoreFixedSlotV::Value, ins->value());
LStoreFixedSlotV* lir = new(alloc()) LStoreFixedSlotV(useRegister(ins->object()),
useBox(ins->value()));
add(lir, ins);
} else {
LStoreFixedSlotT* lir = new(alloc()) LStoreFixedSlotT(useRegister(ins->object()),
@@ -3395,13 +3351,15 @@ LIRGenerator::visitGetPropertyCache(MGetPropertyCache* ins)
bool useConstId = id->type() == MIRType_String || id->type() == MIRType_Symbol;
if (ins->type() == MIRType_Value) {
LGetPropertyCacheV* lir = new(alloc()) LGetPropertyCacheV(useRegister(ins->object()));
useBoxOrTypedOrConstant(lir, LGetPropertyCacheV::Id, id, useConstId);
LGetPropertyCacheV* lir =
new(alloc()) LGetPropertyCacheV(useRegister(ins->object()),
useBoxOrTypedOrConstant(id, useConstId));
defineBox(lir, ins);
assignSafepoint(lir, ins);
} else {
LGetPropertyCacheT* lir = new(alloc()) LGetPropertyCacheT(useRegister(ins->object()));
useBoxOrTypedOrConstant(lir, LGetPropertyCacheT::Id, id, useConstId);
LGetPropertyCacheT* lir =
new(alloc()) LGetPropertyCacheT(useRegister(ins->object()),
useBoxOrTypedOrConstant(id, useConstId));
define(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3433,8 +3391,9 @@ LIRGenerator::visitSetPropertyPolymorphic(MSetPropertyPolymorphic* ins)
if (ins->value()->type() == MIRType_Value) {
LSetPropertyPolymorphicV* lir =
new(alloc()) LSetPropertyPolymorphicV(useRegister(ins->obj()), temp());
useBox(lir, LSetPropertyPolymorphicV::Value, ins->value());
new(alloc()) LSetPropertyPolymorphicV(useRegister(ins->obj()),
useBox(ins->value()),
temp());
assignSnapshot(lir, Bailout_ShapeGuard);
add(lir, ins);
} else {
@@ -3575,8 +3534,7 @@ LIRGenerator::visitAssertRange(MAssertRange* ins)
break;
}
case MIRType_Value:
lir = new(alloc()) LAssertRangeV(tempToUnbox(), tempDouble(), tempDouble());
useBox(lir, LAssertRangeV::Input, input);
lir = new(alloc()) LAssertRangeV(useBox(input), tempToUnbox(), tempDouble(), tempDouble());
break;
default:
@@ -3591,8 +3549,7 @@ LIRGenerator::visitAssertRange(MAssertRange* ins)
void
LIRGenerator::visitCallGetProperty(MCallGetProperty* ins)
{
LCallGetProperty* lir = new(alloc()) LCallGetProperty();
useBoxAtStart(lir, LCallGetProperty::Value, ins->value());
LCallGetProperty* lir = new(alloc()) LCallGetProperty(useBoxAtStart(ins->value()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3603,9 +3560,8 @@ LIRGenerator::visitCallGetElement(MCallGetElement* ins)
MOZ_ASSERT(ins->lhs()->type() == MIRType_Value);
MOZ_ASSERT(ins->rhs()->type() == MIRType_Value);
LCallGetElement* lir = new(alloc()) LCallGetElement();
useBoxAtStart(lir, LCallGetElement::LhsInput, ins->lhs());
useBoxAtStart(lir, LCallGetElement::RhsInput, ins->rhs());
LCallGetElement* lir = new(alloc()) LCallGetElement(useBoxAtStart(ins->lhs()),
useBoxAtStart(ins->rhs()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3613,8 +3569,8 @@ LIRGenerator::visitCallGetElement(MCallGetElement* ins)
void
LIRGenerator::visitCallSetProperty(MCallSetProperty* ins)
{
LInstruction* lir = new(alloc()) LCallSetProperty(useRegisterAtStart(ins->object()));
useBoxAtStart(lir, LCallSetProperty::Value, ins->value());
LInstruction* lir = new(alloc()) LCallSetProperty(useRegisterAtStart(ins->object()),
useBoxAtStart(ins->value()));
add(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3622,8 +3578,7 @@ LIRGenerator::visitCallSetProperty(MCallSetProperty* ins)
void
LIRGenerator::visitDeleteProperty(MDeleteProperty* ins)
{
LCallDeleteProperty* lir = new(alloc()) LCallDeleteProperty();
useBoxAtStart(lir, LCallDeleteProperty::Value, ins->value());
LCallDeleteProperty* lir = new(alloc()) LCallDeleteProperty(useBoxAtStart(ins->value()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3631,9 +3586,8 @@ LIRGenerator::visitDeleteProperty(MDeleteProperty* ins)
void
LIRGenerator::visitDeleteElement(MDeleteElement* ins)
{
LCallDeleteElement* lir = new(alloc()) LCallDeleteElement();
useBoxAtStart(lir, LCallDeleteElement::Value, ins->value());
useBoxAtStart(lir, LCallDeleteElement::Index, ins->index());
LCallDeleteElement* lir = new(alloc()) LCallDeleteElement(useBoxAtStart(ins->value()),
useBoxAtStart(ins->index()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3672,11 +3626,12 @@ LIRGenerator::visitSetPropertyCache(MSetPropertyCache* ins)
tempF32 = hasUnaliasedDouble() ? tempFloat32() : LDefinition::BogusTemp();
}
LInstruction* lir = new(alloc()) LSetPropertyCache(useRegister(ins->object()), temp(),
tempToUnboxIndex, tempD, tempF32);
useBoxOrTypedOrConstant(lir, LSetPropertyCache::Id, id, useConstId);
useBoxOrTypedOrConstant(lir, LSetPropertyCache::Value, ins->value(), useConstValue);
LInstruction* lir =
new(alloc()) LSetPropertyCache(useRegister(ins->object()),
useBoxOrTypedOrConstant(id, useConstId),
useBoxOrTypedOrConstant(ins->value(), useConstValue),
temp(),
tempToUnboxIndex, tempD, tempF32);
add(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3688,10 +3643,9 @@ LIRGenerator::visitCallSetElement(MCallSetElement* ins)
MOZ_ASSERT(ins->index()->type() == MIRType_Value);
MOZ_ASSERT(ins->value()->type() == MIRType_Value);
LCallSetElement* lir = new(alloc()) LCallSetElement();
lir->setOperand(0, useRegisterAtStart(ins->object()));
useBoxAtStart(lir, LCallSetElement::Index, ins->index());
useBoxAtStart(lir, LCallSetElement::Value, ins->value());
LCallSetElement* lir = new(alloc()) LCallSetElement(useRegisterAtStart(ins->object()),
useBoxAtStart(ins->index()),
useBoxAtStart(ins->value()));
add(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3699,9 +3653,8 @@ LIRGenerator::visitCallSetElement(MCallSetElement* ins)
void
LIRGenerator::visitCallInitElementArray(MCallInitElementArray* ins)
{
LCallInitElementArray* lir = new(alloc()) LCallInitElementArray();
lir->setOperand(0, useRegisterAtStart(ins->object()));
useBoxAtStart(lir, LCallInitElementArray::Value, ins->value());
LCallInitElementArray* lir = new(alloc()) LCallInitElementArray(useRegisterAtStart(ins->object()),
useBoxAtStart(ins->value()));
add(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3777,8 +3730,7 @@ LIRGenerator::visitSetFrameArgument(MSetFrameArgument* ins)
MDefinition* input = ins->input();
if (input->type() == MIRType_Value) {
LSetFrameArgumentV* lir = new(alloc()) LSetFrameArgumentV();
useBox(lir, LSetFrameArgumentV::Input, input);
LSetFrameArgumentV* lir = new(alloc()) LSetFrameArgumentV(useBox(input));
add(lir, ins);
} else if (input->type() == MIRType_Undefined || input->type() == MIRType_Null) {
Value val = input->type() == MIRType_Undefined ? UndefinedValue() : NullValue();
@@ -3817,8 +3769,7 @@ LIRGenerator::visitThrow(MThrow* ins)
MDefinition* value = ins->getOperand(0);
MOZ_ASSERT(value->type() == MIRType_Value);
LThrow* lir = new(alloc()) LThrow;
useBoxAtStart(lir, LThrow::Value, value);
LThrow* lir = new(alloc()) LThrow(useBoxAtStart(value));
add(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3832,8 +3783,7 @@ LIRGenerator::visitIn(MIn* ins)
MOZ_ASSERT(lhs->type() == MIRType_Value);
MOZ_ASSERT(rhs->type() == MIRType_Object);
LIn* lir = new(alloc()) LIn(useRegisterAtStart(rhs));
useBoxAtStart(lir, LIn::LHS, lhs);
LIn* lir = new(alloc()) LIn(useBoxAtStart(lhs), useRegisterAtStart(rhs));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3850,8 +3800,7 @@ LIRGenerator::visitInstanceOf(MInstanceOf* ins)
define(lir, ins);
assignSafepoint(lir, ins);
} else {
LInstanceOfV* lir = new(alloc()) LInstanceOfV();
useBox(lir, LInstanceOfV::LHS, lhs);
LInstanceOfV* lir = new(alloc()) LInstanceOfV(useBox(lhs));
define(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3866,8 +3815,8 @@ LIRGenerator::visitCallInstanceOf(MCallInstanceOf* ins)
MOZ_ASSERT(lhs->type() == MIRType_Value);
MOZ_ASSERT(rhs->type() == MIRType_Object);
LCallInstanceOf* lir = new(alloc()) LCallInstanceOf(useRegisterAtStart(rhs));
useBoxAtStart(lir, LCallInstanceOf::LHS, lhs);
LCallInstanceOf* lir = new(alloc()) LCallInstanceOf(useBoxAtStart(lhs),
useRegisterAtStart(rhs));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
@@ -3911,8 +3860,7 @@ LIRGenerator::visitIsObject(MIsObject* ins)
MDefinition* opd = ins->input();
MOZ_ASSERT(opd->type() == MIRType_Value);
LIsObject* lir = new(alloc()) LIsObject();
useBoxAtStart(lir, LIsObject::Input, opd);
LIsObject* lir = new(alloc()) LIsObject(useBoxAtStart(opd));
define(lir, ins);
}
@@ -3967,6 +3915,10 @@ LIRGenerator::visitAsmJSReturn(MAsmJSReturn* ins)
lir->setOperand(0, useFixed(rval, ReturnSimd128Reg));
else if (rval->type() == MIRType_Int32)
lir->setOperand(0, useFixed(rval, ReturnReg));
#if JS_BITS_PER_WORD == 64
else if (rval->type() == MIRType_Int64)
lir->setOperand(0, useFixed(rval, ReturnReg));
#endif
else
MOZ_CRASH("Unexpected asm.js return type");
add(lir);
@@ -4023,10 +3975,6 @@ LIRGenerator::visitSetDOMProperty(MSetDOMProperty* ins)
GetTempRegForIntArg(1, 0, &objReg);
GetTempRegForIntArg(2, 0, &privReg);
GetTempRegForIntArg(3, 0, &valueReg);
LSetDOMProperty* lir = new(alloc()) LSetDOMProperty(tempFixed(cxReg),
useFixed(ins->object(), objReg),
tempFixed(privReg),
tempFixed(valueReg));
// Keep using GetTempRegForIntArg, since we want to make sure we
// don't clobber registers we're already using.
@@ -4034,7 +3982,12 @@ LIRGenerator::visitSetDOMProperty(MSetDOMProperty* ins)
GetTempRegForIntArg(4, 0, &tempReg1);
mozilla::DebugOnly<bool> ok = GetTempRegForIntArg(5, 0, &tempReg2);
MOZ_ASSERT(ok, "How can we not have six temp registers?");
useBoxFixed(lir, LSetDOMProperty::Value, val, tempReg1, tempReg2);
LSetDOMProperty* lir = new(alloc()) LSetDOMProperty(tempFixed(cxReg),
useFixed(ins->object(), objReg),
useBoxFixed(val, tempReg1, tempReg2),
tempFixed(privReg),
tempFixed(valueReg));
add(lir, ins);
assignSafepoint(lir, ins);
}
@@ -4422,7 +4375,10 @@ LIRGenerator::visitSimdShift(MSimdShift* ins)
LUse vector = useRegisterAtStart(ins->lhs());
LAllocation value = useRegisterOrConstant(ins->rhs());
LSimdShift* lir = new(alloc()) LSimdShift(vector, value);
// We need a temp register to mask the shift amount, but not if the shift
// amount is a constant.
LDefinition tempReg = value.isConstant() ? LDefinition::BogusTemp() : temp();
LSimdShift* lir = new(alloc()) LSimdShift(vector, value, tempReg);
defineReuseInput(lir, ins, 0);
}
@@ -4431,8 +4387,7 @@ LIRGenerator::visitLexicalCheck(MLexicalCheck* ins)
{
MDefinition* input = ins->input();
MOZ_ASSERT(input->type() == MIRType_Value);
LLexicalCheck* lir = new(alloc()) LLexicalCheck();
useBox(lir, LLexicalCheck::Input, input);
LLexicalCheck* lir = new(alloc()) LLexicalCheck(useBox(input));
assignSnapshot(lir, ins->bailoutKind());
add(lir, ins);
redefine(ins, input);
@@ -4476,9 +4431,7 @@ LIRGenerator::visitCheckReturn(MCheckReturn* ins)
MOZ_ASSERT(retVal->type() == MIRType_Value);
MOZ_ASSERT(thisVal->type() == MIRType_Value);
LCheckReturn* lir = new(alloc()) LCheckReturn();
useBoxAtStart(lir, LCheckReturn::ReturnValue, retVal);
useBoxAtStart(lir, LCheckReturn::ThisValue, thisVal);
LCheckReturn* lir = new(alloc()) LCheckReturn(useBoxAtStart(retVal), useBoxAtStart(thisVal));
assignSnapshot(lir, Bailout_BadDerivedConstructorReturn);
add(lir, ins);
redefine(ins, retVal);
@@ -4490,8 +4443,7 @@ LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins)
MDefinition* checkVal = ins->checkValue();
MOZ_ASSERT(checkVal->type() == MIRType_Value);
LCheckObjCoercible* lir = new(alloc()) LCheckObjCoercible();
useBoxAtStart(lir, LCheckObjCoercible::CheckValue, checkVal);
LCheckObjCoercible* lir = new(alloc()) LCheckObjCoercible(useBoxAtStart(checkVal));
redefine(ins, checkVal);
add(lir, ins);
assignSafepoint(lir, ins);
@@ -4503,8 +4455,7 @@ LIRGenerator::visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins)
MDefinition* checkVal = ins->checkValue();
MOZ_ASSERT(checkVal->type() == MIRType_Value);
LDebugCheckSelfHosted* lir = new (alloc()) LDebugCheckSelfHosted();
useBoxAtStart(lir, LDebugCheckSelfHosted::CheckValue, checkVal);
LDebugCheckSelfHosted* lir = new (alloc()) LDebugCheckSelfHosted(useBoxAtStart(checkVal));
redefine(ins, checkVal);
add(lir, ins);
assignSafepoint(lir, ins);
+2 -4
View File
@@ -49,10 +49,8 @@ class LIRGenerator : public LIRGeneratorSpecific
bool generate();
private:
void useBoxAtStart(LInstruction* lir, size_t n, MDefinition* mir,
LUse::Policy policy = LUse::REGISTER);
void useBoxFixedAtStart(LInstruction* lir, size_t n, MDefinition* mir, ValueOperand op);
LBoxAllocation useBoxFixedAtStart(MDefinition* mir, ValueOperand op);
LBoxAllocation useBoxAtStart(MDefinition* mir, LUse::Policy policy = LUse::REGISTER);
void lowerBitOp(JSOp op, MInstruction* ins);
void lowerShiftOp(JSOp op, MShiftInstruction* ins);
-4
View File
@@ -3158,10 +3158,6 @@ IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type)
return inlineSimdShift(callInfo, native, MSimdShift::lsh, type);
case SimdOperation::Fn_shiftRightByScalar:
return inlineSimdShift(callInfo, native, MSimdShift::rshForSign(GetSimdSign(type)), type);
case SimdOperation::Fn_shiftRightArithmeticByScalar:
return inlineSimdShift(callInfo, native, MSimdShift::rsh, type);
case SimdOperation::Fn_shiftRightLogicalByScalar:
return inlineSimdShift(callInfo, native, MSimdShift::ursh, type);
// Boolean unary.
case SimdOperation::Fn_allTrue:
+6
View File
@@ -672,6 +672,12 @@ class MacroAssembler : public MacroAssemblerSpecific
CodeOffset selfReferencePatch_;
public:
// ===============================================================
// Move instructions
inline void move64(Imm64 imm, Register64 dest) PER_ARCH;
inline void move64(Register64 src, Register64 dest) PER_ARCH;
// ===============================================================
// Logical instructions
+7 -4
View File
@@ -60,9 +60,11 @@ class LUnboxFloatingPoint : public LInstructionHelper<1, 2, 0>
static const size_t Input = 0;
LUnboxFloatingPoint(MIRType type)
LUnboxFloatingPoint(const LBoxAllocation& input, MIRType type)
: type_(type)
{ }
{
setBoxOperand(Input, input);
}
MUnbox* mir() const {
return mir_->toUnbox();
@@ -301,9 +303,10 @@ class LTableSwitchV : public LInstructionHelper<0, BOX_PIECES, 2>
public:
LIR_HEADER(TableSwitchV);
LTableSwitchV(const LDefinition& inputCopy, const LDefinition& floatCopy,
MTableSwitch* ins)
LTableSwitchV(const LBoxAllocation& input, const LDefinition& inputCopy,
const LDefinition& floatCopy, MTableSwitch* ins)
{
setBoxOperand(InputValue, input);
setTemp(0, inputCopy);
setTemp(1, floatCopy);
setMir(ins);
+7 -8
View File
@@ -17,16 +17,15 @@ using namespace js::jit;
using mozilla::FloorLog2;
void
LIRGeneratorARM::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1,
Register reg2, bool useAtStart)
LBoxAllocation
LIRGeneratorARM::useBoxFixed(MDefinition* mir, Register reg1, Register reg2, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
MOZ_ASSERT(reg1 != reg2);
ensureDefined(mir);
lir->setOperand(n, LUse(reg1, mir->virtualRegister(), useAtStart));
lir->setOperand(n + 1, LUse(reg2, VirtualRegisterOfPayload(mir), useAtStart));
return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart),
LUse(reg2, VirtualRegisterOfPayload(mir), useAtStart));
}
LAllocation
@@ -108,10 +107,9 @@ LIRGeneratorARM::visitUnbox(MUnbox* unbox)
ensureDefined(inner);
if (IsFloatingPointType(unbox->type())) {
LUnboxFloatingPoint* lir = new(alloc()) LUnboxFloatingPoint(unbox->type());
LUnboxFloatingPoint* lir = new(alloc()) LUnboxFloatingPoint(useBox(inner), unbox->type());
if (unbox->fallible())
assignSnapshot(lir, unbox->bailoutKind());
useBox(lir, LUnboxFloatingPoint::Input, inner);
define(lir, unbox);
return;
}
@@ -345,7 +343,8 @@ LIRGeneratorARM::newLTableSwitch(const LAllocation& in, const LDefinition& input
LTableSwitchV*
LIRGeneratorARM::newLTableSwitchV(MTableSwitch* tableswitch)
{
return new(alloc()) LTableSwitchV(temp(), tempDouble(), tableswitch);
return new(alloc()) LTableSwitchV(useBox(tableswitch->getOperand(0)),
temp(), tempDouble(), tableswitch);
}
void
+3 -4
View File
@@ -20,10 +20,9 @@ class LIRGeneratorARM : public LIRGeneratorShared
{ }
protected:
// Adds a box input to an instruction, setting operand |n| to the type and
// |n+1| to the payload.
void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
// Returns a box allocation with type set to reg1 and payload set to reg2.
LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
// x86 has constraints on what registers can be formatted for 1-byte
// stores and loads; on ARM all registers are okay.
+15
View File
@@ -13,6 +13,21 @@ namespace js {
namespace jit {
//{{{ check_macroassembler_style
void
MacroAssembler::move64(Register64 src, Register64 dest)
{
move32(src.low, dest.low);
move32(src.high, dest.high);
}
void
MacroAssembler::move64(Imm64 imm, Register64 dest)
{
move32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
move32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
}
// ===============================================================
// Logical instructions
-4
View File
@@ -1016,10 +1016,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
void movePtr(ImmPtr imm, Register dest);
void movePtr(wasm::SymbolicAddress imm, Register dest);
void movePtr(ImmGCPtr imm, Register dest);
void move64(Register64 src, Register64 dest) {
move32(src.low, dest.low);
move32(src.high, dest.high);
}
void load8SignExtend(const Address& address, Register dest);
void load8SignExtend(const BaseIndex& src, Register dest);
+3 -2
View File
@@ -282,9 +282,10 @@ class LTableSwitchV : public LInstructionHelper<0, BOX_PIECES, 2>
public:
LIR_HEADER(TableSwitchV);
LTableSwitchV(const LDefinition& inputCopy, const LDefinition& floatCopy,
MTableSwitch* ins)
LTableSwitchV(const LBoxAllocation& input, const LDefinition& inputCopy,
const LDefinition& floatCopy, MTableSwitch* ins)
{
setBoxOperand(InputValue, input);
setTemp(0, inputCopy);
setTemp(1, floatCopy);
setMir(ins);
+2 -2
View File
@@ -17,8 +17,8 @@ using namespace js::jit;
using mozilla::FloorLog2;
void
LIRGeneratorARM64::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register, bool useAtStart)
LBoxAllocation
LIRGeneratorARM64::useBoxFixed(MDefinition* mir, Register reg1, Register, bool useAtStart)
{
MOZ_CRASH("useBoxFixed");
}
+3 -4
View File
@@ -20,10 +20,9 @@ class LIRGeneratorARM64 : public LIRGeneratorShared
{ }
protected:
// Adds a box input to an instruction, setting operand |n| to the type and
// |n+1| to the payload.
void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
// Returns a box allocation. reg2 is ignored on 64-bit platforms.
LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
LAllocation useByteOpRegister(MDefinition* mir);
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir);
@@ -13,6 +13,19 @@ namespace js {
namespace jit {
//{{{ check_macroassembler_style
void
MacroAssembler::move64(Register64 src, Register64 dest)
{
movePtr(src.reg, dest.reg);
}
void
MacroAssembler::move64(Imm64 imm, Register64 dest)
{
movePtr(ImmWord(imm.value), dest.reg);
}
// ===============================================================
// Logical instructions
-3
View File
@@ -763,9 +763,6 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
BufferOffset load = movePatchablePtr(ImmPtr(imm.value), dest);
writeDataRelocation(imm, load);
}
void move64(Register64 src, Register64 dest) {
movePtr(src.reg, dest.reg);
}
void mov(ImmWord imm, Register dest) {
movePtr(imm, dest);
+4 -2
View File
@@ -182,9 +182,11 @@ class LTableSwitchV : public LInstructionHelper<0, BOX_PIECES, 3>
public:
LIR_HEADER(TableSwitchV);
LTableSwitchV(const LDefinition& inputCopy, const LDefinition& floatCopy,
const LDefinition& jumpTablePointer, MTableSwitch* ins)
LTableSwitchV(const LBoxAllocation& input, const LDefinition& inputCopy,
const LDefinition& floatCopy, const LDefinition& jumpTablePointer,
MTableSwitch* ins)
{
setBoxOperand(InputValue, input);
setTemp(0, inputCopy);
setTemp(1, floatCopy);
setTemp(2, jumpTablePointer);
@@ -106,7 +106,7 @@ LIRGeneratorMIPSShared::lowerDivI(MDiv* div)
// Division instructions are slow. Division by constant denominators can be
// rewritten to use other instructions.
if (div->rhs()->isConstant()) {
int32_t rhs = div->rhs()->toConstant()->value().toInt32();
int32_t rhs = div->rhs()->toConstant()->toInt32();
// Check for division by a positive power of two, which is an easy and
// important case to optimize. Note that other optimizations are also
// possible; division by negative powers of two can be optimized in a
@@ -147,7 +147,7 @@ LIRGeneratorMIPSShared::lowerModI(MMod* mod)
}
if (mod->rhs()->isConstant()) {
int32_t rhs = mod->rhs()->toConstant()->value().toInt32();
int32_t rhs = mod->rhs()->toConstant()->toInt32();
int32_t shift = FloorLog2(rhs);
if (rhs > 0 && 1 << shift == rhs) {
LModPowTwoI* lir = new(alloc()) LModPowTwoI(useRegister(mod->lhs()), shift);
@@ -193,7 +193,8 @@ LIRGeneratorMIPSShared::newLTableSwitch(const LAllocation& in, const LDefinition
LTableSwitchV*
LIRGeneratorMIPSShared::newLTableSwitchV(MTableSwitch* tableswitch)
{
return new(alloc()) LTableSwitchV(temp(), tempDouble(), temp(), tableswitch);
return new(alloc()) LTableSwitchV(useBox(tableswitch->getOperand(0)),
temp(), tempDouble(), temp(), tableswitch);
}
void
@@ -301,10 +302,10 @@ LIRGeneratorMIPSShared::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
// For MIPS it is best to keep the 'ptr' in a register if a bounds check
// is needed.
if (ptr->isConstantValue() && !ins->needsBoundsCheck()) {
if (ptr->isConstant() && !ins->needsBoundsCheck()) {
// A bounds check is only skipped for a positive index.
MOZ_ASSERT(ptr->constantValue().toInt32() >= 0);
ptrAlloc = LAllocation(ptr->constantVp());
MOZ_ASSERT(ptr->toConstant()->toInt32() >= 0);
ptrAlloc = LAllocation(ptr->toConstant());
} else
ptrAlloc = useRegisterAtStart(ptr);
@@ -318,9 +319,9 @@ LIRGeneratorMIPSShared::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
MOZ_ASSERT(ptr->type() == MIRType_Int32);
LAllocation ptrAlloc;
if (ptr->isConstantValue() && !ins->needsBoundsCheck()) {
MOZ_ASSERT(ptr->constantValue().toInt32() >= 0);
ptrAlloc = LAllocation(ptr->constantVp());
if (ptr->isConstant() && !ins->needsBoundsCheck()) {
MOZ_ASSERT(ptr->toConstant()->toInt32() >= 0);
ptrAlloc = LAllocation(ptr->toConstant());
} else
ptrAlloc = useRegisterAtStart(ptr);
+4 -2
View File
@@ -60,9 +60,11 @@ class LUnboxFloatingPoint : public LInstructionHelper<1, 2, 0>
static const size_t Input = 0;
LUnboxFloatingPoint(MIRType type)
LUnboxFloatingPoint(const LBoxAllocation& input, MIRType type)
: type_(type)
{ }
{
setBoxOperand(Input, input);
}
MUnbox* mir() const {
return mir_->toUnbox();
+6 -8
View File
@@ -15,16 +15,15 @@
using namespace js;
using namespace js::jit;
void
LIRGeneratorMIPS::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1,
Register reg2, bool useAtStart)
LBoxAllocation
LIRGeneratorMIPS::useBoxFixed(MDefinition* mir, Register reg1, Register reg2, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
MOZ_ASSERT(reg1 != reg2);
ensureDefined(mir);
lir->setOperand(n, LUse(reg1, mir->virtualRegister(), useAtStart));
lir->setOperand(n + 1, LUse(reg2, VirtualRegisterOfPayload(mir), useAtStart));
return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart),
LUse(reg2, VirtualRegisterOfPayload(mir), useAtStart));
}
void
@@ -45,7 +44,7 @@ LIRGeneratorMIPS::visitBox(MBox* box)
}
if (inner->isConstant()) {
defineBox(new(alloc()) LValue(inner->toConstant()->value()), box);
defineBox(new(alloc()) LValue(inner->toConstant()->toJSValue()), box);
return;
}
@@ -88,10 +87,9 @@ LIRGeneratorMIPS::visitUnbox(MUnbox* unbox)
ensureDefined(inner);
if (IsFloatingPointType(unbox->type())) {
LUnboxFloatingPoint* lir = new(alloc()) LUnboxFloatingPoint(unbox->type());
LUnboxFloatingPoint* lir = new(alloc()) LUnboxFloatingPoint(useBox(inner), unbox->type());
if (unbox->fallible())
assignSnapshot(lir, unbox->bailoutKind());
useBox(lir, LUnboxFloatingPoint::Input, inner);
define(lir, unbox);
return;
}
+3 -4
View File
@@ -20,10 +20,9 @@ class LIRGeneratorMIPS : public LIRGeneratorMIPSShared
{ }
protected:
// Adds a box input to an instruction, setting operand |n| to the type and
// |n+1| to the payload.
void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
// Returns a box allocation with type set to reg1 and payload set to reg2.
LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
inline LDefinition tempToUnbox() {
return LDefinition::BogusTemp();
@@ -15,6 +15,21 @@ namespace js {
namespace jit {
//{{{ check_macroassembler_style
void
MacroAssembler::move64(Register64 src, Register64 dest)
{
move32(src.low, dest.low);
move32(src.high, dest.high);
}
void
MacroAssembler::move64(Imm64 imm, Register64 dest)
{
move32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
move32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
}
// ===============================================================
// Logical instructions
@@ -936,10 +936,6 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
void move32(Imm32 imm, Register dest);
void move32(Register src, Register dest);
void move64(Register64 src, Register64 dest) {
move32(src.low, dest.low);
move32(src.high, dest.high);
}
void movePtr(Register src, Register dest);
void movePtr(ImmWord imm, Register dest);
+2 -2
View File
@@ -222,7 +222,7 @@ CodeGeneratorMIPS64::visitCompareB(LCompareB* lir)
// Load boxed boolean in ScratchRegister.
if (rhs->isConstant())
masm.moveValue(*rhs->toConstant(), ScratchRegister);
masm.moveValue(rhs->toConstant()->toJSValue(), ScratchRegister);
else
masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
@@ -241,7 +241,7 @@ CodeGeneratorMIPS64::visitCompareBAndBranch(LCompareBAndBranch* lir)
// Load boxed boolean in ScratchRegister.
if (rhs->isConstant())
masm.moveValue(*rhs->toConstant(), ScratchRegister);
masm.moveValue(rhs->toConstant()->toJSValue(), ScratchRegister);
else
masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
+4 -5
View File
@@ -15,14 +15,13 @@
using namespace js;
using namespace js::jit;
void
LIRGeneratorMIPS64::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1,
Register reg2, bool useAtStart)
LBoxAllocation
LIRGeneratorMIPS64::useBoxFixed(MDefinition* mir, Register reg1, Register reg2, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
ensureDefined(mir);
lir->setOperand(n, LUse(reg1, mir->virtualRegister(), useAtStart));
return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart));
}
void
@@ -37,7 +36,7 @@ LIRGeneratorMIPS64::visitBox(MBox* box)
}
if (opd->isConstant()) {
define(new(alloc()) LValue(opd->toConstant()->value()), box, LDefinition(LDefinition::BOX));
define(new(alloc()) LValue(opd->toConstant()->toJSValue()), box, LDefinition(LDefinition::BOX));
} else {
LBox* ins = new(alloc()) LBox(useRegister(opd), opd->type());
define(ins, box, LDefinition(LDefinition::BOX));
+3 -4
View File
@@ -20,10 +20,9 @@ class LIRGeneratorMIPS64 : public LIRGeneratorMIPSShared
{ }
protected:
// Adds a box input to an instruction, setting operand |n| to the type and
// |n+1| to the payload.
void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
// Returns a box allocation. reg2 is ignored on 64-bit platforms.
LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
inline LDefinition tempToUnbox() {
return temp();
@@ -15,6 +15,19 @@ namespace js {
namespace jit {
//{{{ check_macroassembler_style
void
MacroAssembler::move64(Register64 src, Register64 dest)
{
movePtr(src.reg, dest.reg);
}
void
MacroAssembler::move64(Imm64 imm, Register64 dest)
{
movePtr(ImmWord(imm.value), dest.reg);
}
// ===============================================================
// Logical instructions
@@ -950,9 +950,6 @@ class MacroAssemblerMIPS64Compat : public MacroAssemblerMIPS64
void move32(Imm32 imm, Register dest);
void move32(Register src, Register dest);
void move64(Register64 src, Register64 dest) {
movePtr(src.reg, dest.reg);
}
void movePtr(Register src, Register dest);
void movePtr(ImmWord imm, Register dest);
+1 -1
View File
@@ -21,7 +21,7 @@ class LIRGeneratorNone : public LIRGeneratorShared
MOZ_CRASH();
}
void useBoxFixed(LInstruction*, size_t, MDefinition*, Register, Register, bool useAtStart = false) { MOZ_CRASH(); }
LBoxAllocation useBoxFixed(MDefinition*, Register, Register, bool useAtStart = false) { MOZ_CRASH(); }
LAllocation useByteOpRegister(MDefinition*) { MOZ_CRASH(); }
LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition*) { MOZ_CRASH(); }
@@ -50,6 +50,18 @@ ToRegister(const LDefinition* def)
return ToRegister(*def->output());
}
static inline Register64
ToOutRegister64(LInstruction* ins)
{
#if JS_BITS_PER_WORD == 32
Register loReg = ToRegister(ins->getDef(0));
Register hiReg = ToRegister(ins->getDef(1));
return Register64(hiReg, loReg);
#else
return Register64(ToRegister(ins->getDef(0)));
#endif
}
static inline Register
ToTempRegisterOrInvalid(const LDefinition* def)
{
+261 -69
View File
@@ -578,13 +578,17 @@ class LSimdBinaryBitwiseX4 : public LInstructionHelper<1, 2, 0>
}
};
class LSimdShift : public LInstructionHelper<1, 2, 0>
// Shift a SIMD vector by a scalar amount.
// The temp register is only required if the shift amount is a dynamical
// value. If it is a constant, use a BogusTemp instead.
class LSimdShift : public LInstructionHelper<1, 2, 1>
{
public:
LIR_HEADER(SimdShift)
LSimdShift(const LAllocation& vec, const LAllocation& val) {
LSimdShift(const LAllocation& vec, const LAllocation& val, const LDefinition& temp) {
setOperand(0, vec);
setOperand(1, val);
setTemp(0, temp);
}
const LAllocation* vector() {
return getOperand(0);
@@ -592,6 +596,9 @@ class LSimdShift : public LInstructionHelper<1, 2, 0>
const LAllocation* value() {
return getOperand(1);
}
const LDefinition* temp() {
return getTemp(0);
}
MSimdShift::Operation operation() const {
return mir_->toSimdShift()->operation();
}
@@ -674,6 +681,23 @@ class LInteger : public LInstructionHelper<1, 0, 0>
}
};
// Constant 64-bit integer.
class LInteger64 : public LInstructionHelper<INT64_PIECES, 0, 0>
{
int64_t i64_;
public:
LIR_HEADER(Integer64)
explicit LInteger64(int64_t i64)
: i64_(i64)
{ }
int64_t getValue() const {
return i64_;
}
};
// Constant pointer.
class LPointer : public LInstructionHelper<1, 0, 0>
{
@@ -1087,8 +1111,10 @@ class LInitElem : public LCallInstructionHelper<0, 1 + 2*BOX_PIECES, 0>
public:
LIR_HEADER(InitElem)
explicit LInitElem(const LAllocation& object) {
LInitElem(const LAllocation& object, const LBoxAllocation& id, const LBoxAllocation& value) {
setOperand(0, object);
setBoxOperand(IdIndex, id);
setBoxOperand(ValueIndex, value);
}
static const size_t IdIndex = 1;
@@ -1107,9 +1133,11 @@ class LInitElemGetterSetter : public LCallInstructionHelper<0, 2 + BOX_PIECES, 0
public:
LIR_HEADER(InitElemGetterSetter)
LInitElemGetterSetter(const LAllocation& object, const LAllocation& value) {
LInitElemGetterSetter(const LAllocation& object, const LBoxAllocation& id,
const LAllocation& value) {
setOperand(0, object);
setOperand(1, value);
setBoxOperand(IdIndex, id);
}
static const size_t IdIndex = 2;
@@ -1131,8 +1159,9 @@ class LMutateProto : public LCallInstructionHelper<0, 1 + BOX_PIECES, 0>
public:
LIR_HEADER(MutateProto)
explicit LMutateProto(const LAllocation& object) {
LMutateProto(const LAllocation& object, const LBoxAllocation& value) {
setOperand(0, object);
setBoxOperand(ValueIndex, value);
}
static const size_t ValueIndex = 1;
@@ -1151,8 +1180,9 @@ class LInitProp : public LCallInstructionHelper<0, 1 + BOX_PIECES, 0>
public:
LIR_HEADER(InitProp)
explicit LInitProp(const LAllocation& object) {
LInitProp(const LAllocation& object, const LBoxAllocation& value) {
setOperand(0, object);
setBoxOperand(ValueIndex, value);
}
static const size_t ValueIndex = 1;
@@ -1306,7 +1336,8 @@ class LTypeOfV : public LInstructionHelper<1, BOX_PIECES, 1>
public:
LIR_HEADER(TypeOfV)
explicit LTypeOfV(const LDefinition& tempToUnbox) {
LTypeOfV(const LBoxAllocation& input, const LDefinition& tempToUnbox) {
setBoxOperand(Input, input);
setTemp(0, tempToUnbox);
}
@@ -1326,12 +1357,13 @@ class LToIdV : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1>
public:
LIR_HEADER(ToIdV)
explicit LToIdV(const LDefinition& temp)
LToIdV(const LBoxAllocation& input, const LDefinition& temp)
{
setBoxOperand(Input, input);
setTemp(0, temp);
}
static const size_t Index = 0;
static const size_t Input = 0;
MToId* mir() const {
return mir_->toToId();
@@ -1465,9 +1497,11 @@ class LSetArgumentsObjectArg : public LInstructionHelper<0, 1 + BOX_PIECES, 1>
public:
LIR_HEADER(SetArgumentsObjectArg)
LSetArgumentsObjectArg(const LAllocation& argsObj, const LDefinition& temp)
LSetArgumentsObjectArg(const LAllocation& argsObj, const LBoxAllocation& value,
const LDefinition& temp)
{
setOperand(0, argsObj);
setBoxOperand(ValueIndex, value);
setTemp(0, temp);
}
@@ -1489,14 +1523,14 @@ class LReturnFromCtor : public LInstructionHelper<1, BOX_PIECES + 1, 0>
public:
LIR_HEADER(ReturnFromCtor)
explicit LReturnFromCtor(const LAllocation& object)
LReturnFromCtor(const LBoxAllocation& value, const LAllocation& object)
{
// Value set by useBox() during lowering.
setOperand(LReturnFromCtor::ObjectIndex, object);
setBoxOperand(ValueIndex, value);
setOperand(ObjectIndex, object);
}
const LAllocation* getObject() {
return getOperand(LReturnFromCtor::ObjectIndex);
return getOperand(ObjectIndex);
}
static const size_t ValueIndex = 0;
@@ -1510,6 +1544,10 @@ class LComputeThis : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
static const size_t ValueIndex = 0;
explicit LComputeThis(const LBoxAllocation& value) {
setBoxOperand(ValueIndex, value);
}
const LDefinition* output() {
return getDef(0);
}
@@ -1553,9 +1591,11 @@ class LStackArgV : public LInstructionHelper<0, BOX_PIECES, 0>
public:
LIR_HEADER(StackArgV)
explicit LStackArgV(uint32_t argslot)
LStackArgV(uint32_t argslot, const LBoxAllocation& value)
: argslot_(argslot)
{ }
{
setBoxOperand(0, value);
}
uint32_t argslot() const {
return argslot_;
@@ -1803,10 +1843,13 @@ class LSetDOMProperty : public LDOMPropertyInstructionHelper<0, BOX_PIECES>
LIR_HEADER(SetDOMProperty)
LSetDOMProperty(const LDefinition& JSContextReg, const LAllocation& ObjectReg,
const LDefinition& PrivReg, const LDefinition& ValueReg)
const LBoxAllocation& value, const LDefinition& PrivReg,
const LDefinition& ValueReg)
: LDOMPropertyInstructionHelper<0, BOX_PIECES>(JSContextReg, ObjectReg,
PrivReg, ValueReg)
{ }
{
setBoxOperand(Value, value);
}
static const size_t Value = 1;
@@ -1823,10 +1866,12 @@ class LApplyArgsGeneric : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES +
LIR_HEADER(ApplyArgsGeneric)
LApplyArgsGeneric(const LAllocation& func, const LAllocation& argc,
const LDefinition& tmpobjreg, const LDefinition& tmpcopy)
const LBoxAllocation& thisv, const LDefinition& tmpobjreg,
const LDefinition& tmpcopy)
{
setOperand(0, func);
setOperand(1, argc);
setBoxOperand(ThisIndex, thisv);
setTemp(0, tmpobjreg);
setTemp(1, tmpcopy);
}
@@ -1864,10 +1909,12 @@ class LApplyArrayGeneric : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES
LIR_HEADER(ApplyArrayGeneric)
LApplyArrayGeneric(const LAllocation& func, const LAllocation& elements,
const LDefinition& tmpobjreg, const LDefinition& tmpcopy)
const LBoxAllocation& thisv, const LDefinition& tmpobjreg,
const LDefinition& tmpcopy)
{
setOperand(0, func);
setOperand(1, elements);
setBoxOperand(ThisIndex, thisv);
setTemp(0, tmpobjreg);
setTemp(1, tmpcopy);
}
@@ -1969,19 +2016,20 @@ class LGetDynamicName : public LCallInstructionHelper<BOX_PIECES, 2, 3>
}
};
class LCallDirectEval : public LCallInstructionHelper<BOX_PIECES, 2 + (2 * BOX_PIECES), 0>
class LCallDirectEval : public LCallInstructionHelper<BOX_PIECES, 2 + BOX_PIECES, 0>
{
public:
LIR_HEADER(CallDirectEval)
LCallDirectEval(const LAllocation& scopeChain, const LAllocation& string)
LCallDirectEval(const LAllocation& scopeChain, const LAllocation& string,
const LBoxAllocation& newTarget)
{
setOperand(0, scopeChain);
setOperand(1, string);
setBoxOperand(NewTarget, newTarget);
}
static const size_t ThisValue = 2;
static const size_t NewTarget = 2 + BOX_PIECES;
static const size_t NewTarget = 2;
MCallDirectEval* mir() const {
return mir_->toCallDirectEval();
@@ -2096,11 +2144,12 @@ class LTestVAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 3>
public:
LIR_HEADER(TestVAndBranch)
LTestVAndBranch(MBasicBlock* ifTruthy, MBasicBlock* ifFalsy, const LDefinition& temp0,
const LDefinition& temp1, const LDefinition& temp2)
LTestVAndBranch(MBasicBlock* ifTruthy, MBasicBlock* ifFalsy, const LBoxAllocation& input,
const LDefinition& temp0, const LDefinition& temp1, const LDefinition& temp2)
{
setSuccessor(0, ifTruthy);
setSuccessor(1, ifFalsy);
setBoxOperand(Input, input);
setTemp(0, temp0);
setTemp(1, temp1);
setTemp(2, temp2);
@@ -2396,7 +2445,8 @@ class LCompareStrictS : public LInstructionHelper<1, BOX_PIECES + 1, 1>
{
public:
LIR_HEADER(CompareStrictS)
LCompareStrictS(const LAllocation& rhs, const LDefinition& temp) {
LCompareStrictS(const LBoxAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
setBoxOperand(Lhs, lhs);
setOperand(BOX_PIECES, rhs);
setTemp(0, temp);
}
@@ -2422,7 +2472,8 @@ class LCompareB : public LInstructionHelper<1, BOX_PIECES + 1, 0>
public:
LIR_HEADER(CompareB)
explicit LCompareB(const LAllocation& rhs) {
LCompareB(const LBoxAllocation& lhs, const LAllocation& rhs) {
setBoxOperand(Lhs, lhs);
setOperand(BOX_PIECES, rhs);
}
@@ -2444,10 +2495,11 @@ class LCompareBAndBranch : public LControlInstructionHelper<2, BOX_PIECES + 1, 0
public:
LIR_HEADER(CompareBAndBranch)
LCompareBAndBranch(MCompare* cmpMir, const LAllocation& rhs,
LCompareBAndBranch(MCompare* cmpMir, const LBoxAllocation& lhs, const LAllocation& rhs,
MBasicBlock* ifTrue, MBasicBlock* ifFalse)
: cmpMir_(cmpMir)
{
setBoxOperand(Lhs, lhs);
setOperand(BOX_PIECES, rhs);
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
@@ -2481,6 +2533,11 @@ class LCompareBitwise : public LInstructionHelper<1, 2 * BOX_PIECES, 0>
static const size_t LhsInput = 0;
static const size_t RhsInput = BOX_PIECES;
LCompareBitwise(const LBoxAllocation& lhs, const LBoxAllocation& rhs) {
setBoxOperand(LhsInput, lhs);
setBoxOperand(RhsInput, rhs);
}
MCompare* mir() const {
return mir_->toCompare();
}
@@ -2496,11 +2553,14 @@ class LCompareBitwiseAndBranch : public LControlInstructionHelper<2, 2 * BOX_PIE
static const size_t LhsInput = 0;
static const size_t RhsInput = BOX_PIECES;
LCompareBitwiseAndBranch(MCompare* cmpMir, MBasicBlock* ifTrue, MBasicBlock* ifFalse)
LCompareBitwiseAndBranch(MCompare* cmpMir, MBasicBlock* ifTrue, MBasicBlock* ifFalse,
const LBoxAllocation& lhs, const LBoxAllocation& rhs)
: cmpMir_(cmpMir)
{
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
setBoxOperand(LhsInput, lhs);
setBoxOperand(RhsInput, rhs);
}
MBasicBlock* ifTrue() const {
@@ -2525,6 +2585,11 @@ class LCompareVM : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
static const size_t LhsInput = 0;
static const size_t RhsInput = BOX_PIECES;
LCompareVM(const LBoxAllocation& lhs, const LBoxAllocation& rhs) {
setBoxOperand(LhsInput, lhs);
setBoxOperand(RhsInput, rhs);
}
MCompare* mir() const {
return mir_->toCompare();
}
@@ -2562,8 +2627,10 @@ class LIsNullOrLikeUndefinedV : public LInstructionHelper<1, BOX_PIECES, 2>
public:
LIR_HEADER(IsNullOrLikeUndefinedV)
LIsNullOrLikeUndefinedV(const LDefinition& temp, const LDefinition& tempToUnbox)
LIsNullOrLikeUndefinedV(const LBoxAllocation& value, const LDefinition& temp,
const LDefinition& tempToUnbox)
{
setBoxOperand(Value, value);
setTemp(0, temp);
setTemp(1, tempToUnbox);
}
@@ -2608,11 +2675,13 @@ class LIsNullOrLikeUndefinedAndBranchV : public LControlInstructionHelper<2, BOX
LIR_HEADER(IsNullOrLikeUndefinedAndBranchV)
LIsNullOrLikeUndefinedAndBranchV(MCompare* cmpMir, MBasicBlock* ifTrue, MBasicBlock* ifFalse,
const LDefinition& temp, const LDefinition& tempToUnbox)
const LBoxAllocation& value, const LDefinition& temp,
const LDefinition& tempToUnbox)
: cmpMir_(cmpMir)
{
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
setBoxOperand(Value, value);
setTemp(0, temp);
setTemp(1, tempToUnbox);
}
@@ -2738,8 +2807,10 @@ class LNotV : public LInstructionHelper<1, BOX_PIECES, 3>
LIR_HEADER(NotV)
static const size_t Input = 0;
LNotV(const LDefinition& temp0, const LDefinition& temp1, const LDefinition& temp2)
LNotV(const LBoxAllocation& input, const LDefinition& temp0, const LDefinition& temp1,
const LDefinition& temp2)
{
setBoxOperand(Input, input);
setTemp(0, temp0);
setTemp(1, temp1);
setTemp(2, temp2);
@@ -2777,6 +2848,10 @@ class LBitNotV : public LCallInstructionHelper<1, BOX_PIECES, 0>
LIR_HEADER(BitNotV)
static const size_t Input = 0;
explicit LBitNotV(const LBoxAllocation& input) {
setBoxOperand(Input, input);
}
};
// Binary bitwise operation, taking two 32-bit integers as inputs and returning
@@ -2811,9 +2886,12 @@ class LBitOpV : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
public:
LIR_HEADER(BitOpV)
explicit LBitOpV(JSOp jsop)
LBitOpV(JSOp jsop, const LBoxAllocation& lhs, const LBoxAllocation& rhs)
: jsop_(jsop)
{ }
{
setBoxOperand(LhsInput, lhs);
setBoxOperand(RhsInput, rhs);
}
JSOp jsop() const {
return jsop_;
@@ -2882,6 +2960,10 @@ class LThrow : public LCallInstructionHelper<0, BOX_PIECES, 0>
LIR_HEADER(Throw)
static const size_t Value = 0;
explicit LThrow(const LBoxAllocation& value) {
setBoxOperand(Value, value);
}
};
class LMinMaxBase : public LInstructionHelper<1, 2, 0>
@@ -3314,9 +3396,12 @@ class LBinaryV : public LCallInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 0>
public:
LIR_HEADER(BinaryV)
explicit LBinaryV(JSOp jsop)
LBinaryV(JSOp jsop, const LBoxAllocation& lhs, const LBoxAllocation& rhs)
: jsop_(jsop)
{ }
{
setBoxOperand(LhsInput, lhs);
setBoxOperand(RhsInput, rhs);
}
JSOp jsop() const {
return jsop_;
@@ -3547,6 +3632,10 @@ class LValueToDouble : public LInstructionHelper<1, BOX_PIECES, 0>
LIR_HEADER(ValueToDouble)
static const size_t Input = 0;
explicit LValueToDouble(const LBoxAllocation& input) {
setBoxOperand(Input, input);
}
MToDouble* mir() {
return mir_->toToDouble();
}
@@ -3559,6 +3648,10 @@ class LValueToFloat32 : public LInstructionHelper<1, BOX_PIECES, 0>
LIR_HEADER(ValueToFloat32)
static const size_t Input = 0;
explicit LValueToFloat32(const LBoxAllocation& input) {
setBoxOperand(Input, input);
}
MToFloat32* mir() {
return mir_->toToFloat32();
}
@@ -3585,9 +3678,11 @@ class LValueToInt32 : public LInstructionHelper<1, BOX_PIECES, 2>
public:
LIR_HEADER(ValueToInt32)
LValueToInt32(const LDefinition& temp0, const LDefinition& temp1, Mode mode)
LValueToInt32(const LBoxAllocation& input, const LDefinition& temp0, const LDefinition& temp1,
Mode mode)
: mode_(mode)
{
setBoxOperand(Input, input);
setTemp(0, temp0);
setTemp(1, temp1);
}
@@ -3755,8 +3850,9 @@ class LValueToString : public LInstructionHelper<1, BOX_PIECES, 1>
public:
LIR_HEADER(ValueToString)
explicit LValueToString(const LDefinition& tempToUnbox)
LValueToString(const LBoxAllocation& input, const LDefinition& tempToUnbox)
{
setBoxOperand(Input, input);
setTemp(0, tempToUnbox);
}
@@ -3777,8 +3873,9 @@ class LValueToObjectOrNull : public LInstructionHelper<1, BOX_PIECES, 0>
public:
LIR_HEADER(ValueToObjectOrNull)
explicit LValueToObjectOrNull()
{}
explicit LValueToObjectOrNull(const LBoxAllocation& input) {
setBoxOperand(Input, input);
}
static const size_t Input = 0;
@@ -4093,6 +4190,11 @@ class LBinarySharedStub : public LCallInstructionHelper<BOX_PIECES, 2 * BOX_PIEC
public:
LIR_HEADER(BinarySharedStub)
LBinarySharedStub(const LBoxAllocation& lhs, const LBoxAllocation& rhs) {
setBoxOperand(LhsInput, lhs);
setBoxOperand(RhsInput, rhs);
}
const MBinarySharedStub* mir() const {
return mir_->toBinarySharedStub();
}
@@ -4106,6 +4208,10 @@ class LUnarySharedStub : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0
public:
LIR_HEADER(UnarySharedStub)
explicit LUnarySharedStub(const LBoxAllocation& input) {
setBoxOperand(Input, input);
}
const MUnarySharedStub* mir() const {
return mir_->toUnarySharedStub();
}
@@ -4157,8 +4263,9 @@ class LLambdaArrow : public LInstructionHelper<1, 1 + BOX_PIECES, 0>
static const size_t NewTargetValue = 1;
explicit LLambdaArrow(const LAllocation& scopeChain) {
LLambdaArrow(const LAllocation& scopeChain, const LBoxAllocation& newTarget) {
setOperand(0, scopeChain);
setBoxOperand(NewTargetValue, newTarget);
}
const LAllocation* scopeChain() {
return getOperand(0);
@@ -4795,9 +4902,11 @@ class LStoreElementV : public LInstructionHelper<0, 2 + BOX_PIECES, 0>
public:
LIR_HEADER(StoreElementV)
LStoreElementV(const LAllocation& elements, const LAllocation& index) {
LStoreElementV(const LAllocation& elements, const LAllocation& index,
const LBoxAllocation& value) {
setOperand(0, elements);
setOperand(1, index);
setBoxOperand(Value, value);
}
const char* extraName() const {
@@ -4857,10 +4966,12 @@ class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1>
LIR_HEADER(StoreElementHoleV)
LStoreElementHoleV(const LAllocation& object, const LAllocation& elements,
const LAllocation& index, const LDefinition& temp) {
const LAllocation& index, const LBoxAllocation& value,
const LDefinition& temp) {
setOperand(0, object);
setOperand(1, elements);
setOperand(2, index);
setBoxOperand(Value, value);
setTemp(0, temp);
}
@@ -5019,8 +5130,9 @@ class LArrayPushV : public LInstructionHelper<1, 1 + BOX_PIECES, 1>
public:
LIR_HEADER(ArrayPushV)
LArrayPushV(const LAllocation& object, const LDefinition& temp) {
LArrayPushV(const LAllocation& object, const LBoxAllocation& value, const LDefinition& temp) {
setOperand(0, object);
setBoxOperand(Value, value);
setTemp(0, temp);
}
@@ -5582,7 +5694,8 @@ class LClampVToUint8 : public LInstructionHelper<1, BOX_PIECES, 1>
public:
LIR_HEADER(ClampVToUint8)
explicit LClampVToUint8(const LDefinition& tempFloat) {
LClampVToUint8(const LBoxAllocation& input, const LDefinition& tempFloat) {
setBoxOperand(Input, input);
setTemp(0, tempFloat);
}
@@ -5644,8 +5757,9 @@ class LStoreFixedSlotV : public LInstructionHelper<0, 1 + BOX_PIECES, 0>
public:
LIR_HEADER(StoreFixedSlotV)
explicit LStoreFixedSlotV(const LAllocation& obj) {
LStoreFixedSlotV(const LAllocation& obj, const LBoxAllocation& value) {
setOperand(0, obj);
setBoxOperand(Value, value);
}
static const size_t Value = 1;
@@ -5716,8 +5830,9 @@ class LGetPropertyCacheV : public LInstructionHelper<BOX_PIECES, 1 + BOX_PIECES,
static const size_t Id = 1;
explicit LGetPropertyCacheV(const LAllocation& object) {
LGetPropertyCacheV(const LAllocation& object, const LBoxAllocation& id) {
setOperand(0, object);
setBoxOperand(Id, id);
}
const MGetPropertyCache* mir() const {
return mir_->toGetPropertyCache();
@@ -5733,8 +5848,9 @@ class LGetPropertyCacheT : public LInstructionHelper<1, 1 + BOX_PIECES, 0>
static const size_t Id = 1;
explicit LGetPropertyCacheT(const LAllocation& object) {
LGetPropertyCacheT(const LAllocation& object, const LBoxAllocation& id) {
setOperand(0, object);
setBoxOperand(Id, id);
}
const MGetPropertyCache* mir() const {
return mir_->toGetPropertyCache();
@@ -5788,8 +5904,10 @@ class LSetPropertyPolymorphicV : public LInstructionHelper<0, 1 + BOX_PIECES, 1>
public:
LIR_HEADER(SetPropertyPolymorphicV)
LSetPropertyPolymorphicV(const LAllocation& obj, const LDefinition& temp) {
LSetPropertyPolymorphicV(const LAllocation& obj, const LBoxAllocation& value,
const LDefinition& temp) {
setOperand(0, obj);
setBoxOperand(Value, value);
setTemp(0, temp);
}
@@ -5918,8 +6036,9 @@ class LStoreSlotV : public LInstructionHelper<0, 1 + BOX_PIECES, 0>
public:
LIR_HEADER(StoreSlotV)
explicit LStoreSlotV(const LAllocation& slots) {
LStoreSlotV(const LAllocation& slots, const LBoxAllocation& value) {
setOperand(0, slots);
setBoxOperand(Value, value);
}
static const size_t Value = 1;
@@ -6076,6 +6195,10 @@ class LCallGetProperty : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0
static const size_t Value = 0;
explicit LCallGetProperty(const LBoxAllocation& val) {
setBoxOperand(Value, val);
}
MCallGetProperty* mir() const {
return mir_->toCallGetProperty();
}
@@ -6090,6 +6213,11 @@ class LCallGetElement : public LCallInstructionHelper<BOX_PIECES, 2 * BOX_PIECES
static const size_t LhsInput = 0;
static const size_t RhsInput = BOX_PIECES;
LCallGetElement(const LBoxAllocation& lhs, const LBoxAllocation& rhs) {
setBoxOperand(LhsInput, lhs);
setBoxOperand(RhsInput, rhs);
}
MCallGetElement* mir() const {
return mir_->toCallGetElement();
}
@@ -6104,6 +6232,13 @@ class LCallSetElement : public LCallInstructionHelper<0, 1 + 2 * BOX_PIECES, 0>
static const size_t Index = 1;
static const size_t Value = 1 + BOX_PIECES;
LCallSetElement(const LAllocation& obj, const LBoxAllocation& index,
const LBoxAllocation& value) {
setOperand(0, obj);
setBoxOperand(Index, index);
setBoxOperand(Value, value);
}
const MCallSetElement* mir() const {
return mir_->toCallSetElement();
}
@@ -6117,6 +6252,11 @@ public:
static const size_t Value = 1;
LCallInitElementArray(const LAllocation& obj, const LBoxAllocation& value) {
setOperand(0, obj);
setBoxOperand(Value, value);
}
const MCallInitElementArray* mir() const {
return mir_->toCallInitElementArray();
}
@@ -6128,8 +6268,9 @@ class LCallSetProperty : public LCallInstructionHelper<0, 1 + BOX_PIECES, 0>
public:
LIR_HEADER(CallSetProperty)
explicit LCallSetProperty(const LAllocation& obj) {
LCallSetProperty(const LAllocation& obj, const LBoxAllocation& value) {
setOperand(0, obj);
setBoxOperand(Value, value);
}
static const size_t Value = 1;
@@ -6146,6 +6287,10 @@ class LCallDeleteProperty : public LCallInstructionHelper<1, BOX_PIECES, 0>
static const size_t Value = 0;
explicit LCallDeleteProperty(const LBoxAllocation& value) {
setBoxOperand(Value, value);
}
MDeleteProperty* mir() const {
return mir_->toDeleteProperty();
}
@@ -6159,6 +6304,11 @@ class LCallDeleteElement : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
static const size_t Value = 0;
static const size_t Index = BOX_PIECES;
LCallDeleteElement(const LBoxAllocation& value, const LBoxAllocation& index) {
setBoxOperand(Value, value);
setBoxOperand(Index, index);
}
MDeleteElement* mir() const {
return mir_->toDeleteElement();
}
@@ -6170,10 +6320,13 @@ class LSetPropertyCache : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 4>
public:
LIR_HEADER(SetPropertyCache)
LSetPropertyCache(const LAllocation& object, const LDefinition& temp,
LSetPropertyCache(const LAllocation& object, const LBoxAllocation& id,
const LBoxAllocation& value, const LDefinition& temp,
const LDefinition& tempToUnboxIndex, const LDefinition& tempDouble,
const LDefinition& tempFloat32) {
setOperand(0, object);
setBoxOperand(Id, id);
setBoxOperand(Value, value);
setTemp(0, temp);
setTemp(1, tempToUnboxIndex);
setTemp(2, tempDouble);
@@ -6273,9 +6426,10 @@ class LIsNoIterAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 0>
public:
LIR_HEADER(IsNoIterAndBranch)
LIsNoIterAndBranch(MBasicBlock* ifTrue, MBasicBlock* ifFalse) {
LIsNoIterAndBranch(MBasicBlock* ifTrue, MBasicBlock* ifFalse, const LBoxAllocation& input) {
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
setBoxOperand(Input, input);
}
static const size_t Input = 0;
@@ -6380,7 +6534,9 @@ class LSetFrameArgumentV : public LInstructionHelper<0, BOX_PIECES, 0>
public:
LIR_HEADER(SetFrameArgumentV)
LSetFrameArgumentV() {}
explicit LSetFrameArgumentV(const LBoxAllocation& input) {
setBoxOperand(Input, input);
}
static const size_t Input = 0;
@@ -6479,7 +6635,8 @@ class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1>
public:
LIR_HEADER(TypeBarrierV)
explicit LTypeBarrierV(const LDefinition& temp) {
LTypeBarrierV(const LBoxAllocation& input, const LDefinition& temp) {
setBoxOperand(Input, input);
setTemp(0, temp);
}
@@ -6520,7 +6677,8 @@ class LMonitorTypes : public LInstructionHelper<0, BOX_PIECES, 1>
public:
LIR_HEADER(MonitorTypes)
explicit LMonitorTypes(const LDefinition& temp) {
LMonitorTypes(const LBoxAllocation& input, const LDefinition& temp) {
setBoxOperand(Input, input);
setTemp(0, temp);
}
@@ -6567,8 +6725,10 @@ class LPostWriteBarrierV : public LInstructionHelper<0, 1 + BOX_PIECES, 1>
public:
LIR_HEADER(PostWriteBarrierV)
LPostWriteBarrierV(const LAllocation& obj, const LDefinition& temp) {
LPostWriteBarrierV(const LAllocation& obj, const LBoxAllocation& value,
const LDefinition& temp) {
setOperand(0, obj);
setBoxOperand(Input, value);
setTemp(0, temp);
}
@@ -6629,9 +6789,10 @@ class LPostWriteElementBarrierV : public LInstructionHelper<0, 2 + BOX_PIECES, 1
LIR_HEADER(PostWriteElementBarrierV)
LPostWriteElementBarrierV(const LAllocation& obj, const LAllocation& index,
const LDefinition& temp) {
const LBoxAllocation& value, const LDefinition& temp) {
setOperand(0, obj);
setOperand(1, index);
setBoxOperand(Input, value);
setTemp(0, temp);
}
@@ -6715,7 +6876,8 @@ class LIn : public LCallInstructionHelper<1, BOX_PIECES+1, 0>
{
public:
LIR_HEADER(In)
explicit LIn(const LAllocation& rhs) {
LIn(const LBoxAllocation& lhs, const LAllocation& rhs) {
setBoxOperand(LHS, lhs);
setOperand(RHS, rhs);
}
@@ -6751,7 +6913,8 @@ class LInstanceOfV : public LInstructionHelper<1, BOX_PIECES, 0>
{
public:
LIR_HEADER(InstanceOfV)
LInstanceOfV() {
explicit LInstanceOfV(const LBoxAllocation& lhs) {
setBoxOperand(LHS, lhs);
}
MInstanceOf* mir() const {
@@ -6769,7 +6932,8 @@ class LCallInstanceOf : public LCallInstructionHelper<1, BOX_PIECES+1, 0>
{
public:
LIR_HEADER(CallInstanceOf)
explicit LCallInstanceOf(const LAllocation& rhs) {
LCallInstanceOf(const LBoxAllocation& lhs, const LAllocation& rhs) {
setBoxOperand(LHS, lhs);
setOperand(RHS, rhs);
}
@@ -6808,6 +6972,11 @@ class LIsObject : public LInstructionHelper<1, BOX_PIECES, 0>
public:
LIR_HEADER(IsObject);
static const size_t Input = 0;
explicit LIsObject(const LBoxAllocation& input) {
setBoxOperand(Input, input);
}
MIsObject* mir() const {
return mir_->toIsObject();
}
@@ -6818,9 +6987,10 @@ class LIsObjectAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 0>
public:
LIR_HEADER(IsObjectAndBranch)
LIsObjectAndBranch(MBasicBlock* ifTrue, MBasicBlock* ifFalse) {
LIsObjectAndBranch(MBasicBlock* ifTrue, MBasicBlock* ifFalse, const LBoxAllocation& input) {
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
setBoxOperand(Input, input);
}
static const size_t Input = 0;
@@ -7339,9 +7509,10 @@ class LAssertRangeV : public LInstructionHelper<0, BOX_PIECES, 3>
public:
LIR_HEADER(AssertRangeV)
LAssertRangeV(const LDefinition& temp, const LDefinition& floatTemp1,
const LDefinition& floatTemp2)
LAssertRangeV(const LBoxAllocation& input, const LDefinition& temp,
const LDefinition& floatTemp1, const LDefinition& floatTemp2)
{
setBoxOperand(Input, input);
setTemp(0, temp);
setTemp(1, floatTemp1);
setTemp(2, floatTemp2);
@@ -7387,6 +7558,10 @@ class LAssertResultV : public LInstructionHelper<0, BOX_PIECES, 0>
LIR_HEADER(AssertResultV)
static const size_t Input = 0;
explicit LAssertResultV(const LBoxAllocation& input) {
setBoxOperand(Input, input);
}
};
class LRecompileCheck : public LInstructionHelper<0, 0, 1>
@@ -7411,6 +7586,10 @@ class LLexicalCheck : public LInstructionHelper<0, BOX_PIECES, 0>
public:
LIR_HEADER(LexicalCheck)
explicit LLexicalCheck(const LBoxAllocation& input) {
setBoxOperand(Input, input);
}
MLexicalCheck* mir() {
return mir_->toLexicalCheck();
}
@@ -7545,26 +7724,39 @@ class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS>
class LCheckReturn : public LCallInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 0>
{
public:
LIR_HEADER(CheckReturn)
LCheckReturn(const LBoxAllocation& retVal, const LBoxAllocation& thisVal) {
setBoxOperand(ReturnValue, retVal);
setBoxOperand(ThisValue, thisVal);
}
static const size_t ReturnValue = 0;
static const size_t ThisValue = BOX_PIECES;
LIR_HEADER(CheckReturn)
};
class LCheckObjCoercible : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
{
public:
LIR_HEADER(CheckObjCoercible)
static const size_t CheckValue = 0;
LIR_HEADER(CheckObjCoercible)
explicit LCheckObjCoercible(const LBoxAllocation& value) {
setBoxOperand(CheckValue, value);
}
};
class LDebugCheckSelfHosted : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
{
public:
LIR_HEADER(DebugCheckSelfHosted)
static const size_t CheckValue = 0;
LIR_HEADER(DebugCheckSelfHosted)
explicit LDebugCheckSelfHosted(const LBoxAllocation& value) {
setBoxOperand(CheckValue, value);
}
};
} // namespace jit
+1
View File
@@ -14,6 +14,7 @@
_(OsiPoint) \
_(MoveGroup) \
_(Integer) \
_(Integer64) \
_(Pointer) \
_(Double) \
_(Float32) \
+45 -19
View File
@@ -108,6 +108,28 @@ LIRGeneratorShared::defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, M
add(lir);
}
template <size_t Ops, size_t Temps> void
LIRGeneratorShared::defineInt64(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
LDefinition::Policy policy)
{
// Call instructions should use defineReturn.
MOZ_ASSERT(!lir->isCall());
uint32_t vreg = getVirtualRegister();
#if JS_BITS_PER_WORD == 32
lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL, policy));
lir->setDef(1, LDefinition(vreg + 1, LDefinition::GENERAL, policy));
getVirtualRegister();
#else
lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL, policy));
#endif
lir->setMir(mir);
mir->setVirtualRegister(vreg);
add(lir);
}
void
LIRGeneratorShared::defineSharedStubReturn(LInstruction* lir, MDefinition* mir)
{
@@ -298,8 +320,7 @@ LIRGeneratorShared::redefine(MDefinition* def, MDefinition* as)
break;
}
case MIRType_Value: {
LAssertResultV* check = new(alloc()) LAssertResultV();
useBox(check, LAssertRangeV::Input, def);
LAssertResultV* check = new(alloc()) LAssertResultV(useBox(def));
add(check, def->toInstruction());
break;
}
@@ -606,35 +627,40 @@ LIRGeneratorShared::useRegisterForTypedLoad(MDefinition* mir, MIRType type)
return useRegisterAtStart(mir);
}
void
LIRGeneratorShared::useBox(LInstruction* lir, size_t n, MDefinition* mir,
LUse::Policy policy, bool useAtStart)
LBoxAllocation
LIRGeneratorShared::useBox(MDefinition* mir, LUse::Policy policy, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
ensureDefined(mir);
lir->setOperand(n, LUse(mir->virtualRegister(), policy, useAtStart));
#if defined(JS_NUNBOX32)
lir->setOperand(n + 1, LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
return LBoxAllocation(LUse(mir->virtualRegister(), policy, useAtStart),
LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
#else
return LBoxAllocation(LUse(mir->virtualRegister(), policy, useAtStart));
#endif
}
void
LIRGeneratorShared::useBoxOrTypedOrConstant(LInstruction* lir, size_t n, MDefinition* mir,
bool useConstant)
LBoxAllocation
LIRGeneratorShared::useBoxOrTypedOrConstant(MDefinition* mir, bool useConstant)
{
if (mir->type() == MIRType_Value) {
useBox(lir, n, mir);
return;
if (mir->type() == MIRType_Value)
return useBox(mir);
if (useConstant && mir->isConstant()) {
#if defined(JS_NUNBOX32)
return LBoxAllocation(LAllocation(mir->toConstant()), LAllocation());
#else
return LBoxAllocation(LAllocation(mir->toConstant()));
#endif
}
if (useConstant && mir->isConstant())
lir->setOperand(n, LAllocation(mir->toConstant()));
else
lir->setOperand(n, useRegister(mir));
#if defined(JS_NUNBOX32)
lir->setOperand(n + 1, LAllocation());
return LBoxAllocation(useRegister(mir), LAllocation());
#else
return LBoxAllocation(useRegister(mir));
#endif
}
+3
View File
@@ -92,6 +92,9 @@ LIRGeneratorShared::visitConstant(MConstant* ins)
case MIRType_Int32:
define(new(alloc()) LInteger(ins->toInt32()), ins);
break;
case MIRType_Int64:
defineInt64(new(alloc()) LInteger64(ins->toInt64()), ins);
break;
case MIRType_String:
define(new(alloc()) LPointer(ins->toString()), ins);
break;
+10 -7
View File
@@ -142,6 +142,10 @@ class LIRGeneratorShared : public MDefinitionVisitor
inline void defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir,
LDefinition::Policy policy = LDefinition::REGISTER);
template <size_t Ops, size_t Temps>
inline void defineInt64(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
LDefinition::Policy policy = LDefinition::REGISTER);
template <size_t Ops, size_t Temps>
inline void defineSinCos(LInstructionHelper<2, Ops, Temps> *lir, MDefinition *mir,
LDefinition::Policy policy = LDefinition::REGISTER);
@@ -159,14 +163,13 @@ class LIRGeneratorShared : public MDefinitionVisitor
template <size_t Ops, size_t Temps>
inline void defineReuseInput(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir, uint32_t operand);
// Adds a use at operand |n| of a value-typed insturction.
inline void useBox(LInstruction* lir, size_t n, MDefinition* mir,
LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
// Returns a box allocation for a Value-typed instruction.
inline LBoxAllocation useBox(MDefinition* mir, LUse::Policy policy = LUse::REGISTER,
bool useAtStart = false);
// Adds a use at operand |n|. The use is either typed, a Value, or a
// constant (if useConstant is true).
inline void useBoxOrTypedOrConstant(LInstruction* lir, size_t n, MDefinition* mir,
bool useConstant);
// Returns a box allocation. The use is either typed, a Value, or
// a constant (if useConstant is true).
inline LBoxAllocation useBoxOrTypedOrConstant(MDefinition* mir, bool useConstant);
// Rather than defining a new virtual register, sets |ins| to have the same
// virtual register as |as|.
+2
View File
@@ -45,6 +45,7 @@ ABIArgGenerator::next(MIRType type)
}
switch (type) {
case MIRType_Int32:
case MIRType_Int64:
case MIRType_Pointer:
current_ = ABIArg(IntArgRegs[regIndex_++]);
break;
@@ -69,6 +70,7 @@ ABIArgGenerator::next(MIRType type)
#else
switch (type) {
case MIRType_Int32:
case MIRType_Int64:
case MIRType_Pointer:
if (intRegIndex_ == NumIntArgRegs) {
current_ = ABIArg(stackOffset_);
+3 -3
View File
@@ -14,13 +14,13 @@
using namespace js;
using namespace js::jit;
void
LIRGeneratorX64::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register, bool useAtStart)
LBoxAllocation
LIRGeneratorX64::useBoxFixed(MDefinition* mir, Register reg1, Register, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
ensureDefined(mir);
lir->setOperand(n, LUse(reg1, mir->virtualRegister(), useAtStart));
return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart));
}
LAllocation
+2 -2
View File
@@ -23,8 +23,8 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
void defineUntypedPhi(MPhi* phi, size_t lirIndex);
// Adds a use at operand |n| of a value-typed insturction.
void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register, bool useAtStart = false);
// Returns a box allocation. reg2 is ignored on 64-bit platforms.
LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register, bool useAtStart = false);
// x86 has constraints on what registers can be formatted for 1-byte
// stores and loads; on x64 all registers are okay.
+12
View File
@@ -17,6 +17,18 @@ namespace jit {
//{{{ check_macroassembler_style
// ===============================================================
void
MacroAssembler::move64(Imm64 imm, Register64 dest)
{
movq(ImmWord(imm.value), dest.reg);
}
void
MacroAssembler::move64(Register64 src, Register64 dest)
{
movq(src.reg, dest.reg);
}
void
MacroAssembler::andPtr(Register src, Register dest)
{
-3
View File
@@ -573,9 +573,6 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
void movePtr(ImmGCPtr imm, Register dest) {
movq(imm, dest);
}
void move64(Register64 src, Register64 dest) {
movq(src.reg, dest.reg);
}
void loadPtr(AbsoluteAddress address, Register dest) {
if (X86Encoding::IsAddressImmediate(address.addr)) {
movq(Operand(address), dest);
@@ -2408,7 +2408,11 @@ CodeGeneratorX86Shared::visitFloat32x4ToUint32x4(LFloat32x4ToUint32x4* ins)
masm.vcmpleps(Operand(in), scratch, scratch);
masm.vmovmskps(scratch, temp);
masm.cmp32(temp, Imm32(15));
bailoutIf(Assembler::NotEqual, ins->snapshot());
if (gen->compilingAsmJS())
masm.j(Assembler::NotEqual, wasm::JumpTarget::ConversionError);
else
bailoutIf(Assembler::NotEqual, ins->snapshot());
// TODO: If the majority of lanes are A-lanes, it could be faster to compute
// A first, use vmovmskps to check for any non-A-lanes and handle them in
@@ -2443,7 +2447,11 @@ CodeGeneratorX86Shared::visitFloat32x4ToUint32x4(LFloat32x4ToUint32x4* ins)
// the remaining negative lanes in B.
masm.vmovmskps(scratch, temp);
masm.cmp32(temp, Imm32(0));
bailoutIf(Assembler::NotEqual, ins->snapshot());
if (gen->compilingAsmJS())
masm.j(Assembler::NotEqual, wasm::JumpTarget::ConversionError);
else
bailoutIf(Assembler::NotEqual, ins->snapshot());
}
void
@@ -3425,24 +3433,11 @@ CodeGeneratorX86Shared::visitSimdShift(LSimdShift* ins)
FloatRegister out = ToFloatRegister(ins->output());
MOZ_ASSERT(ToFloatRegister(ins->vector()) == out); // defineReuseInput(0);
// If the shift count is greater than 31, this will just zero all lanes by
// default for lsh and ursh, and for rsh extend the sign bit to all bits,
// per the SIMD.js spec (as of March 19th 2015).
// If the shift count is out of range, only use the low 5 bits.
const LAllocation* val = ins->value();
if (val->isConstant()) {
uint32_t c = uint32_t(ToInt32(val));
if (c > 31) {
switch (ins->operation()) {
case MSimdShift::lsh:
case MSimdShift::ursh:
masm.zeroInt32x4(out);
return;
default:
c = 31;
break;
}
}
Imm32 count(c);
MOZ_ASSERT(ins->temp()->isBogusTemp());
Imm32 count(uint32_t(ToInt32(val)) % 32);
switch (ins->operation()) {
case MSimdShift::lsh:
masm.packedLeftShiftByScalar(count, out);
@@ -3457,9 +3452,13 @@ CodeGeneratorX86Shared::visitSimdShift(LSimdShift* ins)
MOZ_CRASH("unexpected SIMD bitwise op");
}
// Truncate val to 5 bits. We should have a temp register for that.
MOZ_ASSERT(val->isRegister());
Register count = ToRegister(ins->temp());
masm.mov(ToRegister(val), count);
masm.andl(Imm32(31), count);
ScratchFloat32Scope scratch(masm);
masm.vmovd(ToRegister(val), scratch);
masm.vmovd(count, scratch);
switch (ins->operation()) {
case MSimdShift::lsh:
+4 -2
View File
@@ -254,9 +254,11 @@ class LTableSwitchV : public LInstructionHelper<0, BOX_PIECES, 3>
public:
LIR_HEADER(TableSwitchV)
LTableSwitchV(const LDefinition& inputCopy, const LDefinition& floatCopy,
const LDefinition& jumpTablePointer, MTableSwitch* ins)
LTableSwitchV(const LBoxAllocation& input, const LDefinition& inputCopy,
const LDefinition& floatCopy, const LDefinition& jumpTablePointer,
MTableSwitch* ins)
{
setBoxOperand(InputValue, input);
setTemp(0, inputCopy);
setTemp(1, floatCopy);
setTemp(2, jumpTablePointer);
@@ -29,7 +29,8 @@ LIRGeneratorX86Shared::newLTableSwitch(const LAllocation& in, const LDefinition&
LTableSwitchV*
LIRGeneratorX86Shared::newLTableSwitchV(MTableSwitch* tableswitch)
{
return new(alloc()) LTableSwitchV(temp(), tempDouble(), temp(), tableswitch);
return new(alloc()) LTableSwitchV(useBox(tableswitch->getOperand(0)),
temp(), tempDouble(), temp(), tableswitch);
}
void
+4 -2
View File
@@ -61,9 +61,11 @@ class LUnboxFloatingPoint : public LInstructionHelper<1, 2, 0>
static const size_t Input = 0;
LUnboxFloatingPoint(MIRType type)
LUnboxFloatingPoint(const LBoxAllocation& input, MIRType type)
: type_(type)
{ }
{
setBoxOperand(Input, input);
}
MUnbox* mir() const {
return mir_->toUnbox();
+5 -7
View File
@@ -14,16 +14,15 @@
using namespace js;
using namespace js::jit;
void
LIRGeneratorX86::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1,
Register reg2, bool useAtStart)
LBoxAllocation
LIRGeneratorX86::useBoxFixed(MDefinition* mir, Register reg1, Register reg2, bool useAtStart)
{
MOZ_ASSERT(mir->type() == MIRType_Value);
MOZ_ASSERT(reg1 != reg2);
ensureDefined(mir);
lir->setOperand(n, LUse(reg1, mir->virtualRegister(), useAtStart));
lir->setOperand(n + 1, LUse(reg2, VirtualRegisterOfPayload(mir), useAtStart));
return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart),
LUse(reg2, VirtualRegisterOfPayload(mir), useAtStart));
}
LAllocation
@@ -105,10 +104,9 @@ LIRGeneratorX86::visitUnbox(MUnbox* unbox)
ensureDefined(inner);
if (IsFloatingPointType(unbox->type())) {
LUnboxFloatingPoint* lir = new(alloc()) LUnboxFloatingPoint(unbox->type());
LUnboxFloatingPoint* lir = new(alloc()) LUnboxFloatingPoint(useBox(inner), unbox->type());
if (unbox->fallible())
assignSnapshot(lir, unbox->bailoutKind());
useBox(lir, LUnboxFloatingPoint::Input, inner);
define(lir, unbox);
return;
}
+3 -4
View File
@@ -20,10 +20,9 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
{ }
protected:
// Adds a box input to an instruction, setting operand |n| to the type and
// |n+1| to the payload.
void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
// Returns a box allocation with type set to reg1 and payload set to reg2.
LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register reg2,
bool useAtStart = false);
// It's a trap! On x86, the 1-byte store can only use one of
// {al,bl,cl,dl,ah,bh,ch,dh}. That means if the register allocator
+15
View File
@@ -15,6 +15,21 @@ namespace js {
namespace jit {
//{{{ check_macroassembler_style
void
MacroAssembler::move64(Imm64 imm, Register64 dest)
{
movl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
movl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
}
void
MacroAssembler::move64(Register64 src, Register64 dest)
{
movl(src.low, dest.low);
movl(src.high, dest.high);
}
// ===============================================================
// Logical functions
-4
View File
@@ -601,10 +601,6 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
void movePtr(ImmGCPtr imm, Register dest) {
movl(imm, dest);
}
void move64(Register64 src, Register64 dest) {
movl(src.low, dest.low);
movl(src.high, dest.high);
}
void loadPtr(const Address& address, Register dest) {
movl(Operand(address), dest);
}
+18 -42
View File
@@ -14,68 +14,68 @@ var Uint32x4 = SIMD.Uint32x4;
// Int8 shifts.
function lsh8(a, b) {
return (b >>> 0) >= 8 ? 0 : (a << b) << 24 >> 24;
return (a << (b & 7)) << 24 >> 24;
}
function rsha8(a, b) {
return (a >> Math.min(b >>> 0, 7)) << 24 >> 24;
return (a >> (b & 7)) << 24 >> 24;
}
function rshl8(a, b) {
return (b >>> 0) >= 8 ? 0 : (a >>> b) << 24 >> 24;
return (a >>> (b & 7)) << 24 >> 24;
}
// Int16 shifts.
function lsh16(a, b) {
return (b >>> 0) >= 16 ? 0 : (a << b) << 16 >> 16;
return (a << (b & 15)) << 16 >> 16;
}
function rsha16(a, b) {
return (a >> Math.min(b >>> 0, 15)) << 16 >> 16;
return (a >> (b & 15)) << 16 >> 16;
}
function rshl16(a, b) {
return (b >>> 0) >= 16 ? 0 : (a >>> b) << 16 >> 16;
return (a >>> (b & 15)) << 16 >> 16;
}
// Int32 shifts.
function lsh32(a, b) {
return (b >>> 0) >= 32 ? 0 : (a << b) | 0;
return (a << (b & 31)) | 0;
}
function rsha32(a, b) {
return (a >> Math.min(b >>> 0, 31)) | 0;
return (a >> (b & 31)) | 0;
}
function rshl32(a, b) {
return (b >>> 0) >= 32 ? 0 : (a >>> b) | 0;
return (a >>> (b & 31)) | 0;
}
// Uint8 shifts.
function ulsh8(a, b) {
return (b >>> 0) >= 8 ? 0 : (a << b) << 24 >>> 24;
return (a << (b & 7)) << 24 >>> 24;
}
function ursha8(a, b) {
return ((a << 24 >> 24) >> Math.min(b >>> 0, 7)) << 24 >>> 24;
return ((a << 24 >> 24) >> (b & 7)) << 24 >>> 24;
}
function urshl8(a, b) {
return (b >>> 0) >= 8 ? 0 : (a >>> b) << 24 >>> 24;
return (a >>> (b & 7)) << 24 >>> 24;
}
// Uint16 shifts.
function ulsh16(a, b) {
return (b >>> 0) >= 16 ? 0 : (a << b) << 16 >>> 16;
return (a << (b & 15)) << 16 >>> 16;
}
function ursha16(a, b) {
return ((a << 16 >> 16) >> Math.min(b >>> 0, 15)) << 16 >>> 16;
return ((a << 16 >> 16) >> (b & 15)) << 16 >>> 16;
}
function urshl16(a, b) {
return (b >>> 0) >= 16 ? 0 : (a >>> b) << 16 >>> 16;
return (a >>> (b & 15)) << 16 >>> 16;
}
// Uint32 shifts.
function ulsh32(a, b) {
return (b >>> 0) >= 32 ? 0 : (a << b) >>> 0;
return (a << (b & 31)) >>> 0;
}
function ursha32(a, b) {
return ((a | 0) >> Math.min(b >>> 0, 31)) >>> 0;
return ((a | 0) >> (b & 31)) >>> 0;
}
function urshl32(a, b) {
return (b >>> 0) >= 32 ? 0 : (a >>> b) >>> 0;
return (a >>> (b & 31)) >>> 0;
}
function test() {
@@ -92,8 +92,6 @@ function test() {
for (var bits = -2; bits < 12; bits++) {
testBinaryScalarFunc(v, bits, Int8x16.shiftLeftByScalar, lsh8);
testBinaryScalarFunc(v, bits, Int8x16.shiftRightByScalar, rsha8);
testBinaryScalarFunc(v, bits, Int8x16.shiftRightArithmeticByScalar, rsha8);
testBinaryScalarFunc(v, bits, Int8x16.shiftRightLogicalByScalar, rshl8);
}
// Test that the shift count is coerced to an int32.
testBinaryScalarFunc(v, undefined, Int8x16.shiftLeftByScalar, lsh8);
@@ -108,8 +106,6 @@ function test() {
for (var bits = -2; bits < 20; bits++) {
testBinaryScalarFunc(v, bits, Int16x8.shiftLeftByScalar, lsh16);
testBinaryScalarFunc(v, bits, Int16x8.shiftRightByScalar, rsha16);
testBinaryScalarFunc(v, bits, Int16x8.shiftRightArithmeticByScalar, rsha16);
testBinaryScalarFunc(v, bits, Int16x8.shiftRightLogicalByScalar, rshl16);
}
// Test that the shift count is coerced to an int32.
testBinaryScalarFunc(v, undefined, Int16x8.shiftLeftByScalar, lsh16);
@@ -124,8 +120,6 @@ function test() {
for (var bits = -2; bits < 36; bits++) {
testBinaryScalarFunc(v, bits, Int32x4.shiftLeftByScalar, lsh32);
testBinaryScalarFunc(v, bits, Int32x4.shiftRightByScalar, rsha32);
testBinaryScalarFunc(v, bits, Int32x4.shiftRightArithmeticByScalar, rsha32);
testBinaryScalarFunc(v, bits, Int32x4.shiftRightLogicalByScalar, rshl32);
}
// Test that the shift count is coerced to an int32.
testBinaryScalarFunc(v, undefined, Int32x4.shiftLeftByScalar, lsh32);
@@ -141,8 +135,6 @@ function test() {
for (var bits = -2; bits < 12; bits++) {
testBinaryScalarFunc(v, bits, Uint8x16.shiftLeftByScalar, ulsh8);
testBinaryScalarFunc(v, bits, Uint8x16.shiftRightByScalar, urshl8);
testBinaryScalarFunc(v, bits, Uint8x16.shiftRightArithmeticByScalar, ursha8);
testBinaryScalarFunc(v, bits, Uint8x16.shiftRightLogicalByScalar, urshl8);
}
// Test that the shift count is coerced to an int32.
testBinaryScalarFunc(v, undefined, Uint8x16.shiftLeftByScalar, ulsh8);
@@ -157,8 +149,6 @@ function test() {
for (var bits = -2; bits < 20; bits++) {
testBinaryScalarFunc(v, bits, Uint16x8.shiftLeftByScalar, ulsh16);
testBinaryScalarFunc(v, bits, Uint16x8.shiftRightByScalar, urshl16);
testBinaryScalarFunc(v, bits, Uint16x8.shiftRightArithmeticByScalar, ursha16);
testBinaryScalarFunc(v, bits, Uint16x8.shiftRightLogicalByScalar, urshl16);
}
// Test that the shift count is coerced to an int32.
testBinaryScalarFunc(v, undefined, Uint16x8.shiftLeftByScalar, ulsh16);
@@ -174,8 +164,6 @@ function test() {
for (var bits = -2; bits < 36; bits++) {
testBinaryScalarFunc(v, bits, Uint32x4.shiftLeftByScalar, ulsh32);
testBinaryScalarFunc(v, bits, Uint32x4.shiftRightByScalar, urshl32);
testBinaryScalarFunc(v, bits, Uint32x4.shiftRightArithmeticByScalar, ursha32);
testBinaryScalarFunc(v, bits, Uint32x4.shiftRightLogicalByScalar, urshl32);
}
// Test that the shift count is coerced to an int32.
testBinaryScalarFunc(v, undefined, Uint32x4.shiftLeftByScalar, ulsh32);
@@ -186,38 +174,26 @@ function test() {
var v = SIMD.Int8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);
assertThrowsInstanceOf(() => SIMD.Int8x16.shiftLeftByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Int8x16.shiftRightByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Int8x16.shiftRightArithmeticByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Int8x16.shiftRightLogicalByScalar(v, bad), TestError);
var v = SIMD.Int16x8(1,2,3,4,5,6,7,8);
assertThrowsInstanceOf(() => SIMD.Int16x8.shiftLeftByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Int16x8.shiftRightByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Int16x8.shiftRightArithmeticByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Int16x8.shiftRightLogicalByScalar(v, bad), TestError);
var v = SIMD.Int32x4(1,2,3,4);
assertThrowsInstanceOf(() => SIMD.Int32x4.shiftLeftByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Int32x4.shiftRightByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Int32x4.shiftRightArithmeticByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Int32x4.shiftRightLogicalByScalar(v, bad), TestError);
var v = SIMD.Uint8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);
assertThrowsInstanceOf(() => SIMD.Uint8x16.shiftLeftByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Uint8x16.shiftRightByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Uint8x16.shiftRightArithmeticByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Uint8x16.shiftRightLogicalByScalar(v, bad), TestError);
var v = SIMD.Uint16x8(1,2,3,4,5,6,7,8);
assertThrowsInstanceOf(() => SIMD.Uint16x8.shiftLeftByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Uint16x8.shiftRightByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Uint16x8.shiftRightArithmeticByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Uint16x8.shiftRightLogicalByScalar(v, bad), TestError);
var v = SIMD.Uint32x4(1,2,3,4);
assertThrowsInstanceOf(() => SIMD.Uint32x4.shiftLeftByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Uint32x4.shiftRightByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Uint32x4.shiftRightArithmeticByScalar(v, bad), TestError);
assertThrowsInstanceOf(() => SIMD.Uint32x4.shiftRightLogicalByScalar(v, bad), TestError);
if (typeof reportCompare === "function")
reportCompare(true, true);
+1 -1
View File
@@ -527,7 +527,7 @@ random-if(cocoaWidget) == 350506-1.html 350506-1-ref.html
== 356774-1.html 356774-1-ref.html
== 356775-1.html 356775-1-ref.html
== 359869-1.html 359869-1-ref.html
fails-if(OSX) != 359903-1.html 359903-1-ref.html # erosion of padding removed in bug 1010675 # failure is bug 1145589
!= 359903-1.html 359903-1-ref.html # erosion of padding removed in bug 1010675
!= 359903-2.html 359903-2-ref.html # erosion of padding removed in bug 1010675
== 360065-1.html 360065-1-ref.html
== 360746-1.html 360746-1-ref.html
@@ -1,8 +1,8 @@
# B2G failures: bug 855352.
fails-if(B2G||Mulet||Android) fuzzy-if(OSX==1006,8,128) skip-if((B2G&&browserIsRemote)||Mulet) == simple.html simple-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
fails-if(B2G||Mulet||Android) fuzzy-if(OSX==1006,8,64) skip-if((B2G&&browserIsRemote)||Mulet) == rtl.html rtl-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
fails-if(B2G||Mulet||Android) fuzzy-if(OSX==1006,8,128) skip-if((B2G&&browserIsRemote)||Mulet) == size.html simple-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
fails-if(B2G||Mulet||Android) fuzzy-if(OSX==1006,8,64) skip-if((B2G&&browserIsRemote)||Mulet) == background.html background-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
fails-if(B2G||Mulet||Android) fuzzy-if(OSX==1006,8,152) skip-if((B2G&&browserIsRemote)||Mulet) == simple.html simple-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
fails-if(B2G||Mulet||Android) fuzzy-if(OSX==1006,8,76) skip-if((B2G&&browserIsRemote)||Mulet) == rtl.html rtl-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
fails-if(B2G||Mulet||Android) fuzzy-if(OSX==1006,8,152) skip-if((B2G&&browserIsRemote)||Mulet) == size.html simple-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
fails-if(B2G||Mulet||Android) fuzzy-if(OSX==1006,8,76) skip-if((B2G&&browserIsRemote)||Mulet) == background.html background-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
fails-if(B2G||Mulet||Android) skip-if((B2G&&browserIsRemote)||Mulet) == style.html style-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
!= width-clip.html width-clip-ref.html
fails-if(B2G||Mulet||Android) == color-inherit.html color-inherit-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+10 -10
View File
@@ -3,8 +3,8 @@
!= data:text/plain,HELLO about:blank
# these tests make sure async reftests work:
skip-if(B2G||Mulet) == test-async.xul test-async-ref.xul # bug 785074 # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) == test-async.html test-async-ref.html # bug 785074 # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) == test-async.xul test-async-ref.xul # bug 785074 # Initial mulet triage: parity with B2G
skip-if(B2G||Mulet) == test-async.html test-async-ref.html # bug 785074 # Initial mulet triage: parity with B2G
# test that zoom works (and really zooms, not just scales)
== test-zoom.html test-zoom-ref.html
@@ -36,15 +36,15 @@ HTTP == data:text/html,<div>Text</div> default.html
!= blank.html default.html
HTTP != blank.html default.html
skip-if(B2G||Mulet) HTTP(..) == filter-1.xhtml filter-1-ref.xhtml # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) HTTP(..) == filter-2.xhtml filter-2-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) HTTP(..) == filter-1.xhtml filter-1-ref.xhtml # Initial mulet triage: parity with B2G
skip-if(B2G||Mulet) HTTP(..) == filter-2.xhtml filter-2-ref.xhtml # bug 773482 # Initial mulet triage: parity with B2G
# test that the MozReftestInvalidate event fires
== invalidation.html about:blank
== zoom-invalidation.html zoom-invalidation-ref.html # bug 773482
# test that xulRuntime.OS works
skip-if(B2G||B2GDT||Mulet) fails-if(xulRuntime.OS!="Linux"&&!Android) == data:text/html,<body>Linux data:text/html,<script>document.write(navigator.platform.substr(0,5))</script> # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) fails-if(xulRuntime.OS!="Linux"&&!Android) == data:text/html,<body>Linux data:text/html,<script>document.write(navigator.platform.substr(0,5))</script> # Initial mulet triage: parity with B2G
fails-if(xulRuntime.OS!="WINNT") == data:text/html,<body>Win data:text/html,<script>document.write(navigator.platform.substr(0,3))</script>
fails-if(xulRuntime.OS!="Darwin") == data:text/html,<body>Mac data:text/html,<script>document.write(navigator.platform.substr(0,3))</script>
@@ -76,9 +76,9 @@ skip-if(Android&&AndroidVersion==15) != corners-4.html corners-4-ref.html # s
# Test that the harness gives the correct page dimensions.
!= page-width-3.9in.html page-width-4in.html
skip-if(B2G||B2GDT||Mulet) == page-width-4.1in.html page-width-4in.html # bug 774396 # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||B2GDT||Mulet) == page-width-auto.html page-width-4in.html # bug 774396 # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||B2GDT||Mulet) != page-height-2in.html page-height-2.1in.html # bug 774396 # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) == page-width-4.1in.html page-width-4in.html # bug 774396 # Initial mulet triage: parity with B2G
skip-if(B2G||Mulet) == page-width-auto.html page-width-4in.html # bug 774396 # Initial mulet triage: parity with B2G
skip-if(B2G||Mulet) != page-height-2in.html page-height-2.1in.html # bug 774396 # Initial mulet triage: parity with B2G
== page-height-2in.html page-height-nobreak.html
== page-height-2.1in.html page-height-forcebreak.html
@@ -91,8 +91,8 @@ needs-focus == data:text/plain, about:blank
# Sanity check of viewport+displayport overrides
pref(dom.meta-viewport.enabled,true) skip-if(!browserIsRemote) != test-displayport-2.html test-displayport-ref.html # bug 593168
skip-if(!browserIsRemote) fails-if(OSX&&layersGPUAccelerated) fuzzy-if(layersOMTC,1,1390) random-if(Android&&AndroidVersion<15) random-if(B2G||B2GDT||Mulet) random-if(transparentScrollbars) == 647192-1.html 647192-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(!browserIsRemote||((B2G&&browserIsRemote)||Mulet)) == 656041-1.html 656041-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(!browserIsRemote) fails-if(OSX&&layersGPUAccelerated) fuzzy-if(layersOMTC,1,1390) random-if(Android&&AndroidVersion<15) random-if(B2G||Mulet) random-if(transparentScrollbars) == 647192-1.html 647192-1-ref.html # Initial mulet triage: parity with B2G
skip-if(!browserIsRemote||((B2G&&browserIsRemote)||Mulet)) == 656041-1.html 656041-1-ref.html # Initial mulet triage: parity with B2G
pref(dom.meta-viewport.enabled,true) skip-if(!browserIsRemote||layersOMTC) == test-displayport-bg.html test-displayport-ref.html # bug 694706
# IPC Position-fixed frames/layers test
+2
View File
@@ -694,10 +694,12 @@ input[type="color"]:-moz-system-metric(color-picker-available):active:hover,
input[type="reset"]:active:hover,
input[type="button"]:active:hover,
input[type="submit"]:active:hover {
%ifndef XP_MACOSX
padding-block-start: 0px;
-moz-padding-end: 5px;
padding-block-end: 0px;
-moz-padding-start: 7px;
%endif
border-style: inset;
background-color: ButtonFace;
}
+2 -1
View File
@@ -10,8 +10,9 @@ _HARNESS_FILES = \
$(srcdir)/reftestcommandline.py \
$(srcdir)/remotereftest.py \
$(srcdir)/runreftestb2g.py \
$(srcdir)/b2g_desktop.py \
$(srcdir)/runreftestmulet.py \
$(srcdir)/gaia_lock_screen.js \
$(srcdir)/output.py \
automation.py \
$(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanager.py \
$(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py \
+1
View File
@@ -2,6 +2,7 @@ reftest.jar:
% content reftest %content/
* content/reftest-content.js (reftest-content.js)
content/httpd.jsm (../../../netwerk/test/httpserver/httpd.js)
content/StructuredLog.jsm (../../../testing/modules/StructuredLog.jsm)
#ifdef BOOTSTRAP
* content/reftest.jsm (reftest.js)
#else
+179 -82
View File
@@ -4,7 +4,6 @@
from __future__ import absolute_import, unicode_literals
import mozpack.path as mozpath
import os
import re
import sys
@@ -18,7 +17,6 @@ from mozbuild.base import (
)
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
@@ -33,8 +31,8 @@ If you have a B2G build, this can be found in
'''.lstrip()
GAIA_PROFILE_NOT_FOUND = '''
The %s command requires a non-debug gaia profile. Either pass in --profile,
or set the GAIA_PROFILE environment variable.
The reftest command requires a non-debug gaia profile on Mulet.
Either pass in --profile, or set the GAIA_PROFILE environment variable.
If you do not have a non-debug gaia profile, you can build one:
$ git clone https://github.com/mozilla-b2g/gaia
@@ -45,8 +43,8 @@ The profile should be generated in a directory called 'profile'.
'''.lstrip()
GAIA_PROFILE_IS_DEBUG = '''
The %s command requires a non-debug gaia profile. The specified profile,
%s, is a debug profile.
The reftest command requires a non-debug gaia profile on Mulet.
The specified profile, %s, is a debug profile.
If you do not have a non-debug gaia profile, you can build one:
$ git clone https://github.com/mozilla-b2g/gaia
@@ -57,12 +55,13 @@ The profile should be generated in a directory called 'profile'.
'''.lstrip()
MARIONETTE_DISABLED = '''
The %s command requires a marionette enabled build.
The reftest command requires a marionette enabled build on Mulet.
Add 'ENABLE_MARIONETTE=1' to your mozconfig file and re-build the application.
Your currently active mozconfig is %s.
'''.lstrip()
class ReftestRunner(MozbuildObject):
"""Easily run reftests.
@@ -83,13 +82,28 @@ class ReftestRunner(MozbuildObject):
def _make_shell_string(self, s):
return "'%s'" % re.sub("'", r"'\''", s)
def _setup_objdir(self, **kwargs):
# reftest imports will happen from the objdir
sys.path.insert(0, self.reftest_dir)
if not kwargs["tests"]:
test_subdir = {
"reftest": os.path.join('layout', 'reftests'),
"crashtest": os.path.join('layout', 'crashtest'),
}[kwargs['suite']]
kwargs["tests"] = [test_subdir]
tests = os.path.join(self.reftest_dir, 'tests')
if not os.path.isdir(tests):
os.symlink(self.topsrcdir, tests)
def run_b2g_test(self, b2g_home=None, xre_path=None, **kwargs):
"""Runs a b2g reftest.
filter is a regular expression (in JS syntax, as could be passed to the
RegExp constructor) to select which reftests to run from the manifest.
test_file is a path to a test file. It can be a relative path from the
tests is a list of paths. It can be a relative path from the
top source directory, an absolute filename, or a directory containing
test files.
@@ -99,21 +113,8 @@ class ReftestRunner(MozbuildObject):
if kwargs["suite"] not in ('reftest', 'crashtest'):
raise Exception('None or unrecognized reftest suite type.')
sys.path.insert(0, self.reftest_dir)
test_subdir = {"reftest": os.path.join('layout', 'reftests'),
"crashtest": os.path.join('layout', 'crashtest')}[kwargs["suite"]]
# Find the manifest file
if not kwargs["tests"]:
if not os.path.exists(os.path.join(self.topsrcdir, test_subdir)):
test_file = mozpath.relpath(os.path.abspath(test_subdir),
self.topsrcdir)
kwargs["tests"] = [test_subdir]
tests = os.path.join(self.reftest_dir, 'tests')
if not os.path.isdir(tests):
os.symlink(self.topsrcdir, tests)
self._setup_objdir(**kwargs)
import runreftestb2g
for i, path in enumerate(kwargs["tests"]):
# Non-absolute paths are relative to the packaged directory, which
@@ -122,45 +123,6 @@ class ReftestRunner(MozbuildObject):
path = os.path.relpath(path, os.path.join(self.topsrcdir))
kwargs["tests"][i] = os.path.join('tests', path)
if conditions.is_b2g_desktop(self):
return self.run_b2g_desktop(**kwargs)
return self.run_b2g_remote(b2g_home, xre_path, **kwargs)
def run_b2g_desktop(self, **kwargs):
if self.substs.get('ENABLE_MARIONETTE') != '1':
print(MARIONETTE_DISABLED % ('mochitest-b2g-desktop',
self.mozconfig['path']))
return 1
if not kwargs["profile"]:
gaia_profile = os.environ.get('GAIA_PROFILE')
if not gaia_profile:
print(GAIA_PROFILE_NOT_FOUND % 'reftest-b2g-desktop')
return 1
kwargs["profile"] = gaia_profile
if os.path.isfile(os.path.join(kwargs["profile"], 'extensions',
'httpd@gaiamobile.org')):
print(GAIA_PROFILE_IS_DEBUG % ('mochitest-b2g-desktop',
kwargs["profile"]))
return 1
kwargs["desktop"] = True
kwargs["app"] = self.get_binary_path()
if kwargs["oop"]:
options.browser_arg = '-oop'
if not kwargs["app"].endswith('-bin'):
kwargs["app"] = '%s-bin' % options.app
if not os.path.isfile(kwargs["app"]):
options.app = kwargs["app"][:-len('-bin')]
return runreftestb2g.run(**kwargs)
def run_b2g_remote(self, b2g_home, xre_path, **kwargs):
import runreftestb2g
try:
which.which('adb')
except which.WhichError:
@@ -177,10 +139,44 @@ class ReftestRunner(MozbuildObject):
if kwargs["suite"] == 'reftest':
kwargs["oop"] = True
return runreftestb2g.run_remote(**kwargs)
return runreftestb2g.run(**kwargs)
def run_mulet_test(self, **kwargs):
"""Runs a mulet reftest."""
self._setup_objdir(**kwargs)
import runreftestmulet
if self.substs.get('ENABLE_MARIONETTE') != '1':
print(MARIONETTE_DISABLED % self.mozconfig['path'])
return 1
if not kwargs["profile"]:
gaia_profile = os.environ.get('GAIA_PROFILE')
if not gaia_profile:
print(GAIA_PROFILE_NOT_FOUND)
return 1
kwargs["profile"] = gaia_profile
if os.path.isfile(os.path.join(kwargs["profile"], 'extensions',
'httpd@gaiamobile.org')):
print(GAIA_PROFILE_IS_DEBUG % kwargs["profile"])
return 1
kwargs['app'] = self.get_binary_path()
kwargs['mulet'] = True
kwargs['oop'] = True
if kwargs['oop']:
kwargs['browser_arg'] = '-oop'
if not kwargs['app'].endswith('-bin'):
kwargs['app'] = '%s-bin' % kwargs['app']
if not os.path.isfile(kwargs['app']):
kwargs['app'] = kwargs['app'][:-len('-bin')]
return runreftestmulet.run(**kwargs)
def run_desktop_test(self, **kwargs):
"""Runs a reftest."""
"""Runs a reftest, in desktop Firefox."""
import runreftest
if kwargs["suite"] not in ('reftest', 'crashtest', 'jstestbrowser'):
@@ -193,7 +189,7 @@ class ReftestRunner(MozbuildObject):
"jstests.list")
}
kwargs["extraProfileFiles"] = [os.path.join(self.topobjdir, "dist", "plugins")]
kwargs["extraProfileFiles"].append(os.path.join(self.topobjdir, "dist", "plugins"))
kwargs["symbolsPath"] = os.path.join(self.topobjdir, "crashreporter-symbols")
if not kwargs["tests"]:
@@ -207,20 +203,121 @@ class ReftestRunner(MozbuildObject):
if not kwargs["runTestsInParallel"]:
kwargs["logFile"] = "%s.log" % kwargs["suite"]
#Remove the stdout handler from the internal logger and let mach deal with it
runreftest.log.removeHandler(runreftest.log.handlers[0])
self.log_manager.enable_unstructured()
rv = runreftest.run(**kwargs)
self.log_manager.disable_unstructured()
try:
rv = runreftest.run(**kwargs)
finally:
self.log_manager.disable_unstructured()
return rv
def run_android_test(self, **kwargs):
"""Runs a reftest, in Firefox for Android."""
import runreftest
if kwargs["suite"] not in ('reftest', 'crashtest', 'jstestbrowser'):
raise Exception('None or unrecognized reftest suite type.')
if "ipc" in kwargs.keys():
raise Exception('IPC tests not supported on Android.')
default_manifest = {
"reftest": (self.topsrcdir, "layout", "reftests", "reftest.list"),
"crashtest": (self.topsrcdir, "testing", "crashtest", "crashtests.list"),
"jstestbrowser": ("jsreftest", "tests", "jstests.list")
}
if not kwargs["tests"]:
kwargs["tests"] = [os.path.join(*default_manifest[kwargs["suite"]])]
kwargs["extraProfileFiles"].append(
os.path.join(self.topobjdir, "dist", "bin", "res", "fonts"))
if not kwargs["httpdPath"]:
kwargs["httpdPath"] = os.path.join(self.tests_dir, "modules")
if not kwargs["symbolsPath"]:
kwargs["symbolsPath"] = os.path.join(self.topobjdir, "crashreporter-symbols")
if not kwargs["xrePath"]:
kwargs["xrePath"] = os.environ.get("MOZ_HOST_BIN")
if not kwargs["app"]:
kwargs["app"] = self.substs["ANDROID_PACKAGE_NAME"]
kwargs["dm_trans"] = "adb"
kwargs["ignoreWindowSize"] = True
kwargs["printDeviceInfo"] = False
# A symlink and some path manipulations are required so that test
# manifests can be found both locally and remotely (via a url)
# using the same relative path.
if kwargs["suite"] == "jstestbrowser":
staged_js_dir = os.path.join(self.topobjdir, "dist", "test-stage", "jsreftest")
tests = os.path.join(self.reftest_dir, 'jsreftest')
if not os.path.isdir(tests):
os.symlink(staged_js_dir, tests)
kwargs["extraProfileFiles"].append(os.path.join(staged_js_dir, "tests", "user.js"))
else:
tests = os.path.join(self.reftest_dir, "tests")
if not os.path.isdir(tests):
os.symlink(self.topsrcdir, tests)
for i, path in enumerate(kwargs["tests"]):
# Non-absolute paths are relative to the packaged directory, which
# has an extra tests/ at the start
if os.path.exists(os.path.abspath(path)):
path = os.path.relpath(path, os.path.join(self.topsrcdir))
kwargs["tests"][i] = os.path.join('tests', path)
# Need to chdir to reftest_dir otherwise imports fail below.
os.chdir(self.reftest_dir)
# The imp module can spew warnings if the modules below have
# already been imported, ignore them.
with warnings.catch_warnings():
warnings.simplefilter('ignore')
import imp
path = os.path.join(self.reftest_dir, 'remotereftest.py')
with open(path, 'r') as fh:
imp.load_module('reftest', fh, path, ('.py', 'r', imp.PY_SOURCE))
import reftest
# Remove the stdout handler from the internal logger and let mach deal with it
runreftest.log.removeHandler(runreftest.log.handlers[0])
self.log_manager.enable_unstructured()
try:
rv = reftest.run(**kwargs)
finally:
self.log_manager.disable_unstructured()
return rv
def process_test_objects(kwargs):
"""|mach test| works by providing a test_objects argument, from
which the test path must be extracted and converted into a normal
reftest tests argument."""
if "test_objects" in kwargs:
if kwargs["tests"] is None:
kwargs["tests"] = []
kwargs["tests"].extend(item["path"] for item in kwargs["test_objects"])
del kwargs["test_objects"]
def get_parser():
here = os.path.abspath(os.path.dirname(__file__))
build_obj = MozbuildObject.from_environment(cwd=here)
if conditions.is_android(build_obj):
return reftestcommandline.RemoteArgumentsParser()
elif conditions.is_mulet(build_obj):
return reftestcommandline.B2GArgumentParser()
else:
return reftestcommandline.DesktopArgumentsParser()
@CommandProvider
class MachCommands(MachCommandBase):
@Command('reftest',
category='testing',
description='Run reftests (layout and graphics correctness).',
parser=reftestcommandline.DesktopArgumentsParser)
parser=get_parser)
def run_reftest(self, **kwargs):
kwargs["suite"] = "reftest"
return self._run_reftest(**kwargs)
@@ -228,7 +325,7 @@ class MachCommands(MachCommandBase):
@Command('jstestbrowser',
category='testing',
description='Run js/src/tests in the browser.',
parser=reftestcommandline.DesktopArgumentsParser)
parser=get_parser)
def run_jstestbrowser(self, **kwargs):
self._mach_context.commands.dispatch("build",
self._mach_context,
@@ -239,7 +336,7 @@ class MachCommands(MachCommandBase):
@Command('reftest-ipc',
category='testing',
description='Run IPC reftests (layout and graphics correctness, separate process).',
parser=reftestcommandline.DesktopArgumentsParser)
parser=get_parser)
def run_ipc(self, **kwargs):
kwargs["ipc"] = True
kwargs["suite"] = "reftest"
@@ -248,7 +345,7 @@ class MachCommands(MachCommandBase):
@Command('crashtest',
category='testing',
description='Run crashtests (Check if crashes on a page).',
parser=reftestcommandline.DesktopArgumentsParser)
parser=get_parser)
def run_crashtest(self, **kwargs):
kwargs["suite"] = "crashtest"
return self._run_reftest(**kwargs)
@@ -256,14 +353,21 @@ class MachCommands(MachCommandBase):
@Command('crashtest-ipc',
category='testing',
description='Run IPC crashtests (Check if crashes on a page, separate process).',
parser=reftestcommandline.DesktopArgumentsParser)
parser=get_parser)
def run_crashtest_ipc(self, **kwargs):
kwargs["ipc"] = True
kwargs["suite"] = "crashtest"
return self._run_reftest(**kwargs)
def _run_reftest(self, **kwargs):
process_test_objects(kwargs)
reftest = self._spawn(ReftestRunner)
if conditions.is_android(self):
from mozrunner.devices.android_device import verify_android_device
verify_android_device(self, install=True, xre=True)
return reftest.run_android_test(**kwargs)
elif conditions.is_mulet(self):
return reftest.run_mulet_test(**kwargs)
return reftest.run_desktop_test(**kwargs)
@@ -290,14 +394,6 @@ class B2GCommands(MachCommandBase):
kwargs["suite"] = "reftest"
return self._run_reftest(**kwargs)
@Command('reftest-b2g-desktop', category='testing',
description='Run a b2g desktop reftest (b2g desktop layout and graphics correctness).',
conditions=[conditions.is_b2g_desktop],
parser=reftestcommandline.B2GArgumentParser)
def run_reftest_b2g_desktop(self, **kwargs):
kwargs["suite"] = "reftest"
return self._run_reftest(**kwargs)
@Command('crashtest-remote', category='testing',
description='Run a remote crashtest (Check if b2g crashes on a page, remote device).',
conditions=[conditions.is_b2g, is_emulator],
@@ -307,6 +403,7 @@ class B2GCommands(MachCommandBase):
return self._run_reftest(**kwargs)
def _run_reftest(self, **kwargs):
process_test_objects(kwargs)
if self.device_name:
if self.device_name.startswith('emulator'):
emulator = 'arm'
+144
View File
@@ -0,0 +1,144 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import json
import os
import threading
from mozlog.formatters import TbplFormatter
from mozrunner.utils import get_stack_fixer_function
class ReftestFormatter(TbplFormatter):
"""
Formatter designed to preserve the legacy "tbpl" format in reftest.
This is needed for both the reftest-analyzer and mozharness log parsing.
We can change this format when both reftest-analyzer and mozharness have
been changed to read structured logs.
"""
def __call__(self, data):
if 'component' in data and data['component'] == 'mozleak':
# Output from mozleak requires that no prefix be added
# so that mozharness will pick up these failures.
return "%s\n" % data['message']
formatted = TbplFormatter.__call__(self, data)
if data['action'] == 'process_output':
return formatted
return 'REFTEST %s' % formatted
def log(self, data):
prefix = "%s |" % data['level'].upper()
return "%s %s\n" % (prefix, data['message'])
def test_end(self, data):
extra = data.get('extra', {})
status = data['status']
status_msg = "TEST-"
if 'expected' in data:
status_msg += "UNEXPECTED-%s" % status
else:
if status != "PASS":
status_msg += "KNOWN-"
status_msg += status
if extra.get('status_msg') == 'Random':
status_msg += "(EXPECTED RANDOM)"
test = self.id_str(data['test'])
if 'message' in data:
status_details = data['message']
elif isinstance(data['test'], tuple):
status_details = 'image comparison ({})'.format(data['test'][1])
else:
status_details = '(LOAD ONLY)'
output_text = "%s | %s | %s" % (status_msg, test, status_details)
if 'differences' in extra:
diff_msg = (", max difference: %(max_difference)s"
", number of differing pixels: %(differences)s") % extra
diagnostic_data = ("REFTEST IMAGE 1 (TEST): %(image1)s\n"
"REFTEST IMAGE 2 (REFERENCE): %(image2)s") % extra
output_text += '%s\n%s' % (diff_msg, diagnostic_data)
elif "image1" in extra:
diagnostic_data = "REFTEST IMAGE: %(image1)s" % extra
output_text += '\n%s' % diagnostic_data
output_text += "\nREFTEST TEST-END | %s" % test
return "%s\n" % output_text
def process_output(self, data):
return "%s\n" % data["data"]
def suite_end(self, data):
lines = []
summary = data['extra']['results']
summary['success'] = summary['Pass'] + summary['LoadOnly']
lines.append("Successful: %(success)s (%(Pass)s pass, %(LoadOnly)s load only)" %
summary)
summary['unexpected'] = (summary['Exception'] + summary['FailedLoad'] +
summary['UnexpectedFail'] + summary['UnexpectedPass'] +
summary['AssertionUnexpected'] +
summary['AssertionUnexpectedFixed'])
lines.append(("Unexpected: %(unexpected)s (%(UnexpectedFail)s unexpected fail, "
"%(UnexpectedPass)s unexpected pass, "
"%(AssertionUnexpected)s unexpected asserts, "
"%(FailedLoad)s failed load, "
"%(Exception)s exception)") % summary)
summary['known'] = (summary['KnownFail'] + summary['AssertionKnown'] +
summary['Random'] + summary['Skip'] + summary['Slow'])
lines.append(("Known problems: %(known)s (" +
"%(KnownFail)s known fail, " +
"%(AssertionKnown)s known asserts, " +
"%(Random)s random, " +
"%(Skip)s skipped, " +
"%(Slow)s slow)") % summary)
lines = ["REFTEST INFO | %s" % s for s in lines]
lines.append("REFTEST SUITE-END | Shutdown")
return "INFO | Result summary:\n{}\n".format('\n'.join(lines))
def id_str(self, test_id):
if isinstance(test_id, basestring):
return test_id
return test_id[0]
class OutputHandler(object):
"""Process the output of a process during a test run and translate
raw data logged from reftest.js to an appropriate structured log action,
where applicable.
"""
def __init__(self, log, utilityPath, symbolsPath=None):
self.stack_fixer_function = get_stack_fixer_function(utilityPath, symbolsPath)
self.log = log
# needed for b2gautomation.py
self.suite_finished = False
def __call__(self, line):
# need to return processed messages to appease remoteautomation.py
if not line.strip():
return []
try:
data = json.loads(line)
except ValueError:
self.verbatim(line)
return [line]
if isinstance(data, dict) and 'action' in data:
if data['action'] == 'suite_end':
self.suite_finished = True
self.log.log_raw(data)
else:
self.verbatim(json.dumps(data))
return [data]
def verbatim(self, line):
if self.stack_fixer_function:
line = self.stack_fixer_function(line)
self.log.process_output(threading.current_thread().name, line)
+167 -180
View File
@@ -9,7 +9,7 @@ this.EXPORTED_SYMBOLS = ["OnRefTestLoad"];
#endif
const CC = Components.classes;
var CC = Components.classes;
const CI = Components.interfaces;
const CR = Components.results;
const CU = Components.utils;
@@ -38,13 +38,17 @@ const NS_OBSERVER_SERVICE_CONTRACTID =
CU.import("resource://gre/modules/FileUtils.jsm");
CU.import("chrome://reftest/content/httpd.jsm", this);
CU.import("chrome://reftest/content/StructuredLog.jsm", this);
CU.import("resource://gre/modules/Services.jsm");
CU.import("resource://gre/modules/NetUtil.jsm");
var gLoadTimeout = 0;
var gTimeoutHook = null;
var gRemote = false;
var gIgnoreWindowSize = false;
var gShuffle = false;
var gRepeat = null;
var gRunUntilFailure = false;
var gTotalChunks = 0;
var gThisChunk = 0;
var gContainingWindow = null;
@@ -96,6 +100,7 @@ var gTotalTests = 0;
var gState;
var gCurrentURL;
var gTestLog = [];
var gLogLevel;
var gServer;
var gCount = 0;
var gAssertionCount = 0;
@@ -160,33 +165,41 @@ var gNoCanvasCache = false;
var gRecycledCanvases = new Array();
// By default we just log to stdout
var gDumpLog = dump;
var gVerbose = false;
// Only dump the sandbox once, because it doesn't depend on the
// manifest URL (yet!).
var gDumpedConditionSandbox = false;
function LogWarning(str)
function HasUnexpectedResult()
{
gDumpLog("REFTEST INFO | " + str + "\n");
return gTestResults.Exception > 0 ||
gTestResults.FailedLoad > 0 ||
gTestResults.UnexpectedFail > 0 ||
gTestResults.UnexpectedPass > 0 ||
gTestResults.AssertionUnexpected > 0 ||
gTestResults.AssertionUnexpectedFixed > 0;
}
// By default we just log to stdout
var gDumpFn = dump;
var gDumpRawLog = function(record) {
// Dump JSON representation of data on a single line
var line = JSON.stringify(record);
gDumpFn("\n" + line + "\n");
}
var logger = new StructuredLogger('reftest', gDumpRawLog);
function TestBuffer(str)
{
logger.debug(str);
gTestLog.push(str);
}
function LogInfo(str)
function FlushTestBuffer()
{
if (gVerbose)
gDumpLog("REFTEST INFO | " + str + "\n");
gTestLog.push(str);
}
function FlushTestLog()
{
if (!gVerbose) {
// In verbose mode, we've dumped all these messages already.
// In debug mode, we've dumped all these messages already.
if (gLogLevel !== 'debug') {
for (var i = 0; i < gTestLog.length; ++i) {
gDumpLog("REFTEST INFO | Saved log: " + gTestLog[i] + "\n");
logger.info("Saved log: " + gTestLog[i]);
}
}
gTestLog = [];
@@ -231,7 +244,7 @@ function getTestPlugin(aName) {
return tags[i];
}
LogWarning("Failed to find the test-plugin.");
logger.warning("Failed to find the test-plugin.");
return null;
}
@@ -244,7 +257,6 @@ this.OnRefTestLoad = function OnRefTestLoad(win)
var env = CC["@mozilla.org/process/environment;1"].
getService(CI.nsIEnvironment);
gVerbose = !!env.get("MOZ_REFTEST_VERBOSE");
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
@@ -266,6 +278,12 @@ this.OnRefTestLoad = function OnRefTestLoad(win)
gBrowserIsIframe = false;
}
try {
gLogLevel = prefs.getCharPref("reftest.logLevel");
} catch (e) {
gLogLevel ='info';
}
if (win === undefined || win == null) {
win = window;
}
@@ -309,7 +327,7 @@ this.OnRefTestLoad = function OnRefTestLoad(win)
plugin1.enabledState = CI.nsIPluginTag.STATE_ENABLED;
plugin2.enabledState = CI.nsIPluginTag.STATE_ENABLED;
} else {
LogWarning("Could not get test plugin tags.");
logger.warning("Could not get test plugin tags.");
}
gBrowserMessageManager = gBrowser.QueryInterface(CI.nsIFrameLoaderOwner)
@@ -326,7 +344,7 @@ function InitAndStartRefTests()
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
} catch(e) {
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + e + "\n");
logger.error("EXCEPTION: " + e);
}
try {
@@ -348,7 +366,7 @@ function InitAndStartRefTests()
var f = FileUtils.File(logFile);
var mfl = FileUtils.openFileOutputStream(f, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE);
// Set to mirror to stdout as well as the file
gDumpLog = function (msg) {
gDumpFn = function (msg) {
#ifdef BOOTSTRAP
#ifdef REFTEST_B2G
dump(msg);
@@ -363,7 +381,7 @@ function InitAndStartRefTests()
}
catch(e) {
// If there is a problem, just use stdout
gDumpLog = dump;
gDumpFn = dump;
}
}
} catch(e) {}
@@ -414,7 +432,7 @@ function InitAndStartRefTests()
} catch (ex) {
//gBrowser.loadURI('data:text/plain,' + ex);
++gTestResults.Exception;
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + ex + "\n");
logger.error("EXCEPTION: " + ex);
DoneTests();
}
@@ -452,7 +470,7 @@ function StartTests()
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
} catch(e) {
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + e + "\n");
logger.error("EXCEPTION: " + e);
}
try {
@@ -467,6 +485,22 @@ function StartTests()
gShuffle = false;
}
try {
gRunUntilFailure = prefs.getBoolPref("reftest.runUntilFailure");
} catch (e) {
gRunUntilFailure = false;
}
// When we repeat this function is called again, so really only want to set
// gRepeat once.
if (gRepeat == null) {
try {
gRepeat = prefs.getIntPref("reftest.repeat");
} catch (e) {
gRepeat = 0;
}
}
try {
gRunSlowTests = prefs.getIntPref("reftest.skipslowtests");
} catch(e) {
@@ -478,12 +512,13 @@ function StartTests()
}
gURLs = [];
gManifestsLoaded = {};
try {
var manifests = JSON.parse(prefs.getCharPref("reftest.manifests"));
gURLFilterRegex = manifests[null];
} catch(e) {
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | Unable to find reftest.manifests pref. Please ensure your profile is setup properly\n");
logger.error("Unable to find reftest.manifests pref. Please ensure your profile is setup properly");
DoneTests();
}
@@ -495,7 +530,7 @@ function StartTests()
// process includes before reading the included manifest again
manifestURLs.sort(function(a,b) {return a.length - b.length})
manifestURLs.forEach(function(manifestURL) {
gDumpLog("Readings manifest" + manifestURL + "\n");
logger.info("Reading manifest " + manifestURL);
var filter = manifests[manifestURL] ? new RegExp(manifests[manifestURL]) : null;
ReadTopManifest(manifestURL, [globalFilter, filter, false]);
});
@@ -504,6 +539,7 @@ function StartTests()
// Filter tests which will be skipped to get a more even distribution when chunking
// tURLs is a temporary array containing all active tests
var tURLs = new Array();
var tIDs = new Array();
for (var i = 0; i < gURLs.length; ++i) {
if (gURLs[i].expected == EXPECTED_DEATH)
continue;
@@ -515,9 +551,10 @@ function StartTests()
continue;
tURLs.push(gURLs[i]);
tIDs.push(gURLs[i].identifier);
}
gDumpLog("REFTEST INFO | Discovered " + gURLs.length + " tests, after filtering SKIP tests, we have " + tURLs.length + "\n");
logger.suiteStart(tIDs, {"skipped": gURLs.length - tURLs.length});
if (gTotalChunks > 0 && gThisChunk > 0) {
// Calculate start and end indices of this chunk if tURLs array were
@@ -532,8 +569,8 @@ function StartTests()
end = gThisChunk == gTotalChunks ? gURLs.length : gURLs.indexOf(tURLs[end + 1]) - 1;
gURLs = gURLs.slice(start, end);
gDumpLog("REFTEST INFO | Running chunk " + gThisChunk + " out of " + gTotalChunks + " chunks. ");
gDumpLog("tests " + (start+1) + "-" + end + "/" + gURLs.length + "\n");
logger.info("Running chunk " + gThisChunk + " out of " + gTotalChunks + " chunks. " +
"tests " + (start+1) + "-" + end + "/" + gURLs.length);
}
if (gShuffle) {
@@ -550,7 +587,7 @@ function StartTests()
} catch (ex) {
//gBrowser.loadURI('data:text/plain,' + ex);
++gTestResults.Exception;
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + ex + "\n");
logger.error("EXCEPTION: " + ex);
DoneTests();
}
}
@@ -563,7 +600,7 @@ function OnRefTestUnload()
plugin1.enabledState = gTestPluginEnabledStates[0];
plugin2.enabledState = gTestPluginEnabledStates[1];
} else {
LogWarning("Failed to get test plugin tags.");
logger.warning("Failed to get test plugin tags.");
}
}
@@ -635,7 +672,6 @@ function BuildConditionSandbox(aURL) {
// Shortcuts for widget toolkits.
sandbox.B2G = xr.widgetToolkit == "gonk";
sandbox.B2GDT = appInfo.name.toLowerCase() == "b2g" && !sandbox.B2G;
sandbox.Android = xr.OS == "Android" && !sandbox.B2G;
sandbox.cocoaWidget = xr.widgetToolkit == "cocoa";
sandbox.gtkWidget = xr.widgetToolkit == "gtk2"
@@ -751,8 +787,8 @@ function BuildConditionSandbox(aURL) {
}
if (!gDumpedConditionSandbox) {
dump("REFTEST INFO | Dumping JSON representation of sandbox \n");
dump("REFTEST INFO | " + JSON.stringify(CU.waiveXrays(sandbox)) + " \n");
logger.info("Dumping JSON representation of sandbox");
logger.info(JSON.stringify(CU.waiveXrays(sandbox)));
gDumpedConditionSandbox = true;
}
@@ -813,6 +849,12 @@ function AddTestItem(aTest, aFilter)
if (gFocusFilterMode == FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS &&
aTest.needsFocus)
return;
if (aTest.url2 !== null)
aTest.identifier = [aTest.prettyPath, aTest.type, aTest.url2.spec];
else
aTest.identifier = aTest.prettyPath;
gURLs.push(aTest);
}
@@ -835,17 +877,11 @@ function ReadManifest(aURL, inherited_status, aFilter)
.getService(CI.nsIScriptSecurityManager);
var listURL = aURL;
var channel = gIOService.newChannelFromURI2(aURL,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
CI.nsILoadInfo.SEC_NORMAL,
CI.nsIContentPolicy.TYPE_OTHER);
var inputStream = channel.open();
var channel = NetUtil.newChannel({uri: aURL, loadUsingSystemPrincipal: true});
var inputStream = channel.open2();
if (channel instanceof Components.interfaces.nsIHttpChannel
&& channel.responseStatus != 200) {
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | HTTP ERROR : " +
channel.responseStatus + "\n");
logger.error("HTTP ERROR : " + channel.responseStatus);
}
var streamBuf = getStreamContent(inputStream);
inputStream.close();
@@ -1245,31 +1281,35 @@ function StartCurrentTest()
// make sure we don't run tests that are expected to kill the browser
while (gURLs.length > 0) {
var test = gURLs[0];
logger.testStart(test.identifier);
if (test.expected == EXPECTED_DEATH) {
++gTestResults.Skip;
gDumpLog("REFTEST TEST-KNOWN-FAIL | " + test.url1.spec + " | (SKIP)\n");
logger.testEnd(test.identifier, "SKIP");
gURLs.shift();
} else if (test.needsFocus && !Focus()) {
// FIXME: Marking this as a known fail is dangerous! What
// if it starts failing all the time?
++gTestResults.Skip;
gDumpLog("REFTEST TEST-KNOWN-FAIL | " + test.url1.spec + " | (SKIPPED; COULDN'T GET FOCUS)\n");
logger.testEnd(test.identifier, "SKIP", null, "(COULDN'T GET FOCUS)");
gURLs.shift();
} else if (test.slow && !gRunSlowTests) {
++gTestResults.Slow;
gDumpLog("REFTEST TEST-KNOWN-SLOW | " + test.url1.spec + " | (SLOW)\n");
logger.testEnd(test.identifier, "SKIP", null, "(SLOW)");
gURLs.shift();
} else {
break;
}
}
if (gURLs.length == 0) {
if ((gURLs.length == 0 && gRepeat == 0) ||
(gRunUntilFailure && HasUnexpectedResult())) {
RestoreChangedPreferences();
DoneTests();
}
else {
gDumpLog("REFTEST TEST-START | " + gURLs[0].prettyPath + "\n");
} else if (gURLs.length == 0 && gRepeat > 0) {
// Repeat
gRepeat--;
StartTests();
} else {
if (gURLs[0].chaosMode) {
gWindowUtils.enterChaosMode();
}
@@ -1335,32 +1375,30 @@ function StartCurrentURI(aState)
} else if (ps.type == PREF_INTEGER) {
prefs.setIntPref(ps.name, value);
}
gDumpLog("SET PREFERENCE pref(" + ps.name + "," + value + ")\n");
logger.info("SET PREFERENCE pref(" + ps.name + "," + value + ")");
}
});
} catch (e) {
if (e == "bad pref") {
var test = gURLs[0];
if (test.expected == EXPECTED_FAIL) {
gDumpLog("REFTEST TEST-KNOWN-FAIL | " + test.url1.spec +
" | (SKIPPED; " + badPref + " not known or wrong type)\n");
logger.testEnd(test.identifier, "FAIL", "FAIL",
"(SKIPPED; " + badPref + " not known or wrong type)");
++gTestResults.Skip;
} else {
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + test.url1.spec +
" | " + badPref + " not known or wrong type\n");
logger.testEnd(test.identifier, "FAIL", "PASS",
badPref + " not known or wrong type");
++gTestResults.UnexpectedFail;
}
// skip the test that had a bad preference
gURLs.shift();
StartCurrentTest();
return;
} else {
throw e;
}
}
if (badPref != undefined) {
// skip the test that had a bad preference
gURLs.shift();
StartCurrentTest();
return;
}
}
if (prefSettings.length == 0 &&
@@ -1373,9 +1411,11 @@ function StartCurrentURI(aState)
gContainingWindow.setTimeout(RecordResult, 0);
} else {
var currentTest = gTotalTests - gURLs.length;
gDumpLog("REFTEST TEST-LOAD | " + gCurrentURL + " | " + currentTest + " / " + gTotalTests +
" (" + Math.floor(100 * (currentTest / gTotalTests)) + "%)\n");
LogInfo("START " + gCurrentURL);
// Log this to preserve the same overall log format,
// should be removed if the format is updated
gDumpFn("REFTEST TEST-LOAD | " + gCurrentURL + " | " + currentTest + " / " + gTotalTests +
" (" + Math.floor(100 * (currentTest / gTotalTests)) + "%)\n");
TestBuffer("START " + gCurrentURL);
var type = gURLs[0].type
if (TYPE_SCRIPT == type) {
SendLoadScriptTest(gCurrentURL, gLoadTimeout);
@@ -1387,37 +1427,10 @@ function StartCurrentURI(aState)
function DoneTests()
{
gDumpLog("REFTEST FINISHED: Slowest test took " + gSlowestTestTime +
"ms (" + gSlowestTestURL + ")\n");
logger.suiteEnd(extra={'results': gTestResults});
logger.info("Slowest test took " + gSlowestTestTime + "ms (" + gSlowestTestURL + ")");
logger.info("Total canvas count = " + gRecycledCanvases.length);
gDumpLog("REFTEST INFO | Result summary:\n");
var count = gTestResults.Pass + gTestResults.LoadOnly;
gDumpLog("REFTEST INFO | Successful: " + count + " (" +
gTestResults.Pass + " pass, " +
gTestResults.LoadOnly + " load only)\n");
count = gTestResults.Exception + gTestResults.FailedLoad +
gTestResults.UnexpectedFail + gTestResults.UnexpectedPass +
gTestResults.AssertionUnexpected +
gTestResults.AssertionUnexpectedFixed;
gDumpLog("REFTEST INFO | Unexpected: " + count + " (" +
gTestResults.UnexpectedFail + " unexpected fail, " +
gTestResults.UnexpectedPass + " unexpected pass, " +
gTestResults.AssertionUnexpected + " unexpected asserts, " +
gTestResults.AssertionUnexpectedFixed + " unexpected fixed asserts, " +
gTestResults.FailedLoad + " failed load, " +
gTestResults.Exception + " exception)\n");
count = gTestResults.KnownFail + gTestResults.AssertionKnown +
gTestResults.Random + gTestResults.Skip + gTestResults.Slow;
gDumpLog("REFTEST INFO | Known problems: " + count + " (" +
gTestResults.KnownFail + " known fail, " +
gTestResults.AssertionKnown + " known asserts, " +
gTestResults.Random + " random, " +
gTestResults.Skip + " skipped, " +
gTestResults.Slow + " slow)\n");
gDumpLog("REFTEST INFO | Total canvas count = " + gRecycledCanvases.length + "\n");
gDumpLog("REFTEST TEST-START | Shutdown\n");
function onStopped() {
let appStartup = CC["@mozilla.org/toolkit/app-startup;1"].getService(CI.nsIAppStartup);
appStartup.quit(CI.nsIAppStartup.eForceQuit);
@@ -1465,7 +1478,7 @@ function DoDrawWindow(ctx, x, y, w, h)
// browser element
flags |= ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
} else if (gBrowserIsRemote) {
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | can't drawWindow remote content\n");
logger.error(gCurrentURL + " | can't drawWindow remote content");
++gTestResults.Exception;
}
@@ -1478,22 +1491,21 @@ function DoDrawWindow(ctx, x, y, w, h)
} else {
// Output a special warning because we need to be able to detect
// this whenever it happens.
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | WARNING: USE_WIDGET_LAYERS disabled\n");
logger.error("WARNING: USE_WIDGET_LAYERS disabled");
}
gDumpLog("REFTEST INFO | drawWindow flags = " + flagsStr +
"; window size = " + gContainingWindow.innerWidth + "," + gContainingWindow.innerHeight +
"; test browser size = " + testRect.width + "," + testRect.height +
"\n");
logger.info("drawWindow flags = " + flagsStr +
"; window size = " + gContainingWindow.innerWidth + "," + gContainingWindow.innerHeight +
"; test browser size = " + testRect.width + "," + testRect.height);
}
LogInfo("DoDrawWindow " + x + "," + y + "," + w + "," + h);
TestBuffer("DoDrawWindow " + x + "," + y + "," + w + "," + h);
ctx.drawWindow(gContainingWindow, x, y, w, h, "rgb(255,255,255)",
gDrawWindowFlags);
}
function InitCurrentCanvasWithSnapshot()
{
LogInfo("Initializing canvas snapshot");
TestBuffer("Initializing canvas snapshot");
if (gURLs[0].type == TYPE_LOAD || gURLs[0].type == TYPE_SCRIPT) {
// We don't want to snapshot this kind of test
@@ -1511,7 +1523,7 @@ function InitCurrentCanvasWithSnapshot()
function UpdateCurrentCanvasForInvalidation(rects)
{
LogInfo("Updating canvas for invalidation");
TestBuffer("Updating canvas for invalidation");
if (!gCurrentCanvas) {
return;
@@ -1541,7 +1553,7 @@ function UpdateCurrentCanvasForInvalidation(rects)
function UpdateWholeCurrentCanvasForInvalidation()
{
LogInfo("Updating entire canvas for invalidation");
TestBuffer("Updating entire canvas for invalidation");
if (!gCurrentCanvas) {
return;
@@ -1553,7 +1565,7 @@ function UpdateWholeCurrentCanvasForInvalidation()
function RecordResult(testRunTime, errorMsg, scriptResults)
{
LogInfo("RecordResult fired");
TestBuffer("RecordResult fired");
// Keep track of which test was slowest, and how long it took.
if (testRunTime > gSlowestTestTime) {
@@ -1563,26 +1575,26 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
// Not 'const ...' because of 'EXPECTED_*' value dependency.
var outputs = {};
const randomMsg = "(EXPECTED RANDOM)";
outputs[EXPECTED_PASS] = {
true: {s: "TEST-PASS" , n: "Pass"},
false: {s: "TEST-UNEXPECTED-FAIL" , n: "UnexpectedFail"}
true: {s: ["PASS", "PASS"], n: "Pass"},
false: {s: ["FAIL", "PASS"], n: "UnexpectedFail"}
};
outputs[EXPECTED_FAIL] = {
true: {s: "TEST-UNEXPECTED-PASS" , n: "UnexpectedPass"},
false: {s: "TEST-KNOWN-FAIL" , n: "KnownFail"}
true: {s: ["PASS", "FAIL"], n: "UnexpectedPass"},
false: {s: ["FAIL", "FAIL"], n: "KnownFail"}
};
outputs[EXPECTED_RANDOM] = {
true: {s: "TEST-PASS" + randomMsg , n: "Random"},
false: {s: "TEST-KNOWN-FAIL" + randomMsg, n: "Random"}
true: {s: ["PASS", "PASS"], n: "Random"},
false: {s: ["FAIL", "FAIL"], n: "Random"}
};
outputs[EXPECTED_FUZZY] = outputs[EXPECTED_PASS];
var output;
var extra;
if (gURLs[0].type == TYPE_LOAD) {
++gTestResults.LoadOnly;
gDumpLog("REFTEST TEST-PASS | " + gURLs[0].prettyPath + " | (LOAD ONLY)\n");
logger.testEnd(gURLs[0].identifier, "PASS", "PASS", "(LOAD ONLY)");
gCurrentCanvas = null;
FinishTestItem();
return;
@@ -1600,17 +1612,14 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
if (!gURLs[0].allowSilentFail)
errorMsg = "No test results reported. (SCRIPT)\n";
else
gDumpLog("REFTEST INFO | An expected silent failure occurred \n");
logger.info("An expected silent failure occurred");
}
if (errorMsg) {
output = outputs[expected][false];
extra = { status_msg: output.n };
++gTestResults[output.n];
var result = "REFTEST " + output.s + " | " +
gURLs[0].prettyPath + " | " + // the URL being tested
errorMsg;
gDumpLog(result);
logger.testEnd(gURLs[0].identifier, output.s[0], output.s[1], errorMsg, null, extra);
FinishTestItem();
return;
}
@@ -1630,16 +1639,15 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
var index = 0;
scriptResults.forEach(function(result) {
var output = outputPair[result.passed];
var extra = { status_msg: output.n };
++gTestResults[output.n];
result = "REFTEST " + output.s + " | " +
gURLs[0].prettyPath + " | " + // the URL being tested
result.description + " item " + (++index) + "\n";
gDumpLog(result);
logger.testEnd(gURLs[0].identifier, output.s[0], output.s[1],
result.description + " item " + (++index), null, extra);
});
if (anyFailed && expected == EXPECTED_PASS) {
FlushTestLog();
FlushTestBuffer();
}
FinishTestItem();
@@ -1651,7 +1659,7 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
gCurrentCanvas = gURICanvases[gCurrentURL];
}
if (gCurrentCanvas == null) {
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | program error managing snapshots\n");
logger.error(gCurrentURL, "program error managing snapshots");
++gTestResults.Exception;
}
if (gState == 1) {
@@ -1694,7 +1702,7 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
throw "Inconsistent result from compareCanvases.";
}
equal = expected == EXPECTED_FUZZY;
gDumpLog("REFTEST fuzzy match\n");
logger.info("REFTEST fuzzy match");
}
var failedExtraCheck = gFailedNoPaint || gFailedOpaqueLayer || gFailedAssignedLayer;
@@ -1703,6 +1711,7 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
var test_passed = (equal == (gURLs[0].type == TYPE_REFTEST_EQUAL)) && !failedExtraCheck;
output = outputs[expected][test_passed];
extra = { status_msg: output.n };
++gTestResults[output.n];
@@ -1721,39 +1730,21 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
failures.push("failed reftest-assigned-layer: " + gFailedAssignedLayerMessages.join(", "));
}
var failureString = failures.join(", ");
if (expected == EXPECTED_FAIL) {
gDumpLog("REFTEST TEST-KNOWN-FAIL | " + gURLs[0].prettyPath + " | " + failureString + "\n");
} else {
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gURLs[0].prettyPath + " | " + failureString + "\n");
}
logger.testEnd(gURLs[0].identifier, output.s[0], output.s[1], failureString, null, extra);
} else {
var result = "REFTEST " + output.s + " | " +
gURLs[0].prettyPath + " | "; // the URL being tested
switch (gURLs[0].type) {
case TYPE_REFTEST_NOTEQUAL:
result += "image comparison (!=)";
break;
case TYPE_REFTEST_EQUAL:
result += "image comparison (==)";
break;
}
if (!test_passed && expected == EXPECTED_PASS ||
!test_passed && expected == EXPECTED_FUZZY ||
test_passed && expected == EXPECTED_FAIL) {
if (!equal) {
result += ", max difference: " + maxDifference.value + ", number of differing pixels: " + differences + "\n";
result += "REFTEST IMAGE 1 (TEST): " + gCanvas1.toDataURL() + "\n";
result += "REFTEST IMAGE 2 (REFERENCE): " + gCanvas2.toDataURL() + "\n";
extra.max_difference = maxDifference.value;
extra.differences = differences;
extra.image1 = gCanvas1.toDataURL();
extra.image2 = gCanvas2.toDataURL();
} else {
result += "\n";
result += "REFTEST IMAGE: " + gCanvas1.toDataURL() + "\n";
extra.image1 = gCanvas1.toDataURL();
}
} else {
result += "\n";
}
gDumpLog(result);
logger.testEnd(gURLs[0].identifier, output.s[0], output.s[1], null, null, extra);
if (gURLs[0].prefSettings1.length == 0) {
UpdateCanvasCache(gURLs[0].url1, gCanvas1);
@@ -1764,7 +1755,7 @@ function RecordResult(testRunTime, errorMsg, scriptResults)
}
if ((!test_passed && expected == EXPECTED_PASS) || (test_passed && expected == EXPECTED_FAIL)) {
FlushTestLog();
FlushTestBuffer();
}
CleanUpCrashDumpFiles();
@@ -1781,11 +1772,10 @@ function LoadFailed(why)
// Once bug 896840 is fixed, this can go away, but for now it will give log
// output that is TBPL starable for bug 789751 and bug 720452.
if (!why) {
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | load failed with unknown reason\n");
logger.error("load failed with unknown reason");
}
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " +
gURLs[0]["url" + gState].spec + " | load failed: " + why + "\n");
FlushTestLog();
logger.testEnd(gURLs[0]["url" + gState].spec, "FAIL", "PASS", "load failed: " + why);
FlushTestBuffer();
FinishTestItem();
}
@@ -1822,11 +1812,9 @@ function FindUnexpectedCrashDumpFiles()
if (!foundCrashDumpFile) {
++gTestResults.UnexpectedFail;
foundCrashDumpFile = true;
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL +
" | This test left crash dumps behind, but we weren't expecting it to!\n");
logger.testEnd(gCurrentURL, "FAIL", "PASS", "This test left crash dumps behind, but we weren't expecting it to!");
}
gDumpLog("REFTEST INFO | Found unexpected crash dump file " + path +
".\n");
logger.info("Found unexpected crash dump file " + path);
gUnexpectedCrashDumpFiles[path] = true;
}
}
@@ -1843,7 +1831,7 @@ function FinishTestItem()
{
// Replace document with BLANK_URL_FOR_CLEARING in case there are
// assertions when unloading.
gDumpLog("REFTEST INFO | Loading a blank page\n");
logger.debug("Loading a blank page");
// After clearing, content will notify us of the assertion count
// and tests will continue.
SendClear();
@@ -1878,26 +1866,25 @@ function DoAssertionCheck(numAsserts)
if (numAsserts < minAsserts) {
++gTestResults.AssertionUnexpectedFixed;
gDumpLog("REFTEST TEST-UNEXPECTED-PASS | " + gURLs[0].prettyPath +
" | assertion count " + numAsserts + " is less than " +
expectedAssertions + "\n");
gDumpFn("REFTEST TEST-UNEXPECTED-PASS | " + gURLs[0].prettyPath +
" | assertion count" + numAsserts + " is less than " +
expectedAssertions + "\n");
} else if (numAsserts > maxAsserts) {
++gTestResults.AssertionUnexpected;
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gURLs[0].prettyPath +
" | assertion count " + numAsserts + " is more than " +
expectedAssertions + "\n");
gDumpFn("REFTEST TEST-UNEXPECTED-FAIL | " + gURLs[0].prettyPath +
" | assertion count " + numAsserts + " is more than " +
expectedAssertions + "\n");
} else if (numAsserts != 0) {
++gTestResults.AssertionKnown;
gDumpLog("REFTEST TEST-KNOWN-FAIL | " + gURLs[0].prettyPath +
" | assertion count " + numAsserts + " matches " +
expectedAssertions + "\n");
gDumpFn("REFTEST TEST-KNOWN-FAIL | " + gURLs[0].prettyPath +
"assertion count " + numAsserts + " matches " +
expectedAssertions + "\n");
}
}
if (gURLs[0].chaosMode) {
gWindowUtils.leaveChaosMode();
}
gDumpLog("REFTEST TEST-END | " + gURLs[0].prettyPath + "\n");
// And start the next test.
gURLs.shift();
@@ -1926,7 +1913,7 @@ function RestoreChangedPreferences()
} else if (ps.type == PREF_INTEGER) {
prefs.setIntPref(ps.name, value);
}
gDumpLog("RESTORE PREFERENCE pref(" + ps.name + "," + value + ")\n");
logger.info("RESTORE PREFERENCE pref(" + ps.name + "," + value + ")");
});
gPrefsToRestore = [];
}
@@ -2007,7 +1994,7 @@ function RecvContentReady()
function RecvException(what)
{
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | " + what + "\n");
logger.error(gCurrentURL + " | " + what);
++gTestResults.Exception;
}
@@ -2039,13 +2026,13 @@ function RecvInitCanvasWithSnapshot()
function RecvLog(type, msg)
{
msg = "[CONTENT] "+ msg;
msg = "[CONTENT] " + msg;
if (type == "info") {
LogInfo(msg);
TestBuffer(msg);
} else if (type == "warning") {
LogWarning(msg);
logger.warning(msg);
} else {
gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | unknown log type " + type + "\n");
logger.error("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + " | unknown log type " + type + "\n");
++gTestResults.Exception;
}
}
+27 -10
View File
@@ -1,8 +1,11 @@
import argparse
import os
import sys
from collections import OrderedDict
from urlparse import urlparse
import mozlog
here = os.path.abspath(os.path.dirname(__file__))
@@ -150,6 +153,20 @@ class ReftestArgumentsParser(argparse.ArgumentParser):
dest="shuffle",
help="run reftests in random order")
self.add_argument("--run-until-failure",
action="store_true",
default=False,
dest="runUntilFailure",
help="stop running on the first failure. Useful for RR recordings.")
self.add_argument("--repeat",
action="store",
type=int,
default=0,
dest="repeat",
help="number of times the select test(s) will be executed. Useful for "
"finding intermittent failures.")
self.add_argument("--focus-filter-mode",
action="store",
type=str,
@@ -193,6 +210,8 @@ class ReftestArgumentsParser(argparse.ArgumentParser):
nargs="*",
help="Path to test file, manifest file, or directory containing tests")
mozlog.commandline.add_logging_group(self)
def get_ip(self):
import moznetwork
if os.name != "nt":
@@ -221,8 +240,6 @@ class ReftestArgumentsParser(argparse.ArgumentParser):
self.error("Failed to determine test suite; supply --suite to set this explicitly")
def validate(self, options, reftest):
import sys
if not options.tests:
# Can't just set this in the argument parser because mach will set a default
self.error("Must supply at least one path to a manifest file, test directory, or test file to run.")
@@ -469,15 +486,9 @@ class B2GArgumentParser(ReftestArgumentsParser):
action="store",
type=str,
dest="profile",
help="for desktop testing, the path to the "
help="for mulet testing, the path to the "
"gaia profile to use")
self.add_argument("--desktop",
action="store_true",
dest="desktop",
default=False,
help="Run the tests on a B2G desktop build")
self.add_argument("--mulet",
action="store_true",
dest="mulet",
@@ -664,6 +675,12 @@ class RemoteArgumentsParser(ReftestArgumentsParser):
dest="httpdPath",
help="path to the httpd.js file")
self.add_argument("--no-device-info",
action="store_false",
dest="printDeviceInfo",
default=True,
help="do not display verbose diagnostics about the remote device")
def validate_remote(self, options, automation):
# Ensure our defaults are set properly for everything we can infer
if not options.remoteTestRoot:
@@ -735,6 +752,6 @@ class RemoteArgumentsParser(ReftestArgumentsParser):
'screen')['screen'][0].split()
width = int(parts[0].split(':')[1])
height = int(parts[1].split(':')[1])
if (width < 1050 or height < 1050):
if (width < 1366 or height < 1050):
self.error("ERROR: Invalid screen resolution %sx%s, please adjust to 1366x1050 or higher" % (
width, height))
+35 -13
View File
@@ -8,19 +8,21 @@ import time
import tempfile
import traceback
# We need to know our current directory so that we can serve our test files from it.
SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
from runreftest import RefTest, ReftestResolver
from automation import Automation
import devicemanager
import droid
import mozinfo
import moznetwork
from automation import Automation
from remoteautomation import RemoteAutomation, fennecLogcatFilters
from output import OutputHandler
from runreftest import RefTest, ReftestResolver
import reftestcommandline
# We need to know our current directory so that we can serve our test files from it.
SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
class RemoteReftestResolver(ReftestResolver):
def absManifestPath(self, path):
script_abs_path = os.path.join(SCRIPT_DIRECTORY, path)
@@ -146,6 +148,13 @@ class RemoteReftest(RefTest):
self.automation.deleteANRs()
self.automation.deleteTombstones()
self._populate_logger(options)
outputHandler = OutputHandler(self.log, options.utilityPath, options.symbolsPath)
# RemoteAutomation.py's 'messageLogger' is also used by mochitest. Mimic a mochitest
# MessageLogger object to re-use this code path.
outputHandler.write = outputHandler.__call__
self.automation._processArgs['messageLogger'] = outputHandler
def findPath(self, paths, filename = None):
for path in paths:
p = path
@@ -336,11 +345,7 @@ class RemoteReftest(RefTest):
except:
print "Warning: cleaning up pidfile '%s' was unsuccessful from the test harness" % self.pidFile
def main():
automation = RemoteAutomation(None)
parser = reftestcommandline.RemoteArgumentsParser()
options = parser.parse_args()
def runTests(options, parser):
if (options.dm_trans == 'sut' and options.deviceIP == None):
print "Error: If --dm_trans = sut, you must provide a device IP to connect to via the --deviceIP option"
return 1
@@ -359,6 +364,7 @@ def main():
print "Automation Error: exception while initializing devicemanager. Most likely the device is not in a testable state."
return 1
automation = RemoteAutomation(None)
automation.setDeviceManager(dm)
if (options.remoteProductName != None):
@@ -396,7 +402,8 @@ def main():
if (dm.processExist(procName)):
dm.killProcess(procName)
reftest.printDeviceInfo()
if options.printDeviceInfo:
reftest.printDeviceInfo()
#an example manifest name to use on the cli
# manifest = "http://" + options.remoteWebServer + "/reftests/layout/reftests/reftest-sanity/reftest.list"
@@ -411,10 +418,25 @@ def main():
reftest.stopWebServer(options)
reftest.printDeviceInfo(printLogcat=True)
if options.printDeviceInfo:
reftest.printDeviceInfo(printLogcat=True)
return retVal
def run(**kwargs):
# Mach gives us kwargs; this is a way to turn them back into an
# options object
parser = reftestcommandline.RemoteArgumentsParser()
parser.set_defaults(**kwargs)
options = parser.parse_args(kwargs["tests"])
retVal = runTests(options, parser)
return retVal
def main():
parser = reftestcommandline.RemoteArgumentsParser()
options = parser.parse_args()
retVal = runTests(options, parser)
return retVal
if __name__ == "__main__":
sys.exit(main())
+68 -119
View File
@@ -26,26 +26,23 @@ import mozcrash
import mozdebug
import mozinfo
import mozleak
import mozlog
import mozprocess
import mozprofile
import mozrunner
from mozrunner.utils import get_stack_fixer_function, test_environment
from mozscreenshot import printstatus, dump_screen
from output import OutputHandler, ReftestFormatter
import reftestcommandline
# set up logging handler a la automation.py.in for compatability
import logging
log = logging.getLogger()
here = os.path.abspath(os.path.dirname(__file__))
def resetGlobalLog():
while log.handlers:
log.removeHandler(log.handlers[0])
handler = logging.StreamHandler(sys.stdout)
log.setLevel(logging.INFO)
log.addHandler(handler)
resetGlobalLog()
try:
from mozbuild.base import MozbuildObject
build_obj = MozbuildObject.from_environment(cwd=here)
except ImportError:
build_obj = None
def categoriesToRegex(categoryList):
@@ -202,6 +199,21 @@ class RefTest(object):
self.lastTestSeen = 'reftest'
self.haveDumpedScreen = False
self.resolver = self.resolver_cls()
self.log = None
def _populate_logger(self, options):
if self.log:
return
mozlog.commandline.log_formatters["tbpl"] = (ReftestFormatter,
"Reftest specific formatter for the"
"benefit of legacy log parsers and"
"tools such as the reftest analyzer")
fmt_options = {}
if not options.log_tbpl_level and os.environ.get('MOZ_REFTEST_VERBOSE'):
options.log_tbpl_level = fmt_options['level'] = 'debug'
self.log = mozlog.commandline.setup_logging(
"reftest harness", options, {"tbpl": sys.stdout}, fmt_options)
def update_mozinfo(self):
"""walk up directories to find mozinfo.json update the info"""
@@ -249,7 +261,12 @@ class RefTest(object):
prefs['reftest.ignoreWindowSize'] = True
if options.shuffle:
prefs['reftest.shuffle'] = True
if options.repeat:
prefs['reftest.repeat'] = options.repeat
if options.runUntilFailure:
prefs['reftest.runUntilFailure'] = True
prefs['reftest.focusFilterMode'] = options.focusFilterMode
prefs['reftest.logLevel'] = options.log_tbpl_level or 'info'
prefs['reftest.manifests'] = json.dumps(manifests)
# Ensure that telemetry is disabled, so we don't connect to the telemetry
@@ -323,7 +340,7 @@ class RefTest(object):
return profile
def environment(self, **kwargs):
kwargs['log'] = log
kwargs['log'] = self.log
return test_environment(**kwargs)
def buildBrowserEnv(self, options, profileDir):
@@ -345,11 +362,11 @@ class RefTest(object):
def killNamedOrphans(self, pname):
""" Kill orphan processes matching the given command name """
log.info("Checking for orphan %s processes..." % pname)
self.log.info("Checking for orphan %s processes..." % pname)
def _psInfo(line):
if pname in line:
log.info(line)
self.log.info(line)
process = mozprocess.ProcessHandler(['ps', '-f'],
processOutputLine=_psInfo)
process.run()
@@ -360,13 +377,13 @@ class RefTest(object):
if len(parts) == 3 and parts[0].isdigit():
pid = int(parts[0])
if parts[2] == pname and parts[1] == '1':
log.info("killing %s orphan with pid %d" % (pname, pid))
self.log.info("killing %s orphan with pid %d" % (pname, pid))
try:
os.kill(
pid, getattr(signal, "SIGKILL", signal.SIGTERM))
except Exception as e:
log.info("Failed to kill process %d: %s" %
(pid, str(e)))
self.log.info("Failed to kill process %d: %s" %
(pid, str(e)))
process = mozprocess.ProcessHandler(['ps', '-o', 'pid,ppid,comm'],
processOutputLine=_psKill)
process.run()
@@ -377,6 +394,8 @@ class RefTest(object):
shutil.rmtree(profileDir, True)
def runTests(self, tests, options, cmdlineArgs=None):
self._populate_logger(options)
# Despite our efforts to clean up servers started by this script, in practice
# we still see infrequent cases where a process is orphaned and interferes
# with future tests, typically because the old server is keeping the port in use.
@@ -469,17 +488,16 @@ class RefTest(object):
"""handle process output timeout"""
# TODO: bug 913975 : _processOutput should call self.processOutputLine
# one more time one timeout (I think)
log.error("TEST-UNEXPECTED-FAIL | %s | application timed out after %d seconds with no output" %
(self.lastTestSeen, int(timeout)))
self.log.error("%s | application timed out after %d seconds with no output" % (self.lastTestSeen, int(timeout)))
self.killAndGetStack(
proc, utilityPath, debuggerInfo, dump_screen=not debuggerInfo)
def dumpScreen(self, utilityPath):
if self.haveDumpedScreen:
log.info("Not taking screenshot here: see the one that was previously logged")
self.log.info("Not taking screenshot here: see the one that was previously logged")
return
self.haveDumpedScreen = True
dump_screen(utilityPath, log)
dump_screen(utilityPath, self.log)
def killAndGetStack(self, process, utilityPath, debuggerInfo, dump_screen=False):
"""
@@ -507,75 +525,11 @@ class RefTest(object):
process.kill(sig=signal.SIGABRT)
except OSError:
# https://bugzilla.mozilla.org/show_bug.cgi?id=921509
log.info("Can't trigger Breakpad, process no longer exists")
self.log.info("Can't trigger Breakpad, process no longer exists")
return
log.info("Can't trigger Breakpad, just killing process")
self.log.info("Can't trigger Breakpad, just killing process")
process.kill()
# output processing
class OutputHandler(object):
"""line output handler for mozrunner"""
def __init__(self, harness, utilityPath, symbolsPath=None, dump_screen_on_timeout=True):
"""
harness -- harness instance
dump_screen_on_timeout -- whether to dump the screen on timeout
"""
self.harness = harness
self.utilityPath = utilityPath
self.symbolsPath = symbolsPath
self.dump_screen_on_timeout = dump_screen_on_timeout
self.stack_fixer_function = self.stack_fixer()
def processOutputLine(self, line):
"""per line handler of output for mozprocess"""
for handler in self.output_handlers():
line = handler(line)
__call__ = processOutputLine
def output_handlers(self):
"""returns ordered list of output handlers"""
return [self.fix_stack,
self.format,
self.record_last_test,
self.handle_timeout_and_dump_screen,
self.log,
]
def stack_fixer(self):
"""
return get_stack_fixer_function, if any, to use on the output lines
"""
return get_stack_fixer_function(self.utilityPath, self.symbolsPath)
# output line handlers:
# these take a line and return a line
def fix_stack(self, line):
if self.stack_fixer_function:
return self.stack_fixer_function(line)
return line
def format(self, line):
"""format the line"""
return line.rstrip().decode("UTF-8", "ignore")
def record_last_test(self, line):
"""record last test on harness"""
if "TEST-START" in line and "|" in line:
self.harness.lastTestSeen = line.split("|")[1].strip()
return line
def handle_timeout_and_dump_screen(self, line):
if self.dump_screen_on_timeout and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line:
self.harness.dumpScreen(self.utilityPath)
return line
def log(self, line):
log.info(line)
return line
def runApp(self, profile, binary, cmdargs, env,
timeout=None, debuggerInfo=None,
symbolsPath=None, options=None,
@@ -591,10 +545,14 @@ class RefTest(object):
interactive = debuggerInfo.interactive
debug_args = [debuggerInfo.path] + debuggerInfo.args
outputHandler = self.OutputHandler(harness=self,
utilityPath=options.utilityPath,
symbolsPath=symbolsPath,
dump_screen_on_timeout=not debuggerInfo)
def record_last_test(message):
"""Records the last test seen by this harness for the benefit of crash logging."""
if message['action'] == 'test_start':
self.lastTestSeen = message['test']
self.log.add_handler(record_last_test)
outputHandler = OutputHandler(self.log, options.utilityPath, symbolsPath=symbolsPath)
kp_kwargs = {
'kill_on_timeout': False,
@@ -624,21 +582,18 @@ class RefTest(object):
interactive=interactive,
outputTimeout=timeout)
proc = runner.process_handler
status = runner.wait()
runner.process_handler = None
if timeout is None:
didTimeout = False
else:
didTimeout = proc.didTimeout
if status:
log.info("TEST-UNEXPECTED-FAIL | %s | application terminated with exit code %s",
self.lastTestSeen, status)
self.log.error("TEST-UNEXPECTED-FAIL | %s | application terminated with exit code %s" % (self.lastTestSeen, status))
else:
self.lastTestSeen = 'Main app process exited normally'
crashed = mozcrash.check_for_crashes(os.path.join(profile.profile, "minidumps"),
symbolsPath, test_name=self.lastTestSeen)
crashed = mozcrash.log_crashes(self.log, os.path.join(profile.profile, 'minidumps'),
symbolsPath, test=self.lastTestSeen)
runner.cleanup()
if not status and crashed:
status = 1
@@ -652,7 +607,7 @@ class RefTest(object):
profileDir = None
try:
if cmdlineArgs == None:
if cmdlineArgs is None:
cmdlineArgs = []
profile = self.createReftestProfile(options, manifests)
profileDir = profile.profile # name makes more sense
@@ -660,7 +615,6 @@ class RefTest(object):
# browser environment
browserEnv = self.buildBrowserEnv(options, profileDir)
log.info("REFTEST INFO | runreftest.py | Running tests: start.\n")
status = self.runApp(profile,
binary=options.app,
cmdargs=cmdlineArgs,
@@ -673,11 +627,9 @@ class RefTest(object):
debuggerInfo=debuggerInfo)
mozleak.process_leak_log(self.leakLogFile,
leak_thresholds=options.leakThresholds,
log=log,
stack_fixer=get_stack_fixer_function(options.utilityPath,
options.symbolsPath),
)
log.info("\nREFTEST INFO | runreftest.py | Running tests: end.")
finally:
self.cleanup(profileDir)
return status
@@ -697,31 +649,28 @@ class RefTest(object):
dest = os.path.join(profileDir, os.path.basename(abspath))
shutil.copytree(abspath, dest)
else:
log.warning(
"WARNING | runreftest.py | Failed to copy %s to profile", abspath)
self.log.warning(
"runreftest.py | Failed to copy %s to profile" % abspath)
continue
def run(**kwargs):
parser = reftestcommandline.DesktopArgumentsParser()
# Mach gives us kwargs; this is a way to turn them back into an
# options object
parser = reftestcommandline.DesktopArgumentsParser()
reftest = RefTest()
parser.set_defaults(**kwargs)
options = parser.parse_args(kwargs["tests"])
if 'tests' in kwargs:
options = parser.parse_args(kwargs["tests"])
else:
options = parser.parse_args()
reftest = RefTest()
parser.validate(options, reftest)
return reftest.runTests(options.tests, options)
def main():
parser = reftestcommandline.DesktopArgumentsParser()
reftest = RefTest()
options = parser.parse_args()
parser.validate(options, reftest)
sys.exit(reftest.runTests(options.tests, options))
if __name__ == "__main__":
main()
sys.exit(run())
+16 -12
View File
@@ -15,7 +15,8 @@ if here not in sys.path:
from automation import Automation
from b2gautomation import B2GRemoteAutomation
from b2g_desktop import run_desktop_reftests
from runreftestmulet import run_test_harness as run_mulet_reftests
from output import OutputHandler
from remotereftest import RemoteReftestResolver, ReftestServer
from runreftest import RefTest
import reftestcommandline
@@ -311,6 +312,8 @@ class B2GRemoteReftest(RefTest):
timeout=None, debuggerInfo=None,
symbolsPath=None, options=None,
valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None):
outputHandler = OutputHandler(self.log, options.utilityPath, options.symbolsPath)
status = self.automation.runApp(None, env,
binary,
profile.profile,
@@ -319,7 +322,8 @@ class B2GRemoteReftest(RefTest):
xrePath=options.xrePath,
debuggerInfo=debuggerInfo,
symbolsPath=symbolsPath,
timeout=timeout)
timeout=timeout,
outputHandler=outputHandler)
return status
@@ -380,7 +384,6 @@ def run_remote_reftests(parser, options):
auto.setProduct("b2g")
auto.test_script = os.path.join(here, 'b2g_start_script.js')
auto.test_script_args = [options.remoteWebServer, options.httpPort]
auto.logFinish = "REFTEST TEST-START | Shutdown"
reftest = B2GRemoteReftest(auto, dm, options, here)
parser.validate(options, reftest)
@@ -418,23 +421,24 @@ def run_remote_reftests(parser, options):
reftest.stopWebServer(options)
return retVal
def run_remote(**kwargs):
# Tests need to be served from a subdirectory of the server. Symlink
# topsrcdir here to get around this.
def run(**kwargs):
# Mach gives us kwargs; this is a way to turn them back into an
# options object
parser = reftestcommandline.B2GArgumentParser()
parser.set_defaults(**kwargs)
options = parser.parse_args(kwargs["tests"])
return run_remote_reftests(parser, options)
def main():
parser = reftestcommandline.B2GArgumentParser()
options = parser.parse_args()
if options.desktop or options.mulet:
return run_desktop_reftests(parser, options)
def main(args=sys.argv[1:]):
parser = reftestcommandline.B2GArgumentParser()
options = parser.parse_args(args)
if options.mulet:
return run_mulet_reftests(parser, options)
return run_remote_reftests(parser, options)
if __name__ == "__main__":
sys.exit(main())
@@ -9,8 +9,6 @@ import sys
here = os.path.abspath(os.path.dirname(__file__))
from runreftest import RefTest
from marionette_driver import expected
from marionette_driver.by import By
from marionette_driver.marionette import Marionette
@@ -21,10 +19,13 @@ from mozrunner import FirefoxRunner
import mozinfo
import mozlog
log = mozlog.unstructured.getLogger('REFTEST')
from runreftest import RefTest
from output import OutputHandler
import reftestcommandline
class B2GDesktopReftest(RefTest):
build_type = "desktop"
class MuletReftest(RefTest):
build_type = "mulet"
marionette = None
def __init__(self, marionette_args):
@@ -57,7 +58,11 @@ class B2GDesktopReftest(RefTest):
self.profile = self.create_profile(options, manifests,
profile_to_clone=options.profile)
env = self.buildBrowserEnv(options, self.profile.profile)
kp_kwargs = { 'processOutputLine': [self._on_output],
self._populate_logger(options)
outputHandler = OutputHandler(self.log, options.utilityPath, symbolsPath=options.symbolsPath)
kp_kwargs = { 'processOutputLine': [outputHandler],
'onTimeout': [self._on_timeout],
'kill_on_timeout': False }
@@ -69,7 +74,7 @@ class B2GDesktopReftest(RefTest):
options.timeout = 300
self.timeout = options.timeout + 30.0
log.info("%s | Running tests: start.", os.path.basename(__file__))
self.log.info("%s | Running tests: start." % os.path.basename(__file__))
cmd, args = self.build_command_line(options.app,
ignore_window_size=options.ignoreWindowSize,
browser_arg=options.browser_arg)
@@ -84,9 +89,9 @@ class B2GDesktopReftest(RefTest):
status = 0
try:
self.runner.start(outputTimeout=self.timeout)
log.info("%s | Application pid: %d",
self.log.info("%s | Application pid: %d" % (
os.path.basename(__file__),
self.runner.process_handler.pid)
self.runner.process_handler.pid))
# kick starts the reftest harness
self.run_marionette_script()
@@ -96,13 +101,13 @@ class B2GDesktopReftest(RefTest):
self.runner.cleanup()
if status > 0:
log.testFail("%s | application terminated with exit code %s",
self.last_test, status)
self.log.testFail("%s | application terminated with exit code %s" % (
self.last_test, status))
elif status < 0:
log.info("%s | application killed with signal %s",
self.last_test, -status)
self.log.info("%s | application killed with signal %s" % (
self.last_test, -status))
log.info("%s | Running tests: end.", os.path.basename(__file__))
self.log.info("%s | Running tests: end." % os.path.basename(__file__))
return status
def create_profile(self, options, manifests, profile_to_clone=None):
@@ -157,22 +162,13 @@ class B2GDesktopReftest(RefTest):
args += ['-chrome', 'chrome://b2g/content/shell.html']
return cmd, args
def _on_output(self, line):
print(line)
# TODO use structured logging
if "TEST-START" in line and "|" in line:
self.last_test = line.split("|")[1].strip()
def _on_timeout(self):
msg = "%s | application timed out after %s seconds with no output"
log.testFail(msg % (self.last_test, self.timeout))
self.log.testFail(msg % (self.last_test, self.timeout))
# kill process to get a stack
self.runner.stop(sig=signal.SIGABRT)
class MuletReftest(B2GDesktopReftest):
build_type = "mulet"
def _unlockScreen(self):
self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
self.marionette.import_script(os.path.abspath(
@@ -181,22 +177,19 @@ class MuletReftest(B2GDesktopReftest):
self.marionette.execute_async_script('GaiaLockScreen.unlock()')
def _wait_for_homescreen(self, timeout):
log.info("Waiting for home screen to load")
self.log.info("Waiting for home screen to load")
Wait(self.marionette, timeout).until(expected.element_present(
By.CSS_SELECTOR, '#homescreen[loading-state=false]'))
def run_desktop_reftests(parser, options):
def run_test_harness(parser, options):
marionette_args = {}
if options.marionette:
host, port = options.marionette.split(':')
marionette_args['host'] = host
marionette_args['port'] = int(port)
if options.mulet:
reftest = MuletReftest(marionette_args)
else:
reftest = B2GDesktopReftest(marionette_args)
reftest = MuletReftest(marionette_args)
parser.validate(options, reftest)
# add a -bin suffix if b2g-bin exists, but just b2g was specified
@@ -207,7 +200,16 @@ def run_desktop_reftests(parser, options):
if options.xrePath is None:
options.xrePath = os.path.dirname(options.app)
if options.desktop and not options.profile:
raise Exception("must specify --profile when specifying --desktop")
if options.mulet and not options.profile:
raise Exception("must specify --profile when specifying --mulet")
sys.exit(reftest.run_tests(options.tests, options))
def run(**kwargs):
# Mach gives us kwargs; this is a way to turn them back into an
# options object
parser = reftestcommandline.B2GArgumentParser()
parser.set_defaults(**kwargs)
options = parser.parse_args(kwargs['tests'])
return run_test_harness(parser, options)
+95
View File
@@ -0,0 +1,95 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "SimpleBuffer.h"
#include <algorithm>
namespace mozilla {
namespace net {
SimpleBuffer::SimpleBuffer()
: mStatus(NS_OK)
, mAvailable(0)
{
mOwningThread = PR_GetCurrentThread();
}
nsresult SimpleBuffer::Write(char *src, size_t len)
{
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
if (NS_FAILED(mStatus)) {
return mStatus;
}
while (len > 0) {
SimpleBufferPage *p = mBufferList.getLast();
if (p && (p->mWriteOffset == SimpleBufferPage::kSimpleBufferPageSize)) {
// no room.. make a new page
p = nullptr;
}
if (!p) {
p = new (fallible) SimpleBufferPage();
if (!p) {
mStatus = NS_ERROR_OUT_OF_MEMORY;
return mStatus;
}
mBufferList.insertBack(p);
}
size_t roomOnPage = SimpleBufferPage::kSimpleBufferPageSize - p->mWriteOffset;
size_t toWrite = std::min(roomOnPage, len);
memcpy(p->mBuffer + p->mWriteOffset, src, toWrite);
src += toWrite;
len -= toWrite;
p->mWriteOffset += toWrite;
mAvailable += toWrite;
}
return NS_OK;
}
size_t SimpleBuffer::Read(char *dest, size_t maxLen)
{
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
if (NS_FAILED(mStatus)) {
return 0;
}
size_t rv = 0;
for (SimpleBufferPage *p = mBufferList.getFirst();
p && (rv < maxLen); p = mBufferList.getFirst()) {
size_t avail = p->mWriteOffset - p->mReadOffset;
size_t toRead = std::min(avail, (maxLen - rv));
memcpy(dest + rv, p->mBuffer + p->mReadOffset, toRead);
rv += toRead;
p->mReadOffset += toRead;
if (p->mReadOffset == p->mWriteOffset) {
p->remove();
delete p;
}
}
MOZ_ASSERT(mAvailable >= rv);
mAvailable -= rv;
return rv;
}
size_t SimpleBuffer::Available()
{
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
return NS_SUCCEEDED(mStatus) ? mAvailable : 0;
}
void SimpleBuffer::Clear()
{
MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
SimpleBufferPage *p;
while ((p = mBufferList.popFirst())) {
delete p;
}
mAvailable = 0;
}
} // namespace net
} // namespace mozilla

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