Diff
Modified: trunk/JSTests/ChangeLog (217059 => 217060)
--- trunk/JSTests/ChangeLog 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/JSTests/ChangeLog 2017-05-18 19:38:10 UTC (rev 217060)
@@ -1,3 +1,26 @@
+2017-05-18 Saam Barati <[email protected]>
+
+ WebAssembly: perform stack checks
+ https://bugs.webkit.org/show_bug.cgi?id=165546
+ <rdar://problem/29760307>
+
+ Reviewed by Filip Pizlo.
+
+ * wasm.yaml:
+ * wasm/function-tests/factorial.js:
+ * wasm/function-tests/float-sub.js:
+ * wasm/function-tests/stack-overflow.js: Added.
+ (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance):
+ (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.assertOverflows):
+ (assertOverflows.makeInstance):
+ (assertOverflows.makeInstance2):
+ (assertOverflows.assertThrows):
+ (assertOverflows):
+ (assertThrows.test.makeSignature):
+ (assertThrows.test.makeInstance):
+ (assertThrows.test):
+ (assertThrows):
+
2017-05-18 Keith Miller <[email protected]>
WebAssembly API: test with neutered inputs
Modified: trunk/JSTests/wasm/function-tests/factorial.js (217059 => 217060)
--- trunk/JSTests/wasm/function-tests/factorial.js 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/JSTests/wasm/function-tests/factorial.js 2017-05-18 19:38:10 UTC (rev 217060)
@@ -1,30 +1,36 @@
+import * as assert from '../assert.js'
import Builder from '../Builder.js'
const b = new Builder();
b.Type().End()
+ .Import().End()
.Function().End()
+ .Export()
+ .Function("fac")
+ .End()
.Code()
- .Function({ params: ["i32"], ret: "i32" }, [])
- .GetLocal(0)
- .I32Const(0)
- .I32Eq()
- .If("void", b =>
- b.I32Const(1)
- .Return()
- )
- .GetLocal(0)
- .GetLocal(0)
- .I32Const(1)
- .I32Sub()
- .Call(0)
- .I32Mul()
- .Return()
+ .Function("fac", { params: ["i32"], ret: "i32" })
+ .GetLocal(0)
+ .I32Const(0)
+ .I32Eq()
+ .If("void", b =>
+ b.I32Const(1)
+ .Return()
+ )
+ .GetLocal(0)
+ .GetLocal(0)
+ .I32Const(1)
+ .I32Sub()
+ .Call(0)
+ .I32Mul()
+ .Return()
+ .End()
.End()
-const bin = b.WebAssembly()
-bin.trim();
-testWasmModuleFunctions(bin.get(), 1, [[{type: "i32", value: 1 }, [{ type: "i32", value: 0 }]],
- [{type: "i32", value: 1 }, [{ type: "i32", value: 1 }]],
- [{type: "i32", value: 2 }, [{ type: "i32", value: 2 }]],
- [{type: "i32", value: 24 }, [{ type: "i32", value: 4 }]],
- ]);
+const m = new WebAssembly.Module(b.WebAssembly().get());
+const fac = (new WebAssembly.Instance(m)).exports.fac;
+assert.eq(fac(0), 1);
+assert.eq(fac(1), 1);
+assert.eq(fac(2), 2);
+assert.eq(fac(4), 24);
+assert.throws(() => fac(1e7), RangeError, "Maximum call stack size exceeded.");
Modified: trunk/JSTests/wasm/function-tests/float-sub.js (217059 => 217060)
--- trunk/JSTests/wasm/function-tests/float-sub.js 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/JSTests/wasm/function-tests/float-sub.js 2017-05-18 19:38:10 UTC (rev 217060)
@@ -1,10 +1,15 @@
+import * as assert from '../assert.js'
import Builder from '../Builder.js'
const b = new Builder();
b.Type().End()
.Function().End()
+ .Export()
+ .Function("foo")
+ .Function("bar")
+ .End()
.Code()
- .Function({ params: ["f32", "f32"], ret: "f32" }, [])
+ .Function("bar", { params: ["f32", "f32"], ret: "f32" }, [])
.GetLocal(0)
.GetLocal(1)
.F32Sub()
@@ -11,20 +16,27 @@
.Return()
.End()
- .Function({ params: ["f32", "f32"], ret: "f32" }, [])
+ .Function("foo", { params: ["f32", "f32"], ret: "f32" }, [])
.GetLocal(0)
.GetLocal(1)
.Call(0)
.Return()
.End()
+ .End()
const bin = b.WebAssembly()
bin.trim();
-testWasmModuleFunctions(bin.get(), 2,
- [[{type: "f32", value: -1.5 }, [{ type: "f32", value: 0 }, { type: "f32", value: 1.5 }]],
- [{type: "f32", value: 87.6234 }, [{ type: "f32", value: 100.1234 }, { type: "f32", value: 12.5 }]]
- ],
- [[{type: "f32", value: -1.5 }, [{ type: "f32", value: 0 }, { type: "f32", value: 1.5 }]],
- [{type: "f32", value: 87.6234 }, [{ type: "f32", value: 100.1234 }, { type: "f32", value: 12.5 }]]
- ]
- );
+const instance = new WebAssembly.Instance(new WebAssembly.Module(bin.get()));
+
+let x = new Float32Array(3);
+x[0] = 0;
+x[1] = 1.5;
+x[2] = x[0] - x[1];
+assert.eq(instance.exports.bar(x[0], x[1]), x[2]);
+assert.eq(instance.exports.foo(x[0], x[1]), x[2]);
+
+x[0] = 100.1234
+x[1] = 12.5;
+x[2] = x[0] - x[1];
+assert.eq(instance.exports.bar(x[0], x[1]), x[2]);
+assert.eq(instance.exports.foo(x[0], x[1]), x[2]);
Added: trunk/JSTests/wasm/function-tests/stack-overflow.js (0 => 217060)
--- trunk/JSTests/wasm/function-tests/stack-overflow.js (rev 0)
+++ trunk/JSTests/wasm/function-tests/stack-overflow.js 2017-05-18 19:38:10 UTC (rev 217060)
@@ -0,0 +1,221 @@
+import Builder from '../Builder.js'
+import * as assert from '../assert.js'
+
+{
+ function makeInstance() {
+ const tableDescription = {initial: 1, element: "anyfunc"};
+ const builder = new Builder()
+ .Type()
+ .Func(["i32"], "void")
+ .End()
+ .Import()
+ .Table("imp", "table", tableDescription)
+ .End()
+ .Function().End()
+ .Export()
+ .Function("foo")
+ .End()
+ .Code()
+ .Function("foo", 0 /*['i32'] => 'void'*/)
+ .GetLocal(0) // parameter to call
+ .GetLocal(0) // call index
+ .CallIndirect(0, 0) // calling function of type ['i32'] => 'i32'
+ .Return()
+ .End()
+ .End();
+
+
+ const bin = builder.WebAssembly().get();
+ const module = new WebAssembly.Module(bin);
+ const table = new WebAssembly.Table(tableDescription);
+ return {instance: new WebAssembly.Instance(module, {imp: {table}}), table};
+ }
+
+ const {instance: i1, table: t1} = makeInstance();
+ const {instance: i2, table: t2} = makeInstance();
+ t2.set(0, i1.exports.foo);
+ t1.set(0, i2.exports.foo);
+
+ function assertOverflows(instance) {
+ let stack;
+ try {
+ instance.exports.foo(0)
+ } catch(e) {
+ stack = e.stack;
+ }
+ stack = stack.split("\n");
+ assert.truthy(stack.length > 50);
+ for (let i = 0; i < 50; ++i) {
+ let item = stack[stack.length - i - 1];
+ assert.eq(item, "wasm function: 0@[wasm code]");
+ }
+ }
+ assertOverflows(i1);
+ assertOverflows(i2);
+
+}
+
+{
+ function makeInstance() {
+ const tableDescription = {initial: 1, element: "anyfunc"};
+ const builder = new Builder()
+ .Type()
+ .Func([], "void")
+ .End()
+ .Import()
+ .Table("imp", "table", tableDescription)
+ .End()
+ .Function().End()
+ .Export()
+ .Function("foo")
+ .End()
+ .Code()
+ .Function("foo", {params:["i32"], ret:"void"})
+ .GetLocal(0) // parameter to call
+ .GetLocal(0) // call index
+ .CallIndirect(0, 0) // calling function of type [] => 'void'
+ .Return()
+ .End()
+ .End();
+
+
+ const bin = builder.WebAssembly().get();
+ const module = new WebAssembly.Module(bin);
+ const table = new WebAssembly.Table(tableDescription);
+ return {instance: new WebAssembly.Instance(module, {imp: {table}}), table};
+ }
+
+ function makeInstance2(f) {
+ const builder = new Builder()
+ .Type()
+ .End()
+ .Import()
+ .Function("imp", "f", {params:['i32'], ret:"void"})
+ .End()
+ .Function().End()
+ .Export()
+ .Function("foo")
+ .End()
+ .Code()
+ .Function("foo", {params: [], ret: "void" })
+ .I32Const(0)
+ .Call(0)
+ .Return()
+ .End()
+ .End();
+
+
+ const bin = builder.WebAssembly().get();
+ const module = new WebAssembly.Module(bin);
+ return new WebAssembly.Instance(module, {imp: {f}});
+ }
+
+ const {instance: i1, table: t1} = makeInstance();
+ const foo = i1.exports.foo;
+ const i2 = makeInstance2(i1.exports.foo);
+ t1.set(0, i2.exports.foo);
+
+ function assertThrows(instance) {
+ let stack;
+ try {
+ instance.exports.foo();
+ } catch(e) {
+ stack = e.stack;
+ }
+ assert.truthy(stack);
+
+ stack = stack.split("\n");
+ assert.truthy(stack.length > 50);
+ const _oneString_ = "wasm function: 1@[wasm code]";
+ const zeroString = "wasm function: 0@[wasm code]";
+ let currentIndex = stack[stack.length - 1] === oneString ? 1 : 0;
+ for (let i = 0; i < 50; ++i) {
+ let item = stack[stack.length - 1 - i];
+ if (currentIndex === 1) {
+ assert.eq(item, oneString);
+ currentIndex = 0;
+ } else {
+ assert.eq(currentIndex, 0);
+ assert.eq(item, zeroString);
+ currentIndex = 1;
+ }
+ }
+ }
+
+ for (let i = 0; i < 20; ++i) {
+ assertThrows(i2);
+ assertThrows(i1);
+ }
+
+ for (let i = 0; i < 20; ++i) {
+ assertThrows(i1);
+ assertThrows(i2);
+ }
+}
+
+{
+ function test(numArgs) {
+ function makeSignature() {
+ let args = [];
+ for (let i = 0; i < numArgs; ++i) {
+ args.push("i32");
+ }
+ return {params: args};
+ }
+ function makeInstance(f) {
+ let builder = new Builder()
+ .Type()
+ .Func([], "void")
+ .End()
+ .Import()
+ .Function("imp", "f", makeSignature())
+ .End()
+ .Function().End()
+ .Export()
+ .Function("foo")
+ .End()
+ .Code()
+ .Function("foo", {params:[], ret:"void"});
+ for (let i = 0; i < numArgs; ++i) {
+ builder = builder.I32Const(i);
+ }
+
+ builder = builder.Call(0).Return().End().End();
+ const bin = builder.WebAssembly().get();
+ const module = new WebAssembly.Module(bin);
+ return new WebAssembly.Instance(module, {imp: {f}});
+ }
+
+ function f(...args) {
+ assert.eq(args.length, numArgs);
+ for (let i = 0; i < args.length; ++i)
+ assert.eq(args[i], i);
+
+ instance.exports.foo();
+ }
+ let instance = makeInstance(f);
+
+ let stack;
+ try {
+ instance.exports.foo();
+ } catch(e) {
+ stack = e.stack;
+ }
+ assert.truthy(stack.split("\n").length > 25);
+ }
+
+ for (let i = 0; i < 70; ++i) {
+ let r = Math.random() * 1000 | 0;
+ test(r);
+ }
+
+ test(20);
+ test(20);
+ test(1000);
+ test(2);
+ test(1);
+ test(0);
+ test(700);
+ test(433);
+ test(42);
+}
Modified: trunk/JSTests/wasm.yaml (217059 => 217060)
--- trunk/JSTests/wasm.yaml 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/JSTests/wasm.yaml 2017-05-18 19:38:10 UTC (rev 217060)
@@ -56,10 +56,10 @@
cmd: runWebAssemblySpecTest :normal
- path: wasm/spec-tests/call.wast.js
- cmd: runWebAssemblySpecTest :skip
+ cmd: runWebAssemblySpecTest :normal
- path: wasm/spec-tests/call_indirect.wast.js
- cmd: runWebAssemblySpecTest :skip
+ cmd: runWebAssemblySpecTest :normal
- path: wasm/spec-tests/comments.wast.js
cmd: runWebAssemblySpecTest :normal
@@ -86,7 +86,7 @@
cmd: runWebAssemblySpecTest :normal
- path: wasm/spec-tests/fac.wast.js
- cmd: runWebAssemblySpecTest :skip
+ cmd: runWebAssemblySpecTest :normal
- path: wasm/spec-tests/float_literals.wast.js
cmd: runWebAssemblySpecTest :normal
@@ -167,7 +167,7 @@
cmd: runWebAssemblySpecTest :normal
- path: wasm/spec-tests/skip-stack-guard-page.wast.js
- cmd: runWebAssemblySpecTest :skip
+ cmd: runWebAssemblySpecTest :normal
- path: wasm/spec-tests/stack.wast.js
cmd: runWebAssemblySpecTest :normal
Modified: trunk/Source/_javascript_Core/ChangeLog (217059 => 217060)
--- trunk/Source/_javascript_Core/ChangeLog 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-05-18 19:38:10 UTC (rev 217060)
@@ -1,3 +1,89 @@
+2017-05-18 Saam Barati <[email protected]>
+
+ WebAssembly: perform stack checks
+ https://bugs.webkit.org/show_bug.cgi?id=165546
+ <rdar://problem/29760307>
+
+ Reviewed by Filip Pizlo.
+
+ This patch adds stack checks to wasm. It implements it by storing the stack
+ bounds on the Context.
+
+ Stack checking works as normal, except we do a small optimization for terminal
+ nodes in the call tree (nodes that don't make any calls). These nodes will
+ only do a stack check if their frame size is beyond 1024 bytes. Otherwise,
+ it's assumed the parent that called them did their stack check for them.
+ This is because all things that make calls make sure to do an extra 1024
+ bytes whenever doing a stack check.
+
+ We also take into account stack size for potential JS calls when doing
+ stack checks since our JS stubs don't do this on their own. Each frame
+ will ensure it does a stack check large enough for any potential JS call
+ stubs it'll execute.
+
+ Surprisingly, this patch is neutral on WasmBench and TitzerBench.
+
+ * llint/LLIntData.cpp:
+ (JSC::LLInt::Data::performAssertions):
+ * llint/LowLevelInterpreter.asm:
+ * runtime/Error.cpp:
+ (JSC::createRangeError):
+ (JSC::addErrorInfoAndGetBytecodeOffset):
+ I fixed a bug here where we assumed that the first frame that has line
+ and column info would be in our stack trace. This is not correct
+ since we limit our stack trace size. If everything in our limited
+ size stack trace is Wasm, then we won't have any frames with line
+ and column info.
+ * runtime/Error.h:
+ * runtime/ExceptionHelpers.cpp:
+ (JSC::createStackOverflowError):
+ * runtime/ExceptionHelpers.h:
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::init):
+ (JSC::JSGlobalObject::visitChildren):
+ * runtime/JSGlobalObject.h:
+ (JSC::JSGlobalObject::webAssemblyToJSCalleeStructure):
+ * runtime/JSType.h:
+ * runtime/Options.h: I've added a new option that controls
+ whether or not we use fast TLS for the wasm context.
+ * runtime/VM.cpp:
+ (JSC::VM::VM):
+ * runtime/VM.h:
+ * wasm/WasmB3IRGenerator.cpp:
+ (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+ * wasm/WasmBinding.cpp:
+ (JSC::Wasm::wasmToWasm):
+ * wasm/WasmContext.cpp:
+ (JSC::Wasm::loadContext):
+ (JSC::Wasm::storeContext):
+ * wasm/WasmContext.h:
+ (JSC::Wasm::useFastTLSForContext):
+ * wasm/WasmExceptionType.h:
+ * wasm/WasmMemoryInformation.h:
+ (JSC::Wasm::PinnedRegisterInfo::toSave):
+ * wasm/WasmThunks.cpp:
+ (JSC::Wasm::throwExceptionFromWasmThunkGenerator):
+ (JSC::Wasm::throwStackOverflowFromWasmThunkGenerator):
+ (JSC::Wasm::Thunks::stub):
+ * wasm/WasmThunks.h:
+ * wasm/js/JSWebAssemblyInstance.h:
+ (JSC::JSWebAssemblyInstance::offsetOfCachedStackLimit):
+ (JSC::JSWebAssemblyInstance::cachedStackLimit):
+ (JSC::JSWebAssemblyInstance::setCachedStackLimit):
+ * wasm/js/JSWebAssemblyModule.cpp:
+ (JSC::JSWebAssemblyModule::finishCreation):
+ * wasm/js/WebAssemblyFunction.cpp:
+ (JSC::callWebAssemblyFunction):
+ * wasm/js/WebAssemblyToJSCallee.cpp: Make this a descendent of object.
+ This is needed for correctness because we may call into JS,
+ and then the first JS frame could stack overflow. When it stack
+ overflows, it rolls back one frame to the wasm->js call stub with
+ the wasm->js callee. It gets the lexical global object from this
+ frame, meaning it gets the global object from the callee. Therefore,
+ we must make it an object since all objects have global objects.
+ (JSC::WebAssemblyToJSCallee::create):
+ * wasm/js/WebAssemblyToJSCallee.h:
+
2017-05-18 Keith Miller <[email protected]>
WebAssembly API: test with neutered inputs
Modified: trunk/Source/_javascript_Core/llint/LLIntData.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/llint/LLIntData.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/llint/LLIntData.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -156,21 +156,21 @@
STATIC_ASSERT(StringType == 6);
STATIC_ASSERT(SymbolType == 7);
- STATIC_ASSERT(ObjectType == 24);
- STATIC_ASSERT(FinalObjectType == 25);
- STATIC_ASSERT(JSFunctionType == 27);
- STATIC_ASSERT(ArrayType == 35);
- STATIC_ASSERT(DerivedArrayType == 36);
- STATIC_ASSERT(ProxyObjectType == 54);
- STATIC_ASSERT(Int8ArrayType == 37);
- STATIC_ASSERT(Int16ArrayType == 38);
- STATIC_ASSERT(Int32ArrayType == 39);
- STATIC_ASSERT(Uint8ArrayType == 40);
- STATIC_ASSERT(Uint8ClampedArrayType == 41);
- STATIC_ASSERT(Uint16ArrayType == 42);
- STATIC_ASSERT(Uint32ArrayType == 43);
- STATIC_ASSERT(Float32ArrayType == 44);
- STATIC_ASSERT(Float64ArrayType == 45);
+ STATIC_ASSERT(ObjectType == 23);
+ STATIC_ASSERT(FinalObjectType == 24);
+ STATIC_ASSERT(JSFunctionType == 26);
+ STATIC_ASSERT(ArrayType == 34);
+ STATIC_ASSERT(DerivedArrayType == 35);
+ STATIC_ASSERT(ProxyObjectType == 53);
+ STATIC_ASSERT(Int8ArrayType == 36);
+ STATIC_ASSERT(Int16ArrayType == 37);
+ STATIC_ASSERT(Int32ArrayType == 38);
+ STATIC_ASSERT(Uint8ArrayType == 39);
+ STATIC_ASSERT(Uint8ClampedArrayType == 40);
+ STATIC_ASSERT(Uint16ArrayType == 41);
+ STATIC_ASSERT(Uint32ArrayType == 42);
+ STATIC_ASSERT(Float32ArrayType == 43);
+ STATIC_ASSERT(Float64ArrayType == 44);
STATIC_ASSERT(MasqueradesAsUndefined == 1);
STATIC_ASSERT(ImplementsDefaultHasInstance == 2);
STATIC_ASSERT(FirstConstantRegisterIndex == 0x40000000);
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm (217059 => 217060)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm 2017-05-18 19:38:10 UTC (rev 217060)
@@ -345,24 +345,24 @@
# Type constants.
const StringType = 6
const SymbolType = 7
-const ObjectType = 24
-const FinalObjectType = 25
-const JSFunctionType = 27
-const ArrayType = 35
-const DerivedArrayType = 36
-const ProxyObjectType = 54
+const ObjectType = 23
+const FinalObjectType = 24
+const JSFunctionType = 26
+const ArrayType = 34
+const DerivedArrayType = 35
+const ProxyObjectType = 53
# The typed array types need to be numbered in a particular order because of the manually written
# switch statement in get_by_val and put_by_val.
-const Int8ArrayType = 37
-const Int16ArrayType = 38
-const Int32ArrayType = 39
-const Uint8ArrayType = 40
-const Uint8ClampedArrayType = 41
-const Uint16ArrayType = 42
-const Uint32ArrayType = 43
-const Float32ArrayType = 44
-const Float64ArrayType = 45
+const Int8ArrayType = 36
+const Int16ArrayType = 37
+const Int32ArrayType = 38
+const Uint8ArrayType = 39
+const Uint8ClampedArrayType = 40
+const Uint16ArrayType = 41
+const Uint32ArrayType = 42
+const Float32ArrayType = 43
+const Float64ArrayType = 44
const FirstArrayType = Int8ArrayType
const LastArrayType = Float64ArrayType
Modified: trunk/Source/_javascript_Core/runtime/Error.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/runtime/Error.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/runtime/Error.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -60,8 +60,13 @@
JSObject* createRangeError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender)
{
+ JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+ return createRangeError(exec, globalObject, message, appender);
+}
+
+JSObject* createRangeError(ExecState* exec, JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender)
+{
ASSERT(!message.isEmpty());
- JSGlobalObject* globalObject = exec->lexicalGlobalObject();
return ErrorInstance::create(exec, globalObject->vm(), globalObject->rangeErrorConstructor()->errorStructure(), message, appender, TypeNothing, true);
}
@@ -182,7 +187,7 @@
callFrame = functor.foundCallFrame();
unsigned stackIndex = functor.index();
*bytecodeOffset = 0;
- if (stackTrace.at(stackIndex).hasBytecodeOffset())
+ if (stackIndex < stackTrace.size() && stackTrace.at(stackIndex).hasBytecodeOffset())
*bytecodeOffset = stackTrace.at(stackIndex).bytecodeOffset();
}
@@ -268,6 +273,11 @@
return createRangeError(exec, message, nullptr);
}
+JSObject* createRangeError(ExecState* exec, JSGlobalObject* globalObject, const String& message)
+{
+ return createRangeError(exec, globalObject, message, nullptr);
+}
+
JSObject* createReferenceError(ExecState* exec, const String& message)
{
return createReferenceError(exec, message, nullptr);
Modified: trunk/Source/_javascript_Core/runtime/Error.h (217059 => 217060)
--- trunk/Source/_javascript_Core/runtime/Error.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/runtime/Error.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -52,6 +52,7 @@
JSObject* createError(ExecState*, const String&, ErrorInstance::SourceAppender);
JSObject* createEvalError(ExecState*, const String&, ErrorInstance::SourceAppender);
JSObject* createRangeError(ExecState*, const String&, ErrorInstance::SourceAppender);
+JSObject* createRangeError(ExecState*, JSGlobalObject*, const String&, ErrorInstance::SourceAppender);
JSObject* createReferenceError(ExecState*, const String&, ErrorInstance::SourceAppender);
JSObject* createSyntaxError(ExecState*, const String&, ErrorInstance::SourceAppender);
JSObject* createTypeError(ExecState*, const String&, ErrorInstance::SourceAppender, RuntimeType);
@@ -62,6 +63,7 @@
JS_EXPORT_PRIVATE JSObject* createError(ExecState*, const String&);
JS_EXPORT_PRIVATE JSObject* createEvalError(ExecState*, const String&);
JS_EXPORT_PRIVATE JSObject* createRangeError(ExecState*, const String&);
+JS_EXPORT_PRIVATE JSObject* createRangeError(ExecState*, JSGlobalObject*, const String&);
JS_EXPORT_PRIVATE JSObject* createReferenceError(ExecState*, const String&);
JS_EXPORT_PRIVATE JSObject* createSyntaxError(ExecState*, const String&);
JS_EXPORT_PRIVATE JSObject* createTypeError(ExecState*);
Modified: trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/runtime/ExceptionHelpers.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -69,9 +69,14 @@
JSObject* createStackOverflowError(ExecState* exec)
{
- return createRangeError(exec, ASCIILiteral("Maximum call stack size exceeded."));
+ return createStackOverflowError(exec, exec->lexicalGlobalObject());
}
+JSObject* createStackOverflowError(ExecState* exec, JSGlobalObject* globalObject)
+{
+ return createRangeError(exec, globalObject, ASCIILiteral("Maximum call stack size exceeded."));
+}
+
JSObject* createUndefinedVariableError(ExecState* exec, const Identifier& ident)
{
if (exec->propertyNames().isPrivateName(ident)) {
Modified: trunk/Source/_javascript_Core/runtime/ExceptionHelpers.h (217059 => 217060)
--- trunk/Source/_javascript_Core/runtime/ExceptionHelpers.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/runtime/ExceptionHelpers.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -43,6 +43,7 @@
JS_EXPORT_PRIVATE bool isTerminatedExecutionException(VM&, Exception*);
JS_EXPORT_PRIVATE JSObject* createError(ExecState*, JSValue, const String&, ErrorInstance::SourceAppender);
JS_EXPORT_PRIVATE JSObject* createStackOverflowError(ExecState*);
+JSObject* createStackOverflowError(ExecState*, JSGlobalObject*);
JSObject* createUndefinedVariableError(ExecState*, const Identifier&);
JSObject* createTDZError(ExecState*);
JSObject* createNotAnObjectError(ExecState*, JSValue);
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -163,6 +163,7 @@
#include "WeakMapPrototype.h"
#include "WeakSetConstructor.h"
#include "WeakSetPrototype.h"
+#include "WebAssemblyToJSCallee.h"
#include <wtf/RandomNumber.h>
#if ENABLE(INTL)
@@ -887,6 +888,7 @@
m_webAssemblyModuleRecordStructure.set(vm, this, WebAssemblyModuleRecord::createStructure(vm, this, m_objectPrototype.get()));
m_webAssemblyFunctionStructure.set(vm, this, WebAssemblyFunction::createStructure(vm, this, m_functionPrototype.get()));
m_webAssemblyWrapperFunctionStructure.set(vm, this, WebAssemblyWrapperFunction::createStructure(vm, this, m_functionPrototype.get()));
+ m_webAssemblyToJSCalleeStructure.set(vm, this, WebAssemblyToJSCallee::createStructure(vm, this, jsNull()));
auto* webAssembly = JSWebAssembly::create(vm, this, m_webAssemblyStructure.get());
putDirectWithoutTransition(vm, Identifier::fromString(exec, "WebAssembly"), webAssembly, DontEnum);
@@ -1270,6 +1272,7 @@
visitor.append(thisObject->m_webAssemblyModuleRecordStructure);
visitor.append(thisObject->m_webAssemblyFunctionStructure);
visitor.append(thisObject->m_webAssemblyWrapperFunctionStructure);
+ visitor.append(thisObject->m_webAssemblyToJSCalleeStructure);
FOR_EACH_WEBASSEMBLY_CONSTRUCTOR_TYPE(VISIT_SIMPLE_TYPE)
#endif // ENABLE(WEBASSEMBLY)
Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (217059 => 217060)
--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -351,6 +351,7 @@
WriteBarrier<Structure> m_webAssemblyModuleRecordStructure;
WriteBarrier<Structure> m_webAssemblyFunctionStructure;
WriteBarrier<Structure> m_webAssemblyWrapperFunctionStructure;
+ WriteBarrier<Structure> m_webAssemblyToJSCalleeStructure;
FOR_EACH_WEBASSEMBLY_CONSTRUCTOR_TYPE(DEFINE_STORAGE_FOR_SIMPLE_TYPE)
#endif // ENABLE(WEBASSEMBLY)
@@ -621,6 +622,7 @@
Structure* webAssemblyModuleRecordStructure() const { return m_webAssemblyModuleRecordStructure.get(); }
Structure* webAssemblyFunctionStructure() const { return m_webAssemblyFunctionStructure.get(); }
Structure* webAssemblyWrapperFunctionStructure() const { return m_webAssemblyWrapperFunctionStructure.get(); }
+ Structure* webAssemblyToJSCalleeStructure() const { return m_webAssemblyToJSCalleeStructure.get(); }
#endif // ENABLE(WEBASSEMBLY)
JS_EXPORT_PRIVATE void setRemoteDebuggingEnabled(bool);
Modified: trunk/Source/_javascript_Core/runtime/JSType.h (217059 => 217060)
--- trunk/Source/_javascript_Core/runtime/JSType.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/runtime/JSType.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -53,8 +53,6 @@
JSSourceCodeType,
JSScriptFetcherType,
- WebAssemblyToJSCalleeType,
-
// The ObjectType value must come before any JSType that is a subclass of JSObject.
ObjectType,
FinalObjectType,
@@ -101,7 +99,9 @@
ClonedArgumentsType,
- LastJSCObjectType = ClonedArgumentsType,
+ WebAssemblyToJSCalleeType,
+
+ LastJSCObjectType = WebAssemblyToJSCalleeType,
MaxJSType = 0b11111111,
};
Modified: trunk/Source/_javascript_Core/runtime/Options.h (217059 => 217060)
--- trunk/Source/_javascript_Core/runtime/Options.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/runtime/Options.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -455,6 +455,7 @@
v(bool, crashIfWebAssemblyCantFastMemory, false, Normal, "If true, we will crash if we can't obtain fast memory for wasm.") \
v(unsigned, webAssemblyFastMemoryPreallocateCount, 0, Normal, "WebAssembly fast memories can be pre-allocated at program startup and remain cached to avoid fragmentation leading to bounds-checked memory. This number is an upper bound on initial allocation as well as total count of fast memories. Zero means no pre-allocation, no caching, and no limit to the number of runtime allocations.") \
v(bool, useWebAssemblyFastTLS, true, Normal, "If true, we will try to use fast thread-local storage if available on the current platform.") \
+ v(bool, useFastTLSForWasmContext, true, Normal, "If true (and fast TLS is enabled), we will store context in fast TLS. If false, we will pin it to a register.") \
v(bool, useCallICsForWebAssemblyToJSCalls, true, Normal, "If true, we will use CallLinkInfo to inline cache Wasm to JS calls.")
Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/runtime/VM.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -239,7 +239,6 @@
programExecutableStructure.set(*this, ProgramExecutable::createStructure(*this, 0, jsNull()));
functionExecutableStructure.set(*this, FunctionExecutable::createStructure(*this, 0, jsNull()));
#if ENABLE(WEBASSEMBLY)
- webAssemblyToJSCalleeStructure.set(*this, WebAssemblyToJSCallee::createStructure(*this, 0, jsNull()));
webAssemblyCodeBlockStructure.set(*this, JSWebAssemblyCodeBlock::createStructure(*this, 0, jsNull()));
#endif
moduleProgramExecutableStructure.set(*this, ModuleProgramExecutable::createStructure(*this, 0, jsNull()));
Modified: trunk/Source/_javascript_Core/runtime/VM.h (217059 => 217060)
--- trunk/Source/_javascript_Core/runtime/VM.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/runtime/VM.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -321,7 +321,6 @@
Strong<Structure> programExecutableStructure;
Strong<Structure> functionExecutableStructure;
#if ENABLE(WEBASSEMBLY)
- Strong<Structure> webAssemblyToJSCalleeStructure;
Strong<Structure> webAssemblyCodeBlockStructure;
#endif
Strong<Structure> moduleProgramExecutableStructure;
Modified: trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -267,6 +267,8 @@
GPRReg m_memorySizeGPR { InvalidGPRReg };
GPRReg m_wasmContextGPR;
Value* m_instanceValue; // FIXME: make this lazy https://bugs.webkit.org/show_bug.cgi?id=169792
+ bool m_makesCalls { false };
+ uint32_t m_maxNumJSCallArguments { 0 };
};
// Memory accesses in WebAssembly have unsigned 32-bit offsets, whereas they have signed 32-bit offsets in B3.
@@ -381,9 +383,52 @@
wasmCallingConvention().setupFrameInPrologue(&compilation->wasmCalleeMoveLocation, m_proc, Origin(), m_currentBlock);
+ m_instanceValue = materializeWasmContext(m_currentBlock);
+
+ {
+ B3::Value* framePointer = m_currentBlock->appendNew<B3::Value>(m_proc, B3::FramePointer, Origin());
+ B3::PatchpointValue* stackOverflowCheck = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, Origin());
+ stackOverflowCheck->appendSomeRegister(framePointer);
+ stackOverflowCheck->appendSomeRegister(m_instanceValue);
+ stackOverflowCheck->clobber(RegisterSet::macroScratchRegisters());
+ stackOverflowCheck->numGPScratchRegisters = 2;
+ stackOverflowCheck->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+ GPRReg fp = params[0].gpr();
+ GPRReg context = params[1].gpr();
+ GPRReg scratch1 = params.gpScratch(0);
+ GPRReg scratch2 = params.gpScratch(1);
+
+ const Checked<int32_t> wasmFrameSize = params.proc().frameSize();
+ const unsigned minimumParentCheckSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), 1024);
+ const unsigned extraFrameSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), std::max<uint32_t>(
+ // This allows us to elide stack checks for functions that are terminal nodes in the call
+ // tree, (e.g they don't make any calls) and have a small enough frame size. This works by
+ // having any such terminal node have its parent caller include some extra size in its
+ // own check for it. The goal here is twofold:
+ // 1. Emit less code.
+ // 2. Try to speed things up by skipping stack checks.
+ minimumParentCheckSize,
+ // This allows us to elide stack checks in the Wasm -> JS call IC stub. Since these will
+ // spill all arguments to the stack, we ensure that a stack check here covers the
+ // stack that such a stub would use.
+ (Checked<uint32_t>(m_maxNumJSCallArguments) * sizeof(Register) + jscCallingConvention().headerSizeInBytes()).unsafeGet()
+ ));
+ const int32_t checkSize = m_makesCalls ? (wasmFrameSize + extraFrameSize).unsafeGet() : wasmFrameSize.unsafeGet();
+ // This allows leaf functions to not do stack checks if their frame size is within
+ // certain limits since their caller would have already done the check.
+ if (m_makesCalls || wasmFrameSize >= minimumParentCheckSize) {
+ jit.loadPtr(CCallHelpers::Address(context, Context::offsetOfCachedStackLimit()), scratch2);
+ jit.addPtr(CCallHelpers::TrustedImm32(-checkSize), fp, scratch1);
+ auto overflow = jit.branchPtr(CCallHelpers::Below, scratch1, scratch2);
+ jit.addLinkTask([overflow] (LinkBuffer& linkBuffer) {
+ linkBuffer.link(overflow, CodeLocationLabel(Thunks::singleton().stub(throwStackOverflowFromWasmThunkGenerator).code()));
+ });
+ }
+ });
+ }
+
m_currentBlock = emitTierUpCheck(m_currentBlock, TierUpCount::functionEntryDecrement(), Origin());
-
- m_instanceValue = materializeWasmContext(m_currentBlock);
}
void B3IRGenerator::restoreWebAssemblyGlobalState(const MemoryInformation& memory, Value* instance, Procedure& proc, BasicBlock* block)
@@ -998,10 +1043,14 @@
{
ASSERT(signature.argumentCount() == args.size());
+ m_makesCalls = true;
+
Type returnType = signature.returnType();
Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls = &m_unlinkedWasmToWasmCalls;
if (m_info.isImportedFunctionFromFunctionIndexSpace(functionIndex)) {
+ m_maxNumJSCallArguments = std::max(m_maxNumJSCallArguments, static_cast<uint32_t>(args.size()));
+
// FIXME imports can be linked here, instead of generating a patchpoint, because all import stubs are generated before B3 compilation starts. https://bugs.webkit.org/show_bug.cgi?id=166462
Value* functionImport = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), m_instanceValue, safeCast<int32_t>(JSWebAssemblyInstance::offsetOfImportFunction(functionIndex)));
Value* jsTypeOfImport = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, origin(), functionImport, safeCast<int32_t>(JSCell::typeInfoTypeOffset()));
@@ -1017,7 +1066,9 @@
patchpoint->effects.writesPinned = true;
patchpoint->effects.readsPinned = true;
// We need to clobber all potential pinned registers since we might be leaving the instance.
- patchpoint->clobberLate(PinnedRegisterInfo::get().toSave());
+ // We pessimistically assume we could be calling to something that is bounds checking.
+ // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
+ patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
AllowMacroScratchRegisterUsage allowScratch(jit);
CCallHelpers::Call call = jit.threadSafePatchableNearCall();
@@ -1043,7 +1094,9 @@
patchpoint->effects.readsPinned = true;
patchpoint->append(jumpDestination, ValueRep::SomeRegister);
// We need to clobber all potential pinned registers since we might be leaving the instance.
- patchpoint->clobberLate(PinnedRegisterInfo::get().toSave());
+ // We pessimistically assume we could be calling to something that is bounds checking.
+ // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
+ patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
patchpoint->setGenerator([returnType] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
AllowMacroScratchRegisterUsage allowScratch(jit);
jit.call(params[returnType == Void ? 0 : 1].gpr());
@@ -1088,6 +1141,12 @@
ExpressionType calleeIndex = args.takeLast();
ASSERT(signature.argumentCount() == args.size());
+ m_makesCalls = true;
+ // Note: call indirect can call either WebAssemblyFunction or WebAssemblyWrapperFunction. Because
+ // WebAssemblyWrapperFunction is like calling into JS, we conservatively assume all call indirects
+ // can be to JS for our stack check calculation.
+ m_maxNumJSCallArguments = std::max(m_maxNumJSCallArguments, static_cast<uint32_t>(args.size()));
+
ExpressionType callableFunctionBuffer;
ExpressionType jsFunctionBuffer;
ExpressionType callableFunctionBufferSize;
@@ -1168,14 +1227,17 @@
patchpoint->clobber(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
patchpoint->clobber(RegisterSet::macroScratchRegisters());
patchpoint->append(newContext, ValueRep::SomeRegister);
+ patchpoint->append(m_instanceValue, ValueRep::SomeRegister);
patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
AllowMacroScratchRegisterUsage allowScratch(jit);
GPRReg newContext = params[0].gpr();
+ GPRReg oldContext = params[1].gpr();
const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
const auto& sizeRegs = pinnedRegs.sizeRegisters;
GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
ASSERT(newContext != baseMemory);
-
+ jit.loadPtr(CCallHelpers::Address(oldContext, Context::offsetOfCachedStackLimit()), baseMemory);
+ jit.storePtr(baseMemory, CCallHelpers::Address(newContext, Context::offsetOfCachedStackLimit()));
jit.storeWasmContext(newContext);
jit.loadPtr(CCallHelpers::Address(newContext, Context::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*.
ASSERT(sizeRegs.size() == 1);
@@ -1200,7 +1262,11 @@
patchpoint->effects.writesPinned = true;
patchpoint->effects.readsPinned = true;
// We need to clobber all potential pinned registers since we might be leaving the instance.
- patchpoint->clobberLate(PinnedRegisterInfo::get().toSave());
+ // We pessimistically assume we're always calling something that is bounds checking so
+ // because the wasm->wasm thunk unconditionally overrides the size registers.
+ // FIXME: We should not have to do this, but the wasm->wasm stub assumes it can
+ // use all the pinned registers as scratch: https://bugs.webkit.org/show_bug.cgi?id=172181
+ patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
patchpoint->append(calleeCode, ValueRep::SomeRegister);
patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
Modified: trunk/Source/_javascript_Core/wasm/WasmBinding.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/WasmBinding.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/WasmBinding.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -36,6 +36,7 @@
#include "LinkBuffer.h"
#include "NativeErrorConstructor.h"
#include "WasmCallingConvention.h"
+#include "WasmContext.h"
#include "WasmExceptionType.h"
namespace JSC { namespace Wasm {
@@ -608,23 +609,29 @@
JIT jit;
GPRReg scratch = GPRInfo::nonPreservedNonArgumentGPR;
+ GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
+ ASSERT(baseMemory != scratch);
+ const auto& sizeRegs = pinnedRegs.sizeRegisters;
+ ASSERT(sizeRegs.size() >= 1);
+ ASSERT(sizeRegs[0].sizeRegister != baseMemory);
+ ASSERT(sizeRegs[0].sizeRegister != scratch);
+ GPRReg sizeRegAsScratch = sizeRegs[0].sizeRegister;
+ static_assert(std::is_same<Context, JSWebAssemblyInstance>::value, "This is assumed in the code below.");
// B3's call codegen ensures that the JSCell is a WebAssemblyFunction.
- materializeImportJSCell(jit, importIndex, scratch);
+ jit.loadWasmContext(sizeRegAsScratch); // Old Instance*
+ jit.loadPtr(JIT::Address(sizeRegAsScratch, JSWebAssemblyInstance::offsetOfImportFunction(importIndex)), scratch);
// Get the callee's WebAssembly.Instance and set it as WasmContext. The caller will take care of restoring its own Instance.
- GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
- ASSERT(baseMemory != scratch);
jit.loadPtr(JIT::Address(scratch, WebAssemblyFunction::offsetOfInstance()), baseMemory); // Instance*.
jit.storeWasmContext(baseMemory);
+ jit.loadPtr(JIT::Address(sizeRegAsScratch, JSWebAssemblyInstance::offsetOfCachedStackLimit()), sizeRegAsScratch);
+ jit.storePtr(sizeRegAsScratch, JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfCachedStackLimit()));
+
// FIXME the following code assumes that all WebAssembly.Instance have the same pinned registers. https://bugs.webkit.org/show_bug.cgi?id=162952
// Set up the callee's baseMemory register as well as the memory size registers.
jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*.
- const auto& sizeRegs = pinnedRegs.sizeRegisters;
- ASSERT(sizeRegs.size() >= 1);
- ASSERT(sizeRegs[0].sizeRegister != baseMemory);
- ASSERT(sizeRegs[0].sizeRegister != scratch);
ASSERT(!sizeRegs[0].sizeOffset); // The following code assumes we start at 0, and calculates subsequent size registers relative to 0.
jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfSize()), sizeRegs[0].sizeRegister); // Memory size.
jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfMemory()), baseMemory); // WasmMemory::void*.
Modified: trunk/Source/_javascript_Core/wasm/WasmContext.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/WasmContext.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/WasmContext.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -40,7 +40,6 @@
if (useFastTLSForContext())
return bitwise_cast<Context*>(_pthread_getspecific_direct(WTF_WASM_CONTEXT_KEY));
#endif
- // FIXME: Save this state elsewhere to allow PIC. https://bugs.webkit.org/show_bug.cgi?id=169773
return vm.wasmContext;
}
@@ -50,8 +49,9 @@
if (useFastTLSForContext())
_pthread_setspecific_direct(WTF_WASM_CONTEXT_KEY, bitwise_cast<void*>(context));
#endif
- // FIXME: Save this state elsewhere to allow PIC. https://bugs.webkit.org/show_bug.cgi?id=169773
vm.wasmContext = context;
+ if (context)
+ context->setCachedStackLimit(vm.softStackLimit());
}
} } // namespace JSC::Wasm
Modified: trunk/Source/_javascript_Core/wasm/WasmContext.h (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/WasmContext.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/WasmContext.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -51,7 +51,9 @@
inline bool useFastTLSForContext()
{
- return useFastTLS();
+ if (useFastTLS())
+ return Options::useFastTLSForWasmContext();
+ return false;
}
Context* loadContext(VM&);
Modified: trunk/Source/_javascript_Core/wasm/WasmExceptionType.h (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/WasmExceptionType.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/WasmExceptionType.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -39,7 +39,8 @@
macro(OutOfBoundsTrunc, "Out of bounds Trunc operation") \
macro(Unreachable, "Unreachable code should not be executed") \
macro(DivisionByZero, "Division by zero") \
- macro(IntegerOverflow, "Integer overflow")
+ macro(IntegerOverflow, "Integer overflow") \
+ macro(StackOverflow, "Stack overflow")
enum class ExceptionType : uint32_t {
#define MAKE_ENUM(enumName, error) enumName,
Modified: trunk/Source/_javascript_Core/wasm/WasmMemoryInformation.h (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/WasmMemoryInformation.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/WasmMemoryInformation.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -48,7 +48,7 @@
static const PinnedRegisterInfo& get();
PinnedRegisterInfo(Vector<PinnedSizeRegisterInfo>&&, GPRReg, GPRReg);
- RegisterSet toSave(MemoryMode mode = MemoryMode::BoundsChecking) const
+ RegisterSet toSave(MemoryMode mode) const
{
RegisterSet result;
result.set(baseMemoryPointer);
Modified: trunk/Source/_javascript_Core/wasm/WasmThunks.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/WasmThunks.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/WasmThunks.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -40,7 +40,7 @@
namespace JSC { namespace Wasm {
-MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator()
+MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator(const AbstractLocker&)
{
CCallHelpers jit;
@@ -62,8 +62,11 @@
auto throwScope = DECLARE_THROW_SCOPE(*vm);
JSGlobalObject* globalObject = wasmContext->globalObject();
- JSWebAssemblyRuntimeError* error = JSWebAssemblyRuntimeError::create(
- exec, *vm, globalObject->WebAssemblyRuntimeErrorStructure(), Wasm::errorMessageForExceptionType(type));
+ JSObject* error;
+ if (type == ExceptionType::StackOverflow)
+ error = createStackOverflowError(exec, globalObject);
+ else
+ error = JSWebAssemblyRuntimeError::create(exec, *vm, globalObject->WebAssemblyRuntimeErrorStructure(), Wasm::errorMessageForExceptionType(type));
throwException(exec, throwScope, error);
}
@@ -86,6 +89,18 @@
return FINALIZE_CODE(linkBuffer, ("Throw exception from Wasm"));
}
+MacroAssemblerCodeRef throwStackOverflowFromWasmThunkGenerator(const AbstractLocker& locker)
+{
+ CCallHelpers jit;
+
+ jit.move(GPRInfo::callFrameRegister, MacroAssembler::stackPointerRegister);
+ jit.move(CCallHelpers::TrustedImm32(static_cast<uint32_t>(ExceptionType::StackOverflow)), GPRInfo::argumentGPR1);
+ auto jumpToExceptionHandler = jit.jump();
+ LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
+ linkBuffer.link(jumpToExceptionHandler, CodeLocationLabel(Thunks::singleton().stub(locker, throwExceptionFromWasmThunkGenerator).code()));
+ return FINALIZE_CODE(linkBuffer, ("Throw stack overflow from Wasm"));
+}
+
static Thunks* thunks;
void Thunks::initialize()
{
@@ -101,12 +116,22 @@
MacroAssemblerCodeRef Thunks::stub(ThunkGenerator generator)
{
auto locker = holdLock(m_lock);
+ return stub(locker, generator);
+}
+MacroAssemblerCodeRef Thunks::stub(const AbstractLocker& locker, ThunkGenerator generator)
+{
ASSERT(!!generator);
- auto addResult = m_stubs.add(generator, MacroAssemblerCodeRef());
- if (addResult.isNewEntry)
- addResult.iterator->value = generator();
- return addResult.iterator->value;
+ {
+ auto addResult = m_stubs.add(generator, MacroAssemblerCodeRef());
+ if (!addResult.isNewEntry)
+ return addResult.iterator->value;
+ }
+
+ MacroAssemblerCodeRef code = generator(locker);
+ // We specifically don't use the iterator here to allow generator to recursively change m_stubs.
+ m_stubs.set(generator, code);
+ return code;
}
MacroAssemblerCodeRef Thunks::existingStub(ThunkGenerator generator)
Modified: trunk/Source/_javascript_Core/wasm/WasmThunks.h (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/WasmThunks.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/WasmThunks.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -31,9 +31,10 @@
namespace JSC { namespace Wasm {
-MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator();
+MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator(const AbstractLocker&);
+MacroAssemblerCodeRef throwStackOverflowFromWasmThunkGenerator(const AbstractLocker&);
-typedef MacroAssemblerCodeRef (*ThunkGenerator)();
+typedef MacroAssemblerCodeRef (*ThunkGenerator)(const AbstractLocker&);
class Thunks {
public:
@@ -41,6 +42,7 @@
static Thunks& singleton();
MacroAssemblerCodeRef stub(ThunkGenerator);
+ MacroAssemblerCodeRef stub(const AbstractLocker&, ThunkGenerator);
MacroAssemblerCodeRef existingStub(ThunkGenerator);
private:
Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.h (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyInstance.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -73,11 +73,15 @@
static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_globals); }
static ptrdiff_t offsetOfVM() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_vm); }
static ptrdiff_t offsetOfCodeBlock() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_codeBlock); }
+ static ptrdiff_t offsetOfCachedStackLimit() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_cachedStackLimit); }
static size_t offsetOfImportFunctions() { return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSCell>)>(sizeof(JSWebAssemblyInstance)); }
static size_t offsetOfImportFunction(size_t importFunctionNum) { return offsetOfImportFunctions() + importFunctionNum * sizeof(sizeof(WriteBarrier<JSCell>)); }
WebAssemblyToJSCallee* webAssemblyToJSCallee() { return m_callee.get(); }
+ void* cachedStackLimit() const { return m_cachedStackLimit; }
+ void setCachedStackLimit(void* limit) { m_cachedStackLimit = limit; }
+
protected:
JSWebAssemblyInstance(VM&, Structure*, unsigned numImportFunctions);
void finishCreation(VM&, JSWebAssemblyModule*, JSModuleNamespaceObject*);
@@ -101,6 +105,7 @@
WriteBarrier<JSWebAssemblyTable> m_table;
WriteBarrier<WebAssemblyToJSCallee> m_callee;
MallocPtr<uint64_t> m_globals;
+ void* m_cachedStackLimit { bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()) };
unsigned m_numImportFunctions;
};
Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyModule.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyModule.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyModule.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -82,7 +82,7 @@
}
m_exportSymbolTable.set(vm, this, exportSymbolTable);
- m_callee.set(vm, this, WebAssemblyToJSCallee::create(vm, vm.webAssemblyToJSCalleeStructure.get(), this));
+ m_callee.set(vm, this, WebAssemblyToJSCallee::create(vm, this));
}
void JSWebAssemblyModule::destroy(JSCell* cell)
Modified: trunk/Source/_javascript_Core/wasm/js/WebAssemblyFunction.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/js/WebAssemblyFunction.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/js/WebAssemblyFunction.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -128,6 +128,15 @@
// FIXME Do away with this entire function, and only use the entrypoint generated by B3. https://bugs.webkit.org/show_bug.cgi?id=166486
Wasm::Context* prevWasmContext = Wasm::loadContext(vm);
+ {
+ // We do the stack check here for the wrapper function because we don't
+ // want to emit a stack check inside every wrapper function.
+ const intptr_t sp = bitwise_cast<intptr_t>(&sp); // A proxy for the current stack pointer.
+ const intptr_t frameSize = (boxedArgs.size() + CallFrame::headerSizeInRegisters) * sizeof(Register);
+ const intptr_t stackSpaceUsed = 2 * frameSize; // We're making two calls. One to the wrapper, and one to the actual wasm code.
+ if (UNLIKELY((sp - stackSpaceUsed) < bitwise_cast<intptr_t>(vm.softStackLimit())))
+ return JSValue::encode(throwException(exec, scope, createStackOverflowError(exec)));
+ }
Wasm::storeContext(vm, wasmContext);
ASSERT(wasmFunction->instance());
ASSERT(wasmFunction->instance() == Wasm::loadContext(vm));
@@ -135,7 +144,14 @@
// We need to make sure this is in a register or on the stack since it's stored in Vector<JSValue>.
// This probably isn't strictly necessary, since the WebAssemblyFunction* should keep the instance
// alive. But it's good hygiene.
- wasmContext->use();
+ wasmContext->use();
+ if (prevWasmContext != wasmContext) {
+ // This is just for some extra safety instead of leaving a cached
+ // value in there. If we ever forget to set the value to be a real
+ // bounds, this will force every stack overflow check to immediately
+ // fire.
+ wasmContext->setCachedStackLimit(bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()));
+ }
Wasm::storeContext(vm, prevWasmContext);
RETURN_IF_EXCEPTION(scope, { });
Modified: trunk/Source/_javascript_Core/wasm/js/WebAssemblyToJSCallee.cpp (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/js/WebAssemblyToJSCallee.cpp 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/js/WebAssemblyToJSCallee.cpp 2017-05-18 19:38:10 UTC (rev 217060)
@@ -33,10 +33,11 @@
namespace JSC {
-const ClassInfo WebAssemblyToJSCallee::s_info = { "WebAssemblyToJSCallee", nullptr, 0, CREATE_METHOD_TABLE(WebAssemblyToJSCallee) };
+const ClassInfo WebAssemblyToJSCallee::s_info = { "WebAssemblyToJSCallee", &Base::s_info, 0, CREATE_METHOD_TABLE(WebAssemblyToJSCallee) };
-WebAssemblyToJSCallee* WebAssemblyToJSCallee::create(VM& vm, Structure* structure, JSWebAssemblyModule* module)
+WebAssemblyToJSCallee* WebAssemblyToJSCallee::create(VM& vm, JSWebAssemblyModule* module)
{
+ Structure* structure = module->globalObject()->webAssemblyToJSCalleeStructure();
WebAssemblyToJSCallee* callee = new (NotNull, allocateCell<WebAssemblyToJSCallee>(vm.heap)) WebAssemblyToJSCallee(vm, structure);
callee->finishCreation(vm, module);
return callee;
Modified: trunk/Source/_javascript_Core/wasm/js/WebAssemblyToJSCallee.h (217059 => 217060)
--- trunk/Source/_javascript_Core/wasm/js/WebAssemblyToJSCallee.h 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Source/_javascript_Core/wasm/js/WebAssemblyToJSCallee.h 2017-05-18 19:38:10 UTC (rev 217060)
@@ -27,18 +27,18 @@
#if ENABLE(WEBASSEMBLY)
-#include "JSCell.h"
+#include "JSObject.h"
namespace JSC {
class JSWebAssemblyModule;
-class WebAssemblyToJSCallee final : public JSCell {
+class WebAssemblyToJSCallee final : public JSNonFinalObject {
public:
- typedef JSCell Base;
- static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
+ using Base = JSNonFinalObject;
+ static const unsigned StructureFlags = Base::StructureFlags;
- static WebAssemblyToJSCallee* create(VM&, Structure*, JSWebAssemblyModule*);
+ static WebAssemblyToJSCallee* create(VM&, JSWebAssemblyModule*);
static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
DECLARE_EXPORT_INFO;
Modified: trunk/Tools/ChangeLog (217059 => 217060)
--- trunk/Tools/ChangeLog 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Tools/ChangeLog 2017-05-18 19:38:10 UTC (rev 217060)
@@ -1,3 +1,15 @@
+2017-05-18 Saam Barati <[email protected]>
+
+ WebAssembly: perform stack checks
+ https://bugs.webkit.org/show_bug.cgi?id=165546
+ <rdar://problem/29760307>
+
+ Reviewed by Filip Pizlo.
+
+ Add some new testing modes for using and not using fast TLS wasm contexts.
+
+ * Scripts/run-jsc-stress-tests:
+
2017-05-18 Daniel Bates <[email protected]>
REGRESSION (r209608): Cross-origin plugin document opened in child window blocked by parent
Modified: trunk/Tools/Scripts/run-jsc-stress-tests (217059 => 217060)
--- trunk/Tools/Scripts/run-jsc-stress-tests 2017-05-18 19:29:32 UTC (rev 217059)
+++ trunk/Tools/Scripts/run-jsc-stress-tests 2017-05-18 19:38:10 UTC (rev 217060)
@@ -1199,9 +1199,10 @@
prepareExtraAbsoluteFiles(WASMTESTS_PATH, ["wasm.json"])
prepareExtraRelativeFiles(modules.map { |f| "../" + f }, $collection)
run("default-wasm", "-m", *FTL_OPTIONS)
- run("wasm-no-cjit", "-m", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
+ run("wasm-no-cjit-yes-tls-context", "-m", "--useFastTLSForWasmContext=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
run("wasm-eager-jettison", "-m", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
run("wasm-no-call-ic", "-m", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
+ run("wasm-no-tls-context", "-m", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
end
def runWebAssemblyEmscripten(mode)
@@ -1214,9 +1215,10 @@
wasm = $benchmark.to_s.sub! '.js', '.wasm'
prepareExtraRelativeFiles([Pathname('..') + wasm], $collection)
run("default-wasm", *FTL_OPTIONS)
- run("wasm-no-cjit", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
+ run("wasm-no-cjit-yes-tls-context", "--useFastTLSForWasmContext=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
run("wasm-eager-jettison", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
run("wasm-no-call-ic", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
+ run("wasm-no-tls-context", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
end
def runWebAssemblySpecTest(mode)
@@ -1236,9 +1238,10 @@
prepareExtraRelativeFiles(harness.map { |f| "../../spec-harness/" + f }, $collection)
runWithOutputHandler("default-wasm", noisyOutputHandler, "../spec-harness.js", *FTL_OPTIONS)
- runWithOutputHandler("wasm-no-cjit", noisyOutputHandler, "../spec-harness.js", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
+ runWithOutputHandler("wasm-no-cjit-yes-tls-context", noisyOutputHandler, "../spec-harness.js", "--useFastTLSForWasmContext=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
runWithOutputHandler("wasm-eager-jettison", noisyOutputHandler, "../spec-harness.js", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
runWithOutputHandler("wasm-no-call-ic", noisyOutputHandler, "../spec-harness.js", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
+ runWithOutputHandler("wasm-no-tls-context", noisyOutputHandler, "../spec-harness.js", "--useFastTLSForWasmContext=false", *FTL_OPTIONS)
end
def runChakra(mode, exception, baselineFile, extraFiles)