authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-28 20:58:18+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-28 20:58:18+02:00
logc38b4bcee7c9e6913fdf95c1572a7d8125eab1a4
treededc9ad09cc815f11a4ca72f31c43caef240ed3e
parent25d4c5df706bb8ca6e7882f80d6f8209b9ad31f8
parente6729036e44af2a52c52ce9f1f99e01d1e642cf3
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11334 from ziglang/stage2-x64-overflow

x64: implement add, sub and mul with overflow

4 files changed, 280 insertions(+), 108 deletions(-)

src/arch/x86_64/CodeGen.zig+260-101
......@@ -119,6 +119,12 @@ pub const MCValue = union(enum) {
119119 immediate: u64,
120120 /// The value is in a target-specific register.
121121 register: Register,
122 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the register,
123 /// and the operation is an unsigned operation.
124 register_overflow_unsigned: Register,
125 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the register,
126 /// and the operation is a signed operation.
127 register_overflow_signed: Register,
122128 /// The value is in memory at a hard-coded address.
123129 /// If the type is a pointer, it means the pointer address is at this memory location.
124130 memory: u64,
......@@ -144,7 +150,10 @@ pub const MCValue = union(enum) {
144150
145151 fn isMemory(mcv: MCValue) bool {
146152 return switch (mcv) {
147 .memory, .stack_offset => true,
153 .memory,
154 .stack_offset,
155 .ptr_stack_offset,
156 => true,
148157 else => false,
149158 };
150159 }
......@@ -168,6 +177,8 @@ pub const MCValue = union(enum) {
168177 .compare_flags_signed,
169178 .ptr_stack_offset,
170179 .undef,
180 .register_overflow_unsigned,
181 .register_overflow_signed,
171182 => false,
172183
173184 .register,
......@@ -176,16 +187,33 @@ pub const MCValue = union(enum) {
176187 };
177188 }
178189
190 fn usesCompareFlags(mcv: MCValue) bool {
191 return switch (mcv) {
192 .compare_flags_unsigned,
193 .compare_flags_signed,
194 .register_overflow_unsigned,
195 .register_overflow_signed,
196 => true,
197 else => false,
198 };
199 }
200
179201 fn isRegister(mcv: MCValue) bool {
180202 return switch (mcv) {
181 .register => true,
203 .register,
204 .register_overflow_unsigned,
205 .register_overflow_signed,
206 => true,
182207 else => false,
183208 };
184209 }
185210
186211 fn freezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void {
187212 switch (mcv) {
188 .register => |reg| {
213 .register,
214 .register_overflow_signed,
215 .register_overflow_unsigned,
216 => |reg| {
189217 mgr.freezeRegs(&.{reg});
190218 },
191219 else => {},
......@@ -194,12 +222,25 @@ pub const MCValue = union(enum) {
194222
195223 fn unfreezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void {
196224 switch (mcv) {
197 .register => |reg| {
225 .register,
226 .register_overflow_signed,
227 .register_overflow_unsigned,
228 => |reg| {
198229 mgr.unfreezeRegs(&.{reg});
199230 },
200231 else => {},
201232 }
202233 }
234
235 fn asRegister(mcv: MCValue) ?Register {
236 return switch (mcv) {
237 .register,
238 .register_overflow_signed,
239 .register_overflow_unsigned,
240 => |reg| reg,
241 else => null,
242 };
243 }
203244};
204245
205246const Branch = struct {
......@@ -807,8 +848,11 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
807848 branch.inst_table.putAssumeCapacity(inst, .dead);
808849 switch (prev_value) {
809850 .register => |reg| {
810 const canon_reg = reg.to64();
811 self.register_manager.freeReg(canon_reg);
851 self.register_manager.freeReg(reg.to64());
852 },
853 .register_overflow_signed, .register_overflow_unsigned => |reg| {
854 self.register_manager.freeReg(reg.to64());
855 self.compare_flags_inst = null;
812856 },
813857 .compare_flags_signed, .compare_flags_unsigned => {
814858 self.compare_flags_inst = null;
......@@ -841,18 +885,15 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
841885 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
842886 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
843887
844 switch (result) {
845 .register => |reg| {
846 // In some cases (such as bitcast), an operand
847 // may be the same MCValue as the result. If
848 // that operand died and was a register, it
849 // was freed by processDeath. We have to
850 // "re-allocate" the register.
851 if (self.register_manager.isRegFree(reg)) {
852 self.register_manager.getRegAssumeFree(reg, inst);
853 }
854 },
855 else => {},
888 if (result.asRegister()) |reg| {
889 // In some cases (such as bitcast), an operand
890 // may be the same MCValue as the result. If
891 // that operand died and was a register, it
892 // was freed by processDeath. We have to
893 // "re-allocate" the register.
894 if (self.register_manager.isRegFree(reg)) {
895 self.register_manager.getRegAssumeFree(reg, inst);
896 }
856897 }
857898 }
858899 self.finishAirBookkeeping();
......@@ -924,7 +965,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
924965 const stack_mcv = try self.allocRegOrMem(inst, false);
925966 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
926967 const reg_mcv = self.getResolvedInstValue(inst);
927 assert(reg.to64() == reg_mcv.register.to64());
968 assert(reg.to64() == reg_mcv.asRegister().?.to64());
928969 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
929970 try branch.inst_table.put(self.gpa, inst, stack_mcv);
930971 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});
......@@ -933,7 +974,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
933974pub fn spillCompareFlagsIfOccupied(self: *Self) !void {
934975 if (self.compare_flags_inst) |inst_to_save| {
935976 const mcv = self.getResolvedInstValue(inst_to_save);
936 assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned);
977 assert(mcv.usesCompareFlags());
937978
938979 const new_mcv = try self.allocRegOrMem(inst_to_save, true);
939980 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
......@@ -1291,17 +1332,21 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
12911332 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12921333}
12931334
1335/// Result is always a register.
12941336fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
1295 const dst_ty = self.air.typeOfIndex(inst);
1337 const dst_ty = self.air.typeOf(op_lhs);
1338
12961339 const lhs = try self.resolveInst(op_lhs);
1297 const rhs = try self.resolveInst(op_rhs);
1340 lhs.freezeIfRegister(&self.register_manager);
1341 defer lhs.unfreezeIfRegister(&self.register_manager);
12981342
1343 const rhs = try self.resolveInst(op_rhs);
12991344 rhs.freezeIfRegister(&self.register_manager);
13001345 defer rhs.unfreezeIfRegister(&self.register_manager);
13011346
13021347 const dst_mcv = blk: {
1303 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {
1304 if (lhs.isMemory() or lhs.isRegister()) break :blk lhs;
1348 if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) {
1349 break :blk lhs;
13051350 }
13061351 break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
13071352 };
......@@ -1310,7 +1355,7 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air
13101355 defer dst_mcv.unfreezeIfRegister(&self.register_manager);
13111356
13121357 const rhs_mcv = blk: {
1313 if (rhs.isRegister()) break :blk rhs;
1358 if (rhs.isMemory() or rhs.isRegister()) break :blk rhs;
13141359 break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) };
13151360 };
13161361
......@@ -1377,18 +1422,78 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
13771422}
13781423
13791424fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1380 _ = inst;
1381 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
1425 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1426 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1427
1428 if (self.liveness.isUnused(inst)) {
1429 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1430 }
1431
1432 const ty = self.air.typeOf(bin_op.lhs);
1433 const signedness: std.builtin.Signedness = blk: {
1434 if (ty.zigTypeTag() != .Int) {
1435 return self.fail("TODO implement airAddWithOverflow for type {}", .{ty.fmtDebug()});
1436 }
1437 break :blk ty.intInfo(self.target.*).signedness;
1438 };
1439
1440 const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs);
1441 const result: MCValue = switch (signedness) {
1442 .signed => .{ .register_overflow_signed = partial.register },
1443 .unsigned => .{ .register_overflow_unsigned = partial.register },
1444 };
1445
1446 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13821447}
13831448
13841449fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1385 _ = inst;
1386 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
1450 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1451 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1452
1453 if (self.liveness.isUnused(inst)) {
1454 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1455 }
1456
1457 const ty = self.air.typeOf(bin_op.lhs);
1458 const signedness: std.builtin.Signedness = blk: {
1459 if (ty.zigTypeTag() != .Int) {
1460 return self.fail("TODO implement airSubWithOverflow for type {}", .{ty.fmtDebug()});
1461 }
1462 break :blk ty.intInfo(self.target.*).signedness;
1463 };
1464
1465 const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs);
1466 const result: MCValue = switch (signedness) {
1467 .signed => .{ .register_overflow_signed = partial.register },
1468 .unsigned => .{ .register_overflow_unsigned = partial.register },
1469 };
1470
1471 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13871472}
13881473
13891474fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1390 _ = inst;
1391 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
1475 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1476 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1477
1478 if (self.liveness.isUnused(inst)) {
1479 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1480 }
1481
1482 const ty = self.air.typeOf(bin_op.lhs);
1483 const signedness: std.builtin.Signedness = blk: {
1484 if (ty.zigTypeTag() != .Int) {
1485 return self.fail("TODO implement airMulWithOverflow for type {}", .{ty.fmtDebug()});
1486 }
1487 break :blk ty.intInfo(self.target.*).signedness;
1488 };
1489
1490 const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs);
1491 const result: MCValue = switch (signedness) {
1492 .signed => .{ .register_overflow_signed = partial.register },
1493 .unsigned => .{ .register_overflow_unsigned = partial.register },
1494 };
1495
1496 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13921497}
13931498
13941499fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
......@@ -2422,6 +2527,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
24222527 .dead => unreachable,
24232528 .compare_flags_unsigned => unreachable,
24242529 .compare_flags_signed => unreachable,
2530 .register_overflow_unsigned => unreachable,
2531 .register_overflow_signed => unreachable,
24252532 .immediate => |imm| {
24262533 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });
24272534 },
......@@ -2545,6 +2652,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
25452652 .dead => unreachable,
25462653 .compare_flags_unsigned => unreachable,
25472654 .compare_flags_signed => unreachable,
2655 .register_overflow_unsigned => unreachable,
2656 .register_overflow_signed => unreachable,
25482657 .immediate => |imm| {
25492658 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
25502659 },
......@@ -2899,6 +3008,38 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
28993008
29003009 break :result dst_mcv;
29013010 },
3011 .register_overflow_unsigned,
3012 .register_overflow_signed,
3013 => |reg| {
3014 switch (index) {
3015 0 => {
3016 // Get wrapped value for overflow operation.
3017 break :result MCValue{ .register = reg };
3018 },
3019 1 => {
3020 // Get overflow bit.
3021 mcv.freezeIfRegister(&self.register_manager);
3022 defer mcv.unfreezeIfRegister(&self.register_manager);
3023
3024 const dst_reg = try self.register_manager.allocReg(inst);
3025 const flags: u2 = switch (mcv) {
3026 .register_overflow_unsigned => 0b10,
3027 .register_overflow_signed => 0b00,
3028 else => unreachable,
3029 };
3030 _ = try self.addInst(.{
3031 .tag = .cond_set_byte_overflow,
3032 .ops = (Mir.Ops{
3033 .reg1 = dst_reg.to8(),
3034 .flags = flags,
3035 }).encode(),
3036 .data = undefined,
3037 });
3038 break :result MCValue{ .register = dst_reg.to8() };
3039 },
3040 else => unreachable,
3041 }
3042 },
29023043 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
29033044 }
29043045 };
......@@ -2915,84 +3056,47 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
29153056 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
29163057}
29173058
2918/// Perform "binary" operators, excluding comparisons.
2919/// Currently, the following ops are supported:
2920/// ADD, SUB, XOR, OR, AND
3059/// Result is always a register.
29213060fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
2922 // TODO: make this algorithm less bad
3061 const dst_ty = self.air.typeOf(op_lhs);
3062
29233063 const lhs = try self.resolveInst(op_lhs);
3064 lhs.freezeIfRegister(&self.register_manager);
3065 defer lhs.unfreezeIfRegister(&self.register_manager);
3066
29243067 const rhs = try self.resolveInst(op_rhs);
3068 rhs.freezeIfRegister(&self.register_manager);
3069 defer rhs.unfreezeIfRegister(&self.register_manager);
29253070
2926 // There are 2 operands, destination and source.
2927 // Either one, but not both, can be a memory operand.
2928 // Source operand can be an immediate, 8 bits or 32 bits.
2929 // So, if either one of the operands dies with this instruction, we can use it
2930 // as the result MCValue.
2931 const dst_ty = self.air.typeOfIndex(inst);
2932 var dst_mcv: MCValue = undefined;
2933 var src_mcv: MCValue = undefined;
2934
2935 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {
2936 // LHS dies; use it as the destination.
2937 // Both operands cannot be memory.
2938 if (lhs.isMemory() and rhs.isMemory()) {
2939 dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
2940 src_mcv = rhs;
2941 } else {
2942 dst_mcv = lhs;
2943 src_mcv = rhs;
2944 }
2945 } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) {
2946 // RHS dies; use it as the destination.
2947 // Both operands cannot be memory.
2948 if (lhs.isMemory() and rhs.isMemory()) {
2949 dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, rhs);
2950 src_mcv = lhs;
2951 } else {
2952 dst_mcv = rhs;
2953 src_mcv = lhs;
3071 var flipped: bool = false;
3072 const dst_mcv = blk: {
3073 if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) {
3074 break :blk lhs;
29543075 }
2955 } else {
2956 if (lhs.isMemory()) {
2957 rhs.freezeIfRegister(&self.register_manager);
2958 defer rhs.unfreezeIfRegister(&self.register_manager);
2959
2960 dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
2961 src_mcv = rhs;
2962 } else {
2963 lhs.freezeIfRegister(&self.register_manager);
2964 defer lhs.unfreezeIfRegister(&self.register_manager);
2965
2966 dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, rhs);
2967 src_mcv = lhs;
3076 if (self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) {
3077 flipped = true;
3078 break :blk rhs;
29683079 }
2969 }
2970 // This instruction supports only signed 32-bit immediates at most. If the immediate
2971 // value is larger than this, we put it in a register.
2972 // A potential opportunity for future optimization here would be keeping track
2973 // of the fact that the instruction is available both as an immediate
2974 // and as a register.
2975 // TODO consolidate with limitImmediateType() function
2976 switch (src_mcv) {
2977 .immediate => |imm| {
2978 if (imm > math.maxInt(u31)) {
2979 dst_mcv.freezeIfRegister(&self.register_manager);
2980 defer dst_mcv.unfreezeIfRegister(&self.register_manager);
3080 break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
3081 };
3082 dst_mcv.freezeIfRegister(&self.register_manager);
3083 defer dst_mcv.unfreezeIfRegister(&self.register_manager);
29813084
2982 src_mcv = MCValue{ .register = try self.copyToTmpRegister(Type.usize, src_mcv) };
2983 }
2984 },
2985 else => {},
2986 }
3085 const src_mcv = blk: {
3086 const mcv = if (flipped) lhs else rhs;
3087 if (mcv.isRegister() or mcv.isMemory()) break :blk mcv;
3088 break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, mcv) };
3089 };
3090 src_mcv.freezeIfRegister(&self.register_manager);
3091 defer src_mcv.unfreezeIfRegister(&self.register_manager);
29873092
29883093 const tag = self.air.instructions.items(.tag)[inst];
29893094 switch (tag) {
2990 .add, .addwrap => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv),
3095 .add, .addwrap, .add_with_overflow => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv),
29913096 .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv),
29923097 .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv),
2993 .sub, .subwrap => try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, src_mcv),
29943098 .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv),
2995 .mul, .mulwrap => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv),
3099 .mul, .mulwrap, .mul_with_overflow => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv),
29963100 else => unreachable,
29973101 }
29983102 return dst_mcv;
......@@ -3007,11 +3111,15 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
30073111 .compare_flags_unsigned => unreachable,
30083112 .compare_flags_signed => unreachable,
30093113 .ptr_stack_offset => unreachable,
3114 .register_overflow_unsigned => unreachable,
3115 .register_overflow_signed => unreachable,
30103116 .register => |dst_reg| {
30113117 switch (src_mcv) {
30123118 .none => unreachable,
30133119 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
30143120 .dead, .unreach => unreachable,
3121 .register_overflow_unsigned => unreachable,
3122 .register_overflow_signed => unreachable,
30153123 .ptr_stack_offset => {
30163124 self.register_manager.freezeRegs(&.{dst_reg});
30173125 defer self.register_manager.unfreezeRegs(&.{dst_reg});
......@@ -3078,6 +3186,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
30783186 .undef => return self.genSetStack(dst_ty, off, .undef, .{}),
30793187 .dead, .unreach => unreachable,
30803188 .ptr_stack_offset => unreachable,
3189 .register_overflow_unsigned => unreachable,
3190 .register_overflow_signed => unreachable,
30813191 .register => |src_reg| {
30823192 _ = try self.addInst(.{
30833193 .tag = mir_tag,
......@@ -3144,6 +3254,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
31443254
31453255// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.
31463256fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
3257 const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
31473258 switch (dst_mcv) {
31483259 .none => unreachable,
31493260 .undef => unreachable,
......@@ -3151,19 +3262,23 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
31513262 .compare_flags_unsigned => unreachable,
31523263 .compare_flags_signed => unreachable,
31533264 .ptr_stack_offset => unreachable,
3265 .register_overflow_unsigned => unreachable,
3266 .register_overflow_signed => unreachable,
31543267 .register => |dst_reg| {
31553268 switch (src_mcv) {
31563269 .none => unreachable,
31573270 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
31583271 .dead, .unreach => unreachable,
31593272 .ptr_stack_offset => unreachable,
3273 .register_overflow_unsigned => unreachable,
3274 .register_overflow_signed => unreachable,
31603275 .register => |src_reg| {
31613276 // register, register
31623277 _ = try self.addInst(.{
31633278 .tag = .imul_complex,
31643279 .ops = (Mir.Ops{
3165 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),
3166 .reg2 = src_reg,
3280 .reg1 = registerAlias(dst_reg, abi_size),
3281 .reg2 = registerAlias(src_reg, abi_size),
31673282 }).encode(),
31683283 .data = undefined,
31693284 });
......@@ -3191,7 +3306,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
31913306 _ = try self.addInst(.{
31923307 .tag = .imul_complex,
31933308 .ops = (Mir.Ops{
3194 .reg1 = dst_reg,
3309 .reg1 = registerAlias(dst_reg, abi_size),
31953310 .reg2 = .rbp,
31963311 .flags = 0b01,
31973312 }).encode(),
......@@ -3218,6 +3333,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
32183333 .undef => return self.genSetStack(dst_ty, off, .undef, .{}),
32193334 .dead, .unreach => unreachable,
32203335 .ptr_stack_offset => unreachable,
3336 .register_overflow_unsigned => unreachable,
3337 .register_overflow_signed => unreachable,
32213338 .register => |src_reg| {
32223339 // copy dst to a register
32233340 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
......@@ -3226,8 +3343,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
32263343 _ = try self.addInst(.{
32273344 .tag = .imul_complex,
32283345 .ops = (Mir.Ops{
3229 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),
3230 .reg2 = src_reg,
3346 .reg1 = registerAlias(dst_reg, abi_size),
3347 .reg2 = registerAlias(src_reg, abi_size),
32313348 }).encode(),
32323349 .data = undefined,
32333350 });
......@@ -3366,6 +3483,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
33663483 try self.genSetReg(arg_ty, reg, arg_mcv);
33673484 },
33683485 .stack_offset => |off| {
3486 // TODO rewrite using `genSetStack`
33693487 try self.genSetStackArg(arg_ty, off, arg_mcv);
33703488 },
33713489 .ptr_stack_offset => {
......@@ -3380,6 +3498,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
33803498 .direct_load => unreachable,
33813499 .compare_flags_signed => unreachable,
33823500 .compare_flags_unsigned => unreachable,
3501 .register_overflow_signed => unreachable,
3502 .register_overflow_unsigned => unreachable,
33833503 }
33843504 }
33853505
......@@ -4610,6 +4730,9 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
46104730 .{ .dest_stack_base = .rsp },
46114731 );
46124732 },
4733 .register_overflow_unsigned,
4734 .register_overflow_signed,
4735 => return self.fail("TODO genSetStackArg for register with overflow bit", .{}),
46134736 .compare_flags_unsigned,
46144737 .compare_flags_signed,
46154738 => {
......@@ -4712,6 +4835,39 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
47124835 ),
47134836 }
47144837 },
4838 .register_overflow_unsigned,
4839 .register_overflow_signed,
4840 => |reg| {
4841 self.register_manager.freezeRegs(&.{reg});
4842 defer self.register_manager.unfreezeRegs(&.{reg});
4843
4844 const wrapped_ty = ty.structFieldType(0);
4845 try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{});
4846
4847 const overflow_bit_ty = ty.structFieldType(1);
4848 const overflow_bit_offset = ty.structFieldOffset(1, self.target.*);
4849 const tmp_reg = try self.register_manager.allocReg(null);
4850 const flags: u2 = switch (mcv) {
4851 .register_overflow_unsigned => 0b10,
4852 .register_overflow_signed => 0b00,
4853 else => unreachable,
4854 };
4855 _ = try self.addInst(.{
4856 .tag = .cond_set_byte_overflow,
4857 .ops = (Mir.Ops{
4858 .reg1 = tmp_reg.to8(),
4859 .flags = flags,
4860 }).encode(),
4861 .data = undefined,
4862 });
4863
4864 return self.genSetStack(
4865 overflow_bit_ty,
4866 stack_offset - @intCast(i32, overflow_bit_offset),
4867 .{ .register = tmp_reg.to8() },
4868 .{},
4869 );
4870 },
47154871 .compare_flags_unsigned,
47164872 .compare_flags_signed,
47174873 => {
......@@ -5170,6 +5326,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
51705326 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
51715327 switch (mcv) {
51725328 .dead => unreachable,
5329 .register_overflow_unsigned,
5330 .register_overflow_signed,
5331 => unreachable,
51735332 .ptr_stack_offset => |off| {
51745333 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
51755334 return self.fail("stack offset too large", .{});
src/arch/x86_64/Emit.zig+7
......@@ -165,6 +165,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
165165 .cond_set_byte_greater_less,
166166 .cond_set_byte_above_below,
167167 .cond_set_byte_eq_ne,
168 .cond_set_byte_overflow,
168169 => try emit.mirCondSetByte(tag, inst),
169170
170171 .cond_mov_eq => try emit.mirCondMov(.cmove, inst),
......@@ -378,6 +379,12 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne
378379 0b0 => Tag.setne,
379380 0b1 => Tag.sete,
380381 },
382 .cond_set_byte_overflow => switch (ops.flags) {
383 0b00 => Tag.seto,
384 0b01 => Tag.setno,
385 0b10 => Tag.setc,
386 0b11 => Tag.setnc,
387 },
381388 else => unreachable,
382389 };
383390 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code);
src/arch/x86_64/Mir.zig+7
......@@ -309,6 +309,13 @@ pub const Inst = struct {
309309 cond_mov_lt,
310310 cond_mov_below,
311311
312 /// ops flags:
313 /// 0b00 reg1 if OF = 1
314 /// 0b01 reg1 if OF = 0
315 /// 0b10 reg1 if CF = 1
316 /// 0b11 reg1 if CF = 0
317 cond_set_byte_overflow,
318
312319 /// ops flags: form:
313320 /// 0b00 reg1
314321 /// 0b01 [reg1 + imm32]
test/behavior/math.zig+6-7
......@@ -639,7 +639,6 @@ test "128-bit multiplication" {
639639
640640test "@addWithOverflow" {
641641 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
642 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
643642 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
644643 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
645644
......@@ -684,7 +683,6 @@ test "small int addition" {
684683
685684test "@mulWithOverflow" {
686685 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
687 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
688686 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
689687 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
690688
......@@ -698,14 +696,16 @@ test "@mulWithOverflow" {
698696 var b: u8 = 2;
699697 try expect(!@mulWithOverflow(u8, a, b, &result));
700698 try expect(result == 246);
701 b = 4;
702 try expect(@mulWithOverflow(u8, a, b, &result));
703 try expect(result == 236);
699
700 if (builtin.zig_backend != .stage2_x86_64) { // TODO fix mul/imul on x86_64
701 b = 4;
702 try expect(@mulWithOverflow(u8, a, b, &result));
703 try expect(result == 236);
704 }
704705}
705706
706707test "@subWithOverflow" {
707708 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
708 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
709709 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
710710 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
711711
......@@ -747,7 +747,6 @@ test "@shlWithOverflow" {
747747
748748test "overflow arithmetic with u0 values" {
749749 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
750 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
751750
752751 var result: u0 = undefined;
753752 try expect(!@addWithOverflow(u0, 0, 0, &result));