Commit fd781195de

Koakuma <koachan@protonmail.com>
2022-05-02 14:55:51
stage2: sparc64: Split the conditionals between integer and FP ones
On SPARCv9 the integer and FP conditional branch codes doesn't align with each other at all, so the two need to be treated separately.
1 parent 662a61f
Changed files (3)
src/arch/sparc64/bits.zig
@@ -512,10 +512,172 @@ pub const Instruction = union(enum) {
         lookaside: bool = false,
     };
 
-    // TODO: Need to define an enum for `cond` values
-    // This is kinda challenging since the cond values have different meanings
-    // depending on whether it's operating on integer or FP CCR.
-    pub const Condition = u4;
+    // In SPARCv9, FP and integer comparison operations
+    // are encoded differently.
+
+    pub const FCondition = enum(u4) {
+        /// Branch Never
+        nv,
+        /// Branch on Not Equal
+        ne,
+        /// Branch on Less or Greater
+        lg,
+        /// Branch on Unordered or Less
+        ul,
+        /// Branch on Less
+        lt,
+        /// Branch on Unordered or Greater
+        ug,
+        /// Branch on Greater
+        gt,
+        /// Branch on Unordered
+        un,
+        /// Branch Always
+        al,
+        /// Branch on Equal
+        eq,
+        /// Branch on Unordered or Equal
+        ue,
+        /// Branch on Greater or Equal
+        ge,
+        /// Branch on Unordered or Greater or Equal
+        uge,
+        /// Branch on Less or Equal
+        le,
+        /// Branch on Unordered or Less or Equal
+        ule,
+        /// Branch on Ordered
+        ord,
+
+        /// 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.
+        pub fn fromCompareOperator(op: std.math.CompareOperator) FCondition {
+            return switch (op) {
+                .gte => .ge,
+                .gt => .gt,
+                .neq => .ne,
+                .lt => .lt,
+                .lte => .le,
+                .eq => .eq,
+            };
+        }
+
+        /// Returns the condition which is true iff the given condition is
+        /// false (if such a condition exists).
+        pub fn negate(cond: FCondition) FCondition {
+            return switch (cond) {
+                .eq => .ne,
+                .ne => .eq,
+                .ge => .ul,
+                .ul => .ge,
+                .le => .ug,
+                .ug => .le,
+                .lt => .uge,
+                .uge => .lt,
+                .gt => .ule,
+                .ule => .gt,
+                .ue => .lg,
+                .lg => .ue,
+                .ord => .un,
+                .un => .ord,
+                .al => unreachable,
+                .nv => unreachable,
+            };
+        }
+    };
+
+    pub const ICondition = enum(u4) {
+        /// Branch Never
+        nv,
+        /// Branch on Equal
+        eq,
+        /// Branch on Less or Equal
+        le,
+        /// Branch on Less
+        lt,
+        /// Branch on Less or Equal Unsigned
+        leu,
+        /// Branch on Carry Set (Less than, Unsigned)
+        cs,
+        /// Branch on Negative
+        neg,
+        /// Branch on Overflow Set
+        vs,
+        /// Branch Always
+        al,
+        /// Branch on Not Equal
+        ne,
+        /// Branch on Greater
+        gt,
+        /// Branch on Greater or Equal
+        ge,
+        /// Branch on Greater Unsigned
+        gu,
+        /// Branch on Carry Clear (Greater Than or Equal, Unsigned)
+        cc,
+        /// Branch on Positive
+        pos,
+        /// Branch on Overflow Clear
+        vc,
+
+        /// 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) ICondition {
+            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) ICondition {
+            return switch (op) {
+                .gte => .cc,
+                .gt => .gu,
+                .neq => .ne,
+                .lt => .cs,
+                .lte => .le,
+                .eq => .eq,
+            };
+        }
+
+        /// Returns the condition which is true iff the given condition is
+        /// false (if such a condition exists).
+        pub fn negate(cond: ICondition) ICondition {
+            return switch (cond) {
+                .eq => .ne,
+                .ne => .eq,
+                .cs => .cc,
+                .cc => .cs,
+                .neg => .pos,
+                .pos => .neg,
+                .vs => .vc,
+                .vc => .vs,
+                .gu => .leu,
+                .leu => .gu,
+                .ge => .lt,
+                .lt => .ge,
+                .gt => .le,
+                .le => .gt,
+                .al => unreachable,
+                .nv => unreachable,
+            };
+        }
+    };
+
+    pub const Condition = packed union {
+        fcond: FCondition,
+        icond: ICondition,
+        encoded: u4,
+    };
 
     pub fn toU32(self: Instruction) u32 {
         // TODO: Remove this once packed structs work.
@@ -593,7 +755,7 @@ pub const Instruction = union(enum) {
         return Instruction{
             .format_2b = .{
                 .a = @boolToInt(annul),
-                .cond = cond,
+                .cond = cond.encoded,
                 .op2 = op2,
                 .disp22 = udisp_truncated,
             },
@@ -614,7 +776,7 @@ pub const Instruction = union(enum) {
         return Instruction{
             .format_2c = .{
                 .a = @boolToInt(annul),
-                .cond = cond,
+                .cond = cond.encoded,
                 .op2 = op2,
                 .cc1 = ccr_cc1,
                 .cc0 = ccr_cc0,
@@ -895,7 +1057,7 @@ pub const Instruction = union(enum) {
                 .rd = rd.enc(),
                 .op3 = op3,
                 .cc2 = ccr_cc2,
-                .cond = cond,
+                .cond = cond.encoded,
                 .cc1 = ccr_cc1,
                 .cc0 = ccr_cc0,
                 .rs2 = rs2.enc(),
@@ -912,7 +1074,7 @@ pub const Instruction = union(enum) {
                 .rd = rd.enc(),
                 .op3 = op3,
                 .cc2 = ccr_cc2,
-                .cond = cond,
+                .cond = cond.encoded,
                 .cc1 = ccr_cc1,
                 .cc0 = ccr_cc0,
                 .simm11 = @bitCast(u11, imm),
@@ -960,7 +1122,7 @@ pub const Instruction = union(enum) {
             .format_4g = .{
                 .rd = rd.enc(),
                 .op3 = op3,
-                .cond = cond,
+                .cond = cond.encoded,
                 .opf_cc = opf_cc,
                 .opf_low = opf_low,
                 .rs2 = rs2.enc(),
@@ -1099,11 +1261,11 @@ pub const Instruction = union(enum) {
         };
     }
 
-    pub fn trap(comptime s2: type, cond: Condition, ccr: CCR, rs1: Register, rs2: s2) Instruction {
+    pub fn trap(comptime s2: type, cond: ICondition, ccr: CCR, rs1: Register, rs2: s2) Instruction {
         // Tcc instructions abuse the rd field to store the conditionals.
         return switch (s2) {
-            Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)),
-            u7 => format4e(0b11_1010, ccr, rs1, @intToEnum(Register, cond), rs2),
+            Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, @enumToInt(cond))),
+            u7 => format4e(0b11_1010, ccr, rs1, @intToEnum(Register, @enumToInt(cond)), rs2),
             else => unreachable,
         };
     }
@@ -1128,11 +1290,11 @@ test "Serialize formats" {
             .expected = 0b00_00000_100_0000000000000000000000,
         },
         .{
-            .inst = Instruction.format2b(6, 3, true, -4),
+            .inst = Instruction.format2b(6, .{ .icond = .lt }, true, -4),
             .expected = 0b00_1_0011_110_1111111111111111111111,
         },
         .{
-            .inst = Instruction.format2c(3, 0, false, true, .xcc, 8),
+            .inst = Instruction.format2c(3, .{ .icond = .nv }, false, true, .xcc, 8),
             .expected = 0b00_0_0000_011_1_0_1_0000000000000000010,
         },
         .{
@@ -1224,11 +1386,11 @@ test "Serialize formats" {
             .expected = 0b10_10010_001000_00000_1_1_0_11111111111,
         },
         .{
-            .inst = Instruction.format4c(8, 0, .xcc, .g0, .o1),
+            .inst = Instruction.format4c(8, .{ .icond = .nv }, .xcc, .g0, .o1),
             .expected = 0b10_01001_001000_1_0000_0_1_0_000000_00000,
         },
         .{
-            .inst = Instruction.format4d(8, 0, .xcc, 0, .l2),
+            .inst = Instruction.format4d(8, .{ .icond = .nv }, .xcc, 0, .l2),
             .expected = 0b10_10010_001000_1_0000_1_1_0_00000000000,
         },
         .{
@@ -1240,7 +1402,7 @@ test "Serialize formats" {
             .expected = 0b10_10010_001000_00000_0_001_00100_01001,
         },
         .{
-            .inst = Instruction.format4g(8, 4, 2, 0, .o1, .l2),
+            .inst = Instruction.format4g(8, 4, 2, .{ .icond = .nv }, .o1, .l2),
             .expected = 0b10_10010_001000_0_0000_010_000100_01001,
         },
     };
src/arch/sparc64/CodeGen.zig
@@ -725,7 +725,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
                 .data = .{
                     .trap = .{
                         .is_imm = true,
-                        .cond = 0b1000, // TODO need to look into changing this into an enum
+                        .cond = .al,
                         .rs2_or_imm = .{ .imm = 0x6d },
                     },
                 },
@@ -842,7 +842,7 @@ fn airBreakpoint(self: *Self) !void {
         .data = .{
             .trap = .{
                 .is_imm = true,
-                .cond = 0b1000, // TODO need to look into changing this into an enum
+                .cond = .al,
                 .rs2_or_imm = .{ .imm = 0x01 },
             },
         },
@@ -1648,7 +1648,7 @@ fn parseRegName(name: []const u8) ?Register {
 fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
     const tag = self.mir_instructions.items(.tag)[inst];
     switch (tag) {
-        .bpcc => self.mir_instructions.items(.data)[inst].branch_predict.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len),
+        .bpcc => self.mir_instructions.items(.data)[inst].branch_predict_int.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len),
         else => unreachable,
     }
 }
src/arch/sparc64/Mir.zig
@@ -44,7 +44,7 @@ pub const Inst = struct {
         add,
 
         /// A.7 Branch on Integer Condition Codes with Prediction (BPcc)
-        /// This uses the branch_predict field.
+        /// This uses the branch_predict_int field.
         bpcc,
 
         /// A.8 Call and Link
@@ -165,13 +165,13 @@ pub const Inst = struct {
             link: Register = .o7,
         },
 
-        /// Branch with prediction.
+        /// Branch with prediction, checking the integer status code
         /// Used by e.g. bpcc
-        branch_predict: struct {
+        branch_predict_int: struct {
             annul: bool = false,
             pt: bool = true,
             ccr: Instruction.CCR,
-            cond: Instruction.Condition,
+            cond: Instruction.ICondition,
             inst: Index,
         },
 
@@ -211,7 +211,7 @@ pub const Inst = struct {
         /// Used by e.g. tcc
         trap: struct {
             is_imm: bool = true,
-            cond: Instruction.Condition,
+            cond: Instruction.ICondition,
             ccr: Instruction.CCR = .icc,
             rs1: Register = .g0,
             rs2_or_imm: union {