| ... | ... | @@ -813,6 +813,17 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live |
| 813 | 813 | self.register_manager.getRegAssumeFree(reg, inst); |
| 814 | 814 | } |
| 815 | 815 | }, |
| 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 | }, |
| 816 | 827 | else => {}, |
| 817 | 828 | } |
| 818 | 829 | } |
| ... | ... | @@ -2462,56 +2473,28 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2462 | 2473 | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); |
| 2463 | 2474 | defer self.register_manager.unlockReg(reg_lock); |
| 2464 | 2475 | |
| 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 }, |
| 2480 | 2479 | |
| 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 | } }, |
| 2484 | 2486 | |
| 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 | }; |
| 2494 | 2489 | |
| 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); |
| 2510 | 2496 | |
| 2511 | | break :result MCValue{ .register = dest_reg }; |
| 2512 | | } |
| 2513 | | }, |
| 2514 | | else => unreachable, |
| 2497 | break :result MCValue{ .register = dest_reg }; |
| 2515 | 2498 | } |
| 2516 | 2499 | }, |
| 2517 | 2500 | 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 { |
| 3570 | 3553 | |
| 3571 | 3554 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3572 | 3555 | 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); |
| 3599 | 3557 | |
| 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 | }; |
| 3604 | 3566 | |
| 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 | } |
| 3610 | 3569 | |
| 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 | | } |
| 3570 | const 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 | |
| 3582 | fn 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, |
| 3618 | 3608 | }; |
| 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 | } |
| 3620 | 3639 | } |
| 3621 | 3640 | |
| 3622 | 3641 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -4081,10 +4100,137 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 4081 | 4100 | |
| 4082 | 4101 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 4083 | 4102 | 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 }); |
| 4088 | 4234 | } |
| 4089 | 4235 | |
| 4090 | 4236 | fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { |