| ... | @@ -597,7 +597,8 @@ fn resolveInst(self: Self, ref: Air.Inst.Ref) WValue { | ... | @@ -597,7 +597,8 @@ fn resolveInst(self: Self, ref: Air.Inst.Ref) WValue { |
| 597 | }; | 597 | }; |
| 598 | | 598 | |
| 599 | const inst_type = self.air.typeOfIndex(inst_index); | 599 | const inst_type = self.air.typeOfIndex(inst_index); |
| 600 | if (!inst_type.hasCodeGenBits()) return .none; | 600 | // It's allowed to have 0-bit integers |
| | 601 | if (!inst_type.hasCodeGenBits() and !inst_type.isInt()) return WValue{ .none = {} }; |
| 601 | | 602 | |
| 602 | if (self.air.instructions.items(.tag)[inst_index] == .constant) { | 603 | if (self.air.instructions.items(.tag)[inst_index] == .constant) { |
| 603 | const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl; | 604 | const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl; |
| ... | @@ -689,7 +690,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { | ... | @@ -689,7 +690,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { |
| 689 | const info = ty.intInfo(self.target); | 690 | const info = ty.intInfo(self.target); |
| 690 | if (info.bits <= 32) break :blk wasm.Valtype.i32; | 691 | if (info.bits <= 32) break :blk wasm.Valtype.i32; |
| 691 | if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64; | 692 | if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64; |
| 692 | return self.fail("Integer bit size not supported by wasm: '{d}'", .{info.bits}); | 693 | return self.fail("TODO: Support integer bitsize: '{d}'", .{info.bits}); |
| 693 | }, | 694 | }, |
| 694 | .Enum => switch (ty.tag()) { | 695 | .Enum => switch (ty.tag()) { |
| 695 | .enum_simple => wasm.Valtype.i32, | 696 | .enum_simple => wasm.Valtype.i32, |
| ... | @@ -900,17 +901,6 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -900,17 +901,6 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 900 | .Array => switch (val.tag()) { | 901 | .Array => switch (val.tag()) { |
| 901 | .bytes => { | 902 | .bytes => { |
| 902 | const payload = val.castTag(.bytes).?; | 903 | const payload = val.castTag(.bytes).?; |
| 903 | if (ty.sentinel()) |sentinel| { | | |
| 904 | try self.code.appendSlice(payload.data); | | |
| 905 | | | |
| 906 | switch (try self.genTypedValue(ty.childType(), sentinel)) { | | |
| 907 | .appended => return Result.appended, | | |
| 908 | .externally_managed => |data| { | | |
| 909 | try self.code.appendSlice(data); | | |
| 910 | return Result.appended; | | |
| 911 | }, | | |
| 912 | } | | |
| 913 | } | | |
| 914 | return Result{ .externally_managed = payload.data }; | 904 | return Result{ .externally_managed = payload.data }; |
| 915 | }, | 905 | }, |
| 916 | .array => { | 906 | .array => { |
| ... | @@ -1318,6 +1308,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1318,6 +1308,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1318 | .bit_or => self.airBinOp(inst, .@"or"), | 1308 | .bit_or => self.airBinOp(inst, .@"or"), |
| 1319 | .bool_and => self.airBinOp(inst, .@"and"), | 1309 | .bool_and => self.airBinOp(inst, .@"and"), |
| 1320 | .bool_or => self.airBinOp(inst, .@"or"), | 1310 | .bool_or => self.airBinOp(inst, .@"or"), |
| | 1311 | .shl => self.airBinOp(inst, .shl), |
| | 1312 | .shr => self.airBinOp(inst, .shr), |
| 1321 | .xor => self.airBinOp(inst, .xor), | 1313 | .xor => self.airBinOp(inst, .xor), |
| 1322 | | 1314 | |
| 1323 | .cmp_eq => self.airCmp(inst, .eq), | 1315 | .cmp_eq => self.airCmp(inst, .eq), |
| ... | @@ -1341,6 +1333,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1341,6 +1333,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1341 | .constant => unreachable, | 1333 | .constant => unreachable, |
| 1342 | .dbg_stmt => WValue.none, | 1334 | .dbg_stmt => WValue.none, |
| 1343 | .intcast => self.airIntcast(inst), | 1335 | .intcast => self.airIntcast(inst), |
| | 1336 | .float_to_int => self.airFloatToInt(inst), |
| 1344 | | 1337 | |
| 1345 | .is_err => self.airIsErr(inst, .i32_ne), | 1338 | .is_err => self.airIsErr(inst, .i32_ne), |
| 1346 | .is_non_err => self.airIsErr(inst, .i32_eq), | 1339 | .is_non_err => self.airIsErr(inst, .i32_eq), |
| ... | @@ -1417,17 +1410,16 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1417,17 +1410,16 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1417 | | 1410 | |
| 1418 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1411 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1419 | const child_type = self.air.typeOfIndex(inst).childType(); | 1412 | const child_type = self.air.typeOfIndex(inst).childType(); |
| 1420 | | | |
| 1421 | // Initialize the stack | | |
| 1422 | if (self.initial_stack_value == .none) { | | |
| 1423 | try self.initializeStack(); | | |
| 1424 | } | | |
| 1425 | | | |
| 1426 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; | 1413 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; |
| 1427 | | 1414 | |
| 1428 | if (isByRef(child_type)) { | 1415 | if (isByRef(child_type)) { |
| 1429 | return self.return_value; | 1416 | return self.return_value; |
| 1430 | } | 1417 | } |
| | 1418 | |
| | 1419 | // Initialize the stack |
| | 1420 | if (self.initial_stack_value == .none) { |
| | 1421 | try self.initializeStack(); |
| | 1422 | } |
| 1431 | return self.allocStack(child_type); | 1423 | return self.allocStack(child_type); |
| 1432 | } | 1424 | } |
| 1433 | | 1425 | |
| ... | @@ -1665,10 +1657,20 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1665,10 +1657,20 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1665 | const ptr_local = try self.allocLocal(Type.usize); | 1657 | const ptr_local = try self.allocLocal(Type.usize); |
| 1666 | const len_offset = self.ptrSize(); | 1658 | const len_offset = self.ptrSize(); |
| 1667 | if (val.castTag(.decl_ref)) |decl| { | 1659 | if (val.castTag(.decl_ref)) |decl| { |
| 1668 | // for decl references we also need to retrieve the length and the original decl's pointer | 1660 | const decl_ty: Type = decl.data.ty; |
| 1669 | try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = self.ptrSize() }); | 1661 | if (decl_ty.isSlice()) { |
| 1670 | try self.addLabel(.memory_address, decl.data.link.wasm.sym_index); | 1662 | // for decl references we also need to retrieve the length and the original decl's pointer |
| 1671 | try self.addMemArg(.i32_load, .{ .offset = len_offset, .alignment = self.ptrSize() }); | 1663 | try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = self.ptrSize() }); |
| | 1664 | try self.addLabel(.memory_address, decl.data.link.wasm.sym_index); |
| | 1665 | try self.addMemArg(.i32_load, .{ .offset = len_offset, .alignment = self.ptrSize() }); |
| | 1666 | } else if (decl_ty.zigTypeTag() == .Array) { |
| | 1667 | const len = decl_ty.arrayLen(); |
| | 1668 | switch (self.ptrSize()) { |
| | 1669 | 4 => try self.addImm32(@bitCast(i32, @intCast(u32, len))), |
| | 1670 | 8 => try self.addImm64(len), |
| | 1671 | else => unreachable, |
| | 1672 | } |
| | 1673 | } else return self.fail("Wasm todo: Implement storing slices for decl_ref with type: {}", .{decl_ty}); |
| 1672 | } | 1674 | } |
| 1673 | try self.addLabel(.local_set, len_local.local); | 1675 | try self.addLabel(.local_set, len_local.local); |
| 1674 | try self.addLabel(.local_set, ptr_local.local); | 1676 | try self.addLabel(.local_set, ptr_local.local); |
| ... | @@ -1677,14 +1679,22 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1677,14 +1679,22 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1677 | return; | 1679 | return; |
| 1678 | } else if (ty.isSlice()) { | 1680 | } else if (ty.isSlice()) { |
| 1679 | // store pointer first | 1681 | // store pointer first |
| 1680 | const ptr_local = try self.load(rhs, Type.@"usize", 0); | 1682 | const ptr_local = try self.load(rhs, Type.usize, 0); |
| 1681 | try self.store(lhs, ptr_local, Type.@"usize", 0); | 1683 | try self.store(lhs, ptr_local, Type.usize, 0); |
| 1682 | | 1684 | |
| 1683 | // retrieve length from rhs, and store that alongside lhs as well | 1685 | // retrieve length from rhs, and store that alongside lhs as well |
| 1684 | const len_local = try self.load(rhs, Type.@"usize", 4); | 1686 | const len_local = try self.load(rhs, Type.usize, self.ptrSize()); |
| 1685 | try self.store(lhs, len_local, Type.@"usize", 4); | 1687 | try self.store(lhs, len_local, Type.usize, self.ptrSize()); |
| | 1688 | return; |
| | 1689 | } |
| | 1690 | }, |
| | 1691 | .Int => if (ty.intInfo(self.target).bits > 64) { |
| | 1692 | if (rhs == .constant) { |
| | 1693 | try self.emitWValue(rhs); |
| | 1694 | try self.addLabel(.local_set, lhs.local); |
| 1686 | return; | 1695 | return; |
| 1687 | } | 1696 | } |
| | 1697 | return try self.memCopy(ty, lhs, rhs); |
| 1688 | }, | 1698 | }, |
| 1689 | else => {}, | 1699 | else => {}, |
| 1690 | } | 1700 | } |
| ... | @@ -1787,6 +1797,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1787,6 +1797,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1787 | } | 1797 | } |
| 1788 | | 1798 | |
| 1789 | fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | 1799 | fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| | 1800 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 1801 | |
| 1790 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1802 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1791 | const lhs = self.resolveInst(bin_op.lhs); | 1803 | const lhs = self.resolveInst(bin_op.lhs); |
| 1792 | const rhs = self.resolveInst(bin_op.rhs); | 1804 | const rhs = self.resolveInst(bin_op.rhs); |
| ... | @@ -1865,16 +1877,38 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1865,16 +1877,38 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1865 | // write constant | 1877 | // write constant |
| 1866 | switch (int_info.signedness) { | 1878 | switch (int_info.signedness) { |
| 1867 | .signed => switch (int_info.bits) { | 1879 | .signed => switch (int_info.bits) { |
| 1868 | 0...32 => try self.addImm32(@intCast(i32, val.toSignedInt())), | 1880 | 0...32 => return try self.addImm32(@intCast(i32, val.toSignedInt())), |
| 1869 | 33...64 => try self.addImm64(@bitCast(u64, val.toSignedInt())), | 1881 | 33...64 => return try self.addImm64(@bitCast(u64, val.toSignedInt())), |
| | 1882 | 65...128 => {}, |
| 1870 | else => |bits| return self.fail("Wasm todo: emitConstant for integer with {d} bits", .{bits}), | 1883 | else => |bits| return self.fail("Wasm todo: emitConstant for integer with {d} bits", .{bits}), |
| 1871 | }, | 1884 | }, |
| 1872 | .unsigned => switch (int_info.bits) { | 1885 | .unsigned => switch (int_info.bits) { |
| 1873 | 0...32 => try self.addImm32(@bitCast(i32, @intCast(u32, val.toUnsignedInt()))), | 1886 | 0...32 => return try self.addImm32(@bitCast(i32, @intCast(u32, val.toUnsignedInt()))), |
| 1874 | 33...64 => try self.addImm64(val.toUnsignedInt()), | 1887 | 33...64 => return try self.addImm64(val.toUnsignedInt()), |
| | 1888 | 65...128 => {}, |
| 1875 | else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}), | 1889 | else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}), |
| 1876 | }, | 1890 | }, |
| 1877 | } | 1891 | } |
| | 1892 | const result = try self.allocStack(ty); |
| | 1893 | var space: Value.BigIntSpace = undefined; |
| | 1894 | const bigint = val.toBigInt(&space); |
| | 1895 | if (bigint.limbs.len == 1) { |
| | 1896 | // value is '0'. As wasm's values are zeroed by default, |
| | 1897 | // just return and pop its stack pointer value. |
| | 1898 | try self.addLabel(.local_get, result.local); |
| | 1899 | return; |
| | 1900 | } |
| | 1901 | if (@sizeOf(usize) != @sizeOf(u64)) { |
| | 1902 | return self.fail("Wasm todo: Implement big integers for 32bit compiler", .{}); |
| | 1903 | } |
| | 1904 | |
| | 1905 | for (bigint.limbs) |_, index| { |
| | 1906 | const limb = bigint.limbs[bigint.limbs.len - index - 1]; |
| | 1907 | try self.addLabel(.local_get, result.local); |
| | 1908 | try self.addImm64(limb); |
| | 1909 | try self.addMemArg(.i64_store, .{ .offset = @intCast(u32, index * 8), .alignment = 8 }); |
| | 1910 | } |
| | 1911 | try self.addLabel(.local_get, result.local); |
| 1878 | }, | 1912 | }, |
| 1879 | .Bool => try self.addImm32(@intCast(i32, val.toSignedInt())), | 1913 | .Bool => try self.addImm32(@intCast(i32, val.toSignedInt())), |
| 1880 | .Float => { | 1914 | .Float => { |
| ... | @@ -2632,6 +2666,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2632,6 +2666,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2632 | } | 2666 | } |
| 2633 | | 2667 | |
| 2634 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2668 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2669 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2670 | |
| 2635 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2671 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2636 | const ty = self.air.getRefType(ty_op.ty); | 2672 | const ty = self.air.getRefType(ty_op.ty); |
| 2637 | const operand = self.resolveInst(ty_op.operand); | 2673 | const operand = self.resolveInst(ty_op.operand); |
| ... | @@ -2656,7 +2692,8 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2656,7 +2692,8 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2656 | .signed => .i64_extend_i32_s, | 2692 | .signed => .i64_extend_i32_s, |
| 2657 | .unsigned => .i64_extend_i32_u, | 2693 | .unsigned => .i64_extend_i32_u, |
| 2658 | }); | 2694 | }); |
| 2659 | } | 2695 | } else unreachable; |
| | 2696 | |
| 2660 | const result = try self.allocLocal(ty); | 2697 | const result = try self.allocLocal(ty); |
| 2661 | try self.addLabel(.local_set, result.local); | 2698 | try self.addLabel(.local_set, result.local); |
| 2662 | return result; | 2699 | return result; |
| ... | @@ -3146,3 +3183,25 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3146,3 +3183,25 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3146 | } | 3183 | } |
| 3147 | return try self.load(result, elem_ty, 0); | 3184 | return try self.load(result, elem_ty, 0); |
| 3148 | } | 3185 | } |
| | 3186 | |
| | 3187 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 3188 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 3189 | |
| | 3190 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 3191 | const operand = self.resolveInst(ty_op.operand); |
| | 3192 | const dest_ty = self.air.typeOfIndex(inst); |
| | 3193 | const op_ty = self.air.typeOf(ty_op.operand); |
| | 3194 | |
| | 3195 | try self.emitWValue(operand); |
| | 3196 | const op = buildOpcode(.{ |
| | 3197 | .op = .trunc, |
| | 3198 | .valtype1 = try self.typeToValtype(dest_ty), |
| | 3199 | .valtype2 = try self.typeToValtype(op_ty), |
| | 3200 | .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned, |
| | 3201 | }); |
| | 3202 | try self.addTag(Mir.Inst.Tag.fromOpcode(op)); |
| | 3203 | |
| | 3204 | const result = try self.allocLocal(dest_ty); |
| | 3205 | try self.addLabel(.local_set, result.local); |
| | 3206 | return result; |
| | 3207 | } |