Diff
Modified: trunk/JSTests/ChangeLog (209651 => 209652)
--- trunk/JSTests/ChangeLog 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/JSTests/ChangeLog 2016-12-10 07:12:53 UTC (rev 209652)
@@ -1,3 +1,20 @@
+2016-12-09 Keith Miller <keith_mil...@apple.com>
+
+ Wasm should support call_indirect
+ https://bugs.webkit.org/show_bug.cgi?id=165718
+
+ Reviewed by Filip Pizlo.
+
+ * wasm/Builder.js:
+ * wasm/function-tests/call-indirect-params.js: Added.
+ * wasm/function-tests/call-indirect.js: Added.
+ * wasm/js-api/call-indirect.js: Added.
+ (const.wasmModuleWhichImportJS):
+ (MonomorphicImport):
+ (Polyphic2Import):
+ (VirtualImport):
+ * wasm/wasm.json:
+
2016-12-09 JF Bastien <jfbast...@apple.com>
WebAssembly: implement data section
Modified: trunk/JSTests/wasm/Builder.js (209651 => 209652)
--- trunk/JSTests/wasm/Builder.js 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/JSTests/wasm/Builder.js 2016-12-10 07:12:53 UTC (rev 209652)
@@ -236,6 +236,7 @@
break;
case "target_count": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
case "target_table": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
+ case "reserved": break; // improve checking https://bugs.webkit.org/show_bug.cgi?id=163421
default: throw new Error(`Implementation problem: unhandled immediate "${expect.name}" on "${op}"`);
}
}
Added: trunk/JSTests/wasm/function-tests/call-indirect-params.js (0 => 209652)
--- trunk/JSTests/wasm/function-tests/call-indirect-params.js (rev 0)
+++ trunk/JSTests/wasm/function-tests/call-indirect-params.js 2016-12-10 07:12:53 UTC (rev 209652)
@@ -0,0 +1,28 @@
+import Builder from '../Builder.js'
+
+const b = new Builder();
+b.Type().End()
+ .Function().End()
+ .Code()
+
+ .Function({ params: ["i32"], ret: "i32" })
+ .I32Const(1)
+ .End()
+
+ .Function({ params: ["i32"], ret: "i32" })
+ .GetLocal(0)
+ .End()
+
+ .Function({ params: ["i32", "i32"], ret: "i32" })
+ .GetLocal(1)
+ .GetLocal(0)
+ .CallIndirect(0, 0)
+ .End()
+
+
+const bin = b.WebAssembly()
+bin.trim();
+testWasmModuleFunctions(bin.get(), 3, [], [],
+ [[{ type: "i32", value: 1 }, [{ type: "i32", value: 0 }, { type: "i32", value: 4 }]],
+ [{ type: "i32", value: 4 }, [{ type: "i32", value: 1 }, { type: "i32", value: 4 }]],
+ ]);
Added: trunk/JSTests/wasm/function-tests/call-indirect.js (0 => 209652)
--- trunk/JSTests/wasm/function-tests/call-indirect.js (rev 0)
+++ trunk/JSTests/wasm/function-tests/call-indirect.js 2016-12-10 07:12:53 UTC (rev 209652)
@@ -0,0 +1,27 @@
+import Builder from '../Builder.js'
+
+const b = new Builder();
+b.Type().End()
+ .Function().End()
+ .Code()
+
+ .Function({ params: [], ret: "i32" })
+ .I32Const(1)
+ .End()
+
+ .Function({ params: [], ret: "i32" })
+ .I32Const(2)
+ .End()
+
+ .Function({ params: ["i32"], ret: "i32" })
+ .GetLocal(0)
+ .CallIndirect(0, 0)
+ .End()
+
+
+const bin = b.WebAssembly()
+bin.trim();
+testWasmModuleFunctions(bin.get(), 3, [], [],
+ [[{ type: "i32", value: 1 }, [{ type: "i32", value: 0 }]],
+ [{ type: "i32", value: 1 }, [{ type: "i32", value: 0 }]],
+ ]);
Added: trunk/JSTests/wasm/js-api/call-indirect.js (0 => 209652)
--- trunk/JSTests/wasm/js-api/call-indirect.js (rev 0)
+++ trunk/JSTests/wasm/js-api/call-indirect.js 2016-12-10 07:12:53 UTC (rev 209652)
@@ -0,0 +1,87 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const wasmModuleWhichImportJS = () => {
+ const builder = (new Builder())
+ .Type().End()
+ .Import()
+ .Function("imp", "func", { params: ["i32"] })
+ .End()
+ .Function().End()
+ .Export()
+ .Function("changeCounter")
+ .End()
+ .Code()
+ .Function("changeCounter", { params: ["i32", "i32"] })
+ .I32Const(42)
+ .GetLocal(0)
+ .I32Add()
+ .GetLocal(1)
+ .CallIndirect(0, 0) // Calls func(param[0] + 42).
+ .End()
+ .End();
+ const bin = builder.WebAssembly().get();
+ const module = new WebAssembly.Module(bin);
+ return module;
+};
+
+
+(function MonomorphicImport() {
+ let counter = 0;
+ const counterSetter = v => counter = v;
+ const module = wasmModuleWhichImportJS();
+ const instance = new WebAssembly.Instance(module, { imp: { func: counterSetter } });
+ for (let i = 0; i < 4096; ++i) {
+ // Invoke this a bunch of times to make sure the IC in the wasm -> JS stub works correctly.
+ instance.exports.changeCounter(i, 0);
+ assert.eq(counter, i + 42);
+ }
+})();
+
+(function Polyphic2Import() {
+ let counterA = 0;
+ let counterB = undefined;
+ const counterASetter = v => counterA = v;
+ const counterBSetter = v => counterB = { valueB: v };
+ const module = wasmModuleWhichImportJS();
+ const instanceA = new WebAssembly.Instance(module, { imp: { func: counterASetter } });
+ const instanceB = new WebAssembly.Instance(module, { imp: { func: counterBSetter } });
+ for (let i = 0; i < 2048; ++i) {
+ instanceA.exports.changeCounter(i, 0);
+ assert.isA(counterA, "number");
+ assert.eq(counterA, i + 42);
+ instanceB.exports.changeCounter(i, 0);
+ assert.isA(counterB, "object");
+ assert.eq(counterB.valueB, i + 42);
+ }
+})();
+
+(function VirtualImport() {
+ const num = 10; // It's definitely going virtual at 10!
+ let counters = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ const counterSetters = [
+ v => counters[0] = v,
+ v => counters[1] = v + 1,
+ v => counters[2] = v + 2,
+ v => counters[3] = v + 3,
+ v => counters[4] = v + 4,
+ v => counters[5] = v + 5,
+ v => counters[6] = v + 6,
+ v => counters[7] = v + 7,
+ v => counters[8] = v + 8,
+ v => counters[9] = v + 9,
+ ];
+ assert.eq(counters.length, num);
+ assert.eq(counterSetters.length, num);
+ const module = wasmModuleWhichImportJS();
+ let instances = [];
+ for (let i = 0; i < num; ++i)
+ instances[i] = new WebAssembly.Instance(module, { imp: { func: counterSetters[i] } });
+ for (let i = 0; i < 2048; ++i) {
+ for (let j = 0; j < num; ++j) {
+ instances[j].exports.changeCounter(i, 0);
+ assert.isA(counters[j], "number");
+ assert.eq(counters[j], i + 42 + j);
+ }
+ }
+})();
Modified: trunk/JSTests/wasm/wasm.json (209651 => 209652)
--- trunk/JSTests/wasm/wasm.json 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/JSTests/wasm/wasm.json 2016-12-10 07:12:53 UTC (rev 209652)
@@ -64,7 +64,7 @@
"get_global": { "category": "special", "value": 35, "return": ["global"], "parameter": [], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "read a global variable" },
"set_global": { "category": "special", "value": 36, "return": [""], "parameter": ["global"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "write a global variable" },
"call": { "category": "call", "value": 16, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "function_index", "type": "varuint32"}], "description": "call a function by its index" },
- "call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}], "description": "call a function indirect with an expected signature" },
+ "call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}, {"name": "reserved", "type": "varuint1"}], "description": "call a function indirect with an expected signature" },
"i32.load8_s": { "category": "memory", "value": 44, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
"i32.load8_u": { "category": "memory", "value": 45, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
"i32.load16_s": { "category": "memory", "value": 46, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
Modified: trunk/Source/_javascript_Core/ChangeLog (209651 => 209652)
--- trunk/Source/_javascript_Core/ChangeLog 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-12-10 07:12:53 UTC (rev 209652)
@@ -1,3 +1,55 @@
+2016-12-09 Keith Miller <keith_mil...@apple.com>
+
+ Wasm should support call_indirect
+ https://bugs.webkit.org/show_bug.cgi?id=165718
+
+ Reviewed by Filip Pizlo.
+
+ This patch adds support for call_indirect. The basic framework for
+ an indirect call is that the module holds a buffer containing a
+ stub for each function in the index space. Whenever a function
+ needs to do an indirect call it gets a index into that table. In
+ order to ensure call_indirect is calling a valid function the
+ functionIndexSpace also needs a pointer to a canonicalized
+ signature. When making an indirect call, we first check the index
+ is in range, then check the signature matches the value we were given.
+
+ This patch also differentiates between FunctionIndexSpaces and
+ ImmutableFunctionIndexSpaces. Since we don't know the size of the
+ FunctionIndexSpace when we start parsing we need to be able to
+ resize the IndexSpace. However, once we have finished parsing all
+ the sections we want to prevent an relocation of the function
+ index space pointer.
+
+ * wasm/WasmB3IRGenerator.cpp:
+ (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+ (JSC::Wasm::B3IRGenerator::addCall):
+ (JSC::Wasm::B3IRGenerator::addCallIndirect):
+ (JSC::Wasm::createJSToWasmWrapper):
+ (JSC::Wasm::parseAndCompile):
+ * wasm/WasmB3IRGenerator.h:
+ * wasm/WasmCallingConvention.h:
+ (JSC::Wasm::CallingConvention::setupCall):
+ * wasm/WasmFormat.h:
+ * wasm/WasmFunctionParser.h:
+ (JSC::Wasm::FunctionParser::setErrorMessage):
+ (JSC::Wasm::FunctionParser<Context>::FunctionParser):
+ (JSC::Wasm::FunctionParser<Context>::parseExpression):
+ * wasm/WasmPlan.cpp:
+ (JSC::Wasm::Plan::run):
+ * wasm/WasmPlan.h:
+ (JSC::Wasm::Plan::takeFunctionIndexSpace):
+ * wasm/WasmValidate.cpp:
+ (JSC::Wasm::Validate::addCallIndirect):
+ (JSC::Wasm::validateFunction):
+ * wasm/WasmValidate.h:
+ * wasm/js/JSWebAssemblyModule.cpp:
+ (JSC::JSWebAssemblyModule::create):
+ (JSC::JSWebAssemblyModule::JSWebAssemblyModule):
+ * wasm/js/JSWebAssemblyModule.h:
+ (JSC::JSWebAssemblyModule::signatureForFunctionIndexSpace):
+ (JSC::JSWebAssemblyModule::offsetOfFunctionIndexSpace):
+
2016-12-09 JF Bastien <jfbast...@apple.com>
WebAssembly: implement data section
Modified: trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp 2016-12-10 07:12:53 UTC (rev 209652)
@@ -40,6 +40,8 @@
#include "B3VariableValue.h"
#include "B3WasmAddressValue.h"
#include "B3WasmBoundsCheckValue.h"
+#include "JSWebAssemblyInstance.h"
+#include "JSWebAssemblyModule.h"
#include "VirtualRegister.h"
#include "WasmCallingConvention.h"
#include "WasmFunctionParser.h"
@@ -130,7 +132,7 @@
static constexpr ExpressionType emptyExpression = nullptr;
- B3IRGenerator(MemoryInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&);
+ B3IRGenerator(const MemoryInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&);
bool WARN_UNUSED_RETURN addArguments(const Vector<Type>&);
bool WARN_UNUSED_RETURN addLocal(Type, uint32_t);
@@ -164,7 +166,9 @@
bool WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
bool WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
- bool WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature*, Vector<ExpressionType>& args, ExpressionType& result);
+ // Calls
+ bool WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature*, Vector<ExpressionType>& args, ExpressionType& result);
+ bool WARN_UNUSED_RETURN addCallIndirect(const Signature*, Vector<ExpressionType>& args, ExpressionType& result);
void dump(const Vector<ControlEntry>& controlStack, const ExpressionList& expressionStack);
@@ -179,6 +183,7 @@
void unifyValuesWithBlock(const ExpressionList& resultStack, ResultList& stack);
Value* zeroForType(Type);
+ const ImmutableFunctionIndexSpace& m_functionIndexSpace;
Procedure& m_proc;
BasicBlock* m_currentBlock;
Vector<Variable*> m_locals;
@@ -186,10 +191,12 @@
GPRReg m_memoryBaseGPR;
GPRReg m_memorySizeGPR;
Value* m_zeroValues[numTypes];
+ Value* m_functionIndexSpaceValue;
};
-B3IRGenerator::B3IRGenerator(MemoryInformation& memory, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls)
- : m_proc(procedure)
+B3IRGenerator::B3IRGenerator(const MemoryInformation& memory, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace)
+ : m_functionIndexSpace(functionIndexSpace)
+ , m_proc(procedure)
, m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls)
{
m_currentBlock = m_proc.addBlock();
@@ -224,6 +231,8 @@
}
wasmCallingConvention().setupFrameInPrologue(compilation, m_proc, Origin(), m_currentBlock);
+
+ m_functionIndexSpaceValue = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), functionIndexSpace.buffer.get());
}
Value* B3IRGenerator::zeroForType(Type type)
@@ -592,7 +601,7 @@
return true;
}
-bool B3IRGenerator::addCall(unsigned functionIndex, const Signature* signature, Vector<ExpressionType>& args, ExpressionType& result)
+bool B3IRGenerator::addCall(uint32_t functionIndex, const Signature* signature, Vector<ExpressionType>& args, ExpressionType& result)
{
ASSERT(signature->arguments.size() == args.size());
@@ -618,6 +627,57 @@
return true;
}
+bool B3IRGenerator::addCallIndirect(const Signature* signature, Vector<ExpressionType>& args, ExpressionType& result)
+{
+ ExpressionType calleeIndex = args.takeLast();
+ ASSERT(signature->arguments.size() == args.size());
+
+ // Check the index we are looking for is valid.
+ {
+ ExpressionType maxValidIndex = m_currentBlock->appendIntConstant(m_proc, Origin(), Int32, m_functionIndexSpace.size);
+ CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(),
+ m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), m_zeroValues[linearizeType(I32)],
+ m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), calleeIndex, maxValidIndex)));
+
+ check->setGenerator([] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
+ jit.breakpoint();
+ });
+ }
+
+ // Compute the offset in the function index space we are looking for.
+ ExpressionType offset = m_currentBlock->appendNew<Value>(m_proc, Mul, Origin(),
+ m_currentBlock->appendNew<Value>(m_proc, ZExt32, Origin(), calleeIndex),
+ m_currentBlock->appendIntConstant(m_proc, Origin(), pointerType(), sizeof(CallableFunction)));
+ ExpressionType callableFunction = m_currentBlock->appendNew<Value>(m_proc, Add, Origin(), m_functionIndexSpaceValue, offset);
+
+ // Check the signature matches the value we expect.
+ {
+ ExpressionType calleeSignature = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), callableFunction, OBJECT_OFFSETOF(CallableFunction, signature));
+ ExpressionType expectedSignature = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), signature);
+ CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(),
+ m_currentBlock->appendNew<Value>(m_proc, NotEqual, Origin(), calleeSignature, expectedSignature));
+
+ check->setGenerator([] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
+ jit.breakpoint();
+ });
+ }
+
+ ExpressionType calleeCode = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), callableFunction, OBJECT_OFFSETOF(CallableFunction, code));
+
+ result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, Origin(), args, toB3Type(signature->returnType),
+ [&] (PatchpointValue* patchpoint) {
+ patchpoint->effects.writesPinned = true;
+ patchpoint->effects.readsPinned = true;
+
+ patchpoint->append(calleeCode, ValueRep::SomeRegister);
+
+ patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) {
+ jit.call(params[0].gpr());
+ });
+ });
+ return true;
+}
+
void B3IRGenerator::unify(Variable* variable, ExpressionType source)
{
m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), variable, source);
@@ -657,7 +717,7 @@
dataLogLn("\n");
}
-static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signature* signature, MacroAssemblerCodePtr mainFunction, MemoryInformation& memory)
+static std::unique_ptr<Compilation> createJSToWasmWrapper(VM& vm, const Signature* signature, MacroAssemblerCodePtr mainFunction, const MemoryInformation& memory)
{
Procedure proc;
BasicBlock* block = proc.addBlock();
@@ -737,13 +797,13 @@
return std::make_unique<Compilation>(vm, proc);
}
-std::unique_ptr<WasmInternalFunction> parseAndCompile(VM& vm, const uint8_t* functionStart, size_t functionLength, MemoryInformation& memory, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const FunctionIndexSpace& functionIndexSpace, unsigned optLevel)
+std::unique_ptr<WasmInternalFunction> parseAndCompile(VM& vm, const uint8_t* functionStart, size_t functionLength, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& info, unsigned optLevel)
{
auto result = std::make_unique<WasmInternalFunction>();
Procedure procedure;
- B3IRGenerator context(memory, procedure, result.get(), unlinkedWasmToWasmCalls);
- FunctionParser<B3IRGenerator> parser(context, functionStart, functionLength, signature, functionIndexSpace);
+ B3IRGenerator context(info.memory, procedure, result.get(), unlinkedWasmToWasmCalls, functionIndexSpace);
+ FunctionParser<B3IRGenerator> parser(context, functionStart, functionLength, signature, functionIndexSpace, info);
if (!parser.parse())
RELEASE_ASSERT_NOT_REACHED();
@@ -757,7 +817,7 @@
dataLog("Post SSA: ", procedure);
result->code = std::make_unique<Compilation>(vm, procedure, optLevel);
- result->jsToWasmEntryPoint = createJSToWasmWrapper(vm, signature, result->code->code(), memory);
+ result->jsToWasmEntryPoint = createJSToWasmWrapper(vm, signature, result->code->code(), info.memory);
return result;
}
Modified: trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.h (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.h 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.h 2016-12-10 07:12:53 UTC (rev 209652)
@@ -37,7 +37,7 @@
class MemoryInformation;
-std::unique_ptr<WasmInternalFunction> parseAndCompile(VM&, const uint8_t*, size_t, MemoryInformation&, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const FunctionIndexSpace&, unsigned optLevel = 1);
+std::unique_ptr<WasmInternalFunction> parseAndCompile(VM&, const uint8_t*, size_t, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const ImmutableFunctionIndexSpace&, const ModuleInformation&, unsigned optLevel = 1);
} } // namespace JSC::Wasm
Modified: trunk/Source/_javascript_Core/wasm/WasmCallingConvention.h (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/WasmCallingConvention.h 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/WasmCallingConvention.h 2016-12-10 07:12:53 UTC (rev 209652)
@@ -163,8 +163,8 @@
B3::PatchpointValue* patchpoint = block->appendNew<B3::PatchpointValue>(proc, returnType, origin);
patchpoint->clobberEarly(RegisterSet::macroScratchRegisters());
patchpoint->clobberLate(RegisterSet::volatileRegistersForJSCall());
+ patchpointFunctor(patchpoint);
patchpoint->appendVector(constrainedArguments);
- patchpointFunctor(patchpoint);
switch (returnType) {
case B3::Void:
Modified: trunk/Source/_javascript_Core/wasm/WasmFormat.h (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/WasmFormat.h 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/WasmFormat.h 2016-12-10 07:12:53 UTC (rev 209652)
@@ -187,6 +187,12 @@
};
typedef Vector<CallableFunction> FunctionIndexSpace;
+
+struct ImmutableFunctionIndexSpace {
+ MallocPtr<CallableFunction> buffer;
+ size_t size;
+};
+
} } // namespace JSC::Wasm
#endif // ENABLE(WEBASSEMBLY)
Modified: trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h 2016-12-10 07:12:53 UTC (rev 209652)
@@ -45,7 +45,7 @@
typedef typename Context::ControlType ControlType;
typedef typename Context::ExpressionList ExpressionList;
- FunctionParser(Context&, const uint8_t* functionStart, size_t functionLength, const Signature*, const FunctionIndexSpace&);
+ FunctionParser(Context&, const uint8_t* functionStart, size_t functionLength, const Signature*, const ImmutableFunctionIndexSpace&, const ModuleInformation&);
bool WARN_UNUSED_RETURN parse();
@@ -71,22 +71,28 @@
template<OpType>
bool WARN_UNUSED_RETURN binaryCase();
- void setErrorMessage(String&& message) { m_context.setErrorMessage(WTFMove(message)); }
+ bool setErrorMessage(String&& message)
+ {
+ m_context.setErrorMessage(WTFMove(message));
+ return false;
+ }
Context& m_context;
ExpressionList m_expressionStack;
Vector<ControlEntry> m_controlStack;
const Signature* m_signature;
- const FunctionIndexSpace& m_functionIndexSpace;
+ const ImmutableFunctionIndexSpace& m_functionIndexSpace;
+ const ModuleInformation& m_info;
unsigned m_unreachableBlocks { 0 };
};
template<typename Context>
-FunctionParser<Context>::FunctionParser(Context& context, const uint8_t* functionStart, size_t functionLength, const Signature* signature, const FunctionIndexSpace& functionIndexSpace)
+FunctionParser<Context>::FunctionParser(Context& context, const uint8_t* functionStart, size_t functionLength, const Signature* signature, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& info)
: Parser(functionStart, functionLength)
, m_context(context)
, m_signature(signature)
, m_functionIndexSpace(functionIndexSpace)
+ , m_info(info)
{
if (verbose)
dataLogLn("Parsing function starting at: ", (uintptr_t)functionStart, " of length: ", functionLength);
@@ -350,10 +356,10 @@
if (!parseVarUInt32(functionIndex))
return false;
- if (functionIndex >= m_functionIndexSpace.size())
+ if (functionIndex >= m_functionIndexSpace.size)
return false;
- const Signature* calleeSignature = m_functionIndexSpace[functionIndex].signature;
+ const Signature* calleeSignature = m_functionIndexSpace.buffer.get()[functionIndex].signature;
if (calleeSignature->arguments.size() > m_expressionStack.size())
return false;
@@ -375,6 +381,42 @@
return true;
}
+ case OpType::CallIndirect: {
+ uint32_t signatureIndex;
+ if (!parseVarUInt32(signatureIndex))
+ return false;
+
+ uint8_t reserved;
+ if (!parseVarUInt1(reserved))
+ return false;
+
+ if (m_info.signatures.size() <= signatureIndex)
+ return setErrorMessage("Tried to use a signature outside the range of valid signatures");
+
+ const Signature* calleeSignature = &m_info.signatures[signatureIndex];
+ size_t argumentCount = calleeSignature->arguments.size() + 1; // Add the callee's index.
+ if (argumentCount > m_expressionStack.size())
+ return setErrorMessage("Not enough values on the stack for call_indirect");
+
+ Vector<ExpressionType> args;
+ if (!args.tryReserveCapacity(argumentCount))
+ return setErrorMessage("Out of memory");
+
+ size_t firstArgumentIndex = m_expressionStack.size() - argumentCount;
+ for (unsigned i = firstArgumentIndex; i < m_expressionStack.size(); ++i)
+ args.uncheckedAppend(m_expressionStack[i]);
+ m_expressionStack.shrink(firstArgumentIndex);
+
+ ExpressionType result = Context::emptyExpression;
+ if (!m_context.addCallIndirect(calleeSignature, args, result))
+ return false;
+
+ if (result != Context::emptyExpression)
+ m_expressionStack.append(result);
+
+ return true;
+ }
+
case OpType::Block: {
Type inlineSignature;
if (!parseResultType(inlineSignature))
@@ -517,8 +559,7 @@
case OpType::GrowMemory:
case OpType::CurrentMemory:
case OpType::GetGlobal:
- case OpType::SetGlobal:
- case OpType::CallIndirect: {
+ case OpType::SetGlobal: {
// FIXME: Not yet implemented.
return false;
}
Modified: trunk/Source/_javascript_Core/wasm/WasmPlan.cpp (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/WasmPlan.cpp 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/WasmPlan.cpp 2016-12-10 07:12:53 UTC (rev 209652)
@@ -72,7 +72,8 @@
}
m_moduleInformation = WTFMove(moduleParser.moduleInformation());
m_functionLocationInBinary = WTFMove(moduleParser.functionLocationInBinary());
- m_functionIndexSpace = WTFMove(moduleParser.functionIndexSpace());
+ m_functionIndexSpace.size = moduleParser.functionIndexSpace().size();
+ m_functionIndexSpace.buffer = moduleParser.functionIndexSpace().releaseBuffer();
}
if (verbose)
dataLogLn("Parsed module.");
@@ -103,7 +104,7 @@
dataLogLn("Processing import function number ", importFunctionIndex, ": ", import->module, ": ", import->field);
Signature* signature = m_moduleInformation->importFunctions.at(import->kindIndex);
m_wasmToJSStubs.uncheckedAppend(importStubGenerator(m_vm, m_callLinkInfos, signature, importFunctionIndex));
- m_functionIndexSpace[importFunctionIndex].code = m_wasmToJSStubs[importFunctionIndex].code().executableAddress();
+ m_functionIndexSpace.buffer.get()[importFunctionIndex].code = m_wasmToJSStubs[importFunctionIndex].code().executableAddress();
}
for (unsigned functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); ++functionIndex) {
@@ -114,9 +115,9 @@
ASSERT(functionLength <= m_sourceLength);
Signature* signature = m_moduleInformation->internalFunctionSignatures[functionIndex];
unsigned functionIndexSpace = m_wasmToJSStubs.size() + functionIndex;
- ASSERT(m_functionIndexSpace[functionIndexSpace].signature == signature);
+ ASSERT(m_functionIndexSpace.buffer.get()[functionIndexSpace].signature == signature);
- String error = validateFunction(functionStart, functionLength, signature, m_functionIndexSpace, m_moduleInformation->memory);
+ String error = validateFunction(functionStart, functionLength, signature, m_functionIndexSpace, *m_moduleInformation);
if (!error.isNull()) {
if (verbose) {
for (unsigned i = 0; i < functionLength; ++i)
@@ -128,14 +129,14 @@
}
unlinkedWasmToWasmCalls.uncheckedAppend(Vector<UnlinkedWasmToWasmCall>());
- m_wasmInternalFunctions.uncheckedAppend(parseAndCompile(*m_vm, functionStart, functionLength, m_moduleInformation->memory, signature, unlinkedWasmToWasmCalls.at(functionIndex), m_functionIndexSpace));
- m_functionIndexSpace[functionIndexSpace].code = m_wasmInternalFunctions[functionIndex]->code->code().executableAddress();
+ m_wasmInternalFunctions.uncheckedAppend(parseAndCompile(*m_vm, functionStart, functionLength, signature, unlinkedWasmToWasmCalls.at(functionIndex), m_functionIndexSpace, *m_moduleInformation));
+ m_functionIndexSpace.buffer.get()[functionIndexSpace].code = m_wasmInternalFunctions[functionIndex]->code->code().executableAddress();
}
// Patch the call sites for each WebAssembly function.
for (auto& unlinked : unlinkedWasmToWasmCalls) {
for (auto& call : unlinked)
- MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(m_functionIndexSpace[call.functionIndex].code));
+ MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(m_functionIndexSpace.buffer.get()[call.functionIndex].code));
}
m_failed = false;
Modified: trunk/Source/_javascript_Core/wasm/WasmPlan.h (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/WasmPlan.h 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/WasmPlan.h 2016-12-10 07:12:53 UTC (rev 209652)
@@ -95,7 +95,7 @@
return WTFMove(m_wasmToJSStubs);
}
- FunctionIndexSpace&& takeFunctionIndexSpace()
+ ImmutableFunctionIndexSpace&& takeFunctionIndexSpace()
{
RELEASE_ASSERT(!failed());
return WTFMove(m_functionIndexSpace);
@@ -107,7 +107,7 @@
Bag<CallLinkInfo> m_callLinkInfos;
Vector<WasmToJSStub> m_wasmToJSStubs;
Vector<std::unique_ptr<WasmInternalFunction>> m_wasmInternalFunctions;
- FunctionIndexSpace m_functionIndexSpace;
+ ImmutableFunctionIndexSpace m_functionIndexSpace;
VM* m_vm;
const uint8_t* m_source;
Modified: trunk/Source/_javascript_Core/wasm/WasmValidate.cpp (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/WasmValidate.cpp 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/WasmValidate.cpp 2016-12-10 07:12:53 UTC (rev 209652)
@@ -110,8 +110,9 @@
bool WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
bool WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
-
+ // Calls
bool WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature*, const Vector<ExpressionType>& args, ExpressionType& result);
+ bool WARN_UNUSED_RETURN addCallIndirect(const Signature*, const Vector<ExpressionType>& args, ExpressionType& result);
void dump(const Vector<ControlEntry>& controlStack, const ExpressionList& expressionStack);
@@ -347,6 +348,35 @@
return true;
}
+bool Validate::addCallIndirect(const Signature* signature, const Vector<ExpressionType>& args, ExpressionType& result)
+{
+ const auto argumentCount = signature->arguments.size();
+ if (argumentCount != args.size() - 1) {
+ StringBuilder builder;
+ builder.append("Arity mismatch in call_indirect, expected: ");
+ builder.appendNumber(signature->arguments.size());
+ builder.append(" but got: ");
+ builder.appendNumber(args.size());
+ m_errorMessage = builder.toString();
+ return false;
+ }
+
+ for (unsigned i = 0; i < argumentCount; ++i) {
+ if (args[i] != signature->arguments[i]) {
+ m_errorMessage = makeString("Expected argument type: ", toString(signature->arguments[i]), " does not match passed argument type: ", toString(args[i]));
+ return false;
+ }
+ }
+
+ if (args.last() != I32) {
+ m_errorMessage = makeString("Expected call_indirect target index to have type: i32 but got type: ", toString(args.last()));
+ return false;
+ }
+
+ result = signature->returnType;
+ return true;
+}
+
bool Validate::unify(const ExpressionList& values, const ControlType& block)
{
ASSERT(values.size() <= 1);
@@ -371,10 +401,11 @@
// Think of this as penance for the sin of bad error messages.
}
-String validateFunction(const uint8_t* source, size_t length, const Signature* signature, const FunctionIndexSpace& functionIndexSpace, const MemoryInformation& memory)
+String validateFunction(const uint8_t* source, size_t length, const Signature* signature, const ImmutableFunctionIndexSpace& functionIndexSpace, const ModuleInformation& info)
{
- Validate context(signature->returnType, memory);
- FunctionParser<Validate> validator(context, source, length, signature, functionIndexSpace);
+ Validate context(signature->returnType, info.memory);
+ FunctionParser<Validate> validator(context, source, length, signature, functionIndexSpace, info);
+
if (!validator.parse()) {
// FIXME: add better location information here. see: https://bugs.webkit.org/show_bug.cgi?id=164288
// FIXME: We should never not have an error message if we return false.
Modified: trunk/Source/_javascript_Core/wasm/WasmValidate.h (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/WasmValidate.h 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/WasmValidate.h 2016-12-10 07:12:53 UTC (rev 209652)
@@ -31,7 +31,7 @@
namespace JSC { namespace Wasm {
-String validateFunction(const uint8_t*, size_t, const Signature*, const FunctionIndexSpace&, const MemoryInformation&);
+String validateFunction(const uint8_t*, size_t, const Signature*, const ImmutableFunctionIndexSpace&, const ModuleInformation&);
} } // namespace JSC::Wasm
Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyModule.cpp (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyModule.cpp 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyModule.cpp 2016-12-10 07:12:53 UTC (rev 209652)
@@ -38,9 +38,9 @@
const ClassInfo JSWebAssemblyModule::s_info = { "WebAssembly.Module", &Base::s_info, nullptr, CREATE_METHOD_TABLE(JSWebAssemblyModule) };
-JSWebAssemblyModule* JSWebAssemblyModule::create(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmToJSStub>&& wasmToJSStubs, Wasm::FunctionIndexSpace&& functionIndexSpace, SymbolTable* exportSymbolTable, unsigned calleeCount)
+JSWebAssemblyModule* JSWebAssemblyModule::create(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmToJSStub>&& wasmToJSStubs, Wasm::ImmutableFunctionIndexSpace&& functionIndexSpace, SymbolTable* exportSymbolTable, unsigned calleeCount)
{
- auto* instance = new (NotNull, allocateCell<JSWebAssemblyModule>(vm.heap, allocationSize(calleeCount))) JSWebAssemblyModule(vm, structure, std::forward<std::unique_ptr<Wasm::ModuleInformation>>(moduleInformation), std::forward<Bag<CallLinkInfo>>(callLinkInfos), std::forward<Vector<Wasm::WasmToJSStub>>(wasmToJSStubs), std::forward<Wasm::FunctionIndexSpace>(functionIndexSpace), calleeCount);
+ auto* instance = new (NotNull, allocateCell<JSWebAssemblyModule>(vm.heap, allocationSize(calleeCount))) JSWebAssemblyModule(vm, structure, std::forward<std::unique_ptr<Wasm::ModuleInformation>>(moduleInformation), std::forward<Bag<CallLinkInfo>>(callLinkInfos), std::forward<Vector<Wasm::WasmToJSStub>>(wasmToJSStubs), std::forward<Wasm::ImmutableFunctionIndexSpace>(functionIndexSpace), calleeCount);
instance->finishCreation(vm, exportSymbolTable);
return instance;
}
@@ -50,7 +50,7 @@
return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
}
-JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmToJSStub>&& wasmToJSStubs, Wasm::FunctionIndexSpace&& functionIndexSpace, unsigned calleeCount)
+JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmToJSStub>&& wasmToJSStubs, Wasm::ImmutableFunctionIndexSpace&& functionIndexSpace, unsigned calleeCount)
: Base(vm, structure)
, m_moduleInformation(WTFMove(moduleInformation))
, m_callLinkInfos(WTFMove(callLinkInfos))
Modified: trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyModule.h (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyModule.h 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/js/JSWebAssemblyModule.h 2016-12-10 07:12:53 UTC (rev 209652)
@@ -42,7 +42,7 @@
public:
typedef JSDestructibleObject Base;
- static JSWebAssemblyModule* create(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmToJSStub>&&, Wasm::FunctionIndexSpace&&, SymbolTable*, unsigned);
+ static JSWebAssemblyModule* create(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmToJSStub>&&, Wasm::ImmutableFunctionIndexSpace&&, SymbolTable*, unsigned);
static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
DECLARE_INFO;
@@ -49,7 +49,7 @@
const Wasm::ModuleInformation& moduleInformation() const { return *m_moduleInformation.get(); }
SymbolTable* exportSymbolTable() const { return m_exportSymbolTable.get(); }
- Wasm::Signature* signatureForFunctionIndexSpace(unsigned functionIndexSpace) const { return m_functionIndexSpace.at(functionIndexSpace).signature; }
+ Wasm::Signature* signatureForFunctionIndexSpace(unsigned functionIndexSpace) const { ASSERT(functionIndexSpace < m_functionIndexSpace.size); return m_functionIndexSpace.buffer.get()[functionIndexSpace].signature; }
unsigned importCount() const { return m_wasmToJSStubs.size(); }
JSWebAssemblyCallee* calleeFromFunctionIndexSpace(unsigned functionIndexSpace)
@@ -65,8 +65,10 @@
return bitwise_cast<WriteBarrier<JSWebAssemblyCallee>*>(bitwise_cast<char*>(this) + offsetOfCallees());
}
+ static ptrdiff_t offsetOfFunctionIndexSpace() { return OBJECT_OFFSETOF(JSWebAssemblyModule, m_functionIndexSpace); }
+
protected:
- JSWebAssemblyModule(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmToJSStub>&&, Wasm::FunctionIndexSpace&&, unsigned calleeCount);
+ JSWebAssemblyModule(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmToJSStub>&&, Wasm::ImmutableFunctionIndexSpace&&, unsigned calleeCount);
void finishCreation(VM&, SymbolTable*);
static void destroy(JSCell*);
static void visitChildren(JSCell*, SlotVisitor&);
@@ -86,7 +88,7 @@
Bag<CallLinkInfo> m_callLinkInfos;
WriteBarrier<SymbolTable> m_exportSymbolTable;
Vector<Wasm::WasmToJSStub> m_wasmToJSStubs;
- Wasm::FunctionIndexSpace m_functionIndexSpace;
+ const Wasm::ImmutableFunctionIndexSpace m_functionIndexSpace;
unsigned m_calleeCount;
};
Modified: trunk/Source/_javascript_Core/wasm/wasm.json (209651 => 209652)
--- trunk/Source/_javascript_Core/wasm/wasm.json 2016-12-10 07:08:31 UTC (rev 209651)
+++ trunk/Source/_javascript_Core/wasm/wasm.json 2016-12-10 07:12:53 UTC (rev 209652)
@@ -64,7 +64,7 @@
"get_global": { "category": "special", "value": 35, "return": ["global"], "parameter": [], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "read a global variable" },
"set_global": { "category": "special", "value": 36, "return": [""], "parameter": ["global"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "write a global variable" },
"call": { "category": "call", "value": 16, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "function_index", "type": "varuint32"}], "description": "call a function by its index" },
- "call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}], "description": "call a function indirect with an expected signature" },
+ "call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}, {"name": "reserved", "type": "varuint1"}], "description": "call a function indirect with an expected signature" },
"i32.load8_s": { "category": "memory", "value": 44, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
"i32.load8_u": { "category": "memory", "value": 45, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },
"i32.load16_s": { "category": "memory", "value": 46, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" },