authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-26 20:20:48+07:00
committergravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-06-06 20:34:53+07:00
log093332c02ed828c6c24d6c7e113ae2804ba7e6f3
tree3012fb6b684e016a778c24c280be07f5af3841e9
parent38aa431e03b19897a7c3c8505f66f4305d8c29b2

stage2: sparc64: Implement condition code spilling


3 files changed, 90 insertions(+), 20 deletions(-)

src/arch/sparc64/CodeGen.zig+68-20
...@@ -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 },
140146
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;
10871093
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 });
26462695
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 {
3014fn spillCompareFlagsIfOccupied(self: *Self) !void {3063fn 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 flags3070 else => unreachable, // mcv doesn't occupy the compare flags
3022 }3071 };
30233072
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 });
30273075
src/arch/sparc64/Emit.zig+2
...@@ -94,6 +94,8 @@ pub fn emitMir(...@@ -94,6 +94,8 @@ pub fn emitMir(
9494
95 .@"or" => try emit.mirArithmetic3Op(inst),95 .@"or" => try emit.mirArithmetic3Op(inst),
9696
97 .movcc => @panic("TODO implement sparc64 movcc"),
98
97 .mulx => try emit.mirArithmetic3Op(inst),99 .mulx => try emit.mirArithmetic3Op(inst),
98100
99 .nop => try emit.mirNop(),101 .nop => try emit.mirNop(),
src/arch/sparc64/Mir.zig+20
...@@ -74,6 +74,10 @@ pub const Inst = struct {...@@ -74,6 +74,10 @@ pub const Inst = struct {
74 // TODO add other operations.74 // TODO add other operations.
75 @"or",75 @"or",
7676
77 /// A.35 Move Integer Register on Condition (MOVcc)
78 /// This uses the conditional_move field.
79 movcc,
80
77 /// A.37 Multiply and Divide (64-bit)81 /// A.37 Multiply and Divide (64-bit)
78 /// This uses the arithmetic_3op field.82 /// This uses the arithmetic_3op field.
79 // TODO add other operations.83 // TODO add other operations.
...@@ -216,6 +220,22 @@ pub const Inst = struct {...@@ -216,6 +220,22 @@ pub const Inst = struct {
216 inst: Index,220 inst: Index,
217 },221 },
218222
223 /// Conditional move.
224 /// if is_imm true then it uses the imm field of rs2_or_imm,
225 /// otherwise it uses rs2 field.
226 ///
227 /// Used by e.g. movcc
228 conditional_move: struct {
229 is_imm: bool,
230 ccr: Instruction.CCR,
231 cond: Instruction.Condition,
232 rd: Register,
233 rs2_or_imm: union {
234 rs2: Register,
235 imm: i11,
236 },
237 },
238
219 /// No additional data239 /// No additional data
220 ///240 ///
221 /// Used by e.g. flushw241 /// Used by e.g. flushw