authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-23 23:07:23-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-08-23 23:07:23-04:00
log385786eedc7927a76006741218b949a87d42ad76
treebc944f09d9408c79ec24e12770059e8a74b2e4f7
parent4bbea3422a7b312a4996e0d6fc35c30dabd692b8
parent4f2618e75bfbaaea755c7fec14f5ae844eb3d0f8
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6097 from joachimschmidt557/stage2-arm

Start working on stage2 ARM backend

4 files changed, 768 insertions(+), 2 deletions(-)

src-self-hosted/codegen.zig+120-2
...@@ -76,8 +76,8 @@ pub fn generateSymbol(...@@ -76,8 +76,8 @@ pub fn generateSymbol(
76 switch (bin_file.options.target.cpu.arch) {76 switch (bin_file.options.target.cpu.arch) {
77 .wasm32 => unreachable, // has its own code path77 .wasm32 => unreachable, // has its own code path
78 .wasm64 => unreachable, // has its own code path78 .wasm64 => unreachable, // has its own code path
79 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),79 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
80 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),80 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
81 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),81 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
82 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),82 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
83 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),83 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line, dbg_info, dbg_info_type_relocs),
...@@ -1270,6 +1270,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1270,6 +1270,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1270 var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined1 };1270 var instr = Instruction{ .condition = .always, .input0 = .zero, .input1 = .zero, .modify_flags = false, .output = .discard, .command = .undefined1 };
1271 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr));1271 mem.writeIntLittle(u16, self.code.items[self.code.items.len - 2 ..][0..2], @bitCast(u16, instr));
1272 },1272 },
1273 .arm => {
1274 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());
1275 },
1276 .armeb => {
1277 mem.writeIntBig(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());
1278 },
1273 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),1279 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
1274 }1280 }
1275 return .none;1281 return .none;
...@@ -1393,6 +1399,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1393,6 +1399,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1393 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});1399 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1394 }1400 }
1395 },1401 },
1402 .arm => {
1403 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
1404
1405 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
1406 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
1407 const func = func_val.func;
1408 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1409 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1410 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1411 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1412
1413 // TODO only works with leaf functions
1414 // at the moment, which works fine for
1415 // Hello World, but not for real code
1416 // of course. Add pushing lr to stack
1417 // and popping after call
1418 try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
1419 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
1420 } else {
1421 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1422 }
1423 } else {
1424 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
1425 }
1426 },
1396 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),1427 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
1397 }1428 }
1398 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {1429 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
...@@ -1449,6 +1480,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1449,6 +1480,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1449 .riscv64 => {1480 .riscv64 => {
1450 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());1481 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
1451 },1482 },
1483 .arm => {
1484 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
1485 },
1452 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),1486 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
1453 }1487 }
1454 return .unreach;1488 return .unreach;
...@@ -1699,6 +1733,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1699,6 +1733,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1699 return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{});1733 return self.fail(inst.base.src, "TODO implement support for more SPU II assembly instructions", .{});
1700 }1734 }
1701 },1735 },
1736 .arm => {
1737 for (inst.inputs) |input, i| {
1738 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
1739 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
1740 }
1741 const reg_name = input[1 .. input.len - 1];
1742 const reg = parseRegName(reg_name) orelse
1743 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1744 const arg = try self.resolveInst(inst.args[i]);
1745 try self.genSetReg(inst.base.src, reg, arg);
1746 }
1747
1748 if (mem.eql(u8, inst.asm_source, "svc #0")) {
1749 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());
1750 } else {
1751 return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
1752 }
1753
1754 if (inst.output) |output| {
1755 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1756 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
1757 }
1758 const reg_name = output[2 .. output.len - 1];
1759 const reg = parseRegName(reg_name) orelse
1760 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
1761 return MCValue{ .register = reg };
1762 } else {
1763 return MCValue.none;
1764 }
1765 },
1702 .riscv64 => {1766 .riscv64 => {
1703 for (inst.inputs) |input, i| {1767 for (inst.inputs) |input, i| {
1704 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {1768 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
...@@ -1892,6 +1956,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1892,6 +1956,58 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18921956
1893 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {1957 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
1894 switch (arch) {1958 switch (arch) {
1959 .arm => switch (mcv) {
1960 .dead => unreachable,
1961 .ptr_stack_offset => unreachable,
1962 .ptr_embedded_in_code => unreachable,
1963 .unreach, .none => return, // Nothing to do.
1964 .undef => {
1965 if (!self.wantSafety())
1966 return; // The already existing value will do just fine.
1967 // Write the debug undefined value.
1968 return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa });
1969 },
1970 .immediate => |x| {
1971 // TODO better analysis of x to determine the
1972 // least amount of necessary instructions (use
1973 // more intelligent rotating)
1974 if (x <= math.maxInt(u8)) {
1975 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
1976 return;
1977 } else if (x <= math.maxInt(u16)) {
1978 // TODO Use movw Note: Not supported on
1979 // all ARM targets!
1980
1981 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
1982 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
1983 } else if (x <= math.maxInt(u32)) {
1984 // TODO Use movw and movt Note: Not
1985 // supported on all ARM targets! Also TODO
1986 // write constant to code and load
1987 // relative to pc
1988
1989 // immediate: 0xaabbccdd
1990 // mov reg, #0xaa
1991 // orr reg, reg, #0xbb, 24
1992 // orr reg, reg, #0xcc, 16
1993 // orr reg, reg, #0xdd, 8
1994 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
1995 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
1996 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
1997 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
1998 return;
1999 } else {
2000 return self.fail(src, "ARM registers are 32-bit wide", .{});
2001 }
2002 },
2003 .memory => |addr| {
2004 // The value is in memory at a hard-coded address.
2005 // If the type is a pointer, it means the pointer address is at this memory location.
2006 try self.genSetReg(src, reg, .{ .immediate = addr });
2007 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32());
2008 },
2009 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
2010 },
1895 .riscv64 => switch (mcv) {2011 .riscv64 => switch (mcv) {
1896 .dead => unreachable,2012 .dead => unreachable,
1897 .ptr_stack_offset => unreachable,2013 .ptr_stack_offset => unreachable,
...@@ -2372,6 +2488,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2372,6 +2488,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2372 .x86_64 => @import("codegen/x86_64.zig"),2488 .x86_64 => @import("codegen/x86_64.zig"),
2373 .riscv64 => @import("codegen/riscv64.zig"),2489 .riscv64 => @import("codegen/riscv64.zig"),
2374 .spu_2 => @import("codegen/spu-mk2.zig"),2490 .spu_2 => @import("codegen/spu-mk2.zig"),
2491 .arm => @import("codegen/arm.zig"),
2492 .armeb => @import("codegen/arm.zig"),
2375 else => struct {2493 else => struct {
2376 pub const Register = enum {2494 pub const Register = enum {
2377 dummy,2495 dummy,
src-self-hosted/codegen/arm.zig created+607
...@@ -0,0 +1,607 @@
1const std = @import("std");
2const DW = std.dwarf;
3const testing = std.testing;
4
5/// The condition field specifies the flags neccessary for an
6/// Instruction to be executed
7pub const Condition = enum(u4) {
8 /// equal
9 eq,
10 /// not equal
11 ne,
12 /// unsigned higher or same
13 cs,
14 /// unsigned lower
15 cc,
16 /// negative
17 mi,
18 /// positive or zero
19 pl,
20 /// overflow
21 vs,
22 /// no overflow
23 vc,
24 /// unsigned higer
25 hi,
26 /// unsigned lower or same
27 ls,
28 /// greater or equal
29 ge,
30 /// less than
31 lt,
32 /// greater than
33 gt,
34 /// less than or equal
35 le,
36 /// always
37 al,
38};
39
40/// Represents a register in the ARM instruction set architecture
41pub const Register = enum(u5) {
42 r0,
43 r1,
44 r2,
45 r3,
46 r4,
47 r5,
48 r6,
49 r7,
50 r8,
51 r9,
52 r10,
53 r11,
54 r12,
55 r13,
56 r14,
57 r15,
58
59 /// Argument / result / scratch register 1
60 a1,
61 /// Argument / result / scratch register 2
62 a2,
63 /// Argument / scratch register 3
64 a3,
65 /// Argument / scratch register 4
66 a4,
67 /// Variable-register 1
68 v1,
69 /// Variable-register 2
70 v2,
71 /// Variable-register 3
72 v3,
73 /// Variable-register 4
74 v4,
75 /// Variable-register 5
76 v5,
77 /// Platform register
78 v6,
79 /// Variable-register 7
80 v7,
81 /// Frame pointer or Variable-register 8
82 fp,
83 /// Intra-Procedure-call scratch register
84 ip,
85 /// Stack pointer
86 sp,
87 /// Link register
88 lr,
89 /// Program counter
90 pc,
91
92 /// Returns the unique 4-bit ID of this register which is used in
93 /// the machine code
94 pub fn id(self: Register) u4 {
95 return @truncate(u4, @enumToInt(self));
96 }
97
98 /// Returns the index into `callee_preserved_regs`.
99 pub fn allocIndex(self: Register) ?u4 {
100 inline for (callee_preserved_regs) |cpreg, i| {
101 if (self.id() == cpreg.id()) return i;
102 }
103 return null;
104 }
105
106 pub fn dwarfLocOp(self: Register) u8 {
107 return @as(u8, self.id()) + DW.OP_reg0;
108 }
109};
110
111test "Register.id" {
112 testing.expectEqual(@as(u4, 15), Register.r15.id());
113 testing.expectEqual(@as(u4, 15), Register.pc.id());
114}
115
116pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
117pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
118pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
119
120/// Represents an instruction in the ARM instruction set architecture
121pub const Instruction = union(enum) {
122 DataProcessing: packed struct {
123 // Note to self: The order of the fields top-to-bottom is
124 // right-to-left in the actual 32-bit int representation
125 op2: u12,
126 rd: u4,
127 rn: u4,
128 s: u1,
129 opcode: u4,
130 i: u1,
131 fixed: u2 = 0b00,
132 cond: u4,
133 },
134 SingleDataTransfer: packed struct {
135 offset: u12,
136 rd: u4,
137 rn: u4,
138 l: u1,
139 w: u1,
140 b: u1,
141 u: u1,
142 p: u1,
143 i: u1,
144 fixed: u2 = 0b01,
145 cond: u4,
146 },
147 Branch: packed struct {
148 offset: u24,
149 link: u1,
150 fixed: u3 = 0b101,
151 cond: u4,
152 },
153 BranchExchange: packed struct {
154 rn: u4,
155 fixed_1: u1 = 0b1,
156 link: u1,
157 fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,
158 cond: u4,
159 },
160 SupervisorCall: packed struct {
161 comment: u24,
162 fixed: u4 = 0b1111,
163 cond: u4,
164 },
165 Breakpoint: packed struct {
166 imm4: u4,
167 fixed_1: u4 = 0b0111,
168 imm12: u12,
169 fixed_2_and_cond: u12 = 0b1110_0001_0010,
170 },
171
172 /// Represents the possible operations which can be performed by a
173 /// DataProcessing instruction
174 const Opcode = enum(u4) {
175 // Rd := Op1 AND Op2
176 @"and",
177 // Rd := Op1 EOR Op2
178 eor,
179 // Rd := Op1 - Op2
180 sub,
181 // Rd := Op2 - Op1
182 rsb,
183 // Rd := Op1 + Op2
184 add,
185 // Rd := Op1 + Op2 + C
186 adc,
187 // Rd := Op1 - Op2 + C - 1
188 sbc,
189 // Rd := Op2 - Op1 + C - 1
190 rsc,
191 // set condition codes on Op1 AND Op2
192 tst,
193 // set condition codes on Op1 EOR Op2
194 teq,
195 // set condition codes on Op1 - Op2
196 cmp,
197 // set condition codes on Op1 + Op2
198 cmn,
199 // Rd := Op1 OR Op2
200 orr,
201 // Rd := Op2
202 mov,
203 // Rd := Op1 AND NOT Op2
204 bic,
205 // Rd := NOT Op2
206 mvn,
207 };
208
209 /// Represents the second operand to a data processing instruction
210 /// which can either be content from a register or an immediate
211 /// value
212 pub const Operand = union(enum) {
213 Register: packed struct {
214 rm: u4,
215 shift: u8,
216 },
217 Immediate: packed struct {
218 imm: u8,
219 rotate: u4,
220 },
221
222 /// Represents multiple ways a register can be shifted. A
223 /// register can be shifted by a specific immediate value or
224 /// by the contents of another register
225 pub const Shift = union(enum) {
226 Immediate: packed struct {
227 fixed: u1 = 0b0,
228 typ: u2,
229 amount: u5,
230 },
231 Register: packed struct {
232 fixed_1: u1 = 0b1,
233 typ: u2,
234 fixed_2: u1 = 0b0,
235 rs: u4,
236 },
237
238 const Type = enum(u2) {
239 LogicalLeft,
240 LogicalRight,
241 ArithmeticRight,
242 RotateRight,
243 };
244
245 const none = Shift{
246 .Immediate = .{
247 .amount = 0,
248 .typ = 0,
249 },
250 };
251
252 pub fn toU8(self: Shift) u8 {
253 return switch (self) {
254 .Register => |v| @bitCast(u8, v),
255 .Immediate => |v| @bitCast(u8, v),
256 };
257 }
258
259 pub fn reg(rs: Register, typ: Type) Shift {
260 return Shift{
261 .Register = .{
262 .rs = rs.id(),
263 .typ = @enumToInt(typ),
264 },
265 };
266 }
267
268 pub fn imm(amount: u5, typ: Type) Shift {
269 return Shift{
270 .Immediate = .{
271 .amount = amount,
272 .typ = @enumToInt(typ),
273 },
274 };
275 }
276 };
277
278 pub fn toU12(self: Operand) u12 {
279 return switch (self) {
280 .Register => |v| @bitCast(u12, v),
281 .Immediate => |v| @bitCast(u12, v),
282 };
283 }
284
285 pub fn reg(rm: Register, shift: Shift) Operand {
286 return Operand{
287 .Register = .{
288 .rm = rm.id(),
289 .shift = shift.toU8(),
290 },
291 };
292 }
293
294 pub fn imm(immediate: u8, rotate: u4) Operand {
295 return Operand{
296 .Immediate = .{
297 .imm = immediate,
298 .rotate = rotate,
299 },
300 };
301 }
302 };
303
304 /// Represents the offset operand of a load or store
305 /// instruction. Data can be loaded from memory with either an
306 /// immediate offset or an offset that is stored in some register.
307 pub const Offset = union(enum) {
308 Immediate: u12,
309 Register: packed struct {
310 rm: u4,
311 shift: u8,
312 },
313
314 pub const none = Offset{
315 .Immediate = 0,
316 };
317
318 pub fn toU12(self: Offset) u12 {
319 return switch (self) {
320 .Register => |v| @bitCast(u12, v),
321 .Immediate => |v| v,
322 };
323 }
324
325 pub fn reg(rm: Register, shift: u8) Offset {
326 return Offset{
327 .Register = .{
328 .rm = rm.id(),
329 .shift = shift,
330 },
331 };
332 }
333
334 pub fn imm(immediate: u8) Offset {
335 return Offset{
336 .Immediate = immediate,
337 };
338 }
339 };
340
341 pub fn toU32(self: Instruction) u32 {
342 return switch (self) {
343 .DataProcessing => |v| @bitCast(u32, v),
344 .SingleDataTransfer => |v| @bitCast(u32, v),
345 .Branch => |v| @bitCast(u32, v),
346 .BranchExchange => |v| @bitCast(u32, v),
347 .SupervisorCall => |v| @bitCast(u32, v),
348 .Breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),
349 };
350 }
351
352 // Helper functions for the "real" functions below
353
354 fn dataProcessing(
355 cond: Condition,
356 opcode: Opcode,
357 s: u1,
358 rd: Register,
359 rn: Register,
360 op2: Operand,
361 ) Instruction {
362 return Instruction{
363 .DataProcessing = .{
364 .cond = @enumToInt(cond),
365 .i = if (op2 == .Immediate) 1 else 0,
366 .opcode = @enumToInt(opcode),
367 .s = s,
368 .rn = rn.id(),
369 .rd = rd.id(),
370 .op2 = op2.toU12(),
371 },
372 };
373 }
374
375 fn singleDataTransfer(
376 cond: Condition,
377 rd: Register,
378 rn: Register,
379 offset: Offset,
380 pre_post: u1,
381 up_down: u1,
382 byte_word: u1,
383 writeback: u1,
384 load_store: u1,
385 ) Instruction {
386 return Instruction{
387 .SingleDataTransfer = .{
388 .cond = @enumToInt(cond),
389 .rn = rn.id(),
390 .rd = rd.id(),
391 .offset = offset.toU12(),
392 .l = load_store,
393 .w = writeback,
394 .b = byte_word,
395 .u = up_down,
396 .p = pre_post,
397 .i = if (offset == .Immediate) 0 else 1,
398 },
399 };
400 }
401
402 fn branch(cond: Condition, offset: i24, link: u1) Instruction {
403 return Instruction{
404 .Branch = .{
405 .cond = @enumToInt(cond),
406 .link = link,
407 .offset = @bitCast(u24, offset),
408 },
409 };
410 }
411
412 fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction {
413 return Instruction{
414 .BranchExchange = .{
415 .cond = @enumToInt(cond),
416 .link = link,
417 .rn = rn.id(),
418 },
419 };
420 }
421
422 fn supervisorCall(cond: Condition, comment: u24) Instruction {
423 return Instruction{
424 .SupervisorCall = .{
425 .cond = @enumToInt(cond),
426 .comment = comment,
427 },
428 };
429 }
430
431 fn breakpoint(imm: u16) Instruction {
432 return Instruction{
433 .Breakpoint = .{
434 .imm12 = @truncate(u12, imm >> 4),
435 .imm4 = @truncate(u4, imm),
436 },
437 };
438 }
439
440 // Public functions replicating assembler syntax as closely as
441 // possible
442
443 // Data processing
444
445 pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
446 return dataProcessing(cond, .@"and", s, rd, rn, op2);
447 }
448
449 pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
450 return dataProcessing(cond, .eor, s, rd, rn, op2);
451 }
452
453 pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
454 return dataProcessing(cond, .sub, s, rd, rn, op2);
455 }
456
457 pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
458 return dataProcessing(cond, .rsb, s, rd, rn, op2);
459 }
460
461 pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
462 return dataProcessing(cond, .add, s, rd, rn, op2);
463 }
464
465 pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
466 return dataProcessing(cond, .adc, s, rd, rn, op2);
467 }
468
469 pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
470 return dataProcessing(cond, .sbc, s, rd, rn, op2);
471 }
472
473 pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
474 return dataProcessing(cond, .rsc, s, rd, rn, op2);
475 }
476
477 pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
478 return dataProcessing(cond, .tst, 1, .r0, rn, op2);
479 }
480
481 pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction {
482 return dataProcessing(cond, .teq, 1, .r0, rn, op2);
483 }
484
485 pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction {
486 return dataProcessing(cond, .cmp, 1, .r0, rn, op2);
487 }
488
489 pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction {
490 return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
491 }
492
493 pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
494 return dataProcessing(cond, .orr, s, rd, rn, op2);
495 }
496
497 pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
498 return dataProcessing(cond, .mov, s, rd, .r0, op2);
499 }
500
501 pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
502 return dataProcessing(cond, .bic, s, rd, rn, op2);
503 }
504
505 pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
506 return dataProcessing(cond, .mvn, s, rd, .r0, op2);
507 }
508
509 // Single data transfer
510
511 pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
512 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1);
513 }
514
515 pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
516 return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);
517 }
518
519 // Branch
520
521 pub fn b(cond: Condition, offset: i24) Instruction {
522 return branch(cond, offset, 0);
523 }
524
525 pub fn bl(cond: Condition, offset: i24) Instruction {
526 return branch(cond, offset, 1);
527 }
528
529 // Branch and exchange
530
531 pub fn bx(cond: Condition, rn: Register) Instruction {
532 return branchExchange(cond, rn, 0);
533 }
534
535 pub fn blx(cond: Condition, rn: Register) Instruction {
536 return branchExchange(cond, rn, 1);
537 }
538
539 // Supervisor Call
540
541 pub const swi = svc;
542
543 pub fn svc(cond: Condition, comment: u24) Instruction {
544 return supervisorCall(cond, comment);
545 }
546
547 // Breakpoint
548
549 pub fn bkpt(imm: u16) Instruction {
550 return breakpoint(imm);
551 }
552};
553
554test "serialize instructions" {
555 const Testcase = struct {
556 inst: Instruction,
557 expected: u32,
558 };
559
560 const testcases = [_]Testcase{
561 .{ // add r0, r0, r0
562 .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
563 .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
564 },
565 .{ // mov r4, r2
566 .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
567 .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
568 },
569 .{ // mov r0, #42
570 .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)),
571 .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
572 },
573 .{ // ldr r0, [r2, #42]
574 .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),
575 .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
576 },
577 .{ // str r0, [r3]
578 .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none),
579 .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
580 },
581 .{ // b #12
582 .inst = Instruction.b(.al, 12),
583 .expected = 0b1110_101_0_0000_0000_0000_0000_0000_1100,
584 },
585 .{ // bl #-4
586 .inst = Instruction.bl(.al, -4),
587 .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1100,
588 },
589 .{ // bx lr
590 .inst = Instruction.bx(.al, .lr),
591 .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110,
592 },
593 .{ // svc #0
594 .inst = Instruction.svc(.al, 0),
595 .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000,
596 },
597 .{ // bkpt #42
598 .inst = Instruction.bkpt(42),
599 .expected = 0b1110_0001_0010_000000000010_0111_1010,
600 },
601 };
602
603 for (testcases) |case| {
604 const actual = case.inst.toU32();
605 testing.expectEqual(case.expected, actual);
606 }
607}
src-self-hosted/type.zig+1
...@@ -756,6 +756,7 @@ pub const Type = extern union {...@@ -756,6 +756,7 @@ pub const Type = extern union {
756 .fn_ccc_void_no_args, // represents machine code; not a pointer756 .fn_ccc_void_no_args, // represents machine code; not a pointer
757 .function, // represents machine code; not a pointer757 .function, // represents machine code; not a pointer
758 => return switch (target.cpu.arch) {758 => return switch (target.cpu.arch) {
759 .arm => 4,
759 .riscv64 => 2,760 .riscv64 => 2,
760 else => 1,761 else => 1,
761 },762 },
test/stage2/test.zig+40
...@@ -18,6 +18,11 @@ const linux_riscv64 = std.zig.CrossTarget{...@@ -18,6 +18,11 @@ const linux_riscv64 = std.zig.CrossTarget{
18 .os_tag = .linux,18 .os_tag = .linux,
19};19};
2020
21const linux_arm = std.zig.CrossTarget{
22 .cpu_arch = .arm,
23 .os_tag = .linux,
24};
25
21const wasi = std.zig.CrossTarget{26const wasi = std.zig.CrossTarget{
22 .cpu_arch = .wasm32,27 .cpu_arch = .wasm32,
23 .os_tag = .wasi,28 .os_tag = .wasi,
...@@ -181,6 +186,41 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -181,6 +186,41 @@ pub fn addCases(ctx: *TestContext) !void {
181 );186 );
182 }187 }
183188
189 {
190 var case = ctx.exe("hello world", linux_arm);
191 // Regular old hello world
192 case.addCompareOutput(
193 \\export fn _start() noreturn {
194 \\ print();
195 \\ exit();
196 \\}
197 \\
198 \\fn print() void {
199 \\ asm volatile ("svc #0"
200 \\ :
201 \\ : [number] "{r7}" (4),
202 \\ [arg1] "{r0}" (1),
203 \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
204 \\ [arg3] "{r2}" (14)
205 \\ : "memory"
206 \\ );
207 \\ return;
208 \\}
209 \\
210 \\fn exit() noreturn {
211 \\ asm volatile ("svc #0"
212 \\ :
213 \\ : [number] "{r7}" (1),
214 \\ [arg1] "{r0}" (0)
215 \\ : "memory"
216 \\ );
217 \\ unreachable;
218 \\}
219 ,
220 "Hello, World!\n",
221 );
222 }
223
184 {224 {
185 var case = ctx.exe("adding numbers at comptime", linux_x64);225 var case = ctx.exe("adding numbers at comptime", linux_x64);
186 case.addCompareOutput(226 case.addCompareOutput(