Skip to content

Commit 7011e47

Browse files
authored
Generate sexp instruction parser (#1754)
Also fix broken tests surfaced by the new parser.
1 parent fa0dfb6 commit 7011e47

File tree

11 files changed

+1134
-301
lines changed

11 files changed

+1134
-301
lines changed

.flake8

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
[flake8]
2-
ignore = E111,E114,E501,E121
2+
ignore =
3+
E111, # indentation not a multiple of 4 (we use 2))
4+
E114, # comment indentation not a multiple of 4
5+
E501, # line too long
6+
E121, # continuation line under-indented for hanging indent
7+
E241 # space after comma (ignored for list in gen-s-parser.py)
38
exclude = ./test/emscripten,./test/spec,./test/wasm-install

.travis.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ jobs:
1515
- &test-ubuntu
1616
stage: test
1717
compiler: clang
18-
python: 2.7
18+
python:
19+
- 2.7
20+
- 3.6
1921
addons:
2022
apt:
2123
sources: ['ubuntu-toolchain-r-test']
@@ -33,6 +35,8 @@ jobs:
3335
script:
3436
- set -o errexit
3537
- flake8
38+
# ensure generated parser is up to date
39+
- ./scripts/gen-s-parser.py | diff src/gen-s-parser.inc -
3640
- BUILD_SUBDIR=${BUILD_SUBDIR:-.}
3741
- mkdir -p ${BUILD_SUBDIR} && cd ${BUILD_SUBDIR}
3842
- cmake ${TRAVIS_BUILD_DIR} -DCMAKE_C_FLAGS="$COMPILER_FLAGS" -DCMAKE_CXX_FLAGS="$COMPILER_FLAGS" -DCMAKE_INSTALL_PREFIX=install

scripts/gen-s-parser.py

Lines changed: 408 additions & 0 deletions
Large diffs are not rendered by default.

src/gen-s-parser.inc

Lines changed: 688 additions & 0 deletions
Large diffs are not rendered by default.

src/wasm-s-parser.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,10 @@ class SExpressionWasmBuilder {
167167

168168
private:
169169
Expression* makeExpression(Element& s);
170-
Expression* makeBinary(Element& s, BinaryOp op, Type type);
171-
Expression* makeUnary(Element& s, UnaryOp op, Type type);
170+
Expression* makeUnreachable();
171+
Expression* makeNop();
172+
Expression* makeBinary(Element& s, BinaryOp op);
173+
Expression* makeUnary(Element& s, UnaryOp op);
172174
Expression* makeSelect(Element& s);
173175
Expression* makeDrop(Element& s);
174176
Expression* makeHost(Element& s, HostOp op);

src/wasm/wasm-s-parser.cpp

Lines changed: 12 additions & 290 deletions
Original file line numberDiff line numberDiff line change
@@ -658,241 +658,19 @@ Expression* SExpressionWasmBuilder::parseExpression(Element& s) {
658658
}
659659

660660
Expression* SExpressionWasmBuilder::makeExpression(Element& s) {
661-
IString id = s[0]->str();
662-
const char *str = id.str;
663-
const char *dot = strchr(str, '.');
664-
if (dot) {
665-
// type.operation (e.g. i32.add)
666-
Type type = stringToType(str, false, true);
667-
// Local copy to index into op without bounds checking.
668-
enum { maxNameSize = 15 };
669-
char op[maxNameSize + 1] = {'\0'};
670-
strncpy(op, dot + 1, maxNameSize);
671-
#define BINARY_INT_OR_FLOAT(op) (type == i32 ? BinaryOp::op##Int32 : (type == i64 ? BinaryOp::op##Int64 : (type == f32 ? BinaryOp::op##Float32 : BinaryOp::op##Float64)))
672-
#define BINARY_INT(op) (type == i32 ? BinaryOp::op##Int32 : BinaryOp::op##Int64)
673-
#define BINARY_FLOAT(op) (type == f32 ? BinaryOp::op##Float32 : BinaryOp::op##Float64)
674-
switch (op[0]) {
675-
case 'a': {
676-
if (op[1] == 'b') return makeUnary(s, type == f32 ? UnaryOp::AbsFloat32 : UnaryOp::AbsFloat64, type);
677-
if (op[1] == 'd') return makeBinary(s, BINARY_INT_OR_FLOAT(Add), type);
678-
if (op[1] == 'n') return makeBinary(s, BINARY_INT(And), type);
679-
if (op[1] == 't' && !strncmp(op, "atomic.", strlen("atomic."))) {
680-
if (op[7] == 'l') return makeLoad(s, type, /*isAtomic=*/true);
681-
if (op[7] == 's') return makeStore(s, type, /*isAtomic=*/true);
682-
if (op[7] == 'r') return makeAtomicRMWOrCmpxchg(s, type);
683-
}
684-
abort_on(op);
685-
}
686-
case 'c': {
687-
if (op[1] == 'e') return makeUnary(s, type == f32 ? UnaryOp::CeilFloat32 : UnaryOp::CeilFloat64, type);
688-
if (op[1] == 'l') return makeUnary(s, type == i32 ? UnaryOp::ClzInt32 : UnaryOp::ClzInt64, type);
689-
if (op[1] == 'o') {
690-
if (op[2] == 'p') return makeBinary(s, BINARY_FLOAT(CopySign), type);
691-
if (op[2] == 'n') {
692-
if (op[3] == 'v') {
693-
if (op[8] == 's') return makeUnary(s, op[11] == '3' ? (type == f32 ? UnaryOp::ConvertSInt32ToFloat32 : UnaryOp::ConvertSInt32ToFloat64) : (type == f32 ? UnaryOp::ConvertSInt64ToFloat32 : UnaryOp::ConvertSInt64ToFloat64), type);
694-
if (op[8] == 'u') return makeUnary(s, op[11] == '3' ? (type == f32 ? UnaryOp::ConvertUInt32ToFloat32 : UnaryOp::ConvertUInt32ToFloat64) : (type == f32 ? UnaryOp::ConvertUInt64ToFloat32 : UnaryOp::ConvertUInt64ToFloat64), type);
695-
}
696-
if (op[3] == 's') return makeConst(s, type);
697-
}
698-
}
699-
if (op[1] == 't') return makeUnary(s, type == i32 ? UnaryOp::CtzInt32 : UnaryOp::CtzInt64, type);
700-
abort_on(op);
701-
}
702-
case 'd': {
703-
if (op[1] == 'i') {
704-
if (op[3] == '_') return makeBinary(s, op[4] == 'u' ? BINARY_INT(DivU) : BINARY_INT(DivS), type);
705-
if (op[3] == 0) return makeBinary(s, BINARY_FLOAT(Div), type);
706-
}
707-
if (op[1] == 'e') return makeUnary(s, UnaryOp::DemoteFloat64, type);
708-
abort_on(op);
709-
}
710-
case 'e': {
711-
if (op[1] == 'q') {
712-
if (op[2] == 0) return makeBinary(s, BINARY_INT_OR_FLOAT(Eq), type);
713-
if (op[2] == 'z') return makeUnary(s, type == i32 ? UnaryOp::EqZInt32 : UnaryOp::EqZInt64, type);
714-
}
715-
if (op[1] == 'x') {
716-
if (op[6] == '8') return makeUnary(s, type == i32 ? UnaryOp::ExtendS8Int32 : UnaryOp::ExtendS8Int64, type);
717-
if (op[6] == '1') return makeUnary(s, type == i32 ? UnaryOp::ExtendS16Int32 : UnaryOp::ExtendS16Int64, type);
718-
if (op[6] == '3') return makeUnary(s, UnaryOp::ExtendS32Int64, type);
719-
return makeUnary(s, op[7] == 'u' ? UnaryOp::ExtendUInt32 : UnaryOp::ExtendSInt32, type);
720-
}
721-
abort_on(op);
722-
}
723-
case 'f': {
724-
if (op[1] == 'l') return makeUnary(s, type == f32 ? UnaryOp::FloorFloat32 : UnaryOp::FloorFloat64, type);
725-
abort_on(op);
726-
}
727-
case 'g': {
728-
if (op[1] == 't') {
729-
if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(GtU) : BINARY_INT(GtS), type);
730-
if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Gt), type);
731-
}
732-
if (op[1] == 'e') {
733-
if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(GeU) : BINARY_INT(GeS), type);
734-
if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Ge), type);
735-
}
736-
abort_on(op);
737-
}
738-
case 'l': {
739-
if (op[1] == 't') {
740-
if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(LtU) : BINARY_INT(LtS), type);
741-
if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Lt), type);
742-
}
743-
if (op[1] == 'e') {
744-
if (op[2] == '_') return makeBinary(s, op[3] == 'u' ? BINARY_INT(LeU) : BINARY_INT(LeS), type);
745-
if (op[2] == 0) return makeBinary(s, BINARY_FLOAT(Le), type);
746-
}
747-
if (op[1] == 'o') return makeLoad(s, type, /*isAtomic=*/false);
748-
abort_on(op);
749-
}
750-
case 'm': {
751-
if (op[1] == 'i') return makeBinary(s, BINARY_FLOAT(Min), type);
752-
if (op[1] == 'a') return makeBinary(s, BINARY_FLOAT(Max), type);
753-
if (op[1] == 'u') return makeBinary(s, BINARY_INT_OR_FLOAT(Mul), type);
754-
abort_on(op);
755-
}
756-
case 'n': {
757-
if (op[1] == 'e') {
758-
if (op[2] == 0) return makeBinary(s, BINARY_INT_OR_FLOAT(Ne), type);
759-
if (op[2] == 'a') return makeUnary(s, type == f32 ? UnaryOp::NearestFloat32 : UnaryOp::NearestFloat64, type);
760-
if (op[2] == 'g') return makeUnary(s, type == f32 ? UnaryOp::NegFloat32 : UnaryOp::NegFloat64, type);
761-
}
762-
abort_on(op);
763-
}
764-
case 'o': {
765-
if (op[1] == 'r') return makeBinary(s, BINARY_INT(Or), type);
766-
abort_on(op);
767-
}
768-
case 'p': {
769-
if (op[1] == 'r') return makeUnary(s, UnaryOp::PromoteFloat32, type);
770-
if (op[1] == 'o') return makeUnary(s, type == i32 ? UnaryOp::PopcntInt32 : UnaryOp::PopcntInt64, type);
771-
abort_on(op);
772-
}
773-
case 'r': {
774-
if (op[1] == 'e') {
775-
if (op[2] == 'm') return makeBinary(s, op[4] == 'u' ? BINARY_INT(RemU) : BINARY_INT(RemS), type);
776-
if (op[2] == 'i') return makeUnary(s, isFloatType(type) ? (type == f32 ? UnaryOp::ReinterpretInt32 : UnaryOp::ReinterpretInt64) : (type == i32 ? UnaryOp::ReinterpretFloat32 : UnaryOp::ReinterpretFloat64), type);
777-
}
778-
if (op[1] == 'o' && op[2] == 't') {
779-
return makeBinary(s, op[3] == 'l' ? BINARY_INT(RotL) : BINARY_INT(RotR), type);
780-
}
781-
abort_on(op);
782-
}
783-
case 's': {
784-
if (op[1] == 'h') {
785-
if (op[2] == 'l') return makeBinary(s, BINARY_INT(Shl), type);
786-
return makeBinary(s, op[4] == 'u' ? BINARY_INT(ShrU) : BINARY_INT(ShrS), type);
787-
}
788-
if (op[1] == 'u') return makeBinary(s, BINARY_INT_OR_FLOAT(Sub), type);
789-
if (op[1] == 'q') return makeUnary(s, type == f32 ? UnaryOp::SqrtFloat32 : UnaryOp::SqrtFloat64, type);
790-
if (op[1] == 't') return makeStore(s, type, /*isAtomic=*/false);
791-
abort_on(op);
792-
}
793-
case 't': {
794-
if (op[1] == 'r') {
795-
if (op[6] == 's') return makeUnary(s, op[9] == '3' ? (type == i32 ? UnaryOp::TruncSFloat32ToInt32 : UnaryOp::TruncSFloat32ToInt64) : (type == i32 ? UnaryOp::TruncSFloat64ToInt32 : UnaryOp::TruncSFloat64ToInt64), type);
796-
if (op[6] == 'u') return makeUnary(s, op[9] == '3' ? (type == i32 ? UnaryOp::TruncUFloat32ToInt32 : UnaryOp::TruncUFloat32ToInt64) : (type == i32 ? UnaryOp::TruncUFloat64ToInt32 : UnaryOp::TruncUFloat64ToInt64), type);
797-
if (op[2] == 'u') return makeUnary(s, type == f32 ? UnaryOp::TruncFloat32 : UnaryOp::TruncFloat64, type);
798-
}
799-
abort_on(op);
800-
}
801-
case 'w': {
802-
if (!strncmp(op, "wait", strlen("wait"))) return makeAtomicWait(s, type);
803-
if (op[1] == 'r') return makeUnary(s, UnaryOp::WrapInt64, type);
804-
abort_on(op);
805-
}
806-
case 'x': {
807-
if (op[1] == 'o') return makeBinary(s, BINARY_INT(Xor), type);
808-
abort_on(op);
809-
}
810-
default: abort_on(op);
811-
}
812-
} else {
813-
// other expression
814-
switch (str[0]) {
815-
case 'b': {
816-
if (str[1] == 'l') return makeBlock(s);
817-
if (str[1] == 'r') {
818-
if (str[2] == '_' && str[3] == 't') return makeBreakTable(s);
819-
return makeBreak(s);
820-
}
821-
abort_on(str);
822-
}
823-
case 'c': {
824-
if (str[1] == 'a') {
825-
if (id == CALL) return makeCall(s);
826-
if (id == CALL_INDIRECT) return makeCallIndirect(s);
827-
} else if (str[1] == 'u') return makeHost(s, HostOp::CurrentMemory);
828-
abort_on(str);
829-
}
830-
case 'd': {
831-
if (str[1] == 'r') return makeDrop(s);
832-
abort_on(str);
833-
}
834-
case 'e': {
835-
if (str[1] == 'l') return makeThenOrElse(s);
836-
abort_on(str);
837-
}
838-
case 'g': {
839-
if (str[1] == 'e') {
840-
if (str[4] == 'l') return makeGetLocal(s);
841-
if (str[4] == 'g') return makeGetGlobal(s);
842-
}
843-
if (str[1] == 'r') return makeHost(s, HostOp::GrowMemory);
844-
abort_on(str);
845-
}
846-
case 'h': {
847-
abort_on(str);
848-
}
849-
case 'i': {
850-
if (str[1] == 'f') return makeIf(s);
851-
abort_on(str);
852-
}
853-
case 'l': {
854-
if (str[1] == 'o') return makeLoop(s);
855-
abort_on(str);
856-
}
857-
case 'n': {
858-
if (str[1] == 'o') return allocator.alloc<Nop>();
859-
abort_on(str);
860-
}
861-
case 'p': {
862-
abort_on(str);
863-
}
864-
case 's': {
865-
if (str[1] == 'e' && str[2] == 't') {
866-
if (str[4] == 'l') return makeSetLocal(s);
867-
if (str[4] == 'g') return makeSetGlobal(s);
868-
}
869-
if (str[1] == 'e' && str[2] == 'l') return makeSelect(s);
870-
abort_on(str);
871-
}
872-
case 'r': {
873-
if (str[1] == 'e') return makeReturn(s);
874-
abort_on(str);
875-
}
876-
case 't': {
877-
if (str[1] == 'h') return makeThenOrElse(s);
878-
if (str[1] == 'e' && str[2] == 'e') return makeTeeLocal(s);
879-
abort_on(str);
880-
}
881-
case 'u': {
882-
if (str[1] == 'n') return allocator.alloc<Unreachable>();
883-
abort_on(str);
884-
}
885-
case 'w': {
886-
if (!strncmp(str, "wake", strlen("wake"))) return makeAtomicWake(s);
887-
abort_on(str);
888-
}
889-
default: abort_on(str);
890-
}
891-
}
892-
abort_on("unrecognized input string for parsing");
661+
#define INSTRUCTION_PARSER
662+
#include "gen-s-parser.inc"
893663
}
894664

