authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-09 17:49:27+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-10 21:01:00+01:00
loge7b7088056d55744694c304f95308ba6e1965455
treedc5e34f2cb6a6d1da9738fe039f2cb0e5668f32a
parenta4e6291fbdf83dec0d353af745c241bc7e01b3f2
signature Commit is signed but in an unrecognized format.

wasm: Implement float_to_int

- This implements the float_to_int AIR instruction. - Lowering a decl_ref to a slice was previously assumed to contain a pointer to a slice, rather than an array. This is now fixed, making `@src()` work as well. - Some preliminary work on 128bit integers have been done to find out what needs to be done to implement 128bit arithmetic.

5 files changed, 137 insertions(+), 39 deletions(-)

src/arch/wasm/CodeGen.zig+91-32
......@@ -597,7 +597,8 @@ fn resolveInst(self: Self, ref: Air.Inst.Ref) WValue {
597597 };
598598
599599 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 = {} };
601602
602603 if (self.air.instructions.items(.tag)[inst_index] == .constant) {
603604 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 {
689690 const info = ty.intInfo(self.target);
690691 if (info.bits <= 32) break :blk wasm.Valtype.i32;
691692 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});
693694 },
694695 .Enum => switch (ty.tag()) {
695696 .enum_simple => wasm.Valtype.i32,
......@@ -900,17 +901,6 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result {
900901 .Array => switch (val.tag()) {
901902 .bytes => {
902903 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 }
914904 return Result{ .externally_managed = payload.data };
915905 },
916906 .array => {
......@@ -1318,6 +1308,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
13181308 .bit_or => self.airBinOp(inst, .@"or"),
13191309 .bool_and => self.airBinOp(inst, .@"and"),
13201310 .bool_or => self.airBinOp(inst, .@"or"),
1311 .shl => self.airBinOp(inst, .shl),
1312 .shr => self.airBinOp(inst, .shr),
13211313 .xor => self.airBinOp(inst, .xor),
13221314
13231315 .cmp_eq => self.airCmp(inst, .eq),
......@@ -1341,6 +1333,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
13411333 .constant => unreachable,
13421334 .dbg_stmt => WValue.none,
13431335 .intcast => self.airIntcast(inst),
1336 .float_to_int => self.airFloatToInt(inst),
13441337
13451338 .is_err => self.airIsErr(inst, .i32_ne),
13461339 .is_non_err => self.airIsErr(inst, .i32_eq),
......@@ -1417,17 +1410,16 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14171410
14181411fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14191412 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
14261413 if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };
14271414
14281415 if (isByRef(child_type)) {
14291416 return self.return_value;
14301417 }
1418
1419 // Initialize the stack
1420 if (self.initial_stack_value == .none) {
1421 try self.initializeStack();
1422 }
14311423 return self.allocStack(child_type);
14321424}
14331425
......@@ -1665,10 +1657,20 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
16651657 const ptr_local = try self.allocLocal(Type.usize);
16661658 const len_offset = self.ptrSize();
16671659 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});
16721674 }
16731675 try self.addLabel(.local_set, len_local.local);
16741676 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
16771679 return;
16781680 } else if (ty.isSlice()) {
16791681 // 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);
16821684
16831685 // 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);
16861695 return;
16871696 }
1697 return try self.memCopy(ty, lhs, rhs);
16881698 },
16891699 else => {},
16901700 }
......@@ -1787,6 +1797,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
17871797}
17881798
17891799fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1800 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
1801
17901802 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
17911803 const lhs = self.resolveInst(bin_op.lhs);
17921804 const rhs = self.resolveInst(bin_op.rhs);
......@@ -1865,16 +1877,38 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
18651877 // write constant
18661878 switch (int_info.signedness) {
18671879 .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 => {},
18701883 else => |bits| return self.fail("Wasm todo: emitConstant for integer with {d} bits", .{bits}),
18711884 },
18721885 .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 => {},
18751889 else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}),
18761890 },
18771891 }
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);
18781912 },
18791913 .Bool => try self.addImm32(@intCast(i32, val.toSignedInt())),
18801914 .Float => {
......@@ -2632,6 +2666,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26322666}
26332667
26342668fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2669 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2670
26352671 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
26362672 const ty = self.air.getRefType(ty_op.ty);
26372673 const operand = self.resolveInst(ty_op.operand);
......@@ -2656,7 +2692,8 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26562692 .signed => .i64_extend_i32_s,
26572693 .unsigned => .i64_extend_i32_u,
26582694 });
2659 }
2695 } else unreachable;
2696
26602697 const result = try self.allocLocal(ty);
26612698 try self.addLabel(.local_set, result.local);
26622699 return result;
......@@ -3146,3 +3183,25 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
31463183 }
31473184 return try self.load(result, elem_ty, 0);
31483185}
3186
3187fn 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}
src/arch/wasm/Emit.zig+12
......@@ -161,6 +161,18 @@ pub fn emitMir(emit: *Emit) InnerError!void {
161161 .i64_extend8_s => try emit.emitTag(tag),
162162 .i64_extend16_s => try emit.emitTag(tag),
163163 .i64_extend32_s => try emit.emitTag(tag),
164 .i32_reinterpret_f32 => try emit.emitTag(tag),
165 .i64_reinterpret_f64 => try emit.emitTag(tag),
166 .f32_reinterpret_i32 => try emit.emitTag(tag),
167 .f64_reinterpret_i64 => try emit.emitTag(tag),
168 .i32_trunc_f32_s => try emit.emitTag(tag),
169 .i32_trunc_f32_u => try emit.emitTag(tag),
170 .i32_trunc_f64_s => try emit.emitTag(tag),
171 .i32_trunc_f64_u => try emit.emitTag(tag),
172 .i64_trunc_f32_s => try emit.emitTag(tag),
173 .i64_trunc_f32_u => try emit.emitTag(tag),
174 .i64_trunc_f64_s => try emit.emitTag(tag),
175 .i64_trunc_f64_u => try emit.emitTag(tag),
164176
165177 .extended => try emit.emitExtended(inst),
166178 }
src/arch/wasm/Mir.zig+24
......@@ -363,10 +363,34 @@ pub const Inst = struct {
363363 /// Uses `tag`
364364 i32_wrap_i64 = 0xA7,
365365 /// Uses `tag`
366 i32_trunc_f32_s = 0xA8,
367 /// Uses `tag`
368 i32_trunc_f32_u = 0xA9,
369 /// Uses `tag`
370 i32_trunc_f64_s = 0xAA,
371 /// Uses `tag`
372 i32_trunc_f64_u = 0xAB,
373 /// Uses `tag`
366374 i64_extend_i32_s = 0xAC,
367375 /// Uses `tag`
368376 i64_extend_i32_u = 0xAD,
369377 /// Uses `tag`
378 i64_trunc_f32_s = 0xAE,
379 /// Uses `tag`
380 i64_trunc_f32_u = 0xAF,
381 /// Uses `tag`
382 i64_trunc_f64_s = 0xB0,
383 /// Uses `tag`
384 i64_trunc_f64_u = 0xB1,
385 /// Uses `tag`
386 i32_reinterpret_f32 = 0xBC,
387 /// Uses `tag`
388 i64_reinterpret_f64 = 0xBD,
389 /// Uses `tag`
390 f32_reinterpret_i32 = 0xBE,
391 /// Uses `tag`
392 f64_reinterpret_i64 = 0xBF,
393 /// Uses `tag`
370394 i32_extend8_s = 0xC0,
371395 /// Uses `tag`
372396 i32_extend16_s = 0xC1,
test/behavior/cast.zig-7
......@@ -43,13 +43,6 @@ fn testResolveUndefWithInt(b: bool, x: i32) !void {
4343 }
4444}
4545
46test "@intCast i32 to u7" {
47 var x: u128 = maxInt(u128);
48 var y: i32 = 120;
49 var z = x >> @intCast(u7, y);
50 try expect(z == 0xff);
51}
52
5346test "@intCast to comptime_int" {
5447 try expect(@intCast(comptime_int, 0) == 0);
5548}
test/behavior/cast_int.zig created+10
......@@ -0,0 +1,10 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const maxInt = std.math.maxInt;
4
5test "@intCast i32 to u7" {
6 var x: u128 = maxInt(u128);
7 var y: i32 = 120;
8 var z = x >> @intCast(u7, y);
9 try expect(z == 0xff);
10}