| ... | ... | @@ -93,8 +93,8 @@ register_manager: RegisterManager = .{}, |
| 93 | 93 | /// Maps offset to what is stored there. |
| 94 | 94 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 95 | 95 | |
| 96 | | /// Tracks the current instruction allocated to the compare flags |
| 97 | | compare_flags_inst: ?Air.Inst.Index = null, |
| 96 | /// Tracks the current instruction allocated to the condition flags |
| 97 | condition_flags_inst: ?Air.Inst.Index = null, |
| 98 | 98 | |
| 99 | 99 | /// Offset from the stack base, representing the end of the stack frame. |
| 100 | 100 | max_end_stack: u32 = 0, |
| ... | ... | @@ -141,16 +141,11 @@ const MCValue = union(enum) { |
| 141 | 141 | stack_offset: u32, |
| 142 | 142 | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 143 | 143 | ptr_stack_offset: u32, |
| 144 | | /// The value is in the specified CCR assuming an unsigned operation, |
| 145 | | /// with the operator applied on top of it. |
| 146 | | compare_flags_unsigned: struct { |
| 147 | | cmp: math.CompareOperator, |
| 148 | | ccr: Instruction.CCR, |
| 149 | | }, |
| 150 | | /// The value is in the specified CCR assuming an signed operation, |
| 151 | | /// with the operator applied on top of it. |
| 152 | | compare_flags_signed: struct { |
| 153 | | cmp: math.CompareOperator, |
| 144 | /// The value is in the specified CCR. The value is 1 (if |
| 145 | /// the type is u1) or true (if the type in bool) iff the |
| 146 | /// specified condition is true. |
| 147 | condition_flags: struct { |
| 148 | cond: Instruction.Condition, |
| 154 | 149 | ccr: Instruction.CCR, |
| 155 | 150 | }, |
| 156 | 151 | |
| ... | ... | @@ -738,8 +733,8 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 738 | 733 | else => unreachable, |
| 739 | 734 | }; |
| 740 | 735 | |
| 741 | | try self.spillCompareFlagsIfOccupied(); |
| 742 | | self.compare_flags_inst = inst; |
| 736 | try self.spillConditionFlagsIfOccupied(); |
| 737 | self.condition_flags_inst = inst; |
| 743 | 738 | |
| 744 | 739 | const dest = blk: { |
| 745 | 740 | if (rhs_immediate_ok) { |
| ... | ... | @@ -1072,7 +1067,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1072 | 1067 | |
| 1073 | 1068 | // CCR is volatile across function calls |
| 1074 | 1069 | // (SCD 2.4.1, page 3P-10) |
| 1075 | | try self.spillCompareFlagsIfOccupied(); |
| 1070 | try self.spillConditionFlagsIfOccupied(); |
| 1076 | 1071 | |
| 1077 | 1072 | for (info.args) |mc_arg, arg_i| { |
| 1078 | 1073 | const arg = args[arg_i]; |
| ... | ... | @@ -1208,12 +1203,18 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1208 | 1203 | .inst = inst, |
| 1209 | 1204 | }); |
| 1210 | 1205 | |
| 1211 | | try self.spillCompareFlagsIfOccupied(); |
| 1212 | | self.compare_flags_inst = inst; |
| 1206 | try self.spillConditionFlagsIfOccupied(); |
| 1207 | self.condition_flags_inst = inst; |
| 1213 | 1208 | |
| 1214 | 1209 | break :result switch (int_info.signedness) { |
| 1215 | | .signed => MCValue{ .compare_flags_signed = .{ .cmp = op, .ccr = .xcc } }, |
| 1216 | | .unsigned => MCValue{ .compare_flags_unsigned = .{ .cmp = op, .ccr = .xcc } }, |
| 1210 | .signed => MCValue{ .condition_flags = .{ |
| 1211 | .cond = .{ .icond = Instruction.ICondition.fromCompareOperatorSigned(op) }, |
| 1212 | .ccr = .xcc, |
| 1213 | } }, |
| 1214 | .unsigned => MCValue{ .condition_flags = .{ |
| 1215 | .cond = .{ .icond = Instruction.ICondition.fromCompareOperatorUnsigned(op) }, |
| 1216 | .ccr = .xcc, |
| 1217 | } }, |
| 1217 | 1218 | }; |
| 1218 | 1219 | } else { |
| 1219 | 1220 | return self.fail("TODO SPARCv9 cmp for ints > 64 bits", .{}); |
| ... | ... | @@ -1224,7 +1225,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1224 | 1225 | |
| 1225 | 1226 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1226 | 1227 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 1227 | | const cond = try self.resolveInst(pl_op.operand); |
| 1228 | const condition = try self.resolveInst(pl_op.operand); |
| 1228 | 1229 | const extra = self.air.extraData(Air.CondBr, pl_op.payload); |
| 1229 | 1230 | const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; |
| 1230 | 1231 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| ... | ... | @@ -1232,39 +1233,22 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1232 | 1233 | |
| 1233 | 1234 | // Here we either emit a BPcc for branching on CCR content, |
| 1234 | 1235 | // or emit a BPr to branch on register content. |
| 1235 | | const reloc: Mir.Inst.Index = switch (cond) { |
| 1236 | | .compare_flags_signed, |
| 1237 | | .compare_flags_unsigned, |
| 1238 | | => try self.addInst(.{ |
| 1236 | const reloc: Mir.Inst.Index = switch (condition) { |
| 1237 | .condition_flags => |flags| try self.addInst(.{ |
| 1239 | 1238 | .tag = .bpcc, |
| 1240 | 1239 | .data = .{ |
| 1241 | 1240 | .branch_predict_int = .{ |
| 1242 | | .ccr = switch (cond) { |
| 1243 | | .compare_flags_signed => |cmp_op| cmp_op.ccr, |
| 1244 | | .compare_flags_unsigned => |cmp_op| cmp_op.ccr, |
| 1245 | | else => unreachable, |
| 1246 | | }, |
| 1247 | | .cond = switch (cond) { |
| 1248 | | .compare_flags_signed => |cmp_op| blk: { |
| 1249 | | // Here we map to the opposite condition because the jump is to the false branch. |
| 1250 | | const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op.cmp); |
| 1251 | | break :blk condition.negate(); |
| 1252 | | }, |
| 1253 | | .compare_flags_unsigned => |cmp_op| blk: { |
| 1254 | | // Here we map to the opposite condition because the jump is to the false branch. |
| 1255 | | const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op.cmp); |
| 1256 | | break :blk condition.negate(); |
| 1257 | | }, |
| 1258 | | else => unreachable, |
| 1259 | | }, |
| 1241 | .ccr = flags.ccr, |
| 1242 | // Here we map to the opposite condition because the jump is to the false branch. |
| 1243 | .cond = flags.cond.icond.negate(), |
| 1260 | 1244 | .inst = undefined, // Will be filled by performReloc |
| 1261 | 1245 | }, |
| 1262 | 1246 | }, |
| 1263 | 1247 | }), |
| 1264 | 1248 | else => blk: { |
| 1265 | | const reg = switch (cond) { |
| 1249 | const reg = switch (condition) { |
| 1266 | 1250 | .register => |r| r, |
| 1267 | | else => try self.copyToTmpRegister(Type.bool, cond), |
| 1251 | else => try self.copyToTmpRegister(Type.bool, condition), |
| 1268 | 1252 | }; |
| 1269 | 1253 | |
| 1270 | 1254 | break :blk try self.addInst(.{ |
| ... | ... | @@ -1305,7 +1289,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1305 | 1289 | var parent_stack = try self.stack.clone(self.gpa); |
| 1306 | 1290 | defer parent_stack.deinit(self.gpa); |
| 1307 | 1291 | const parent_registers = self.register_manager.registers; |
| 1308 | | const parent_compare_flags_inst = self.compare_flags_inst; |
| 1292 | const parent_condition_flags_inst = self.condition_flags_inst; |
| 1309 | 1293 | |
| 1310 | 1294 | try self.branch_stack.append(.{}); |
| 1311 | 1295 | errdefer { |
| ... | ... | @@ -1324,7 +1308,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1324 | 1308 | defer saved_then_branch.deinit(self.gpa); |
| 1325 | 1309 | |
| 1326 | 1310 | self.register_manager.registers = parent_registers; |
| 1327 | | self.compare_flags_inst = parent_compare_flags_inst; |
| 1311 | self.condition_flags_inst = parent_condition_flags_inst; |
| 1328 | 1312 | |
| 1329 | 1313 | self.stack.deinit(self.gpa); |
| 1330 | 1314 | self.stack = parent_stack; |
| ... | ... | @@ -1537,37 +1521,13 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1537 | 1521 | switch (operand) { |
| 1538 | 1522 | .dead => unreachable, |
| 1539 | 1523 | .unreach => unreachable, |
| 1540 | | .compare_flags_unsigned => |op| { |
| 1541 | | const r = MCValue{ |
| 1542 | | .compare_flags_unsigned = .{ |
| 1543 | | .cmp = switch (op.cmp) { |
| 1544 | | .gte => .lt, |
| 1545 | | .gt => .lte, |
| 1546 | | .neq => .eq, |
| 1547 | | .lt => .gte, |
| 1548 | | .lte => .gt, |
| 1549 | | .eq => .neq, |
| 1550 | | }, |
| 1524 | .condition_flags => |op| { |
| 1525 | break :result MCValue{ |
| 1526 | .condition_flags = .{ |
| 1527 | .cond = op.cond.negate(), |
| 1551 | 1528 | .ccr = op.ccr, |
| 1552 | 1529 | }, |
| 1553 | 1530 | }; |
| 1554 | | break :result r; |
| 1555 | | }, |
| 1556 | | .compare_flags_signed => |op| { |
| 1557 | | const r = MCValue{ |
| 1558 | | .compare_flags_signed = .{ |
| 1559 | | .cmp = switch (op.cmp) { |
| 1560 | | .gte => .lt, |
| 1561 | | .gt => .lte, |
| 1562 | | .neq => .eq, |
| 1563 | | .lt => .gte, |
| 1564 | | .lte => .gt, |
| 1565 | | .eq => .neq, |
| 1566 | | }, |
| 1567 | | .ccr = op.ccr, |
| 1568 | | }, |
| 1569 | | }; |
| 1570 | | break :result r; |
| 1571 | 1531 | }, |
| 1572 | 1532 | else => { |
| 1573 | 1533 | switch (operand_ty.zigTypeTag()) { |
| ... | ... | @@ -2667,20 +2627,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2667 | 2627 | switch (mcv) { |
| 2668 | 2628 | .dead => unreachable, |
| 2669 | 2629 | .unreach, .none => return, // Nothing to do. |
| 2670 | | .compare_flags_signed, |
| 2671 | | .compare_flags_unsigned, |
| 2672 | | => { |
| 2673 | | const condition = switch (mcv) { |
| 2674 | | .compare_flags_unsigned => |op| Instruction.ICondition.fromCompareOperatorUnsigned(op.cmp), |
| 2675 | | .compare_flags_signed => |op| Instruction.ICondition.fromCompareOperatorSigned(op.cmp), |
| 2676 | | else => unreachable, |
| 2677 | | }; |
| 2630 | .condition_flags => |op| { |
| 2631 | const condition = op.cond; |
| 2632 | const ccr = op.ccr; |
| 2678 | 2633 | |
| 2679 | | const ccr = switch (mcv) { |
| 2680 | | .compare_flags_unsigned => |op| op.ccr, |
| 2681 | | .compare_flags_signed => |op| op.ccr, |
| 2682 | | else => unreachable, |
| 2683 | | }; |
| 2684 | 2634 | // TODO handle floating point CCRs |
| 2685 | 2635 | assert(ccr == .xcc or ccr == .icc); |
| 2686 | 2636 | |
| ... | ... | @@ -2700,7 +2650,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2700 | 2650 | .data = .{ |
| 2701 | 2651 | .conditional_move = .{ |
| 2702 | 2652 | .ccr = ccr, |
| 2703 | | .cond = .{ .icond = condition }, |
| 2653 | .cond = condition, |
| 2704 | 2654 | .is_imm = true, |
| 2705 | 2655 | .rd = reg, |
| 2706 | 2656 | .rs2_or_imm = .{ .imm = 1 }, |
| ... | ... | @@ -2874,8 +2824,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2874 | 2824 | else => return self.fail("TODO implement memset", .{}), |
| 2875 | 2825 | } |
| 2876 | 2826 | }, |
| 2877 | | .compare_flags_unsigned, |
| 2878 | | .compare_flags_signed, |
| 2827 | .condition_flags, |
| 2879 | 2828 | .immediate, |
| 2880 | 2829 | .ptr_stack_offset, |
| 2881 | 2830 | => { |
| ... | ... | @@ -3103,7 +3052,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3103 | 3052 | } }, |
| 3104 | 3053 | }); |
| 3105 | 3054 | |
| 3106 | | return MCValue{ .compare_flags_unsigned = .{ .cmp = .gt, .ccr = .xcc } }; |
| 3055 | return MCValue{ .condition_flags = .{ .cond = .{ .icond = .gu }, .ccr = .xcc } }; |
| 3107 | 3056 | } else { |
| 3108 | 3057 | return self.fail("TODO isErr for errors with size > 8", .{}); |
| 3109 | 3058 | } |
| ... | ... | @@ -3116,9 +3065,8 @@ fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3116 | 3065 | // Call isErr, then negate the result. |
| 3117 | 3066 | const is_err_result = try self.isErr(ty, operand); |
| 3118 | 3067 | switch (is_err_result) { |
| 3119 | | .compare_flags_unsigned => |op| { |
| 3120 | | assert(op.cmp == .gt); |
| 3121 | | return MCValue{ .compare_flags_unsigned = .{ .cmp = .lte, .ccr = op.ccr } }; |
| 3068 | .condition_flags => |op| { |
| 3069 | return MCValue{ .condition_flags = .{ .cond = op.cond.negate(), .ccr = op.ccr } }; |
| 3122 | 3070 | }, |
| 3123 | 3071 | .immediate => |imm| { |
| 3124 | 3072 | assert(imm == 0); |
| ... | ... | @@ -3168,8 +3116,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 3168 | 3116 | .undef => unreachable, |
| 3169 | 3117 | .unreach => unreachable, |
| 3170 | 3118 | .dead => unreachable, |
| 3171 | | .compare_flags_unsigned, |
| 3172 | | .compare_flags_signed, |
| 3119 | .condition_flags, |
| 3173 | 3120 | .register_with_overflow, |
| 3174 | 3121 | => unreachable, // cannot hold an address |
| 3175 | 3122 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| ... | ... | @@ -3181,7 +3128,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 3181 | 3128 | switch (dst_mcv) { |
| 3182 | 3129 | .dead => unreachable, |
| 3183 | 3130 | .undef => unreachable, |
| 3184 | | .compare_flags_signed, .compare_flags_unsigned => unreachable, |
| 3131 | .condition_flags => unreachable, |
| 3185 | 3132 | .register => |dst_reg| { |
| 3186 | 3133 | try self.genLoad(dst_reg, addr_reg, i13, 0, elem_size); |
| 3187 | 3134 | }, |
| ... | ... | @@ -3277,10 +3224,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 3277 | 3224 | }, |
| 3278 | 3225 | .register_with_overflow => |rwo| { |
| 3279 | 3226 | self.register_manager.freeReg(rwo.reg); |
| 3280 | | self.compare_flags_inst = null; |
| 3227 | self.condition_flags_inst = null; |
| 3281 | 3228 | }, |
| 3282 | | .compare_flags_signed, .compare_flags_unsigned => { |
| 3283 | | self.compare_flags_inst = null; |
| 3229 | .condition_flags => { |
| 3230 | self.condition_flags_inst = null; |
| 3284 | 3231 | }, |
| 3285 | 3232 | else => {}, // TODO process stack allocation death |
| 3286 | 3233 | } |
| ... | ... | @@ -3474,15 +3421,13 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { |
| 3474 | 3421 | } |
| 3475 | 3422 | } |
| 3476 | 3423 | |
| 3477 | | /// Save the current instruction stored in the compare flags if |
| 3424 | /// Save the current instruction stored in the condition flags if |
| 3478 | 3425 | /// occupied |
| 3479 | | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 3480 | | if (self.compare_flags_inst) |inst_to_save| { |
| 3426 | fn spillConditionFlagsIfOccupied(self: *Self) !void { |
| 3427 | if (self.condition_flags_inst) |inst_to_save| { |
| 3481 | 3428 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 3482 | 3429 | const new_mcv = switch (mcv) { |
| 3483 | | .compare_flags_signed, |
| 3484 | | .compare_flags_unsigned, |
| 3485 | | => try self.allocRegOrMem(inst_to_save, true), |
| 3430 | .condition_flags => try self.allocRegOrMem(inst_to_save, true), |
| 3486 | 3431 | .register_with_overflow => try self.allocRegOrMem(inst_to_save, false), |
| 3487 | 3432 | else => unreachable, // mcv doesn't occupy the compare flags |
| 3488 | 3433 | }; |
| ... | ... | @@ -3493,7 +3438,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 3493 | 3438 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 3494 | 3439 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); |
| 3495 | 3440 | |
| 3496 | | self.compare_flags_inst = null; |
| 3441 | self.condition_flags_inst = null; |
| 3497 | 3442 | |
| 3498 | 3443 | // TODO consolidate with register manager and spillInstruction |
| 3499 | 3444 | // this call should really belong in the register manager! |
| ... | ... | @@ -3522,8 +3467,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3522 | 3467 | .undef => unreachable, |
| 3523 | 3468 | .unreach => unreachable, |
| 3524 | 3469 | .dead => unreachable, |
| 3525 | | .compare_flags_unsigned, |
| 3526 | | .compare_flags_signed, |
| 3470 | .condition_flags, |
| 3527 | 3471 | .register_with_overflow, |
| 3528 | 3472 | => unreachable, // cannot hold an address |
| 3529 | 3473 | .immediate => |imm| { |