JSTests/ChangeLog

 12017-05-17 Saam Barati <sbarati@apple.com>
 2
 3 WebAssembly: perform stack checks
 4 https://bugs.webkit.org/show_bug.cgi?id=165546
 5 <rdar://problem/29760307>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * wasm.yaml:
 10 * wasm/function-tests/factorial.js:
 11 * wasm/function-tests/float-sub.js:
 12 * wasm/function-tests/stack-overflow.js: Added.
 13 (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.makeInstance):
 14 (import.Builder.from.string_appeared_here.import.as.assert.from.string_appeared_here.assertOverflows):
 15 (assertOverflows.makeInstance):
 16 (assertOverflows.makeInstance2):
 17 (assertOverflows.assertThrows):
 18 (assertOverflows):
 19
1202017-05-17 Filip Pizlo <fpizlo@apple.com>
221
322 Unreviewed, address mlam's review feedback.
217019

JSTests/wasm.yaml

5656 cmd: runWebAssemblySpecTest :normal
5757
5858- path: wasm/spec-tests/call.wast.js
59  cmd: runWebAssemblySpecTest :skip
 59 cmd: runWebAssemblySpecTest :normal
6060
6161- path: wasm/spec-tests/call_indirect.wast.js
62  cmd: runWebAssemblySpecTest :skip
 62 cmd: runWebAssemblySpecTest :normal
6363
6464- path: wasm/spec-tests/comments.wast.js
6565 cmd: runWebAssemblySpecTest :normal

8686 cmd: runWebAssemblySpecTest :normal
8787
8888- path: wasm/spec-tests/fac.wast.js
89  cmd: runWebAssemblySpecTest :skip
 89 cmd: runWebAssemblySpecTest :normal
9090
9191- path: wasm/spec-tests/float_literals.wast.js
9292 cmd: runWebAssemblySpecTest :normal

167167 cmd: runWebAssemblySpecTest :normal
168168
169169- path: wasm/spec-tests/skip-stack-guard-page.wast.js
170  cmd: runWebAssemblySpecTest :skip
 170 cmd: runWebAssemblySpecTest :normal
171171
172172- path: wasm/spec-tests/stack.wast.js
173173 cmd: runWebAssemblySpecTest :normal
217019

JSTests/wasm/function-tests/factorial.js

 1import * as assert from '../assert.js'
12import Builder from '../Builder.js'
23
34const b = new Builder();
45b.Type().End()
 6 .Import().End()
57 .Function().End()
 8 .Export()
 9 .Function("fac")
 10 .End()
611 .Code()
7  .Function({ params: ["i32"], ret: "i32" }, [])
8  .GetLocal(0)
9  .I32Const(0)
10  .I32Eq()
11  .If("void", b =>
12  b.I32Const(1)
13  .Return()
14  )
15  .GetLocal(0)
16  .GetLocal(0)
17  .I32Const(1)
18  .I32Sub()
19  .Call(0)
20  .I32Mul()
21  .Return()
 12 .Function("fac", { params: ["i32"], ret: "i32" })
 13 .GetLocal(0)
 14 .I32Const(0)
 15 .I32Eq()
 16 .If("void", b =>
 17 b.I32Const(1)
 18 .Return()
 19 )
 20 .GetLocal(0)
 21 .GetLocal(0)
 22 .I32Const(1)
 23 .I32Sub()
 24 .Call(0)
 25 .I32Mul()
 26 .Return()
 27 .End()
2228 .End()
2329
24 const bin = b.WebAssembly()
25 bin.trim();
26 testWasmModuleFunctions(bin.get(), 1, [[{type: "i32", value: 1 }, [{ type: "i32", value: 0 }]],
27  [{type: "i32", value: 1 }, [{ type: "i32", value: 1 }]],
28  [{type: "i32", value: 2 }, [{ type: "i32", value: 2 }]],
29  [{type: "i32", value: 24 }, [{ type: "i32", value: 4 }]],
30  ]);
 30const m = new WebAssembly.Module(b.WebAssembly().get());
 31const fac = (new WebAssembly.Instance(m)).exports.fac;
 32assert.eq(fac(0), 1);
 33assert.eq(fac(1), 1);
 34assert.eq(fac(2), 2);
 35assert.eq(fac(4), 24);
 36assert.throws(() => fac(10000000000000), RangeError, "Maximum call stack size exceeded.");
217019

JSTests/wasm/function-tests/float-sub.js

 1import * as assert from '../assert.js'
12import Builder from '../Builder.js'
23
34const b = new Builder();
45b.Type().End()
56 .Function().End()
 7 .Export()
 8 .Function("foo")
 9 .Function("bar")
 10 .End()
611 .Code()
7  .Function({ params: ["f32", "f32"], ret: "f32" }, [])
 12 .Function("bar", { params: ["f32", "f32"], ret: "f32" }, [])
813 .GetLocal(0)
914 .GetLocal(1)
1015 .F32Sub()
1116 .Return()
1217 .End()
1318
14  .Function({ params: ["f32", "f32"], ret: "f32" }, [])
 19 .Function("foo", { params: ["f32", "f32"], ret: "f32" }, [])
1520 .GetLocal(0)
1621 .GetLocal(1)
1722 .Call(0)
1823 .Return()
1924 .End()
 25 .End()
2026
2127const bin = b.WebAssembly()
2228bin.trim();
23 testWasmModuleFunctions(bin.get(), 2,
24  [[{type: "f32", value: -1.5 }, [{ type: "f32", value: 0 }, { type: "f32", value: 1.5 }]],
25  [{type: "f32", value: 87.6234 }, [{ type: "f32", value: 100.1234 }, { type: "f32", value: 12.5 }]]
26  ],
27  [[{type: "f32", value: -1.5 }, [{ type: "f32", value: 0 }, { type: "f32", value: 1.5 }]],
28  [{type: "f32", value: 87.6234 }, [{ type: "f32", value: 100.1234 }, { type: "f32", value: 12.5 }]]
29  ]
30  );
 29const instance = new WebAssembly.Instance(new WebAssembly.Module(bin.get()));
 30
 31let x = new Float32Array(3);
 32x[0] = 0;
 33x[1] = 1.5;
 34x[2] = x[0] - x[1];
 35assert.eq(instance.exports.bar(x[0], x[1]), x[2]);
 36assert.eq(instance.exports.foo(x[0], x[1]), x[2]);
 37
 38x[0] = 100.1234
 39x[1] = 12.5;
 40x[2] = x[0] - x[1];
 41assert.eq(instance.exports.bar(x[0], x[1]), x[2]);
 42assert.eq(instance.exports.foo(x[0], x[1]), x[2]);
