Bug 854209 - add asm.js mochitests (r=sstangl)
authorLuke Wagner <luke@mozilla.com>
Fri, 03 May 2013 14:45:07 -0700
changeset 141742 78802b1601ed91151334614986233769705e9bfc
parent 141741 fb1bdd43832de3bdfa7049dfd66c97862ab9f25c
child 141743 036cde16f9d56ba4b0a312f9d05885a9da899f92
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssstangl
bugs854209
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 854209 - add asm.js mochitests (r=sstangl)
js/src/builtin/TestingFunctions.cpp
js/src/ion/AsmJS.cpp
js/src/ion/AsmJS.h
js/src/ion/AsmJSLink.cpp
js/src/jit-test/lib/asm.js
js/xpconnect/tests/mochitest/Makefile.in
js/xpconnect/tests/mochitest/file_asmjs.js
js/xpconnect/tests/mochitest/test_asmjs.html
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -920,16 +920,32 @@ js::testingFunc_inParallelSection(JSCont
 #ifndef JS_ION
 JSBool
 js::IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().set(BooleanValue(false));
     return true;
 }
+
+JSBool
+js::IsAsmJSModule(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().set(BooleanValue(false));
+    return true;
+}
+
+JSBool
+js::IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().set(BooleanValue(false));
+    return true;
+}
 #endif
 
 static JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment')",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc."),
@@ -1066,16 +1082,26 @@ static JSFunctionSpecWithHelp TestingFun
 "  inferred name based on where the function was defined. This can be\n"
 "  different from the 'name' property on the function."),
 
     JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0,
 "isAsmJSCompilationAvailable",
 "  Returns whether asm.js compilation is currently available or whether it is disabled\n"
 "  (e.g., by the debugger)."),
 
+    JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
+"isAsmJSModule(fn)",
+"  Returns whether the given value is a function containing \"use asm\" that has been\n"
+"  validated according to the asm.js spec."),
+
+    JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0,
+"isAsmJSModule(fn)",
+"  Returns whether the given value is a nested function in an asm.js module that has been\n"
+"  both compile- and link-time validated."),
+
     JS_FN_HELP("inParallelSection", testingFunc_inParallelSection, 0, 0,
 "inParallelSection()",
 "  True if this code is executing within a parallel section."),
 
     JS_FS_HELP_END
 };
 
 bool
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -5609,13 +5609,44 @@ js::IsAsmJSCompilationAvailable(JSContex
 #else
     bool available = false;
 #endif
 
     args.rval().set(BooleanValue(available));
     return true;
 }
 
+static bool
+IsMaybeWrappedNativeFunction(const Value &v, Native native)
+{
+    if (!v.isObject())
+        return false;
+
+    JSObject *obj = CheckedUnwrap(&v.toObject());
+    if (!obj)
+        return false;
+
+    return obj->isFunction() && obj->toFunction()->maybeNative() == native;
+}
+
+JSBool
+js::IsAsmJSModule(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], LinkAsmJS);
+    args.rval().set(BooleanValue(rval));
+    return true;
+}
+
+JSBool
+js::IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], CallAsmJS);
+    args.rval().set(BooleanValue(rval));
+    return true;
+}
+
 AsmJSModule::~AsmJSModule()
 {
     for (size_t i = 0; i < numFunctionCounts(); i++)
         js_delete(functionCounts(i));
 }
