| ... | @@ -131,12 +131,18 @@ const MCValue = union(enum) { | ... | @@ -131,12 +131,18 @@ const MCValue = union(enum) { |
| 131 | stack_offset: u32, | 131 | stack_offset: u32, |
| 132 | /// The value is a pointer to one of the stack variables (payload is stack offset). | 132 | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 133 | ptr_stack_offset: u32, | 133 | ptr_stack_offset: u32, |
| 134 | /// The value is in the compare flags assuming an unsigned operation, | 134 | /// The value is in the specified CCR assuming an unsigned operation, |
| 135 | /// with this operator applied on top of it. | 135 | /// with the operator applied on top of it. |
| 136 | compare_flags_unsigned: math.CompareOperator, | 136 | compare_flags_unsigned: struct { |
| 137 | /// The value is in the compare flags assuming a signed operation, | 137 | cmp: math.CompareOperator, |
| 138 | /// with this operator applied on top of it. | 138 | ccr: Instruction.CCR, |
| 139 | compare_flags_signed: math.CompareOperator, | 139 | }, |
| | 140 | /// The value is in the specified CCR assuming an signed operation, |
| | 141 | /// with the operator applied on top of it. |
| | 142 | compare_flags_signed: struct { |
| | 143 | cmp: math.CompareOperator, |
| | 144 | ccr: Instruction.CCR, |
| | 145 | }, |
| 140 | | 146 | |
| 141 | fn isMemory(mcv: MCValue) bool { | 147 | fn isMemory(mcv: MCValue) bool { |
| 142 | return switch (mcv) { | 148 | return switch (mcv) { |
| ... | @@ -1086,8 +1092,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | ... | @@ -1086,8 +1092,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1086 | self.compare_flags_inst = inst; | 1092 | self.compare_flags_inst = inst; |
| 1087 | | 1093 | |
| 1088 | break :result switch (int_info.signedness) { | 1094 | break :result switch (int_info.signedness) { |
| 1089 | .signed => MCValue{ .compare_flags_signed = op }, | 1095 | .signed => MCValue{ .compare_flags_signed = .{ .cmp = op, .ccr = .xcc } }, |
| 1090 | .unsigned => MCValue{ .compare_flags_unsigned = op }, | 1096 | .unsigned => MCValue{ .compare_flags_unsigned = .{ .cmp = op, .ccr = .xcc } }, |
| 1091 | }; | 1097 | }; |
| 1092 | } else { | 1098 | } else { |
| 1093 | return self.fail("TODO SPARCv9 cmp for ints > 64 bits", .{}); | 1099 | return self.fail("TODO SPARCv9 cmp for ints > 64 bits", .{}); |
| ... | @@ -1113,16 +1119,20 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1113,16 +1119,20 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1113 | .tag = .bpcc, | 1119 | .tag = .bpcc, |
| 1114 | .data = .{ | 1120 | .data = .{ |
| 1115 | .branch_predict_int = .{ | 1121 | .branch_predict_int = .{ |
| 1116 | .ccr = .xcc, | 1122 | .ccr = switch (cond) { |
| | 1123 | .compare_flags_signed => |cmp_op| cmp_op.ccr, |
| | 1124 | .compare_flags_unsigned => |cmp_op| cmp_op.ccr, |
| | 1125 | else => unreachable, |
| | 1126 | }, |
| 1117 | .cond = switch (cond) { | 1127 | .cond = switch (cond) { |
| 1118 | .compare_flags_signed => |cmp_op| blk: { | 1128 | .compare_flags_signed => |cmp_op| blk: { |
| 1119 | // Here we map to the opposite condition because the jump is to the false branch. | 1129 | // Here we map to the opposite condition because the jump is to the false branch. |
| 1120 | const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op); | 1130 | const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op.cmp); |
| 1121 | break :blk condition.negate(); | 1131 | break :blk condition.negate(); |
| 1122 | }, | 1132 | }, |
| 1123 | .compare_flags_unsigned => |cmp_op| blk: { | 1133 | .compare_flags_unsigned => |cmp_op| blk: { |
| 1124 | // Here we map to the opposite condition because the jump is to the false branch. | 1134 | // Here we map to the opposite condition because the jump is to the false branch. |
| 1125 | const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op); | 1135 | const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op.cmp); |
| 1126 | break :blk condition.negate(); | 1136 | break :blk condition.negate(); |
| 1127 | }, | 1137 | }, |
| 1128 | else => unreachable, | 1138 | else => unreachable, |
| ... | @@ -2290,8 +2300,47 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -2290,8 +2300,47 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2290 | switch (mcv) { | 2300 | switch (mcv) { |
| 2291 | .dead => unreachable, | 2301 | .dead => unreachable, |
| 2292 | .unreach, .none => return, // Nothing to do. | 2302 | .unreach, .none => return, // Nothing to do. |
| 2293 | .compare_flags_signed => return self.fail("TODO: genSetReg for compare_flags_signed", .{}), | 2303 | .compare_flags_signed, |
| 2294 | .compare_flags_unsigned => return self.fail("TODO: genSetReg for compare_flags_unsigned", .{}), | 2304 | .compare_flags_unsigned, |
| | 2305 | => { |
| | 2306 | const condition = switch (mcv) { |
| | 2307 | .compare_flags_unsigned => |op| Instruction.ICondition.fromCompareOperatorUnsigned(op.cmp), |
| | 2308 | .compare_flags_signed => |op| Instruction.ICondition.fromCompareOperatorSigned(op.cmp), |
| | 2309 | else => unreachable, |
| | 2310 | }; |
| | 2311 | |
| | 2312 | const ccr = switch (mcv) { |
| | 2313 | .compare_flags_unsigned => |op| op.ccr, |
| | 2314 | .compare_flags_signed => |op| op.ccr, |
| | 2315 | else => unreachable, |
| | 2316 | }; |
| | 2317 | // TODO handle floating point CCRs |
| | 2318 | assert(ccr == .xcc or ccr == .icc); |
| | 2319 | |
| | 2320 | _ = try self.addInst(.{ |
| | 2321 | .tag = .mov, |
| | 2322 | .data = .{ |
| | 2323 | .arithmetic_2op = .{ |
| | 2324 | .is_imm = false, |
| | 2325 | .rs1 = reg, |
| | 2326 | .rs2_or_imm = .{ .rs2 = .g0 }, |
| | 2327 | }, |
| | 2328 | }, |
| | 2329 | }); |
| | 2330 | |
| | 2331 | _ = try self.addInst(.{ |
| | 2332 | .tag = .movcc, |
| | 2333 | .data = .{ |
| | 2334 | .conditional_move = .{ |
| | 2335 | .ccr = ccr, |
| | 2336 | .cond = .{ .icond = condition }, |
| | 2337 | .is_imm = true, |
| | 2338 | .rd = reg, |
| | 2339 | .rs2_or_imm = .{ .imm = 1 }, |
| | 2340 | }, |
| | 2341 | }, |
| | 2342 | }); |
| | 2343 | }, |
| 2295 | .undef => { | 2344 | .undef => { |
| 2296 | if (!self.wantSafety()) | 2345 | if (!self.wantSafety()) |
| 2297 | return; // The already existing value will do just fine. | 2346 | return; // The already existing value will do just fine. |
| ... | @@ -2644,7 +2693,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | ... | @@ -2644,7 +2693,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2644 | } }, | 2693 | } }, |
| 2645 | }); | 2694 | }); |
| 2646 | | 2695 | |
| 2647 | return MCValue{ .compare_flags_unsigned = .gt }; | 2696 | return MCValue{ .compare_flags_unsigned = .{ .cmp = .gt, .ccr = .xcc } }; |
| 2648 | } else { | 2697 | } else { |
| 2649 | return self.fail("TODO isErr for errors with size > 8", .{}); | 2698 | return self.fail("TODO isErr for errors with size > 8", .{}); |
| 2650 | } | 2699 | } |
| ... | @@ -2658,8 +2707,8 @@ fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | ... | @@ -2658,8 +2707,8 @@ fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2658 | const is_err_result = try self.isErr(ty, operand); | 2707 | const is_err_result = try self.isErr(ty, operand); |
| 2659 | switch (is_err_result) { | 2708 | switch (is_err_result) { |
| 2660 | .compare_flags_unsigned => |op| { | 2709 | .compare_flags_unsigned => |op| { |
| 2661 | assert(op == .gt); | 2710 | assert(op.cmp == .gt); |
| 2662 | return MCValue{ .compare_flags_unsigned = .lte }; | 2711 | return MCValue{ .compare_flags_unsigned = .{ .cmp = .gt, .ccr = op.ccr } }; |
| 2663 | }, | 2712 | }, |
| 2664 | .immediate => |imm| { | 2713 | .immediate => |imm| { |
| 2665 | assert(imm == 0); | 2714 | assert(imm == 0); |
| ... | @@ -3014,14 +3063,13 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { | ... | @@ -3014,14 +3063,13 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { |
| 3014 | fn spillCompareFlagsIfOccupied(self: *Self) !void { | 3063 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 3015 | if (self.compare_flags_inst) |inst_to_save| { | 3064 | if (self.compare_flags_inst) |inst_to_save| { |
| 3016 | const mcv = self.getResolvedInstValue(inst_to_save); | 3065 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 3017 | switch (mcv) { | 3066 | const new_mcv = switch (mcv) { |
| 3018 | .compare_flags_signed, | 3067 | .compare_flags_signed, |
| 3019 | .compare_flags_unsigned, | 3068 | .compare_flags_unsigned, |
| 3020 | => {}, | 3069 | => try self.allocRegOrMem(inst_to_save, true), |
| 3021 | else => unreachable, // mcv doesn't occupy the compare flags | 3070 | else => unreachable, // mcv doesn't occupy the compare flags |
| 3022 | } | 3071 | }; |
| 3023 | | 3072 | |
| 3024 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); | | |
| 3025 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); | 3073 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| 3026 | log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); | 3074 | log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); |
| 3027 | | 3075 | |