217019

JSTests/wasm/function-tests/stack-overflow.js

 1import Builder from '../Builder.js'
 2import * as assert from '../assert.js'
 3
 4{
 5 function makeInstance() {
 6 const tableDescription = {initial: 1, element: "anyfunc"};
 7 const builder = new Builder()
 8 .Type()
 9 .Func(["i32"], "void")
 10 .End()
 11 .Import()
 12 .Table("imp", "table", tableDescription)
 13 .End()
 14 .Function().End()
 15 .Export()
 16 .Function("foo")
 17 .End()
 18 .Code()
 19 .Function("foo", 0 /*['i32'] => 'void'*/)
 20 .GetLocal(0) // parameter to call
 21 .GetLocal(0) // call index
 22 .CallIndirect(0, 0) // calling function of type ['i32'] => 'i32'
 23 .Return()
 24 .End()
 25 .End();
 26
 27
 28 const bin = builder.WebAssembly().get();
 29 const module = new WebAssembly.Module(bin);
 30 const table = new WebAssembly.Table(tableDescription);
 31 return {instance: new WebAssembly.Instance(module, {imp: {table}}), table};
 32 }
 33
 34 const {instance: i1, table: t1} = makeInstance();
 35 const {instance: i2, table: t2} = makeInstance();
 36 t2.set(0, i1.exports.foo);
 37 t1.set(0, i2.exports.foo);
 38
 39 // FIXME: asssert some stack size
 40 function assertOverflows(instance) {
 41 let stack;
 42 try {
 43 instance.exports.foo(0)
 44 } catch(e) {
 45 stack = e.stack;
 46 }
 47 stack = stack.split("\n");
 48 assert.truthy(stack.length > 50);
 49 for (let i = 0; i < 50; ++i) {
 50 let item = stack[stack.length - i - 1];
 51 assert.eq(item, "wasm function: 0@[wasm code]");
 52 }
 53 }
 54 assertOverflows(i1);
 55 assertOverflows(i2);
 56
 57}
 58
 59{
 60 function makeInstance() {
 61 const tableDescription = {initial: 1, element: "anyfunc"};
 62 const builder = new Builder()
 63 .Type()
 64 .Func([], "void")
 65 .End()
 66 .Import()
 67 .Table("imp", "table", tableDescription)
 68 .End()
 69 .Function().End()
 70 .Export()
 71 .Function("foo")
 72 .End()
 73 .Code()
 74 .Function("foo", {params:["i32"], ret:"void"})
 75 .GetLocal(0) // parameter to call
 76 .GetLocal(0) // call index
 77 .CallIndirect(0, 0) // calling function of type [] => 'void'
 78 .Return()
 79 .End()
 80 .End();
 81
 82
 83 const bin = builder.WebAssembly().get();
 84 const module = new WebAssembly.Module(bin);
 85 const table = new WebAssembly.Table(tableDescription);
 86 return {instance: new WebAssembly.Instance(module, {imp: {table}}), table};
 87 }
 88
 89 function makeInstance2(f) {
 90 const builder = new Builder()
 91 .Type()
 92 .End()
 93 .Import()
 94 .Function("imp", "f", {params:['i32'], ret:"void"})
 95 .End()
 96 .Function().End()
 97 .Export()
 98 .Function("foo")
 99 .End()
 100 .Code()
 101 .Function("foo", {params: [], ret: "void" })
 102 .I32Const(0)
 103 .Call(0)
 104 .Return()
 105 .End()
 106 .End();
 107
 108
 109 const bin = builder.WebAssembly().get();
 110 const module = new WebAssembly.Module(bin);
 111 return new WebAssembly.Instance(module, {imp: {f}});
 112 }
 113
 114 const {instance: i1, table: t1} = makeInstance();
 115 const foo = i1.exports.foo;
 116 const i2 = makeInstance2(i1.exports.foo);
 117 t1.set(0, i2.exports.foo);
 118
 119 function assertThrows(instance) {
 120 let stack;
 121 try {
 122 instance.exports.foo();
 123 } catch(e) {
 124 stack = e.stack;
 125 }
 126 assert.truthy(stack);
 127
 128 stack = stack.split("\n");
 129 assert.truthy(stack.length > 50);
 130 const oneString = "wasm function: 1@[wasm code]";
 131 const zeroString = "wasm function: 0@[wasm code]";
 132 let currentIndex = stack[stack.length - 1] === oneString ? 1 : 0;
 133 for (let i = 0; i < 50; ++i) {
 134 let item = stack[stack.length - 1 - i];
 135 if (currentIndex === 1) {
 136 assert.eq(item, oneString);
 137 currentIndex = 0;
 138 } else {
 139 assert.eq(currentIndex, 0);
 140 assert.eq(item, zeroString);
 141 currentIndex = 1;
 142 }
 143 }
 144 }
 145
 146 for (let i = 0; i < 20; ++i) {
 147 assertThrows(i2);
 148 assertThrows(i1);
 149 }
 150
 151 for (let i = 0; i < 20; ++i) {
 152 assertThrows(i1);
 153 assertThrows(i2);
 154 }
 155}
 156
 157{
 158 function test(numArgs) {
 159 function makeSignature() {
 160 let args = [];
 161 for (let i = 0; i < numArgs; ++i) {
 162 args.push("i32");
 163 }
 164 return {params: args};
 165 }
 166 function makeInstance(f) {
 167 let builder = new Builder()
 168 .Type()
 169 .Func([], "void")
 170 .End()
 171 .Import()
 172 .Function("imp", "f", makeSignature())
 173 .End()
 174 .Function().End()
 175 .Export()
 176 .Function("foo")
 177 .End()
 178 .Code()
 179 .Function("foo", {params:[], ret:"void"});
 180 for (let i = 0; i < numArgs; ++i) {
 181 builder = builder.I32Const(i);
 182 }
 183
 184 builder = builder.Call(0).Return().End().End();
 185 const bin = builder.WebAssembly().get();
 186 const module = new WebAssembly.Module(bin);
 187 return new WebAssembly.Instance(module, {imp: {f}});
 188 }
 189
 190 function f(...args) {
 191 assert.eq(args.length, numArgs);
 192 for (let i = 0; i < args.length; ++i)
 193 assert.eq(args[i], i);
 194
 195 instance.exports.foo();
 196 }
 197 let instance = makeInstance(f);
 198
 199 let stack;
 200 try {
 201 instance.exports.foo();
 202 } catch(e) {
 203 stack = e.stack;
 204 }
 205 assert.truthy(stack.split("\n").length > 25);
 206 }
 207
 208 test(20);
 209 test(20);
 210 test(1000);
 211 test(2);
 212 test(1);
 213 test(0);
 214 test(700);
 215 test(433);
 216 test(42);
 217}
