authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-10 23:43:06+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-10 23:43:06+01:00
logada8e171373017cfd2a92267797c473c02aba130
tree306e5444aae35e32cccd6828e00152f02ff8594f
parent97c6d4fb3e78815d08cd61c63fcce57ab08b55ea
parentbf46aee878aa3ac6824047038ed887744da3e259
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10565 from Luukdegram/wasm-cast

Stage2: wasm - Implement optional equality, casts and more

6 files changed, 264 insertions(+), 73 deletions(-)

src/arch/wasm/CodeGen.zig+211-60
......@@ -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 break :blk wasm.Valtype.i32; // represented as pointer to stack
693694 },
694695 .Enum => switch (ty.tag()) {
695696 .enum_simple => wasm.Valtype.i32,
......@@ -752,7 +753,7 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {
752753 defer returns.deinit();
753754 const return_type = fn_ty.fnReturnType();
754755
755 const want_sret = isByRef(return_type);
756 const want_sret = self.isByRef(return_type);
756757
757758 if (want_sret) {
758759 try params.append(try self.typeToValtype(Type.usize));
......@@ -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 => {
......@@ -1094,7 +1084,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
10941084 const ret_ty = fn_ty.fnReturnType();
10951085 // Check if we store the result as a pointer to the stack rather than
10961086 // by value
1097 if (isByRef(ret_ty)) {
1087 if (self.isByRef(ret_ty)) {
10981088 // the sret arg will be passed as first argument, therefore we
10991089 // set the `return_value` before allocating locals for regular args.
11001090 result.return_value = .{ .local = self.local_index };
......@@ -1219,7 +1209,7 @@ fn ptrSize(self: *const Self) u16 {
12191209
12201210/// For a given `Type`, will return true when the type will be passed
12211211/// by reference, rather than by value.
1222fn isByRef(ty: Type) bool {
1212fn isByRef(self: Self, ty: Type) bool {
12231213 switch (ty.zigTypeTag()) {
12241214 .Type,
12251215 .ComptimeInt,
......@@ -1234,7 +1224,6 @@ fn isByRef(ty: Type) bool {
12341224 .NoReturn,
12351225 .Void,
12361226 .Bool,
1237 .Int,
12381227 .Float,
12391228 .ErrorSet,
12401229 .Fn,
......@@ -1248,6 +1237,7 @@ fn isByRef(ty: Type) bool {
12481237 .Frame,
12491238 .Union,
12501239 => return ty.hasCodeGenBits(),
1240 .Int => return if (ty.intInfo(self.target).bits > 64) true else false,
12511241 .ErrorUnion => {
12521242 const has_tag = ty.errorUnionSet().hasCodeGenBits();
12531243 const has_pl = ty.errorUnionPayload().hasCodeGenBits();
......@@ -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();
1413 if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };
1414
1415 if (self.isByRef(child_type)) {
1416 return self.return_value;
1417 }
14201418
14211419 // Initialize the stack
14221420 if (self.initial_stack_value == .none) {
14231421 try self.initializeStack();
14241422 }
1425
1426 if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };
1427
1428 if (isByRef(child_type)) {
1429 return self.return_value;
1430 }
14311423 return self.allocStack(child_type);
14321424}
14331425
......@@ -1437,7 +1429,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14371429 const ret_ty = self.air.typeOf(un_op).childType();
14381430 if (!ret_ty.hasCodeGenBits()) return WValue.none;
14391431
1440 if (!isByRef(ret_ty)) {
1432 if (!self.isByRef(ret_ty)) {
14411433 const result = try self.load(operand, ret_ty, 0);
14421434 try self.emitWValue(result);
14431435 }
......@@ -1459,7 +1451,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14591451 else => unreachable,
14601452 };
14611453 const ret_ty = fn_ty.fnReturnType();
1462 const first_param_sret = isByRef(ret_ty);
1454 const first_param_sret = self.isByRef(ret_ty);
14631455
14641456 const target: ?*Decl = blk: {
14651457 const func_val = self.air.value(pl_op.operand) orelse break :blk null;
......@@ -1487,7 +1479,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14871479
14881480 // If we need to pass by reference, but the argument is a constant,
14891481 // we must first lower it before passing it.
1490 if (isByRef(arg_ty) and arg_val == .constant) {
1482 if (self.isByRef(arg_ty) and arg_val == .constant) {
14911483 const arg_local = try self.allocStack(arg_ty);
14921484 try self.store(arg_local, arg_val, arg_ty, 0);
14931485 try self.emitWValue(arg_local);
......@@ -1599,7 +1591,12 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
15991591 if (payload_ty.hasCodeGenBits()) {
16001592 const payload_local = try self.allocLocal(payload_ty);
16011593 try self.addLabel(.local_set, payload_local.local);
1602 try self.store(lhs, payload_local, payload_ty, payload_offset);
1594 if (self.isByRef(payload_ty)) {
1595 const ptr = try self.buildPointerOffset(lhs, payload_offset, .new);
1596 try self.store(ptr, payload_local, payload_ty, 0);
1597 } else {
1598 try self.store(lhs, payload_local, payload_ty, payload_offset);
1599 }
16031600 }
16041601 try self.addLabel(.local_set, tag_local.local);
16051602
......@@ -1616,7 +1613,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
16161613 // Load values from `rhs` stack position and store in `lhs` instead
16171614 const tag_local = try self.load(rhs, tag_ty, 0);
16181615 if (payload_ty.hasCodeGenBits()) {
1619 if (isByRef(payload_ty)) {
1616 if (self.isByRef(payload_ty)) {
16201617 const payload_ptr = try self.buildPointerOffset(rhs, payload_offset, .new);
16211618 const lhs_payload_ptr = try self.buildPointerOffset(lhs, payload_offset, .new);
16221619 try self.store(lhs_payload_ptr, payload_ptr, payload_ty, 0);
......@@ -1649,12 +1646,13 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
16491646 }
16501647 },
16511648 .Struct, .Array => {
1652 if (rhs == .constant) {
1649 const final_rhs = if (rhs == .constant) blk: {
1650 const tmp = try self.allocLocal(Type.usize);
16531651 try self.emitWValue(rhs);
1654 try self.addLabel(.local_set, lhs.local);
1655 return;
1656 }
1657 return try self.memCopy(ty, lhs, rhs);
1652 try self.addLabel(.local_set, tmp.local);
1653 break :blk tmp;
1654 } else rhs;
1655 return try self.memCopy(ty, lhs, final_rhs);
16581656 },
16591657 .Pointer => {
16601658 if (ty.isSlice() and rhs == .constant) {
......@@ -1665,10 +1663,20 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
16651663 const ptr_local = try self.allocLocal(Type.usize);
16661664 const len_offset = self.ptrSize();
16671665 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() });
1666 const decl_ty: Type = decl.data.ty;
1667 if (decl_ty.isSlice()) {
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() });
1672 } else if (decl_ty.zigTypeTag() == .Array) {
1673 const len = decl_ty.arrayLen();
1674 switch (self.ptrSize()) {
1675 4 => try self.addImm32(@bitCast(i32, @intCast(u32, len))),
1676 8 => try self.addImm64(len),
1677 else => unreachable,
1678 }
1679 } else return self.fail("Wasm todo: Implement storing slices for decl_ref with type: {}", .{decl_ty});
16721680 }
16731681 try self.addLabel(.local_set, len_local.local);
16741682 try self.addLabel(.local_set, ptr_local.local);
......@@ -1677,15 +1685,23 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
16771685 return;
16781686 } else if (ty.isSlice()) {
16791687 // store pointer first
1680 const ptr_local = try self.load(rhs, Type.@"usize", 0);
1681 try self.store(lhs, ptr_local, Type.@"usize", 0);
1688 const ptr_local = try self.load(rhs, Type.usize, 0);
1689 try self.store(lhs, ptr_local, Type.usize, 0);
16821690
16831691 // 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);
1692 const len_local = try self.load(rhs, Type.usize, self.ptrSize());
1693 try self.store(lhs, len_local, Type.usize, self.ptrSize());
16861694 return;
16871695 }
16881696 },
1697 .Int => if (ty.intInfo(self.target).bits > 64) {
1698 if (rhs == .constant) {
1699 try self.emitWValue(rhs);
1700 try self.addLabel(.local_set, lhs.local);
1701 return;
1702 }
1703 return try self.memCopy(ty, lhs, rhs);
1704 },
16891705 else => {},
16901706 }
16911707 try self.emitWValue(lhs);
......@@ -1723,7 +1739,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
17231739
17241740 if (!ty.hasCodeGenBits()) return WValue{ .none = {} };
17251741
1726 if (isByRef(ty)) {
1742 if (self.isByRef(ty)) {
17271743 const new_local = try self.allocStack(ty);
17281744 try self.store(new_local, operand, ty, 0);
17291745 return new_local;
......@@ -1787,9 +1803,16 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
17871803}
17881804
17891805fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
1806 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
1807
17901808 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
17911809 const lhs = self.resolveInst(bin_op.lhs);
17921810 const rhs = self.resolveInst(bin_op.rhs);
1811 const operand_ty = self.air.typeOfIndex(inst);
1812
1813 if (self.isByRef(operand_ty)) {
1814 return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty});
1815 }
17931816
17941817 try self.emitWValue(lhs);
17951818 try self.emitWValue(rhs);
......@@ -1865,16 +1888,36 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
18651888 // write constant
18661889 switch (int_info.signedness) {
18671890 .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())),
1891 0...32 => return try self.addImm32(@intCast(i32, val.toSignedInt())),
1892 33...64 => return try self.addImm64(@bitCast(u64, val.toSignedInt())),
1893 65...128 => {},
18701894 else => |bits| return self.fail("Wasm todo: emitConstant for integer with {d} bits", .{bits}),
18711895 },
18721896 .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()),
1897 0...32 => return try self.addImm32(@bitCast(i32, @intCast(u32, val.toUnsignedInt()))),
1898 33...64 => return try self.addImm64(val.toUnsignedInt()),
1899 65...128 => {},
18751900 else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}),
18761901 },
18771902 }
1903 const result = try self.allocStack(ty);
1904 var space: Value.BigIntSpace = undefined;
1905 const bigint = val.toBigInt(&space);
1906 if (bigint.limbs.len == 1 and bigint.limbs[0] == 0) {
1907 try self.addLabel(.local_get, result.local);
1908 return;
1909 }
1910 if (@sizeOf(usize) != @sizeOf(u64)) {
1911 return self.fail("Wasm todo: Implement big integers for 32bit compiler", .{});
1912 }
1913
1914 for (bigint.limbs) |_, index| {
1915 const limb = bigint.limbs[bigint.limbs.len - index - 1];
1916 try self.addLabel(.local_get, result.local);
1917 try self.addImm64(limb);
1918 try self.addMemArg(.i64_store, .{ .offset = @intCast(u32, index * 8), .alignment = 8 });
1919 }
1920 try self.addLabel(.local_get, result.local);
18781921 },
18791922 .Bool => try self.addImm32(@intCast(i32, val.toSignedInt())),
18801923 .Float => {
......@@ -2233,9 +2276,6 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
22332276 const rhs = self.resolveInst(bin_op.rhs);
22342277 const operand_ty = self.air.typeOf(bin_op.lhs);
22352278
2236 try self.emitWValue(lhs);
2237 try self.emitWValue(rhs);
2238
22392279 if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) {
22402280 var buf: Type.Payload.ElemType = undefined;
22412281 const payload_ty = operand_ty.optionalChild(&buf);
......@@ -2243,10 +2283,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
22432283 // When we hit this case, we must check the value of optionals
22442284 // that are not pointers. This means first checking against non-null for
22452285 // both lhs and rhs, as well as checking the payload are matching of lhs and rhs
2246 return self.fail("TODO: Implement airCmp for comparing optionals", .{});
2286 return self.cmpOptionals(lhs, rhs, operand_ty, op);
22472287 }
2288 } else if (self.isByRef(operand_ty)) {
2289 return self.cmpBigInt(lhs, rhs, operand_ty, op);
22482290 }
22492291
2292 try self.emitWValue(lhs);
2293 try self.emitWValue(rhs);
2294
22502295 const signedness: std.builtin.Signedness = blk: {
22512296 // by default we tell the operand type is unsigned (i.e. bools and enum values)
22522297 if (operand_ty.zigTypeTag() != .Int) break :blk .unsigned;
......@@ -2390,7 +2435,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23902435 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
23912436 };
23922437
2393 if (isByRef(field_ty)) {
2438 if (self.isByRef(field_ty)) {
23942439 return WValue{ .local_with_offset = .{ .local = operand.local, .offset = offset } };
23952440 }
23962441
......@@ -2573,13 +2618,16 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
25732618}
25742619
25752620fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2576 if (self.liveness.isUnused(inst)) return WValue.none;
2621 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
25772622 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
25782623 const operand = self.resolveInst(ty_op.operand);
25792624 const err_ty = self.air.typeOf(ty_op.operand);
25802625 const payload_ty = err_ty.errorUnionPayload();
2581 if (!payload_ty.hasCodeGenBits()) return WValue.none;
2626 if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} };
25822627 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));
2628 if (self.isByRef(payload_ty)) {
2629 return self.buildPointerOffset(operand, offset, .new);
2630 }
25832631 return try self.load(operand, payload_ty, offset);
25842632}
25852633
......@@ -2632,6 +2680,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26322680}
26332681
26342682fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2683 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2684
26352685 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
26362686 const ty = self.air.getRefType(ty_op.ty);
26372687 const operand = self.resolveInst(ty_op.operand);
......@@ -2656,7 +2706,8 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26562706 .signed => .i64_extend_i32_s,
26572707 .unsigned => .i64_extend_i32_u,
26582708 });
2659 }
2709 } else unreachable;
2710
26602711 const result = try self.allocLocal(ty);
26612712 try self.addLabel(.local_set, result.local);
26622713 return result;
......@@ -2668,12 +2719,16 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en
26682719
26692720 const op_ty = self.air.typeOf(un_op);
26702721 const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty;
2722 return self.isNull(operand, optional_ty, opcode);
2723}
2724
2725fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
26712726 try self.emitWValue(operand);
26722727 if (!optional_ty.isPtrLikeOptional()) {
26732728 var buf: Type.Payload.ElemType = undefined;
26742729 const payload_ty = optional_ty.optionalChild(&buf);
2675 // When payload is zero-bits, we can treat operand as a value, rather than a
2676 // stack value
2730 // When payload is zero-bits, we can treat operand as a value, rather than
2731 // a pointer to the stack value
26772732 if (payload_ty.hasCodeGenBits()) {
26782733 try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 });
26792734 }
......@@ -2699,7 +2754,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
26992754
27002755 const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
27012756
2702 if (isByRef(payload_ty)) {
2757 if (self.isByRef(payload_ty)) {
27032758 return self.buildPointerOffset(operand, offset, .new);
27042759 }
27052760
......@@ -2830,7 +2885,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28302885 const result = try self.allocLocal(elem_ty);
28312886 try self.addLabel(.local_set, result.local);
28322887
2833 if (isByRef(elem_ty)) {
2888 if (self.isByRef(elem_ty)) {
28342889 return result;
28352890 }
28362891 return try self.load(result, elem_ty, 0);
......@@ -2991,7 +3046,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29913046
29923047 const result = try self.allocLocal(elem_ty);
29933048 try self.addLabel(.local_set, result.local);
2994 if (isByRef(elem_ty)) {
3049 if (self.isByRef(elem_ty)) {
29953050 return result;
29963051 }
29973052 return try self.load(result, elem_ty, 0);
......@@ -3141,8 +3196,104 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
31413196 const result = try self.allocLocal(elem_ty);
31423197 try self.addLabel(.local_set, result.local);
31433198
3144 if (isByRef(elem_ty)) {
3199 if (self.isByRef(elem_ty)) {
31453200 return result;
31463201 }
31473202 return try self.load(result, elem_ty, 0);
31483203}
3204
3205fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3206 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3207
3208 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3209 const operand = self.resolveInst(ty_op.operand);
3210 const dest_ty = self.air.typeOfIndex(inst);
3211 const op_ty = self.air.typeOf(ty_op.operand);
3212
3213 try self.emitWValue(operand);
3214 const op = buildOpcode(.{
3215 .op = .trunc,
3216 .valtype1 = try self.typeToValtype(dest_ty),
3217 .valtype2 = try self.typeToValtype(op_ty),
3218 .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned,
3219 });
3220 try self.addTag(Mir.Inst.Tag.fromOpcode(op));
3221
3222 const result = try self.allocLocal(dest_ty);
3223 try self.addLabel(.local_set, result.local);
3224 return result;
3225}
3226
3227fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
3228 assert(operand_ty.hasCodeGenBits());
3229 assert(op == .eq or op == .neq);
3230 var buf: Type.Payload.ElemType = undefined;
3231 const payload_ty = operand_ty.optionalChild(&buf);
3232 const offset = @intCast(u32, operand_ty.abiSize(self.target) - payload_ty.abiSize(self.target));
3233
3234 const lhs_is_null = try self.isNull(lhs, operand_ty, .i32_eq);
3235 const rhs_is_null = try self.isNull(rhs, operand_ty, .i32_eq);
3236
3237 // We store the final result in here that will be validated
3238 // if the optional is truly equal.
3239 const result = try self.allocLocal(Type.initTag(.i32));
3240
3241 try self.startBlock(.block, wasm.block_empty);
3242 try self.emitWValue(lhs_is_null);
3243 try self.emitWValue(rhs_is_null);
3244 try self.addTag(.i32_ne); // inverse so we can exit early
3245 try self.addLabel(.br_if, 0);
3246
3247 const lhs_pl = try self.load(lhs, payload_ty, offset);
3248 const rhs_pl = try self.load(rhs, payload_ty, offset);
3249
3250 try self.emitWValue(lhs_pl);
3251 try self.emitWValue(rhs_pl);
3252 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = try self.typeToValtype(payload_ty) });
3253 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
3254 try self.addLabel(.br_if, 0);
3255
3256 try self.addImm32(1);
3257 try self.addLabel(.local_set, result.local);
3258 try self.endBlock();
3259
3260 try self.emitWValue(result);
3261 try self.addImm32(0);
3262 try self.addTag(if (op == .eq) .i32_ne else .i32_eq);
3263 try self.addLabel(.local_set, result.local);
3264 return result;
3265}
3266
3267/// Compares big integers by checking both its high bits and low bits.
3268/// TODO: Lower this to compiler_rt call
3269fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
3270 if (operand_ty.intInfo(self.target).bits > 128) {
3271 return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits});
3272 }
3273
3274 const result = try self.allocLocal(Type.initTag(.i32));
3275 {
3276 try self.startBlock(.block, wasm.block_empty);
3277 const lhs_high_bit = try self.load(lhs, Type.initTag(.u64), 0);
3278 const lhs_low_bit = try self.load(lhs, Type.initTag(.u64), 8);
3279 const rhs_high_bit = try self.load(rhs, Type.initTag(.u64), 0);
3280 const rhs_low_bit = try self.load(rhs, Type.initTag(.u64), 8);
3281 try self.emitWValue(lhs_high_bit);
3282 try self.emitWValue(rhs_high_bit);
3283 try self.addTag(.i64_ne);
3284 try self.addLabel(.br_if, 0);
3285 try self.emitWValue(lhs_low_bit);
3286 try self.emitWValue(rhs_low_bit);
3287 try self.addTag(.i64_ne);
3288 try self.addLabel(.br_if, 0);
3289 try self.addImm32(1);
3290 try self.addLabel(.local_set, result.local);
3291 try self.endBlock();
3292 }
3293
3294 try self.emitWValue(result);
3295 try self.addImm32(0);
3296 try self.addTag(if (op == .eq) .i32_ne else .i32_eq);
3297 try self.addLabel(.local_set, result.local);
3298 return result;
3299}
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.zig+7-6
......@@ -20,8 +20,8 @@ test {
2020
2121 if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) {
2222 // Tests that pass for stage1, llvm backend, C backend, wasm backend.
23 _ = @import("behavior/align.zig");
2324 _ = @import("behavior/array.zig");
24 _ = @import("behavior/bugs/3586.zig");
2525 _ = @import("behavior/basic.zig");
2626 _ = @import("behavior/bitcast.zig");
2727 _ = @import("behavior/bugs/624.zig");
......@@ -31,12 +31,14 @@ test {
3131 _ = @import("behavior/bugs/2692.zig");
3232 _ = @import("behavior/bugs/2889.zig");
3333 _ = @import("behavior/bugs/3046.zig");
34 _ = @import("behavior/bugs/3586.zig");
3435 _ = @import("behavior/bugs/4560.zig");
3536 _ = @import("behavior/bugs/4769_a.zig");
3637 _ = @import("behavior/bugs/4769_b.zig");
3738 _ = @import("behavior/bugs/4954.zig");
3839 _ = @import("behavior/byval_arg_var.zig");
3940 _ = @import("behavior/call.zig");
41 _ = @import("behavior/cast.zig");
4042 _ = @import("behavior/defer.zig");
4143 _ = @import("behavior/enum.zig");
4244 _ = @import("behavior/error.zig");
......@@ -48,12 +50,15 @@ test {
4850 _ = @import("behavior/inttoptr.zig");
4951 _ = @import("behavior/member_func.zig");
5052 _ = @import("behavior/null.zig");
53 _ = @import("behavior/optional.zig");
5154 _ = @import("behavior/pointers.zig");
5255 _ = @import("behavior/ptrcast.zig");
5356 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
57 _ = @import("behavior/src.zig");
5458 _ = @import("behavior/struct.zig");
5559 _ = @import("behavior/this.zig");
5660 _ = @import("behavior/truncate.zig");
61 _ = @import("behavior/try.zig");
5762 _ = @import("behavior/undefined.zig");
5863 _ = @import("behavior/underscore.zig");
5964 _ = @import("behavior/usingnamespace.zig");
......@@ -62,13 +67,9 @@ test {
6267
6368 if (builtin.zig_backend != .stage2_wasm) {
6469 // Tests that pass for stage1, llvm backend, C backend
65 _ = @import("behavior/align.zig");
66 _ = @import("behavior/cast.zig");
70 _ = @import("behavior/cast_int.zig");
6771 _ = @import("behavior/int128.zig");
68 _ = @import("behavior/optional.zig");
6972 _ = @import("behavior/translate_c_macros.zig");
70 _ = @import("behavior/try.zig");
71 _ = @import("behavior/src.zig");
7273
7374 if (builtin.zig_backend != .stage2_c) {
7475 // Tests that pass for stage1 and the llvm backend.
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}