Log Message
Add support for Wasm br_table https://bugs.webkit.org/show_bug.cgi?id=164429
Reviewed by Michael Saboff. This patch adds support for Wasm br_table. The Wasm br_table opcode essentially directly maps to B3's switch opcode. There are also three other minor changes: 1) all non-argument locals should be initialized to zero at function entry. 2) add new setErrorMessage member to WasmFunctionParser.h 3) return does not decode an extra immediate anymore. * testWasm.cpp: (runWasmTests): * wasm/WasmB3IRGenerator.cpp: * wasm/WasmFunctionParser.h: (JSC::Wasm::FunctionParser::setErrorMessage): (JSC::Wasm::FunctionParser<Context>::parseExpression): (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression): (JSC::Wasm::FunctionParser<Context>::popExpressionStack): * wasm/WasmValidate.cpp: (JSC::Wasm::Validate::checkBranchTarget): (JSC::Wasm::Validate::addBranch): (JSC::Wasm::Validate::addSwitch):
Modified Paths
Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (208401 => 208402)
--- trunk/Source/_javascript_Core/ChangeLog 2016-11-04 22:12:12 UTC (rev 208401)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-11-04 22:53:21 UTC (rev 208402)
@@ -1,3 +1,31 @@
+2016-11-04 Keith Miller <[email protected]>
+
+ Add support for Wasm br_table
+ https://bugs.webkit.org/show_bug.cgi?id=164429
+
+ Reviewed by Michael Saboff.
+
+ This patch adds support for Wasm br_table. The Wasm br_table
+ opcode essentially directly maps to B3's switch opcode.
+
+ There are also three other minor changes:
+ 1) all non-argument locals should be initialized to zero at function entry.
+ 2) add new setErrorMessage member to WasmFunctionParser.h
+ 3) return does not decode an extra immediate anymore.
+
+ * testWasm.cpp:
+ (runWasmTests):
+ * wasm/WasmB3IRGenerator.cpp:
+ * wasm/WasmFunctionParser.h:
+ (JSC::Wasm::FunctionParser::setErrorMessage):
+ (JSC::Wasm::FunctionParser<Context>::parseExpression):
+ (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression):
+ (JSC::Wasm::FunctionParser<Context>::popExpressionStack):
+ * wasm/WasmValidate.cpp:
+ (JSC::Wasm::Validate::checkBranchTarget):
+ (JSC::Wasm::Validate::addBranch):
+ (JSC::Wasm::Validate::addSwitch):
+
2016-11-04 JF Bastien <[email protected]>
WebAssembly JS API: implement more sections
Modified: trunk/Source/_javascript_Core/testWasm.cpp (208401 => 208402)
--- trunk/Source/_javascript_Core/testWasm.cpp 2016-11-04 22:12:12 UTC (rev 208401)
+++ trunk/Source/_javascript_Core/testWasm.cpp 2016-11-04 22:53:21 UTC (rev 208402)
@@ -296,6 +296,126 @@
{
// Generated from:
// (module
+ // (func (export "br_table-with-loop") (param $x i32) (result i32)
+ // (local $i i32)
+ // (loop
+ // (block
+ // (get_local $x)
+ // (set_local $i (i32.add (get_local $i) (i32.const 1)))
+ // (set_local $x (i32.sub (get_local $x) (i32.const 1)))
+ // (br_table 0 1)
+ // )
+ // )
+ // (get_local $i)
+ // )
+ // )
+ Vector<uint8_t> vector = {
+ 0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
+ 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x96, 0x80, 0x80,
+ 0x80, 0x00, 0x01, 0x12, 0x62, 0x72, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2d, 0x77, 0x69, 0x74,
+ 0x68, 0x2d, 0x6c, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x0a, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x01, 0xa0,
+ 0x80, 0x80, 0x80, 0x00, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x00, 0x14, 0x00, 0x14, 0x01, 0x10,
+ 0x01, 0x40, 0x15, 0x01, 0x14, 0x00, 0x10, 0x01, 0x41, 0x15, 0x00, 0x08, 0x01, 0x00, 0x01, 0x0f,
+ 0x0f, 0x14, 0x01, 0x0f
+ };
+
+ Plan plan(*vm, vector);
+ checkPlan(plan, 1);
+
+ // Test this doesn't crash.
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(0) }), 1);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(1) }), 2);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(100) }), 101);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(122) }), 123);
+ }
+
+ {
+ // Generated from:
+ // (module
+ // (func (export "multiple-value") (param i32) (result i32)
+ // (local i32)
+ // (set_local 1 (block i32
+ // (set_local 1 (block i32
+ // (set_local 1 (block i32
+ // (set_local 1 (block i32
+ // (set_local 1 (block i32
+ // (br_table 3 2 1 0 4 (i32.const 200) (get_local 0))
+ // (return (i32.add (get_local 1) (i32.const 99)))
+ // ))
+ // (return (i32.add (get_local 1) (i32.const 10)))
+ // ))
+ // (return (i32.add (get_local 1) (i32.const 11)))
+ // ))
+ // (return (i32.add (get_local 1) (i32.const 12)))
+ // ))
+ // (return (i32.add (get_local 1) (i32.const 13)))
+ // ))
+ // (i32.add (get_local 1) (i32.const 14))
+ // )
+ // )
+ Vector<uint8_t> vector = {
+ 0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
+ 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x92, 0x80, 0x80,
+ 0x80, 0x00, 0x01, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x2d, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x00, 0x00, 0x0a, 0xd3, 0x80, 0x80, 0x80, 0x00, 0x01, 0xcd, 0x80, 0x80, 0x80, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0xc8, 0x01,
+ 0x14, 0x00, 0x08, 0x04, 0x03, 0x02, 0x01, 0x00, 0x04, 0x14, 0x01, 0x10, 0xe3, 0x00, 0x40, 0x09,
+ 0x0f, 0x15, 0x01, 0x14, 0x01, 0x10, 0x0a, 0x40, 0x09, 0x0f, 0x15, 0x01, 0x14, 0x01, 0x10, 0x0b,
+ 0x40, 0x09, 0x0f, 0x15, 0x01, 0x14, 0x01, 0x10, 0x0c, 0x40, 0x09, 0x0f, 0x15, 0x01, 0x14, 0x01,
+ 0x10, 0x0d, 0x40, 0x09, 0x0f, 0x15, 0x01, 0x14, 0x01, 0x10, 0x0e, 0x40, 0x0f
+ };
+
+ Plan plan(*vm, vector);
+ checkPlan(plan, 1);
+
+ // Test this doesn't crash.
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(0) }), 213);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(1) }), 212);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(2) }), 211);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(3) }), 210);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(3) }), 210);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(4) }), 214);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(5) }), 214);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(-1) }), 214);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(-1000) }), 214);
+ }
+
+ {
+ // Generated from:
+ // (module
+ // (func (export "singleton") (param i32) (result i32)
+ // (block
+ // (block
+ // (br_table 1 0 (get_local 0))
+ // (return (i32.const 21))
+ // )
+ // (return (i32.const 20))
+ // )
+ // (i32.const 22)
+ // )
+ // )
+ Vector<uint8_t> vector = {
+ 0x00, 0x61, 0x73, 0x6d, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x86, 0x80, 0x80, 0x80, 0x00, 0x01, 0x40,
+ 0x01, 0x01, 0x01, 0x01, 0x03, 0x82, 0x80, 0x80, 0x80, 0x00, 0x01, 0x00, 0x07, 0x8d, 0x80, 0x80,
+ 0x80, 0x00, 0x01, 0x09, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x74, 0x6f, 0x6e, 0x00, 0x00, 0x0a,
+ 0x9c, 0x80, 0x80, 0x80, 0x00, 0x01, 0x96, 0x80, 0x80, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x14, 0x00, 0x08, 0x01, 0x01, 0x00, 0x10, 0x15, 0x09, 0x0f, 0x10, 0x14, 0x09, 0x0f, 0x10, 0x16,
+ 0x0f
+ };
+
+ Plan plan(*vm, vector);
+ checkPlan(plan, 1);
+
+ // Test this doesn't crash.
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(0) }), 22);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(1) }), 20);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(11) }), 20);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(-100) }), 20);
+ }
+
+ {
+ // Generated from:
+ // (module
// (func (export "if-then-both-fallthrough") (param $x i32) (param $y i32) (result i32)
// (block $block i32
// (if i32 (i32.eq (get_local $x) (i32.const 0))
@@ -320,8 +440,8 @@
checkPlan(plan, 1);
// Test this doesn't crash.
- CHECK(isIdentical(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { boxf(0), boxf(32) }), 1));
- CHECK(isIdentical(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { boxf(1), boxf(32) }), 2));
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(0), box(32) }), 1);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(1), box(32) }), 2);
}
{
@@ -347,8 +467,8 @@
checkPlan(plan, 1);
// Test this doesn't crash.
- CHECK(isIdentical(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { boxf(0), boxf(32) }), 1));
- CHECK(isIdentical(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { boxf(1), boxf(32) }), 2));
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(0), box(32) }), 1);
+ CHECK_EQ(invoke<int>(*plan.compiledFunction(0)->jsEntryPoint, { box(1), box(32) }), 2);
}
{
Modified: trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp (208401 => 208402)
--- trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp 2016-11-04 22:12:12 UTC (rev 208401)
+++ trunk/Source/_javascript_Core/wasm/WasmB3IRGenerator.cpp 2016-11-04 22:53:21 UTC (rev 208402)
@@ -32,6 +32,7 @@
#include "B3ConstPtrValue.h"
#include "B3FixSSA.h"
#include "B3StackmapGenerationParams.h"
+#include "B3SwitchValue.h"
#include "B3Validate.h"
#include "B3ValueInlines.h"
#include "B3Variable.h"
@@ -175,6 +176,7 @@
bool WARN_UNUSED_RETURN addReturn(const ExpressionList& returnValues);
bool WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues);
+ bool WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTargets, const ExpressionList& expressionStack);
bool WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
bool WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
@@ -191,6 +193,7 @@
void unify(Variable* target, const ExpressionType source);
void unifyValuesWithBlock(const ExpressionList& resultStack, ResultList& stack);
+ Value* zeroForType(Type);
Memory* m_memory;
Procedure& m_proc;
@@ -200,6 +203,7 @@
Vector<UnlinkedCall>& m_unlinkedCalls;
GPRReg m_memoryBaseGPR;
GPRReg m_memorySizeGPR;
+ Value* m_zeroValues[Type::LastValueType];
};
B3IRGenerator::B3IRGenerator(Memory* memory, Procedure& procedure, Vector<UnlinkedCall>& unlinkedCalls)
@@ -209,6 +213,9 @@
{
m_currentBlock = m_proc.addBlock();
+ for (unsigned i = 0; i < Type::LastValueType; ++i)
+ m_zeroValues[i] = m_currentBlock->appendIntConstant(m_proc, Origin(), toB3Type(static_cast<Type>(i + 1)), 0);
+
if (m_memory) {
m_memoryBaseGPR = m_memory->pinnedRegisters().baseMemoryPointer;
m_proc.pinRegister(m_memoryBaseGPR);
@@ -225,13 +232,22 @@
}
}
+Value* B3IRGenerator::zeroForType(Type type)
+{
+ ASSERT(type != Void);
+ return m_zeroValues[type - 1];
+}
+
bool B3IRGenerator::addLocal(Type type, uint32_t count)
{
if (!m_locals.tryReserveCapacity(m_locals.size() + count))
return false;
- for (uint32_t i = 0; i < count; ++i)
- m_locals.uncheckedAppend(m_proc.addVariable(toB3Type(type)));
+ for (uint32_t i = 0; i < count; ++i) {
+ Variable* local = m_proc.addVariable(toB3Type(type));
+ m_locals.uncheckedAppend(local);
+ m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), local, zeroForType(type));
+ }
return true;
}
@@ -545,6 +561,20 @@
return true;
}
+bool B3IRGenerator::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack)
+{
+ for (size_t i = 0; i < targets.size(); ++i)
+ unifyValuesWithBlock(expressionStack, targets[i]->result);
+ unifyValuesWithBlock(expressionStack, defaultTarget.result);
+
+ SwitchValue* switchValue = m_currentBlock->appendNew<SwitchValue>(m_proc, Origin(), condition);
+ switchValue->setFallThrough(FrequentedBlock(defaultTarget.targetBlockForBranch()));
+ for (size_t i = 0; i < targets.size(); ++i)
+ switchValue->appendCase(SwitchCase(i, FrequentedBlock(targets[i]->targetBlockForBranch())));
+
+ return true;
+}
+
bool B3IRGenerator::endBlock(ControlEntry& entry, ExpressionList& expressionStack)
{
ControlData& data = ""
Modified: trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h (208401 => 208402)
--- trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h 2016-11-04 22:12:12 UTC (rev 208401)
+++ trunk/Source/_javascript_Core/wasm/WasmFunctionParser.h 2016-11-04 22:53:21 UTC (rev 208402)
@@ -65,6 +65,8 @@
bool WARN_UNUSED_RETURN popExpressionStack(ExpressionType& result);
+ void setErrorMessage(String&& message) { m_context.setErrorMessage(WTFMove(message)); }
+
Context& m_context;
ExpressionList m_expressionStack;
Vector<ControlEntry> m_controlStack;
@@ -345,7 +347,7 @@
case OpType::Else: {
if (!m_controlStack.size()) {
- m_context.setErrorMessage("Attempted to use else block at the top-level of a function");
+ setErrorMessage("Attempted to use else block at the top-level of a function");
return false;
}
@@ -373,6 +375,44 @@
return m_context.addBranch(data, condition, m_expressionStack);
}
+ case OpType::BrTable: {
+ uint32_t numberOfTargets;
+ if (!parseVarUInt32(numberOfTargets))
+ return false;
+
+ Vector<ControlType*> targets;
+ if (!targets.tryReserveCapacity(numberOfTargets))
+ return false;
+
+ for (uint32_t i = 0; i < numberOfTargets; ++i) {
+ uint32_t target;
+ if (!parseVarUInt32(target))
+ return false;
+
+ if (target >= m_controlStack.size())
+ return false;
+
+ targets.uncheckedAppend(&m_controlStack[m_controlStack.size() - 1 - target].controlData);
+ }
+
+ uint32_t defaultTarget;
+ if (!parseVarUInt32(defaultTarget))
+ return false;
+
+ if (defaultTarget >= m_controlStack.size())
+ return false;
+
+ ExpressionType condition;
+ if (!popExpressionStack(condition))
+ return false;
+
+ if (!m_context.addSwitch(condition, targets, m_controlStack[m_controlStack.size() - 1 - defaultTarget].controlData, m_expressionStack))
+ return false;
+
+ m_unreachableBlocks = 1;
+ return true;
+ }
+
case OpType::Return: {
return addReturn();
}
@@ -394,7 +434,6 @@
}
case OpType::Select:
- case OpType::BrTable:
case OpType::Nop:
case OpType::Drop:
case OpType::TeeLocal:
@@ -453,7 +492,6 @@
}
// one immediate cases
- case OpType::Return:
case OpType::F32Const:
case OpType::I32Const:
case OpType::F64Const:
@@ -478,7 +516,7 @@
return true;
}
- m_context.setErrorMessage("Attempted to use a stack value when none existed");
+ setErrorMessage("Attempted to use a stack value when none existed");
return false;
}
Modified: trunk/Source/_javascript_Core/wasm/WasmValidate.cpp (208401 => 208402)
--- trunk/Source/_javascript_Core/wasm/WasmValidate.cpp 2016-11-04 22:12:12 UTC (rev 208401)
+++ trunk/Source/_javascript_Core/wasm/WasmValidate.cpp 2016-11-04 22:53:21 UTC (rev 208402)
@@ -102,7 +102,8 @@
bool WARN_UNUSED_RETURN addElseToUnreachable(ControlData&);
bool WARN_UNUSED_RETURN addReturn(const ExpressionList& returnValues);
- bool WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues);
+ bool WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& expressionStack);
+ bool WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack);
bool WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
bool WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&);
@@ -122,6 +123,8 @@
bool unify(Type, Type);
bool unify(const ExpressionList&, const ControlData&);
+ bool checkBranchTarget(ControlData& target, const ExpressionList& expressionStack);
+
ExpressionType m_returnType;
Vector<Type> m_locals;
String m_errorMessage;
@@ -223,26 +226,52 @@
return false;
}
+bool Validate::checkBranchTarget(ControlType& target, const ExpressionList& expressionStack)
+ {
+ if (target.type() == BlockType::Loop)
+ return true;
+
+ if (target.signature() == Void)
+ return true;
+
+ if (!expressionStack.size()) {
+ m_errorMessage = makeString("Attempting to branch to block with expected type: ", toString(target.signature()), " but the stack was empty");
+ return false;
+ }
+
+ if (target.signature() == expressionStack.last())
+ return true;
+
+ m_errorMessage = makeString("Attempting to branch to block with expected type: ", toString(target.signature()), " but stack has type: ", toString(target.signature()));
+ return false;
+ }
+
bool Validate::addBranch(ControlType& target, ExpressionType condition, const ExpressionList& stack)
{
- // Void means this is not a conditional branch.
+ // Void means this is an unconditional branch.
if (condition != Void && condition != I32) {
m_errorMessage = makeString("Attempting to add a conditional branch with condition type: ", toString(condition), " but expected i32.");
return false;
}
- if (target.type() == BlockType::Loop)
- return true;
+ return checkBranchTarget(target, stack);
+}
- if (target.signature() == Void)
- return true;
+bool Validate::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack)
+{
+ if (condition != I32) {
+ m_errorMessage = makeString("Attempting to add a br_table with condition type: ", toString(condition), " but expected i32.");
+ return false;
+ }
- ASSERT(stack.size() == 1);
- if (target.signature() == stack[0])
- return true;
+ for (auto target : targets) {
+ if (defaultTarget.signature() != target->signature()) {
+ m_errorMessage = makeString("Attempting to add a br_table with different expected types. Default target has type: ", toString(defaultTarget.signature()), " but case has type: ", toString(target->signature()));
+ return false;
+ }
+ }
- m_errorMessage = makeString("Attempting to branch to block with expected type: ", toString(target.signature()), " but branching with type: ", toString(target.signature()));
- return false;
+ return checkBranchTarget(defaultTarget, expressionStack);
}
bool Validate::endBlock(ControlEntry& entry, ExpressionList& stack)
_______________________________________________ webkit-changes mailing list [email protected] https://lists.webkit.org/mailman/listinfo/webkit-changes
