mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
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:
+10
-4
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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::
|
||||
|
||||
@@ -799,6 +799,8 @@ protected:
|
||||
|
||||
void ResetResponse();
|
||||
|
||||
bool ShouldBlockAuthPrompt();
|
||||
|
||||
struct RequestHeader
|
||||
{
|
||||
nsCString header;
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "mozilla/LinkedList.h"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -101,6 +101,7 @@ UNIFIED_SOURCES += [
|
||||
'WebGLExtensionColorBufferFloat.cpp',
|
||||
'WebGLExtensionColorBufferHalfFloat.cpp',
|
||||
'WebGLExtensionCompressedTextureATC.cpp',
|
||||
'WebGLExtensionCompressedTextureES3.cpp',
|
||||
'WebGLExtensionCompressedTextureETC1.cpp',
|
||||
'WebGLExtensionCompressedTexturePVRTC.cpp',
|
||||
'WebGLExtensionCompressedTextureS3TC.cpp',
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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, ¬Int32);
|
||||
masm.moveValue(index, out);
|
||||
masm.moveValue(input, out);
|
||||
masm.jump(ool->rejoin());
|
||||
|
||||
masm.bind(¬Int32);
|
||||
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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
_(OsiPoint) \
|
||||
_(MoveGroup) \
|
||||
_(Integer) \
|
||||
_(Integer64) \
|
||||
_(Pointer) \
|
||||
_(Double) \
|
||||
_(Float32) \
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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|.
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
@@ -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
Reference in New Issue
Block a user