Commit a5a012e859

joachimschmidt557 <joachim.schmidt557@outlook.com>
2021-11-07 17:57:21
stage2 AArch64: implement genSetReg for condition flags
1 parent 8cb0051
Changed files (4)
src/arch/aarch64/bits.zig
@@ -321,6 +321,17 @@ pub const Instruction = union(enum) {
         fixed: u6 = 0b011010,
         sf: u1,
     },
+    conditional_select: struct {
+        rd: u5,
+        rn: u5,
+        op2: u2,
+        cond: u4,
+        rm: u5,
+        fixed: u8 = 0b11010100,
+        s: u1,
+        op: u1,
+        sf: u1,
+    },
 
     pub const Shift = struct {
         shift: Type = .lsl,
@@ -388,6 +399,57 @@ pub const Instruction = union(enum) {
         /// Integer: Always
         /// Floating point: Always
         nv,
+
+        /// Converts a std.math.CompareOperator into a condition flag,
+        /// i.e. returns the condition that is true iff the result of the
+        /// comparison is true. Assumes signed comparison
+        pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
+            return switch (op) {
+                .gte => .ge,
+                .gt => .gt,
+                .neq => .ne,
+                .lt => .lt,
+                .lte => .le,
+                .eq => .eq,
+            };
+        }
+
+        /// Converts a std.math.CompareOperator into a condition flag,
+        /// i.e. returns the condition that is true iff the result of the
+        /// comparison is true. Assumes unsigned comparison
+        pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
+            return switch (op) {
+                .gte => .cs,
+                .gt => .hi,
+                .neq => .ne,
+                .lt => .cc,
+                .lte => .ls,
+                .eq => .eq,
+            };
+        }
+
+        /// Returns the condition which is true iff the given condition is
+        /// false (if such a condition exists)
+        pub fn negate(cond: Condition) Condition {
+            return switch (cond) {
+                .eq => .ne,
+                .ne => .eq,
+                .cs => .cc,
+                .cc => .cs,
+                .mi => .pl,
+                .pl => .mi,
+                .vs => .vc,
+                .vc => .vs,
+                .hi => .ls,
+                .ls => .hi,
+                .ge => .lt,
+                .lt => .ge,
+                .gt => .le,
+                .le => .gt,
+                .al => unreachable,
+                .nv => unreachable,
+            };
+        }
     };
 
     pub fn toU32(self: Instruction) u32 {
@@ -407,6 +469,7 @@ pub const Instruction = union(enum) {
             // TODO once packed structs work, this can be refactored
             .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25),
             .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31),
+            .conditional_select => |v| @as(u32, v.rd) | @as(u32, v.rn) << 5 | @as(u32, v.op2) << 10 | @as(u32, v.cond) << 12 | @as(u32, v.rm) << 16 | @as(u32, v.fixed) << 21 | @as(u32, v.s) << 29 | @as(u32, v.op) << 30 | @as(u32, v.sf) << 31,
         };
     }
 
@@ -883,6 +946,33 @@ pub const Instruction = union(enum) {
         };
     }
 
+    fn conditionalSelect(
+        op2: u2,
+        op: u1,
+        s: u1,
+        rd: Register,
+        rn: Register,
+        rm: Register,
+        cond: Condition,
+    ) Instruction {
+        return Instruction{
+            .conditional_select = .{
+                .rd = rd.id(),
+                .rn = rn.id(),
+                .op2 = op2,
+                .cond = @enumToInt(cond),
+                .rm = rm.id(),
+                .s = s,
+                .op = op,
+                .sf = switch (rd.size()) {
+                    32 => 0b0,
+                    64 => 0b1,
+                    else => unreachable, // unexpected register size
+                },
+            },
+        };
+    }
+
     // Helper functions for assembly syntax functions
 
     // Move wide (immediate)