--- a/js/src/ion/AsmJS.h
+++ b/js/src/ion/AsmJS.h
@@ -19,21 +19,16 @@
 namespace js {
 
 class ScriptSource;
 class SPSProfiler;
 class AsmJSModule;
 namespace frontend { struct TokenStream; struct ParseNode; }
 namespace ion { class MIRGenerator; class LIRGraph; }
 
-// Return whether asm.js optimization is inhibitted by the platform or
-// dynamically disabled. (Exposed as JSNative for shell testing.)
-extern JSBool
-IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp);
-
 // Called after parsing a function 'fn' which contains the "use asm" directive.
 // This function performs type-checking and code-generation. If type-checking
 // succeeds, the generated native function is assigned to |moduleFun|.
 // Otherwise, a warning will be emitted and |moduleFun| is left unchanged. The
 // function returns 'false' only if a real JS semantic error (probably OOM) is
 // pending.
 extern bool
 CompileAsmJS(JSContext *cx, frontend::TokenStream &ts, frontend::ParseNode *fn,
@@ -46,16 +41,21 @@ CompileAsmJS(JSContext *cx, frontend::To
 // given arguments. If validation succeeds, the module's return value (its
 // exports) are returned via |vp|.  Otherwise, there was a validation error and
 // execution fall back to the usual path (bytecode generation, interpretation,
 // etc). The function returns 'false' only if a real JS semantic error (OOM or
 // exception thrown when executing GetProperty on the arguments) is pending.
 extern JSBool
 LinkAsmJS(JSContext *cx, unsigned argc, JS::Value *vp);
 
+// The js::Native for the functions nested in an asm.js module. Calling this
+// native will trampoline into generated code.
+extern JSBool
+CallAsmJS(JSContext *cx, unsigned argc, JS::Value *vp);
+
 // Force any currently-executing asm.js code to call
 // js_HandleExecutionInterrupt.
 void
 TriggerOperationCallbackForAsmJSCode(JSRuntime *rt);
 
 // The JSRuntime maintains a stack of AsmJSModule activations. An "activation"
 // of module A is an initial call from outside A into a function inside A,
 // followed by a sequence of calls inside A, and terminated by a call that
@@ -152,11 +152,28 @@ IsAsmJSModuleNative(js::Native native);
 #else
 static inline bool
 IsAsmJSModuleNative(js::Native native)
 {
     return false;
 }
 #endif
 
+// Exposed for shell testing:
+
+// Return whether asm.js optimization is inhibitted by the platform or
+// dynamically disabled:
+extern JSBool
+IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp);
+
+// Return whether the given value is a function containing "use asm" that has
+// been validated according to the asm.js spec.
+extern JSBool
+IsAsmJSModule(JSContext *cx, unsigned argc, Value *vp);
+
+// Return whether the given value is a nested function in an asm.js module that
+// has been both compile- and link-time validated.
+extern JSBool
+IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp);
+
 } // namespace js
 
 #endif // jsion_asmjs_h__
--- a/js/src/ion/AsmJSLink.cpp
+++ b/js/src/ion/AsmJSLink.cpp
@@ -276,18 +276,18 @@ AsmJSActivation::~AsmJSActivation()
 
     JSRuntime::AutoLockForOperationCallback lock(cx_->runtime);
     cx_->runtime->mainThread.asmJSActivationStack_ = prev_;
 }
 
 static const unsigned ASM_MODULE_SLOT = 0;
 static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
 
-static JSBool
-CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
+extern JSBool
+js::CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs callArgs = CallArgsFromVp(argc, vp);
     RootedFunction callee(cx, callArgs.callee().toFunction());
 
     // An asm.js function stores, in its extended slots:
     //  - a pointer to the module from which it was returned
     //  - its index in the ordered list of exported functions
     RootedObject moduleObj(cx, &callee->getExtendedSlot(ASM_MODULE_SLOT).toObject());
