authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-05 09:10:12+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-06-05 09:10:12+02:00
log1b5dd4e1487dfe45c6931b14da5053878ca9a00c
tree6210c471176d906b90a530b24f647aef41f99b13
parent43db697b46e362e5991005f6c7b8b16ddc9bddbb
parentd5ee45117722c1685f4b739686d74ccc7cf5b8d9
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11790 from joachimschmidt557/stage2-arm

stage2 ARM: implement basic switch expressions

2 files changed, 282 insertions(+), 128 deletions(-)

src/arch/arm/CodeGen.zig+282-122
...@@ -813,6 +813,17 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live...@@ -813,6 +813,17 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
813 self.register_manager.getRegAssumeFree(reg, inst);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 else => {},827 else => {},
817 }828 }
818 }829 }
...@@ -942,15 +953,6 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {...@@ -942,15 +953,6 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
942 return reg;953 return reg;
943}954}
944955
945/// Allocates a new register and copies `mcv` into it.
946/// `reg_owner` is the instruction that gets associated with the register in the register table.
947/// This can have a side effect of spilling instructions to the stack to free up a register.
948fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
949 const reg = try self.register_manager.allocReg(reg_owner, gp);
950 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
951 return MCValue{ .register = reg };
952}
953
954fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {956fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
955 const stack_offset = try self.allocMemPtr(inst);957 const stack_offset = try self.allocMemPtr(inst);
956 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });958 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
...@@ -2174,6 +2176,9 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind...@@ -2174,6 +2176,9 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind
2174 .stack_offset => |off| {2176 .stack_offset => |off| {
2175 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });2177 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
2176 },2178 },
2179 .cpsr_flags => {
2180 log.debug("%{d} => cpsr_flags (reused)", .{inst});
2181 },
2177 else => return false,2182 else => return false,
2178 }2183 }
21792184
...@@ -2462,56 +2467,28 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2462,56 +2467,28 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2462 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);2467 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
2463 defer self.register_manager.unlockReg(reg_lock);2468 defer self.register_manager.unlockReg(reg_lock);
24642469
2465 switch (index) {2470 const field: MCValue = switch (index) {
2466 0 => {2471 // get wrapped value: return register
2467 // get wrapped value: return register2472 0 => MCValue{ .register = reg },
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 };
24802473
2481 break :result MCValue{ .cpsr_flags = cond };2474 // get overflow bit: return C or V flag
2482 } else {2475 1 => MCValue{ .cpsr_flags = switch (mcv) {
2483 const dest_reg = try self.register_manager.allocReg(null, gp);2476 .register_c_flag => .cs,
2477 .register_v_flag => .vs,
2478 else => unreachable,
2479 } },
24842480
2485 // mov reg, #02481 else => unreachable,
2486 _ = try self.addInst(.{2482 };
2487 .tag = .mov,
2488 .data = .{ .rr_op = .{
2489 .rd = dest_reg,
2490 .rn = .r0,
2491 .op = Instruction.Operand.fromU32(0).?,
2492 } },
2493 });
24942483
2495 // C flag: movcs reg, #12484 if (self.reuseOperand(inst, operand, 0, field)) {
2496 // V flag: movvs reg, #12485 break :result field;
2497 _ = try self.addInst(.{2486 } else {
2498 .tag = .mov,2487 // Copy to new register
2499 .cond = switch (mcv) {2488 const dest_reg = try self.register_manager.allocReg(null, gp);
2500 .register_c_flag => .cs,2489 try self.genSetReg(struct_ty.structFieldType(index), dest_reg, field);
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 });
25102490
2511 break :result MCValue{ .register = dest_reg };2491 break :result MCValue{ .register = dest_reg };
2512 }
2513 },
2514 else => unreachable,
2515 }2492 }
2516 },2493 },
2517 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),2494 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
...@@ -2528,6 +2505,41 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2528,6 +2505,41 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
2528 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2505 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2529}2506}
25302507
2508/// Allocates a new register. If Inst in non-null, additionally tracks
2509/// this register and the corresponding int and removes all previous
2510/// tracking. Does not do the actual moving (that is handled by
2511/// genSetReg).
2512fn prepareNewRegForMoving(
2513 self: *Self,
2514 track_inst: ?Air.Inst.Index,
2515 register_class: RegisterManager.RegisterBitSet,
2516 mcv: MCValue,
2517) !Register {
2518 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2519 const reg = try self.register_manager.allocReg(track_inst, register_class);
2520
2521 if (track_inst) |inst| {
2522 // Overwrite the MCValue associated with this inst
2523 branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
2524
2525 // If the previous MCValue occupied some space we track, we
2526 // need to make sure it is marked as free now.
2527 switch (mcv) {
2528 .cpsr_flags => {
2529 assert(self.cpsr_flags_inst.? == inst);
2530 self.cpsr_flags_inst = null;
2531 },
2532 .register => |prev_reg| {
2533 assert(!self.register_manager.isRegFree(prev_reg));
2534 self.register_manager.freeReg(prev_reg);
2535 },
2536 else => {},
2537 }
2538 }
2539
2540 return reg;
2541}
2542
2531/// Don't call this function directly. Use binOp instead.2543/// Don't call this function directly. Use binOp instead.
2532///2544///
2533/// Calling this function signals an intention to generate a Mir2545/// Calling this function signals an intention to generate a Mir
...@@ -2554,18 +2566,12 @@ fn binOpRegister(...@@ -2554,18 +2566,12 @@ fn binOpRegister(
2554 null;2566 null;
2555 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);2567 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
25562568
2557 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2558
2559 const lhs_reg = if (lhs_is_register) lhs.register else blk: {2569 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2560 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {2570 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
2561 break :inst Air.refToIndex(md.lhs).?;2571 break :inst Air.refToIndex(md.lhs).?;
2562 } else null;2572 } else null;
25632573
2564 const reg = try self.register_manager.allocReg(track_inst, gp);2574 break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs);
2565
2566 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
2567
2568 break :blk reg;
2569 };2575 };
2570 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);2576 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
2571 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);2577 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
...@@ -2575,11 +2581,7 @@ fn binOpRegister(...@@ -2575,11 +2581,7 @@ fn binOpRegister(
2575 break :inst Air.refToIndex(md.rhs).?;2581 break :inst Air.refToIndex(md.rhs).?;
2576 } else null;2582 } else null;
25772583
2578 const reg = try self.register_manager.allocReg(track_inst, gp);2584 break :blk try self.prepareNewRegForMoving(track_inst, gp, rhs);
2579
2580 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
2581
2582 break :blk reg;
2583 };2585 };
2584 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);2586 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
2585 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);2587 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
...@@ -2669,8 +2671,6 @@ fn binOpImmediate(...@@ -2669,8 +2671,6 @@ fn binOpImmediate(
2669 null;2671 null;
2670 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);2672 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
26712673
2672 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2673
2674 const lhs_reg = if (lhs_is_register) lhs.register else blk: {2674 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2675 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {2675 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
2676 break :inst Air.refToIndex(2676 break :inst Air.refToIndex(
...@@ -2678,11 +2678,7 @@ fn binOpImmediate(...@@ -2678,11 +2678,7 @@ fn binOpImmediate(
2678 ).?;2678 ).?;
2679 } else null;2679 } else null;
26802680
2681 const reg = try self.register_manager.allocReg(track_inst, gp);2681 break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs);
2682
2683 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
2684
2685 break :blk reg;
2686 };2682 };
2687 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);2683 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
2688 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);2684 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
...@@ -3461,7 +3457,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3461,7 +3457,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3461 if (RegisterManager.indexOfRegIntoTracked(reg) == null) {3457 if (RegisterManager.indexOfRegIntoTracked(reg) == null) {
3462 // Save function return value into a tracked register3458 // Save function return value into a tracked register
3463 log.debug("airCall: copying {} as it is not tracked", .{reg});3459 log.debug("airCall: copying {} as it is not tracked", .{reg});
3464 break :result try self.copyToNewRegister(inst, info.return_value);3460 const new_reg = try self.copyToTmpRegister(fn_ty.fnReturnType(), info.return_value);
3461 break :result MCValue{ .register = new_reg };
3465 }3462 }
3466 },3463 },
3467 else => {},3464 else => {},
...@@ -3570,53 +3567,89 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -3570,53 +3567,89 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
35703567
3571fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {3568fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
3572 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3569 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3573 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {3570 const lhs_ty = self.air.typeOf(bin_op.lhs);
3574 const lhs = try self.resolveInst(bin_op.lhs);3571
3575 const rhs = try self.resolveInst(bin_op.rhs);3572 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
3576 const lhs_ty = self.air.typeOf(bin_op.lhs);3573 const operands: BinOpOperands = .{ .inst = .{
35773574 .inst = inst,
3578 var int_buffer: Type.Payload.Bits = undefined;3575 .lhs = bin_op.lhs,
3579 const int_ty = switch (lhs_ty.zigTypeTag()) {3576 .rhs = bin_op.rhs,
3580 .Optional => blk: {3577 } };
3581 var opt_buffer: Type.Payload.ElemType = undefined;3578 break :blk try self.cmp(operands, lhs_ty, op);
3582 const payload_ty = lhs_ty.optionalChild(&opt_buffer);3579 };
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 };
35993580
3600 const int_info = int_ty.intInfo(self.target.*);3581 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3601 if (int_info.bits <= 32) {3582}
3602 try self.spillCompareFlagsIfOccupied();
3603 self.cpsr_flags_inst = inst;
36043583
3605 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{3584const BinOpOperands = union(enum) {
3606 .lhs = bin_op.lhs,3585 inst: struct {
3607 .rhs = bin_op.rhs,3586 inst: Air.Inst.Index,
3608 .inst = inst,3587 lhs: Air.Inst.Ref,
3609 });3588 rhs: Air.Inst.Ref,
3589 },
3590 mcv: struct {
3591 lhs: MCValue,
3592 rhs: MCValue,
3593 },
3594};
36103595
3611 break :result switch (int_info.signedness) {3596fn cmp(
3612 .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) },3597 self: *Self,
3613 .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) },3598 operands: BinOpOperands,
3614 };3599 lhs_ty: Type,
3615 } else {3600 op: math.CompareOperator,
3616 return self.fail("TODO ARM cmp for ints > 32 bits", .{});3601) !MCValue {
3617 }3602 var int_buffer: Type.Payload.Bits = undefined;
3603 const int_ty = switch (lhs_ty.zigTypeTag()) {
3604 .Optional => blk: {
3605 var opt_buffer: Type.Payload.ElemType = undefined;
3606 const payload_ty = lhs_ty.optionalChild(&opt_buffer);
3607 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3608 break :blk Type.initTag(.u1);
3609 } else if (lhs_ty.isPtrLikeOptional()) {
3610 break :blk Type.usize;
3611 } else {
3612 return self.fail("TODO ARM cmp non-pointer optionals", .{});
3613 }
3614 },
3615 .Float => return self.fail("TODO ARM cmp floats", .{}),
3616 .Enum => lhs_ty.intTagType(&int_buffer),
3617 .Int => lhs_ty,
3618 .Bool => Type.initTag(.u1),
3619 .Pointer => Type.usize,
3620 .ErrorSet => Type.initTag(.u16),
3621 else => unreachable,
3618 };3622 };
3619 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });3623
3624 const int_info = int_ty.intInfo(self.target.*);
3625 if (int_info.bits <= 32) {
3626 try self.spillCompareFlagsIfOccupied();
3627
3628 switch (operands) {
3629 .inst => |inst_op| {
3630 const metadata: BinOpMetadata = .{
3631 .inst = inst_op.inst,
3632 .lhs = inst_op.lhs,
3633 .rhs = inst_op.rhs,
3634 };
3635 const lhs = try self.resolveInst(inst_op.lhs);
3636 const rhs = try self.resolveInst(inst_op.rhs);
3637
3638 self.cpsr_flags_inst = inst_op.inst;
3639 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, metadata);
3640 },
3641 .mcv => |mcv_op| {
3642 _ = try self.binOp(.cmp_eq, mcv_op.lhs, mcv_op.rhs, int_ty, int_ty, null);
3643 },
3644 }
3645
3646 return switch (int_info.signedness) {
3647 .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) },
3648 .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) },
3649 };
3650 } else {
3651 return self.fail("TODO ARM cmp for ints > 32 bits", .{});
3652 }
3620}3653}
36213654
3622fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {3655fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void {
...@@ -4081,10 +4114,137 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -4081,10 +4114,137 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
40814114
4082fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {4115fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
4083 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4116 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4084 const condition = pl_op.operand;4117 const condition_ty = self.air.typeOf(pl_op.operand);
4085 _ = condition;4118 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
4086 return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch});4119 const liveness = try self.liveness.getSwitchBr(
4087 // return self.finishAir(inst, .dead, .{ condition, .none, .none });4120 self.gpa,
4121 inst,
4122 switch_br.data.cases_len + 1,
4123 );
4124 defer self.gpa.free(liveness.deaths);
4125
4126 // If the condition dies here in this switch instruction, process
4127 // that death now instead of later as this has an effect on
4128 // whether it needs to be spilled in the branches
4129 if (self.liveness.operandDies(inst, 0)) {
4130 const op_int = @enumToInt(pl_op.operand);
4131 if (op_int >= Air.Inst.Ref.typed_value_map.len) {
4132 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
4133 self.processDeath(op_index);
4134 }
4135 }
4136
4137 var extra_index: usize = switch_br.end;
4138 var case_i: u32 = 0;
4139 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
4140 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
4141 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
4142 assert(items.len > 0);
4143 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
4144 extra_index = case.end + items.len + case_body.len;
4145
4146 var relocs = try self.gpa.alloc(u32, items.len);
4147 defer self.gpa.free(relocs);
4148
4149 if (items.len == 1) {
4150 const condition = try self.resolveInst(pl_op.operand);
4151 const item = try self.resolveInst(items[0]);
4152
4153 const operands: BinOpOperands = .{ .mcv = .{
4154 .lhs = condition,
4155 .rhs = item,
4156 } };
4157 const cmp_result = try self.cmp(operands, condition_ty, .neq);
4158
4159 relocs[0] = try self.addInst(.{
4160 .tag = .b,
4161 .cond = cmp_result.cpsr_flags,
4162 .data = .{ .inst = undefined }, // populated later through performReloc
4163 });
4164 } else {
4165 return self.fail("TODO switch with multiple items", .{});
4166 }
4167
4168 // Capture the state of register and stack allocation state so that we can revert to it.
4169 const parent_next_stack_offset = self.next_stack_offset;
4170 const parent_free_registers = self.register_manager.free_registers;
4171 const parent_cpsr_flags_inst = self.cpsr_flags_inst;
4172 var parent_stack = try self.stack.clone(self.gpa);
4173 defer parent_stack.deinit(self.gpa);
4174 const parent_registers = self.register_manager.registers;
4175
4176 try self.branch_stack.append(.{});
4177 errdefer {
4178 _ = self.branch_stack.pop();
4179 }
4180
4181 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
4182 for (liveness.deaths[case_i]) |operand| {
4183 self.processDeath(operand);
4184 }
4185 try self.genBody(case_body);
4186
4187 // Revert to the previous register and stack allocation state.
4188 var saved_case_branch = self.branch_stack.pop();
4189 defer saved_case_branch.deinit(self.gpa);
4190
4191 self.register_manager.registers = parent_registers;
4192 self.cpsr_flags_inst = parent_cpsr_flags_inst;
4193 self.stack.deinit(self.gpa);
4194 self.stack = parent_stack;
4195 parent_stack = .{};
4196
4197 self.next_stack_offset = parent_next_stack_offset;
4198 self.register_manager.free_registers = parent_free_registers;
4199
4200 for (relocs) |reloc| {
4201 try self.performReloc(reloc);
4202 }
4203 }
4204
4205 if (switch_br.data.else_body_len > 0) {
4206 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
4207
4208 // Capture the state of register and stack allocation state so that we can revert to it.
4209 const parent_next_stack_offset = self.next_stack_offset;
4210 const parent_free_registers = self.register_manager.free_registers;
4211 const parent_cpsr_flags_inst = self.cpsr_flags_inst;
4212 var parent_stack = try self.stack.clone(self.gpa);
4213 defer parent_stack.deinit(self.gpa);
4214 const parent_registers = self.register_manager.registers;
4215
4216 try self.branch_stack.append(.{});
4217 errdefer {
4218 _ = self.branch_stack.pop();
4219 }
4220
4221 const else_deaths = liveness.deaths.len - 1;
4222 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
4223 for (liveness.deaths[else_deaths]) |operand| {
4224 self.processDeath(operand);
4225 }
4226 try self.genBody(else_body);
4227
4228 // Revert to the previous register and stack allocation state.
4229 var saved_case_branch = self.branch_stack.pop();
4230 defer saved_case_branch.deinit(self.gpa);
4231
4232 self.register_manager.registers = parent_registers;
4233 self.cpsr_flags_inst = parent_cpsr_flags_inst;
4234 self.stack.deinit(self.gpa);
4235 self.stack = parent_stack;
4236 parent_stack = .{};
4237
4238 self.next_stack_offset = parent_next_stack_offset;
4239 self.register_manager.free_registers = parent_free_registers;
4240
4241 // TODO consolidate returned MCValues between prongs and else branch like we do
4242 // in airCondBr.
4243 }
4244
4245 // We already took care of pl_op.operand earlier, so we're going
4246 // to pass .none here
4247 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
4088}4248}
40894249
4090fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {4250fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
test/behavior/switch.zig-6
...@@ -5,7 +5,6 @@ const expectError = std.testing.expectError;...@@ -5,7 +5,6 @@ const expectError = std.testing.expectError;
5const expectEqual = std.testing.expectEqual;5const expectEqual = std.testing.expectEqual;
66
7test "switch with numbers" {7test "switch with numbers" {
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
109
11 try testSwitchWithNumbers(13);10 try testSwitchWithNumbers(13);
...@@ -21,7 +20,6 @@ fn testSwitchWithNumbers(x: u32) !void {...@@ -21,7 +20,6 @@ fn testSwitchWithNumbers(x: u32) !void {
21}20}
2221
23test "switch with all ranges" {22test "switch with all ranges" {
24 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
25 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO23 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2624
27 try expect(testSwitchWithAllRanges(50, 3) == 1);25 try expect(testSwitchWithAllRanges(50, 3) == 1);
...@@ -176,7 +174,6 @@ test "undefined.u0" {...@@ -176,7 +174,6 @@ test "undefined.u0" {
176}174}
177175
178test "switch with disjoint range" {176test "switch with disjoint range" {
179 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
180 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO177 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
181178
182 var q: u8 = 0;179 var q: u8 = 0;
...@@ -397,7 +394,6 @@ fn switchWithUnreachable(x: i32) i32 {...@@ -397,7 +394,6 @@ fn switchWithUnreachable(x: i32) i32 {
397}394}
398395
399test "capture value of switch with all unreachable prongs" {396test "capture value of switch with all unreachable prongs" {
400 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
401 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO397 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
402398
403 const x = return_a_number() catch |err| switch (err) {399 const x = return_a_number() catch |err| switch (err) {
...@@ -412,7 +408,6 @@ fn return_a_number() anyerror!i32 {...@@ -412,7 +408,6 @@ fn return_a_number() anyerror!i32 {
412408
413test "switch on integer with else capturing expr" {409test "switch on integer with else capturing expr" {
414 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO410 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
415 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
416 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO411 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
417412
418 const S = struct {413 const S = struct {
...@@ -658,7 +653,6 @@ test "switch capture copies its payload" {...@@ -658,7 +653,6 @@ test "switch capture copies its payload" {
658}653}
659654
660test "capture of integer forwards the switch condition directly" {655test "capture of integer forwards the switch condition directly" {
661 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
662 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO656 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
663657
664 const S = struct {658 const S = struct {