@@ -1154,6 +1244,24 @@ pub const Instruction = union(enum) {
     pub fn cbnz(rt: Register, offset: i21) Instruction {
         return compareAndBranch(0b1, rt, offset);
     }
+
+    // Conditional select
+
+    pub fn csel(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
+        return conditionalSelect(0b00, 0b0, 0b0, rd, rn, rm, cond);
+    }
+
+    pub fn csinc(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
+        return conditionalSelect(0b01, 0b0, 0b0, rd, rn, rm, cond);
+    }
+
+    pub fn csinv(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
+        return conditionalSelect(0b00, 0b1, 0b0, rd, rn, rm, cond);
+    }
+
+    pub fn csneg(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
+        return conditionalSelect(0b01, 0b1, 0b0, rd, rn, rm, cond);
+    }
 };
 
 test {
@@ -1319,6 +1427,10 @@ test "serialize instructions" {
             .inst = Instruction.addShiftedRegister(.x0, .x1, .x2, .lsl, 5),
             .expected = 0b1_0_0_01011_00_0_00010_000101_00001_00000,
         },
+        .{ // csinc x1, x2, x4, eq
+            .inst = Instruction.csinc(.x1, .x2, .x4, .eq),
+            .expected = 0b1_0_0_11010100_00100_0000_0_1_00010_00001,
+        },
     };
 
     for (testcases) |case| {
src/arch/aarch64/CodeGen.zig
@@ -2182,6 +2182,25 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                 else => unreachable, // unexpected register size
             }
         },
+        .compare_flags_unsigned,
+        .compare_flags_signed,
+        => |op| {
+            const condition = switch (mcv) {
+                .compare_flags_unsigned => Instruction.Condition.fromCompareOperatorUnsigned(op),
+                .compare_flags_signed => Instruction.Condition.fromCompareOperatorSigned(op),
+                else => unreachable,
+            };
+
+            _ = try self.addInst(.{
+                .tag = .cset,
+                .data = .{ .rrr_cond = .{
+                    .rd = reg,
+                    .rn = .xzr,
+                    .rm = .xzr,
+                    .cond = condition,
+                } },
+            });
+        },
         .immediate => |x| {
             _ = try self.addInst(.{
                 .tag = .movz,
src/arch/aarch64/Emit.zig
@@ -81,6 +81,8 @@ pub fn emitMir(
 
             .cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
 
+            .cset => try emit.mirConditionalSelect(inst),
+
             .dbg_line => try emit.mirDbgLine(inst),
 
             .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
@@ -478,6 +480,21 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
     }
 }
 
+fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void {
+    const tag = emit.mir.instructions.items(.tag)[inst];
+    const rrr_cond = emit.mir.instructions.items(.data)[inst].rrr_cond;
+
+    switch (tag) {
+        .cset => try emit.writeInstruction(Instruction.csinc(
+            rrr_cond.rd,
+            rrr_cond.rn,
+            rrr_cond.rm,
+            rrr_cond.cond,
+        )),
+        else => unreachable,
+    }
+}
+
 fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void {
     assert(emit.mir.instructions.items(.tag)[inst] == .load_memory);
     const payload = emit.mir.instructions.items(.data)[inst].payload;
src/arch/aarch64/Mir.zig
@@ -40,6 +40,8 @@ pub const Inst = struct {
         cmp_immediate,
         /// Compare (shifted register)
         cmp_shifted_register,
+        /// Conditional set
+        cset,
         /// Pseudo-instruction: End of prologue
         dbg_prologue_end,
         /// Pseudo-instruction: Beginning of epilogue
@@ -155,6 +157,15 @@ pub const Inst = struct {
             imm6: u6,
             shift: bits.Instruction.AddSubtractShiftedRegisterShift,
         },
+        /// Three registers and a condition
+        ///
+        /// Used by e.g. cset
+        rrr_cond: struct {
+            rd: Register,
+            rn: Register,
+            rm: Register,
+            cond: bits.Instruction.Condition,
+        },
         /// Three registers and a LoadStoreOffset
         ///
         /// Used by e.g. str_register