--- a/js/src/jit-test/lib/asm.js
+++ b/js/src/jit-test/lib/asm.js
@@ -9,42 +9,19 @@ const USE_ASM = "'use asm';";
 const HEAP_IMPORTS = "var i8=new glob.Int8Array(b);var u8=new glob.Uint8Array(b);"+
                      "var i16=new glob.Int16Array(b);var u16=new glob.Uint16Array(b);"+
                      "var i32=new glob.Int32Array(b);var u32=new glob.Uint32Array(b);"+
                      "var f32=new glob.Float32Array(b);var f64=new glob.Float64Array(b);";
 const BUF_64KB = new ArrayBuffer(64 * 1024);
 
 function asmCompile()
 {
-    if (!isAsmJSCompilationAvailable())
-        return Function.apply(null, arguments);
-
-    // asm.js emits a warning on successful compilation
-
-    // Turn on warnings-as-errors
-    var oldOpts = options("werror");
-    assertEq(oldOpts.indexOf("werror"), -1);
-
-    // Verify that the code is succesfully compiled
-    var caught = false;
-    try {
-        Function.apply(null, arguments);
-    } catch (e) {
-        if ((''+e).indexOf(ASM_OK_STRING) == -1)
-            throw new Error("Didn't catch the expected success error; instead caught: " + e);
-        caught = true;
-    }
-    if (!caught)
-        throw new Error("Didn't catch the success error");
-
-    // Turn warnings-as-errors back off
-    options("werror");
-
-    // Compile for real
-    return Function.apply(null, arguments);
+    var f = Function.apply(null, arguments);
+    assertEq(!isAsmJSCompilationAvailable() || isAsmJSModule(f), true);
+    return f;
 }
 
 function assertAsmTypeFail()
 {
     if (!isAsmJSCompilationAvailable())
         return;
 
     // Verify no error is thrown with warnings off
--- a/js/xpconnect/tests/mochitest/Makefile.in
+++ b/js/xpconnect/tests/mochitest/Makefile.in
@@ -90,12 +90,14 @@ MOCHITEST_FILES =	chrome_wrappers_helper
 		test_bug802557.html \
 		file_bug802557.html \
 		test_bug803730.html \
 		test_bug809547.html \
 		test_bug809674.html \
 		test_bug865260.html \
 		file_crosscompartment_weakmap.html \
 		test_crosscompartment_weakmap.html \
+		test_asmjs.html \
+		file_asmjs.js \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_asmjs.js
@@ -0,0 +1,26 @@
+function f(stdlib, foreign, buffer) {
+    "use asm";
+    var i32 = new stdlib.Int32Array(buffer);
+    function main(n) {
+        n = n|0;
+        var i = 0, sum = 0;
+        for (; (i|0) < (n|0); i=(i+1)|0)
+            sum = (sum + (i32[(i<<2)>>2]|0))|0;
+        return sum|0;
+    }
+    return main;
+}
+
+var i32 = new Int32Array(4096/4);
+for (var i = 0; i < 100; i++)
+    i32[i] = i;
+
+var fMain = f(this, null, i32.buffer);
+if (fMain(4) !== 6)
+    throw "f.main(4)";
+if (fMain(100) !== 4950)
+    throw "f.main(100)";
+if (fMain(3000000) !== 4950)
+    throw "f.main(3000000)";
+
+postMessage("ok");
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_asmjs.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=854209
+-->
+<head>
+  <meta charset="utf-8">
+  <title>asm.js browser tests</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=854209">asm.js browser tests</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
+
+ok(jsFuns.isAsmJSCompilationAvailable());
+
+function f1() { "use asm"; function g() {} return g }
+ok(jsFuns.isAsmJSModule(f1), "f1 is an asm.js module");
+ok(jsFuns.isAsmJSFunction(f1()), "f1.g is an asm.js function");
+
+function f2(stdlib, foreign, buffer) {
+    "use asm";
+    var i32 = new stdlib.Int32Array(buffer);
+    function main(n) {
+        n = n|0;
+        var i = 0, sum = 0;
+        for (; (i|0) < (n|0); i=(i+1)|0)
+            sum = (sum + (i32[(i<<2)>>2]|0))|0;
+        return sum|0;
+    }
+    return main;
+}
+ok(jsFuns.isAsmJSModule(f2), "f2 is an asm.js module");
+var i32 = new Int32Array(4096/4);
+for (var i = 0; i < 100; i++)
+    i32[i] = i;
+var f2Main = f2(this, null, i32.buffer);
+ok(f2Main, "f2.main is an asm.js function");
+ok(f2Main(4) == 6, "f2.main(4)");
+ok(f2Main(100) == 4950, "f2.main(100)");
+ok(f2Main(3000000) == 4950, "f2.main(3000000)");
+
+var w = new Worker('http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_asmjs.js');
+w.onmessage = function(e) {
+    ok(e.data === "ok", "Worker asm.js tests");
+    SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>