authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-28 13:20:09+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-28 16:37:35+02:00
log107052aded5dc1666c687838e8e2995654f17a45
tree7505f2f74cf7ea53d6dac7c1404bd97d7b658de9
parent25d4c5df706bb8ca6e7882f80d6f8209b9ad31f8

x64: implement add, sub and mul with overflow


3 files changed, 269 insertions(+), 97 deletions(-)

src/arch/x86_64/CodeGen.zig+255-97
...@@ -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 }3075 }
2945 } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) {3076 if (self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) {
2946 // RHS dies; use it as the destination.3077 flipped = true;
2947 // Both operands cannot be memory.3078 break :blk rhs;
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 }3079 }
2955 } else {3080 break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
2956 if (lhs.isMemory()) {3081 };
2957 rhs.freezeIfRegister(&self.register_manager);3082 dst_mcv.freezeIfRegister(&self.register_manager);
2958 defer rhs.unfreezeIfRegister(&self.register_manager);3083 defer dst_mcv.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 }
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);
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,
...@@ -3151,12 +3261,16 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -3151,12 +3261,16 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
3151 .compare_flags_unsigned => unreachable,3261 .compare_flags_unsigned => unreachable,
3152 .compare_flags_signed => unreachable,3262 .compare_flags_signed => unreachable,
3153 .ptr_stack_offset => unreachable,3263 .ptr_stack_offset => unreachable,
3264 .register_overflow_unsigned => unreachable,
3265 .register_overflow_signed => unreachable,
3154 .register => |dst_reg| {3266 .register => |dst_reg| {
3155 switch (src_mcv) {3267 switch (src_mcv) {
3156 .none => unreachable,3268 .none => unreachable,
3157 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),3269 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
3158 .dead, .unreach => unreachable,3270 .dead, .unreach => unreachable,
3159 .ptr_stack_offset => unreachable,3271 .ptr_stack_offset => unreachable,
3272 .register_overflow_unsigned => unreachable,
3273 .register_overflow_signed => unreachable,
3160 .register => |src_reg| {3274 .register => |src_reg| {
3161 // register, register3275 // register, register
3162 _ = try self.addInst(.{3276 _ = try self.addInst(.{
...@@ -3191,7 +3305,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -3191,7 +3305,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
3191 _ = try self.addInst(.{3305 _ = try self.addInst(.{
3192 .tag = .imul_complex,3306 .tag = .imul_complex,
3193 .ops = (Mir.Ops{3307 .ops = (Mir.Ops{
3194 .reg1 = dst_reg,3308 .reg1 = registerAlias(dst_reg, @intCast(u32, dst_ty.abiSize(self.target.*))),
3195 .reg2 = .rbp,3309 .reg2 = .rbp,
3196 .flags = 0b01,3310 .flags = 0b01,
3197 }).encode(),3311 }).encode(),
...@@ -3218,6 +3332,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -3218,6 +3332,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
3218 .undef => return self.genSetStack(dst_ty, off, .undef, .{}),3332 .undef => return self.genSetStack(dst_ty, off, .undef, .{}),
3219 .dead, .unreach => unreachable,3333 .dead, .unreach => unreachable,
3220 .ptr_stack_offset => unreachable,3334 .ptr_stack_offset => unreachable,
3335 .register_overflow_unsigned => unreachable,
3336 .register_overflow_signed => unreachable,
3221 .register => |src_reg| {3337 .register => |src_reg| {
3222 // copy dst to a register3338 // copy dst to a register
3223 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);3339 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
...@@ -3366,6 +3482,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3366,6 +3482,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3366 try self.genSetReg(arg_ty, reg, arg_mcv);3482 try self.genSetReg(arg_ty, reg, arg_mcv);
3367 },3483 },
3368 .stack_offset => |off| {3484 .stack_offset => |off| {
3485 // TODO rewrite using `genSetStack`
3369 try self.genSetStackArg(arg_ty, off, arg_mcv);3486 try self.genSetStackArg(arg_ty, off, arg_mcv);
3370 },3487 },
3371 .ptr_stack_offset => {3488 .ptr_stack_offset => {
...@@ -3380,6 +3497,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3380,6 +3497,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3380 .direct_load => unreachable,3497 .direct_load => unreachable,
3381 .compare_flags_signed => unreachable,3498 .compare_flags_signed => unreachable,
3382 .compare_flags_unsigned => unreachable,3499 .compare_flags_unsigned => unreachable,
3500 .register_overflow_signed => unreachable,
3501 .register_overflow_unsigned => unreachable,
3383 }3502 }
3384 }3503 }
33853504
...@@ -4610,6 +4729,9 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -4610,6 +4729,9 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
4610 .{ .dest_stack_base = .rsp },4729 .{ .dest_stack_base = .rsp },
4611 );4730 );
4612 },4731 },
4732 .register_overflow_unsigned,
4733 .register_overflow_signed,
4734 => return self.fail("TODO genSetStackArg for register with overflow bit", .{}),
4613 .compare_flags_unsigned,4735 .compare_flags_unsigned,
4614 .compare_flags_signed,4736 .compare_flags_signed,
4615 => {4737 => {
...@@ -4712,6 +4834,39 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl...@@ -4712,6 +4834,39 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
4712 ),4834 ),
4713 }4835 }
4714 },4836 },
4837 .register_overflow_unsigned,
4838 .register_overflow_signed,
4839 => |reg| {
4840 self.register_manager.freezeRegs(&.{reg});
4841 defer self.register_manager.unfreezeRegs(&.{reg});
4842
4843 const wrapped_ty = ty.structFieldType(0);
4844 try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{});
4845
4846 const overflow_bit_ty = ty.structFieldType(1);
4847 const overflow_bit_offset = ty.structFieldOffset(1, self.target.*);
4848 const tmp_reg = try self.register_manager.allocReg(null);
4849 const flags: u2 = switch (mcv) {
4850 .register_overflow_unsigned => 0b10,
4851 .register_overflow_signed => 0b00,
4852 else => unreachable,
4853 };
4854 _ = try self.addInst(.{
4855 .tag = .cond_set_byte_overflow,
4856 .ops = (Mir.Ops{
4857 .reg1 = tmp_reg.to8(),
4858 .flags = flags,
4859 }).encode(),
4860 .data = undefined,
4861 });
4862
4863 return self.genSetStack(
4864 overflow_bit_ty,
4865 stack_offset - @intCast(i32, overflow_bit_offset),
4866 .{ .register = tmp_reg.to8() },
4867 .{},
4868 );
4869 },
4715 .compare_flags_unsigned,4870 .compare_flags_unsigned,
4716 .compare_flags_signed,4871 .compare_flags_signed,
4717 => {4872 => {
...@@ -5170,6 +5325,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5170,6 +5325,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5170 const abi_size = @intCast(u32, ty.abiSize(self.target.*));5325 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
5171 switch (mcv) {5326 switch (mcv) {
5172 .dead => unreachable,5327 .dead => unreachable,
5328 .register_overflow_unsigned,
5329 .register_overflow_signed,
5330 => unreachable,
5173 .ptr_stack_offset => |off| {5331 .ptr_stack_offset => |off| {
5174 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {5332 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
5175 return self.fail("stack offset too large", .{});5333 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]