895-
Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op, Type type) {
665+
Expression* SExpressionWasmBuilder::makeUnreachable() {
666+
return allocator.alloc<Unreachable>();
667+
}
668+
669+
Expression* SExpressionWasmBuilder::makeNop() {
670+
return allocator.alloc<Nop>();
671+
}
672+
673+
Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op) {
896674
auto ret = allocator.alloc<Binary>();
897675
ret->op = op;
898676
ret->left = parseExpression(s[1]);
@@ -902,67 +680,11 @@ Expression* SExpressionWasmBuilder::makeBinary(Element& s, BinaryOp op, Type typ
902680
}
903681

904682

905-
Expression* SExpressionWasmBuilder::makeUnary(Element& s, UnaryOp op, Type type) {
683+
Expression* SExpressionWasmBuilder::makeUnary(Element& s, UnaryOp op) {
906684
auto ret = allocator.alloc<Unary>();
907685
ret->op = op;
908686
ret->value = parseExpression(s[1]);
909687
ret->finalize();
910-
// type is the reported type, e.g. i64.ctz reports i64 (but has a return type of i32, in this case)
911-
// verify the reported type is correct
912-
switch (op) {
913-
case EqZInt32:
914-
case NegFloat32:
915-
case AbsFloat32:
916-
case CeilFloat32:
917-
case FloorFloat32:
918-
case TruncFloat32:
919-
case NearestFloat32:
920-
case SqrtFloat32:
921-
case ClzInt32:
922-
case CtzInt32:
923-
case PopcntInt32:
924-
case EqZInt64:
925-
case NegFloat64:
926-
case AbsFloat64:
927-
case CeilFloat64:
928-
case FloorFloat64:
929-
case TruncFloat64:
930-
case NearestFloat64:
931-
case SqrtFloat64:
932-
case ClzInt64:
933-
case CtzInt64:
934-
case PopcntInt64: {
935-
if (ret->value->type != unreachable && type != ret->value->type) throw ParseException(std::string("bad type for ") + getExpressionName(ret) + ": " + printType(type) + " vs value type " + printType(ret->value->type), s.line, s.col);
936-
break;
937-
}
938-
case ExtendSInt32: case ExtendUInt32:
939-
case ExtendS8Int32: case ExtendS16Int32:
940-
case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64:
941-
case WrapInt64:
942-
case PromoteFloat32:
943-
case DemoteFloat64:
944-
case TruncSFloat32ToInt32:
945-
case TruncUFloat32ToInt32:
946-
case TruncSFloat64ToInt32:
947-
case TruncUFloat64ToInt32:
948-
case ReinterpretFloat32:
949-
case TruncSFloat32ToInt64:
950-
case TruncUFloat32ToInt64:
951-
case TruncSFloat64ToInt64:
952-
case TruncUFloat64ToInt64:
953-
case ReinterpretFloat64:
954-
case ReinterpretInt32:
955-
case ConvertSInt32ToFloat32:
956-
case ConvertUInt32ToFloat32:
957-
case ConvertSInt64ToFloat32:
958-
case ConvertUInt64ToFloat32:
959-
case ReinterpretInt64:
960-
case ConvertSInt32ToFloat64:
961-
case ConvertUInt32ToFloat64:
962-
case ConvertSInt64ToFloat64:
963-
case ConvertUInt64ToFloat64: break;
964-
default: WASM_UNREACHABLE();
965-
}
966688
return ret;
967689
}
968690

test/ctor-eval/basics-flatten.wast

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
)
1818
(func $test2
1919
(drop (i32.load (i32.const 12))) ;; a safe load
20-
(drop (i32.load16 (i32.const 12)))
21-
(drop (i32.load8 (i32.const 12)))
20+
(drop (i32.load16_s (i32.const 12)))
21+
(drop (i32.load8_s (i32.const 12)))
22+
(drop (i32.load16_u (i32.const 12)))
23+
(drop (i32.load8_u (i32.const 12)))
2224
)
2325
(func $test3
2426
(i32.store (i32.const 12) (i32.const 115)) ;; a safe store, should alter memory

test/ctor-eval/basics.wast

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
)
1515
(func $test2
1616
(drop (i32.load (i32.const 12))) ;; a safe load
17-
(drop (i32.load16 (i32.const 12)))
18-
(drop (i32.load8 (i32.const 12)))
17+
(drop (i32.load16_s (i32.const 12)))
18+
(drop (i32.load8_s (i32.const 12)))
19+
(drop (i32.load16_u (i32.const 12)))
20+
(drop (i32.load8_u (i32.const 12)))
1921
)
2022
(func $test3
2123
(i32.store (i32.const 12) (i32.const 115)) ;; a safe store, should alter memory

test/passes/duplicate-function-elimination_optimize-level=1.wast

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@
733733
(memory 10)
734734
(type $0 (func))
735735
(func $keep2 (type $0)
736-
(i32.store32 offset=3
736+
(i32.store offset=3
737737
(i32.const 0)
738738
(i32.const 100)
739739
)

0 commit comments

Comments
 (0)