authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-05-20 21:33:48+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-05-20 21:33:48+02:00
log56d8a1c89c4dd66c497ed37eac0643c8a1834597
tree1223bef9d5b0f2b9a8a59209d0346fa0a21f4d05
parent38b83d9d93db400e8103f02eeb77729040bd3666
parent832330094c00391ecd6f0ea4abf2d05261b5a10c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15663 from Luukdegram/wasm-test-runner

wasm: enable standard test runner

6 files changed, 432 insertions(+), 244 deletions(-)

lib/test_runner.zig+1-3
......@@ -12,9 +12,7 @@ var cmdline_buffer: [4096]u8 = undefined;
1212var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
1313
1414pub fn main() void {
15 if (builtin.zig_backend == .stage2_wasm or
16 builtin.zig_backend == .stage2_aarch64)
17 {
15 if (builtin.zig_backend == .stage2_aarch64) {
1816 return mainSimple() catch @panic("test failure");
1917 }
2018
src/arch/wasm/CodeGen.zig+431-224
......@@ -29,6 +29,9 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset;
2929
3030/// Wasm Value, created when generating an instruction
3131const WValue = union(enum) {
32 /// `WValue` which has been freed and may no longer hold
33 /// any references.
34 dead: void,
3235 /// May be referenced but is unused
3336 none: void,
3437 /// The value lives on top of the stack
......@@ -86,6 +89,7 @@ const WValue = union(enum) {
8689 fn offset(value: WValue) u32 {
8790 switch (value) {
8891 .stack_offset => |stack_offset| return stack_offset.value,
92 .dead => unreachable,
8993 else => return 0,
9094 }
9195 }
......@@ -123,7 +127,8 @@ const WValue = union(enum) {
123127 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,
124128 .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return,
125129 }
126 value.* = undefined;
130 log.debug("freed local ({d}) of type {}", .{ local_value, valtype });
131 value.* = .dead;
127132 }
128133};
129134
......@@ -832,6 +837,7 @@ const Branch = struct {
832837
833838 fn deinit(branch: *Branch, gpa: Allocator) void {
834839 branch.values.deinit(gpa);
840 branch.* = undefined;
835841 }
836842};
837843
......@@ -880,7 +886,11 @@ fn processDeath(func: *CodeGen, ref: Air.Inst.Ref) void {
880886 // TODO: Upon branch consolidation free any locals if needed.
881887 const value = func.currentBranch().values.getPtr(ref) orelse return;
882888 if (value.* != .local) return;
883 log.debug("Decreasing reference for ref: %{?d}\n", .{Air.refToIndex(ref)});
889 const reserved_indexes = func.args.len + @boolToInt(func.return_value != .none);
890 if (value.local.value < reserved_indexes) {
891 return; // function arguments can never be re-used
892 }
893 log.debug("Decreasing reference for ref: %{?d}, using local '{d}'", .{ Air.refToIndex(ref), value.local.value });
884894 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer
885895 if (value.local.references == 0) {
886896 value.free(func);
......@@ -1026,6 +1036,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 {
10261036/// Writes the bytecode depending on the given `WValue` in `val`
10271037fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
10281038 switch (value) {
1039 .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?)
10291040 .none, .stack => {}, // no-op
10301041 .local => |idx| try func.addLabel(.local_get, idx.value),
10311042 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),
......@@ -1082,27 +1093,27 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
10821093 const valtype = typeToValtype(ty, func.target);
10831094 switch (valtype) {
10841095 .i32 => if (func.free_locals_i32.popOrNull()) |index| {
1085 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1096 log.debug("reusing local ({d}) of type {}", .{ index, valtype });
10861097 return WValue{ .local = .{ .value = index, .references = 1 } };
10871098 },
10881099 .i64 => if (func.free_locals_i64.popOrNull()) |index| {
1089 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1100 log.debug("reusing local ({d}) of type {}", .{ index, valtype });
10901101 return WValue{ .local = .{ .value = index, .references = 1 } };
10911102 },
10921103 .f32 => if (func.free_locals_f32.popOrNull()) |index| {
1093 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1104 log.debug("reusing local ({d}) of type {}", .{ index, valtype });
10941105 return WValue{ .local = .{ .value = index, .references = 1 } };
10951106 },
10961107 .f64 => if (func.free_locals_f64.popOrNull()) |index| {
1097 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1108 log.debug("reusing local ({d}) of type {}", .{ index, valtype });
10981109 return WValue{ .local = .{ .value = index, .references = 1 } };
10991110 },
11001111 .v128 => if (func.free_locals_v128.popOrNull()) |index| {
1101 log.debug("reusing local ({d}) of type {}\n", .{ index, valtype });
1112 log.debug("reusing local ({d}) of type {}", .{ index, valtype });
11021113 return WValue{ .local = .{ .value = index, .references = 1 } };
11031114 },
11041115 }
1105 log.debug("new local of type {}\n", .{valtype});
1116 log.debug("new local of type {}", .{valtype});
11061117 // no local was free to be re-used, so allocate a new local instead
11071118 return func.ensureAllocLocal(ty);
11081119}
......@@ -1222,6 +1233,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
12221233 defer {
12231234 var outer_branch = func.branches.pop();
12241235 outer_branch.deinit(func.gpa);
1236 assert(func.branches.items.len == 0); // missing branch merge
12251237 }
12261238 // Generate MIR for function body
12271239 try func.genBody(func.air.getMainBody());
......@@ -1242,7 +1254,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
12421254
12431255 // check if we have to initialize and allocate anything into the stack frame.
12441256 // If so, create enough stack space and insert the instructions at the front of the list.
1245 if (func.stack_size > 0) {
1257 if (func.initial_stack_value != .none) {
12461258 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
12471259 defer prologue.deinit();
12481260
......@@ -1593,10 +1605,16 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
15931605 else => {},
15941606 }
15951607
1596 // TODO: We should probably lower this to a call to compiler_rt
1597 // But for now, we implement it manually
1598 var offset = try func.ensureAllocLocal(Type.usize); // local for counter
1608 // allocate a local for the offset, and set it to 0.
1609 // This to ensure that inside loops we correctly re-set the counter.
1610 var offset = try func.allocLocal(Type.usize); // local for counter
15991611 defer offset.free(func);
1612 switch (func.arch()) {
1613 .wasm32 => try func.addImm32(0),
1614 .wasm64 => try func.addImm64(0),
1615 else => unreachable,
1616 }
1617 try func.addLabel(.local_set, offset.local.value);
16001618
16011619 // outer block to jump to when loop is done
16021620 try func.startBlock(.block, wasm.block_empty);
......@@ -1796,10 +1814,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
17961814 .subwrap => func.airWrapBinOp(inst, .sub),
17971815 .mul => func.airBinOp(inst, .mul),
17981816 .mulwrap => func.airWrapBinOp(inst, .mul),
1799 .div_float,
1800 .div_exact,
1801 .div_trunc,
1802 => func.airDiv(inst),
1817 .div_float, .div_exact => func.airDiv(inst),
1818 .div_trunc => func.airDivTrunc(inst),
18031819 .div_floor => func.airDivFloor(inst),
18041820 .bit_and => func.airBinOp(inst, .@"and"),
18051821 .bit_or => func.airBinOp(inst, .@"or"),
......@@ -1963,11 +1979,11 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19631979 .tag_name => func.airTagName(inst),
19641980
19651981 .error_set_has_value => func.airErrorSetHasValue(inst),
1982 .frame_addr => func.airFrameAddress(inst),
19661983
19671984 .mul_sat,
19681985 .mod,
19691986 .assembly,
1970 .frame_addr,
19711987 .bit_reverse,
19721988 .is_err_ptr,
19731989 .is_non_err_ptr,
......@@ -2110,16 +2126,13 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
21102126 const un_op = func.air.instructions.items(.data)[inst].un_op;
21112127 const operand = try func.resolveInst(un_op);
21122128 const ret_ty = func.air.typeOf(un_op).childType();
2129
2130 const fn_info = func.decl.ty.fnInfo();
21132131 if (!ret_ty.hasRuntimeBitsIgnoreComptime()) {
21142132 if (ret_ty.isError()) {
21152133 try func.addImm32(0);
2116 } else {
2117 return func.finishAir(inst, .none, &.{});
21182134 }
2119 }
2120
2121 const fn_info = func.decl.ty.fnInfo();
2122 if (!firstParamSRet(fn_info.cc, fn_info.return_type, func.target)) {
2135 } else if (!firstParamSRet(fn_info.cc, fn_info.return_type, func.target)) {
21232136 // leave on the stack
21242137 _ = try func.load(operand, ret_ty, 0);
21252138 }
......@@ -2523,10 +2536,34 @@ fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
25232536 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
25242537 const lhs = try func.resolveInst(bin_op.lhs);
25252538 const rhs = try func.resolveInst(bin_op.rhs);
2526 const ty = func.air.typeOf(bin_op.lhs);
2539 const lhs_ty = func.air.typeOf(bin_op.lhs);
2540 const rhs_ty = func.air.typeOf(bin_op.rhs);
2541
2542 // For certain operations, such as shifting, the types are different.
2543 // When converting this to a WebAssembly type, they *must* match to perform
2544 // an operation. For this reason we verify if the WebAssembly type is different, in which
2545 // case we first coerce the operands to the same type before performing the operation.
2546 // For big integers we can ignore this as we will call into compiler-rt which handles this.
2547 const result = switch (op) {
2548 .shr, .shl => res: {
2549 const lhs_wasm_bits = toWasmBits(@intCast(u16, lhs_ty.bitSize(func.target))) orelse {
2550 return func.fail("TODO: implement '{s}' for types larger than 128 bits", .{@tagName(op)});
2551 };
2552 const rhs_wasm_bits = toWasmBits(@intCast(u16, rhs_ty.bitSize(func.target))).?;
2553 const new_rhs = if (lhs_wasm_bits != rhs_wasm_bits and lhs_wasm_bits != 128) blk: {
2554 const tmp = try func.intcast(rhs, rhs_ty, lhs_ty);
2555 break :blk try tmp.toLocal(func, lhs_ty);
2556 } else rhs;
2557 const stack_result = try func.binOp(lhs, new_rhs, lhs_ty, op);
2558 break :res try stack_result.toLocal(func, lhs_ty);
2559 },
2560 else => res: {
2561 const stack_result = try func.binOp(lhs, rhs, lhs_ty, op);
2562 break :res try stack_result.toLocal(func, lhs_ty);
2563 },
2564 };
25272565
2528 const stack_value = try func.binOp(lhs, rhs, ty, op);
2529 func.finishAir(inst, try stack_value.toLocal(func, ty), &.{ bin_op.lhs, bin_op.rhs });
2566 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
25302567}
25312568
25322569/// Performs a binary operation on the given `WValue`'s
......@@ -2565,37 +2602,56 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!
25652602
25662603fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
25672604 if (ty.intInfo(func.target).bits > 128) {
2568 return func.fail("TODO: Implement binary operation for big integer", .{});
2605 return func.fail("TODO: Implement binary operation for big integers larger than 128 bits", .{});
25692606 }
25702607
2571 if (op != .add and op != .sub) {
2572 return func.fail("TODO: Implement binary operation for big integers", .{});
2573 }
2574
2575 const result = try func.allocStack(ty);
2576 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
2577 defer lhs_high_bit.free(func);
2578 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
2579 defer rhs_high_bit.free(func);
2580 var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64);
2581 defer high_op_res.free(func);
2582
2583 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
2584 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
2585 const low_op_res = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
2608 switch (op) {
2609 .mul => return func.callIntrinsic("__multi3", &.{ ty, ty }, ty, &.{ lhs, rhs }),
2610 .shr => return func.callIntrinsic("__lshrti3", &.{ ty, Type.i32 }, ty, &.{ lhs, rhs }),
2611 .shl => return func.callIntrinsic("__ashlti3", &.{ ty, Type.i32 }, ty, &.{ lhs, rhs }),
2612 .xor => {
2613 const result = try func.allocStack(ty);
2614 try func.emitWValue(result);
2615 const lhs_high_bit = try func.load(lhs, Type.u64, 0);
2616 const rhs_high_bit = try func.load(rhs, Type.u64, 0);
2617 const xor_high_bit = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor);
2618 try func.store(.stack, xor_high_bit, Type.u64, result.offset());
25862619
2587 const lt = if (op == .add) blk: {
2588 break :blk try func.cmp(high_op_res, rhs_high_bit, Type.u64, .lt);
2589 } else if (op == .sub) blk: {
2590 break :blk try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
2591 } else unreachable;
2592 const tmp = try func.intcast(lt, Type.u32, Type.u64);
2593 var tmp_op = try (try func.binOp(low_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64);
2594 defer tmp_op.free(func);
2620 try func.emitWValue(result);
2621 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
2622 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
2623 const xor_low_bit = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
2624 try func.store(.stack, xor_low_bit, Type.u64, result.offset() + 8);
2625 return result;
2626 },
2627 .add, .sub => {
2628 const result = try func.allocStack(ty);
2629 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
2630 defer lhs_high_bit.free(func);
2631 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
2632 defer rhs_high_bit.free(func);
2633 var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64);
2634 defer high_op_res.free(func);
25952635
2596 try func.store(result, high_op_res, Type.u64, 0);
2597 try func.store(result, tmp_op, Type.u64, 8);
2598 return result;
2636 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
2637 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
2638 const low_op_res = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
2639
2640 const lt = if (op == .add) blk: {
2641 break :blk try func.cmp(high_op_res, rhs_high_bit, Type.u64, .lt);
2642 } else if (op == .sub) blk: {
2643 break :blk try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
2644 } else unreachable;
2645 const tmp = try func.intcast(lt, Type.u32, Type.u64);
2646 var tmp_op = try (try func.binOp(low_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64);
2647 defer tmp_op.free(func);
2648
2649 try func.store(result, high_op_res, Type.u64, 0);
2650 try func.store(result, tmp_op, Type.u64, 8);
2651 return result;
2652 },
2653 else => return func.fail("TODO: Implement binary operation for big integers: '{s}'", .{@tagName(op)}),
2654 }
25992655}
26002656
26012657const FloatOp = enum {
......@@ -2751,14 +2807,38 @@ fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
27512807
27522808 const lhs = try func.resolveInst(bin_op.lhs);
27532809 const rhs = try func.resolveInst(bin_op.rhs);
2754 const ty = func.air.typeOf(bin_op.lhs);
2810 const lhs_ty = func.air.typeOf(bin_op.lhs);
2811 const rhs_ty = func.air.typeOf(bin_op.rhs);
27552812
2756 if (ty.zigTypeTag() == .Vector) {
2813 if (lhs_ty.zigTypeTag() == .Vector or rhs_ty.zigTypeTag() == .Vector) {
27572814 return func.fail("TODO: Implement wrapping arithmetic for vectors", .{});
27582815 }
27592816
2760 const result = try (try func.wrapBinOp(lhs, rhs, ty, op)).toLocal(func, ty);
2761 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
2817 // For certain operations, such as shifting, the types are different.
2818 // When converting this to a WebAssembly type, they *must* match to perform
2819 // an operation. For this reason we verify if the WebAssembly type is different, in which
2820 // case we first coerce the operands to the same type before performing the operation.
2821 // For big integers we can ignore this as we will call into compiler-rt which handles this.
2822 const result = switch (op) {
2823 .shr, .shl => res: {
2824 const lhs_wasm_bits = toWasmBits(@intCast(u16, lhs_ty.bitSize(func.target))) orelse {
2825 return func.fail("TODO: implement '{s}' for types larger than 128 bits", .{@tagName(op)});
2826 };
2827 const rhs_wasm_bits = toWasmBits(@intCast(u16, rhs_ty.bitSize(func.target))).?;
2828 const new_rhs = if (lhs_wasm_bits != rhs_wasm_bits and lhs_wasm_bits != 128) blk: {
2829 const tmp = try func.intcast(rhs, rhs_ty, lhs_ty);
2830 break :blk try tmp.toLocal(func, lhs_ty);
2831 } else rhs;
2832 const stack_result = try func.wrapBinOp(lhs, new_rhs, lhs_ty, op);
2833 break :res try stack_result.toLocal(func, lhs_ty);
2834 },
2835 else => res: {
2836 const stack_result = try func.wrapBinOp(lhs, rhs, lhs_ty, op);
2837 break :res try stack_result.toLocal(func, lhs_ty);
2838 },
2839 };
2840
2841 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
27622842}
27632843
27642844/// Performs a wrapping binary operation.
......@@ -2810,26 +2890,25 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
28102890 return WValue{ .stack = {} };
28112891}
28122892
2813fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue {
2893fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue {
28142894 switch (ptr_val.tag()) {
28152895 .decl_ref_mut => {
28162896 const decl_index = ptr_val.castTag(.decl_ref_mut).?.data.decl_index;
2817 return func.lowerParentPtrDecl(ptr_val, decl_index);
2897 return func.lowerParentPtrDecl(ptr_val, decl_index, offset);
28182898 },
28192899 .decl_ref => {
28202900 const decl_index = ptr_val.castTag(.decl_ref).?.data;
2821 return func.lowerParentPtrDecl(ptr_val, decl_index);
2901 return func.lowerParentPtrDecl(ptr_val, decl_index, offset);
28222902 },
28232903 .variable => {
28242904 const decl_index = ptr_val.castTag(.variable).?.data.owner_decl;
2825 return func.lowerParentPtrDecl(ptr_val, decl_index);
2905 return func.lowerParentPtrDecl(ptr_val, decl_index, offset);
28262906 },
28272907 .field_ptr => {
28282908 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
28292909 const parent_ty = field_ptr.container_ty;
2830 const parent_ptr = try func.lowerParentPtr(field_ptr.container_ptr, parent_ty);
28312910
2832 const offset = switch (parent_ty.zigTypeTag()) {
2911 const field_offset = switch (parent_ty.zigTypeTag()) {
28332912 .Struct => switch (parent_ty.containerLayout()) {
28342913 .Packed => parent_ty.packedStructFieldByteOffset(field_ptr.field_index, func.target),
28352914 else => parent_ty.structFieldOffset(field_ptr.field_index, func.target),
......@@ -2842,8 +2921,8 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError
28422921 if (layout.payload_align > layout.tag_align) break :blk 0;
28432922
28442923 // tag is stored first so calculate offset from where payload starts
2845 const offset = @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align));
2846 break :blk offset;
2924 const field_offset = @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align));
2925 break :blk field_offset;
28472926 },
28482927 },
28492928 .Pointer => switch (parent_ty.ptrSize()) {
......@@ -2856,43 +2935,23 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError
28562935 },
28572936 else => unreachable,
28582937 };
2859
2860 return switch (parent_ptr) {
2861 .memory => |ptr| WValue{
2862 .memory_offset = .{
2863 .pointer = ptr,
2864 .offset = @intCast(u32, offset),
2865 },
2866 },
2867 .memory_offset => |mem_off| WValue{
2868 .memory_offset = .{
2869 .pointer = mem_off.pointer,
2870 .offset = @intCast(u32, offset) + mem_off.offset,
2871 },
2872 },
2873 else => unreachable,
2874 };
2938 return func.lowerParentPtr(field_ptr.container_ptr, offset + @intCast(u32, field_offset));
28752939 },
28762940 .elem_ptr => {
28772941 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
28782942 const index = elem_ptr.index;
2879 const offset = index * ptr_child_ty.abiSize(func.target);
2880 const array_ptr = try func.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty);
2881
2882 return WValue{ .memory_offset = .{
2883 .pointer = array_ptr.memory,
2884 .offset = @intCast(u32, offset),
2885 } };
2943 const elem_offset = index * elem_ptr.elem_ty.abiSize(func.target);
2944 return func.lowerParentPtr(elem_ptr.array_ptr, offset + @intCast(u32, elem_offset));
28862945 },
28872946 .opt_payload_ptr => {
28882947 const payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
2889 return func.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty);
2948 return func.lowerParentPtr(payload_ptr.container_ptr, offset);
28902949 },
28912950 else => |tag| return func.fail("TODO: Implement lowerParentPtr for tag: {}", .{tag}),
28922951 }
28932952}
28942953
2895fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: Module.Decl.Index) InnerError!WValue {
2954fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: Module.Decl.Index, offset: u32) InnerError!WValue {
28962955 const module = func.bin_file.base.options.module.?;
28972956 const decl = module.declPtr(decl_index);
28982957 module.markDeclAlive(decl);
......@@ -2901,10 +2960,10 @@ fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: Module.Decl.In
29012960 .data = decl.ty,
29022961 };
29032962 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2904 return func.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index);
2963 return func.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index, offset);
29052964}
29062965
2907fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!WValue {
2966fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Index, offset: u32) InnerError!WValue {
29082967 if (tv.ty.isSlice()) {
29092968 return WValue{ .memory = try func.bin_file.lowerUnnamedConst(tv, decl_index) };
29102969 }
......@@ -2923,7 +2982,9 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind
29232982 if (decl.ty.zigTypeTag() == .Fn) {
29242983 try func.bin_file.addTableFunction(target_sym_index);
29252984 return WValue{ .function_index = target_sym_index };
2926 } else return WValue{ .memory = target_sym_index };
2985 } else if (offset == 0) {
2986 return WValue{ .memory = target_sym_index };
2987 } else return WValue{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } };
29272988}
29282989
29292990/// Converts a signed integer to its 2's complement form and returns
......@@ -2950,11 +3011,11 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
29503011 if (val.isUndefDeep()) return func.emitUndefined(ty);
29513012 if (val.castTag(.decl_ref)) |decl_ref| {
29523013 const decl_index = decl_ref.data;
2953 return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
3014 return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index, 0);
29543015 }
29553016 if (val.castTag(.decl_ref_mut)) |decl_ref_mut| {
29563017 const decl_index = decl_ref_mut.data.decl_index;
2957 return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
3018 return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index, 0);
29583019 }
29593020 const target = func.target;
29603021 switch (ty.zigTypeTag()) {
......@@ -2988,9 +3049,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
29883049 else => unreachable,
29893050 },
29903051 .Pointer => switch (val.tag()) {
2991 .field_ptr, .elem_ptr, .opt_payload_ptr => {
2992 return func.lowerParentPtr(val, ty.childType());
2993 },
3052 .field_ptr, .elem_ptr, .opt_payload_ptr => return func.lowerParentPtr(val, 0),
29943053 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) },
29953054 .zero, .null_value => return WValue{ .imm32 = 0 },
29963055 else => return func.fail("Wasm TODO: lowerConstant for other const pointer tag {}", .{val.tag()}),
......@@ -3182,9 +3241,13 @@ fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
31823241 .label = func.block_depth,
31833242 .value = block_result,
31843243 });
3244
31853245 try func.genBody(body);
31863246 try func.endBlock();
31873247
3248 const liveness = func.liveness.getBlock(inst);
3249 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths.len);
3250
31883251 func.finishAir(inst, block_result, &.{});
31893252}
31903253
......@@ -3239,41 +3302,31 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
32393302 try func.addLabel(.br_if, 0);
32403303
32413304 try func.branches.ensureUnusedCapacity(func.gpa, 2);
3242
3243 func.branches.appendAssumeCapacity(.{});
3244 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len));
3245 try func.genBody(else_body);
3246 try func.endBlock();
3247 var else_stack = func.branches.pop();
3248 defer else_stack.deinit(func.gpa);
3305 {
3306 func.branches.appendAssumeCapacity(.{});
3307 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len));
3308 defer {
3309 var else_stack = func.branches.pop();
3310 else_stack.deinit(func.gpa);
3311 }
3312 try func.genBody(else_body);
3313 try func.endBlock();
3314 }
32493315
32503316 // Outer block that matches the condition
3251 func.branches.appendAssumeCapacity(.{});
3252 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len));
3253 try func.genBody(then_body);
3254 var then_stack = func.branches.pop();
3255 defer then_stack.deinit(func.gpa);
3256
3257 try func.mergeBranch(&else_stack);
3258 try func.mergeBranch(&then_stack);
3317 {
3318 func.branches.appendAssumeCapacity(.{});
3319 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len));
3320 defer {
3321 var then_stack = func.branches.pop();
3322 then_stack.deinit(func.gpa);
3323 }
3324 try func.genBody(then_body);
3325 }
32593326
32603327 func.finishAir(inst, .none, &.{});
32613328}
32623329
3263fn mergeBranch(func: *CodeGen, branch: *const Branch) !void {
3264 const parent = func.currentBranch();
3265
3266 const target_slice = branch.values.entries.slice();
3267 const target_keys = target_slice.items(.key);
3268 const target_values = target_slice.items(.value);
3269
3270 try parent.values.ensureTotalCapacity(func.gpa, parent.values.capacity() + branch.values.count());
3271 for (target_keys, 0..) |key, index| {
3272 // TODO: process deaths from branches
3273 parent.values.putAssumeCapacity(key, target_values[index]);
3274 }
3275}
3276
32773330fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
32783331 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
32793332
......@@ -3783,30 +3836,25 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
37833836 }
37843837 }
37853838 func.branches.appendAssumeCapacity(.{});
3786
37873839 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len);
3788 for (liveness.deaths[index]) |operand| {
3789 func.processDeath(Air.indexToRef(operand));
3840 defer {
3841 var case_branch = func.branches.pop();
3842 case_branch.deinit(func.gpa);
37903843 }
37913844 try func.genBody(case.body);
37923845 try func.endBlock();
3793 var case_branch = func.branches.pop();
3794 defer case_branch.deinit(func.gpa);
3795 try func.mergeBranch(&case_branch);
37963846 }
37973847
37983848 if (has_else_body) {
37993849 func.branches.appendAssumeCapacity(.{});
38003850 const else_deaths = liveness.deaths.len - 1;
38013851 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[else_deaths].len);
3802 for (liveness.deaths[else_deaths]) |operand| {
3803 func.processDeath(Air.indexToRef(operand));
3852 defer {
3853 var else_branch = func.branches.pop();
3854 else_branch.deinit(func.gpa);
38043855 }
38053856 try func.genBody(else_body);
38063857 try func.endBlock();
3807 var else_branch = func.branches.pop();
3808 defer else_branch.deinit(func.gpa);
3809 try func.mergeBranch(&else_branch);
38103858 }
38113859 func.finishAir(inst, .none, &.{});
38123860}
......@@ -3935,7 +3983,7 @@ fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
39353983 // write 'undefined' to the payload
39363984 const payload_ptr = try func.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, func.target)), .new);
39373985 const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(func.target));
3938 try func.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa });
3986 try func.memset(Type.u8, payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaa });
39393987
39403988 break :result err_union;
39413989 };
......@@ -3955,7 +4003,13 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
39554003 return func.fail("todo Wasm intcast for bitsize > 128", .{});
39564004 }
39574005
3958 const result = try (try func.intcast(operand, operand_ty, ty)).toLocal(func, ty);
4006 const op_bits = toWasmBits(@intCast(u16, operand_ty.bitSize(func.target))).?;
4007 const wanted_bits = toWasmBits(@intCast(u16, ty.bitSize(func.target))).?;
4008 const result = if (op_bits == wanted_bits)
4009 func.reuseOperand(ty_op.operand, operand)
4010 else
4011 try (try func.intcast(operand, operand_ty, ty)).toLocal(func, ty);
4012
39594013 func.finishAir(inst, result, &.{});
39604014}
39614015
......@@ -4423,7 +4477,14 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void
44234477 .One => @as(WValue, .{ .imm32 = @intCast(u32, ptr_ty.childType().arrayLen()) }),
44244478 .C, .Many => unreachable,
44254479 };
4426 try func.memset(ptr, len, value);
4480
4481 const elem_ty = if (ptr_ty.ptrSize() == .One)
4482 ptr_ty.childType().childType()
4483 else
4484 ptr_ty.childType();
4485
4486 const dst_ptr = try func.sliceOrArrayPtr(ptr, ptr_ty);
4487 try func.memset(elem_ty, dst_ptr, len, value);
44274488
44284489 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
44294490}
......@@ -4432,10 +4493,12 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void
44324493/// When the user has enabled the bulk_memory feature, we lower
44334494/// this to wasm's memset instruction. When the feature is not present,
44344495/// we implement it manually.
4435fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!void {
4496fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void {
4497 const abi_size = @intCast(u32, elem_ty.abiSize(func.target));
4498
44364499 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
4437 // If not, we lower it ourselves
4438 if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory)) {
4500 // If not, we lower it ourselves.
4501 if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory) and abi_size == 1) {
44394502 try func.lowerToStack(ptr);
44404503 try func.emitWValue(value);
44414504 try func.emitWValue(len);
......@@ -4443,74 +4506,79 @@ fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!vo
44434506 return;
44444507 }
44454508
4446 // When the length is comptime-known we do the loop at codegen, rather
4447 // than emitting a runtime loop into the binary
4448 switch (len) {
4449 .imm32, .imm64 => {
4450 const length = switch (len) {
4451 .imm32 => |val| val,
4452 .imm64 => |val| val,
4453 else => unreachable,
4454 };
4455
4456 var offset: u32 = 0;
4457 const base = ptr.offset();
4458 while (offset < length) : (offset += 1) {
4459 try func.emitWValue(ptr);
4460 try func.emitWValue(value);
4461 switch (func.arch()) {
4462 .wasm32 => {
4463 try func.addMemArg(.i32_store8, .{ .offset = base + offset, .alignment = 1 });
4464 },
4465 .wasm64 => {
4466 try func.addMemArg(.i64_store8, .{ .offset = base + offset, .alignment = 1 });
4467 },
4468 else => unreachable,
4469 }
4470 }
4471 },
4472 else => {
4473 // TODO: We should probably lower this to a call to compiler_rt
4474 // But for now, we implement it manually
4475 const offset = try func.ensureAllocLocal(Type.usize); // local for counter
4476 // outer block to jump to when loop is done
4477 try func.startBlock(.block, wasm.block_empty);
4478 try func.startBlock(.loop, wasm.block_empty);
4479 try func.emitWValue(offset);
4509 const final_len = switch (len) {
4510 .imm32 => |val| WValue{ .imm32 = val * abi_size },
4511 .imm64 => |val| WValue{ .imm64 = val * abi_size },
4512 else => if (abi_size != 1) blk: {
4513 const new_len = try func.ensureAllocLocal(Type.usize);
44804514 try func.emitWValue(len);
44814515 switch (func.arch()) {
4482 .wasm32 => try func.addTag(.i32_eq),
4483 .wasm64 => try func.addTag(.i64_eq),
4484 else => unreachable,
4485 }
4486 try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
4487 try func.emitWValue(ptr);
4488 try func.emitWValue(offset);
4489 switch (func.arch()) {
4490 .wasm32 => try func.addTag(.i32_add),
4491 .wasm64 => try func.addTag(.i64_add),
4492 else => unreachable,
4493 }
4494 try func.emitWValue(value);
4495 const mem_store_op: Mir.Inst.Tag = switch (func.arch()) {
4496 .wasm32 => .i32_store8,
4497 .wasm64 => .i64_store8,
4498 else => unreachable,
4499 };
4500 try func.addMemArg(mem_store_op, .{ .offset = ptr.offset(), .alignment = 1 });
4501 try func.emitWValue(offset);
4502 try func.addImm32(1);
4503 switch (func.arch()) {
4504 .wasm32 => try func.addTag(.i32_add),
4505 .wasm64 => try func.addTag(.i64_add),
4516 .wasm32 => {
4517 try func.emitWValue(.{ .imm32 = abi_size });
4518 try func.addTag(.i32_mul);
4519 },
4520 .wasm64 => {
4521 try func.emitWValue(.{ .imm64 = abi_size });
4522 try func.addTag(.i64_mul);
4523 },
45064524 else => unreachable,
45074525 }
4508 try func.addLabel(.local_set, offset.local.value);
4509 try func.addLabel(.br, 0); // jump to start of loop
4510 try func.endBlock();
4511 try func.endBlock();
4526 try func.addLabel(.local_set, new_len.local.value);
4527 break :blk new_len;
4528 } else len,
4529 };
4530
4531 var end_ptr = try func.allocLocal(Type.usize);
4532 defer end_ptr.free(func);
4533 var new_ptr = try func.buildPointerOffset(ptr, 0, .new);
4534 defer new_ptr.free(func);
4535
4536 // get the loop conditional: if current pointer address equals final pointer's address
4537 try func.lowerToStack(ptr);
4538 try func.emitWValue(final_len);
4539 switch (func.arch()) {
4540 .wasm32 => try func.addTag(.i32_add),
4541 .wasm64 => try func.addTag(.i64_add),
4542 else => unreachable,
4543 }
4544 try func.addLabel(.local_set, end_ptr.local.value);
4545
4546 // outer block to jump to when loop is done
4547 try func.startBlock(.block, wasm.block_empty);
4548 try func.startBlock(.loop, wasm.block_empty);
4549
4550 // check for codition for loop end
4551 try func.emitWValue(new_ptr);
4552 try func.emitWValue(end_ptr);
4553 switch (func.arch()) {
4554 .wasm32 => try func.addTag(.i32_eq),
4555 .wasm64 => try func.addTag(.i64_eq),
4556 else => unreachable,
4557 }
4558 try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished)
4559
4560 // store the value at the current position of the pointer
4561 try func.store(new_ptr, value, elem_ty, 0);
4562
4563 // move the pointer to the next element
4564 try func.emitWValue(new_ptr);
4565 switch (func.arch()) {
4566 .wasm32 => {
4567 try func.emitWValue(.{ .imm32 = abi_size });
4568 try func.addTag(.i32_add);
45124569 },
4570 .wasm64 => {
4571 try func.emitWValue(.{ .imm64 = abi_size });
4572 try func.addTag(.i64_add);
4573 },
4574 else => unreachable,
45134575 }
4576 try func.addLabel(.local_set, new_ptr.local.value);
4577
4578 // end of loop
4579 try func.addLabel(.br, 0); // jump to start of loop
4580 try func.endBlock();
4581 try func.endBlock();
45144582}
45154583
45164584fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -4825,8 +4893,15 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
48254893 const struct_obj = result_ty.castTag(.@"struct").?.data;
48264894 const fields = struct_obj.fields.values();
48274895 const backing_type = struct_obj.backing_int_ty;
4828 // we ensure a new local is created so it's zero-initialized
4829 const result = try func.ensureAllocLocal(backing_type);
4896
4897 // ensure the result is zero'd
4898 const result = try func.allocLocal(backing_type);
4899 if (struct_obj.backing_int_ty.bitSize(func.target) <= 32)
4900 try func.addImm32(0)
4901 else
4902 try func.addImm64(0);
4903 try func.addLabel(.local_set, result.local.value);
4904
48304905 var current_bit: u16 = 0;
48314906 for (elements, 0..) |elem, elem_index| {
48324907 const field = fields[elem_index];
......@@ -4884,8 +4959,15 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
48844959 else => unreachable,
48854960 }
48864961 };
4887 // TODO: this is incorrect Liveness handling code
4888 func.finishAir(inst, result, &.{});
4962
4963 if (elements.len <= Liveness.bpi - 1) {
4964 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
4965 @memcpy(buf[0..elements.len], elements);
4966 return func.finishAir(inst, result, &buf);
4967 }
4968 var bt = try func.iterateBigTomb(inst, elements.len);
4969 for (elements) |arg| bt.feed(arg);
4970 return bt.finishAir(result);
48894971}
48904972
48914973fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5212,7 +5294,7 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
52125294 const src_ty = func.air.typeOf(bin_op.rhs);
52135295 const len = switch (dst_ty.ptrSize()) {
52145296 .Slice => try func.sliceLen(dst),
5215 .One => @as(WValue, .{ .imm64 = dst_ty.childType().arrayLen() }),
5297 .One => @as(WValue, .{ .imm32 = @intCast(u32, dst_ty.childType().arrayLen()) }),
52165298 .C, .Many => unreachable,
52175299 };
52185300 const dst_ptr = try func.sliceOrArrayPtr(dst, dst_ty);
......@@ -5372,11 +5454,10 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
53725454 };
53735455
53745456 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty);
5375 defer bin_op.free(func);
53765457 var result = if (wasm_bits != int_info.bits) blk: {
53775458 break :blk try (try func.wrapOperand(bin_op, lhs_ty)).toLocal(func, lhs_ty);
53785459 } else bin_op;
5379 defer result.free(func); // no-op when wasm_bits == int_info.bits
5460 defer result.free(func);
53805461
53815462 const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt;
53825463 const overflow_bit: WValue = if (is_signed) blk: {
......@@ -5532,16 +5613,12 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55325613
55335614 const int_info = lhs_ty.intInfo(func.target);
55345615 const wasm_bits = toWasmBits(int_info.bits) orelse {
5535 return func.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits});
5536 };
5537
5538 if (wasm_bits > 32) {
55395616 return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});
5540 }
5617 };
55415618
55425619 const zero = switch (wasm_bits) {
55435620 32 => WValue{ .imm32 = 0 },
5544 64 => WValue{ .imm64 = 0 },
5621 64, 128 => WValue{ .imm64 = 0 },
55455622 else => unreachable,
55465623 };
55475624
......@@ -5568,7 +5645,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55685645 try func.addLabel(.local_set, overflow_bit.local.value);
55695646 break :blk down_cast;
55705647 }
5571 } else if (int_info.signedness == .signed) blk: {
5648 } else if (int_info.signedness == .signed and wasm_bits == 32) blk: {
55725649 const lhs_abs = try func.signAbsValue(lhs, lhs_ty);
55735650 const rhs_abs = try func.signAbsValue(rhs, lhs_ty);
55745651 const bin_op = try (try func.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(func, lhs_ty);
......@@ -5576,7 +5653,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55765653 _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq);
55775654 try func.addLabel(.local_set, overflow_bit.local.value);
55785655 break :blk try func.wrapOperand(bin_op, lhs_ty);
5579 } else blk: {
5656 } else if (wasm_bits == 32) blk: {
55805657 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(func, lhs_ty);
55815658 defer bin_op.free(func);
55825659 const shift_imm = if (wasm_bits == 32)
......@@ -5587,7 +5664,99 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55875664 _ = try func.cmp(shr, zero, lhs_ty, .neq);
55885665 try func.addLabel(.local_set, overflow_bit.local.value);
55895666 break :blk try func.wrapOperand(bin_op, lhs_ty);
5590 };
5667 } else if (int_info.bits == 64 and int_info.signedness == .unsigned) blk: {
5668 const new_ty = Type.initTag(.u128);
5669 var lhs_upcast = try (try func.intcast(lhs, lhs_ty, new_ty)).toLocal(func, lhs_ty);
5670 defer lhs_upcast.free(func);
5671 var rhs_upcast = try (try func.intcast(rhs, lhs_ty, new_ty)).toLocal(func, lhs_ty);
5672 defer rhs_upcast.free(func);
5673 const bin_op = try func.binOp(lhs_upcast, rhs_upcast, new_ty, .mul);
5674 const lsb = try func.load(bin_op, lhs_ty, 8);
5675 _ = try func.cmp(lsb, zero, lhs_ty, .neq);
5676 try func.addLabel(.local_set, overflow_bit.local.value);
5677
5678 break :blk try func.load(bin_op, lhs_ty, 0);
5679 } else if (int_info.bits == 64 and int_info.signedness == .signed) blk: {
5680 const shift_val: WValue = .{ .imm64 = 63 };
5681 var lhs_shifted = try (try func.binOp(lhs, shift_val, lhs_ty, .shr)).toLocal(func, lhs_ty);
5682 defer lhs_shifted.free(func);
5683 var rhs_shifted = try (try func.binOp(rhs, shift_val, lhs_ty, .shr)).toLocal(func, lhs_ty);
5684 defer rhs_shifted.free(func);
5685
5686 const bin_op = try func.callIntrinsic(
5687 "__multi3",
5688 &[_]Type{Type.i64} ** 4,
5689 Type.initTag(.i128),
5690 &.{ lhs, lhs_shifted, rhs, rhs_shifted },
5691 );
5692 const res = try func.allocLocal(lhs_ty);
5693 const msb = try func.load(bin_op, lhs_ty, 0);
5694 try func.addLabel(.local_tee, res.local.value);
5695 const msb_shifted = try func.binOp(msb, shift_val, lhs_ty, .shr);
5696 const lsb = try func.load(bin_op, lhs_ty, 8);
5697 _ = try func.cmp(lsb, msb_shifted, lhs_ty, .neq);
5698 try func.addLabel(.local_set, overflow_bit.local.value);
5699 break :blk res;
5700 } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: {
5701 var lhs_msb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
5702 defer lhs_msb.free(func);
5703 var lhs_lsb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
5704 defer lhs_lsb.free(func);
5705 var rhs_msb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
5706 defer rhs_msb.free(func);
5707 var rhs_lsb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
5708 defer rhs_lsb.free(func);
5709
5710 const mul1 = try func.callIntrinsic(
5711 "__multi3",
5712 &[_]Type{Type.i64} ** 4,
5713 Type.initTag(.i128),
5714 &.{ lhs_lsb, zero, rhs_msb, zero },
5715 );
5716 const mul2 = try func.callIntrinsic(
5717 "__multi3",
5718 &[_]Type{Type.i64} ** 4,
5719 Type.initTag(.i128),
5720 &.{ rhs_lsb, zero, lhs_msb, zero },
5721 );
5722 const mul3 = try func.callIntrinsic(
5723 "__multi3",
5724 &[_]Type{Type.i64} ** 4,
5725 Type.initTag(.i128),
5726 &.{ lhs_msb, zero, rhs_msb, zero },
5727 );
5728
5729 const rhs_lsb_not_zero = try func.cmp(rhs_lsb, zero, Type.u64, .neq);
5730 const lhs_lsb_not_zero = try func.cmp(lhs_lsb, zero, Type.u64, .neq);
5731 const lsb_and = try func.binOp(rhs_lsb_not_zero, lhs_lsb_not_zero, Type.bool, .@"and");
5732 const mul1_lsb = try func.load(mul1, Type.u64, 8);
5733 const mul1_lsb_not_zero = try func.cmp(mul1_lsb, zero, Type.u64, .neq);
5734 const lsb_or1 = try func.binOp(lsb_and, mul1_lsb_not_zero, Type.bool, .@"or");
5735 const mul2_lsb = try func.load(mul2, Type.u64, 8);
5736 const mul2_lsb_not_zero = try func.cmp(mul2_lsb, zero, Type.u64, .neq);
5737 const lsb_or = try func.binOp(lsb_or1, mul2_lsb_not_zero, Type.bool, .@"or");
5738
5739 const mul1_msb = try func.load(mul1, Type.u64, 0);
5740 const mul2_msb = try func.load(mul2, Type.u64, 0);
5741 const mul_add1 = try func.binOp(mul1_msb, mul2_msb, Type.u64, .add);
5742
5743 var mul3_lsb = try (try func.load(mul3, Type.u64, 8)).toLocal(func, Type.u64);
5744 defer mul3_lsb.free(func);
5745 var mul_add2 = try (try func.binOp(mul_add1, mul3_lsb, Type.u64, .add)).toLocal(func, Type.u64);
5746 defer mul_add2.free(func);
5747 const mul_add_lt = try func.cmp(mul_add2, mul3_lsb, Type.u64, .lt);
5748
5749 // result for overflow bit
5750 _ = try func.binOp(lsb_or, mul_add_lt, Type.bool, .@"or");
5751 try func.addLabel(.local_set, overflow_bit.local.value);
5752
5753 const tmp_result = try func.allocStack(Type.initTag(.u128));
5754 try func.emitWValue(tmp_result);
5755 const mul3_msb = try func.load(mul3, Type.u64, 0);
5756 try func.store(.stack, mul3_msb, Type.u64, tmp_result.offset());
5757 try func.store(tmp_result, mul_add2, Type.u64, 8);
5758 break :blk tmp_result;
5759 } else return func.fail("TODO: @mulWithOverflow for integers between 32 and 64 bits", .{});
55915760 var bin_op_local = try bin_op.toLocal(func, lhs_ty);
55925761 defer bin_op_local.free(func);
55935762
......@@ -5821,7 +5990,7 @@ fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58215990 const extra = func.air.extraData(Air.Try, pl_op.payload);
58225991 const body = func.air.extra[extra.end..][0..extra.data.body_len];
58235992 const err_union_ty = func.air.typeOf(pl_op.operand);
5824 const result = try lowerTry(func, err_union, body, err_union_ty, false);
5993 const result = try lowerTry(func, inst, err_union, body, err_union_ty, false);
58255994 func.finishAir(inst, result, &.{pl_op.operand});
58265995}
58275996
......@@ -5831,12 +6000,13 @@ fn airTryPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58316000 const err_union_ptr = try func.resolveInst(extra.data.ptr);
58326001 const body = func.air.extra[extra.end..][0..extra.data.body_len];
58336002 const err_union_ty = func.air.typeOf(extra.data.ptr).childType();
5834 const result = try lowerTry(func, err_union_ptr, body, err_union_ty, true);
6003 const result = try lowerTry(func, inst, err_union_ptr, body, err_union_ty, true);
58356004 func.finishAir(inst, result, &.{extra.data.ptr});
58366005}
58376006
58386007fn lowerTry(
58396008 func: *CodeGen,
6009 inst: Air.Inst.Index,
58406010 err_union: WValue,
58416011 body: []const Air.Inst.Index,
58426012 err_union_ty: Type,
......@@ -5864,6 +6034,14 @@ fn lowerTry(
58646034 }
58656035 try func.addTag(.i32_eqz);
58666036 try func.addLabel(.br_if, 0); // jump out of block when error is '0'
6037
6038 const liveness = func.liveness.getCondBr(inst);
6039 try func.branches.append(func.gpa, .{});
6040 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.else_deaths.len + liveness.then_deaths.len);
6041 defer {
6042 var branch = func.branches.pop();
6043 branch.deinit(func.gpa);
6044 }
58676045 try func.genBody(body);
58686046 try func.endBlock();
58696047 }
......@@ -5965,6 +6143,26 @@ fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59656143 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
59666144}
59676145
6146fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6147 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
6148
6149 const ty = func.air.typeOfIndex(inst);
6150 const lhs = try func.resolveInst(bin_op.lhs);
6151 const rhs = try func.resolveInst(bin_op.rhs);
6152
6153 const div_result = if (ty.isSignedInt())
6154 try func.divSigned(lhs, rhs, ty)
6155 else
6156 try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty);
6157
6158 if (ty.isAnyFloat()) {
6159 const trunc_result = try (try func.floatOp(.trunc, ty, &.{div_result})).toLocal(func, ty);
6160 return func.finishAir(inst, trunc_result, &.{ bin_op.lhs, bin_op.rhs });
6161 }
6162
6163 return func.finishAir(inst, div_result, &.{ bin_op.lhs, bin_op.rhs });
6164}
6165
59686166fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59696167 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
59706168
......@@ -6969,3 +7167,12 @@ fn airAtomicStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
69697167
69707168 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
69717169}
7170
7171fn airFrameAddress(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7172 if (func.initial_stack_value == .none) {
7173 try func.initializeStack();
7174 }
7175 try func.emitWValue(func.bottom_stack_value);
7176 const result = try WValue.toLocal(.stack, func, Type.usize);
7177 return func.finishAir(inst, result, &.{});
7178}
test/behavior/int128.zig-1
......@@ -87,7 +87,6 @@ test "truncate int128" {
8787}
8888
8989test "shift int128" {
90 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
9190 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9291 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9392 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/math.zig-1
......@@ -486,7 +486,6 @@ fn testDivision() !void {
486486}
487487
488488test "division half-precision floats" {
489 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
490489 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
491490 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
492491 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/memset.zig-14
......@@ -7,10 +7,6 @@ test "@memset on array pointers" {
77 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
88 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
99 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10 if (builtin.zig_backend == .stage2_wasm) {
11 // TODO: implement memset when element ABI size > 1
12 return error.SkipZigTest;
13 }
1410
1511 try testMemsetArray();
1612 try comptime testMemsetArray();
......@@ -40,11 +36,6 @@ test "@memset on slices" {
4036 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
4137 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
4238 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
43 if (builtin.zig_backend == .stage2_wasm) {
44 // TODO: implement memset when element ABI size > 1
45 // TODO: implement memset on slices
46 return error.SkipZigTest;
47 }
4839
4940 try testMemsetSlice();
5041 try comptime testMemsetSlice();
......@@ -78,7 +69,6 @@ test "memset with bool element" {
7869 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7970 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
8071 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
81 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
8272
8373 var buf: [5]bool = undefined;
8474 @memset(&buf, true);
......@@ -91,7 +81,6 @@ test "memset with 1-byte struct element" {
9181 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
9282 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
9383 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
94 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
9584
9685 const S = struct { x: bool };
9786 var buf: [5]S = undefined;
......@@ -105,7 +94,6 @@ test "memset with 1-byte array element" {
10594 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
10695 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
10796 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
108 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
10997
11098 const A = [1]bool;
11199 var buf: [5]A = undefined;
......@@ -119,7 +107,6 @@ test "memset with large array element, runtime known" {
119107 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
120108 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
121109 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
122 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
123110
124111 const A = [128]u64;
125112 var buf: [5]A = undefined;
......@@ -137,7 +124,6 @@ test "memset with large array element, comptime known" {
137124 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
138125 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
139126 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
140 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
141127
142128 const A = [128]u64;
143129 var buf: [5]A = undefined;
test/behavior/slice.zig-1
......@@ -185,7 +185,6 @@ test "slicing zero length array" {
185185}
186186
187187test "slicing pointer by length" {
188 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
189188 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
190189
191190 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };