authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-11-10 18:43:53+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-11-11 14:34:53+01:00
logf1960302d115cf42554481871d22393998b87338
tree26da75fea4be8e3b877d4e2145520e00c4ede207
parentca0016a225e7ad96d286923594d4077dbd215f0d

stage2 aarch64: add ldr instruction + smoke tests


1 files changed, 228 insertions(+), 0 deletions(-)

src/codegen/aarch64.zig+228
...@@ -203,6 +203,21 @@ pub const Instruction = union(enum) {...@@ -203,6 +203,21 @@ pub const Instruction = union(enum) {
203 opc: u2,203 opc: u2,
204 sf: u1,204 sf: u1,
205 },205 },
206 LoadRegister: packed struct {
207 rt: u5,
208 rn: u5,
209 offset: u12,
210 opc: u2 = 0b01,
211 op1: u2,
212 fixed: u4 = 0b111_0,
213 size: u2,
214 },
215 LoadLiteral: packed struct {
216 rt: u5,
217 imm19: u19,
218 fixed: u6 = 0b011_0_00,
219 opc: u2,
220 },
206 ExceptionGeneration: packed struct {221 ExceptionGeneration: packed struct {
207 ll: u2,222 ll: u2,
208 op2: u3,223 op2: u3,
...@@ -227,12 +242,131 @@ pub const Instruction = union(enum) {...@@ -227,12 +242,131 @@ pub const Instruction = union(enum) {
227 pub fn toU32(self: Instruction) u32 {242 pub fn toU32(self: Instruction) u32 {
228 return switch (self) {243 return switch (self) {
229 .MoveWideImmediate => |v| @bitCast(u32, v),244 .MoveWideImmediate => |v| @bitCast(u32, v),
245 .LoadRegister => |v| @bitCast(u32, v),
246 .LoadLiteral => |v| @bitCast(u32, v),
230 .ExceptionGeneration => |v| @bitCast(u32, v),247 .ExceptionGeneration => |v| @bitCast(u32, v),
231 .UnconditionalBranchRegister => |v| @bitCast(u32, v),248 .UnconditionalBranchRegister => |v| @bitCast(u32, v),
232 .UnconditionalBranchImmediate => |v| @bitCast(u32, v),249 .UnconditionalBranchImmediate => |v| @bitCast(u32, v),
233 };250 };
234 }251 }
235252
253 /// Represents the offset operand of a load or store instruction.
254 /// Data can be loaded from memory with either an immediate offset
255 /// or an offset that is stored in some register.
256 pub const Offset = union(enum) {
257 Immediate: union(enum) {
258 PostIndex: i9,
259 PreIndex: i9,
260 Unsigned: u12,
261 },
262 Register: struct {
263 rm: u5,
264 shift: union(enum) {
265 Uxtw: u2,
266 Lsl: u2,
267 Sxtw: u2,
268 Sxtx: u2,
269 },
270 },
271
272 pub const none = Offset{
273 .Immediate = .{ .Unsigned = 0 },
274 };
275
276 pub fn toU12(self: Offset) u12 {
277 return switch (self) {
278 .Immediate => |imm_type| switch (imm_type) {
279 .PostIndex => |v| @intCast(u12, @bitCast(u9, v)) << 2 + 1,
280 .PreIndex => |v| @intCast(u12, @bitCast(u9, v)) << 2 + 3,
281 .Unsigned => |v| v,
282 },
283 .Register => |r| switch (r.shift) {
284 .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050,
285 .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050,
286 .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050,
287 .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050,
288 },
289 };
290 }
291
292 pub fn imm(offset: u12) Offset {
293 return Offset{
294 .Immediate = .{ .Unsigned = offset },
295 };
296 }
297
298 pub fn imm_post_index(offset: i9) Offset {
299 return Offset{
300 .Immediate = .{ .PostIndex = offset },
301 };
302 }
303
304 pub fn imm_pre_index(offset: i9) Offset {
305 return Offset{
306 .Immediate = .{ .PreIndex = offset },
307 };
308 }
309
310 pub fn reg(rm: Register) Offset {
311 return Offset{
312 .Register = .{
313 .rm = rm.id(),
314 .shift = .{
315 .Lsl = 0,
316 },
317 },
318 };
319 }
320
321 pub fn reg_uxtw(rm: Register, shift: u2) Offset {
322 assert(rm.size() == 32 and (shift == 0 or shift == 2));
323 return Offset{
324 .Register = .{
325 .rm = rm.id(),
326 .shift = .{
327 .Uxtw = shift,
328 },
329 },
330 };
331 }
332
333 pub fn reg_lsl(rm: Register, shift: u2) Offset {
334 assert(rm.size() == 64 and (shift == 0 or shift == 3));
335 return Offset{
336 .Register = .{
337 .rm = rm.id(),
338 .shift = .{
339 .Lsl = shift,
340 },
341 },
342 };
343 }
344
345 pub fn reg_sxtw(rm: Register, shift: u2) Offset {
346 assert(rm.size() == 32 and (shift == 0 or shift == 2));
347 return Offset{
348 .Register = .{
349 .rm = rm.id(),
350 .shift = .{
351 .Sxtw = shift,
352 },
353 },
354 };
355 }
356
357 pub fn reg_sxtx(rm: Register, shift: u2) Offset {
358 assert(rm.size() == 64 and (shift == 0 or shift == 3));
359 return Offset{
360 .Register = .{
361 .rm = rm.id(),
362 .shift = .{
363 .Sxtx = shift,
364 },
365 },
366 };
367 }
368 };
369
236 // Helper functions for assembly syntax functions370 // Helper functions for assembly syntax functions
237371
238 fn moveWideImmediate(372 fn moveWideImmediate(
...@@ -270,6 +404,69 @@ pub const Instruction = union(enum) {...@@ -270,6 +404,69 @@ pub const Instruction = union(enum) {
270 }404 }
271 }405 }
272406
407 fn loadRegister(rt: Register, rn: Register, offset: Offset) Instruction {
408 const off = offset.toU12();
409 const op1: u2 = blk: {
410 switch (offset) {
411 .Immediate => |imm| switch (imm) {
412 .Unsigned => break :blk 0b01,
413 else => {},
414 },
415 else => {},
416 }
417 break :blk 0b00;
418 };
419 switch (rt.size()) {
420 32 => {
421 return Instruction{
422 .LoadRegister = .{
423 .rt = rt.id(),
424 .rn = rn.id(),
425 .offset = offset.toU12(),
426 .op1 = op1,
427 .size = 0b10,
428 },
429 };
430 },
431 64 => {
432 return Instruction{
433 .LoadRegister = .{
434 .rt = rt.id(),
435 .rn = rn.id(),
436 .offset = offset.toU12(),
437 .op1 = op1,
438 .size = 0b11,
439 },
440 };
441 },
442 else => unreachable, // unexpected register size
443 }
444 }
445
446 fn loadLiteral(rt: Register, imm19: u19) Instruction {
447 switch (rt.size()) {
448 32 => {
449 return Instruction{
450 .LoadLiteral = .{
451 .rt = rt.id(),
452 .imm19 = imm19,
453 .opc = 0b00,
454 },
455 };
456 },
457 64 => {
458 return Instruction{
459 .LoadLiteral = .{
460 .rt = rt.id(),
461 .imm19 = imm19,
462 .opc = 0b01,
463 },
464 };
465 },
466 else => unreachable, // unexpected register size
467 }
468 }
469
273 fn exceptionGeneration(470 fn exceptionGeneration(
274 opc: u3,471 opc: u3,
275 op2: u3,472 op2: u3,
...@@ -332,6 +529,21 @@ pub const Instruction = union(enum) {...@@ -332,6 +529,21 @@ pub const Instruction = union(enum) {
332 return moveWideImmediate(0b11, rd, imm16, shift);529 return moveWideImmediate(0b11, rd, imm16, shift);
333 }530 }
334531
532 // Load register
533
534 pub const LdrArgs = struct {
535 rn: ?Register = null,
536 offset: Offset = Offset.none,
537 literal: ?u19 = null,
538 };
539 pub fn ldr(rt: Register, args: LdrArgs) Instruction {
540 if (args.rn) |rn| {
541 return loadRegister(rt, rn, args.offset);
542 } else {
543 return loadLiteral(rt, args.literal.?);
544 }
545 }
546
335 // Exception generation547 // Exception generation
336548
337 pub fn svc(imm16: u16) Instruction {549 pub fn svc(imm16: u16) Instruction {
...@@ -379,6 +591,10 @@ pub const Instruction = union(enum) {...@@ -379,6 +591,10 @@ pub const Instruction = union(enum) {
379 }591 }
380};592};
381593
594test "" {
595 testing.refAllDecls(@This());
596}
597
382test "serialize instructions" {598test "serialize instructions" {
383 const Testcase = struct {599 const Testcase = struct {
384 inst: Instruction,600 inst: Instruction,
...@@ -426,6 +642,18 @@ test "serialize instructions" {...@@ -426,6 +642,18 @@ test "serialize instructions" {
426 .inst = Instruction.bl(0x10),642 .inst = Instruction.bl(0x10),
427 .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100,643 .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100,
428 },644 },
645 .{ // ldr x2, [x1]
646 .inst = Instruction.ldr(.x2, .{ .rn = .x1 }),
647 .expected = 0b11_111_0_01_01_000000000000_00001_00010,
648 },
649 .{ // ldr x2, [x1], (x3)
650 .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.reg(.x3) }),
651 .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010,
652 },
653 .{ // ldr x2, label
654 .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }),
655 .expected = 0b01_011_0_00_0000000000000000001_00010,
656 },
429 };657 };
430658
431 for (testcases) |case| {659 for (testcases) |case| {