From 4d2919a1eed3a916f87cc5f838c9cd2b23ddef70 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 27 Dec 2020 18:56:33 +0100 Subject: stage2 ARM: implement genCondBr --- src/codegen.zig | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index f978115ebc..a0e7968b97 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -42,6 +42,11 @@ pub const Reloc = union(enum) { /// To perform the reloc, write 32-bit signed little-endian integer /// which is a relative jump, based on the address following the reloc. rel32: usize, + /// A branch in the ARM instruction set + arm_branch: struct { + pos: usize, + cond: @import("codegen/arm.zig").Condition, + }, }; pub const Result = union(enum) { @@ -653,7 +658,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const mcv = try self.genFuncInst(inst); if (!inst.isUnused()) { - log.debug("{*} => {}", .{ inst, mcv }); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.putNoClobber(self.gpa, inst, mcv); } @@ -2016,6 +2020,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { self.code.items.len += 4; break :reloc reloc; }, + .arm, .armeb => reloc: { + const condition: Condition = switch (cond) { + .register => |reg| blk: { + // cmp reg, 1 + // bne ... + const op = Instruction.Operand.imm(1, 0); + writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32()); + break :blk .ne; + }, + else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }), + }; + + const reloc = Reloc{ + .arm_branch = .{ + .pos = self.code.items.len, + .cond = condition, + }, + }; + try self.code.resize(self.code.items.len + 4); + break :reloc reloc; + }, else => return self.fail(inst.base.src, "TODO implement condbr {}", .{self.target.cpu.arch}), }; @@ -2225,6 +2250,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(src, "unable to perform relocation: jump too far", .{}); mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt); }, + .arm_branch => |info| { + switch (arch) { + .arm, .armeb => { + const amt = self.code.items.len - (info.pos + 4); + if (math.cast(i26, amt)) |delta| { + writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32()); + } else |_| { + return self.fail(src, "TODO: enable larger branch offset", .{}); + } + }, + else => unreachable, // attempting to perfrom an ARM relocation on a non-ARM target arch + } + }, } } @@ -2278,6 +2316,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // Leave the jump offset undefined block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 }); }, + .arm, .armeb => { + try self.code.resize(self.code.items.len + 4); + block.codegen.relocs.appendAssumeCapacity(.{ + .arm_branch = .{ + .pos = self.code.items.len - 4, + .cond = .al, + }, + }); + }, else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}), } return .none; -- cgit v1.2.3 From 85e1b47c40c023d8a3d75fadaeeb643a361bbc4d Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 28 Dec 2020 18:17:22 +0100 Subject: stage2 ARM: implement genCondBr for compare_flags --- src/codegen.zig | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index a0e7968b97..4a02a8ffc0 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1277,11 +1277,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (op) { .add => { - // TODO runtime safety checks (overflow) writeInt(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, dst_reg, operand).toU32()); }, .sub => { - // TODO runtime safety checks (underflow) if (lhs_is_dest) { writeInt(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, dst_reg, operand).toU32()); } else { @@ -1297,6 +1295,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .not, .xor => { writeInt(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, dst_reg, operand).toU32()); }, + .cmp_eq => { + writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, dst_reg, operand).toU32()); + }, else => unreachable, // not a binary instruction } } @@ -1957,6 +1958,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .unsigned => MCValue{ .compare_flags_unsigned = op }, }; }, + .arm, .armeb => { + const lhs = try self.resolveInst(inst.lhs); + const rhs = try self.resolveInst(inst.rhs); + + const src_mcv = rhs; + const dst_mcv = if (lhs != .register) try self.copyToNewRegister(&inst.base, lhs) else lhs; + + try self.genArmBinOpCode(inst.base.src, dst_mcv.register, src_mcv, true, .cmp_eq); + const info = inst.lhs.ty.intInfo(self.target.*); + return switch (info.signedness) { + .signed => MCValue{ .compare_flags_signed = op }, + .unsigned => MCValue{ .compare_flags_unsigned = op }, + }; + }, else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}), } } @@ -2022,6 +2037,30 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, .arm, .armeb => reloc: { const condition: Condition = switch (cond) { + .compare_flags_signed => |cmp_op| blk: { + // Here we map to the opposite condition because the jump is to the false branch. + const condition: Condition = switch (cmp_op) { + .gte => .lt, + .gt => .le, + .neq => .eq, + .lt => .ge, + .lte => .gt, + .eq => .ne, + }; + break :blk condition; + }, + .compare_flags_unsigned => |cmp_op| blk: { + // Here we map to the opposite condition because the jump is to the false branch. + const condition: Condition = switch (cmp_op) { + .gte => .cc, + .gt => .ls, + .neq => .eq, + .lt => .cs, + .lte => .hi, + .eq => .ne, + }; + break :blk condition; + }, .register => |reg| blk: { // cmp reg, 1 // bne ... @@ -2253,7 +2292,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .arm_branch => |info| { switch (arch) { .arm, .armeb => { - const amt = self.code.items.len - (info.pos + 4); + const amt = @intCast(i32, self.code.items.len) - @intCast(i32, info.pos + 8); if (math.cast(i26, amt)) |delta| { writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32()); } else |_| { -- cgit v1.2.3 From c52ca0b1780c2865cb0c242cb2f1a397766e6ce8 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 28 Dec 2020 21:09:48 +0100 Subject: stage2 ARM: implement genSetReg with compare_flags --- src/codegen.zig | 41 ++++++++++++++++++--------------- src/codegen/arm.zig | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 19 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 4a02a8ffc0..66aa32b14e 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -658,6 +658,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const mcv = try self.genFuncInst(inst); if (!inst.isUnused()) { + log.debug("{*} => {}", .{ inst, mcv }); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.putNoClobber(self.gpa, inst, mcv); } @@ -2039,27 +2040,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const condition: Condition = switch (cond) { .compare_flags_signed => |cmp_op| blk: { // Here we map to the opposite condition because the jump is to the false branch. - const condition: Condition = switch (cmp_op) { - .gte => .lt, - .gt => .le, - .neq => .eq, - .lt => .ge, - .lte => .gt, - .eq => .ne, - }; - break :blk condition; + const condition = Condition.fromCompareOperatorSigned(cmp_op); + break :blk condition.negate(); }, .compare_flags_unsigned => |cmp_op| blk: { // Here we map to the opposite condition because the jump is to the false branch. - const condition: Condition = switch (cmp_op) { - .gte => .cc, - .gt => .ls, - .neq => .eq, - .lt => .cs, - .lte => .hi, - .eq => .ne, - }; - break :blk condition; + const condition = Condition.fromCompareOperatorUnsigned(cmp_op); + break :blk condition.negate(); }, .register => |reg| blk: { // cmp reg, 1 @@ -2239,7 +2226,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } }, .arm, .armeb => { - if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len))) |delta| { + if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| { writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32()); } else |err| { return self.fail(src, "TODO: enable larger branch offset", .{}); @@ -2736,6 +2723,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // Write the debug undefined value. return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }); }, + .compare_flags_unsigned, + .compare_flags_signed, + => |op| { + const condition = switch (mcv) { + .compare_flags_unsigned => Condition.fromCompareOperatorUnsigned(op), + .compare_flags_signed => Condition.fromCompareOperatorSigned(op), + else => unreachable, + }; + + // mov reg, 0 + // moveq reg, 1 + const zero = Instruction.Operand.imm(0, 0); + const one = Instruction.Operand.imm(1, 0); + writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, zero).toU32()); + writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32()); + }, .immediate => |x| { if (x > math.maxInt(u32)) return self.fail(src, "ARM registers are 32-bit wide", .{}); diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig index 33ff789648..978c653cb0 100644 --- a/src/codegen/arm.zig +++ b/src/codegen/arm.zig @@ -35,8 +35,74 @@ pub const Condition = enum(u4) { le, /// always al, + + /// 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, + }; + } }; +test "condition from CompareOperator" { + testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq)); + testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq)); + + testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt)); + testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt)); + + testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte)); + testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte)); +} + +test "negate condition" { + testing.expectEqual(@as(Condition, .eq), Condition.ne.negate()); + testing.expectEqual(@as(Condition, .ne), Condition.eq.negate()); +} + /// Represents a register in the ARM instruction set architecture pub const Register = enum(u5) { r0, -- cgit v1.2.3