nonexistent

Source/JavaScriptCore/ChangeLog

 12017-05-17 Saam Barati <sbarati@apple.com>
 2
 3 WebAssembly: perform stack checks
 4 https://bugs.webkit.org/show_bug.cgi?id=165546
 5 <rdar://problem/29760307>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 This patch adds stack checks to wasm. It implements it by storing the stack
 10 bounds on the Context or in TLS. By default, we use TLS. However, there is a
 11 runtime option to configure this.
 12
 13 Stack checking works as normal, except we do a small optimization for terminal
 14 nodes in the call tree (nodes that don't make any calls). These nodes will
 15 only do a stack check if their frame size is beyond 1024 bytes. Otherwise,
 16 it's assumed the parent that called them did their stack check for them.
 17 This is because all things that make calls make sure to do an extra 1024
 18 bytes whenever doing a stack check.
 19
 20 * runtime/Error.cpp:
 21 (JSC::createRangeError):
 22 (JSC::addErrorInfoAndGetBytecodeOffset):
 23 * runtime/Error.h:
 24 * runtime/ExceptionHelpers.cpp:
 25 (JSC::createStackOverflowError):
 26 * runtime/ExceptionHelpers.h:
 27 * runtime/Options.h:
 28 * wasm/WasmB3IRGenerator.cpp:
 29 (JSC::Wasm::B3IRGenerator::B3IRGenerator):
 30 * wasm/WasmBinding.cpp:
 31 (JSC::Wasm::wasmToWasm):
 32 * wasm/WasmContext.cpp:
 33 (JSC::Wasm::loadContext):
 34 (JSC::Wasm::storeContext):
 35 * wasm/WasmContext.h:
 36 (JSC::Wasm::useFastTLSForContext):
 37 (JSC::Wasm::useFastTLSForStackCheck):
 38 * wasm/WasmExceptionType.h:
 39 * wasm/WasmMemoryInformation.h:
 40 (JSC::Wasm::PinnedRegisterInfo::toSave):
 41 * wasm/WasmThunks.cpp:
 42 (JSC::Wasm::throwExceptionFromWasmThunkGenerator):
 43 (JSC::Wasm::throwStackOverflowFromWasmThunkGenerator):
 44 (JSC::Wasm::Thunks::stub):
 45 * wasm/WasmThunks.h:
 46 * wasm/js/JSWebAssemblyInstance.h:
 47 (JSC::JSWebAssemblyInstance::offsetOfCachedStackLimit):
 48 (JSC::JSWebAssemblyInstance::cachedStackLimit):
 49 (JSC::JSWebAssemblyInstance::setCachedStackLimit):
 50 * wasm/js/WebAssemblyFunction.cpp:
 51 (JSC::callWebAssemblyFunction):
 52
1532017-05-17 Saam Barati <sbarati@apple.com>
254
355 We don't do context switches for Wasm->Wasm call indirect
217019

Source/JavaScriptCore/runtime/Error.cpp

@@JSObject* createEvalError(ExecState* exe
6060
6161JSObject* createRangeError(ExecState* exec, const String& message, ErrorInstance::SourceAppender appender)
6262{
63  ASSERT(!message.isEmpty());
6463 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
 64 return createRangeError(exec, globalObject, message, appender);
 65}
 66
 67JSObject* createRangeError(ExecState* exec, JSGlobalObject* globalObject, const String& message, ErrorInstance::SourceAppender appender)
 68{
 69 ASSERT(!message.isEmpty());
6570 return ErrorInstance::create(exec, globalObject->vm(), globalObject->rangeErrorConstructor()->errorStructure(), message, appender, TypeNothing, true);
6671}
6772

@@bool addErrorInfoAndGetBytecodeOffset(Ex
182187 callFrame = functor.foundCallFrame();
183188 unsigned stackIndex = functor.index();
184189 *bytecodeOffset = 0;
185  if (stackTrace.at(stackIndex).hasBytecodeOffset())
 190 if (stackIndex < stackTrace.size() && stackTrace.at(stackIndex).hasBytecodeOffset())
186191 *bytecodeOffset = stackTrace.at(stackIndex).bytecodeOffset();
187192 }
188193

@@JSObject* createRangeError(ExecState* ex
268273 return createRangeError(exec, message, nullptr);
269274}
270275
 276JSObject* createRangeError(ExecState* exec, JSGlobalObject* globalObject, const String& message)
 277{
 278 return createRangeError(exec, globalObject, message, nullptr);
 279}
 280
271281JSObject* createReferenceError(ExecState* exec, const String& message)
272282{
273283 return createReferenceError(exec, message, nullptr);
217019

Source/JavaScriptCore/runtime/Error.h

@@enum class ErrorType : uint8_t {
5252JSObject* createError(ExecState*, const String&, ErrorInstance::SourceAppender);
5353JSObject* createEvalError(ExecState*, const String&, ErrorInstance::SourceAppender);
5454JSObject* createRangeError(ExecState*, const String&, ErrorInstance::SourceAppender);
 55JSObject* createRangeError(ExecState*, JSGlobalObject*, const String&, ErrorInstance::SourceAppender);
5556JSObject* createReferenceError(ExecState*, const String&, ErrorInstance::SourceAppender);
5657JSObject* createSyntaxError(ExecState*, const String&, ErrorInstance::SourceAppender);
5758JSObject* createTypeError(ExecState*, const String&, ErrorInstance::SourceAppender, RuntimeType);

@@JSObject* createURIError(ExecState*, con
6263JS_EXPORT_PRIVATE JSObject* createError(ExecState*, const String&);
6364JS_EXPORT_PRIVATE JSObject* createEvalError(ExecState*, const String&);
6465JS_EXPORT_PRIVATE JSObject* createRangeError(ExecState*, const String&);
 66JS_EXPORT_PRIVATE JSObject* createRangeError(ExecState*, JSGlobalObject*, const String&);
6567JS_EXPORT_PRIVATE JSObject* createReferenceError(ExecState*, const String&);
6668JS_EXPORT_PRIVATE JSObject* createSyntaxError(ExecState*, const String&);
6769JS_EXPORT_PRIVATE JSObject* createTypeError(ExecState*);
217019

Source/JavaScriptCore/runtime/ExceptionHelpers.cpp

@@bool isTerminatedExecutionException(VM&
6969
7070JSObject* createStackOverflowError(ExecState* exec)
7171{
72  return createRangeError(exec, ASCIILiteral("Maximum call stack size exceeded."));
 72 return createStackOverflowError(exec, exec->lexicalGlobalObject());
 73}
 74
 75JSObject* createStackOverflowError(ExecState* exec, JSGlobalObject* globalObject)
 76{
 77 return createRangeError(exec, globalObject, ASCIILiteral("Maximum call stack size exceeded."));
7378}
7479
7580JSObject* createUndefinedVariableError(ExecState* exec, const Identifier& ident)
217019

Source/JavaScriptCore/runtime/ExceptionHelpers.h

@@JSObject* createTerminatedExecutionExcep
4343JS_EXPORT_PRIVATE bool isTerminatedExecutionException(VM&, Exception*);
4444JS_EXPORT_PRIVATE JSObject* createError(ExecState*, JSValue, const String&, ErrorInstance::SourceAppender);
4545JS_EXPORT_PRIVATE JSObject* createStackOverflowError(ExecState*);
 46JSObject* createStackOverflowError(ExecState*, JSGlobalObject*);
4647JSObject* createUndefinedVariableError(ExecState*, const Identifier&);
4748JSObject* createTDZError(ExecState*);
4849JSObject* createNotAnObjectError(ExecState*, JSValue);
217019

Source/JavaScriptCore/runtime/Options.h

@@typedef const char* optionString;
455455 v(bool, crashIfWebAssemblyCantFastMemory, false, Normal, "If true, we will crash if we can't obtain fast memory for wasm.") \
456456 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.") \
457457 v(bool, useWebAssemblyFastTLS, true, Normal, "If true, we will try to use fast thread-local storage if available on the current platform.") \
 458 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.") \
 459 v(bool, useFastTLSForWasmStackChecks, true, Normal, "If true (and fast TLS is enabled), we will store stack bounds in fast TLS. If false, we will store it on the context.") \
458460 v(bool, useCallICsForWebAssemblyToJSCalls, true, Normal, "If true, we will use CallLinkInfo to inline cache Wasm to JS calls.")
459461
460462
217019

Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp

@@private:
267267 GPRReg m_memorySizeGPR { InvalidGPRReg };
268268 GPRReg m_wasmContextGPR;
269269 Value* m_instanceValue; // FIXME: make this lazy https://bugs.webkit.org/show_bug.cgi?id=169792
 270 bool m_makesCalls { false };
 271 uint32_t m_maxNumJSCallArguments { 0 };
270272};
271273
272274// Memory accesses in WebAssembly have unsigned 32-bit offsets, whereas they have signed 32-bit offsets in B3.

@@B3IRGenerator::B3IRGenerator(const Modul
381383
382384 wasmCallingConvention().setupFrameInPrologue(&compilation->wasmCalleeMoveLocation, m_proc, Origin(), m_currentBlock);
383385
384  m_currentBlock = emitTierUpCheck(m_currentBlock, TierUpCount::functionEntryDecrement(), Origin());
385 
386386 m_instanceValue = materializeWasmContext(m_currentBlock);
 387
 388 {
 389 B3::Value* framePointer = m_currentBlock->appendNew<B3::Value>(m_proc, B3::FramePointer, Origin());
 390 B3::PatchpointValue* stackOverflowCheck = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, Origin());
 391 stackOverflowCheck->appendSomeRegister(framePointer);
 392 if (!useFastTLSForStackCheck())
 393 stackOverflowCheck->appendSomeRegister(m_instanceValue);
 394 stackOverflowCheck->clobber(RegisterSet::macroScratchRegisters());
 395 stackOverflowCheck->numGPScratchRegisters = 2;
 396 stackOverflowCheck->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
 397 AllowMacroScratchRegisterUsage allowScratch(jit);
 398 GPRReg fp = params[0].gpr();
 399 GPRReg context = !useFastTLSForStackCheck() ? params[1].gpr() : InvalidGPRReg;
 400 GPRReg scratch1 = params.gpScratch(0);
 401 GPRReg scratch2 = params.gpScratch(1);
 402
 403 const unsigned wasmFrameSize = params.proc().frameSize();
 404 const unsigned minimumParentCheckSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), 1024);
 405 const unsigned extraFrameSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), std::max<uint32_t>(
 406 // This allows us to elide stack checks for functions that are terminal nodes in the call
 407 // tree, (e.g they don't make any calls) and have a small enough frame size. This works by
 408 // having any such terminal node have its parent caller include some extra size in its
 409 // own check for it. The goal here is twofold:
 410 // 1. Emit less code.
 411 // 2. Try to speed things up by skipping stack checks.
 412 minimumParentCheckSize,
 413 // This allows us to elide stack checks in the Wasm -> JS call IC stub. Since these will
 414 // spill all arguments to the stack, we ensure that a stack check here covers the
 415 // stack that such a stub would use.
 416 (Checked<uint32_t>(m_maxNumJSCallArguments) * sizeof(Register) + jscCallingConvention().headerSizeInBytes()).unsafeGet()
 417 ));
 418 const int32_t checkSize = m_makesCalls ? wasmFrameSize + extraFrameSize : wasmFrameSize;
 419 RELEASE_ASSERT(checkSize >= 0);
 420 // This allows leaf functions to not do stack checks if their frame size is within
 421 // certain limits since their caller would have already done the check.
 422 if (m_makesCalls || wasmFrameSize >= minimumParentCheckSize) {
 423#if ENABLE(FAST_TLS_JIT)
 424 if (useFastTLSForStackCheck())
 425 jit.loadFromTLSPtr(fastTLSOffsetForKey(WTF_WASM_STACKCHECK_KEY), scratch2);
 426 else
 427 jit.loadPtr(CCallHelpers::Address(context, Context::offsetOfCachedStackLimit()), scratch2);
 428#else
 429 jit.loadPtr(CCallHelpers::Address(context, Context::offsetOfCachedStackLimit()), scratch2);
 430#endif // ENABLE(FAST_TLS_JIT)
 431
 432 jit.addPtr(CCallHelpers::TrustedImm32(-checkSize), fp, scratch1);
 433 auto overflow = jit.branchPtr(CCallHelpers::Below, scratch1, scratch2);
 434 jit.addLinkTask([overflow] (LinkBuffer& linkBuffer) {
 435 linkBuffer.link(overflow, CodeLocationLabel(Thunks::singleton().stub(throwStackOverflowFromWasmThunkGenerator).code()));
 436 });
 437 }
 438 });
 439 }
 440
 441 m_currentBlock = emitTierUpCheck(m_currentBlock, TierUpCount::functionEntryDecrement(), Origin());
