aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-01-01 13:19:11 -0800
committerGitHub <noreply@github.com>2021-01-01 13:19:11 -0800
commitdfacac916fabe71ac2a2d1794504ba5b82e25f83 (patch)
tree8f4b63d80c184c6c0bb01f0ac009652e21e620b8 /src
parent5dfeb6cbc89d64f2a44eece41918e1c391c7c989 (diff)
parenta2ab2fb9b080e233e1de2cb1ef74bdbc9476acb9 (diff)
downloadzig-dfacac916fabe71ac2a2d1794504ba5b82e25f83.tar.gz
zig-dfacac916fabe71ac2a2d1794504ba5b82e25f83.zip
Merge pull request #7565 from joachimschmidt557/stage2-arm
stage2 ARM: add conditional branches
Diffstat (limited to 'src')
-rw-r--r--src/codegen.zig95
-rw-r--r--src/codegen/arm.zig66
2 files changed, 158 insertions, 3 deletions
diff --git a/src/codegen.zig b/src/codegen.zig
index f978115ebc..66aa32b14e 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) {
@@ -1273,11 +1278,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 {
@@ -1293,6 +1296,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
}
}
@@ -1953,6 +1959,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}),
}
}
@@ -2016,6 +2036,37 @@ 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) {
+ .compare_flags_signed => |cmp_op| blk: {
+ // Here we map to the opposite condition because the jump is to the false branch.
+ 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.fromCompareOperatorUnsigned(cmp_op);
+ break :blk condition.negate();
+ },
+ .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}),
};
@@ -2175,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", .{});
@@ -2225,6 +2276,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 = @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 |_| {
+ 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 +2342,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;
@@ -2650,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,