| ... | @@ -29,6 +29,9 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset; | ... | @@ -29,6 +29,9 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 29 | | 29 | |
| 30 | /// Wasm Value, created when generating an instruction | 30 | /// Wasm Value, created when generating an instruction |
| 31 | const WValue = union(enum) { | 31 | const 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 unused | 35 | /// May be referenced but is unused |
| 33 | none: void, | 36 | none: void, |
| 34 | /// The value lives on top of the stack | 37 | /// 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 | }; |
| 129 | | 134 | |
| ... | @@ -832,6 +837,7 @@ const Branch = struct { | ... | @@ -832,6 +837,7 @@ const Branch = struct { |
| 832 | | 837 | |
| 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 | }; |
| 837 | | 843 | |
| ... | @@ -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 developer | 894 | 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` |
| 1027 | fn emitWValue(func: *CodeGen, value: WValue) InnerError!void { | 1037 | fn 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-op | 1040 | .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 instead | 1117 | // 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 body | 1238 | // 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 { |
| 1242 | | 1254 | |
| 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(); |
| 1248 | | 1260 | |
| ... | @@ -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 | } |
| 1595 | | 1607 | |
| 1596 | // TODO: We should probably lower this to a call to compiler_rt | 1608 | // allocate a local for the offset, and set it to 0. |
| 1597 | // But for now, we implement it manually | 1609 | // This to ensure that inside loops we correctly re-set the counter. |
| 1598 | var offset = try func.ensureAllocLocal(Type.usize); // local for counter | 1610 | 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); |
| 1600 | | 1618 | |
| 1601 | // outer block to jump to when loop is done | 1619 | // 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), |
| 1964 | | 1980 | |
| 1965 | .error_set_has_value => func.airErrorSetHasValue(inst), | 1981 | .error_set_has_value => func.airErrorSetHasValue(inst), |
| | 1982 | .frame_addr => func.airFrameAddress(inst), |
| 1966 | | 1983 | |
| 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 stack | 2136 | // 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 | }; |
| 2527 | | 2565 | |
| 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 | } |
| 2531 | | 2568 | |
| 2532 | /// Performs a binary operation on the given `WValue`'s | 2569 | /// 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! |
| 2565 | | 2602 | |
| 2566 | fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | 2603 | fn 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 | } |
| 2570 | | 2607 | |
| 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 }), |
| 2574 | | 2611 | .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); | | |
| 2586 | | 2619 | |
| 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); |
| 2595 | | 2635 | |
| 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 | } |
| 2600 | | 2656 | |
| 2601 | const FloatOp = enum { | 2657 | const 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 { |
| 2751 | | 2807 | |
| 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); |
| 2755 | | 2812 | |
| 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 | } |
| 2759 | | 2816 | |
| 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 | } |
| 2763 | | 2843 | |
| 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 | } |
| 2812 | | 2892 | |
| 2813 | fn lowerParentPtr(func: *CodeGen, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue { | 2893 | fn 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); | | |
| 2831 | | 2910 | |
| 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; |
| 2843 | | 2922 | |
| 2844 | // tag is stored first so calculate offset from where payload starts | 2923 | // 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 | }; |
| 2859 | | 2938 | 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 | } |
| 2894 | | 2953 | |
| 2895 | fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: Module.Decl.Index) InnerError!WValue { | 2954 | fn 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 | } |
| 2906 | | 2965 | |
| 2907 | fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!WValue { | 2966 | fn 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 | } |
| 2928 | | 2989 | |
| 2929 | /// Converts a signed integer to its 2's complement form and returns | 2990 | /// 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(); |
| 3187 | | 3247 | |
| | 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 | } |
| 3190 | | 3253 | |
| ... | @@ -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); |
| 3240 | | 3303 | |
| 3241 | try func.branches.ensureUnusedCapacity(func.gpa, 2); | 3304 | try func.branches.ensureUnusedCapacity(func.gpa, 2); |
| 3242 | | 3305 | { |
| 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 | } |
| 3249 | | 3315 | |
| 3250 | // Outer block that matches the condition | 3316 | // 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(); |
| 3256 | | 3322 | 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 | } |
| 3259 | | 3326 | |
| 3260 | func.finishAir(inst, .none, &.{}); | 3327 | func.finishAir(inst, .none, &.{}); |
| 3261 | } | 3328 | } |
| 3262 | | 3329 | |
| 3263 | fn 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 | | | |
| 3277 | fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!void { | 3330 | fn 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; |
| 3279 | | 3332 | |
| ... | @@ -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 | } |
| 3797 | | 3847 | |
| 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 payload | 3983 | // 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 }); |
| 3939 | | 3987 | |
| 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 | } |
| 3957 | | 4005 | |
| 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 | } |
| 3961 | | 4015 | |
| ... | @@ -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); |
| 4427 | | 4488 | |
| 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 lower | 4493 | /// 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. |
| 4435 | fn memset(func: *CodeGen, ptr: WValue, len: WValue, value: WValue) InnerError!void { | 4496 | fn 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 ourselves | 4500 | // 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 | } |
| 4445 | | 4508 | |
| 4446 | // When the length is comptime-known we do the loop at codegen, rather | 4509 | const final_len = switch (len) { |
| 4447 | // than emitting a runtime loop into the binary | 4510 | .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 loop | 4527 | 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 | } |
| 4515 | | 4583 | |
| 4516 | fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4584 | fn 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-initialized | 4896 | |
| 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 code | 4962 | |
| 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 | } |
| 4890 | | 4972 | |
| 4891 | fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 4973 | fn 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 | }; |
| 5373 | | 5455 | |
| 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.bits | 5460 | defer result.free(func); |
| 5380 | | 5461 | |
| 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 { |
| 5532 | | 5613 | |
| 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 | }; |
| 5541 | | 5618 | |
| 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 | }; |
| 5547 | | 5624 | |
| ... | @@ -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); |
| 5593 | | 5762 | |
| ... | @@ -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 | } |
| 5827 | | 5996 | |
| ... | @@ -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 | } |
| 5837 | | 6006 | |
| 5838 | fn lowerTry( | 6007 | fn 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 | } |
| 5967 | | 6145 | |
| | 6146 | fn 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 | |
| 5968 | fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6166 | fn 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; |
| 5970 | | 6168 | |
| ... | @@ -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 { |
| 6969 | | 7167 | |
| 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 | |
| | 7171 | fn 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 | } |