authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-10-23 16:53:44+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-11-01 20:43:27+01:00
log3ecec50f0c530760653a73194fa3651b16647ef9
tree1fff385d90d722b87580b5cc9db1d05d90b6d153
parentd2a5a36cabad5a9b44538e50e9288c3a968cb9f3
signaturelock-open Commit is signed but in an unrecognized format.

stage2 AArch64: implement basic switch statements


2 files changed, 190 insertions(+), 61 deletions(-)

src/arch/aarch64/CodeGen.zig+190-36
...@@ -91,7 +91,7 @@ register_manager: RegisterManager = .{},...@@ -91,7 +91,7 @@ register_manager: RegisterManager = .{},
91/// Maps offset to what is stored there.91/// Maps offset to what is stored there.
92stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},92stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
93/// Tracks the current instruction allocated to the compare flags93/// Tracks the current instruction allocated to the compare flags
94condition_flags_inst: ?Air.Inst.Index = null,94compare_flags_inst: ?Air.Inst.Index = null,
9595
96/// Offset from the stack base, representing the end of the stack frame.96/// Offset from the stack base, representing the end of the stack frame.
97max_end_stack: u32 = 0,97max_end_stack: u32 = 0,
...@@ -154,7 +154,7 @@ const MCValue = union(enum) {...@@ -154,7 +154,7 @@ const MCValue = union(enum) {
154 /// The value resides in the N, Z, C, V flags. The value is 1 (if154 /// The value resides in the N, Z, C, V flags. The value is 1 (if
155 /// the type is u1) or true (if the type in bool) iff the155 /// the type is u1) or true (if the type in bool) iff the
156 /// specified condition is true.156 /// specified condition is true.
157 condition_flags: Condition,157 compare_flags: Condition,
158 /// The value is a function argument passed via the stack.158 /// The value is a function argument passed via the stack.
159 stack_argument_offset: u32,159 stack_argument_offset: u32,
160};160};
...@@ -201,6 +201,29 @@ const BigTomb = struct {...@@ -201,6 +201,29 @@ const BigTomb = struct {
201 log.debug("%{d} => {}", .{ bt.inst, result });201 log.debug("%{d} => {}", .{ bt.inst, result });
202 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];202 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
203 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);203 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
204
205 switch (result) {
206 .register => |reg| {
207 // In some cases (such as bitcast), an operand
208 // may be the same MCValue as the result. If
209 // that operand died and was a register, it
210 // was freed by processDeath. We have to
211 // "re-allocate" the register.
212 if (bt.function.register_manager.isRegFree(reg)) {
213 bt.function.register_manager.getRegAssumeFree(reg, bt.inst);
214 }
215 },
216 .register_with_overflow => |rwo| {
217 if (bt.function.register_manager.isRegFree(rwo.reg)) {
218 bt.function.register_manager.getRegAssumeFree(rwo.reg, bt.inst);
219 }
220 bt.function.compare_flags_inst = bt.inst;
221 },
222 .compare_flags => |_| {
223 bt.function.compare_flags_inst = bt.inst;
224 },
225 else => {},
226 }
204 }227 }
205 bt.function.finishAirBookkeeping();228 bt.function.finishAirBookkeeping();
206 }229 }
...@@ -764,10 +787,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {...@@ -764,10 +787,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
764 },787 },
765 .register_with_overflow => |rwo| {788 .register_with_overflow => |rwo| {
766 self.register_manager.freeReg(rwo.reg);789 self.register_manager.freeReg(rwo.reg);
767 self.condition_flags_inst = null;790 self.compare_flags_inst = null;
768 },791 },
769 .condition_flags => {792 .compare_flags => {
770 self.condition_flags_inst = null;793 self.compare_flags_inst = null;
771 },794 },
772 else => {}, // TODO process stack allocation death795 else => {}, // TODO process stack allocation death
773 }796 }
...@@ -808,6 +831,15 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live...@@ -808,6 +831,15 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
808 self.register_manager.getRegAssumeFree(reg, inst);831 self.register_manager.getRegAssumeFree(reg, inst);
809 }832 }
810 },833 },
834 .register_with_overflow => |rwo| {
835 if (self.register_manager.isRegFree(rwo.reg)) {
836 self.register_manager.getRegAssumeFree(rwo.reg, inst);
837 }
838 self.compare_flags_inst = inst;
839 },
840 .compare_flags => |_| {
841 self.compare_flags_inst = inst;
842 },
811 else => {},843 else => {},
812 }844 }
813 }845 }
...@@ -931,11 +963,11 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void...@@ -931,11 +963,11 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
931/// Save the current instruction stored in the compare flags if963/// Save the current instruction stored in the compare flags if
932/// occupied964/// occupied
933fn spillCompareFlagsIfOccupied(self: *Self) !void {965fn spillCompareFlagsIfOccupied(self: *Self) !void {
934 if (self.condition_flags_inst) |inst_to_save| {966 if (self.compare_flags_inst) |inst_to_save| {
935 const ty = self.air.typeOfIndex(inst_to_save);967 const ty = self.air.typeOfIndex(inst_to_save);
936 const mcv = self.getResolvedInstValue(inst_to_save);968 const mcv = self.getResolvedInstValue(inst_to_save);
937 const new_mcv = switch (mcv) {969 const new_mcv = switch (mcv) {
938 .condition_flags => try self.allocRegOrMem(ty, true, inst_to_save),970 .compare_flags => try self.allocRegOrMem(ty, true, inst_to_save),
939 .register_with_overflow => try self.allocRegOrMem(ty, false, inst_to_save),971 .register_with_overflow => try self.allocRegOrMem(ty, false, inst_to_save),
940 else => unreachable, // mcv doesn't occupy the compare flags972 else => unreachable, // mcv doesn't occupy the compare flags
941 };973 };
...@@ -946,7 +978,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {...@@ -946,7 +978,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
946 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];978 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
947 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);979 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
948980
949 self.condition_flags_inst = null;981 self.compare_flags_inst = null;
950982
951 // TODO consolidate with register manager and spillInstruction983 // TODO consolidate with register manager and spillInstruction
952 // this call should really belong in the register manager!984 // this call should really belong in the register manager!
...@@ -1155,7 +1187,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {...@@ -1155,7 +1187,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1155 switch (operand) {1187 switch (operand) {
1156 .dead => unreachable,1188 .dead => unreachable,
1157 .unreach => unreachable,1189 .unreach => unreachable,
1158 .condition_flags => |cond| break :result MCValue{ .condition_flags = cond.negate() },1190 .compare_flags => |cond| break :result MCValue{ .compare_flags = cond.negate() },
1159 else => {1191 else => {
1160 switch (operand_ty.zigTypeTag()) {1192 switch (operand_ty.zigTypeTag()) {
1161 .Bool => {1193 .Bool => {
...@@ -1564,9 +1596,9 @@ fn allocRegs(...@@ -1564,9 +1596,9 @@ fn allocRegs(
1564 // If the previous MCValue occupied some space we track, we1596 // If the previous MCValue occupied some space we track, we
1565 // need to make sure it is marked as free now.1597 // need to make sure it is marked as free now.
1566 switch (mcv) {1598 switch (mcv) {
1567 .condition_flags => {1599 .compare_flags => {
1568 assert(self.condition_flags_inst.? == inst);1600 assert(self.compare_flags_inst.? == inst);
1569 self.condition_flags_inst = null;1601 self.compare_flags_inst = null;
1570 },1602 },
1571 .register => |prev_reg| {1603 .register => |prev_reg| {
1572 assert(!self.register_manager.isRegFree(prev_reg));1604 assert(!self.register_manager.isRegFree(prev_reg));
...@@ -2363,7 +2395,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2363,7 +2395,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2363 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);2395 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
23642396
2365 try self.spillCompareFlagsIfOccupied();2397 try self.spillCompareFlagsIfOccupied();
2366 self.condition_flags_inst = null;2398 self.compare_flags_inst = null;
23672399
2368 const base_tag: Air.Inst.Tag = switch (tag) {2400 const base_tag: Air.Inst.Tag = switch (tag) {
2369 .add_with_overflow => .add,2401 .add_with_overflow => .add,
...@@ -2395,7 +2427,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2395,7 +2427,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2395 });2427 });
23962428
2397 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });2429 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
2398 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne });2430 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne });
23992431
2400 break :result MCValue{ .stack_offset = stack_offset };2432 break :result MCValue{ .stack_offset = stack_offset };
2401 },2433 },
...@@ -2430,7 +2462,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2430,7 +2462,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
2430 };2462 };
24312463
2432 try self.spillCompareFlagsIfOccupied();2464 try self.spillCompareFlagsIfOccupied();
2433 self.condition_flags_inst = inst;2465 self.compare_flags_inst = inst;
24342466
2435 const dest = blk: {2467 const dest = blk: {
2436 if (rhs_immediate_ok) {2468 if (rhs_immediate_ok) {
...@@ -2539,7 +2571,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2539,7 +2571,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2539 }2571 }
25402572
2541 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });2573 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
2542 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne });2574 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne });
25432575
2544 break :result MCValue{ .stack_offset = stack_offset };2576 break :result MCValue{ .stack_offset = stack_offset };
2545 } else if (int_info.bits <= 64) {2577 } else if (int_info.bits <= 64) {
...@@ -2679,7 +2711,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2679,7 +2711,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2679 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);2711 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
26802712
2681 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });2713 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
2682 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne });2714 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne });
26832715
2684 break :result MCValue{ .stack_offset = stack_offset };2716 break :result MCValue{ .stack_offset = stack_offset };
2685 } else return self.fail("TODO implement mul_with_overflow for integers > u64/i64", .{});2717 } else return self.fail("TODO implement mul_with_overflow for integers > u64/i64", .{});
...@@ -2811,7 +2843,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -2811,7 +2843,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
2811 });2843 });
28122844
2813 try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg });2845 try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg });
2814 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne });2846 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne });
28152847
2816 break :result MCValue{ .stack_offset = stack_offset };2848 break :result MCValue{ .stack_offset = stack_offset };
2817 } else {2849 } else {
...@@ -3262,7 +3294,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -3262,7 +3294,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
3262 .undef => unreachable,3294 .undef => unreachable,
3263 .unreach => unreachable,3295 .unreach => unreachable,
3264 .dead => unreachable,3296 .dead => unreachable,
3265 .condition_flags,3297 .compare_flags,
3266 .register_with_overflow,3298 .register_with_overflow,
3267 => unreachable, // cannot hold an address3299 => unreachable, // cannot hold an address
3268 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),3300 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
...@@ -3274,7 +3306,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -3274,7 +3306,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
3274 switch (dst_mcv) {3306 switch (dst_mcv) {
3275 .dead => unreachable,3307 .dead => unreachable,
3276 .undef => unreachable,3308 .undef => unreachable,
3277 .condition_flags => unreachable,3309 .compare_flags => unreachable,
3278 .register => |dst_reg| {3310 .register => |dst_reg| {
3279 try self.genLdrRegister(dst_reg, addr_reg, elem_ty);3311 try self.genLdrRegister(dst_reg, addr_reg, elem_ty);
3280 },3312 },
...@@ -3483,7 +3515,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -3483,7 +3515,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
3483 .undef => unreachable,3515 .undef => unreachable,
3484 .unreach => unreachable,3516 .unreach => unreachable,
3485 .dead => unreachable,3517 .dead => unreachable,
3486 .condition_flags,3518 .compare_flags,
3487 .register_with_overflow,3519 .register_with_overflow,
3488 => unreachable, // cannot hold an address3520 => unreachable, // cannot hold an address
3489 .immediate => |imm| {3521 .immediate => |imm| {
...@@ -3638,7 +3670,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -3638,7 +3670,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
3638 0 => MCValue{ .register = rwo.reg },3670 0 => MCValue{ .register = rwo.reg },
36393671
3640 // get overflow bit: return C or V flag3672 // get overflow bit: return C or V flag
3641 1 => MCValue{ .condition_flags = rwo.flag },3673 1 => MCValue{ .compare_flags = rwo.flag },
36423674
3643 else => unreachable,3675 else => unreachable,
3644 };3676 };
...@@ -4092,8 +4124,8 @@ fn cmp(...@@ -4092,8 +4124,8 @@ fn cmp(
4092 }4124 }
40934125
4094 return switch (int_info.signedness) {4126 return switch (int_info.signedness) {
4095 .signed => MCValue{ .condition_flags = Condition.fromCompareOperatorSigned(op) },4127 .signed => MCValue{ .compare_flags = Condition.fromCompareOperatorSigned(op) },
4096 .unsigned => MCValue{ .condition_flags = Condition.fromCompareOperatorUnsigned(op) },4128 .unsigned => MCValue{ .compare_flags = Condition.fromCompareOperatorUnsigned(op) },
4097 };4129 };
4098 } else {4130 } else {
4099 return self.fail("TODO AArch64 cmp for ints > 64 bits", .{});4131 return self.fail("TODO AArch64 cmp for ints > 64 bits", .{});
...@@ -4151,7 +4183,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {...@@ -4151,7 +4183,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
41514183
4152fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {4184fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
4153 switch (condition) {4185 switch (condition) {
4154 .condition_flags => |cond| return try self.addInst(.{4186 .compare_flags => |cond| return try self.addInst(.{
4155 .tag = .b_cond,4187 .tag = .b_cond,
4156 .data = .{4188 .data = .{
4157 .inst_cond = .{4189 .inst_cond = .{
...@@ -4207,7 +4239,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4207,7 +4239,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4207 var parent_stack = try self.stack.clone(self.gpa);4239 var parent_stack = try self.stack.clone(self.gpa);
4208 defer parent_stack.deinit(self.gpa);4240 defer parent_stack.deinit(self.gpa);
4209 const parent_registers = self.register_manager.registers;4241 const parent_registers = self.register_manager.registers;
4210 const parent_condition_flags_inst = self.condition_flags_inst;4242 const parent_compare_flags_inst = self.compare_flags_inst;
42114243
4212 try self.branch_stack.append(.{});4244 try self.branch_stack.append(.{});
4213 errdefer {4245 errdefer {
...@@ -4226,7 +4258,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4226,7 +4258,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4226 defer saved_then_branch.deinit(self.gpa);4258 defer saved_then_branch.deinit(self.gpa);
42274259
4228 self.register_manager.registers = parent_registers;4260 self.register_manager.registers = parent_registers;
4229 self.condition_flags_inst = parent_condition_flags_inst;4261 self.compare_flags_inst = parent_compare_flags_inst;
42304262
4231 self.stack.deinit(self.gpa);4263 self.stack.deinit(self.gpa);
4232 self.stack = parent_stack;4264 self.stack = parent_stack;
...@@ -4354,9 +4386,9 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {...@@ -4354,9 +4386,9 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
4354fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {4386fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
4355 const is_err_result = try self.isErr(ty, operand);4387 const is_err_result = try self.isErr(ty, operand);
4356 switch (is_err_result) {4388 switch (is_err_result) {
4357 .condition_flags => |cond| {4389 .compare_flags => |cond| {
4358 assert(cond == .hi);4390 assert(cond == .hi);
4359 return MCValue{ .condition_flags = cond.negate() };4391 return MCValue{ .compare_flags = cond.negate() };
4360 },4392 },
4361 .immediate => |imm| {4393 .immediate => |imm| {
4362 assert(imm == 0);4394 assert(imm == 0);
...@@ -4519,10 +4551,132 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -4519,10 +4551,132 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
45194551
4520fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {4552fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
4521 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4553 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4522 const condition = pl_op.operand;4554 const condition_ty = self.air.typeOf(pl_op.operand);
4523 _ = condition;4555 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
4556 const liveness = try self.liveness.getSwitchBr(
4557 self.gpa,
4558 inst,
4559 switch_br.data.cases_len + 1,
4560 );
4561 defer self.gpa.free(liveness.deaths);
4562
4563 var extra_index: usize = switch_br.end;
4564 var case_i: u32 = 0;
4565 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
4566 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
4567 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
4568 assert(items.len > 0);
4569 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
4570 extra_index = case.end + items.len + case_body.len;
4571
4572 // For every item, we compare it to condition and branch into
4573 // the prong if they are equal. After we compared to all
4574 // items, we branch into the next prong (or if no other prongs
4575 // exist out of the switch statement).
4576 //
4577 // cmp condition, item1
4578 // beq prong
4579 // cmp condition, item2
4580 // beq prong
4581 // cmp condition, item3
4582 // beq prong
4583 // b out
4584 // prong: ...
4585 // ...
4586 // out: ...
4587 const branch_into_prong_relocs = try self.gpa.alloc(u32, items.len);
4588 defer self.gpa.free(branch_into_prong_relocs);
4589
4590 for (items) |item, idx| {
4591 const cmp_result = try self.cmp(.{ .inst = pl_op.operand }, .{ .inst = item }, condition_ty, .neq);
4592 branch_into_prong_relocs[idx] = try self.condBr(cmp_result);
4593 }
4594
4595 const branch_away_from_prong_reloc = try self.addInst(.{
4596 .tag = .b,
4597 .data = .{ .inst = undefined }, // populated later through performReloc
4598 });
4599
4600 for (branch_into_prong_relocs) |reloc| {
4601 try self.performReloc(reloc);
4602 }
4603
4604 // Capture the state of register and stack allocation state so that we can revert to it.
4605 const parent_next_stack_offset = self.next_stack_offset;
4606 const parent_free_registers = self.register_manager.free_registers;
4607 const parent_compare_flags_inst = self.compare_flags_inst;
4608 var parent_stack = try self.stack.clone(self.gpa);
4609 defer parent_stack.deinit(self.gpa);
4610 const parent_registers = self.register_manager.registers;
4611
4612 try self.branch_stack.append(.{});
4613 errdefer {
4614 _ = self.branch_stack.pop();
4615 }
4616
4617 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len);
4618 for (liveness.deaths[case_i]) |operand| {
4619 self.processDeath(operand);
4620 }
4621 try self.genBody(case_body);
4622
4623 // Revert to the previous register and stack allocation state.
4624 var saved_case_branch = self.branch_stack.pop();
4625 defer saved_case_branch.deinit(self.gpa);
4626
4627 self.register_manager.registers = parent_registers;
4628 self.compare_flags_inst = parent_compare_flags_inst;
4629 self.stack.deinit(self.gpa);
4630 self.stack = parent_stack;
4631 parent_stack = .{};
4632
4633 self.next_stack_offset = parent_next_stack_offset;
4634 self.register_manager.free_registers = parent_free_registers;
4635
4636 try self.performReloc(branch_away_from_prong_reloc);
4637 }
4638
4639 if (switch_br.data.else_body_len > 0) {
4640 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
4641
4642 // Capture the state of register and stack allocation state so that we can revert to it.
4643 const parent_next_stack_offset = self.next_stack_offset;
4644 const parent_free_registers = self.register_manager.free_registers;
4645 const parent_compare_flags_inst = self.compare_flags_inst;
4646 var parent_stack = try self.stack.clone(self.gpa);
4647 defer parent_stack.deinit(self.gpa);
4648 const parent_registers = self.register_manager.registers;
4649
4650 try self.branch_stack.append(.{});
4651 errdefer {
4652 _ = self.branch_stack.pop();
4653 }
4654
4655 const else_deaths = liveness.deaths.len - 1;
4656 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len);
4657 for (liveness.deaths[else_deaths]) |operand| {
4658 self.processDeath(operand);
4659 }
4660 try self.genBody(else_body);
4661
4662 // Revert to the previous register and stack allocation state.
4663 var saved_case_branch = self.branch_stack.pop();
4664 defer saved_case_branch.deinit(self.gpa);
4665
4666 self.register_manager.registers = parent_registers;
4667 self.compare_flags_inst = parent_compare_flags_inst;
4668 self.stack.deinit(self.gpa);
4669 self.stack = parent_stack;
4670 parent_stack = .{};
4671
4672 self.next_stack_offset = parent_next_stack_offset;
4673 self.register_manager.free_registers = parent_free_registers;
4674
4675 // TODO consolidate returned MCValues between prongs and else branch like we do
4676 // in airCondBr.
4677 }
45244678
4525 return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch});4679 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
4526}4680}
45274681
4528fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {4682fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
...@@ -4551,7 +4705,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {...@@ -4551,7 +4705,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
4551 block_data.mcv = switch (operand_mcv) {4705 block_data.mcv = switch (operand_mcv) {
4552 .none, .dead, .unreach => unreachable,4706 .none, .dead, .unreach => unreachable,
4553 .register, .stack_offset, .memory => operand_mcv,4707 .register, .stack_offset, .memory => operand_mcv,
4554 .immediate, .stack_argument_offset, .condition_flags => blk: {4708 .immediate, .stack_argument_offset, .compare_flags => blk: {
4555 const new_mcv = try self.allocRegOrMem(self.air.typeOfIndex(block), true, block);4709 const new_mcv = try self.allocRegOrMem(self.air.typeOfIndex(block), true, block);
4556 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);4710 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
4557 break :blk new_mcv;4711 break :blk new_mcv;
...@@ -4734,7 +4888,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -4734,7 +4888,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
4734 else => return self.fail("TODO implement memset", .{}),4888 else => return self.fail("TODO implement memset", .{}),
4735 }4889 }
4736 },4890 },
4737 .condition_flags,4891 .compare_flags,
4738 .immediate,4892 .immediate,
4739 .ptr_stack_offset,4893 .ptr_stack_offset,
4740 => {4894 => {
...@@ -4894,7 +5048,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4894,7 +5048,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4894 } },5048 } },
4895 });5049 });
4896 },5050 },
4897 .condition_flags => |condition| {5051 .compare_flags => |condition| {
4898 _ = try self.addInst(.{5052 _ = try self.addInst(.{
4899 .tag = .cset,5053 .tag = .cset,
4900 .data = .{ .r_cond = .{5054 .data = .{ .r_cond = .{
...@@ -5171,7 +5325,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I...@@ -5171,7 +5325,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
5171 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);5325 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
5172 }5326 }
5173 },5327 },
5174 .condition_flags,5328 .compare_flags,
5175 .immediate,5329 .immediate,
5176 .ptr_stack_offset,5330 .ptr_stack_offset,
5177 => {5331 => {
test/behavior/switch.zig-25
...@@ -5,8 +5,6 @@ const expectError = std.testing.expectError;...@@ -5,8 +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_aarch64) return error.SkipZigTest; // TODO
9
10 try testSwitchWithNumbers(13);8 try testSwitchWithNumbers(13);
11}9}
1210
...@@ -20,8 +18,6 @@ fn testSwitchWithNumbers(x: u32) !void {...@@ -20,8 +18,6 @@ fn testSwitchWithNumbers(x: u32) !void {
20}18}
2119
22test "switch with all ranges" {20test "switch with all ranges" {
23 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
24
25 try expect(testSwitchWithAllRanges(50, 3) == 1);21 try expect(testSwitchWithAllRanges(50, 3) == 1);
26 try expect(testSwitchWithAllRanges(101, 0) == 2);22 try expect(testSwitchWithAllRanges(101, 0) == 2);
27 try expect(testSwitchWithAllRanges(300, 5) == 3);23 try expect(testSwitchWithAllRanges(300, 5) == 3);
...@@ -53,8 +49,6 @@ test "implicit comptime switch" {...@@ -53,8 +49,6 @@ test "implicit comptime switch" {
53}49}
5450
55test "switch on enum" {51test "switch on enum" {
56 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
57
58 const fruit = Fruit.Orange;52 const fruit = Fruit.Orange;
59 nonConstSwitchOnEnum(fruit);53 nonConstSwitchOnEnum(fruit);
60}54}
...@@ -72,8 +66,6 @@ fn nonConstSwitchOnEnum(fruit: Fruit) void {...@@ -72,8 +66,6 @@ fn nonConstSwitchOnEnum(fruit: Fruit) void {
72}66}
7367
74test "switch statement" {68test "switch statement" {
75 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
76
77 try nonConstSwitch(SwitchStatementFoo.C);69 try nonConstSwitch(SwitchStatementFoo.C);
78}70}
79fn nonConstSwitch(foo: SwitchStatementFoo) !void {71fn nonConstSwitch(foo: SwitchStatementFoo) !void {
...@@ -89,7 +81,6 @@ const SwitchStatementFoo = enum { A, B, C, D };...@@ -89,7 +81,6 @@ const SwitchStatementFoo = enum { A, B, C, D };
8981
90test "switch with multiple expressions" {82test "switch with multiple expressions" {
91 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO83 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
92 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9384
94 const x = switch (returnsFive()) {85 const x = switch (returnsFive()) {
95 1, 2, 3 => 1,86 1, 2, 3 => 1,
...@@ -103,8 +94,6 @@ fn returnsFive() i32 {...@@ -103,8 +94,6 @@ fn returnsFive() i32 {
103}94}
10495
105test "switch on type" {96test "switch on type" {
106 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
107
108 try expect(trueIfBoolFalseOtherwise(bool));97 try expect(trueIfBoolFalseOtherwise(bool));
109 try expect(!trueIfBoolFalseOtherwise(i32));98 try expect(!trueIfBoolFalseOtherwise(i32));
110}99}
...@@ -117,8 +106,6 @@ fn trueIfBoolFalseOtherwise(comptime T: type) bool {...@@ -117,8 +106,6 @@ fn trueIfBoolFalseOtherwise(comptime T: type) bool {
117}106}
118107
119test "switching on booleans" {108test "switching on booleans" {
120 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
121
122 try testSwitchOnBools();109 try testSwitchOnBools();
123 comptime try testSwitchOnBools();110 comptime try testSwitchOnBools();
124}111}
...@@ -170,8 +157,6 @@ test "undefined.u0" {...@@ -170,8 +157,6 @@ test "undefined.u0" {
170}157}
171158
172test "switch with disjoint range" {159test "switch with disjoint range" {
173 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
174
175 var q: u8 = 0;160 var q: u8 = 0;
176 switch (q) {161 switch (q) {
177 0...125 => {},162 0...125 => {},
...@@ -214,8 +199,6 @@ fn poll() void {...@@ -214,8 +199,6 @@ fn poll() void {
214}199}
215200
216test "switch on global mutable var isn't constant-folded" {201test "switch on global mutable var isn't constant-folded" {
217 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
218
219 while (state < 2) {202 while (state < 2) {
220 poll();203 poll();
221 }204 }
...@@ -273,7 +256,6 @@ fn testSwitchEnumPtrCapture() !void {...@@ -273,7 +256,6 @@ fn testSwitchEnumPtrCapture() !void {
273256
274test "switch handles all cases of number" {257test "switch handles all cases of number" {
275 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO258 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
276 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
277259
278 try testSwitchHandleAllCases();260 try testSwitchHandleAllCases();
279 comptime try testSwitchHandleAllCases();261 comptime try testSwitchHandleAllCases();
...@@ -363,8 +345,6 @@ test "anon enum literal used in switch on union enum" {...@@ -363,8 +345,6 @@ test "anon enum literal used in switch on union enum" {
363}345}
364346
365test "switch all prongs unreachable" {347test "switch all prongs unreachable" {
366 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
367
368 try testAllProngsUnreachable();348 try testAllProngsUnreachable();
369 comptime try testAllProngsUnreachable();349 comptime try testAllProngsUnreachable();
370}350}
...@@ -400,7 +380,6 @@ fn return_a_number() anyerror!i32 {...@@ -400,7 +380,6 @@ fn return_a_number() anyerror!i32 {
400380
401test "switch on integer with else capturing expr" {381test "switch on integer with else capturing expr" {
402 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO382 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
403 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
404383
405 const S = struct {384 const S = struct {
406 fn doTheTest() !void {385 fn doTheTest() !void {
...@@ -641,8 +620,6 @@ test "switch capture copies its payload" {...@@ -641,8 +620,6 @@ test "switch capture copies its payload" {
641}620}
642621
643test "capture of integer forwards the switch condition directly" {622test "capture of integer forwards the switch condition directly" {
644 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
645
646 const S = struct {623 const S = struct {
647 fn foo(x: u8) !void {624 fn foo(x: u8) !void {
648 switch (x) {625 switch (x) {
...@@ -662,8 +639,6 @@ test "capture of integer forwards the switch condition directly" {...@@ -662,8 +639,6 @@ test "capture of integer forwards the switch condition directly" {
662}639}
663640
664test "enum value without tag name used as switch item" {641test "enum value without tag name used as switch item" {
665 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
666
667 const E = enum(u32) {642 const E = enum(u32) {
668 a = 1,643 a = 1,
669 b = 2,644 b = 2,