summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas Lively <7121787+tlively@users.noreply.github.com>2019-02-05 12:35:09 -0800
committerGitHub <noreply@github.com>2019-02-05 12:35:09 -0800
commitf424f81886405fc26a415fc86900c0f8d0df14eb (patch)
tree5896c316f216fca9654f55e41809839d181ca53b /src
parent484f62f985cb2180139d1cf991ac04ee41635417 (diff)
downloadbinaryen-f424f81886405fc26a415fc86900c0f8d0df14eb.tar.gz
binaryen-f424f81886405fc26a415fc86900c0f8d0df14eb.tar.bz2
binaryen-f424f81886405fc26a415fc86900c0f8d0df14eb.zip
Bulk memory operations (#1892)
Bulk memory operations The only parts missing are the interpreter implementation and spec tests.
Diffstat (limited to 'src')
-rw-r--r--src/binaryen-c.cpp138
-rw-r--r--src/binaryen-c.h22
-rw-r--r--src/gen-s-parser.inc28
-rw-r--r--src/ir/ExpressionAnalyzer.cpp46
-rw-r--r--src/ir/ExpressionManipulator.cpp14
-rw-r--r--src/ir/ReFinalize.cpp4
-rw-r--r--src/ir/utils.h8
-rw-r--r--src/js/binaryen.js-post.js50
-rw-r--r--src/passes/DeadCodeElimination.cpp4
-rw-r--r--src/passes/Print.cpp48
-rw-r--r--src/tools/fuzzing.h41
-rw-r--r--src/tools/tool-options.h16
-rw-r--r--src/wasm-binary.h13
-rw-r--r--src/wasm-builder.h31
-rw-r--r--src/wasm-interpreter.h24
-rw-r--r--src/wasm-s-parser.h6
-rw-r--r--src/wasm-stack.h61
-rw-r--r--src/wasm-traversal.h49
-rw-r--r--src/wasm.h56
-rw-r--r--src/wasm/wasm-binary.cpp68
-rw-r--r--src/wasm/wasm-s-parser.cpp35
-rw-r--r--src/wasm/wasm-validator.cpp35
-rw-r--r--src/wasm/wasm.cpp32
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;