authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-02 20:33:55+07:00
committergravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-16 22:47:53+07:00
log2dc2ab091e423fe087a6bc12ea6ada0214fd106b
tree0548140ac06422868c805b3f8e73ada1bf30ca00
parentfd781195de54b58a5f103f86c5f0784454a53439

stage2: sparc64: Implement airCondBr from flags register


1 files changed, 213 insertions(+), 7 deletions(-)

src/arch/sparc64/CodeGen.zig+213-7
...@@ -28,6 +28,7 @@ const build_options = @import("build_options");...@@ -28,6 +28,7 @@ const build_options = @import("build_options");
2828
29const bits = @import("bits.zig");29const bits = @import("bits.zig");
30const abi = @import("abi.zig");30const abi = @import("abi.zig");
31const Instruction = bits.Instruction;
31const Register = bits.Register;32const Register = bits.Register;
3233
33const Self = @This();34const Self = @This();
...@@ -90,6 +91,9 @@ register_manager: RegisterManager = .{},...@@ -90,6 +91,9 @@ register_manager: RegisterManager = .{},
90/// Maps offset to what is stored there.91/// Maps offset to what is stored there.
91stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},92stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
9293
94/// Tracks the current instruction allocated to the compare flags
95compare_flags_inst: ?Air.Inst.Index = null,
96
93/// Offset from the stack base, representing the end of the stack frame.97/// Offset from the stack base, representing the end of the stack frame.
94max_end_stack: u32 = 0,98max_end_stack: u32 = 0,
95/// Represents the current end stack offset. If there is no existing slot99/// Represents the current end stack offset. If there is no existing slot
...@@ -373,18 +377,31 @@ fn gen(self: *Self) !void {...@@ -373,18 +377,31 @@ fn gen(self: *Self) !void {
373377
374 // exitlude jumps378 // exitlude jumps
375 if (self.exitlude_jump_relocs.items.len > 0 and379 if (self.exitlude_jump_relocs.items.len > 0 and
376 self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2)380 self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 3)
377 {381 {
378 // If the last Mir instruction (apart from the382 // If the last Mir instruction (apart from the
379 // dbg_epilogue_begin) is the last exitlude jump383 // dbg_epilogue_begin) is the last exitlude jump
380 // relocation (which would just jump one instruction384 // relocation (which would just jump two instructions
381 // further), it can be safely removed385 // further), it can be safely removed
382 self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop());386 const index = self.exitlude_jump_relocs.pop();
387
388 // First, remove the delay slot, then remove
389 // the branch instruction itself.
390 self.mir_instructions.orderedRemove(index + 1);
391 self.mir_instructions.orderedRemove(index);
383 }392 }
384393
385 for (self.exitlude_jump_relocs.items) |jmp_reloc| {394 for (self.exitlude_jump_relocs.items) |jmp_reloc| {
386 _ = jmp_reloc;395 self.mir_instructions.set(jmp_reloc, .{
387 return self.fail("TODO add branches in sparc64", .{});396 .tag = .bpcc,
397 .data = .{
398 .branch_predict_int = .{
399 .ccr = .xcc,
400 .cond = .al,
401 .inst = @intCast(u32, self.mir_instructions.len),
402 },
403 },
404 });
388 }405 }
389406
390 // Backpatch stack offset407 // Backpatch stack offset
...@@ -531,7 +548,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -531,7 +548,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
531 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),548 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),
532 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),549 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),
533 .fence => @panic("TODO try self.airFence()"),550 .fence => @panic("TODO try self.airFence()"),
534 .cond_br => @panic("TODO try self.airCondBr(inst)"),551 .cond_br => try self.airCondBr(inst),
535 .dbg_stmt => try self.airDbgStmt(inst),552 .dbg_stmt => try self.airDbgStmt(inst),
536 .fptrunc => @panic("TODO try self.airFptrunc(inst)"),553 .fptrunc => @panic("TODO try self.airFptrunc(inst)"),
537 .fpext => @panic("TODO try self.airFpext(inst)"),554 .fpext => @panic("TODO try self.airFpext(inst)"),
...@@ -968,6 +985,188 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -968,6 +985,188 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
968 @panic("TODO handle return value with BigTomb");985 @panic("TODO handle return value with BigTomb");
969}986}
970987
988fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
989 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
990 const cond = try self.resolveInst(pl_op.operand);
991 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
992 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
993 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
994 const liveness_condbr = self.liveness.getCondBr(inst);
995
996 // Here we either emit a BPcc for branching on CCR content,
997 // or emit a BPr to branch on register content.
998 const reloc: Mir.Inst.Index = switch (cond) {
999 .compare_flags_signed,
1000 .compare_flags_unsigned,
1001 => try self.addInst(.{
1002 .tag = .bpcc,
1003 .data = .{
1004 .branch_predict_int = .{
1005 .ccr = .xcc,
1006 .cond = switch (cond) {
1007 .compare_flags_signed => |cmp_op| blk: {
1008 // Here we map to the opposite condition because the jump is to the false branch.
1009 const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op);
1010 break :blk condition.negate();
1011 },
1012 .compare_flags_unsigned => |cmp_op| blk: {
1013 // Here we map to the opposite condition because the jump is to the false branch.
1014 const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op);
1015 break :blk condition.negate();
1016 },
1017 else => unreachable,
1018 },
1019 .inst = undefined, // Will be filled by performReloc
1020 },
1021 },
1022 }),
1023 else => return self.fail("TODO branch on register content (BPr)", .{}),
1024 };
1025
1026 // Regardless of the branch type that's emitted, we need to reserve
1027 // a space for the delay slot.
1028 // TODO Find a way to fill this delay slot
1029 _ = try self.addInst(.{
1030 .tag = .nop,
1031 .data = .{ .nop = {} },
1032 });
1033
1034 // If the condition dies here in this condbr instruction, process
1035 // that death now instead of later as this has an effect on
1036 // whether it needs to be spilled in the branches
1037 if (self.liveness.operandDies(inst, 0)) {
1038 const op_int = @enumToInt(pl_op.operand);
1039 if (op_int >= Air.Inst.Ref.typed_value_map.len) {
1040 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
1041 self.processDeath(op_index);
1042 }
1043 }
1044
1045 // Capture the state of register and stack allocation state so that we can revert to it.
1046 const parent_next_stack_offset = self.next_stack_offset;
1047 const parent_free_registers = self.register_manager.free_registers;
1048 var parent_stack = try self.stack.clone(self.gpa);
1049 defer parent_stack.deinit(self.gpa);
1050 const parent_registers = self.register_manager.registers;
1051 const parent_compare_flags_inst = self.compare_flags_inst;
1052
1053 try self.branch_stack.append(.{});
1054 errdefer {
1055 _ = self.branch_stack.pop();
1056 }
1057
1058 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
1059 for (liveness_condbr.then_deaths) |operand| {
1060 self.processDeath(operand);
1061 }
1062 try self.genBody(then_body);
1063
1064 // Revert to the previous register and stack allocation state.
1065
1066 var saved_then_branch = self.branch_stack.pop();
1067 defer saved_then_branch.deinit(self.gpa);
1068
1069 self.register_manager.registers = parent_registers;
1070 self.compare_flags_inst = parent_compare_flags_inst;
1071
1072 self.stack.deinit(self.gpa);
1073 self.stack = parent_stack;
1074 parent_stack = .{};
1075
1076 self.next_stack_offset = parent_next_stack_offset;
1077 self.register_manager.free_registers = parent_free_registers;
1078
1079 try self.performReloc(reloc);
1080 const else_branch = self.branch_stack.addOneAssumeCapacity();
1081 else_branch.* = .{};
1082
1083 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
1084 for (liveness_condbr.else_deaths) |operand| {
1085 self.processDeath(operand);
1086 }
1087 try self.genBody(else_body);
1088
1089 // At this point, each branch will possibly have conflicting values for where
1090 // each instruction is stored. They agree, however, on which instructions are alive/dead.
1091 // We use the first ("then") branch as canonical, and here emit
1092 // instructions into the second ("else") branch to make it conform.
1093 // We continue respect the data structure semantic guarantees of the else_branch so
1094 // that we can use all the code emitting abstractions. This is why at the bottom we
1095 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
1096 // rather than assigning it.
1097 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
1098 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
1099
1100 const else_slice = else_branch.inst_table.entries.slice();
1101 const else_keys = else_slice.items(.key);
1102 const else_values = else_slice.items(.value);
1103 for (else_keys) |else_key, else_idx| {
1104 const else_value = else_values[else_idx];
1105 const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: {
1106 // The instruction's MCValue is overridden in both branches.
1107 parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value);
1108 if (else_value == .dead) {
1109 assert(then_entry.value == .dead);
1110 continue;
1111 }
1112 break :blk then_entry.value;
1113 } else blk: {
1114 if (else_value == .dead)
1115 continue;
1116 // The instruction is only overridden in the else branch.
1117 var i: usize = self.branch_stack.items.len - 2;
1118 while (true) {
1119 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
1120 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
1121 assert(mcv != .dead);
1122 break :blk mcv;
1123 }
1124 }
1125 };
1126 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv });
1127 // TODO make sure the destination stack offset / register does not already have something
1128 // going on there.
1129 try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value);
1130 // TODO track the new register / stack allocation
1131 }
1132 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
1133 const then_slice = saved_then_branch.inst_table.entries.slice();
1134 const then_keys = then_slice.items(.key);
1135 const then_values = then_slice.items(.value);
1136 for (then_keys) |then_key, then_idx| {
1137 const then_value = then_values[then_idx];
1138 // We already deleted the items from this table that matched the else_branch.
1139 // So these are all instructions that are only overridden in the then branch.
1140 parent_branch.inst_table.putAssumeCapacity(then_key, then_value);
1141 if (then_value == .dead)
1142 continue;
1143 const parent_mcv = blk: {
1144 var i: usize = self.branch_stack.items.len - 2;
1145 while (true) {
1146 i -= 1;
1147 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {
1148 assert(mcv != .dead);
1149 break :blk mcv;
1150 }
1151 }
1152 };
1153 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value });
1154 // TODO make sure the destination stack offset / register does not already have something
1155 // going on there.
1156 try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value);
1157 // TODO track the new register / stack allocation
1158 }
1159
1160 {
1161 var item = self.branch_stack.pop();
1162 item.deinit(self.gpa);
1163 }
1164
1165 // We already took care of pl_op.operand earlier, so we're going
1166 // to pass .none here
1167 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
1168}
1169
971fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {1170fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
972 // TODO emit debug info lexical block1171 // TODO emit debug info lexical block
973 return self.finishAir(inst, .dead, .{ .none, .none, .none });1172 return self.finishAir(inst, .dead, .{ .none, .none, .none });
...@@ -1798,11 +1997,18 @@ fn ret(self: *Self, mcv: MCValue) !void {...@@ -1798,11 +1997,18 @@ fn ret(self: *Self, mcv: MCValue) !void {
1798 const ret_ty = self.fn_type.fnReturnType();1997 const ret_ty = self.fn_type.fnReturnType();
1799 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);1998 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
18001999
1801 // Just add space for an instruction, patch this later2000 // Just add space for a branch instruction, patch this later
1802 const index = try self.addInst(.{2001 const index = try self.addInst(.{
1803 .tag = .nop,2002 .tag = .nop,
1804 .data = .{ .nop = {} },2003 .data = .{ .nop = {} },
1805 });2004 });
2005
2006 // Reserve space for the delay slot too
2007 // TODO find out a way to fill this
2008 _ = try self.addInst(.{
2009 .tag = .nop,
2010 .data = .{ .nop = {} },
2011 });
1806 try self.exitlude_jump_relocs.append(self.gpa, index);2012 try self.exitlude_jump_relocs.append(self.gpa, index);
1807}2013}
18082014