| 1 | // Copyright (c) 2023 l-m.dev. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module wasm |
| 5 | |
| 6 | import v.ast |
| 7 | import v.token |
| 8 | import wasm |
| 9 | |
| 10 | pub fn (mut g Gen) as_numtype(a wasm.ValType) wasm.NumType { |
| 11 | if a in [.funcref_t, .externref_t, .v128_t] { |
| 12 | g.w_error("as_numtype: called with '${a}'") |
| 13 | } |
| 14 | |
| 15 | return unsafe { wasm.NumType(a) } |
| 16 | } |
| 17 | |
| 18 | // unwraps int_literal to i64_t |
| 19 | pub fn (mut g Gen) get_wasm_type_int_literal(typ_ ast.Type) wasm.ValType { |
| 20 | if typ_ == ast.int_literal_type_idx { |
| 21 | return wasm.ValType.i64_t |
| 22 | } |
| 23 | return g.get_wasm_type(typ_) |
| 24 | } |
| 25 | |
| 26 | // "Register size" types such as int, i64 and bool boil down to their WASM counterparts. |
| 27 | // Structures and unions are pointers, i32. |
| 28 | pub fn (mut g Gen) get_wasm_type(typ_ ast.Type) wasm.ValType { |
| 29 | typ := ast.mktyp(typ_) |
| 30 | if typ == ast.void_type_idx { |
| 31 | g.w_error("get_wasm_type: called with 'void'") |
| 32 | } |
| 33 | if typ.is_ptr() || typ.is_pointer() { |
| 34 | return wasm.ValType.i32_t |
| 35 | } |
| 36 | if typ in ast.number_type_idxs { |
| 37 | return match typ { |
| 38 | ast.isize_type_idx, ast.usize_type_idx, ast.i8_type_idx, ast.u8_type_idx, |
| 39 | ast.char_type_idx, ast.rune_type_idx, ast.i16_type_idx, ast.u16_type_idx, |
| 40 | ast.i32_type_idx, ast.u32_type_idx { |
| 41 | wasm.ValType.i32_t |
| 42 | } |
| 43 | ast.int_type_idx { |
| 44 | $if new_int ? && x64 { |
| 45 | wasm.ValType.i64_t |
| 46 | } $else { |
| 47 | wasm.ValType.i32_t |
| 48 | } |
| 49 | } |
| 50 | ast.i64_type_idx, ast.u64_type_idx { |
| 51 | wasm.ValType.i64_t |
| 52 | } |
| 53 | ast.f32_type_idx { |
| 54 | wasm.ValType.f32_t |
| 55 | } |
| 56 | ast.f64_type_idx { |
| 57 | wasm.ValType.f64_t |
| 58 | } |
| 59 | else { |
| 60 | wasm.ValType.i32_t |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | if typ == ast.bool_type_idx { |
| 65 | return wasm.ValType.i32_t |
| 66 | } |
| 67 | ts := g.table.sym(typ) |
| 68 | match ts.info { |
| 69 | ast.Struct { |
| 70 | g.pool.type_size(typ) |
| 71 | return wasm.ValType.i32_t // pointer |
| 72 | } |
| 73 | ast.Alias { |
| 74 | return g.get_wasm_type(ts.info.parent_type) |
| 75 | } |
| 76 | ast.ArrayFixed { |
| 77 | return wasm.ValType.i32_t // pointer |
| 78 | } |
| 79 | ast.Enum { |
| 80 | return g.get_wasm_type(ts.info.typ) |
| 81 | } |
| 82 | else {} |
| 83 | } |
| 84 | |
| 85 | g.w_error("get_wasm_type: unreachable type '${*g.table.sym(typ)}' ${ts.info}") |
| 86 | } |
| 87 | |
| 88 | pub fn (mut g Gen) infix_param_type(typ ast.Type, op token.Kind) { |
| 89 | match typ { |
| 90 | ast.string_type { |
| 91 | g.handle_string_operation(op) |
| 92 | } |
| 93 | else { |
| 94 | eprintln(*g.table.sym(typ)) |
| 95 | panic('unimplemented infix operation for type') |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | pub fn (mut g Gen) infix_from_typ(typ ast.Type, op token.Kind) { |
| 101 | if g.is_param_type(typ) { |
| 102 | g.infix_param_type(typ, op) |
| 103 | } else { |
| 104 | g.infix_numeric_type(typ, op) |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | fn (mut g Gen) infix_numeric_type(typ ast.Type, op token.Kind) { |
| 109 | wasm_typ := g.as_numtype(g.get_wasm_type(typ)) |
| 110 | |
| 111 | // This adds two tiers of comparaison but is a lot cleaner |
| 112 | match op { |
| 113 | .plus, .minus, .mul, .div, .mod { |
| 114 | g.emit_arithmetic_op(wasm_typ, typ, op) |
| 115 | } |
| 116 | .eq, .ne, .gt, .lt, .ge, .le { |
| 117 | g.emit_comparison_op(wasm_typ, typ, op) |
| 118 | } |
| 119 | .xor, .pipe, .amp, .left_shift, .right_shift, .unsigned_right_shift { |
| 120 | g.emit_bitwise_op(wasm_typ, typ, op) |
| 121 | } |
| 122 | else { |
| 123 | g.w_error('bad infix: op `${op}`') |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | fn (mut g Gen) emit_arithmetic_op(wasm_typ wasm.NumType, typ ast.Type, op token.Kind) { |
| 129 | match op { |
| 130 | .plus { g.func.add(wasm_typ) } |
| 131 | .minus { g.func.sub(wasm_typ) } |
| 132 | .mul { g.func.mul(wasm_typ) } |
| 133 | .div { g.func.div(wasm_typ, typ.is_signed()) } |
| 134 | .mod { g.func.rem(wasm_typ, typ.is_signed()) } |
| 135 | else { g.w_error('invalid aritmetic op: `${op}`') } |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | fn (mut g Gen) emit_comparison_op(wasm_typ wasm.NumType, typ ast.Type, op token.Kind) { |
| 140 | is_signed := typ.is_signed() |
| 141 | match op { |
| 142 | .eq { g.func.eq(wasm_typ) } |
| 143 | .ne { g.func.ne(wasm_typ) } |
| 144 | .gt { g.func.gt(wasm_typ, is_signed) } |
| 145 | .lt { g.func.lt(wasm_typ, is_signed) } |
| 146 | .ge { g.func.ge(wasm_typ, is_signed) } |
| 147 | .le { g.func.le(wasm_typ, is_signed) } |
| 148 | else { g.w_error('invalid comparison op: `${op}`') } |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | fn (mut g Gen) emit_bitwise_op(wasm_typ wasm.NumType, _typ ast.Type, op token.Kind) { |
| 153 | match op { |
| 154 | .xor { g.func.b_xor(wasm_typ) } |
| 155 | .pipe { g.func.b_or(wasm_typ) } |
| 156 | .amp { g.func.b_and(wasm_typ) } |
| 157 | .left_shift { g.func.b_shl(wasm_typ) } |
| 158 | .right_shift { g.func.b_shr(wasm_typ, true) } |
| 159 | .unsigned_right_shift { g.func.b_shr(wasm_typ, false) } |
| 160 | else { g.w_error('invalid bitwise op: `${op}`') } |
| 161 | } |
| 162 | } |
| 163 | |