From 0fc79d602bf9b3a5c97cfc28b59193b005692cb2 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Wed, 27 Jul 2022 20:24:33 +0200 Subject: [PATCH] stage2 ARM: more support for switch statements --- src/arch/arm/CodeGen.zig | 59 ++++++++++++++++++++++------------------ test/behavior/switch.zig | 9 ------ 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 8bd589e7ba..93d98c41d3 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4300,17 +4300,6 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { ); defer self.gpa.free(liveness.deaths); - // If the condition dies here in this switch instruction, process - // that death now instead of later as this has an effect on - // whether it needs to be spilled in the branches - if (self.liveness.operandDies(inst, 0)) { - const op_int = @enumToInt(pl_op.operand); - if (op_int >= Air.Inst.Ref.typed_value_map.len) { - const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); - self.processDeath(op_index); - } - } - var extra_index: usize = switch_br.end; var case_i: u32 = 0; while (case_i < switch_br.data.cases_len) : (case_i += 1) { @@ -4320,21 +4309,43 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + items.len + case_body.len; - var relocs = try self.gpa.alloc(u32, items.len); - defer self.gpa.free(relocs); + // For every item, we compare it to condition and branch into + // the prong if they are equal. After we compared to all + // items, we branch into the next prong (or if no other prongs + // exist out of the switch statement). + // + // cmp condition, item1 + // beq prong + // cmp condition, item2 + // beq prong + // cmp condition, item3 + // beq prong + // b out + // prong: ... + // ... + // out: ... + const branch_into_prong_relocs = try self.gpa.alloc(u32, items.len); + defer self.gpa.free(branch_into_prong_relocs); - if (items.len == 1) { + for (items) |item, idx| { const condition = try self.resolveInst(pl_op.operand); - const item = try self.resolveInst(items[0]); + const item_mcv = try self.resolveInst(item); const operands: BinOpOperands = .{ .mcv = .{ .lhs = condition, - .rhs = item, + .rhs = item_mcv, } }; - const cmp_result = try self.cmp(operands, condition_ty, .eq); - relocs[0] = try self.condBr(cmp_result); - } else { - return self.fail("TODO switch with multiple items", .{}); + const cmp_result = try self.cmp(operands, condition_ty, .neq); + branch_into_prong_relocs[idx] = try self.condBr(cmp_result); + } + + const branch_away_from_prong_reloc = try self.addInst(.{ + .tag = .b, + .data = .{ .inst = undefined }, // populated later through performReloc + }); + + for (branch_into_prong_relocs) |reloc| { + try self.performReloc(reloc); } // Capture the state of register and stack allocation state so that we can revert to it. @@ -4369,9 +4380,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { self.next_stack_offset = parent_next_stack_offset; self.register_manager.free_registers = parent_free_registers; - for (relocs) |reloc| { - try self.performReloc(reloc); - } + try self.performReloc(branch_away_from_prong_reloc); } if (switch_br.data.else_body_len > 0) { @@ -4414,9 +4423,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { // in airCondBr. } - // We already took care of pl_op.operand earlier, so we're going - // to pass .none here - return self.finishAir(inst, .unreach, .{ .none, .none, .none }); + return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); } fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 3a49c03b18..4e86bcadeb 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -53,7 +53,6 @@ test "implicit comptime switch" { } test "switch on enum" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const fruit = Fruit.Orange; @@ -73,7 +72,6 @@ fn nonConstSwitchOnEnum(fruit: Fruit) void { } test "switch statement" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try nonConstSwitch(SwitchStatementFoo.C); @@ -91,7 +89,6 @@ const SwitchStatementFoo = enum { A, B, C, D }; test "switch with multiple expressions" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const x = switch (returnsFive()) { @@ -120,7 +117,6 @@ fn trueIfBoolFalseOtherwise(comptime T: type) bool { } test "switching on booleans" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testSwitchOnBools(); @@ -218,7 +214,6 @@ fn poll() void { } test "switch on global mutable var isn't constant-folded" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO while (state < 2) { @@ -278,7 +273,6 @@ fn testSwitchEnumPtrCapture() !void { test "switch handles all cases of number" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testSwitchHandleAllCases(); @@ -370,7 +364,6 @@ test "anon enum literal used in switch on union enum" { } test "switch all prongs unreachable" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testAllProngsUnreachable(); @@ -582,7 +575,6 @@ test "switch on pointer type" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { const X = struct { @@ -674,7 +666,6 @@ test "capture of integer forwards the switch condition directly" { } test "enum value without tag name used as switch item" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const E = enum(u32) {