387442}
388443
389444void B3IRGenerator::restoreWebAssemblyGlobalState(const MemoryInformation& memory, Value* instance, Procedure& proc, BasicBlock* block)

@@auto B3IRGenerator::addCall(uint32_t fun
9981053{
9991054 ASSERT(signature.argumentCount() == args.size());
10001055
 1056 m_makesCalls = true;
 1057
10011058 Type returnType = signature.returnType();
10021059 Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls = &m_unlinkedWasmToWasmCalls;
10031060
10041061 if (m_info.isImportedFunctionFromFunctionIndexSpace(functionIndex)) {
 1062 m_maxNumJSCallArguments = std::max(m_maxNumJSCallArguments, static_cast<uint32_t>(args.size()));
 1063
10051064 // 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
10061065 Value* functionImport = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), origin(), m_instanceValue, safeCast<int32_t>(JSWebAssemblyInstance::offsetOfImportFunction(functionIndex)));
10071066 Value* jsTypeOfImport = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, origin(), functionImport, safeCast<int32_t>(JSCell::typeInfoTypeOffset()));

@@auto B3IRGenerator::addCall(uint32_t fun
10171076 patchpoint->effects.writesPinned = true;
10181077 patchpoint->effects.readsPinned = true;
10191078 // We need to clobber all potential pinned registers since we might be leaving the instance.
1020  patchpoint->clobberLate(PinnedRegisterInfo::get().toSave());
 1079 // We pessimistically assume we could be calling to something that is bounds checking.
 1080 // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
 1081 patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
10211082 patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
10221083 AllowMacroScratchRegisterUsage allowScratch(jit);
10231084 CCallHelpers::Call call = jit.threadSafePatchableNearCall();

@@auto B3IRGenerator::addCall(uint32_t fun
10431104 patchpoint->effects.readsPinned = true;
10441105 patchpoint->append(jumpDestination, ValueRep::SomeRegister);
10451106 // We need to clobber all potential pinned registers since we might be leaving the instance.
1046  patchpoint->clobberLate(PinnedRegisterInfo::get().toSave());
 1107 // We pessimistically assume we could be calling to something that is bounds checking.
 1108 // FIXME: We shouldn't have to do this: https://bugs.webkit.org/show_bug.cgi?id=172181
 1109 patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
10471110 patchpoint->setGenerator([returnType] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
10481111 AllowMacroScratchRegisterUsage allowScratch(jit);
10491112 jit.call(params[returnType == Void ? 0 : 1].gpr());

@@auto B3IRGenerator::addCall(uint32_t fun
10851148
10861149auto B3IRGenerator::addCallIndirect(const Signature& signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult
10871150{
 1151 m_makesCalls = true;
 1152
10881153 ExpressionType calleeIndex = args.takeLast();
10891154 ASSERT(signature.argumentCount() == args.size());
10901155

@@auto B3IRGenerator::addCallIndirect(cons
11681233 patchpoint->clobber(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
11691234 patchpoint->clobber(RegisterSet::macroScratchRegisters());
11701235 patchpoint->append(newContext, ValueRep::SomeRegister);
 1236 patchpoint->append(m_instanceValue, ValueRep::SomeRegister);
11711237 patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
11721238 AllowMacroScratchRegisterUsage allowScratch(jit);
11731239 GPRReg newContext = params[0].gpr();
 1240 GPRReg oldContext = params[1].gpr();
11741241 const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
11751242 const auto& sizeRegs = pinnedRegs.sizeRegisters;
11761243 GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
11771244 ASSERT(newContext != baseMemory);
11781245
 1246 if (!useFastTLSForStackCheck()) {
 1247 jit.loadPtr(CCallHelpers::Address(oldContext, Context::offsetOfCachedStackLimit()), baseMemory);
 1248 jit.storePtr(baseMemory, CCallHelpers::Address(newContext, Context::offsetOfCachedStackLimit()));
 1249 }
 1250
11791251 jit.storeWasmContext(newContext);
11801252 jit.loadPtr(CCallHelpers::Address(newContext, Context::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*.
11811253 ASSERT(sizeRegs.size() == 1);

@@auto B3IRGenerator::addCallIndirect(cons
12001272 patchpoint->effects.writesPinned = true;
12011273 patchpoint->effects.readsPinned = true;
12021274 // We need to clobber all potential pinned registers since we might be leaving the instance.
1203  patchpoint->clobberLate(PinnedRegisterInfo::get().toSave());
 1275 // We pessimistically assume we're always calling something that is bounds checking so
 1276 // because the wasm->wasm thunk unconditionally overrides the size registers.
 1277 // FIXME: We should not have to do this, but the wasm->wasm stub assumes it can
 1278 // use all the pinned registers as scratch: https://bugs.webkit.org/show_bug.cgi?id=172181
 1279 patchpoint->clobberLate(PinnedRegisterInfo::get().toSave(MemoryMode::BoundsChecking));
12041280
12051281 patchpoint->append(calleeCode, ValueRep::SomeRegister);
12061282 patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
217019

Source/JavaScriptCore/wasm/WasmBinding.cpp

3636#include "LinkBuffer.h"
3737#include "NativeErrorConstructor.h"
3838#include "WasmCallingConvention.h"
 39#include "WasmContext.h"
3940#include "WasmExceptionType.h"
4041
4142namespace JSC { namespace Wasm {

@@MacroAssemblerCodeRef wasmToWasm(unsigne
608609 JIT jit;
609610
610611 GPRReg scratch = GPRInfo::nonPreservedNonArgumentGPR;
 612 GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
 613 ASSERT(baseMemory != scratch);
 614 const auto& sizeRegs = pinnedRegs.sizeRegisters;
 615 ASSERT(sizeRegs.size() >= 1);
 616 ASSERT(sizeRegs[0].sizeRegister != baseMemory);
 617 ASSERT(sizeRegs[0].sizeRegister != scratch);
 618 GPRReg sizeRegAsScratch = sizeRegs[0].sizeRegister;
611619
 620 static_assert(std::is_same<Context, JSWebAssemblyInstance>::value, "This is assumed in the code below.");
612621 // B3's call codegen ensures that the JSCell is a WebAssemblyFunction.
613  materializeImportJSCell(jit, importIndex, scratch);
 622 jit.loadWasmContext(sizeRegAsScratch); // Old Instance*
 623 jit.loadPtr(JIT::Address(sizeRegAsScratch, JSWebAssemblyInstance::offsetOfImportFunction(importIndex)), scratch);
614624
615625 // Get the callee's WebAssembly.Instance and set it as WasmContext. The caller will take care of restoring its own Instance.
616  GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
617  ASSERT(baseMemory != scratch);
618626 jit.loadPtr(JIT::Address(scratch, WebAssemblyFunction::offsetOfInstance()), baseMemory); // Instance*.
619627 jit.storeWasmContext(baseMemory);
620628
 629 if (!useFastTLSForStackCheck()) {
 630 jit.loadPtr(JIT::Address(sizeRegAsScratch, JSWebAssemblyInstance::offsetOfCachedStackLimit()), sizeRegAsScratch);
 631 jit.storePtr(sizeRegAsScratch, JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfCachedStackLimit()));
 632 }
 633
621634 // FIXME the following code assumes that all WebAssembly.Instance have the same pinned registers. https://bugs.webkit.org/show_bug.cgi?id=162952
622635 // Set up the callee's baseMemory register as well as the memory size registers.
623636 jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*.
624  const auto& sizeRegs = pinnedRegs.sizeRegisters;
625  ASSERT(sizeRegs.size() >= 1);
626  ASSERT(sizeRegs[0].sizeRegister != baseMemory);
627  ASSERT(sizeRegs[0].sizeRegister != scratch);
628637 ASSERT(!sizeRegs[0].sizeOffset); // The following code assumes we start at 0, and calculates subsequent size registers relative to 0.
629638 jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfSize()), sizeRegs[0].sizeRegister); // Memory size.
630639 jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfMemory()), baseMemory); // WasmMemory::void*.
217019

Source/JavaScriptCore/wasm/WasmContext.cpp

@@Context* loadContext(VM& vm)
4040 if (useFastTLSForContext())
4141 return bitwise_cast<Context*>(_pthread_getspecific_direct(WTF_WASM_CONTEXT_KEY));
4242#endif
43  // FIXME: Save this state elsewhere to allow PIC. https://bugs.webkit.org/show_bug.cgi?id=169773
4443 return vm.wasmContext;
4544}
4645
4746void storeContext(VM& vm, Context* context)
4847{
 48 void* stackLimit = vm.softStackLimit();
4949#if ENABLE(FAST_TLS_JIT)
5050 if (useFastTLSForContext())
5151 _pthread_setspecific_direct(WTF_WASM_CONTEXT_KEY, bitwise_cast<void*>(context));
 52 if (useFastTLSForStackCheck())
 53 _pthread_setspecific_direct(WTF_WASM_STACKCHECK_KEY, stackLimit);
5254#endif
53  // FIXME: Save this state elsewhere to allow PIC. https://bugs.webkit.org/show_bug.cgi?id=169773
5455 vm.wasmContext = context;
 56 if (context)
 57 context->setCachedStackLimit(stackLimit);
5558}
5659
5760} } // namespace JSC::Wasm
217019

Source/JavaScriptCore/wasm/WasmContext.h

@@inline bool useFastTLS()
4949#endif
5050}
5151
 52
5253inline bool useFastTLSForContext()
5354{
54  return useFastTLS();
 55 if (useFastTLS())
 56 return Options::useFastTLSForWasmContext();
 57 return false;
 58}
 59
 60inline bool useFastTLSForStackCheck()
 61{
 62 if (useFastTLS())
 63 return Options::useFastTLSForWasmStackChecks();
 64 return false;
5565}
5666
5767Context* loadContext(VM&);
217019

Source/JavaScriptCore/wasm/WasmExceptionType.h

@@namespace Wasm {
3939 macro(OutOfBoundsTrunc, "Out of bounds Trunc operation") \
4040 macro(Unreachable, "Unreachable code should not be executed") \
4141 macro(DivisionByZero, "Division by zero") \
42  macro(IntegerOverflow, "Integer overflow")
 42 macro(IntegerOverflow, "Integer overflow") \
 43 macro(StackOverflow, "Stack overflow")
4344
4445enum class ExceptionType : uint32_t {
4546#define MAKE_ENUM(enumName, error) enumName,
217019

Source/JavaScriptCore/wasm/WasmMemoryInformation.h

@@struct PinnedRegisterInfo {
4848 static const PinnedRegisterInfo& get();
4949 PinnedRegisterInfo(Vector<PinnedSizeRegisterInfo>&&, GPRReg, GPRReg);
5050
51  RegisterSet toSave(MemoryMode mode = MemoryMode::BoundsChecking) const
 51 RegisterSet toSave(MemoryMode mode) const
5252 {
5353 RegisterSet result;
5454 result.set(baseMemoryPointer);
217019

Source/JavaScriptCore/wasm/WasmThunks.cpp

4040
4141namespace JSC { namespace Wasm {
4242
43 MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator()
 43MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator(const AbstractLocker&)
4444{
4545 CCallHelpers jit;
4646

@@MacroAssemblerCodeRef throwExceptionFrom
6262 auto throwScope = DECLARE_THROW_SCOPE(*vm);
6363 JSGlobalObject* globalObject = wasmContext->globalObject();
6464
65  JSWebAssemblyRuntimeError* error = JSWebAssemblyRuntimeError::create(
66  exec, *vm, globalObject->WebAssemblyRuntimeErrorStructure(), Wasm::errorMessageForExceptionType(type));
 65 JSObject* error;
 66 if (type == ExceptionType::StackOverflow)
 67 error = createStackOverflowError(exec, globalObject);
 68 else
 69 error = JSWebAssemblyRuntimeError::create(exec, *vm, globalObject->WebAssemblyRuntimeErrorStructure(), Wasm::errorMessageForExceptionType(type));
6770 throwException(exec, throwScope, error);
6871 }
6972

@@MacroAssemblerCodeRef throwExceptionFrom
8689 return FINALIZE_CODE(linkBuffer, ("Throw exception from Wasm"));
8790}
8891
 92MacroAssemblerCodeRef throwStackOverflowFromWasmThunkGenerator(const AbstractLocker& locker)
 93{
 94 CCallHelpers jit;
 95
 96 jit.move(GPRInfo::callFrameRegister, MacroAssembler::stackPointerRegister);
 97 jit.move(CCallHelpers::TrustedImm32(static_cast<uint32_t>(ExceptionType::StackOverflow)), GPRInfo::argumentGPR1);
 98 auto jumpToExceptionHandler = jit.jump();
 99 LinkBuffer linkBuffer(jit, GLOBAL_THUNK_ID);
 100 linkBuffer.link(jumpToExceptionHandler, CodeLocationLabel(Thunks::singleton().stub(locker, throwExceptionFromWasmThunkGenerator).code()));
 101 return FINALIZE_CODE(linkBuffer, ("Throw stack overflow from Wasm"));
 102}
 103
89104static Thunks* thunks;
90105void Thunks::initialize()
91106{

@@Thunks& Thunks::singleton()
101116MacroAssemblerCodeRef Thunks::stub(ThunkGenerator generator)
102117{
103118 auto locker = holdLock(m_lock);
 119 return stub(locker, generator);
 120}
104121
 122MacroAssemblerCodeRef Thunks::stub(const AbstractLocker& locker, ThunkGenerator generator)
 123{
105124 ASSERT(!!generator);
106  auto addResult = m_stubs.add(generator, MacroAssemblerCodeRef());
107  if (addResult.isNewEntry)
108  addResult.iterator->value = generator();
109  return addResult.iterator->value;
 125 {
 126 auto addResult = m_stubs.add(generator, MacroAssemblerCodeRef());
 127 if (!addResult.isNewEntry)
 128 return addResult.iterator->value;
 129 }
 130
 131 MacroAssemblerCodeRef code = generator(locker);
 132 // We specifically don't use the iterator here to allow generator to recursively change m_stubs.
 133 m_stubs.set(generator, code);
 134 return code;
110135}
111136
112137MacroAssemblerCodeRef Thunks::existingStub(ThunkGenerator generator)
217019

Source/JavaScriptCore/wasm/WasmThunks.h

3131
3232namespace JSC { namespace Wasm {
3333
34 MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator();
 34MacroAssemblerCodeRef throwExceptionFromWasmThunkGenerator(const AbstractLocker&);
 35MacroAssemblerCodeRef throwStackOverflowFromWasmThunkGenerator(const AbstractLocker&);
3536
36 typedef MacroAssemblerCodeRef (*ThunkGenerator)();
 37typedef MacroAssemblerCodeRef (*ThunkGenerator)(const AbstractLocker&);
3738
3839class Thunks {
3940public:

@@public:
4142 static Thunks& singleton();
4243
4344 MacroAssemblerCodeRef stub(ThunkGenerator);
 45 MacroAssemblerCodeRef stub(const AbstractLocker&, ThunkGenerator);
4446 MacroAssemblerCodeRef existingStub(ThunkGenerator);
4547
4648private:
217019

Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h

@@public:
7373 static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_globals); }
7474 static ptrdiff_t offsetOfVM() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_vm); }
7575 static ptrdiff_t offsetOfCodeBlock() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_codeBlock); }
 76 static ptrdiff_t offsetOfCachedStackLimit() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_cachedStackLimit); }
7677 static size_t offsetOfImportFunctions() { return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSCell>)>(sizeof(JSWebAssemblyInstance)); }
7778 static size_t offsetOfImportFunction(size_t importFunctionNum) { return offsetOfImportFunctions() + importFunctionNum * sizeof(sizeof(WriteBarrier<JSCell>)); }
7879
7980 WebAssemblyToJSCallee* webAssemblyToJSCallee() { return m_callee.get(); }
8081
 82 void* cachedStackLimit() const { return m_cachedStackLimit; }
 83 void setCachedStackLimit(void* limit) { m_cachedStackLimit = limit; }
 84
8185protected:
8286 JSWebAssemblyInstance(VM&, Structure*, unsigned numImportFunctions);
8387 void finishCreation(VM&, JSWebAssemblyModule*, JSModuleNamespaceObject*);

@@private:
101105 WriteBarrier<JSWebAssemblyTable> m_table;
102106 WriteBarrier<WebAssemblyToJSCallee> m_callee;
103107 MallocPtr<uint64_t> m_globals;
 108 void* m_cachedStackLimit { bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()) };
104109 unsigned m_numImportFunctions;
105110};
106111
217019

Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp

@@static EncodedJSValue JSC_HOST_CALL call
128128
129129 // FIXME Do away with this entire function, and only use the entrypoint generated by B3. https://bugs.webkit.org/show_bug.cgi?id=166486
130130 Wasm::Context* prevWasmContext = Wasm::loadContext(vm);
 131 {
 132 // We do the stack check here for the wrapper function because we don't
 133 // want to emit a stack check inside every wrapper function.
 134 const intptr_t sp = bitwise_cast<intptr_t>(&sp); // A proxy for the current stack pointer.
 135 const intptr_t frameSize = (boxedArgs.size() + CallFrame::headerSizeInRegisters) * sizeof(Register);
 136 const intptr_t stackSpaceUsed = 2*frameSize; // We're making two calls. One to the wrapper, and one to the actual wasm code.
 137 if (UNLIKELY((sp - stackSpaceUsed) < bitwise_cast<intptr_t>(vm.softStackLimit())))
 138 return JSValue::encode(throwException(exec, scope, createStackOverflowError(exec)));
 139 }
131140 Wasm::storeContext(vm, wasmContext);
132141 ASSERT(wasmFunction->instance());
133142 ASSERT(wasmFunction->instance() == Wasm::loadContext(vm));

@@static EncodedJSValue JSC_HOST_CALL call
135144 // We need to make sure this is in a register or on the stack since it's stored in Vector<JSValue>.
136145 // This probably isn't strictly necessary, since the WebAssemblyFunction* should keep the instance
137146 // alive. But it's good hygiene.
138  wasmContext->use();
 147 wasmContext->use();
 148 wasmContext->setCachedStackLimit(bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()));
139149 Wasm::storeContext(vm, prevWasmContext);
140150 RETURN_IF_EXCEPTION(scope, { });
141151
217019

Source/WTF/ChangeLog

 12017-05-17 Saam Barati <sbarati@apple.com>
 2
 3 WebAssembly: perform stack checks
 4 https://bugs.webkit.org/show_bug.cgi?id=165546
 5 <rdar://problem/29760307>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 Add a new fast TLS key for Wasm bounds checking.
 10
 11 * wtf/FastTLS.h:
 12
1132017-05-15 Mark Lam <mark.lam@apple.com>
214
315 Rolling out r214038 and r213697: Crashes when using computed properties with rest destructuring and object spread.
217019

Source/WTF/wtf/FastTLS.h

@@namespace WTF {
4646
4747#define WTF_THREAD_DATA_KEY WTF_FAST_TLS_KEY0
4848#define WTF_WASM_CONTEXT_KEY WTF_FAST_TLS_KEY1
 49#define WTF_WASM_STACKCHECK_KEY WTF_FAST_TLS_KEY2
4950#define WTF_TESTING_KEY WTF_FAST_TLS_KEY3
5051
5152#if ENABLE(FAST_TLS_JIT)
217019

Tools/ChangeLog

 12017-05-17 Saam Barati <sbarati@apple.com>
 2
 3 WebAssembly: perform stack checks
 4 https://bugs.webkit.org/show_bug.cgi?id=165546
 5 <rdar://problem/29760307>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 Add some new testing modes for using and not using fast TLS both
 10 for wasm bounds checks and wasm contexts.
 11
 12 * Scripts/run-jsc-stress-tests:
 13
1142017-05-17 Ryan Haddad <ryanhaddad@apple.com>
215
316 Unreviewed, rolling out r217014.
217029

Tools/Scripts/run-jsc-stress-tests

@@def runWebAssembly
11991199 prepareExtraAbsoluteFiles(WASMTESTS_PATH, ["wasm.json"])
12001200 prepareExtraRelativeFiles(modules.map { |f| "../" + f }, $collection)
12011201 run("default-wasm", "-m", *FTL_OPTIONS)
1202  run("wasm-no-cjit", "-m", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
 1202 run("wasm-no-cjit", "-m", "--useFastTLSForWasmContext=true", "--useFastTLSForWasmStackChecks=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
12031203 run("wasm-eager-jettison", "-m", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
1204  run("wasm-no-call-ic", "-m", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
 1204 run("wasm-no-call-ic-yes-tls-context-no-tls-stackcheck", "-m", "--useCallICsForWebAssemblyToJSCalls=false", "--useFastTLSForWasmContext=true", "--useFastTLSForWasmStackChecks=false", *FTL_OPTIONS)
 1205 run("wasm-no-tls-context-yes-tls-stackchecks", "-m", "--useFastTLSForWasmContext=false", "--useFastTLSForWasmStackChecks=true", *FTL_OPTIONS)
 1206 run("wasm-no-tls-context-no-tls-stackchecks", "-m", "--useFastTLSForWasmContext=false", "--useFastTLSForWasmStackChecks=false", *FTL_OPTIONS)
12051207end
12061208
12071209def runWebAssemblyEmscripten(mode)

@@def runWebAssemblyEmscripten(mode)
12141216 wasm = $benchmark.to_s.sub! '.js', '.wasm'
12151217 prepareExtraRelativeFiles([Pathname('..') + wasm], $collection)
12161218 run("default-wasm", *FTL_OPTIONS)
1217  run("wasm-no-cjit", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
 1219 run("wasm-no-cjit", "--useFastTLSForWasmContext=true", "--useFastTLSForWasmStackChecks=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
12181220 run("wasm-eager-jettison", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
1219  run("wasm-no-call-ic", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
 1221 run("wasm-no-call-ic-yes-tls-context-no-tls-stackcheck", "--useCallICsForWebAssemblyToJSCalls=false", "--useFastTLSForWasmContext=true", "--useFastTLSForWasmStackChecks=false", *FTL_OPTIONS)
 1222 run("wasm-no-tls-context-yes-tls-stackchecks", "--useFastTLSForWasmContext=false", "--useFastTLSForWasmStackChecks=true", *FTL_OPTIONS)
 1223 run("wasm-no-tls-context-no-tls-stackchecks", "--useFastTLSForWasmContext=false", "--useFastTLSForWasmStackChecks=false", *FTL_OPTIONS)
12201224end
12211225
12221226def runWebAssemblySpecTest(mode)

@@def runWebAssemblySpecTest(mode)
12361240 prepareExtraRelativeFiles(harness.map { |f| "../../spec-harness/" + f }, $collection)
12371241
12381242 runWithOutputHandler("default-wasm", noisyOutputHandler, "../spec-harness.js", *FTL_OPTIONS)
1239  runWithOutputHandler("wasm-no-cjit", noisyOutputHandler, "../spec-harness.js", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
 1243 runWithOutputHandler("wasm-no-cjit", noisyOutputHandler, "../spec-harness.js", "--useFastTLSForWasmContext=true", "--useFastTLSForWasmStackChecks=true", *(FTL_OPTIONS + NO_CJIT_OPTIONS))
12401244 runWithOutputHandler("wasm-eager-jettison", noisyOutputHandler, "../spec-harness.js", "--forceCodeBlockToJettisonDueToOldAge=true", *FTL_OPTIONS)
1241  runWithOutputHandler("wasm-no-call-ic", noisyOutputHandler, "../spec-harness.js", "--useCallICsForWebAssemblyToJSCalls=false", *FTL_OPTIONS)
 1245 runWithOutputHandler("wasm-no-call-ic-no-tls-context-yes-tls-stackcheck", noisyOutputHandler, "../spec-harness.js", "--useCallICsForWebAssemblyToJSCalls=false", "--useFastTLSForWasmContext=false", "--useFastTLSForWasmStackChecks=true", *FTL_OPTIONS)
 1246 runWithOutputHandler("wasm-no-tls-context-no-tls-stackcheck", noisyOutputHandler, "../spec-harness.js", "--useCallICsForWebAssemblyToJSCalls=false", "--useFastTLSForWasmContext=false", "--useFastTLSForWasmStackChecks=false", *FTL_OPTIONS)
12421247end
12431248
12441249def runChakra(mode, exception, baselineFile, extraFiles)
217019