1 module tagion.wasm.WasmExpr; 2 3 import std.bitmanip : nativeToLittleEndian; 4 import std.format; 5 import std.outbuffer; 6 import std.traits : ForeachType, Unqual, isArray, isIntegral; 7 import tagion.wasm.WasmBase; 8 import tagion.utils.LEB128; 9 10 @safe 11 struct WasmExpr { 12 protected OutBuffer bout; 13 @disable this(); 14 this(OutBuffer bout) pure nothrow { 15 this.bout = bout; 16 } 17 18 ref WasmExpr opCall(Args...)(const IR ir, Args args) { 19 immutable instr = instrTable.get(ir, illegalInstr); 20 bout.write(cast(ubyte) ir); 21 immutable irtype = instr.irtype; 22 with (IRType) { 23 final switch (irtype) { 24 case PREFIX: 25 case CODE: 26 assert(Args.length == 0, 27 format("Instruction %s should have no arguments", instr.name)); 28 // No args 29 break; 30 case BLOCK, BRANCH, BRANCH_IF, CALL, LOCAL, GLOBAL: 31 assert(Args.length == 1, 32 format("Instruction %s only one argument expected", instr.name)); 33 static if (Args.length == 1) { 34 assert(isIntegral!(Args[0]), format("Args idx must be an integer for %s not %s", 35 instr.name, Args[0].stringof)); 36 static if (isIntegral!(Args[0])) { 37 bout.write(encode(args[0])); 38 } 39 } 40 break; 41 case BRANCH_TABLE: 42 scope uint[] table; 43 static foreach (i, a; args) { 44 { 45 enum OK = is(Args[i] : const(uint)) || is(Args[i] : const(uint[])); 46 assert(OK, format("Argument %d must be integer or uint[] of integer not %s", 47 i, Args[i].stringof)); 48 static if (OK) { 49 table ~= a; 50 } 51 } 52 } 53 check(table.length >= 2, format("Too few arguments for %s instruction", instr.name)); 54 bout.write(encode(table.length - 1)); 55 foreach (t; table) { 56 bout.write(encode(t)); 57 } 58 break; 59 case CALL_INDIRECT: 60 assert(Args.length == 1, format("Instruction %s one argument", instr.name)); 61 static if (Args.length == 1) { 62 assert(isIntegral!(Args[0]), 63 format("The funcidx must be an integer for %s", instr.name)); 64 static if (isIntegral!(Args[0])) { 65 bout.write(encode(args[0])); 66 bout.write(cast(ubyte)(0x00)); 67 } 68 } 69 break; 70 case MEMORY: 71 assert(Args.length == 2, format("Instruction %s two arguments", instr.name)); 72 static if (Args.length == 2) { 73 assert(isIntegral!(Args[0]), 74 format("The funcidx must be an integer for %s", instr.name)); 75 assert(isIntegral!(Args[1]), 76 format("The funcidx must be an integer for %s", instr.name)); 77 static if (isIntegral!(Args[0]) && isIntegral!(Args[1])) { 78 bout.write(encode(args[0])); 79 bout.write(encode(args[1])); 80 } 81 } 82 break; 83 case MEMOP: 84 assert(Args.length == 0, 85 format("Instruction %s should have no arguments", instr.name)); 86 bout.write(cast(ubyte)(0x00)); 87 break; 88 case CONST: 89 assert(Args.length == 1, format("Instruction %s one argument", instr.name)); 90 static if (Args.length == 1) { 91 alias BaseArg0 = Unqual!(Args[0]); 92 with (IR) { 93 switch (ir) { 94 case I32_CONST: 95 assert(is(BaseArg0 == int) || is(BaseArg0 == uint), 96 format("Bad type %s for the %s instruction", 97 BaseArg0.stringof, instr.name)); 98 static if (is(BaseArg0 == int) || is(BaseArg0 == uint)) { 99 bout.write(encode(args[0])); 100 } 101 break; 102 case I64_CONST: 103 assert(isIntegral!(BaseArg0), format("Bad type %s for the %s instruction", 104 BaseArg0.stringof, instr.name)); 105 static if (isIntegral!(BaseArg0)) { 106 bout.write(encode(args[0])); 107 } 108 break; 109 case F32_CONST: 110 assert(is(BaseArg0 : float), format("Bad type %s for the %s instruction", 111 Args[0].stringof, instr.name)); 112 static if (is(BaseArg0 : float)) { 113 float x = args[0]; 114 bout.write(nativeToLittleEndian(x)); 115 } 116 break; 117 case F64_CONST: 118 assert(is(BaseArg0 : double), format("Bad type %s for the %s instruction", 119 Args[0].stringof, instr.name)); 120 static if (is(BaseArg0 : double)) { 121 double x = args[0]; 122 bout.write(nativeToLittleEndian(x)); 123 } 124 break; 125 default: 126 assert(0, format("Bad const instruction %s", instr.name)); 127 } 128 } 129 } 130 break; 131 case END: 132 assert(Args.length == 0, 133 format("Instruction %s should have no arguments", instr.name)); 134 break; 135 case ILLEGAL: 136 assert(0, format("Illegal opcode %02X", ir)); 137 break; 138 case SYMBOL: 139 assert(0, "Symbol opcode and it does not have an equivalent opcode"); 140 } 141 } 142 return this; 143 } 144 145 immutable(ubyte[]) serialize() const pure nothrow { 146 return bout.toBytes.idup; 147 } 148 }