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;...@@ -12,9 +12,7 @@ var cmdline_buffer: [4096]u8 = undefined;
12var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);12var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
1313
14pub fn main() void {14pub fn main() void {
15 if (builtin.zig_backend == .stage2_wasm or15 if (builtin.zig_backend == .stage2_aarch64) {
16 builtin.zig_backend == .stage2_aarch64)
17 {
18 return mainSimple() catch @panic("test failure");16 return mainSimple() catch @panic("test failure");
19 }17 }
2018
src/arch/wasm/CodeGen.zig+431-224
...@@ -29,6 +29,9 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset;...@@ -29,6 +29,9 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset;
2929
30/// Wasm Value, created when generating an instruction30/// Wasm Value, created when generating an instruction
31const WValue = union(enum) {31const WValue = union(enum) {
32 /// `WValue` which has been freed and may no longer hold
33 /// any references.
34 dead: void,
32 /// May be referenced but is unused35 /// May be referenced but is unused
33 none: void,36 none: void,
34 /// The value lives on top of the stack37 /// The value lives on top of the stack
...@@ -86,6 +89,7 @@ const WValue = union(enum) {...@@ -86,6 +89,7 @@ const WValue = union(enum) {
86 fn offset(value: WValue) u32 {89 fn offset(value: WValue) u32 {
87 switch (value) {90 switch (value) {
88 .stack_offset => |stack_offset| return stack_offset.value,91 .stack_offset => |stack_offset| return stack_offset.value,
92 .dead => unreachable,
89 else => return 0,93 else => return 0,
90 }94 }
91 }95 }
...@@ -123,7 +127,8 @@ const WValue = union(enum) {...@@ -123,7 +127,8 @@ const WValue = union(enum) {
123 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,127 .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return,
124 .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return,128 .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return,
125 }129 }
126 value.* = undefined;130 log.debug("freed local ({d}) of type {}", .{ local_value, valtype });
131 value.* = .dead;
127 }132 }
128};133};
129134
...@@ -832,6 +837,7 @@ const Branch = struct {...@@ -832,6 +837,7 @@ const Branch = struct {
832837
833 fn deinit(branch: *Branch, gpa: Allocator) void {838 fn deinit(branch: *Branch, gpa: Allocator) void {
834 branch.values.deinit(gpa);839 branch.values.deinit(gpa);
840 branch.* = undefined;
835 }841 }
836};842};
837843
...@@ -880,7 +886,11 @@ fn processDeath(func: *CodeGen, ref: Air.Inst.Ref) void {...@@ -880,7 +886,11 @@ fn processDeath(func: *CodeGen, ref: Air.Inst.Ref) void {
880 // TODO: Upon branch consolidation free any locals if needed.886 // TODO: Upon branch consolidation free any locals if needed.
881 const value = func.currentBranch().values.getPtr(ref) orelse return;887 const value = func.currentBranch().values.getPtr(ref) orelse return;
882 if (value.* != .local) return;888 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 });
884 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer894 value.local.references -= 1; // if this panics, a call to `reuseOperand` was forgotten by the developer
885 if (value.local.references == 0) {895 if (value.local.references == 0) {
886 value.free(func);896 value.free(func);
...@@ -1026,6 +1036,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 {...@@ -1026,6 +1036,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 {
1026/// Writes the bytecode depending on the given `WValue` in `val`1036/// Writes the bytecode depending on the given `WValue` in `val`
1027fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {1037fn emitWValue(func: *CodeGen, value: WValue) InnerError!void {
1028 switch (value) {1038 switch (value) {
1039 .dead => unreachable, // reference to free'd `WValue` (missing reuseOperand?)
1029 .none, .stack => {}, // no-op1040 .none, .stack => {}, // no-op
1030 .local => |idx| try func.addLabel(.local_get, idx.value),1041 .local => |idx| try func.addLabel(.local_get, idx.value),
1031 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),1042 .imm32 => |val| try func.addImm32(@bitCast(i32, val)),
...@@ -1082,27 +1093,27 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {...@@ -1082,27 +1093,27 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue {
1082 const valtype = typeToValtype(ty, func.target);1093 const valtype = typeToValtype(ty, func.target);
1083 switch (valtype) {1094 switch (valtype) {
1084 .i32 => if (func.free_locals_i32.popOrNull()) |index| {1095 .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 });
1086 return WValue{ .local = .{ .value = index, .references = 1 } };1097 return WValue{ .local = .{ .value = index, .references = 1 } };
1087 },1098 },
1088 .i64 => if (func.free_locals_i64.popOrNull()) |index| {1099 .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 });
1090 return WValue{ .local = .{ .value = index, .references = 1 } };1101 return WValue{ .local = .{ .value = index, .references = 1 } };
1091 },1102 },
1092 .f32 => if (func.free_locals_f32.popOrNull()) |index| {1103 .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 });
1094 return WValue{ .local = .{ .value = index, .references = 1 } };1105 return WValue{ .local = .{ .value = index, .references = 1 } };
1095 },1106 },
1096 .f64 => if (func.free_locals_f64.popOrNull()) |index| {1107 .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 });
1098 return WValue{ .local = .{ .value = index, .references = 1 } };1109 return WValue{ .local = .{ .value = index, .references = 1 } };
1099 },1110 },
1100 .v128 => if (func.free_locals_v128.popOrNull()) |index| {1111 .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 });
1102 return WValue{ .local = .{ .value = index, .references = 1 } };1113 return WValue{ .local = .{ .value = index, .references = 1 } };
1103 },1114 },
1104 }1115 }
1105 log.debug("new local of type {}\n", .{valtype});1116 log.debug("new local of type {}", .{valtype});
1106 // no local was free to be re-used, so allocate a new local instead1117 // no local was free to be re-used, so allocate a new local instead
1107 return func.ensureAllocLocal(ty);1118 return func.ensureAllocLocal(ty);
1108}1119}
...@@ -1222,6 +1233,7 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1222,6 +1233,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
1222 defer {1233 defer {
1223 var outer_branch = func.branches.pop();1234 var outer_branch = func.branches.pop();
1224 outer_branch.deinit(func.gpa);1235 outer_branch.deinit(func.gpa);
1236 assert(func.branches.items.len == 0); // missing branch merge
1225 }1237 }
1226 // Generate MIR for function body1238 // Generate MIR for function body
1227 try func.genBody(func.air.getMainBody());1239 try func.genBody(func.air.getMainBody());
...@@ -1242,7 +1254,7 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1242,7 +1254,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
12421254
1243 // check if we have to initialize and allocate anything into the stack frame.1255 // check if we have to initialize and allocate anything into the stack frame.
1244 // If so, create enough stack space and insert the instructions at the front of the list.1256 // 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) {
1246 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);1258 var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
1247 defer prologue.deinit();1259 defer prologue.deinit();
12481260
...@@ -1593,10 +1605,16 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {...@@ -1593,10 +1605,16 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void {
1593 else => {},1605 else => {},
1594 }1606 }
15951607
1596 // TODO: We should probably lower this to a call to compiler_rt1608 // allocate a local for the offset, and set it to 0.
1597 // But for now, we implement it manually1609 // This to ensure that inside loops we correctly re-set the counter.
1598 var offset = try func.ensureAllocLocal(Type.usize); // local for counter1610 var offset = try func.allocLocal(Type.usize); // local for counter
1599 defer offset.free(func);1611 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
1601 // outer block to jump to when loop is done1619 // outer block to jump to when loop is done
1602 try func.startBlock(.block, wasm.block_empty);1620 try func.startBlock(.block, wasm.block_empty);
...@@ -1796,10 +1814,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1796,10 +1814,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1796 .subwrap => func.airWrapBinOp(inst, .sub),1814 .subwrap => func.airWrapBinOp(inst, .sub),
1797 .mul => func.airBinOp(inst, .mul),1815 .mul => func.airBinOp(inst, .mul),
1798 .mulwrap => func.airWrapBinOp(inst, .mul),1816 .mulwrap => func.airWrapBinOp(inst, .mul),
1799 .div_float,1817 .div_float, .div_exact => func.airDiv(inst),
1800 .div_exact,1818 .div_trunc => func.airDivTrunc(inst),
1801 .div_trunc,
1802 => func.airDiv(inst),
1803 .div_floor => func.airDivFloor(inst),1819 .div_floor => func.airDivFloor(inst),
1804 .bit_and => func.airBinOp(inst, .@"and"),1820 .bit_and => func.airBinOp(inst, .@"and"),
1805 .bit_or => func.airBinOp(inst, .@"or"),1821 .bit_or => func.airBinOp(inst, .@"or"),
...@@ -1963,11 +1979,11 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1963,11 +1979,11 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1963 .tag_name => func.airTagName(inst),1979 .tag_name => func.airTagName(inst),
19641980
1965 .error_set_has_value => func.airErrorSetHasValue(inst),1981 .error_set_has_value => func.airErrorSetHasValue(inst),
1982 .frame_addr => func.airFrameAddress(inst),
19661983
1967 .mul_sat,1984 .mul_sat,
1968 .mod,1985 .mod,
1969 .assembly,1986 .assembly,
1970 .frame_addr,
1971 .bit_reverse,1987 .bit_reverse,
1972 .is_err_ptr,1988 .is_err_ptr,
1973 .is_non_err_ptr,1989 .is_non_err_ptr,
...@@ -2110,16 +2126,13 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2110,16 +2126,13 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2110 const un_op = func.air.instructions.items(.data)[inst].un_op;2126 const un_op = func.air.instructions.items(.data)[inst].un_op;
2111 const operand = try func.resolveInst(un_op);2127 const operand = try func.resolveInst(un_op);
2112 const ret_ty = func.air.typeOf(un_op).childType();2128 const ret_ty = func.air.typeOf(un_op).childType();
2129
2130 const fn_info = func.decl.ty.fnInfo();
2113 if (!ret_ty.hasRuntimeBitsIgnoreComptime()) {2131 if (!ret_ty.hasRuntimeBitsIgnoreComptime()) {
2114 if (ret_ty.isError()) {2132 if (ret_ty.isError()) {
2115 try func.addImm32(0);2133 try func.addImm32(0);
2116 } else {
2117 return func.finishAir(inst, .none, &.{});
2118 }2134 }
2119 }2135 } else if (!firstParamSRet(fn_info.cc, fn_info.return_type, func.target)) {
2120
2121 const fn_info = func.decl.ty.fnInfo();
2122 if (!firstParamSRet(fn_info.cc, fn_info.return_type, func.target)) {
2123 // leave on the stack2136 // leave on the stack
2124 _ = try func.load(operand, ret_ty, 0);2137 _ = try func.load(operand, ret_ty, 0);
2125 }2138 }
...@@ -2523,10 +2536,34 @@ fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {...@@ -2523,10 +2536,34 @@ fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
2523 const bin_op = func.air.instructions.items(.data)[inst].bin_op;2536 const bin_op = func.air.instructions.items(.data)[inst].bin_op;
2524 const lhs = try func.resolveInst(bin_op.lhs);2537 const lhs = try func.resolveInst(bin_op.lhs);
2525 const rhs = try func.resolveInst(bin_op.rhs);2538 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);2566 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
2529 func.finishAir(inst, try stack_value.toLocal(func, ty), &.{ bin_op.lhs, bin_op.rhs });
2530}2567}
25312568
2532/// Performs a binary operation on the given `WValue`'s2569/// 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!...@@ -2565,37 +2602,56 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!
25652602
2566fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {2603fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
2567 if (ty.intInfo(func.target).bits > 128) {2604 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", .{});
2569 }2606 }
25702607
2571 if (op != .add and op != .sub) {2608 switch (op) {
2572 return func.fail("TODO: Implement binary operation for big integers", .{});2609 .mul => return func.callIntrinsic("__multi3", &.{ ty, ty }, ty, &.{ lhs, rhs }),
2573 }2610 .shr => return func.callIntrinsic("__lshrti3", &.{ ty, Type.i32 }, ty, &.{ lhs, rhs }),
25742611 .shl => return func.callIntrinsic("__ashlti3", &.{ ty, Type.i32 }, ty, &.{ lhs, rhs }),
2575 const result = try func.allocStack(ty);2612 .xor => {
2576 var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);2613 const result = try func.allocStack(ty);
2577 defer lhs_high_bit.free(func);2614 try func.emitWValue(result);
2578 var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);2615 const lhs_high_bit = try func.load(lhs, Type.u64, 0);
2579 defer rhs_high_bit.free(func);2616 const rhs_high_bit = try func.load(rhs, Type.u64, 0);
2580 var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64);2617 const xor_high_bit = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor);
2581 defer high_op_res.free(func);2618 try func.store(.stack, xor_high_bit, Type.u64, result.offset());
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);
25862619
2587 const lt = if (op == .add) blk: {2620 try func.emitWValue(result);
2588 break :blk try func.cmp(high_op_res, rhs_high_bit, Type.u64, .lt);2621 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
2589 } else if (op == .sub) blk: {2622 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
2590 break :blk try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);2623 const xor_low_bit = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
2591 } else unreachable;2624 try func.store(.stack, xor_low_bit, Type.u64, result.offset() + 8);
2592 const tmp = try func.intcast(lt, Type.u32, Type.u64);2625 return result;
2593 var tmp_op = try (try func.binOp(low_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64);2626 },
2594 defer tmp_op.free(func);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);2636 const lhs_low_bit = try func.load(lhs, Type.u64, 8);
2597 try func.store(result, tmp_op, Type.u64, 8);2637 const rhs_low_bit = try func.load(rhs, Type.u64, 8);
2598 return result;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 }
2599}2655}
26002656
2601const FloatOp = enum {2657const FloatOp = enum {
...@@ -2751,14 +2807,38 @@ fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {...@@ -2751,14 +2807,38 @@ fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
27512807
2752 const lhs = try func.resolveInst(bin_op.lhs);2808 const lhs = try func.resolveInst(bin_op.lhs);
2753 const rhs = try func.resolveInst(bin_op.rhs);2809 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) {
2757 return func.fail("TODO: Implement wrapping arithmetic for vectors", .{});2814 return func.fail("TODO: Implement wrapping arithmetic for vectors", .{});
2758 }2815 }
27592816
2760 const result = try (try func.wrapBinOp(lhs, rhs, ty, op)).toLocal(func, ty);2817 // For certain operations, such as shifting, the types are different.
2761 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });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 });
2762}2842}
27632843
2764/// Performs a wrapping binary operation.2844/// Performs a wrapping binary operation.
...@@ -2810,26 +2890,25 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {...@@ -2810,26 +2890,25 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
2810 return WValue{ .stack = {} };2890 return WValue{ .stack = {} };
2811}2891}
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 {
2814 switch (ptr_val.tag()) {2894 switch (ptr_val.tag()) {
2815 .decl_ref_mut => {2895 .decl_ref_mut => {
2816 const decl_index = ptr_val.castTag(.decl_ref_mut).?.data.decl_index;2896 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);
2818 },2898 },
2819 .decl_ref => {2899 .decl_ref => {
2820 const decl_index = ptr_val.castTag(.decl_ref).?.data;2900 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);
2822 },2902 },
2823 .variable => {2903 .variable => {
2824 const decl_index = ptr_val.castTag(.variable).?.data.owner_decl;2904 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);
2826 },2906 },
2827 .field_ptr => {2907 .field_ptr => {
2828 const field_ptr = ptr_val.castTag(.field_ptr).?.data;2908 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
2829 const parent_ty = field_ptr.container_ty;2909 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()) {
2833 .Struct => switch (parent_ty.containerLayout()) {2912 .Struct => switch (parent_ty.containerLayout()) {
2834 .Packed => parent_ty.packedStructFieldByteOffset(field_ptr.field_index, func.target),2913 .Packed => parent_ty.packedStructFieldByteOffset(field_ptr.field_index, func.target),
2835 else => parent_ty.structFieldOffset(field_ptr.field_index, func.target),2914 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...@@ -2842,8 +2921,8 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError
2842 if (layout.payload_align > layout.tag_align) break :blk 0;2921 if (layout.payload_align > layout.tag_align) break :blk 0;
28432922
2844 // tag is stored first so calculate offset from where payload starts2923 // 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));2924 const field_offset = @intCast(u32, std.mem.alignForwardGeneric(u64, layout.tag_size, layout.tag_align));
2846 break :blk offset;2925 break :blk field_offset;
2847 },2926 },
2848 },2927 },
2849 .Pointer => switch (parent_ty.ptrSize()) {2928 .Pointer => switch (parent_ty.ptrSize()) {
...@@ -2856,43 +2935,23 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError...@@ -2856,43 +2935,23 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError
2856 },2935 },
2857 else => unreachable,2936 else => unreachable,
2858 };2937 };
28592938 return func.lowerParentPtr(field_ptr.container_ptr, offset + @intCast(u32, field_offset));
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 };
2875 },2939 },
2876 .elem_ptr => {2940 .elem_ptr => {
2877 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;2941 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
2878 const index = elem_ptr.index;2942 const index = elem_ptr.index;
2879 const offset = index * ptr_child_ty.abiSize(func.target);2943 const elem_offset = index * elem_ptr.elem_ty.abiSize(func.target);
2880 const array_ptr = try func.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty);2944 return func.lowerParentPtr(elem_ptr.array_ptr, offset + @intCast(u32, elem_offset));
2881
2882 return WValue{ .memory_offset = .{
2883 .pointer = array_ptr.memory,
2884 .offset = @intCast(u32, offset),
2885 } };
2886 },2945 },
2887 .opt_payload_ptr => {2946 .opt_payload_ptr => {
2888 const payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;2947 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);
2890 },2949 },
2891 else => |tag| return func.fail("TODO: Implement lowerParentPtr for tag: {}", .{tag}),2950 else => |tag| return func.fail("TODO: Implement lowerParentPtr for tag: {}", .{tag}),
2892 }2951 }
2893}2952}
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 {
2896 const module = func.bin_file.base.options.module.?;2955 const module = func.bin_file.base.options.module.?;
2897 const decl = module.declPtr(decl_index);2956 const decl = module.declPtr(decl_index);
2898 module.markDeclAlive(decl);2957 module.markDeclAlive(decl);
...@@ -2901,10 +2960,10 @@ fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: Module.Decl.In...@@ -2901,10 +2960,10 @@ fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: Module.Decl.In
2901 .data = decl.ty,2960 .data = decl.ty,
2902 };2961 };
2903 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);2962 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);
2905}2964}
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 {
2908 if (tv.ty.isSlice()) {2967 if (tv.ty.isSlice()) {
2909 return WValue{ .memory = try func.bin_file.lowerUnnamedConst(tv, decl_index) };2968 return WValue{ .memory = try func.bin_file.lowerUnnamedConst(tv, decl_index) };
2910 }2969 }
...@@ -2923,7 +2982,9 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind...@@ -2923,7 +2982,9 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind
2923 if (decl.ty.zigTypeTag() == .Fn) {2982 if (decl.ty.zigTypeTag() == .Fn) {
2924 try func.bin_file.addTableFunction(target_sym_index);2983 try func.bin_file.addTableFunction(target_sym_index);
2925 return WValue{ .function_index = target_sym_index };2984 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 } };
2927}2988}
29282989
2929/// Converts a signed integer to its 2's complement form and returns2990/// 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 {...@@ -2950,11 +3011,11 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
2950 if (val.isUndefDeep()) return func.emitUndefined(ty);3011 if (val.isUndefDeep()) return func.emitUndefined(ty);
2951 if (val.castTag(.decl_ref)) |decl_ref| {3012 if (val.castTag(.decl_ref)) |decl_ref| {
2952 const decl_index = decl_ref.data;3013 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);
2954 }3015 }
2955 if (val.castTag(.decl_ref_mut)) |decl_ref_mut| {3016 if (val.castTag(.decl_ref_mut)) |decl_ref_mut| {
2956 const decl_index = decl_ref_mut.data.decl_index;3017 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);
2958 }3019 }
2959 const target = func.target;3020 const target = func.target;
2960 switch (ty.zigTypeTag()) {3021 switch (ty.zigTypeTag()) {
...@@ -2988,9 +3049,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {...@@ -2988,9 +3049,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
2988 else => unreachable,3049 else => unreachable,
2989 },3050 },
2990 .Pointer => switch (val.tag()) {3051 .Pointer => switch (val.tag()) {
2991 .field_ptr, .elem_ptr, .opt_payload_ptr => {3052 .field_ptr, .elem_ptr, .opt_payload_ptr => return func.lowerParentPtr(val, 0),
2992 return func.lowerParentPtr(val, ty.childType());
2993 },
2994 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) },3053 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) },
2995 .zero, .null_value => return WValue{ .imm32 = 0 },3054 .zero, .null_value => return WValue{ .imm32 = 0 },
2996 else => return func.fail("Wasm TODO: lowerConstant for other const pointer tag {}", .{val.tag()}),3055 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 {...@@ -3182,9 +3241,13 @@ fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3182 .label = func.block_depth,3241 .label = func.block_depth,
3183 .value = block_result,3242 .value = block_result,
3184 });3243 });
3244
3185 try func.genBody(body);3245 try func.genBody(body);
3186 try func.endBlock();3246 try func.endBlock();
31873247
3248 const liveness = func.liveness.getBlock(inst);
3249 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths.len);
3250
3188 func.finishAir(inst, block_result, &.{});3251 func.finishAir(inst, block_result, &.{});
3189}3252}
31903253
...@@ -3239,41 +3302,31 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3239,41 +3302,31 @@ fn airCondBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3239 try func.addLabel(.br_if, 0);3302 try func.addLabel(.br_if, 0);
32403303
3241 try func.branches.ensureUnusedCapacity(func.gpa, 2);3304 try func.branches.ensureUnusedCapacity(func.gpa, 2);
32423305 {
3243 func.branches.appendAssumeCapacity(.{});3306 func.branches.appendAssumeCapacity(.{});
3244 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len));3307 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.else_deaths.len));
3245 try func.genBody(else_body);3308 defer {
3246 try func.endBlock();3309 var else_stack = func.branches.pop();
3247 var else_stack = func.branches.pop();3310 else_stack.deinit(func.gpa);
3248 defer else_stack.deinit(func.gpa);3311 }
3312 try func.genBody(else_body);
3313 try func.endBlock();
3314 }
32493315
3250 // Outer block that matches the condition3316 // Outer block that matches the condition
3251 func.branches.appendAssumeCapacity(.{});3317 {
3252 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len));3318 func.branches.appendAssumeCapacity(.{});
3253 try func.genBody(then_body);3319 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, @intCast(u32, liveness_condbr.then_deaths.len));
3254 var then_stack = func.branches.pop();3320 defer {
3255 defer then_stack.deinit(func.gpa);3321 var then_stack = func.branches.pop();
32563322 then_stack.deinit(func.gpa);
3257 try func.mergeBranch(&else_stack);3323 }
3258 try func.mergeBranch(&then_stack);3324 try func.genBody(then_body);
3325 }
32593326
3260 func.finishAir(inst, .none, &.{});3327 func.finishAir(inst, .none, &.{});
3261}3328}
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
3277fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {3330fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void {
3278 const bin_op = func.air.instructions.items(.data)[inst].bin_op;3331 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 {...@@ -3783,30 +3836,25 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3783 }3836 }
3784 }3837 }
3785 func.branches.appendAssumeCapacity(.{});3838 func.branches.appendAssumeCapacity(.{});
3786
3787 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len);3839 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[index].len);
3788 for (liveness.deaths[index]) |operand| {3840 defer {
3789 func.processDeath(Air.indexToRef(operand));3841 var case_branch = func.branches.pop();
3842 case_branch.deinit(func.gpa);
3790 }3843 }
3791 try func.genBody(case.body);3844 try func.genBody(case.body);
3792 try func.endBlock();3845 try func.endBlock();
3793 var case_branch = func.branches.pop();
3794 defer case_branch.deinit(func.gpa);
3795 try func.mergeBranch(&case_branch);
3796 }3846 }
37973847
3798 if (has_else_body) {3848 if (has_else_body) {
3799 func.branches.appendAssumeCapacity(.{});3849 func.branches.appendAssumeCapacity(.{});
3800 const else_deaths = liveness.deaths.len - 1;3850 const else_deaths = liveness.deaths.len - 1;
3801 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[else_deaths].len);3851 try func.currentBranch().values.ensureUnusedCapacity(func.gpa, liveness.deaths[else_deaths].len);
3802 for (liveness.deaths[else_deaths]) |operand| {3852 defer {
3803 func.processDeath(Air.indexToRef(operand));3853 var else_branch = func.branches.pop();
3854 else_branch.deinit(func.gpa);
3804 }3855 }
3805 try func.genBody(else_body);3856 try func.genBody(else_body);
3806 try func.endBlock();3857 try func.endBlock();
3807 var else_branch = func.branches.pop();
3808 defer else_branch.deinit(func.gpa);
3809 try func.mergeBranch(&else_branch);
3810 }3858 }
3811 func.finishAir(inst, .none, &.{});3859 func.finishAir(inst, .none, &.{});
3812}3860}
...@@ -3935,7 +3983,7 @@ fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3935,7 +3983,7 @@ fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3935 // write 'undefined' to the payload3983 // write 'undefined' to the payload
3936 const payload_ptr = try func.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, func.target)), .new);3984 const payload_ptr = try func.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, func.target)), .new);
3937 const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(func.target));3985 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
3940 break :result err_union;3988 break :result err_union;
3941 };3989 };
...@@ -3955,7 +4003,13 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3955,7 +4003,13 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3955 return func.fail("todo Wasm intcast for bitsize > 128", .{});4003 return func.fail("todo Wasm intcast for bitsize > 128", .{});
3956 }4004 }
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
3959 func.finishAir(inst, result, &.{});4013 func.finishAir(inst, result, &.{});
3960}4014}
39614015
...@@ -4423,7 +4477,14 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void...@@ -4423,7 +4477,14 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void
4423 .One => @as(WValue, .{ .imm32 = @intCast(u32, ptr_ty.childType().arrayLen()) }),4477 .One => @as(WValue, .{ .imm32 = @intCast(u32, ptr_ty.childType().arrayLen()) }),
4424 .C, .Many => unreachable,4478 .C, .Many => unreachable,
4425 };4479 };
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
4428 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });4489 func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
4429}4490}
...@@ -4432,10 +4493,12 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void...@@ -4432,10 +4493,12 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void
4432/// When the user has enabled the bulk_memory feature, we lower4493/// When the user has enabled the bulk_memory feature, we lower
4433/// this to wasm's memset instruction. When the feature is not present,4494/// this to wasm's memset instruction. When the feature is not present,
4434/// we implement it manually.4495/// 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
4436 // When bulk_memory is enabled, we lower it to wasm's memset instruction.4499 // When bulk_memory is enabled, we lower it to wasm's memset instruction.
4437 // If not, we lower it ourselves4500 // If not, we lower it ourselves.
4438 if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory)) {4501 if (std.Target.wasm.featureSetHas(func.target.cpu.features, .bulk_memory) and abi_size == 1) {
4439 try func.lowerToStack(ptr);4502 try func.lowerToStack(ptr);
4440 try func.emitWValue(value);4503 try func.emitWValue(value);
4441 try func.emitWValue(len);4504 try func.emitWValue(len);
...@@ -4443,74 +4506,79 @@ fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!vo...@@ -4443,74 +4506,79 @@ fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!vo
4443 return;4506 return;
4444 }4507 }
44454508
4446 // When the length is comptime-known we do the loop at codegen, rather4509 const final_len = switch (len) {
4447 // than emitting a runtime loop into the binary4510 .imm32 => |val| WValue{ .imm32 = val * abi_size },
4448 switch (len) {4511 .imm64 => |val| WValue{ .imm64 = val * abi_size },
4449 .imm32, .imm64 => {4512 else => if (abi_size != 1) blk: {
4450 const length = switch (len) {4513 const new_len = try func.ensureAllocLocal(Type.usize);
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);
4480 try func.emitWValue(len);4514 try func.emitWValue(len);
4481 switch (func.arch()) {4515 switch (func.arch()) {
4482 .wasm32 => try func.addTag(.i32_eq),4516 .wasm32 => {
4483 .wasm64 => try func.addTag(.i64_eq),4517 try func.emitWValue(.{ .imm32 = abi_size });
4484 else => unreachable,4518 try func.addTag(.i32_mul);
4485 }4519 },
4486 try func.addLabel(.br_if, 1); // jump out of loop into outer block (finished)4520 .wasm64 => {
4487 try func.emitWValue(ptr);4521 try func.emitWValue(.{ .imm64 = abi_size });
4488 try func.emitWValue(offset);4522 try func.addTag(.i64_mul);
4489 switch (func.arch()) {4523 },
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),
4506 else => unreachable,4524 else => unreachable,
4507 }4525 }
4508 try func.addLabel(.local_set, offset.local.value);4526 try func.addLabel(.local_set, new_len.local.value);
4509 try func.addLabel(.br, 0); // jump to start of loop4527 break :blk new_len;
4510 try func.endBlock();4528 } else len,
4511 try func.endBlock();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);
4512 },4569 },
4570 .wasm64 => {
4571 try func.emitWValue(.{ .imm64 = abi_size });
4572 try func.addTag(.i64_add);
4573 },
4574 else => unreachable,
4513 }4575 }
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();
4514}4582}
45154583
4516fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4584fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
...@@ -4825,8 +4893,15 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4825,8 +4893,15 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4825 const struct_obj = result_ty.castTag(.@"struct").?.data;4893 const struct_obj = result_ty.castTag(.@"struct").?.data;
4826 const fields = struct_obj.fields.values();4894 const fields = struct_obj.fields.values();
4827 const backing_type = struct_obj.backing_int_ty;4895 const backing_type = struct_obj.backing_int_ty;
4828 // we ensure a new local is created so it's zero-initialized4896
4829 const result = try func.ensureAllocLocal(backing_type);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
4830 var current_bit: u16 = 0;4905 var current_bit: u16 = 0;
4831 for (elements, 0..) |elem, elem_index| {4906 for (elements, 0..) |elem, elem_index| {
4832 const field = fields[elem_index];4907 const field = fields[elem_index];
...@@ -4884,8 +4959,15 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4884,8 +4959,15 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4884 else => unreachable,4959 else => unreachable,
4885 }4960 }
4886 };4961 };
4887 // TODO: this is incorrect Liveness handling code4962
4888 func.finishAir(inst, result, &.{});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);
4889}4971}
48904972
4891fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {4973fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
...@@ -5212,7 +5294,7 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5212,7 +5294,7 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5212 const src_ty = func.air.typeOf(bin_op.rhs);5294 const src_ty = func.air.typeOf(bin_op.rhs);
5213 const len = switch (dst_ty.ptrSize()) {5295 const len = switch (dst_ty.ptrSize()) {
5214 .Slice => try func.sliceLen(dst),5296 .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()) }),
5216 .C, .Many => unreachable,5298 .C, .Many => unreachable,
5217 };5299 };
5218 const dst_ptr = try func.sliceOrArrayPtr(dst, dst_ty);5300 const dst_ptr = try func.sliceOrArrayPtr(dst, dst_ty);
...@@ -5372,11 +5454,10 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro...@@ -5372,11 +5454,10 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
5372 };5454 };
53735455
5374 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty);5456 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty);
5375 defer bin_op.free(func);
5376 var result = if (wasm_bits != int_info.bits) blk: {5457 var result = if (wasm_bits != int_info.bits) blk: {
5377 break :blk try (try func.wrapOperand(bin_op, lhs_ty)).toLocal(func, lhs_ty);5458 break :blk try (try func.wrapOperand(bin_op, lhs_ty)).toLocal(func, lhs_ty);
5378 } else bin_op;5459 } else bin_op;
5379 defer result.free(func); // no-op when wasm_bits == int_info.bits5460 defer result.free(func);
53805461
5381 const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt;5462 const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt;
5382 const overflow_bit: WValue = if (is_signed) blk: {5463 const overflow_bit: WValue = if (is_signed) blk: {
...@@ -5532,16 +5613,12 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5532,16 +5613,12 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55325613
5533 const int_info = lhs_ty.intInfo(func.target);5614 const int_info = lhs_ty.intInfo(func.target);
5534 const wasm_bits = toWasmBits(int_info.bits) orelse {5615 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) {
5539 return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});5616 return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});
5540 }5617 };
55415618
5542 const zero = switch (wasm_bits) {5619 const zero = switch (wasm_bits) {
5543 32 => WValue{ .imm32 = 0 },5620 32 => WValue{ .imm32 = 0 },
5544 64 => WValue{ .imm64 = 0 },5621 64, 128 => WValue{ .imm64 = 0 },
5545 else => unreachable,5622 else => unreachable,
5546 };5623 };
55475624
...@@ -5568,7 +5645,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5568,7 +5645,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5568 try func.addLabel(.local_set, overflow_bit.local.value);5645 try func.addLabel(.local_set, overflow_bit.local.value);
5569 break :blk down_cast;5646 break :blk down_cast;
5570 }5647 }
5571 } else if (int_info.signedness == .signed) blk: {5648 } else if (int_info.signedness == .signed and wasm_bits == 32) blk: {
5572 const lhs_abs = try func.signAbsValue(lhs, lhs_ty);5649 const lhs_abs = try func.signAbsValue(lhs, lhs_ty);
5573 const rhs_abs = try func.signAbsValue(rhs, lhs_ty);5650 const rhs_abs = try func.signAbsValue(rhs, lhs_ty);
5574 const bin_op = try (try func.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(func, lhs_ty);5651 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 {...@@ -5576,7 +5653,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5576 _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq);5653 _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq);
5577 try func.addLabel(.local_set, overflow_bit.local.value);5654 try func.addLabel(.local_set, overflow_bit.local.value);
5578 break :blk try func.wrapOperand(bin_op, lhs_ty);5655 break :blk try func.wrapOperand(bin_op, lhs_ty);
5579 } else blk: {5656 } else if (wasm_bits == 32) blk: {
5580 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(func, lhs_ty);5657 var bin_op = try (try func.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(func, lhs_ty);
5581 defer bin_op.free(func);5658 defer bin_op.free(func);
5582 const shift_imm = if (wasm_bits == 32)5659 const shift_imm = if (wasm_bits == 32)
...@@ -5587,7 +5664,99 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5587,7 +5664,99 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5587 _ = try func.cmp(shr, zero, lhs_ty, .neq);5664 _ = try func.cmp(shr, zero, lhs_ty, .neq);
5588 try func.addLabel(.local_set, overflow_bit.local.value);5665 try func.addLabel(.local_set, overflow_bit.local.value);
5589 break :blk try func.wrapOperand(bin_op, lhs_ty);5666 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", .{});
5591 var bin_op_local = try bin_op.toLocal(func, lhs_ty);5760 var bin_op_local = try bin_op.toLocal(func, lhs_ty);
5592 defer bin_op_local.free(func);5761 defer bin_op_local.free(func);
55935762
...@@ -5821,7 +5990,7 @@ fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5821,7 +5990,7 @@ fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5821 const extra = func.air.extraData(Air.Try, pl_op.payload);5990 const extra = func.air.extraData(Air.Try, pl_op.payload);
5822 const body = func.air.extra[extra.end..][0..extra.data.body_len];5991 const body = func.air.extra[extra.end..][0..extra.data.body_len];
5823 const err_union_ty = func.air.typeOf(pl_op.operand);5992 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);
5825 func.finishAir(inst, result, &.{pl_op.operand});5994 func.finishAir(inst, result, &.{pl_op.operand});
5826}5995}
58275996
...@@ -5831,12 +6000,13 @@ fn airTryPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5831,12 +6000,13 @@ fn airTryPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5831 const err_union_ptr = try func.resolveInst(extra.data.ptr);6000 const err_union_ptr = try func.resolveInst(extra.data.ptr);
5832 const body = func.air.extra[extra.end..][0..extra.data.body_len];6001 const body = func.air.extra[extra.end..][0..extra.data.body_len];
5833 const err_union_ty = func.air.typeOf(extra.data.ptr).childType();6002 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);
5835 func.finishAir(inst, result, &.{extra.data.ptr});6004 func.finishAir(inst, result, &.{extra.data.ptr});
5836}6005}
58376006
5838fn lowerTry(6007fn lowerTry(
5839 func: *CodeGen,6008 func: *CodeGen,
6009 inst: Air.Inst.Index,
5840 err_union: WValue,6010 err_union: WValue,
5841 body: []const Air.Inst.Index,6011 body: []const Air.Inst.Index,
5842 err_union_ty: Type,6012 err_union_ty: Type,
...@@ -5864,6 +6034,14 @@ fn lowerTry(...@@ -5864,6 +6034,14 @@ fn lowerTry(
5864 }6034 }
5865 try func.addTag(.i32_eqz);6035 try func.addTag(.i32_eqz);
5866 try func.addLabel(.br_if, 0); // jump out of block when error is '0'6036 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 }
5867 try func.genBody(body);6045 try func.genBody(body);
5868 try func.endBlock();6046 try func.endBlock();
5869 }6047 }
...@@ -5965,6 +6143,26 @@ fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5965,6 +6143,26 @@ fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5965 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });6143 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
5966}6144}
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
5968fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6166fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5969 const bin_op = func.air.instructions.items(.data)[inst].bin_op;6167 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 {...@@ -6969,3 +7167,12 @@ fn airAtomicStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
69697167
6970 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });7168 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
6971}7169}
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" {...@@ -87,7 +87,6 @@ test "truncate int128" {
87}87}
8888
89test "shift int128" {89test "shift int128" {
90 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
91 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO90 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
92 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO91 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
93 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO92 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
test/behavior/math.zig-1
...@@ -486,7 +486,6 @@ fn testDivision() !void {...@@ -486,7 +486,6 @@ fn testDivision() !void {
486}486}
487487
488test "division half-precision floats" {488test "division half-precision floats" {
489 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
490 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO489 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
491 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO490 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
492 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO491 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/memset.zig-14
...@@ -7,10 +7,6 @@ test "@memset on array pointers" {...@@ -7,10 +7,6 @@ test "@memset on array pointers" {
7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;9 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
15 try testMemsetArray();11 try testMemsetArray();
16 try comptime testMemsetArray();12 try comptime testMemsetArray();
...@@ -40,11 +36,6 @@ test "@memset on slices" {...@@ -40,11 +36,6 @@ test "@memset on slices" {
40 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;36 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
41 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;37 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
42 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;38 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
49 try testMemsetSlice();40 try testMemsetSlice();
50 try comptime testMemsetSlice();41 try comptime testMemsetSlice();
...@@ -78,7 +69,6 @@ test "memset with bool element" {...@@ -78,7 +69,6 @@ test "memset with bool element" {
78 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;69 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
79 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;70 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
80 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;71 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
81 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
8272
83 var buf: [5]bool = undefined;73 var buf: [5]bool = undefined;
84 @memset(&buf, true);74 @memset(&buf, true);
...@@ -91,7 +81,6 @@ test "memset with 1-byte struct element" {...@@ -91,7 +81,6 @@ test "memset with 1-byte struct element" {
91 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;81 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
92 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;82 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
93 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;83 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
94 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
9584
96 const S = struct { x: bool };85 const S = struct { x: bool };
97 var buf: [5]S = undefined;86 var buf: [5]S = undefined;
...@@ -105,7 +94,6 @@ test "memset with 1-byte array element" {...@@ -105,7 +94,6 @@ test "memset with 1-byte array element" {
105 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;94 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
106 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;95 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
107 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;96 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
108 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
10997
110 const A = [1]bool;98 const A = [1]bool;
111 var buf: [5]A = undefined;99 var buf: [5]A = undefined;
...@@ -119,7 +107,6 @@ test "memset with large array element, runtime known" {...@@ -119,7 +107,6 @@ test "memset with large array element, runtime known" {
119 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;107 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
120 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;108 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
121 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;109 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
122 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
123110
124 const A = [128]u64;111 const A = [128]u64;
125 var buf: [5]A = undefined;112 var buf: [5]A = undefined;
...@@ -137,7 +124,6 @@ test "memset with large array element, comptime known" {...@@ -137,7 +124,6 @@ test "memset with large array element, comptime known" {
137 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;124 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
138 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;125 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
139 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;126 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
140 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
141127
142 const A = [128]u64;128 const A = [128]u64;
143 var buf: [5]A = undefined;129 var buf: [5]A = undefined;
test/behavior/slice.zig-1
...@@ -185,7 +185,6 @@ test "slicing zero length array" {...@@ -185,7 +185,6 @@ test "slicing zero length array" {
185}185}
186186
187test "slicing pointer by length" {187test "slicing pointer by length" {
188 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
189 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;188 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
190189
191 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };190 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };