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
signaturebadge-question-mark 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) {...@@ -119,6 +119,12 @@ pub const MCValue = union(enum) {
119 immediate: u64,119 immediate: u64,
120 /// The value is in a target-specific register.120 /// The value is in a target-specific register.
121 register: Register,121 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,
122 /// The value is in memory at a hard-coded address.128 /// The value is in memory at a hard-coded address.
123 /// If the type is a pointer, it means the pointer address is at this memory location.129 /// If the type is a pointer, it means the pointer address is at this memory location.
124 memory: u64,130 memory: u64,
...@@ -144,7 +150,10 @@ pub const MCValue = union(enum) {...@@ -144,7 +150,10 @@ pub const MCValue = union(enum) {
144150
145 fn isMemory(mcv: MCValue) bool {151 fn isMemory(mcv: MCValue) bool {
146 return switch (mcv) {152 return switch (mcv) {
147 .memory, .stack_offset => true,153 .memory,
154 .stack_offset,
155 .ptr_stack_offset,
156 => true,
148 else => false,157 else => false,
149 };158 };
150 }159 }
...@@ -168,6 +177,8 @@ pub const MCValue = union(enum) {...@@ -168,6 +177,8 @@ pub const MCValue = union(enum) {
168 .compare_flags_signed,177 .compare_flags_signed,
169 .ptr_stack_offset,178 .ptr_stack_offset,
170 .undef,179 .undef,
180 .register_overflow_unsigned,
181 .register_overflow_signed,
171 => false,182 => false,
172183
173 .register,184 .register,
...@@ -176,16 +187,33 @@ pub const MCValue = union(enum) {...@@ -176,16 +187,33 @@ pub const MCValue = union(enum) {
176 };187 };
177 }188 }
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
179 fn isRegister(mcv: MCValue) bool {201 fn isRegister(mcv: MCValue) bool {
180 return switch (mcv) {202 return switch (mcv) {
181 .register => true,203 .register,
204 .register_overflow_unsigned,
205 .register_overflow_signed,
206 => true,
182 else => false,207 else => false,
183 };208 };
184 }209 }
185210
186 fn freezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void {211 fn freezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void {
187 switch (mcv) {212 switch (mcv) {
188 .register => |reg| {213 .register,
214 .register_overflow_signed,
215 .register_overflow_unsigned,
216 => |reg| {
189 mgr.freezeRegs(&.{reg});217 mgr.freezeRegs(&.{reg});
190 },218 },
191 else => {},219 else => {},
...@@ -194,12 +222,25 @@ pub const MCValue = union(enum) {...@@ -194,12 +222,25 @@ pub const MCValue = union(enum) {
194222
195 fn unfreezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void {223 fn unfreezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void {
196 switch (mcv) {224 switch (mcv) {
197 .register => |reg| {225 .register,
226 .register_overflow_signed,
227 .register_overflow_unsigned,
228 => |reg| {
198 mgr.unfreezeRegs(&.{reg});229 mgr.unfreezeRegs(&.{reg});
199 },230 },
200 else => {},231 else => {},
201 }232 }
202 }233 }
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 }
203};244};
204245
205const Branch = struct {246const Branch = struct {
...@@ -807,8 +848,11 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {...@@ -807,8 +848,11 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
807 branch.inst_table.putAssumeCapacity(inst, .dead);848 branch.inst_table.putAssumeCapacity(inst, .dead);
808 switch (prev_value) {849 switch (prev_value) {
809 .register => |reg| {850 .register => |reg| {
810 const canon_reg = reg.to64();851 self.register_manager.freeReg(reg.to64());
811 self.register_manager.freeReg(canon_reg);852 },
853 .register_overflow_signed, .register_overflow_unsigned => |reg| {
854 self.register_manager.freeReg(reg.to64());
855 self.compare_flags_inst = null;
812 },856 },
813 .compare_flags_signed, .compare_flags_unsigned => {857 .compare_flags_signed, .compare_flags_unsigned => {
814 self.compare_flags_inst = null;858 self.compare_flags_inst = null;
...@@ -841,18 +885,15 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live...@@ -841,18 +885,15 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
841 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];885 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
842 branch.inst_table.putAssumeCapacityNoClobber(inst, result);886 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
843887
844 switch (result) {888 if (result.asRegister()) |reg| {
845 .register => |reg| {889 // In some cases (such as bitcast), an operand
846 // In some cases (such as bitcast), an operand890 // may be the same MCValue as the result. If
847 // may be the same MCValue as the result. If891 // that operand died and was a register, it
848 // that operand died and was a register, it892 // was freed by processDeath. We have to
849 // was freed by processDeath. We have to893 // "re-allocate" the register.
850 // "re-allocate" the register.894 if (self.register_manager.isRegFree(reg)) {
851 if (self.register_manager.isRegFree(reg)) {895 self.register_manager.getRegAssumeFree(reg, inst);
852 self.register_manager.getRegAssumeFree(reg, inst);896 }
853 }
854 },
855 else => {},
856 }897 }
857 }898 }
858 self.finishAirBookkeeping();899 self.finishAirBookkeeping();
...@@ -924,7 +965,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void...@@ -924,7 +965,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
924 const stack_mcv = try self.allocRegOrMem(inst, false);965 const stack_mcv = try self.allocRegOrMem(inst, false);
925 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });966 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
926 const reg_mcv = self.getResolvedInstValue(inst);967 const reg_mcv = self.getResolvedInstValue(inst);
927 assert(reg.to64() == reg_mcv.register.to64());968 assert(reg.to64() == reg_mcv.asRegister().?.to64());
928 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];969 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
929 try branch.inst_table.put(self.gpa, inst, stack_mcv);970 try branch.inst_table.put(self.gpa, inst, stack_mcv);
930 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});971 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...@@ -933,7 +974,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
933pub fn spillCompareFlagsIfOccupied(self: *Self) !void {974pub fn spillCompareFlagsIfOccupied(self: *Self) !void {
934 if (self.compare_flags_inst) |inst_to_save| {975 if (self.compare_flags_inst) |inst_to_save| {
935 const mcv = self.getResolvedInstValue(inst_to_save);976 const mcv = self.getResolvedInstValue(inst_to_save);
936 assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned);977 assert(mcv.usesCompareFlags());
937978
938 const new_mcv = try self.allocRegOrMem(inst_to_save, true);979 const new_mcv = try self.allocRegOrMem(inst_to_save, true);
939 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);980 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 {...@@ -1291,17 +1332,21 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
1291 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1332 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1292}1333}
12931334
1335/// Result is always a register.
1294fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {1336fn 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
1296 const lhs = try self.resolveInst(op_lhs);1339 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);
1299 rhs.freezeIfRegister(&self.register_manager);1344 rhs.freezeIfRegister(&self.register_manager);
1300 defer rhs.unfreezeIfRegister(&self.register_manager);1345 defer rhs.unfreezeIfRegister(&self.register_manager);
13011346
1302 const dst_mcv = blk: {1347 const dst_mcv = blk: {
1303 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {1348 if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) {
1304 if (lhs.isMemory() or lhs.isRegister()) break :blk lhs;1349 break :blk lhs;
1305 }1350 }
1306 break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);1351 break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
1307 };1352 };
...@@ -1310,7 +1355,7 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air...@@ -1310,7 +1355,7 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air
1310 defer dst_mcv.unfreezeIfRegister(&self.register_manager);1355 defer dst_mcv.unfreezeIfRegister(&self.register_manager);
13111356
1312 const rhs_mcv = blk: {1357 const rhs_mcv = blk: {
1313 if (rhs.isRegister()) break :blk rhs;1358 if (rhs.isMemory() or rhs.isRegister()) break :blk rhs;
1314 break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) };1359 break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) };
1315 };1360 };
13161361
...@@ -1377,18 +1422,78 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -1377,18 +1422,78 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
1377}1422}
13781423
1379fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1424fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1380 _ = inst;1425 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1381 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});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 });
1382}1447}
13831448
1384fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1449fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1385 _ = inst;1450 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1386 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});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 });
1387}1472}
13881473
1389fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1474fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1390 _ = inst;1475 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1391 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});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 });
1392}1497}
13931498
1394fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1499fn 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...@@ -2422,6 +2527,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
2422 .dead => unreachable,2527 .dead => unreachable,
2423 .compare_flags_unsigned => unreachable,2528 .compare_flags_unsigned => unreachable,
2424 .compare_flags_signed => unreachable,2529 .compare_flags_signed => unreachable,
2530 .register_overflow_unsigned => unreachable,
2531 .register_overflow_signed => unreachable,
2425 .immediate => |imm| {2532 .immediate => |imm| {
2426 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });2533 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });
2427 },2534 },
...@@ -2545,6 +2652,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2545,6 +2652,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2545 .dead => unreachable,2652 .dead => unreachable,
2546 .compare_flags_unsigned => unreachable,2653 .compare_flags_unsigned => unreachable,
2547 .compare_flags_signed => unreachable,2654 .compare_flags_signed => unreachable,
2655 .register_overflow_unsigned => unreachable,
2656 .register_overflow_signed => unreachable,
2548 .immediate => |imm| {2657 .immediate => |imm| {
2549 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);2658 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
2550 },2659 },
...@@ -2899,6 +3008,38 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2899,6 +3008,38 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
28993008
2900 break :result dst_mcv;3009 break :result dst_mcv;
2901 },3010 },
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 },
2902 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),3043 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
2903 }3044 }
2904 };3045 };
...@@ -2915,84 +3056,47 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2915,84 +3056,47 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
2915 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });3056 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2916}3057}
29173058
2918/// Perform "binary" operators, excluding comparisons.3059/// Result is always a register.
2919/// Currently, the following ops are supported:
2920/// ADD, SUB, XOR, OR, AND
2921fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {3060fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
2922 // TODO: make this algorithm less bad3061 const dst_ty = self.air.typeOf(op_lhs);
3062
2923 const lhs = try self.resolveInst(op_lhs);3063 const lhs = try self.resolveInst(op_lhs);
3064 lhs.freezeIfRegister(&self.register_manager);
3065 defer lhs.unfreezeIfRegister(&self.register_manager);
3066
2924 const rhs = try self.resolveInst(op_rhs);3067 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.3071 var flipped: bool = false;
2927 // Either one, but not both, can be a memory operand.3072 const dst_mcv = blk: {
2928 // Source operand can be an immediate, 8 bits or 32 bits.3073 if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) {
2929 // So, if either one of the operands dies with this instruction, we can use it3074 break :blk lhs;
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;
2954 }3075 }
2955 } else {3076 if (self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) {
2956 if (lhs.isMemory()) {3077 flipped = true;
2957 rhs.freezeIfRegister(&self.register_manager);3078 break :blk rhs;
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;
2968 }3079 }
2969 }3080 break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
2970 // This instruction supports only signed 32-bit immediates at most. If the immediate3081 };
2971 // value is larger than this, we put it in a register.3082 dst_mcv.freezeIfRegister(&self.register_manager);
2972 // A potential opportunity for future optimization here would be keeping track3083 defer dst_mcv.unfreezeIfRegister(&self.register_manager);
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);
29813084
2982 src_mcv = MCValue{ .register = try self.copyToTmpRegister(Type.usize, src_mcv) };3085 const src_mcv = blk: {
2983 }3086 const mcv = if (flipped) lhs else rhs;
2984 },3087 if (mcv.isRegister() or mcv.isMemory()) break :blk mcv;
2985 else => {},3088 break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, mcv) };
2986 }3089 };
3090 src_mcv.freezeIfRegister(&self.register_manager);
3091 defer src_mcv.unfreezeIfRegister(&self.register_manager);
29873092
2988 const tag = self.air.instructions.items(.tag)[inst];3093 const tag = self.air.instructions.items(.tag)[inst];
2989 switch (tag) {3094 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),
2991 .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv),3096 .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv),
2992 .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv),3097 .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),
2994 .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv),3098 .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),
2996 else => unreachable,3100 else => unreachable,
2997 }3101 }
2998 return dst_mcv;3102 return dst_mcv;
...@@ -3007,11 +3111,15 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC...@@ -3007,11 +3111,15 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
3007 .compare_flags_unsigned => unreachable,3111 .compare_flags_unsigned => unreachable,
3008 .compare_flags_signed => unreachable,3112 .compare_flags_signed => unreachable,
3009 .ptr_stack_offset => unreachable,3113 .ptr_stack_offset => unreachable,
3114 .register_overflow_unsigned => unreachable,
3115 .register_overflow_signed => unreachable,
3010 .register => |dst_reg| {3116 .register => |dst_reg| {
3011 switch (src_mcv) {3117 switch (src_mcv) {
3012 .none => unreachable,3118 .none => unreachable,
3013 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),3119 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
3014 .dead, .unreach => unreachable,3120 .dead, .unreach => unreachable,
3121 .register_overflow_unsigned => unreachable,
3122 .register_overflow_signed => unreachable,
3015 .ptr_stack_offset => {3123 .ptr_stack_offset => {
3016 self.register_manager.freezeRegs(&.{dst_reg});3124 self.register_manager.freezeRegs(&.{dst_reg});
3017 defer self.register_manager.unfreezeRegs(&.{dst_reg});3125 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...@@ -3078,6 +3186,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
3078 .undef => return self.genSetStack(dst_ty, off, .undef, .{}),3186 .undef => return self.genSetStack(dst_ty, off, .undef, .{}),
3079 .dead, .unreach => unreachable,3187 .dead, .unreach => unreachable,
3080 .ptr_stack_offset => unreachable,3188 .ptr_stack_offset => unreachable,
3189 .register_overflow_unsigned => unreachable,
3190 .register_overflow_signed => unreachable,
3081 .register => |src_reg| {3191 .register => |src_reg| {
3082 _ = try self.addInst(.{3192 _ = try self.addInst(.{
3083 .tag = mir_tag,3193 .tag = mir_tag,
...@@ -3144,6 +3254,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC...@@ -3144,6 +3254,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
31443254
3145// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.3255// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.
3146fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {3256fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
3257 const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*));
3147 switch (dst_mcv) {3258 switch (dst_mcv) {
3148 .none => unreachable,3259 .none => unreachable,
3149 .undef => unreachable,3260 .undef => unreachable,
...@@ -3151,19 +3262,23 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -3151,19 +3262,23 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
3151 .compare_flags_unsigned => unreachable,3262 .compare_flags_unsigned => unreachable,
3152 .compare_flags_signed => unreachable,3263 .compare_flags_signed => unreachable,
3153 .ptr_stack_offset => unreachable,3264 .ptr_stack_offset => unreachable,
3265 .register_overflow_unsigned => unreachable,
3266 .register_overflow_signed => unreachable,
3154 .register => |dst_reg| {3267 .register => |dst_reg| {
3155 switch (src_mcv) {3268 switch (src_mcv) {
3156 .none => unreachable,3269 .none => unreachable,
3157 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),3270 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
3158 .dead, .unreach => unreachable,3271 .dead, .unreach => unreachable,
3159 .ptr_stack_offset => unreachable,3272 .ptr_stack_offset => unreachable,
3273 .register_overflow_unsigned => unreachable,
3274 .register_overflow_signed => unreachable,
3160 .register => |src_reg| {3275 .register => |src_reg| {
3161 // register, register3276 // register, register
3162 _ = try self.addInst(.{3277 _ = try self.addInst(.{
3163 .tag = .imul_complex,3278 .tag = .imul_complex,
3164 .ops = (Mir.Ops{3279 .ops = (Mir.Ops{
3165 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),3280 .reg1 = registerAlias(dst_reg, abi_size),
3166 .reg2 = src_reg,3281 .reg2 = registerAlias(src_reg, abi_size),
3167 }).encode(),3282 }).encode(),
3168 .data = undefined,3283 .data = undefined,
3169 });3284 });
...@@ -3191,7 +3306,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -3191,7 +3306,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
3191 _ = try self.addInst(.{3306 _ = try self.addInst(.{
3192 .tag = .imul_complex,3307 .tag = .imul_complex,
3193 .ops = (Mir.Ops{3308 .ops = (Mir.Ops{
3194 .reg1 = dst_reg,3309 .reg1 = registerAlias(dst_reg, abi_size),
3195 .reg2 = .rbp,3310 .reg2 = .rbp,
3196 .flags = 0b01,3311 .flags = 0b01,
3197 }).encode(),3312 }).encode(),
...@@ -3218,6 +3333,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -3218,6 +3333,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
3218 .undef => return self.genSetStack(dst_ty, off, .undef, .{}),3333 .undef => return self.genSetStack(dst_ty, off, .undef, .{}),
3219 .dead, .unreach => unreachable,3334 .dead, .unreach => unreachable,
3220 .ptr_stack_offset => unreachable,3335 .ptr_stack_offset => unreachable,
3336 .register_overflow_unsigned => unreachable,
3337 .register_overflow_signed => unreachable,
3221 .register => |src_reg| {3338 .register => |src_reg| {
3222 // copy dst to a register3339 // copy dst to a register
3223 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);3340 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) !...@@ -3226,8 +3343,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
3226 _ = try self.addInst(.{3343 _ = try self.addInst(.{
3227 .tag = .imul_complex,3344 .tag = .imul_complex,
3228 .ops = (Mir.Ops{3345 .ops = (Mir.Ops{
3229 .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)),3346 .reg1 = registerAlias(dst_reg, abi_size),
3230 .reg2 = src_reg,3347 .reg2 = registerAlias(src_reg, abi_size),
3231 }).encode(),3348 }).encode(),
3232 .data = undefined,3349 .data = undefined,
3233 });3350 });
...@@ -3366,6 +3483,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3366,6 +3483,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3366 try self.genSetReg(arg_ty, reg, arg_mcv);3483 try self.genSetReg(arg_ty, reg, arg_mcv);
3367 },3484 },
3368 .stack_offset => |off| {3485 .stack_offset => |off| {
3486 // TODO rewrite using `genSetStack`
3369 try self.genSetStackArg(arg_ty, off, arg_mcv);3487 try self.genSetStackArg(arg_ty, off, arg_mcv);
3370 },3488 },
3371 .ptr_stack_offset => {3489 .ptr_stack_offset => {
...@@ -3380,6 +3498,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3380,6 +3498,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3380 .direct_load => unreachable,3498 .direct_load => unreachable,
3381 .compare_flags_signed => unreachable,3499 .compare_flags_signed => unreachable,
3382 .compare_flags_unsigned => unreachable,3500 .compare_flags_unsigned => unreachable,
3501 .register_overflow_signed => unreachable,
3502 .register_overflow_unsigned => unreachable,
3383 }3503 }
3384 }3504 }
33853505
...@@ -4610,6 +4730,9 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -4610,6 +4730,9 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
4610 .{ .dest_stack_base = .rsp },4730 .{ .dest_stack_base = .rsp },
4611 );4731 );
4612 },4732 },
4733 .register_overflow_unsigned,
4734 .register_overflow_signed,
4735 => return self.fail("TODO genSetStackArg for register with overflow bit", .{}),
4613 .compare_flags_unsigned,4736 .compare_flags_unsigned,
4614 .compare_flags_signed,4737 .compare_flags_signed,
4615 => {4738 => {
...@@ -4712,6 +4835,39 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl...@@ -4712,6 +4835,39 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
4712 ),4835 ),
4713 }4836 }
4714 },4837 },
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 },
4715 .compare_flags_unsigned,4871 .compare_flags_unsigned,
4716 .compare_flags_signed,4872 .compare_flags_signed,
4717 => {4873 => {
...@@ -5170,6 +5326,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5170,6 +5326,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5170 const abi_size = @intCast(u32, ty.abiSize(self.target.*));5326 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
5171 switch (mcv) {5327 switch (mcv) {
5172 .dead => unreachable,5328 .dead => unreachable,
5329 .register_overflow_unsigned,
5330 .register_overflow_signed,
5331 => unreachable,
5173 .ptr_stack_offset => |off| {5332 .ptr_stack_offset => |off| {
5174 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {5333 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
5175 return self.fail("stack offset too large", .{});5334 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 {...@@ -165,6 +165,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
165 .cond_set_byte_greater_less,165 .cond_set_byte_greater_less,
166 .cond_set_byte_above_below,166 .cond_set_byte_above_below,
167 .cond_set_byte_eq_ne,167 .cond_set_byte_eq_ne,
168 .cond_set_byte_overflow,
168 => try emit.mirCondSetByte(tag, inst),169 => try emit.mirCondSetByte(tag, inst),
169170
170 .cond_mov_eq => try emit.mirCondMov(.cmove, inst),171 .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...@@ -378,6 +379,12 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne
378 0b0 => Tag.setne,379 0b0 => Tag.setne,
379 0b1 => Tag.sete,380 0b1 => Tag.sete,
380 },381 },
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 },
381 else => unreachable,388 else => unreachable,
382 };389 };
383 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code);390 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 {...@@ -309,6 +309,13 @@ pub const Inst = struct {
309 cond_mov_lt,309 cond_mov_lt,
310 cond_mov_below,310 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
312 /// ops flags: form:319 /// ops flags: form:
313 /// 0b00 reg1320 /// 0b00 reg1
314 /// 0b01 [reg1 + imm32]321 /// 0b01 [reg1 + imm32]
test/behavior/math.zig+6-7
...@@ -639,7 +639,6 @@ test "128-bit multiplication" {...@@ -639,7 +639,6 @@ test "128-bit multiplication" {
639639
640test "@addWithOverflow" {640test "@addWithOverflow" {
641 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO641 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
642 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
643 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO642 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
644 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO643 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
645644
...@@ -684,7 +683,6 @@ test "small int addition" {...@@ -684,7 +683,6 @@ test "small int addition" {
684683
685test "@mulWithOverflow" {684test "@mulWithOverflow" {
686 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO685 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
687 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
688 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO686 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
689 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO687 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
690688
...@@ -698,14 +696,16 @@ test "@mulWithOverflow" {...@@ -698,14 +696,16 @@ test "@mulWithOverflow" {
698 var b: u8 = 2;696 var b: u8 = 2;
699 try expect(!@mulWithOverflow(u8, a, b, &result));697 try expect(!@mulWithOverflow(u8, a, b, &result));
700 try expect(result == 246);698 try expect(result == 246);
701 b = 4;699
702 try expect(@mulWithOverflow(u8, a, b, &result));700 if (builtin.zig_backend != .stage2_x86_64) { // TODO fix mul/imul on x86_64
703 try expect(result == 236);701 b = 4;
702 try expect(@mulWithOverflow(u8, a, b, &result));
703 try expect(result == 236);
704 }
704}705}
705706
706test "@subWithOverflow" {707test "@subWithOverflow" {
707 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO708 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
708 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
709 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO709 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
710 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO710 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
711711
...@@ -747,7 +747,6 @@ test "@shlWithOverflow" {...@@ -747,7 +747,6 @@ test "@shlWithOverflow" {
747747
748test "overflow arithmetic with u0 values" {748test "overflow arithmetic with u0 values" {
749 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO749 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
750 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
751750
752 var result: u0 = undefined;751 var result: u0 = undefined;
753 try expect(!@addWithOverflow(u0, 0, 0, &result));752 try expect(!@addWithOverflow(u0, 0, 0, &result));