diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index d0f08d0546b..5fd9debd54c 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -146,6 +146,8 @@ ("i64.shr_u", "makeBinary(BinaryOp::ShrUInt64)"), ("i64.rotl", "makeBinary(BinaryOp::RotLInt64)"), ("i64.rotr", "makeBinary(BinaryOp::RotRInt64)"), + ("i64.add128", "makeWideIntAddSub(WideIntAddSubOp::AddInt128)"), + ("i64.sub128", "makeWideIntAddSub(WideIntAddSubOp::SubInt128)"), ("f32.abs", "makeUnary(UnaryOp::AbsFloat32)"), ("f32.neg", "makeUnary(UnaryOp::NegFloat32)"), ("f32.ceil", "makeUnary(UnaryOp::CeilFloat32)"), diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index eca86c6ed77..6c4721da059 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -3405,12 +3405,23 @@ switch (buf[0]) { switch (buf[4]) { case 'a': { switch (buf[5]) { - case 'd': - if (op == "i64.add"sv) { - CHECK_ERR(makeBinary(ctx, pos, annotations, BinaryOp::AddInt64)); - return Ok{}; + case 'd': { + switch (buf[7]) { + case '\0': + if (op == "i64.add"sv) { + CHECK_ERR(makeBinary(ctx, pos, annotations, BinaryOp::AddInt64)); + return Ok{}; + } + goto parse_error; + case '1': + if (op == "i64.add128"sv) { + CHECK_ERR(makeWideIntAddSub(ctx, pos, annotations, WideIntAddSubOp::AddInt128)); + return Ok{}; + } + goto parse_error; + default: goto parse_error; } - goto parse_error; + } case 'n': if (op == "i64.and"sv) { CHECK_ERR(makeBinary(ctx, pos, annotations, BinaryOp::AndInt64)); diff --git a/src/interpreter/interpreter.cpp b/src/interpreter/interpreter.cpp index d15abcfd4a0..7d6e483a8c9 100644 --- a/src/interpreter/interpreter.cpp +++ b/src/interpreter/interpreter.cpp @@ -217,6 +217,28 @@ struct ExpressionInterpreter : OverriddenVisitor { } } Flow visitSelect(Select* curr) { WASM_UNREACHABLE("TODO"); } + Flow visitWideIntAddSub(WideIntAddSub* curr) { + if (curr->op == AddInt128 || curr->op == SubInt128) { + uint64_t highRHS = pop().geti64(); + uint64_t lowRHS = pop().geti64(); + uint64_t highLHS = pop().geti64(); + uint64_t lowLHS = pop().geti64(); + + uint64_t lowRes, highRes; + if (curr->op == AddInt128) { + lowRes = lowLHS + lowRHS; + highRes = highLHS + highRHS + (lowRes < lowLHS); + } else { + lowRes = lowLHS - lowRHS; + highRes = highLHS - highRHS - (lowLHS < lowRHS); + } + + push(Literal(lowRes)); + push(Literal(highRes)); + return {}; + } + WASM_UNREACHABLE("TODO"); + } Flow visitDrop(Drop* curr) { WASM_UNREACHABLE("TODO"); } Flow visitReturn(Return* curr) { WASM_UNREACHABLE("TODO"); } Flow visitMemorySize(MemorySize* curr) { WASM_UNREACHABLE("TODO"); } diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 24afae568c5..6c722cbb03c 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -109,6 +109,7 @@ 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(); } +void ReFinalize::visitWideIntAddSub(WideIntAddSub* curr) { curr->finalize(); } void ReFinalize::visitSelect(Select* curr) { curr->finalize(); } void ReFinalize::visitDrop(Drop* curr) { curr->finalize(); } void ReFinalize::visitReturn(Return* curr) { curr->finalize(); } diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 385e0fa8290..b6a38a233b0 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -703,6 +703,13 @@ template struct ChildTyper : OverriddenVisitor { } } + void visitWideIntAddSub(WideIntAddSub* curr) { + note(&curr->leftLow, Type::i64); + note(&curr->leftHigh, Type::i64); + note(&curr->rightLow, Type::i64); + note(&curr->rightHigh, Type::i64); + } + void visitSelect(Select* curr, std::optional type = std::nullopt) { if (type) { note(&curr->ifTrue, *type); diff --git a/src/ir/cost.h b/src/ir/cost.h index 0042d27bcb2..f13d73aed3a 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -573,6 +573,10 @@ struct CostAnalyzer : public OverriddenVisitor { } return ret + visit(curr->left) + visit(curr->right); } + CostType visitWideIntAddSub(WideIntAddSub* curr) { + return 1 + visit(curr->leftLow) + visit(curr->leftHigh) + + visit(curr->rightLow) + visit(curr->rightHigh); + } CostType visitSelect(Select* curr) { return 1 + visit(curr->condition) + visit(curr->ifTrue) + visit(curr->ifFalse); diff --git a/src/ir/effects.h b/src/ir/effects.h index af866b9e536..ec96053a13d 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -945,6 +945,7 @@ class EffectAnalyzer { } } } + void visitWideIntAddSub(WideIntAddSub* curr) {} void visitSelect(Select* curr) {} void visitDrop(Drop* curr) {} void visitReturn(Return* curr) { parent.branchesOut = true; } diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 4dc6da9c9d9..08cf1cf4f8d 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -634,6 +634,7 @@ struct InfoCollector addRoot(curr); } void visitBinary(Binary* curr) { addRoot(curr); } + void visitWideIntAddSub(WideIntAddSub* curr) { addRoot(curr); } void visitSelect(Select* curr) { receiveChildValue(curr->ifTrue, curr); receiveChildValue(curr->ifFalse, curr); diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h index 8a4677ec9da..b0a910f3fa7 100644 --- a/src/ir/subtype-exprs.h +++ b/src/ir/subtype-exprs.h @@ -213,6 +213,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor { void visitConst(Const* curr) {} void visitUnary(Unary* curr) {} void visitBinary(Binary* curr) {} + void visitWideIntAddSub(WideIntAddSub* curr) {} void visitSelect(Select* curr) { self()->noteSubtype(curr->ifTrue, curr); self()->noteSubtype(curr->ifFalse, curr); diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 88b7d8a941c..1c4a5193961 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -476,6 +476,12 @@ struct NullInstrParserCtx { Result<> makeBinary(Index, const std::vector&, BinaryOp) { return Ok{}; } + Result<> makeWideIntAddSub(Index, + const std::vector&, + WideIntAddSubOp) { + return Ok{}; + } + Result<> makeUnary(Index, const std::vector&, UnaryOp) { return Ok{}; } @@ -2159,6 +2165,12 @@ struct ParseDefsCtx : TypeParserCtx, AnnotationParserCtx { return withLoc(pos, irBuilder.makeBinary(op)); } + Result<> makeWideIntAddSub(Index pos, + const std::vector& annotations, + WideIntAddSubOp op) { + return withLoc(pos, irBuilder.makeWideIntAddSub(op)); + } + Result<> makeUnary(Index pos, const std::vector& annotations, UnaryOp op) { return withLoc(pos, irBuilder.makeUnary(op)); diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 4a5fb71b771..aa03aef0df6 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -90,6 +90,10 @@ template Result<> makeNop(Ctx&, Index, const std::vector&); template Result<> makeBinary(Ctx&, Index, const std::vector&, BinaryOp op); +template +Result<> +makeWideIntAddSub(Ctx&, Index, const std::vector&, WideIntAddSubOp op); + template Result<> makeUnary(Ctx&, Index, const std::vector&, UnaryOp op); template @@ -1592,6 +1596,14 @@ Result<> makeBinary(Ctx& ctx, return ctx.makeBinary(pos, annotations, op); } +template +Result<> makeWideIntAddSub(Ctx& ctx, + Index pos, + const std::vector& annotations, + WideIntAddSubOp op) { + return ctx.makeWideIntAddSub(pos, annotations, op); +} + template Result<> makeUnary(Ctx& ctx, Index pos, diff --git a/src/passes/I64ToI32Lowering.cpp b/src/passes/I64ToI32Lowering.cpp index 42feafa3861..0724b448674 100644 --- a/src/passes/I64ToI32Lowering.cpp +++ b/src/passes/I64ToI32Lowering.cpp @@ -1553,6 +1553,10 @@ struct I64ToI32Lowering : public WalkerPass> { } } + void visitWideIntAddSub(WideIntAddSub* curr) { + WASM_UNREACHABLE("TODO: wide arithmetic lowering"); + } + void visitSelect(Select* curr) { if (handleUnreachable(curr)) { return; diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp index fbaf09232b9..3218d93a922 100644 --- a/src/passes/Precompute.cpp +++ b/src/passes/Precompute.cpp @@ -282,6 +282,10 @@ class PrecomputingExpressionRunner return Flow(NONCONSTANT_FLOW); } + Flow visitWideIntAddSub(WideIntAddSub* curr) { + return Super::visitWideIntAddSub(curr); + } + Flow visitStringEncode(StringEncode* curr) { // string.encode_wtf16_array is effectively an Array write operation, so // just like ArraySet and ArrayCopy above we must mark it as disallowed diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 68981953211..45c42bba569 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2025,6 +2025,20 @@ struct PrintExpressionContents } restoreNormalColor(o); } + void visitWideIntAddSub(WideIntAddSub* curr) { + prepareColor(o); + switch (curr->op) { + case AddInt128: + o << "i64.add128"; + break; + case SubInt128: + o << "i64.sub128"; + break; + default: + WASM_UNREACHABLE("invalid wide int binary op"); + } + restoreNormalColor(o); + } void visitSelect(Select* curr) { prepareColor(o) << "select"; restoreNormalColor(o); diff --git a/src/passes/TypeGeneralizing.cpp b/src/passes/TypeGeneralizing.cpp index 03a0a0f11a7..1cd9a1b7885 100644 --- a/src/passes/TypeGeneralizing.cpp +++ b/src/passes/TypeGeneralizing.cpp @@ -433,6 +433,7 @@ struct TransferFn : OverriddenVisitor { void visitConst(Const* curr) {} void visitUnary(Unary* curr) {} void visitBinary(Binary* curr) {} + void visitWideIntAddSub(WideIntAddSub* curr) {} void visitSelect(Select* curr) { if (curr->type.isRef()) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index abacbda0d13..a94f7276076 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1136,6 +1136,11 @@ enum ASTNodes { MemoryCopy = 0x0a, MemoryFill = 0x0b, + // wide arithmetic opcodes + + I64Add128 = 0x13, + I64Sub128 = 0x14, + // reference types opcodes TableGrow = 0x0f, diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 30465e9e128..cd9d0c34ddd 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -660,6 +660,20 @@ class Builder { ret->finalize(); return ret; } + WideIntAddSub* makeWideIntAddSub(WideIntAddSubOp op, + Expression* leftLow, + Expression* leftHigh, + Expression* rightLow, + Expression* rightHigh) { + auto* ret = wasm.allocator.alloc(); + ret->op = op; + ret->leftLow = leftLow; + ret->leftHigh = leftHigh; + ret->rightLow = rightLow; + ret->rightHigh = rightHigh; + ret->finalize(); + return ret; + } Select* makeSelect(Expression* condition, Expression* ifTrue, Expression* ifFalse) { auto* ret = wasm.allocator.alloc