| ... | @@ -590,7 +590,7 @@ pub const Context = struct { | ... | @@ -590,7 +590,7 @@ pub const Context = struct { |
| 590 | .Pointer, | 590 | .Pointer, |
| 591 | .ErrorSet, | 591 | .ErrorSet, |
| 592 | => wasm.Valtype.i32, | 592 | => wasm.Valtype.i32, |
| 593 | .Struct, .ErrorUnion => unreachable, // Multi typed, must be handled individually. | 593 | .Struct, .ErrorUnion, .Optional => unreachable, // Multi typed, must be handled individually. |
| 594 | else => |tag| self.fail("TODO - Wasm valtype for type '{s}'", .{tag}), | 594 | else => |tag| self.fail("TODO - Wasm valtype for type '{s}'", .{tag}), |
| 595 | }; | 595 | }; |
| 596 | } | 596 | } |
| ... | @@ -663,6 +663,23 @@ pub const Context = struct { | ... | @@ -663,6 +663,23 @@ pub const Context = struct { |
| 663 | .count = 2, | 663 | .count = 2, |
| 664 | } }; | 664 | } }; |
| 665 | }, | 665 | }, |
| | 666 | .Optional => { |
| | 667 | var opt_buf: Type.Payload.ElemType = undefined; |
| | 668 | const child_type = ty.optionalChild(&opt_buf); |
| | 669 | if (ty.isPtrLikeOptional()) { |
| | 670 | return self.fail("TODO: wasm optional pointer", .{}); |
| | 671 | } |
| | 672 | |
| | 673 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + 2); |
| | 674 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // optional 'tag' for null-checking is always i32 |
| | 675 | self.locals.appendAssumeCapacity(try self.genValtype(child_type)); |
| | 676 | self.local_index += 2; |
| | 677 | |
| | 678 | return WValue{ .multi_value = .{ |
| | 679 | .index = initial_index, |
| | 680 | .count = 2, |
| | 681 | } }; |
| | 682 | }, |
| 666 | else => { | 683 | else => { |
| 667 | const valtype = try self.genValtype(ty); | 684 | const valtype = try self.genValtype(ty); |
| 668 | try self.locals.append(self.gpa, valtype); | 685 | try self.locals.append(self.gpa, valtype); |
| ... | @@ -830,8 +847,15 @@ pub const Context = struct { | ... | @@ -830,8 +847,15 @@ pub const Context = struct { |
| 830 | .constant => unreachable, | 847 | .constant => unreachable, |
| 831 | .dbg_stmt => WValue.none, | 848 | .dbg_stmt => WValue.none, |
| 832 | .intcast => self.airIntcast(inst), | 849 | .intcast => self.airIntcast(inst), |
| | 850 | |
| 833 | .is_err => self.airIsErr(inst, .i32_ne), | 851 | .is_err => self.airIsErr(inst, .i32_ne), |
| 834 | .is_non_err => self.airIsErr(inst, .i32_eq), | 852 | .is_non_err => self.airIsErr(inst, .i32_eq), |
| | 853 | |
| | 854 | .is_null => self.airIsNull(inst, .i32_ne), |
| | 855 | .is_non_null => self.airIsNull(inst, .i32_eq), |
| | 856 | .is_null_ptr => self.airIsNull(inst, .i32_ne), |
| | 857 | .is_non_null_ptr => self.airIsNull(inst, .i32_eq), |
| | 858 | |
| 835 | .load => self.airLoad(inst), | 859 | .load => self.airLoad(inst), |
| 836 | .loop => self.airLoop(inst), | 860 | .loop => self.airLoop(inst), |
| 837 | .not => self.airNot(inst), | 861 | .not => self.airNot(inst), |
| ... | @@ -840,8 +864,13 @@ pub const Context = struct { | ... | @@ -840,8 +864,13 @@ pub const Context = struct { |
| 840 | .struct_field_ptr => self.airStructFieldPtr(inst), | 864 | .struct_field_ptr => self.airStructFieldPtr(inst), |
| 841 | .switch_br => self.airSwitchBr(inst), | 865 | .switch_br => self.airSwitchBr(inst), |
| 842 | .unreach => self.airUnreachable(inst), | 866 | .unreach => self.airUnreachable(inst), |
| | 867 | .wrap_optional => self.airWrapOptional(inst), |
| | 868 | |
| 843 | .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst), | 869 | .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst), |
| 844 | .wrap_errunion_payload => self.airWrapErrUnionPayload(inst), | 870 | .wrap_errunion_payload => self.airWrapErrUnionPayload(inst), |
| | 871 | |
| | 872 | .optional_payload => self.airOptionalPayload(inst), |
| | 873 | .optional_payload_ptr => self.airOptionalPayload(inst), |
| 845 | else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), | 874 | else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), |
| 846 | }; | 875 | }; |
| 847 | } | 876 | } |
| ... | @@ -926,6 +955,22 @@ pub const Context = struct { | ... | @@ -926,6 +955,22 @@ pub const Context = struct { |
| 926 | try leb.writeULEB128(writer, multi_value.index + i - 1); | 955 | try leb.writeULEB128(writer, multi_value.index + i - 1); |
| 927 | } | 956 | } |
| 928 | }, | 957 | }, |
| | 958 | .local => { |
| | 959 | // This can occur when we wrap a single value into a multi-value, |
| | 960 | // such as wrapping a non-optional value into an optional. |
| | 961 | // This means we must zero the null-tag, and set the payload. |
| | 962 | assert(multi_value.count == 2); |
| | 963 | // set null-tag |
| | 964 | try writer.writeByte(wasm.opcode(.i32_const)); |
| | 965 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 966 | try writer.writeByte(wasm.opcode(.local_set)); |
| | 967 | try leb.writeULEB128(writer, multi_value.index); |
| | 968 | |
| | 969 | // set payload |
| | 970 | try self.emitWValue(rhs); |
| | 971 | try writer.writeByte(wasm.opcode(.local_set)); |
| | 972 | try leb.writeULEB128(writer, multi_value.index + 1); |
| | 973 | }, |
| 929 | else => unreachable, | 974 | else => unreachable, |
| 930 | }, | 975 | }, |
| 931 | .local => |local| { | 976 | .local => |local| { |
| ... | @@ -1088,6 +1133,31 @@ pub const Context = struct { | ... | @@ -1088,6 +1133,31 @@ pub const Context = struct { |
| 1088 | try self.emitConstant(data, payload_type); | 1133 | try self.emitConstant(data, payload_type); |
| 1089 | } | 1134 | } |
| 1090 | }, | 1135 | }, |
| | 1136 | .Optional => { |
| | 1137 | var buf: Type.Payload.ElemType = undefined; |
| | 1138 | const payload_type = ty.optionalChild(&buf); |
| | 1139 | if (ty.isPtrLikeOptional()) { |
| | 1140 | return self.fail("Wasm TODO: emitConstant for optional pointer", .{}); |
| | 1141 | } |
| | 1142 | |
| | 1143 | // When constant has value 'null', set is_null local to '1' |
| | 1144 | // and payload to '0' |
| | 1145 | if (val.tag() == .null_value) { |
| | 1146 | try writer.writeByte(wasm.opcode(.i32_const)); |
| | 1147 | try leb.writeILEB128(writer, @as(i32, 1)); |
| | 1148 | |
| | 1149 | const opcode: wasm.Opcode = buildOpcode(.{ |
| | 1150 | .op = .@"const", |
| | 1151 | .valtype1 = try self.typeToValtype(payload_type), |
| | 1152 | }); |
| | 1153 | try writer.writeByte(wasm.opcode(opcode)); |
| | 1154 | try leb.writeULEB128(writer, @as(u32, 0)); |
| | 1155 | } else { |
| | 1156 | try writer.writeByte(wasm.opcode(.i32_const)); |
| | 1157 | try leb.writeILEB128(writer, @as(i32, 0)); |
| | 1158 | try self.emitConstant(val, payload_type); |
| | 1159 | } |
| | 1160 | }, |
| 1091 | else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), | 1161 | else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), |
| 1092 | } | 1162 | } |
| 1093 | } | 1163 | } |
| ... | @@ -1188,7 +1258,6 @@ pub const Context = struct { | ... | @@ -1188,7 +1258,6 @@ pub const Context = struct { |
| 1188 | const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; | 1258 | const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; |
| 1189 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | 1259 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| 1190 | const writer = self.code.writer(); | 1260 | const writer = self.code.writer(); |
| 1191 | | | |
| 1192 | // TODO: Handle death instructions for then and else body | 1261 | // TODO: Handle death instructions for then and else body |
| 1193 | | 1262 | |
| 1194 | // insert blocks at the position of `offset` so | 1263 | // insert blocks at the position of `offset` so |
| ... | @@ -1521,4 +1590,37 @@ pub const Context = struct { | ... | @@ -1521,4 +1590,37 @@ pub const Context = struct { |
| 1521 | // other cases are no-op | 1590 | // other cases are no-op |
| 1522 | return .none; | 1591 | return .none; |
| 1523 | } | 1592 | } |
| | 1593 | |
| | 1594 | fn airIsNull(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { |
| | 1595 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| | 1596 | const operand = self.resolveInst(un_op); |
| | 1597 | // const offset = self.code.items.len; |
| | 1598 | const writer = self.code.writer(); |
| | 1599 | |
| | 1600 | // load the null value which is positioned at multi_value's index |
| | 1601 | try self.emitWValue(.{ .local = operand.multi_value.index }); |
| | 1602 | // Compare the null value with '0' |
| | 1603 | try writer.writeByte(wasm.opcode(.i32_const)); |
| | 1604 | try leb.writeILEB128(writer, @as(i32, 0)); |
| | 1605 | |
| | 1606 | try writer.writeByte(@enumToInt(opcode)); |
| | 1607 | |
| | 1608 | // we save the result in a new local |
| | 1609 | const local = try self.allocLocal(Type.initTag(.i32)); |
| | 1610 | try writer.writeByte(wasm.opcode(.local_set)); |
| | 1611 | try leb.writeULEB128(writer, local.local); |
| | 1612 | |
| | 1613 | return local; |
| | 1614 | } |
| | 1615 | |
| | 1616 | fn airOptionalPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| | 1617 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 1618 | const operand = self.resolveInst(ty_op.operand); |
| | 1619 | return WValue{ .local = operand.multi_value.index + 1 }; |
| | 1620 | } |
| | 1621 | |
| | 1622 | fn airWrapOptional(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| | 1623 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 1624 | return self.resolveInst(ty_op.operand); |
| | 1625 | } |
| 1524 | }; | 1626 | }; |