authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-06-02 20:19:18+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-06-02 20:19:18+02:00
log4fdacca51231668b72bf302ce97335d2a8d93128
tree0a9f71dbe101c39adac1a710a2daa2b0201a7599
parente498fb155051f548071da1a13098b8793f527275
signaturelock-open Commit is signed but in an unrecognized format.

stage2 ARM: rework cmp in preparation for switch


1 files changed, 239 insertions(+), 93 deletions(-)

src/arch/arm/CodeGen.zig+239-93
......@@ -813,6 +813,17 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
813813 self.register_manager.getRegAssumeFree(reg, inst);
814814 }
815815 },
816 .register_c_flag,
817 .register_v_flag,
818 => |reg| {
819 if (self.register_manager.isRegFree(reg)) {
820 self.register_manager.getRegAssumeFree(reg, inst);
821 }
822 self.cpsr_flags_inst = inst;
823 },
824 .cpsr_flags => {
825 self.cpsr_flags_inst = inst;
826 },
816827 else => {},
817828 }
818829 }
......@@ -2462,56 +2473,28 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
24622473 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
24632474 defer self.register_manager.unlockReg(reg_lock);
24642475
2465 switch (index) {
2466 0 => {
2467 // get wrapped value: return register
2468 break :result MCValue{ .register = reg };
2469 },
2470 1 => {
2471 // get overflow bit: return C or V flag
2472 if (self.liveness.operandDies(inst, 0)) {
2473 self.cpsr_flags_inst = inst;
2474
2475 const cond: Condition = switch (mcv) {
2476 .register_c_flag => .cs,
2477 .register_v_flag => .vs,
2478 else => unreachable,
2479 };
2476 const field: MCValue = switch (index) {
2477 // get wrapped value: return register
2478 0 => MCValue{ .register = reg },
24802479
2481 break :result MCValue{ .cpsr_flags = cond };
2482 } else {
2483 const dest_reg = try self.register_manager.allocReg(null, gp);
2480 // get overflow bit: return C or V flag
2481 1 => MCValue{ .cpsr_flags = switch (mcv) {
2482 .register_c_flag => .cs,
2483 .register_v_flag => .vs,
2484 else => unreachable,
2485 } },
24842486
2485 // mov reg, #0
2486 _ = try self.addInst(.{
2487 .tag = .mov,
2488 .data = .{ .rr_op = .{
2489 .rd = dest_reg,
2490 .rn = .r0,
2491 .op = Instruction.Operand.fromU32(0).?,
2492 } },
2493 });
2487 else => unreachable,
2488 };
24942489
2495 // C flag: movcs reg, #1
2496 // V flag: movvs reg, #1
2497 _ = try self.addInst(.{
2498 .tag = .mov,
2499 .cond = switch (mcv) {
2500 .register_c_flag => .cs,
2501 .register_v_flag => .vs,
2502 else => unreachable,
2503 },
2504 .data = .{ .rr_op = .{
2505 .rd = dest_reg,
2506 .rn = .r0,
2507 .op = Instruction.Operand.fromU32(1).?,
2508 } },
2509 });
2490 if (self.liveness.operandDies(inst, 0)) {
2491 break :result field;
2492 } else {
2493 // Copy to new register
2494 const dest_reg = try self.register_manager.allocReg(null, gp);
2495 try self.genSetReg(struct_ty.structFieldType(index), dest_reg, field);
25102496
2511 break :result MCValue{ .register = dest_reg };
2512 }
2513 },
2514 else => unreachable,
2497 break :result MCValue{ .register = dest_reg };
25152498 }
25162499 },
25172500 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
......@@ -3570,53 +3553,89 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
35703553
35713554fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
35723555 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3573 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3574 const lhs = try self.resolveInst(bin_op.lhs);
3575 const rhs = try self.resolveInst(bin_op.rhs);
3576 const lhs_ty = self.air.typeOf(bin_op.lhs);
3577
3578 var int_buffer: Type.Payload.Bits = undefined;
3579 const int_ty = switch (lhs_ty.zigTypeTag()) {
3580 .Optional => blk: {
3581 var opt_buffer: Type.Payload.ElemType = undefined;
3582 const payload_ty = lhs_ty.optionalChild(&opt_buffer);
3583 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3584 break :blk Type.initTag(.u1);
3585 } else if (lhs_ty.isPtrLikeOptional()) {
3586 break :blk Type.usize;
3587 } else {
3588 return self.fail("TODO ARM cmp non-pointer optionals", .{});
3589 }
3590 },
3591 .Float => return self.fail("TODO ARM cmp floats", .{}),
3592 .Enum => lhs_ty.intTagType(&int_buffer),
3593 .Int => lhs_ty,
3594 .Bool => Type.initTag(.u1),
3595 .Pointer => Type.usize,
3596 .ErrorSet => Type.initTag(.u16),
3597 else => unreachable,
3598 };
3556 const lhs_ty = self.air.typeOf(bin_op.lhs);
35993557
3600 const int_info = int_ty.intInfo(self.target.*);
3601 if (int_info.bits <= 32) {
3602 try self.spillCompareFlagsIfOccupied();
3603 self.cpsr_flags_inst = inst;
3558 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
3559 const operands: BinOpOperands = .{ .inst = .{
3560 .inst = inst,
3561 .lhs = bin_op.lhs,
3562 .rhs = bin_op.rhs,
3563 } };
3564 break :blk try self.cmp(operands, lhs_ty, op);
3565 };
36043566
3605 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{
3606 .lhs = bin_op.lhs,
3607 .rhs = bin_op.rhs,
3608 .inst = inst,
3609 });
3567 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3568}
36103569
3611 break :result switch (int_info.signedness) {
3612 .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) },
3613 .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) },
3614 };
3615 } else {
3616 return self.fail("TODO ARM cmp for ints > 32 bits", .{});
3617 }
3570const BinOpOperands = union(enum) {
3571 inst: struct {
3572 inst: Air.Inst.Index,
3573 lhs: Air.Inst.Ref,
3574 rhs: Air.Inst.Ref,
3575 },
3576 mcv: struct {
3577 lhs: MCValue,
3578 rhs: MCValue,
3579 },
3580};
3581
3582fn cmp(
3583 self: *Self,
3584 operands: BinOpOperands,
3585 lhs_ty: Type,
3586 op: math.CompareOperator,
3587) !MCValue {
3588 var int_buffer: Type.Payload.Bits = undefined;
3589 const int_ty = switch (lhs_ty.zigTypeTag()) {
3590 .Optional => blk: {
3591 var opt_buffer: Type.Payload.ElemType = undefined;
3592 const payload_ty = lhs_ty.optionalChild(&opt_buffer);
3593 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3594 break :blk Type.initTag(.u1);
3595 } else if (lhs_ty.isPtrLikeOptional()) {
3596 break :blk Type.usize;
3597 } else {
3598 return self.fail("TODO ARM cmp non-pointer optionals", .{});
3599 }
3600 },
3601 .Float => return self.fail("TODO ARM cmp floats", .{}),
3602 .Enum => lhs_ty.intTagType(&int_buffer),
3603 .Int => lhs_ty,
3604 .Bool => Type.initTag(.u1),
3605 .Pointer => Type.usize,
3606 .ErrorSet => Type.initTag(.u16),
3607 else => unreachable,
36183608 };
3619 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3609
3610 const int_info = int_ty.intInfo(self.target.*);
3611 if (int_info.bits <= 32) {
3612 try self.spillCompareFlagsIfOccupied();
3613
3614 switch (operands) {
3615 .inst => |inst_op| {
3616 const metadata: BinOpMetadata = .{
3617 .inst = inst_op.inst,
3618 .lhs = inst_op.lhs,
3619 .rhs = inst_op.rhs,
3620 };
3621 const lhs = try self.resolveInst(inst_op.lhs);
3622 const rhs = try self.resolveInst(inst_op.rhs);
3623
3624 self.cpsr_flags_inst = inst_op.inst;
3625 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, metadata);
3626 },
3627 .mcv => |mcv_op| {
3628 _ = try self.binOp(.cmp_eq, mcv_op.lhs, mcv_op.rhs, int_ty, int_ty, null);
3629 },
3630 }
3631
3632 return switch (int_info.signedness) {
3633 .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) },
3634 .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) },
3635 };
3636 } else {
3637 return self.fail("TODO ARM cmp for ints > 32 bits", .{});
3638 }
36203639}
36213640
36223641fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
......@@ -4081,10 +4100,137 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
40814100
40824101fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
40834102 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4084 const condition = pl_op.operand;
4085 _ = condition;
4086 return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch});
4087 // return self.finishAir(inst, .dead, .{ condition, .none, .none });
4103 const condition_ty = self.air.typeOf(pl_op.operand);
4104 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
4105 const liveness = try self.liveness.getSwitchBr(
4106 self.gpa,
4107 inst,
4108 switch_br.data.cases_len + 1,
4109 );
4110 defer self.gpa.free(liveness.deaths);
4111
4112 // If the condition dies here in this switch instruction, process
4113 // that death now instead of later as this has an effect on
4114 // whether it needs to be spilled in the branches
4115 if (self.liveness.operandDies(inst, 0)) {
4116 const op_int = @enumToInt(pl_op.operand);
4117 if (op_int >= Air.Inst.Ref.typed_value_map.len) {
4118 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
4119 self.processDeath(op_index);
4120 }
4121 }
4122
4123 var extra_index: usize = switch_br.end;
4124 var case_i: u32 = 0;
4125 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
4126 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
4127 const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
4128 assert(items.len > 0);
4129 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
4130 extra_index = case.end + items.len + case_body.len;
4131
4132 var relocs = try self.gpa.alloc(u32, items.len);
4133 defer self.gpa.free(relocs);
4134
4135 if (items.len == 1) {
4136 const condition = try self.resolveInst(pl_op.operand);
4137 const item = try self.resolveInst(items[0]);
4138
4139 const operands: BinOpOperands = .{ .mcv = .{
4140 .lhs = condition,
4141 .rhs = item,
4142 } };
4143 const cmp_result = try self.cmp(operands, condition_ty, .neq);
4144
4145 relocs[0] = try self.addInst(.{
4146 .tag = .b,
4147 .cond = cmp_result.cpsr_flags,
4148 .data = .{ .inst = undefined }, // populated later through performReloc
4149 });
4150 } else {
4151 return self.fail("TODO switch with multiple items", .{});
4152 }
4153
4154 // Capture the state of register and stack allocation state so that we can revert to it.
4155 const parent_next_stack_offset = self.next_stack_offset;
4156 const parent_free_registers = self.register_manager.free_registers;
4157 const parent_cpsr_flags_inst = self.cpsr_flags_inst;
4158 var parent_stack = try self.stack.clone(self.gpa);
4159 defer parent_stack.deinit(self.gpa);
4160 const parent_registers = self.register_manager.registers;
4161
4162 try self.branch_stack.append(.{});
4163 errdefer {
4164 _ = self.branch_stack.pop();
4165 }
4166
4167 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
4168 for (liveness.deaths[case_i]) |operand| {
4169 self.processDeath(operand);
4170 }
4171 try self.genBody(case_body);
4172
4173 // Revert to the previous register and stack allocation state.
4174 var saved_case_branch = self.branch_stack.pop();
4175 defer saved_case_branch.deinit(self.gpa);
4176
4177 self.register_manager.registers = parent_registers;
4178 self.cpsr_flags_inst = parent_cpsr_flags_inst;
4179 self.stack.deinit(self.gpa);
4180 self.stack = parent_stack;
4181 parent_stack = .{};
4182
4183 self.next_stack_offset = parent_next_stack_offset;
4184 self.register_manager.free_registers = parent_free_registers;
4185
4186 for (relocs) |reloc| {
4187 try self.performReloc(reloc);
4188 }
4189 }
4190
4191 if (switch_br.data.else_body_len > 0) {
4192 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
4193
4194 // Capture the state of register and stack allocation state so that we can revert to it.
4195 const parent_next_stack_offset = self.next_stack_offset;
4196 const parent_free_registers = self.register_manager.free_registers;
4197 const parent_cpsr_flags_inst = self.cpsr_flags_inst;
4198 var parent_stack = try self.stack.clone(self.gpa);
4199 defer parent_stack.deinit(self.gpa);
4200 const parent_registers = self.register_manager.registers;
4201
4202 try self.branch_stack.append(.{});
4203 errdefer {
4204 _ = self.branch_stack.pop();
4205 }
4206
4207 const else_deaths = liveness.deaths.len - 1;
4208 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
4209 for (liveness.deaths[else_deaths]) |operand| {
4210 self.processDeath(operand);
4211 }
4212 try self.genBody(else_body);
4213
4214 // Revert to the previous register and stack allocation state.
4215 var saved_case_branch = self.branch_stack.pop();
4216 defer saved_case_branch.deinit(self.gpa);
4217
4218 self.register_manager.registers = parent_registers;
4219 self.cpsr_flags_inst = parent_cpsr_flags_inst;
4220 self.stack.deinit(self.gpa);
4221 self.stack = parent_stack;
4222 parent_stack = .{};
4223
4224 self.next_stack_offset = parent_next_stack_offset;
4225 self.register_manager.free_registers = parent_free_registers;
4226
4227 // TODO consolidate returned MCValues between prongs and else branch like we do
4228 // in airCondBr.
4229 }
4230
4231 // We already took care of pl_op.operand earlier, so we're going
4232 // to pass .none here
4233 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
40884234}
40894235
40904236fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {