| ... | ... | @@ -597,7 +597,8 @@ fn resolveInst(self: Self, ref: Air.Inst.Ref) WValue { |
| 597 | 597 | }; |
| 598 | 598 | |
| 599 | 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 | 603 | if (self.air.instructions.items(.tag)[inst_index] == .constant) { |
| 603 | 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 | 690 | const info = ty.intInfo(self.target); |
| 690 | 691 | if (info.bits <= 32) break :blk wasm.Valtype.i32; |
| 691 | 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 | 695 | .Enum => switch (ty.tag()) { |
| 695 | 696 | .enum_simple => wasm.Valtype.i32, |
| ... | ... | @@ -900,17 +901,6 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 900 | 901 | .Array => switch (val.tag()) { |
| 901 | 902 | .bytes => { |
| 902 | 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 | 904 | return Result{ .externally_managed = payload.data }; |
| 915 | 905 | }, |
| 916 | 906 | .array => { |
| ... | ... | @@ -1318,6 +1308,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1318 | 1308 | .bit_or => self.airBinOp(inst, .@"or"), |
| 1319 | 1309 | .bool_and => self.airBinOp(inst, .@"and"), |
| 1320 | 1310 | .bool_or => self.airBinOp(inst, .@"or"), |
| 1311 | .shl => self.airBinOp(inst, .shl), |
| 1312 | .shr => self.airBinOp(inst, .shr), |
| 1321 | 1313 | .xor => self.airBinOp(inst, .xor), |
| 1322 | 1314 | |
| 1323 | 1315 | .cmp_eq => self.airCmp(inst, .eq), |
| ... | ... | @@ -1341,6 +1333,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1341 | 1333 | .constant => unreachable, |
| 1342 | 1334 | .dbg_stmt => WValue.none, |
| 1343 | 1335 | .intcast => self.airIntcast(inst), |
| 1336 | .float_to_int => self.airFloatToInt(inst), |
| 1344 | 1337 | |
| 1345 | 1338 | .is_err => self.airIsErr(inst, .i32_ne), |
| 1346 | 1339 | .is_non_err => self.airIsErr(inst, .i32_eq), |
| ... | ... | @@ -1417,17 +1410,16 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1417 | 1410 | |
| 1418 | 1411 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1419 | 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 | 1413 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; |
| 1427 | 1414 | |
| 1428 | 1415 | if (isByRef(child_type)) { |
| 1429 | 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 | 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 | 1657 | const ptr_local = try self.allocLocal(Type.usize); |
| 1666 | 1658 | const len_offset = self.ptrSize(); |
| 1667 | 1659 | if (val.castTag(.decl_ref)) |decl| { |
| 1668 | | // for decl references we also need to retrieve the length and the original decl's pointer |
| 1669 | | try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = self.ptrSize() }); |
| 1670 | | try self.addLabel(.memory_address, decl.data.link.wasm.sym_index); |
| 1671 | | try self.addMemArg(.i32_load, .{ .offset = len_offset, .alignment = self.ptrSize() }); |
| 1660 | const decl_ty: Type = decl.data.ty; |
| 1661 | if (decl_ty.isSlice()) { |
| 1662 | // for decl references we also need to retrieve the length and the original decl's pointer |
| 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 | 1675 | try self.addLabel(.local_set, len_local.local); |
| 1674 | 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 | 1679 | return; |
| 1678 | 1680 | } else if (ty.isSlice()) { |
| 1679 | 1681 | // store pointer first |
| 1680 | | const ptr_local = try self.load(rhs, Type.@"usize", 0); |
| 1681 | | try self.store(lhs, ptr_local, Type.@"usize", 0); |
| 1682 | const ptr_local = try self.load(rhs, Type.usize, 0); |
| 1683 | try self.store(lhs, ptr_local, Type.usize, 0); |
| 1682 | 1684 | |
| 1683 | 1685 | // retrieve length from rhs, and store that alongside lhs as well |
| 1684 | | const len_local = try self.load(rhs, Type.@"usize", 4); |
| 1685 | | try self.store(lhs, len_local, Type.@"usize", 4); |
| 1686 | const len_local = try self.load(rhs, Type.usize, self.ptrSize()); |
| 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 | 1695 | return; |
| 1687 | 1696 | } |
| 1697 | return try self.memCopy(ty, lhs, rhs); |
| 1688 | 1698 | }, |
| 1689 | 1699 | else => {}, |
| 1690 | 1700 | } |
| ... | ... | @@ -1787,6 +1797,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1787 | 1797 | } |
| 1788 | 1798 | |
| 1789 | 1799 | fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1800 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 1801 | |
| 1790 | 1802 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1791 | 1803 | const lhs = self.resolveInst(bin_op.lhs); |
| 1792 | 1804 | const rhs = self.resolveInst(bin_op.rhs); |
| ... | ... | @@ -1865,16 +1877,38 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1865 | 1877 | // write constant |
| 1866 | 1878 | switch (int_info.signedness) { |
| 1867 | 1879 | .signed => switch (int_info.bits) { |
| 1868 | | 0...32 => try self.addImm32(@intCast(i32, val.toSignedInt())), |
| 1869 | | 33...64 => try self.addImm64(@bitCast(u64, val.toSignedInt())), |
| 1880 | 0...32 => return try self.addImm32(@intCast(i32, val.toSignedInt())), |
| 1881 | 33...64 => return try self.addImm64(@bitCast(u64, val.toSignedInt())), |
| 1882 | 65...128 => {}, |
| 1870 | 1883 | else => |bits| return self.fail("Wasm todo: emitConstant for integer with {d} bits", .{bits}), |
| 1871 | 1884 | }, |
| 1872 | 1885 | .unsigned => switch (int_info.bits) { |
| 1873 | | 0...32 => try self.addImm32(@bitCast(i32, @intCast(u32, val.toUnsignedInt()))), |
| 1874 | | 33...64 => try self.addImm64(val.toUnsignedInt()), |
| 1886 | 0...32 => return try self.addImm32(@bitCast(i32, @intCast(u32, val.toUnsignedInt()))), |
| 1887 | 33...64 => return try self.addImm64(val.toUnsignedInt()), |
| 1888 | 65...128 => {}, |
| 1875 | 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 | 1913 | .Bool => try self.addImm32(@intCast(i32, val.toSignedInt())), |
| 1880 | 1914 | .Float => { |
| ... | ... | @@ -2632,6 +2666,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2632 | 2666 | } |
| 2633 | 2667 | |
| 2634 | 2668 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2669 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2670 | |
| 2635 | 2671 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2636 | 2672 | const ty = self.air.getRefType(ty_op.ty); |
| 2637 | 2673 | const operand = self.resolveInst(ty_op.operand); |
| ... | ... | @@ -2656,7 +2692,8 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2656 | 2692 | .signed => .i64_extend_i32_s, |
| 2657 | 2693 | .unsigned => .i64_extend_i32_u, |
| 2658 | 2694 | }); |
| 2659 | | } |
| 2695 | } else unreachable; |
| 2696 | |
| 2660 | 2697 | const result = try self.allocLocal(ty); |
| 2661 | 2698 | try self.addLabel(.local_set, result.local); |
| 2662 | 2699 | return result; |
| ... | ... | @@ -3146,3 +3183,25 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3146 | 3183 | } |
| 3147 | 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 | } |