authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-06-03 15:52:03+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-06-04 19:58:34+02:00
logd5ee45117722c1685f4b739686d74ccc7cf5b8d9
tree7f6f7bb2390fc52578883515ad4277a536bece03
parent4fdacca51231668b72bf302ce97335d2a8d93128
signaturelock-open Commit is signed but in an unrecognized format.

stage2 ARM: introduce support for basic switch expressions


2 files changed, 45 insertions(+), 37 deletions(-)

src/arch/arm/CodeGen.zig+45-31
...@@ -953,15 +953,6 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {...@@ -953,15 +953,6 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
953 return reg;953 return reg;
954}954}
955955
956/// Allocates a new register and copies `mcv` into it.
957/// `reg_owner` is the instruction that gets associated with the register in the register table.
958/// This can have a side effect of spilling instructions to the stack to free up a register.
959fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
960 const reg = try self.register_manager.allocReg(reg_owner, gp);
961 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
962 return MCValue{ .register = reg };
963}
964
965fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {956fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
966 const stack_offset = try self.allocMemPtr(inst);957 const stack_offset = try self.allocMemPtr(inst);
967 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 });
...@@ -2185,6 +2176,9 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind...@@ -2185,6 +2176,9 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind
2185 .stack_offset => |off| {2176 .stack_offset => |off| {
2186 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });2177 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
2187 },2178 },
2179 .cpsr_flags => {
2180 log.debug("%{d} => cpsr_flags (reused)", .{inst});
2181 },
2188 else => return false,2182 else => return false,
2189 }2183 }
21902184
...@@ -2487,7 +2481,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2487,7 +2481,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2487 else => unreachable,2481 else => unreachable,
2488 };2482 };
24892483
2490 if (self.liveness.operandDies(inst, 0)) {2484 if (self.reuseOperand(inst, operand, 0, field)) {
2491 break :result field;2485 break :result field;
2492 } else {2486 } else {
2493 // Copy to new register2487 // Copy to new register
...@@ -2511,6 +2505,41 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2511,6 +2505,41 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
2511 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 });
2512}2506}
25132507
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
2514/// Don't call this function directly. Use binOp instead.2543/// Don't call this function directly. Use binOp instead.
2515///2544///
2516/// Calling this function signals an intention to generate a Mir2545/// Calling this function signals an intention to generate a Mir
...@@ -2537,18 +2566,12 @@ fn binOpRegister(...@@ -2537,18 +2566,12 @@ fn binOpRegister(
2537 null;2566 null;
2538 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);2567 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
25392568
2540 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2541
2542 const lhs_reg = if (lhs_is_register) lhs.register else blk: {2569 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2543 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {2570 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
2544 break :inst Air.refToIndex(md.lhs).?;2571 break :inst Air.refToIndex(md.lhs).?;
2545 } else null;2572 } else null;
25462573
2547 const reg = try self.register_manager.allocReg(track_inst, gp);2574 break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs);
2548
2549 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
2550
2551 break :blk reg;
2552 };2575 };
2553 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);2576 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
2554 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);2577 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
...@@ -2558,11 +2581,7 @@ fn binOpRegister(...@@ -2558,11 +2581,7 @@ fn binOpRegister(
2558 break :inst Air.refToIndex(md.rhs).?;2581 break :inst Air.refToIndex(md.rhs).?;
2559 } else null;2582 } else null;
25602583
2561 const reg = try self.register_manager.allocReg(track_inst, gp);2584 break :blk try self.prepareNewRegForMoving(track_inst, gp, rhs);
2562
2563 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
2564
2565 break :blk reg;
2566 };2585 };
2567 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);2586 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
2568 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);2587 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
...@@ -2652,8 +2671,6 @@ fn binOpImmediate(...@@ -2652,8 +2671,6 @@ fn binOpImmediate(
2652 null;2671 null;
2653 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);2672 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
26542673
2655 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2656
2657 const lhs_reg = if (lhs_is_register) lhs.register else blk: {2674 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2658 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {2675 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
2659 break :inst Air.refToIndex(2676 break :inst Air.refToIndex(
...@@ -2661,11 +2678,7 @@ fn binOpImmediate(...@@ -2661,11 +2678,7 @@ fn binOpImmediate(
2661 ).?;2678 ).?;
2662 } else null;2679 } else null;
26632680
2664 const reg = try self.register_manager.allocReg(track_inst, gp);2681 break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs);
2665
2666 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
2667
2668 break :blk reg;
2669 };2682 };
2670 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);2683 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
2671 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);2684 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
...@@ -3444,7 +3457,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3444,7 +3457,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3444 if (RegisterManager.indexOfRegIntoTracked(reg) == null) {3457 if (RegisterManager.indexOfRegIntoTracked(reg) == null) {
3445 // Save function return value into a tracked register3458 // Save function return value into a tracked register
3446 log.debug("airCall: copying {} as it is not tracked", .{reg});3459 log.debug("airCall: copying {} as it is not tracked", .{reg});
3447 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 };
3448 }3462 }
3449 },3463 },
3450 else => {},3464 else => {},
...@@ -4124,7 +4138,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -4124,7 +4138,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
4124 var case_i: u32 = 0;4138 var case_i: u32 = 0;
4125 while (case_i < switch_br.data.cases_len) : (case_i += 1) {4139 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
4126 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);4140 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]);4141 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]);
4128 assert(items.len > 0);4142 assert(items.len > 0);
4129 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];4143 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;4144 extra_index = case.end + items.len + case_body.len;
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 {