diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/binaryen-c.cpp | 138 | ||||
-rw-r--r-- | src/binaryen-c.h | 22 | ||||
-rw-r--r-- | src/gen-s-parser.inc | 28 | ||||
-rw-r--r-- | src/ir/ExpressionAnalyzer.cpp | 46 | ||||
-rw-r--r-- | src/ir/ExpressionManipulator.cpp | 14 | ||||
-rw-r--r-- | src/ir/ReFinalize.cpp | 4 | ||||
-rw-r--r-- | src/ir/utils.h | 8 | ||||
-rw-r--r-- | src/js/binaryen.js-post.js | 50 | ||||
-rw-r--r-- | src/passes/DeadCodeElimination.cpp | 4 | ||||
-rw-r--r-- | src/passes/Print.cpp | 48 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 41 | ||||
-rw-r--r-- | src/tools/tool-options.h | 16 | ||||
-rw-r--r-- | src/wasm-binary.h | 13 | ||||
-rw-r--r-- | src/wasm-builder.h | 31 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 24 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 6 | ||||
-rw-r--r-- | src/wasm-stack.h | 61 | ||||
-rw-r--r-- | src/wasm-traversal.h | 49 | ||||
-rw-r--r-- | src/wasm.h | 56 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 68 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 35 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 35 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 32 |
23 files changed, 810 insertions, 19 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 9ed6ad314..36aebb523 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -263,6 +263,10 @@ BinaryenExpressionId BinaryenSIMDReplaceId(void) { return Expression::Id::SIMDRe BinaryenExpressionId BinaryenSIMDShuffleId(void) { return Expression::Id::SIMDShuffleId; } BinaryenExpressionId BinaryenSIMDBitselectId(void) { return Expression::Id::SIMDBitselectId; } BinaryenExpressionId BinaryenSIMDShiftId(void) { return Expression::Id::SIMDShiftId; } +BinaryenExpressionId BinaryenMemoryInitId(void) { return Expression::Id::MemoryInitId; } +BinaryenExpressionId BinaryenDataDropId(void) { return Expression::Id::DataDropId; } +BinaryenExpressionId BinaryenMemoryCopyId(void) { return Expression::Id::MemoryCopyId; } +BinaryenExpressionId BinaryenMemoryFillId(void) { return Expression::Id::MemoryFillId; } // External kinds @@ -1070,6 +1074,37 @@ BinaryenExpressionRef BinaryenSIMDShift(BinaryenModuleRef module, BinaryenOp op, } return static_cast<Expression*>(ret); } +BinaryenExpressionRef BinaryenMemoryInit(BinaryenModuleRef module, uint32_t segment, BinaryenExpressionRef dest, BinaryenExpressionRef offset, BinaryenExpressionRef size) { + auto* ret = Builder(*((Module*)module)).makeMemoryInit(segment, (Expression*)dest, (Expression*)offset, (Expression*)size); + if (tracing) { + traceExpression(ret, "BinaryenMemoryInit", segment, dest, offset, size); + } + return static_cast<Expression*>(ret); +} + +BinaryenExpressionRef BinaryenDataDrop(BinaryenModuleRef module, uint32_t segment) { + auto* ret = Builder(*((Module*)module)).makeDataDrop(segment); + if (tracing) { + traceExpression(ret, "BinaryenDataDrop", segment); + } + return static_cast<Expression*>(ret); +} + +BinaryenExpressionRef BinaryenMemoryCopy(BinaryenModuleRef module, BinaryenExpressionRef dest, BinaryenExpressionRef source, BinaryenExpressionRef size) { + auto* ret = Builder(*((Module*)module)).makeMemoryCopy((Expression*)dest, (Expression*)source, (Expression*)size); + if (tracing) { + traceExpression(ret, "BinaryenMemoryCopy", dest, source, size); + } + return static_cast<Expression*>(ret); +} + +BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module, BinaryenExpressionRef dest, BinaryenExpressionRef value, BinaryenExpressionRef size) { + auto* ret = Builder(*((Module*)module)).makeMemoryFill((Expression*)dest, (Expression*)value, (Expression*)size); + if (tracing) { + traceExpression(ret, "BinaryenMemoryFill", dest, value, size); + } + return static_cast<Expression*>(ret); +} // Expression utility @@ -1969,6 +2004,109 @@ BinaryenExpressionRef BinaryenSIMDShiftGetShift(BinaryenExpressionRef expr) { assert(expression->is<SIMDShift>()); return static_cast<SIMDShift*>(expression)->shift; } +// MemoryInit +uint32_t BinaryenMemoryInitGetSegment(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenMemoryInitGetSegment(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<MemoryInit>()); + return static_cast<MemoryInit*>(expression)->segment; +} +BinaryenExpressionRef BinaryenMemoryInitGetDest(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenMemoryInitGetDest(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<MemoryInit>()); + return static_cast<MemoryInit*>(expression)->dest; +} +BinaryenExpressionRef BinaryenMemoryInitGetOffset(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenMemoryInitGetOffset(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<MemoryInit>()); + return static_cast<MemoryInit*>(expression)->offset; +} +BinaryenExpressionRef BinaryenMemoryInitGetSize(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenMemoryInitGetSize(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<MemoryInit>()); + return static_cast<MemoryInit*>(expression)->size; +} +// DataDrop +uint32_t BinaryenDataDropGetSegment(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenDataDropGetSegment(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<DataDrop>()); + return static_cast<DataDrop*>(expression)->segment; +} +// MemoryCopy +BinaryenExpressionRef BinaryenMemoryCopyGetDest(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenMemoryCopyGetDest(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<MemoryCopy>()); + return static_cast<MemoryCopy*>(expression)->dest; +} +BinaryenExpressionRef BinaryenMemoryCopyGetSource(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenMemoryCopyGetSource(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<MemoryCopy>()); + return static_cast<MemoryCopy*>(expression)->source; +} +BinaryenExpressionRef BinaryenMemoryCopyGetSize(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenMemoryCopyGetSize(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<MemoryCopy>()); + return static_cast<MemoryCopy*>(expression)->size; +} +// MemoryFill +BinaryenExpressionRef BinaryenMemoryFillGetDest(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenMemoryFillGetDest(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<MemoryFill>()); + return static_cast<MemoryFill*>(expression)->dest; +} +BinaryenExpressionRef BinaryenMemoryFillGetValue(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenMemoryFillGetValue(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<MemoryFill>()); + return static_cast<MemoryFill*>(expression)->value; +} +BinaryenExpressionRef BinaryenMemoryFillGetSize(BinaryenExpressionRef expr) { + if (tracing) { + std::cout << " BinaryenMemoryFillGetSize(expressions[" << expressions[expr] << "]);\n"; + } + + auto* expression = (Expression*)expr; + assert(expression->is<MemoryFill>()); + return static_cast<MemoryFill*>(expression)->size; +} // Functions diff --git a/src/binaryen-c.h b/src/binaryen-c.h index 2a3254f7b..5355ceeeb 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -122,6 +122,10 @@ BinaryenExpressionId BinaryenSIMDReplaceId(void); BinaryenExpressionId BinaryenSIMDShuffleId(void); BinaryenExpressionId BinaryenSIMDBitselectId(void); BinaryenExpressionId BinaryenSIMDShiftId(void); +BinaryenExpressionId BinaryenMemoryInitId(void); +BinaryenExpressionId BinaryenDataDropId(void); +BinaryenExpressionId BinaryenMemoryCopyId(void); +BinaryenExpressionId BinaryenMemoryFillId(void); // External kinds (call to get the value of each; you can cache them) @@ -540,6 +544,10 @@ BinaryenExpressionRef BinaryenSIMDReplace(BinaryenModuleRef module, BinaryenOp o BinaryenExpressionRef BinaryenSIMDShuffle(BinaryenModuleRef module, BinaryenExpressionRef left, BinaryenExpressionRef right, const uint8_t mask[16]); BinaryenExpressionRef BinaryenSIMDBitselect(BinaryenModuleRef module, BinaryenExpressionRef left, BinaryenExpressionRef right, BinaryenExpressionRef cond); BinaryenExpressionRef BinaryenSIMDShift(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef vec, BinaryenExpressionRef shift); +BinaryenExpressionRef BinaryenMemoryInit(BinaryenModuleRef module, uint32_t segment, BinaryenExpressionRef dest, BinaryenExpressionRef offset, BinaryenExpressionRef size); +BinaryenExpressionRef BinaryenDataDrop(BinaryenModuleRef module, uint32_t segment); +BinaryenExpressionRef BinaryenMemoryCopy(BinaryenModuleRef module, BinaryenExpressionRef dest, BinaryenExpressionRef source, BinaryenExpressionRef size); +BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module, BinaryenExpressionRef dest, BinaryenExpressionRef value, BinaryenExpressionRef size); BinaryenExpressionId BinaryenExpressionGetId(BinaryenExpressionRef expr); BinaryenType BinaryenExpressionGetType(BinaryenExpressionRef expr); @@ -667,6 +675,20 @@ BinaryenOp BinaryenSIMDShiftGetOp(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenSIMDShiftGetVec(BinaryenExpressionRef expr); BinaryenExpressionRef BinaryenSIMDShiftGetShift(BinaryenExpressionRef expr); +uint32_t BinaryenMemoryInitGetSegment(BinaryenExpressionRef expr); +BinaryenExpressionRef BinaryenMemoryInitGetDest(BinaryenExpressionRef expr); +BinaryenExpressionRef BinaryenMemoryInitGetOffset(BinaryenExpressionRef expr); +BinaryenExpressionRef BinaryenMemoryInitGetSize(BinaryenExpressionRef expr); + +uint32_t BinaryenDataDropGetSegment(BinaryenExpressionRef expr); + +BinaryenExpressionRef BinaryenMemoryCopyGetDest(BinaryenExpressionRef expr); +BinaryenExpressionRef BinaryenMemoryCopyGetSource(BinaryenExpressionRef expr); +BinaryenExpressionRef BinaryenMemoryCopyGetSize(BinaryenExpressionRef expr); + +BinaryenExpressionRef BinaryenMemoryFillGetDest(BinaryenExpressionRef expr); +BinaryenExpressionRef BinaryenMemoryFillGetValue(BinaryenExpressionRef expr); +BinaryenExpressionRef BinaryenMemoryFillGetSize(BinaryenExpressionRef expr); // Functions diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 6db1d7a2c..710f0d829 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -51,9 +51,17 @@ switch (op[0]) { default: goto parse_error; } } - case 'd': - if (strcmp(op, "drop") == 0) return makeDrop(s); - goto parse_error; + case 'd': { + switch (op[1]) { + case 'a': + if (strcmp(op, "data.drop") == 0) return makeDataDrop(s); + goto parse_error; + case 'r': + if (strcmp(op, "drop") == 0) return makeDrop(s); + goto parse_error; + default: goto parse_error; + } + } case 'e': if (strcmp(op, "else") == 0) return makeThenOrElse(s); goto parse_error; @@ -2183,6 +2191,20 @@ switch (op[0]) { default: goto parse_error; } } + case 'm': { + switch (op[7]) { + case 'c': + if (strcmp(op, "memory.copy") == 0) return makeMemoryCopy(s); + goto parse_error; + case 'f': + if (strcmp(op, "memory.fill") == 0) return makeMemoryFill(s); + goto parse_error; + case 'i': + if (strcmp(op, "memory.init") == 0) return makeMemoryInit(s); + goto parse_error; + default: goto parse_error; + } + } case 'n': if (strcmp(op, "nop") == 0) return makeNop(); goto parse_error; diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp index 0efc7b888..cd2946253 100644 --- a/src/ir/ExpressionAnalyzer.cpp +++ b/src/ir/ExpressionAnalyzer.cpp @@ -280,6 +280,29 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, Expr PUSH(SIMDShift, shift); break; } + case Expression::Id::MemoryInitId: { + CHECK(MemoryInit, segment); + PUSH(MemoryInit, dest); + PUSH(MemoryInit, offset); + PUSH(MemoryInit, size); + break; + } + case Expression::Id::DataDropId: { + CHECK(DataDrop, segment); + break; + } + case Expression::Id::MemoryCopyId: { + PUSH(MemoryCopy, dest); + PUSH(MemoryCopy, source); + PUSH(MemoryCopy, size); + break; + } + case Expression::Id::MemoryFillId: { + PUSH(MemoryFill, dest); + PUSH(MemoryFill, value); + PUSH(MemoryFill, size); + break; + } case Expression::Id::ConstId: { if (left->cast<Const>()->value != right->cast<Const>()->value) { return false; @@ -561,6 +584,29 @@ HashType ExpressionAnalyzer::hash(Expression* curr) { PUSH(SIMDShift, shift); break; } + case Expression::Id::MemoryInitId: { + HASH(MemoryInit, segment); + PUSH(MemoryInit, dest); + PUSH(MemoryInit, offset); + PUSH(MemoryInit, size); + break; + } + case Expression::Id::DataDropId: { + HASH(DataDrop, segment); + break; + } + case Expression::Id::MemoryCopyId: { + PUSH(MemoryCopy, dest); + PUSH(MemoryCopy, source); + PUSH(MemoryCopy, size); + break; + } + case Expression::Id::MemoryFillId: { + PUSH(MemoryFill, dest); + PUSH(MemoryFill, value); + PUSH(MemoryFill, size); + break; + } case Expression::Id::ConstId: { auto* c = curr->cast<Const>(); hash(c->type); diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp index 700f7fdb8..32ee442b7 100644 --- a/src/ir/ExpressionManipulator.cpp +++ b/src/ir/ExpressionManipulator.cpp @@ -129,9 +129,21 @@ Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom Expression* visitSIMDShift(SIMDShift* curr) { return builder.makeSIMDShift(curr->op, copy(curr->vec), copy(curr->shift)); } - Expression* visitConst(Const *curr) { + Expression* visitConst(Const* curr) { return builder.makeConst(curr->value); } + Expression* visitMemoryInit(MemoryInit* curr) { + return builder.makeMemoryInit(curr->segment, copy(curr->dest), copy(curr->offset), copy(curr->size)); + } + Expression* visitDataDrop(DataDrop* curr) { + return builder.makeDataDrop(curr->segment); + } + Expression* visitMemoryCopy(MemoryCopy* curr) { + return builder.makeMemoryCopy(copy(curr->dest), copy(curr->source), copy(curr->size)); + } + Expression* visitMemoryFill(MemoryFill* curr) { + return builder.makeMemoryFill(copy(curr->dest), copy(curr->value), copy(curr->size)); + } Expression* visitUnary(Unary *curr) { return builder.makeUnary(curr->op, copy(curr->value)); } diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 68526678a..3f374265c 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -142,6 +142,10 @@ void ReFinalize::visitSIMDReplace(SIMDReplace* curr) { curr->finalize(); } void ReFinalize::visitSIMDShuffle(SIMDShuffle* curr) { curr->finalize(); } void ReFinalize::visitSIMDBitselect(SIMDBitselect* curr) { curr->finalize(); } void ReFinalize::visitSIMDShift(SIMDShift* curr) { curr->finalize(); } +void ReFinalize::visitMemoryInit(MemoryInit* curr) { curr->finalize(); } +void ReFinalize::visitDataDrop(DataDrop* curr) { curr->finalize(); } +void ReFinalize::visitMemoryCopy(MemoryCopy* curr) { curr->finalize(); } +void ReFinalize::visitMemoryFill(MemoryFill* curr) { curr->finalize(); } void ReFinalize::visitConst(Const* curr) { curr->finalize(); } void ReFinalize::visitUnary(Unary* curr) { curr->finalize(); } void ReFinalize::visitBinary(Binary* curr) { curr->finalize(); } diff --git a/src/ir/utils.h b/src/ir/utils.h index afb63b01c..85c485552 100644 --- a/src/ir/utils.h +++ b/src/ir/utils.h @@ -134,6 +134,10 @@ struct ReFinalize : public WalkerPass<PostWalker<ReFinalize, OverriddenVisitor<R void visitSIMDShuffle(SIMDShuffle* curr); void visitSIMDBitselect(SIMDBitselect* curr); void visitSIMDShift(SIMDShift* curr); + void visitMemoryInit(MemoryInit* curr); + void visitDataDrop(DataDrop* curr); + void visitMemoryCopy(MemoryCopy* curr); + void visitMemoryFill(MemoryFill* curr); void visitConst(Const* curr); void visitUnary(Unary* curr); void visitBinary(Binary* curr); @@ -186,6 +190,10 @@ struct ReFinalizeNode : public OverriddenVisitor<ReFinalizeNode> { void visitSIMDShuffle(SIMDShuffle* curr) { curr->finalize(); } void visitSIMDBitselect(SIMDBitselect* curr) { curr->finalize(); } void visitSIMDShift(SIMDShift* curr) { curr->finalize(); } + void visitMemoryInit(MemoryInit* curr) { curr->finalize(); } + void visitDataDrop(DataDrop* curr) { curr->finalize(); } + void visitMemoryCopy(MemoryCopy* curr) { curr->finalize(); } + void visitMemoryFill(MemoryFill* curr) { curr->finalize(); } void visitConst(Const* curr) { curr->finalize(); } void visitUnary(Unary* curr) { curr->finalize(); } void visitBinary(Binary* curr) { curr->finalize(); } diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index aa2e613ce..b2dba075b 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -73,6 +73,10 @@ Module['SIMDReplaceId'] = Module['_BinaryenSIMDReplaceId'](); Module['SIMDShuffleId'] = Module['_BinaryenSIMDShuffleId'](); Module['SIMDBitselectId'] = Module['_BinaryenSIMDBitselectId'](); Module['SIMDShiftId'] = Module['_BinaryenSIMDShiftId'](); +Module['MemoryInitId'] = Module['_BinaryenMemoryInitId'](); +Module['DataDropId'] = Module['_BinaryenDataDropId'](); +Module['MemoryCopyId'] = Module['_BinaryenMemoryCopyId'](); +Module['MemoryFillId'] = Module['_BinaryenMemoryFillId'](); // External kinds Module['ExternalFunction'] = Module['_BinaryenExternalFunction'](); @@ -460,6 +464,24 @@ function wrapModule(module, self) { return Module['_BinaryenHost'](module, Module['GrowMemory'], null, i32sToStack([value]), 1); } + self['memory'] = { + 'init': function(segment, dest, offset, size) { + return Module['_BinaryenMemoryInit'](module, segment, dest, offset, size); + }, + 'copy': function(dest, source, size) { + return Module['_BinaryenMemoryCopy'](module, dest, source, size); + }, + 'fill': function(dest, value, size) { + return Module['_BinaryenMemoryFill'](module, dest, value, size); + } + } + + self['data'] = { + 'drop': function(segment) { + return Module['_BinaryenDataDrop'](module, segment); + } + } + // The Const creation API is a little different: we don't want users to // need to make their own Literals, as the C API handles them by value, // which means we would leak them. Instead, this is the only API that @@ -2211,6 +2233,34 @@ Module['getExpressionInfo'] = function(expr) { 'vec': Module['_BinaryenSIMDShiftGetVec'](expr), 'shift': Module['_BinaryenSIMDShiftGetShift'](expr) }; + case Module['MemoryInitId']: + return { + 'id': id, + 'segment': Module['_BinaryenMemoryInitGetSegment'](expr), + 'dest': Module['_BinaryenMemoryInitGetDest'](expr), + 'offset': Module['_BinaryenMemoryInitGetOffset'](expr), + 'size': Module['_BinaryenMemoryInitGetSize'](expr) + }; + case Module['DataDropId']: + return { + 'id': id, + 'segment': Module['_BinaryenDataDropGetSegment'](expr), + }; + case Module['MemoryCopyId']: + return { + 'id': id, + 'dest': Module['_BinaryenMemoryCopyGetDest'](expr), + 'source': Module['_BinaryenMemoryCopyGetSource'](expr), + 'size': Module['_BinaryenMemoryCopyGetSize'](expr) + }; + case Module['MemoryFillId']: + return { + 'id': id, + 'dest': Module['_BinaryenMemoryFillGetDest'](expr), + 'value': Module['_BinaryenMemoryFillGetValue'](expr), + 'size': Module['_BinaryenMemoryFillGetSize'](expr) + }; + default: throw Error('unexpected id: ' + id); } diff --git a/src/passes/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp index 6e70fc55d..a6b20a7ba 100644 --- a/src/passes/DeadCodeElimination.cpp +++ b/src/passes/DeadCodeElimination.cpp @@ -262,6 +262,10 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>> case Expression::Id::SIMDShuffleId: DELEGATE(SIMDShuffle); case Expression::Id::SIMDBitselectId: DELEGATE(SIMDBitselect); case Expression::Id::SIMDShiftId: DELEGATE(SIMDShift); + case Expression::Id::MemoryInitId: DELEGATE(MemoryInit); + case Expression::Id::DataDropId: DELEGATE(DataDrop); + case Expression::Id::MemoryCopyId: DELEGATE(MemoryCopy); + case Expression::Id::MemoryFillId: DELEGATE(MemoryFill); case Expression::Id::InvalidId: WASM_UNREACHABLE(); case Expression::Id::NumExpressionIds: WASM_UNREACHABLE(); } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 3de8b5c01..761c9e3d4 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -293,6 +293,22 @@ struct PrintExpressionContents : public Visitor<PrintExpressionContents> { case ShrUVecI64x2: o << "i64x2.shr_u"; break; } } + void visitMemoryInit(MemoryInit* curr) { + prepareColor(o); + o << "memory.init " << curr->segment; + } + void visitDataDrop(DataDrop* curr) { + prepareColor(o); + o << "data.drop " << curr->segment; + } + void visitMemoryCopy(MemoryCopy* curr) { + prepareColor(o); + o << "memory.copy"; + } + void visitMemoryFill(MemoryFill* curr) { + prepareColor(o); + o << "memory.fill"; + } void visitConst(Const* curr) { o << curr->value; } @@ -936,6 +952,38 @@ struct PrintSExpression : public Visitor<PrintSExpression> { printFullLine(curr->shift); decIndent(); } + void visitMemoryInit(MemoryInit* curr) { + o << '('; + PrintExpressionContents(currFunction, o).visit(curr); + incIndent(); + printFullLine(curr->dest); + printFullLine(curr->offset); + printFullLine(curr->size); + decIndent(); + } + void visitDataDrop(DataDrop* curr) { + o << '('; + PrintExpressionContents(currFunction, o).visit(curr); + o << ')'; + } + void visitMemoryCopy(MemoryCopy* curr) { + o << '('; + PrintExpressionContents(currFunction, o).visit(curr); + incIndent(); + printFullLine(curr->dest); + printFullLine(curr->source); + printFullLine(curr->size); + decIndent(); + } + void visitMemoryFill(MemoryFill* curr) { + o << '('; + PrintExpressionContents(currFunction, o).visit(curr); + incIndent(); + printFullLine(curr->dest); + printFullLine(curr->value); + printFullLine(curr->size); + decIndent(); + } void visitConst(Const* curr) { o << '('; PrintExpressionContents(currFunction, o).visit(curr); diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 56c633f14..63e7dca43 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -727,6 +727,9 @@ private: &Self::makeSelect, &Self::makeGetGlobal) .add(FeatureSet::SIMD, &Self::makeSIMD); + if (type == none) { + options.add(FeatureSet::BulkMemory, &Self::makeBulkMemory); + } if (type == i32 || type == i64) { options.add(FeatureSet::Atomics, &Self::makeAtomic); } @@ -1713,6 +1716,44 @@ private: return builder.makeSIMDShift(op, vec, shift); } + Expression* makeBulkMemory(Type type) { + assert(features.hasBulkMemory()); + assert(type == none); + switch (upTo(4)) { + case 0: return makeMemoryInit(); + case 1: return makeDataDrop(); + case 2: return makeMemoryCopy(); + case 3: return makeMemoryFill(); + } + WASM_UNREACHABLE(); + } + + Expression* makeMemoryInit() { + auto segment = uint32_t(get32()); + Expression* dest = make(i32); + Expression* offset = make(i32); + Expression* size = make(i32); + return builder.makeMemoryInit(segment, dest, offset, size); + } + + Expression* makeDataDrop() { + return builder.makeDataDrop(get32()); + } + + Expression* makeMemoryCopy() { + Expression* dest = make(i32); + Expression* source = make(i32); + Expression* size = make(i32); + return builder.makeMemoryCopy(dest, source, size); + } + + Expression* makeMemoryFill() { + Expression* dest = make(i32); + Expression* value = make(i32); + Expression* size = make(i32); + return builder.makeMemoryFill(dest, value, size); + } + // special makers Expression* makeLogging() { diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h index 9aff8e619..5620883ec 100644 --- a/src/tools/tool-options.h +++ b/src/tools/tool-options.h @@ -72,17 +72,29 @@ struct ToolOptions : public Options { passOptions.features.setTruncSat(false); }) .add("--enable-simd", "", - "Enable nontrapping float-to-int operations", + "Enable SIMD operations and types", Options::Arguments::Zero, [this](Options *o, const std::string& arguments) { passOptions.features.setSIMD(); }) .add("--disable-simd", "", - "Disable nontrapping float-to-int operations", + "Disable SIMD operations and types", Options::Arguments::Zero, [this](Options *o, const std::string& arguments) { passOptions.features.setSIMD(false); }) + .add("--enable-bulk-memory", "", + "Enable bulk memory operations", + Options::Arguments::Zero, + [this](Options *o, const std::string& arguments) { + passOptions.features.setBulkMemory(); + }) + .add("--disable-bulk-memory", "", + "Disable bulk memory operations", + Options::Arguments::Zero, + [this](Options *o, const std::string& arguments) { + passOptions.features.setBulkMemory(false); + }) .add("--no-validation", "-n", "Disables validation, assumes inputs are correct", Options::Arguments::Zero, [this](Options* o, const std::string& argument) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 2402375dc..a875db791 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -550,7 +550,7 @@ enum ASTNodes { I64ExtendS16 = 0xc3, I64ExtendS32 = 0xc4, - TruncSatPrefix = 0xfc, + MiscPrefix = 0xfc, SIMDPrefix = 0xfd, AtomicPrefix = 0xfe }; @@ -785,6 +785,13 @@ enum SIMDOpcodes { F64x2ConvertUI64x2 = 0xb2 }; +enum BulkMemoryOpcodes { + MemoryInit = 0x08, + DataDrop = 0x09, + MemoryCopy = 0x0a, + MemoryFill = 0x0b +}; + enum MemoryAccess { Offset = 0x10, // bit 4 Alignment = 0x80, // bit 7 @@ -1115,6 +1122,10 @@ public: bool maybeVisitSIMDShuffle(Expression*& out, uint32_t code); bool maybeVisitSIMDBitselect(Expression*& out, uint32_t code); bool maybeVisitSIMDShift(Expression*& out, uint32_t code); + bool maybeVisitMemoryInit(Expression*& out, uint32_t code); + bool maybeVisitDataDrop(Expression*& out, uint32_t code); + bool maybeVisitMemoryCopy(Expression*& out, uint32_t code); + bool maybeVisitMemoryFill(Expression*& out, uint32_t code); void visitSelect(Select* curr); void visitReturn(Return* curr); bool maybeVisitHost(Expression*& out, uint8_t code); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index f182b1df2..eee4e3b79 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -334,6 +334,37 @@ public: ret->finalize(); return ret; } + MemoryInit* makeMemoryInit(uint32_t segment, Expression* dest, Expression* offset, Expression* size) { + auto* ret = allocator.alloc<MemoryInit>(); + ret->segment = segment; + ret->dest = dest; + ret->offset = offset; + ret->size = size; + ret->finalize(); + return ret; + } + DataDrop* makeDataDrop(uint32_t segment) { + auto* ret = allocator.alloc<DataDrop>(); + ret->segment = segment; + ret->finalize(); + return ret; + } + MemoryCopy* makeMemoryCopy(Expression* dest, Expression* source, Expression* size) { + auto* ret = allocator.alloc<MemoryCopy>(); + ret->dest = dest; + ret->source = source; + ret->size = size; + ret->finalize(); + return ret; + } + MemoryFill* makeMemoryFill(Expression* dest, Expression* value, Expression* size) { + auto* ret = allocator.alloc<MemoryFill>(); + ret->dest = dest; + ret->value = value; + ret->size = size; + ret->finalize(); + return ret; + } Const* makeConst(Literal value) { assert(isConcreteType(value.type)); auto* ret = allocator.alloc<Const>(); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index b10f3087f..c584f0ea7 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -727,6 +727,10 @@ public: Flow visitLoad(Load *curr) { WASM_UNREACHABLE(); } Flow visitStore(Store *curr) { WASM_UNREACHABLE(); } Flow visitHost(Host *curr) { WASM_UNREACHABLE(); } + Flow visitMemoryInit(MemoryInit *curr) { WASM_UNREACHABLE(); } + Flow visitDataDrop(DataDrop *curr) { WASM_UNREACHABLE(); } + Flow visitMemoryCopy(MemoryCopy *curr) { WASM_UNREACHABLE(); } + Flow visitMemoryFill(MemoryFill *curr) { WASM_UNREACHABLE(); } }; // @@ -1152,6 +1156,26 @@ public: } WASM_UNREACHABLE(); } + Flow visitMemoryInit(MemoryInit *curr) { + NOTE_ENTER("MemoryInit"); + // TODO(tlively): implement me + return {}; + } + Flow visitDataDrop(DataDrop *curr) { + NOTE_ENTER("DataDrop"); + // TODO(tlively): implement me + return {}; + } + Flow visitMemoryCopy(MemoryCopy *curr) { + NOTE_ENTER("MemoryCopy"); + // TODO(tlively): implement me + return {}; + } + Flow visitMemoryFill(MemoryFill *curr) { + NOTE_ENTER("MemoryFill"); + // TODO(tlively): implement me + return {}; + } void trap(const char* why) override { instance.externalInterface->trap(why); diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index ef6e8d36b..f0ed79409 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -195,7 +195,11 @@ private: Expression* makeSIMDReplace(Element& s, SIMDReplaceOp op, size_t lanes); Expression* makeSIMDShuffle(Element& s); Expression* makeSIMDBitselect(Element& s); - Expression* makeSIMDShift(Element& s, SIMDShiftOp); + Expression* makeSIMDShift(Element& s, SIMDShiftOp op); + Expression* makeMemoryInit(Element& s); + Expression* makeDataDrop(Element& s); + Expression* makeMemoryCopy(Element& s); + Expression* makeMemoryFill(Element& s); Expression* makeIf(Element& s); Expression* makeMaybeBlock(Element& s, size_t i, Type type); Expression* makeLoop(Element& s); diff --git a/src/wasm-stack.h b/src/wasm-stack.h index c64fd2759..7419d5dca 100644 --- a/src/wasm-stack.h +++ b/src/wasm-stack.h @@ -140,6 +140,10 @@ public: void visitSIMDShuffle(SIMDShuffle* curr); void visitSIMDBitselect(SIMDBitselect* curr); void visitSIMDShift(SIMDShift* curr); + void visitMemoryInit(MemoryInit* curr); + void visitDataDrop(DataDrop* curr); + void visitMemoryCopy(MemoryCopy* curr); + void visitMemoryFill(MemoryFill* curr); void visitConst(Const* curr); void visitUnary(Unary* curr); void visitBinary(Binary* curr); @@ -955,6 +959,47 @@ void StackWriter<Mode, Parent>::visitSIMDShift(SIMDShift* curr) { } template<StackWriterMode Mode, typename Parent> +void StackWriter<Mode, Parent>::visitMemoryInit(MemoryInit* curr) { + visitChild(curr->dest); + visitChild(curr->offset); + visitChild(curr->size); + if (justAddToStack(curr)) return; + o << int8_t(BinaryConsts::MiscPrefix); + o << U32LEB(BinaryConsts::MemoryInit); + o << U32LEB(curr->segment) << int8_t(0); +} + +template<StackWriterMode Mode, typename Parent> +void StackWriter<Mode, Parent>::visitDataDrop(DataDrop* curr) { + if (justAddToStack(curr)) return; + o << int8_t(BinaryConsts::MiscPrefix); + o << U32LEB(BinaryConsts::DataDrop); + o << U32LEB(curr->segment); +} + +template<StackWriterMode Mode, typename Parent> +void StackWriter<Mode, Parent>::visitMemoryCopy(MemoryCopy* curr) { + visitChild(curr->dest); + visitChild(curr->source); + visitChild(curr->size); + if (justAddToStack(curr)) return; + o << int8_t(BinaryConsts::MiscPrefix); + o << U32LEB(BinaryConsts::MemoryCopy); + o << int8_t(0) << int8_t(0); +} + +template<StackWriterMode Mode, typename Parent> +void StackWriter<Mode, Parent>::visitMemoryFill(MemoryFill* curr) { + visitChild(curr->dest); + visitChild(curr->value); + visitChild(curr->size); + if (justAddToStack(curr)) return; + o << int8_t(BinaryConsts::MiscPrefix); + o << U32LEB(BinaryConsts::MemoryFill); + o << int8_t(0); +} + +template<StackWriterMode Mode, typename Parent> void StackWriter<Mode, Parent>::visitConst(Const* curr) { if (debug) std::cerr << "zz node: Const" << curr << " : " << curr->type << std::endl; if (justAddToStack(curr)) return; @@ -1052,14 +1097,14 @@ void StackWriter<Mode, Parent>::visitUnary(Unary* curr) { case ExtendS8Int64: o << int8_t(BinaryConsts::I64ExtendS8); break; case ExtendS16Int64: o << int8_t(BinaryConsts::I64ExtendS16); break; case ExtendS32Int64: o << int8_t(BinaryConsts::I64ExtendS32); break; - case TruncSatSFloat32ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32STruncSatF32); break; - case TruncSatUFloat32ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32UTruncSatF32); break; - case TruncSatSFloat64ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32STruncSatF64); break; - case TruncSatUFloat64ToInt32: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I32UTruncSatF64); break; - case TruncSatSFloat32ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64STruncSatF32); break; - case TruncSatUFloat32ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64UTruncSatF32); break; - case TruncSatSFloat64ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64STruncSatF64); break; - case TruncSatUFloat64ToInt64: o << int8_t(BinaryConsts::TruncSatPrefix) << U32LEB(BinaryConsts::I64UTruncSatF64); break; + case TruncSatSFloat32ToInt32: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I32STruncSatF32); break; + case TruncSatUFloat32ToInt32: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I32UTruncSatF32); break; + case TruncSatSFloat64ToInt32: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I32STruncSatF64); break; + case TruncSatUFloat64ToInt32: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I32UTruncSatF64); break; + case TruncSatSFloat32ToInt64: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I64STruncSatF32); break; + case TruncSatUFloat32ToInt64: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I64UTruncSatF32); break; + case TruncSatSFloat64ToInt64: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I64STruncSatF64); break; + case TruncSatUFloat64ToInt64: o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::I64UTruncSatF64); break; case SplatVecI8x16: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I8x16Splat); break; case SplatVecI16x8: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I16x8Splat); break; case SplatVecI32x4: o << int8_t(BinaryConsts::SIMDPrefix) << U32LEB(BinaryConsts::I32x4Splat); break; diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index fe3e3c5dd..f569da888 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -59,6 +59,10 @@ struct Visitor { ReturnType visitSIMDShuffle(SIMDShuffle* curr) { return ReturnType(); } ReturnType visitSIMDBitselect(SIMDBitselect* curr) { return ReturnType(); } ReturnType visitSIMDShift(SIMDShift* curr) { return ReturnType(); } + ReturnType visitMemoryInit(MemoryInit* curr) { return ReturnType(); } + ReturnType visitDataDrop(DataDrop* curr) { return ReturnType(); } + ReturnType visitMemoryCopy(MemoryCopy* curr) { return ReturnType(); } + ReturnType visitMemoryFill(MemoryFill* curr) { return ReturnType(); } ReturnType visitConst(Const* curr) { return ReturnType(); } ReturnType visitUnary(Unary* curr) { return ReturnType(); } ReturnType visitBinary(Binary* curr) { return ReturnType(); } @@ -107,6 +111,10 @@ struct Visitor { case Expression::Id::SIMDShuffleId: DELEGATE(SIMDShuffle); case Expression::Id::SIMDBitselectId: DELEGATE(SIMDBitselect); case Expression::Id::SIMDShiftId: DELEGATE(SIMDShift); + case Expression::Id::MemoryInitId: DELEGATE(MemoryInit); + case Expression::Id::DataDropId: DELEGATE(DataDrop); + case Expression::Id::MemoryCopyId: DELEGATE(MemoryCopy); + case Expression::Id::MemoryFillId: DELEGATE(MemoryFill); case Expression::Id::ConstId: DELEGATE(Const); case Expression::Id::UnaryId: DELEGATE(Unary); case Expression::Id::BinaryId: DELEGATE(Binary); @@ -157,6 +165,10 @@ struct OverriddenVisitor { UNIMPLEMENTED(SIMDShuffle); UNIMPLEMENTED(SIMDBitselect); UNIMPLEMENTED(SIMDShift); + UNIMPLEMENTED(MemoryInit); + UNIMPLEMENTED(DataDrop); + UNIMPLEMENTED(MemoryCopy); + UNIMPLEMENTED(MemoryFill); UNIMPLEMENTED(Const); UNIMPLEMENTED(Unary); UNIMPLEMENTED(Binary); @@ -206,6 +218,10 @@ struct OverriddenVisitor { case Expression::Id::SIMDShuffleId: DELEGATE(SIMDShuffle); case Expression::Id::SIMDBitselectId: DELEGATE(SIMDBitselect); case Expression::Id::SIMDShiftId: DELEGATE(SIMDShift); + case Expression::Id::MemoryInitId: DELEGATE(MemoryInit); + case Expression::Id::DataDropId: DELEGATE(DataDrop); + case Expression::Id::MemoryCopyId: DELEGATE(MemoryCopy); + case Expression::Id::MemoryFillId: DELEGATE(MemoryFill); case Expression::Id::ConstId: DELEGATE(Const); case Expression::Id::UnaryId: DELEGATE(Unary); case Expression::Id::BinaryId: DELEGATE(Binary); @@ -254,6 +270,10 @@ struct UnifiedExpressionVisitor : public Visitor<SubType, ReturnType> { ReturnType visitSIMDShuffle(SIMDShuffle* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitSIMDBitselect(SIMDBitselect* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitSIMDShift(SIMDShift* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } + ReturnType visitMemoryInit(MemoryInit* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } + ReturnType visitDataDrop(DataDrop* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } + ReturnType visitMemoryCopy(MemoryCopy* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } + ReturnType visitMemoryFill(MemoryFill* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitConst(Const* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitUnary(Unary* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitBinary(Binary* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } @@ -444,6 +464,10 @@ struct Walker : public VisitorType { static void doVisitSIMDShuffle(SubType* self, Expression** currp) { self->visitSIMDShuffle((*currp)->cast<SIMDShuffle>()); } static void doVisitSIMDBitselect(SubType* self, Expression** currp) { self->visitSIMDBitselect((*currp)->cast<SIMDBitselect>()); } static void doVisitSIMDShift(SubType* self, Expression** currp) { self->visitSIMDShift((*currp)->cast<SIMDShift>()); } + static void doVisitMemoryInit(SubType* self, Expression** currp) { self->visitMemoryInit((*currp)->cast<MemoryInit>()); } + static void doVisitDataDrop(SubType* self, Expression** currp) { self->visitDataDrop((*currp)->cast<DataDrop>()); } + static void doVisitMemoryCopy(SubType* self, Expression** currp) { self->visitMemoryCopy((*currp)->cast<MemoryCopy>()); } + static void doVisitMemoryFill(SubType* self, Expression** currp) { self->visitMemoryFill((*currp)->cast<MemoryFill>()); } static void doVisitConst(SubType* self, Expression** currp) { self->visitConst((*currp)->cast<Const>()); } static void doVisitUnary(SubType* self, Expression** currp) { self->visitUnary((*currp)->cast<Unary>()); } static void doVisitBinary(SubType* self, Expression** currp) { self->visitBinary((*currp)->cast<Binary>()); } @@ -614,6 +638,31 @@ struct PostWalker : public Walker<SubType, VisitorType> { self->pushTask(SubType::scan, &curr->cast<SIMDShift>()->vec); break; } + case Expression::Id::MemoryInitId: { + self->pushTask(SubType::doVisitMemoryInit, currp); + self->pushTask(SubType::scan, &curr->cast<MemoryInit>()->dest); + self->pushTask(SubType::scan, &curr->cast<MemoryInit>()->offset); + self->pushTask(SubType::scan, &curr->cast<MemoryInit>()->size); + break; + } + case Expression::Id::DataDropId: { + self->pushTask(SubType::doVisitDataDrop, currp); + break; + } + case Expression::Id::MemoryCopyId: { + self->pushTask(SubType::doVisitMemoryCopy, currp); + self->pushTask(SubType::scan, &curr->cast<MemoryCopy>()->dest); + self->pushTask(SubType::scan, &curr->cast<MemoryCopy>()->source); + self->pushTask(SubType::scan, &curr->cast<MemoryCopy>()->size); + break; + } + case Expression::Id::MemoryFillId: { + self->pushTask(SubType::doVisitMemoryFill, currp); + self->pushTask(SubType::scan, &curr->cast<MemoryFill>()->dest); + self->pushTask(SubType::scan, &curr->cast<MemoryFill>()->value); + self->pushTask(SubType::scan, &curr->cast<MemoryFill>()->size); + break; + } case Expression::Id::ConstId: { self->pushTask(SubType::doVisitConst, currp); break; diff --git a/src/wasm.h b/src/wasm.h index dc24b0f1c..0370f8ae8 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -45,7 +45,8 @@ struct FeatureSet { MutableGlobals = 1 << 1, TruncSat = 1 << 2, SIMD = 1 << 3, - All = Atomics | MutableGlobals | TruncSat | SIMD + BulkMemory = 1 << 4, + All = Atomics | MutableGlobals | TruncSat | SIMD | BulkMemory }; FeatureSet() : features(MVP) {} @@ -57,6 +58,7 @@ struct FeatureSet { bool hasMutableGlobals() const { return features & MutableGlobals; } bool hasTruncSat() const { return features & TruncSat; } bool hasSIMD() const { return features & SIMD; } + bool hasBulkMemory() const { return features & BulkMemory; } bool hasAll() const { return features & All; } void makeMVP() { features = MVP; } @@ -65,6 +67,7 @@ struct FeatureSet { void setMutableGlobals(bool v = true) { set(MutableGlobals, v); } void setTruncSat(bool v = true) { set(TruncSat, v); } void setSIMD(bool v = true) { set(SIMD, v); } + void setBulkMemory(bool v = true) { set(BulkMemory, v); } void setAll(bool v = true) { features = v ? All : MVP; } bool operator<=(const FeatureSet& other) { @@ -255,6 +258,10 @@ public: SIMDShuffleId, SIMDBitselectId, SIMDShiftId, + MemoryInitId, + DataDropId, + MemoryCopyId, + MemoryFillId, NumExpressionIds }; Id _id; @@ -613,6 +620,53 @@ class SIMDShift : public SpecificExpression<Expression::SIMDShiftId> { void finalize(); }; +class MemoryInit : public SpecificExpression<Expression::MemoryInitId> { + public: + MemoryInit() = default; + MemoryInit(MixedArena& allocator) : MemoryInit() {} + + uint32_t segment; + Expression* dest; + Expression* offset; + Expression* size; + + void finalize(); +}; + +class DataDrop : public SpecificExpression<Expression::DataDropId> { + public: + DataDrop() = default; + DataDrop(MixedArena& allocator) : DataDrop() {} + + uint32_t segment; + + void finalize(); +}; + +class MemoryCopy : public SpecificExpression<Expression::MemoryCopyId> { + public: + MemoryCopy() = default; + MemoryCopy(MixedArena& allocator) : MemoryCopy() {} + + Expression* dest; + Expression* source; + Expression* size; + + void finalize(); +}; + +class MemoryFill : public SpecificExpression<Expression::MemoryFillId> { + public: + MemoryFill() = default; + MemoryFill(MixedArena& allocator) : MemoryFill() {} + + Expression* dest; + Expression* value; + Expression* size; + + void finalize(); +}; + class Const : public SpecificExpression<Expression::ConstId> { public: Const() = default; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 90a588bb9..721884126 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1715,10 +1715,14 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { throwError("invalid code after atomic prefix: " + std::to_string(code)); break; } - case BinaryConsts::TruncSatPrefix: { + case BinaryConsts::MiscPrefix: { auto opcode = getU32LEB(); if (maybeVisitTruncSat(curr, opcode)) break; - throwError("invalid code after nontrapping float-to-int prefix: " + std::to_string(code)); + if (maybeVisitMemoryInit(curr, opcode)) break; + if (maybeVisitDataDrop(curr, opcode)) break; + if (maybeVisitMemoryCopy(curr, opcode)) break; + if (maybeVisitMemoryFill(curr, opcode)) break; + throwError("invalid code after nontrapping float-to-int prefix: " + std::to_string(opcode)); break; } case BinaryConsts::SIMDPrefix: { @@ -2342,6 +2346,66 @@ bool WasmBinaryBuilder::maybeVisitTruncSat(Expression*& out, uint32_t code) { return true; } +bool WasmBinaryBuilder::maybeVisitMemoryInit(Expression*& out, uint32_t code) { + if (code != BinaryConsts::MemoryInit) { + return false; + } + auto* curr = allocator.alloc<MemoryInit>(); + curr->size = popNonVoidExpression(); + curr->offset = popNonVoidExpression(); + curr->dest = popNonVoidExpression(); + curr->segment = getU32LEB(); + if (getInt8() != 0) { + throwError("Unexpected nonzero memory index"); + } + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitDataDrop(Expression*& out, uint32_t code) { + if (code != BinaryConsts::DataDrop) { + return false; + } + auto* curr = allocator.alloc<DataDrop>(); + curr->segment = getU32LEB(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitMemoryCopy(Expression*& out, uint32_t code) { + if (code != BinaryConsts::MemoryCopy) { + return false; + } + auto* curr = allocator.alloc<MemoryCopy>(); + curr->size = popNonVoidExpression(); + curr->source = popNonVoidExpression(); + curr->dest = popNonVoidExpression(); + if (getInt8() != 0 || getInt8() != 0) { + throwError("Unexpected nonzero memory index"); + } + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitMemoryFill(Expression*& out, uint32_t code) { + if (code != BinaryConsts::MemoryFill) { + return false; + } + auto* curr = allocator.alloc<MemoryFill>(); + curr->size = popNonVoidExpression(); + curr->value = popNonVoidExpression(); + curr->dest = popNonVoidExpression(); + if (getInt8() != 0) { + throwError("Unexpected nonzero memory index"); + } + curr->finalize(); + out = curr; + return true; +} + bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) { Binary* curr; #define INT_TYPED_CODE(code) { \ diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index eca4c7493..09cc044bf 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1134,6 +1134,41 @@ Expression* SExpressionWasmBuilder::makeSIMDShift(Element& s, SIMDShiftOp op) { return ret; } +Expression* SExpressionWasmBuilder::makeMemoryInit(Element& s) { + auto ret = allocator.alloc<MemoryInit>(); + ret->segment = atoi(s[1]->str().c_str()); + ret->dest = parseExpression(s[2]); + ret->offset = parseExpression(s[3]); + ret->size = parseExpression(s[4]); + ret->finalize(); + return ret; +} + +Expression* SExpressionWasmBuilder::makeDataDrop(Element& s) { + auto ret = allocator.alloc<DataDrop>(); + ret->segment = atoi(s[1]->str().c_str()); + ret->finalize(); + return ret; +} + +Expression* SExpressionWasmBuilder::makeMemoryCopy(Element& s) { + auto ret = allocator.alloc<MemoryCopy>(); + ret->dest = parseExpression(s[1]); + ret->source = parseExpression(s[2]); + ret->size = parseExpression(s[3]); + ret->finalize(); + return ret; +} + +Expression* SExpressionWasmBuilder::makeMemoryFill(Element& s) { + auto ret = allocator.alloc<MemoryFill>(); + ret->dest = parseExpression(s[1]); + ret->value = parseExpression(s[2]); + ret->size = parseExpression(s[3]); + ret->finalize(); + return ret; +} + Expression* SExpressionWasmBuilder::makeIf(Element& s) { auto ret = allocator.alloc<If>(); Index i = 1; diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index f1ccb2cfa..84a0efbff 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -250,6 +250,10 @@ public: void visitSIMDShuffle(SIMDShuffle* curr); void visitSIMDBitselect(SIMDBitselect* curr); void visitSIMDShift(SIMDShift* curr); + void visitMemoryInit(MemoryInit* curr); + void visitDataDrop(DataDrop* curr); + void visitMemoryCopy(MemoryCopy* curr); + void visitMemoryFill(MemoryFill* curr); void visitBinary(Binary* curr); void visitUnary(Unary* curr); void visitSelect(Select* curr); @@ -636,6 +640,37 @@ void FunctionValidator::visitSIMDShift(SIMDShift* curr) { shouldBeEqualOrFirstIsUnreachable(curr->shift->type, i32, curr, "expected shift amount to have type i32"); } +void FunctionValidator::visitMemoryInit(MemoryInit* curr) { + shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)"); + shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.init must have type none"); + shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.init dest must be an i32"); + shouldBeEqualOrFirstIsUnreachable(curr->offset->type, i32, curr, "memory.init offset must be an i32"); + shouldBeEqualOrFirstIsUnreachable(curr->size->type, i32, curr, "memory.init size must be an i32"); + shouldBeTrue(curr->segment < getModule()->memory.segments.size(), curr, "memory.init segment index out of bounds"); +} + +void FunctionValidator::visitDataDrop(DataDrop* curr) { + shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)"); + shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "data.drop must have type none"); + shouldBeTrue(curr->segment < getModule()->memory.segments.size(), curr, "data.drop segment index out of bounds"); +} + +void FunctionValidator::visitMemoryCopy(MemoryCopy* curr) { + shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)"); + shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.copy must have type none"); + shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.copy dest must be an i32"); + shouldBeEqualOrFirstIsUnreachable(curr->source->type, i32, curr, "memory.copy source must be an i32"); + shouldBeEqualOrFirstIsUnreachable(curr->size->type, i32, curr, "memory.copy size must be an i32"); +} + +void FunctionValidator::visitMemoryFill(MemoryFill* curr) { + shouldBeTrue(info.features.hasBulkMemory(), curr, "Bulk memory operation (bulk memory is disabled)"); + shouldBeEqualOrFirstIsUnreachable(curr->type, none, curr, "memory.fill must have type none"); + shouldBeEqualOrFirstIsUnreachable(curr->dest->type, i32, curr, "memory.fill dest must be an i32"); + shouldBeEqualOrFirstIsUnreachable(curr->value->type, i32, curr, "memory.fill value must be an i32"); + shouldBeEqualOrFirstIsUnreachable(curr->size->type, i32, curr, "memory.fill size must be an i32"); +} + void FunctionValidator::validateMemBytes(uint8_t bytes, Type type, Expression* curr) { switch (type) { case i32: shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4, curr, "expected i32 operation to touch 1, 2, or 4 bytes"); break; diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 0d5f3d5b5..847df5ce6 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -110,6 +110,10 @@ const char* getExpressionName(Expression* curr) { case Expression::Id::SIMDShuffleId: return "simd_shuffle"; case Expression::Id::SIMDBitselectId: return "simd_bitselect"; case Expression::Id::SIMDShiftId: return "simd_shift"; + case Expression::Id::MemoryInitId: return "memory_init"; + case Expression::Id::DataDropId: return "data_drop"; + case Expression::Id::MemoryCopyId: return "memory_copy"; + case Expression::Id::MemoryFillId: return "memory_fill"; case Expression::Id::NumExpressionIds: WASM_UNREACHABLE(); } WASM_UNREACHABLE(); @@ -464,6 +468,34 @@ void SIMDBitselect::finalize() { } } +void MemoryInit::finalize() { + assert(dest && offset && size); + type = none; + if (dest->type == unreachable || offset->type == unreachable || size->type == unreachable) { + type = unreachable; + } +} + +void DataDrop::finalize() { + type = none; +} + +void MemoryCopy::finalize() { + assert(dest && source && size); + type = none; + if (dest->type == unreachable || source->type == unreachable || size->type == unreachable) { + type = unreachable; + } +} + +void MemoryFill::finalize() { + assert(dest && value && size); + type = none; + if (dest->type == unreachable || value->type == unreachable || size->type == unreachable) { + type = unreachable; + } +} + void SIMDShift::finalize() { assert(vec && shift); type = v128; |