diff options
| author | joachimschmidt557 <joachim.schmidt557@outlook.com> | 2021-11-07 17:57:21 +0100 |
|---|---|---|
| committer | joachimschmidt557 <joachim.schmidt557@outlook.com> | 2021-11-10 19:48:16 +0100 |
| commit | a5a012e8599e57da3e80ff770f9de492037e4f4a (patch) | |
| tree | 86fb5332cb15054858b25900829344678d901e69 /src/arch | |
| parent | 8cb00519cddadae8728d2b2e51a36da71d5bfe67 (diff) | |
| download | zig-a5a012e8599e57da3e80ff770f9de492037e4f4a.tar.gz zig-a5a012e8599e57da3e80ff770f9de492037e4f4a.zip | |
stage2 AArch64: implement genSetReg for condition flags
Diffstat (limited to 'src/arch')
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 19 | ||||
| -rw-r--r-- | src/arch/aarch64/Emit.zig | 17 | ||||
| -rw-r--r-- | src/arch/aarch64/Mir.zig | 11 | ||||
| -rw-r--r-- | src/arch/aarch64/bits.zig | 112 |
4 files changed, 159 insertions, 0 deletions
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 4d56e592ef..dba0c86552 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/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, diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index b5ca0686a1..0354c1f4d4 100644 --- a/src/arch/aarch64/Emit.zig +++ b/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; diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 4bbce48d5b..af102bd2c7 100644 --- a/src/arch/aarch64/Mir.zig +++ b/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 diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index 740cbdd3de..26974a3b6a 100644 --- a/src/arch/aarch64/bits.zig +++ b/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| { |
