authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-01 13:19:11-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-01-01 13:19:11-08:00
logdfacac916fabe71ac2a2d1794504ba5b82e25f83
tree8f4b63d80c184c6c0bb01f0ac009652e21e620b8
parent5dfeb6cbc89d64f2a44eece41918e1c391c7c989
parenta2ab2fb9b080e233e1de2cb1ef74bdbc9476acb9
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7565 from joachimschmidt557/stage2-arm

stage2 ARM: add conditional branches

3 files changed, 220 insertions(+), 3 deletions(-)

src/codegen.zig+92-3
......@@ -42,6 +42,11 @@ pub const Reloc = union(enum) {
4242 /// To perform the reloc, write 32-bit signed little-endian integer
4343 /// which is a relative jump, based on the address following the reloc.
4444 rel32: usize,
45 /// A branch in the ARM instruction set
46 arm_branch: struct {
47 pos: usize,
48 cond: @import("codegen/arm.zig").Condition,
49 },
4550};
4651
4752pub const Result = union(enum) {
......@@ -1273,11 +1278,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12731278
12741279 switch (op) {
12751280 .add => {
1276 // TODO runtime safety checks (overflow)
12771281 writeInt(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, dst_reg, operand).toU32());
12781282 },
12791283 .sub => {
1280 // TODO runtime safety checks (underflow)
12811284 if (lhs_is_dest) {
12821285 writeInt(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, dst_reg, operand).toU32());
12831286 } else {
......@@ -1293,6 +1296,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12931296 .not, .xor => {
12941297 writeInt(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, dst_reg, operand).toU32());
12951298 },
1299 .cmp_eq => {
1300 writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, dst_reg, operand).toU32());
1301 },
12961302 else => unreachable, // not a binary instruction
12971303 }
12981304 }
......@@ -1953,6 +1959,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19531959 .unsigned => MCValue{ .compare_flags_unsigned = op },
19541960 };
19551961 },
1962 .arm, .armeb => {
1963 const lhs = try self.resolveInst(inst.lhs);
1964 const rhs = try self.resolveInst(inst.rhs);
1965
1966 const src_mcv = rhs;
1967 const dst_mcv = if (lhs != .register) try self.copyToNewRegister(&inst.base, lhs) else lhs;
1968
1969 try self.genArmBinOpCode(inst.base.src, dst_mcv.register, src_mcv, true, .cmp_eq);
1970 const info = inst.lhs.ty.intInfo(self.target.*);
1971 return switch (info.signedness) {
1972 .signed => MCValue{ .compare_flags_signed = op },
1973 .unsigned => MCValue{ .compare_flags_unsigned = op },
1974 };
1975 },
19561976 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
19571977 }
19581978 }
......@@ -2016,6 +2036,37 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20162036 self.code.items.len += 4;
20172037 break :reloc reloc;
20182038 },
2039 .arm, .armeb => reloc: {
2040 const condition: Condition = switch (cond) {
2041 .compare_flags_signed => |cmp_op| blk: {
2042 // Here we map to the opposite condition because the jump is to the false branch.
2043 const condition = Condition.fromCompareOperatorSigned(cmp_op);
2044 break :blk condition.negate();
2045 },
2046 .compare_flags_unsigned => |cmp_op| blk: {
2047 // Here we map to the opposite condition because the jump is to the false branch.
2048 const condition = Condition.fromCompareOperatorUnsigned(cmp_op);
2049 break :blk condition.negate();
2050 },
2051 .register => |reg| blk: {
2052 // cmp reg, 1
2053 // bne ...
2054 const op = Instruction.Operand.imm(1, 0);
2055 writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32());
2056 break :blk .ne;
2057 },
2058 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
2059 };
2060
2061 const reloc = Reloc{
2062 .arm_branch = .{
2063 .pos = self.code.items.len,
2064 .cond = condition,
2065 },
2066 };
2067 try self.code.resize(self.code.items.len + 4);
2068 break :reloc reloc;
2069 },
20192070 else => return self.fail(inst.base.src, "TODO implement condbr {}", .{self.target.cpu.arch}),
20202071 };
20212072
......@@ -2175,7 +2226,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21752226 }
21762227 },
21772228 .arm, .armeb => {
2178 if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len))) |delta| {
2229 if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {
21792230 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32());
21802231 } else |err| {
21812232 return self.fail(src, "TODO: enable larger branch offset", .{});
......@@ -2225,6 +2276,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22252276 return self.fail(src, "unable to perform relocation: jump too far", .{});
22262277 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
22272278 },
2279 .arm_branch => |info| {
2280 switch (arch) {
2281 .arm, .armeb => {
2282 const amt = @intCast(i32, self.code.items.len) - @intCast(i32, info.pos + 8);
2283 if (math.cast(i26, amt)) |delta| {
2284 writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32());
2285 } else |_| {
2286 return self.fail(src, "TODO: enable larger branch offset", .{});
2287 }
2288 },
2289 else => unreachable, // attempting to perfrom an ARM relocation on a non-ARM target arch
2290 }
2291 },
22282292 }
22292293 }
22302294
......@@ -2278,6 +2342,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22782342 // Leave the jump offset undefined
22792343 block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 });
22802344 },
2345 .arm, .armeb => {
2346 try self.code.resize(self.code.items.len + 4);
2347 block.codegen.relocs.appendAssumeCapacity(.{
2348 .arm_branch = .{
2349 .pos = self.code.items.len - 4,
2350 .cond = .al,
2351 },
2352 });
2353 },
22812354 else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),
22822355 }
22832356 return .none;
......@@ -2650,6 +2723,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26502723 // Write the debug undefined value.
26512724 return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });
26522725 },
2726 .compare_flags_unsigned,
2727 .compare_flags_signed,
2728 => |op| {
2729 const condition = switch (mcv) {
2730 .compare_flags_unsigned => Condition.fromCompareOperatorUnsigned(op),
2731 .compare_flags_signed => Condition.fromCompareOperatorSigned(op),
2732 else => unreachable,
2733 };
2734
2735 // mov reg, 0
2736 // moveq reg, 1
2737 const zero = Instruction.Operand.imm(0, 0);
2738 const one = Instruction.Operand.imm(1, 0);
2739 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, zero).toU32());
2740 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32());
2741 },
26532742 .immediate => |x| {
26542743 if (x > math.maxInt(u32)) return self.fail(src, "ARM registers are 32-bit wide", .{});
26552744
src/codegen/arm.zig+66
......@@ -35,8 +35,74 @@ pub const Condition = enum(u4) {
3535 le,
3636 /// always
3737 al,
38
39 /// Converts a std.math.CompareOperator into a condition flag,
40 /// i.e. returns the condition that is true iff the result of the
41 /// comparison is true. Assumes signed comparison
42 pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
43 return switch (op) {
44 .gte => .ge,
45 .gt => .gt,
46 .neq => .ne,
47 .lt => .lt,
48 .lte => .le,
49 .eq => .eq,
50 };
51 }
52
53 /// Converts a std.math.CompareOperator into a condition flag,
54 /// i.e. returns the condition that is true iff the result of the
55 /// comparison is true. Assumes unsigned comparison
56 pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
57 return switch (op) {
58 .gte => .cs,
59 .gt => .hi,
60 .neq => .ne,
61 .lt => .cc,
62 .lte => .ls,
63 .eq => .eq,
64 };
65 }
66
67 /// Returns the condition which is true iff the given condition is
68 /// false (if such a condition exists)
69 pub fn negate(cond: Condition) Condition {
70 return switch (cond) {
71 .eq => .ne,
72 .ne => .eq,
73 .cs => .cc,
74 .cc => .cs,
75 .mi => .pl,
76 .pl => .mi,
77 .vs => .vc,
78 .vc => .vs,
79 .hi => .ls,
80 .ls => .hi,
81 .ge => .lt,
82 .lt => .ge,
83 .gt => .le,
84 .le => .gt,
85 .al => unreachable,
86 };
87 }
3888};
3989
90test "condition from CompareOperator" {
91 testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorSigned(.eq));
92 testing.expectEqual(@as(Condition, .eq), Condition.fromCompareOperatorUnsigned(.eq));
93
94 testing.expectEqual(@as(Condition, .gt), Condition.fromCompareOperatorSigned(.gt));
95 testing.expectEqual(@as(Condition, .hi), Condition.fromCompareOperatorUnsigned(.gt));
96
97 testing.expectEqual(@as(Condition, .le), Condition.fromCompareOperatorSigned(.lte));
98 testing.expectEqual(@as(Condition, .ls), Condition.fromCompareOperatorUnsigned(.lte));
99}
100
101test "negate condition" {
102 testing.expectEqual(@as(Condition, .eq), Condition.ne.negate());
103 testing.expectEqual(@as(Condition, .ne), Condition.eq.negate());
104}
105
40106/// Represents a register in the ARM instruction set architecture
41107pub const Register = enum(u5) {
42108 r0,
test/stage2/arm.zig+62
......@@ -282,4 +282,66 @@ pub fn addCases(ctx: *TestContext) !void {
282282 "123456",
283283 );
284284 }
285
286 {
287 var case = ctx.exe("if statements", linux_arm);
288 // Simple if statement in assert
289 case.addCompareOutput(
290 \\export fn _start() noreturn {
291 \\ var x: u32 = 123;
292 \\ var y: u32 = 42;
293 \\ assert(x > y);
294 \\ exit();
295 \\}
296 \\
297 \\fn assert(ok: bool) void {
298 \\ if (!ok) unreachable;
299 \\}
300 \\
301 \\fn exit() noreturn {
302 \\ asm volatile ("svc #0"
303 \\ :
304 \\ : [number] "{r7}" (1),
305 \\ [arg1] "{r0}" (0)
306 \\ : "memory"
307 \\ );
308 \\ unreachable;
309 \\}
310 ,
311 "",
312 );
313 }
314
315 {
316 var case = ctx.exe("while loops", linux_arm);
317 // Simple while loop with assert
318 case.addCompareOutput(
319 \\export fn _start() noreturn {
320 \\ var x: u32 = 2020;
321 \\ var i: u32 = 0;
322 \\ while (x > 0) {
323 \\ x -= 2;
324 \\ i += 1;
325 \\ }
326 \\ assert(i == 1010);
327 \\ exit();
328 \\}
329 \\
330 \\fn assert(ok: bool) void {
331 \\ if (!ok) unreachable;
332 \\}
333 \\
334 \\fn exit() noreturn {
335 \\ asm volatile ("svc #0"
336 \\ :
337 \\ : [number] "{r7}" (1),
338 \\ [arg1] "{r0}" (0)
339 \\ : "memory"
340 \\ );
341 \\ unreachable;
342 \\}
343 ,
344 "",
345 );
346 }
285347}