authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-10-27 19:15:49+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-10-31 09:22:24+01:00
log7fc89f64b46aa2c1430b3098281ec29d22419ee7
treed7b31cd37c4ac8c7c01bcdb354b3b6171cc977e3
parent969bcb6a59eadb2ef46a9784286728a25d275c24
signature Commit is signed but in an unrecognized format.

stage2 AArch64: begin transition to MIR

This commit includes the transitions for the following instructions: - add_immediate - b - bl - blr - brk - ldp - ldr - ldrb - ldrh - mov_to_from_sp - mov_register - movk - movz - nop - ret - stp - str - strb - strh - sub_immediate - svc

4 files changed, 569 insertions(+), 433 deletions(-)

src/arch/aarch64/CodeGen.zig+208-430
......@@ -5,6 +5,8 @@ const math = std.math;
55const assert = std.debug.assert;
66const Air = @import("../../Air.zig");
77const Zir = @import("../../Zir.zig");
8const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");
810const Liveness = @import("../../Liveness.zig");
911const Type = @import("../../type.zig").Type;
1012const Value = @import("../../value.zig").Value;
......@@ -31,14 +33,12 @@ const InnerError = error{
3133 CodegenFail,
3234};
3335
34arch: std.Target.Cpu.Arch,
3536gpa: *Allocator,
3637air: Air,
3738liveness: Liveness,
3839bin_file: *link.File,
3940target: *const std.Target,
4041mod_fn: *const Module.Fn,
41code: *std.ArrayList(u8),
4242debug_output: DebugInfoOutput,
4343err_msg: ?*ErrorMsg,
4444args: []MCValue,
......@@ -48,6 +48,11 @@ arg_index: usize,
4848src_loc: Module.SrcLoc,
4949stack_align: u32,
5050
51/// MIR Instructions
52mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
53/// MIR extra data
54mir_extra: std.ArrayListUnmanaged(u32) = .{},
55
5156prev_di_line: u32,
5257prev_di_column: u32,
5358/// Byte offset within the source file of the ending curly.
......@@ -237,7 +242,6 @@ const BigTomb = struct {
237242const Self = @This();
238243
239244pub fn generate(
240 arch: std.Target.Cpu.Arch,
241245 bin_file: *link.File,
242246 src_loc: Module.SrcLoc,
243247 module_fn: *Module.Fn,
......@@ -246,7 +250,7 @@ pub fn generate(
246250 code: *std.ArrayList(u8),
247251 debug_output: DebugInfoOutput,
248252) GenerateSymbolError!FnResult {
249 if (build_options.skip_non_native and builtin.cpu.arch != arch) {
253 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
250254 @panic("Attempted to compile for architecture that was disabled by build configuration");
251255 }
252256
......@@ -262,14 +266,12 @@ pub fn generate(
262266 try branch_stack.append(.{});
263267
264268 var function = Self{
265 .arch = arch,
266269 .gpa = bin_file.allocator,
267270 .air = air,
268271 .liveness = liveness,
269272 .target = &bin_file.options.target,
270273 .bin_file = bin_file,
271274 .mod_fn = module_fn,
272 .code = code,
273275 .debug_output = debug_output,
274276 .err_msg = null,
275277 .args = undefined, // populated after `resolveCallingConventionValues`
......@@ -305,6 +307,19 @@ pub fn generate(
305307 else => |e| return e,
306308 };
307309
310 var mir = Mir{
311 .instructions = function.mir_instructions.toOwnedSlice(),
312 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
313 };
314 defer mir.deinit(bin_file.allocator);
315
316 var emit = Emit{
317 .mir = mir,
318 .target = &bin_file.options.target,
319 .code = code,
320 };
321 try emit.emitMir();
322
308323 if (function.err_msg) |em| {
309324 return FnResult{ .fail = em };
310325 } else {
......@@ -312,6 +327,16 @@ pub fn generate(
312327 }
313328}
314329
330fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
331 const gpa = self.gpa;
332
333 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
334
335 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);
336 self.mir_instructions.appendAssumeCapacity(inst);
337 return result_index;
338}
339
315340fn gen(self: *Self) !void {
316341 const cc = self.fn_type.fnCallingConvention();
317342 if (cc != .Naked) {
......@@ -320,15 +345,26 @@ fn gen(self: *Self) !void {
320345 // stp fp, lr, [sp, #-16]!
321346 // mov fp, sp
322347 // sub sp, sp, #reloc
323 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp(
324 .x29,
325 .x30,
326 Register.sp,
327 Instruction.LoadStorePairOffset.pre_index(-16),
328 ).toU32());
329 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.x29, .xzr, 0, false).toU32());
330 const backpatch_reloc = self.code.items.len;
331 try self.code.resize(backpatch_reloc + 4);
348
349 _ = try self.addInst(.{
350 .tag = .stp,
351 .data = .{ .load_store_register_pair = .{
352 .rt = .x29,
353 .rt2 = .x30,
354 .rn = Register.sp,
355 .offset = Instruction.LoadStorePairOffset.pre_index(-16),
356 } },
357 });
358
359 _ = try self.addInst(.{
360 .tag = .mov_to_from_sp,
361 .data = .{ .rr = .{ .rd = .x29, .rn = .xzr } },
362 });
363
364 const backpatch_reloc = try self.addInst(.{
365 .tag = .nop,
366 .data = .{ .nop = {} },
367 });
332368
333369 try self.dbgSetPrologueEnd();
334370
......@@ -338,7 +374,10 @@ fn gen(self: *Self) !void {
338374 const stack_end = self.max_end_stack;
339375 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
340376 if (math.cast(u12, aligned_stack_end)) |size| {
341 mem.writeIntLittle(u32, self.code.items[backpatch_reloc..][0..4], Instruction.sub(.xzr, .xzr, size, false).toU32());
377 self.mir_instructions.set(backpatch_reloc, .{
378 .tag = .sub_immediate,
379 .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = size } },
380 });
342381 } else |_| {
343382 return self.failSymbol("TODO AArch64: allow larger stacks", .{});
344383 }
......@@ -352,36 +391,36 @@ fn gen(self: *Self) !void {
352391 // the code. Therefore, we can just delete
353392 // the space initially reserved for the
354393 // jump
355 self.code.items.len -= 4;
394 self.mir_instructions.len -= 1;
356395 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
357 const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8);
358 if (amt == -4) {
359 // This return is at the end of the
360 // code block. We can't just delete
361 // the space because there may be
362 // other jumps we already relocated to
363 // the address. Instead, insert a nop
364 mem.writeIntLittle(u32, self.code.items[jmp_reloc..][0..4], Instruction.nop().toU32());
365 } else {
366 if (math.cast(i28, amt)) |offset| {
367 mem.writeIntLittle(u32, self.code.items[jmp_reloc..][0..4], Instruction.b(offset).toU32());
368 } else |_| {
369 return self.failSymbol("exitlude jump is too large", .{});
370 }
371 }
396 self.mir_instructions.set(jmp_reloc, .{
397 .tag = .b,
398 .data = .{ .inst = @intCast(u32, self.mir_instructions.len) },
399 });
372400 }
373401
374402 // ldp fp, lr, [sp], #16
375 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp(
376 .x29,
377 .x30,
378 Register.sp,
379 Instruction.LoadStorePairOffset.post_index(16),
380 ).toU32());
403 _ = try self.addInst(.{
404 .tag = .ldp,
405 .data = .{ .load_store_register_pair = .{
406 .rt = .x29,
407 .rt2 = .x30,
408 .rn = Register.sp,
409 .offset = Instruction.LoadStorePairOffset.post_index(16),
410 } },
411 });
412
381413 // add sp, sp, #stack_size
382 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.xzr, .xzr, @intCast(u12, aligned_stack_end), false).toU32());
414 _ = try self.addInst(.{
415 .tag = .add_immediate,
416 .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = @intCast(u12, aligned_stack_end) } },
417 });
418
383419 // ret lr
384 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32());
420 _ = try self.addInst(.{
421 .tag = .ret,
422 .data = .{ .reg = .x30 },
423 });
385424 } else {
386425 try self.dbgSetPrologueEnd();
387426 try self.genBody(self.air.getMainBody());
......@@ -389,7 +428,7 @@ fn gen(self: *Self) !void {
389428 }
390429
391430 // Drop them off at the rbrace.
392 try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);
431 // try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);
393432}
394433
395434fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
......@@ -534,7 +573,7 @@ fn dbgSetPrologueEnd(self: *Self) InnerError!void {
534573 switch (self.debug_output) {
535574 .dwarf => |dbg_out| {
536575 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
537 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
576 // try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
538577 },
539578 .plan9 => {},
540579 .none => {},
......@@ -545,7 +584,7 @@ fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
545584 switch (self.debug_output) {
546585 .dwarf => |dbg_out| {
547586 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
548 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
587 // try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
549588 },
550589 .plan9 => {},
551590 .none => {},
......@@ -1297,310 +1336,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
12971336 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
12981337}
12991338
1300fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
1301 return switch (mcv) {
1302 .none => unreachable,
1303 .undef => unreachable,
1304 .dead, .unreach => unreachable,
1305 .compare_flags_unsigned => unreachable,
1306 .compare_flags_signed => unreachable,
1307 .ptr_stack_offset => unreachable,
1308 .ptr_embedded_in_code => unreachable,
1309 .immediate => |imm| blk: {
1310 if (imm > std.math.maxInt(u32)) return self.fail("TODO ARM binary arithmetic immediate larger than u32", .{});
1311
1312 // Load immediate into register if it doesn't fit
1313 // in an operand
1314 break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) == null;
1315 },
1316 .register => true,
1317 .stack_offset,
1318 .embedded_in_code,
1319 .memory,
1320 => true,
1321 };
1322}
1323
1324fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue {
1325 // In the case of bitshifts, the type of rhs is different
1326 // from the resulting type
1327 const ty = self.air.typeOf(op_lhs);
1328
1329 switch (ty.zigTypeTag()) {
1330 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
1331 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
1332 .Bool => {
1333 return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned);
1334 },
1335 .Int => {
1336 const int_info = ty.intInfo(self.target.*);
1337 return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness);
1338 },
1339 else => unreachable,
1340 }
1341}
1342
1343fn genArmBinIntOp(
1344 self: *Self,
1345 inst: Air.Inst.Index,
1346 op_lhs: Air.Inst.Ref,
1347 op_rhs: Air.Inst.Ref,
1348 op: Air.Inst.Tag,
1349 bits: u16,
1350 signedness: std.builtin.Signedness,
1351) !MCValue {
1352 if (bits > 32) {
1353 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
1354 }
1355
1356 const lhs = try self.resolveInst(op_lhs);
1357 const rhs = try self.resolveInst(op_rhs);
1358
1359 const lhs_is_register = lhs == .register;
1360 const rhs_is_register = rhs == .register;
1361 const lhs_should_be_register = switch (op) {
1362 .shr, .shl => true,
1363 else => try self.armOperandShouldBeRegister(lhs),
1364 };
1365 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
1366 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1367 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
1368 const can_swap_lhs_and_rhs = switch (op) {
1369 .shr, .shl => false,
1370 else => true,
1371 };
1372
1373 // Destination must be a register
1374 var dst_mcv: MCValue = undefined;
1375 var lhs_mcv = lhs;
1376 var rhs_mcv = rhs;
1377 var swap_lhs_and_rhs = false;
1378
1379 // Allocate registers for operands and/or destination
1380 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1381 if (reuse_lhs) {
1382 // Allocate 0 or 1 registers
1383 if (!rhs_is_register and rhs_should_be_register) {
1384 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) };
1385 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1386 }
1387 dst_mcv = lhs;
1388 } else if (reuse_rhs and can_swap_lhs_and_rhs) {
1389 // Allocate 0 or 1 registers
1390 if (!lhs_is_register and lhs_should_be_register) {
1391 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) };
1392 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
1393 }
1394 dst_mcv = rhs;
1395
1396 swap_lhs_and_rhs = true;
1397 } else {
1398 // Allocate 1 or 2 registers
1399 if (lhs_should_be_register and rhs_should_be_register) {
1400 if (lhs_is_register and rhs_is_register) {
1401 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{ lhs.register, rhs.register }) };
1402 } else if (lhs_is_register) {
1403 // Move RHS to register
1404 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) };
1405 rhs_mcv = dst_mcv;
1406 } else if (rhs_is_register) {
1407 // Move LHS to register
1408 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) };
1409 lhs_mcv = dst_mcv;
1410 } else {
1411 // Move LHS and RHS to register
1412 const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }, &.{});
1413 lhs_mcv = MCValue{ .register = regs[0] };
1414 rhs_mcv = MCValue{ .register = regs[1] };
1415 dst_mcv = lhs_mcv;
1416
1417 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1418 }
1419 } else if (lhs_should_be_register) {
1420 // RHS is immediate
1421 if (lhs_is_register) {
1422 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) };
1423 } else {
1424 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) };
1425 lhs_mcv = dst_mcv;
1426 }
1427 } else if (rhs_should_be_register and can_swap_lhs_and_rhs) {
1428 // LHS is immediate
1429 if (rhs_is_register) {
1430 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) };
1431 } else {
1432 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) };
1433 rhs_mcv = dst_mcv;
1434 }
1435
1436 swap_lhs_and_rhs = true;
1437 } else unreachable; // binary operation on two immediates
1438 }
1439
1440 // Move the operands to the newly allocated registers
1441 if (lhs_mcv == .register and !lhs_is_register) {
1442 try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs);
1443 }
1444 if (rhs_mcv == .register and !rhs_is_register) {
1445 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);
1446 }
1447
1448 try self.genArmBinOpCode(
1449 dst_mcv.register,
1450 lhs_mcv,
1451 rhs_mcv,
1452 swap_lhs_and_rhs,
1453 op,
1454 signedness,
1455 );
1456 return dst_mcv;
1457}
1458
1459fn genArmBinOpCode(
1460 self: *Self,
1461 dst_reg: Register,
1462 lhs_mcv: MCValue,
1463 rhs_mcv: MCValue,
1464 swap_lhs_and_rhs: bool,
1465 op: Air.Inst.Tag,
1466 signedness: std.builtin.Signedness,
1467) !void {
1468 assert(lhs_mcv == .register or rhs_mcv == .register);
1469
1470 const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register;
1471 const op2 = if (swap_lhs_and_rhs) lhs_mcv else rhs_mcv;
1472
1473 const operand = switch (op2) {
1474 .none => unreachable,
1475 .undef => unreachable,
1476 .dead, .unreach => unreachable,
1477 .compare_flags_unsigned => unreachable,
1478 .compare_flags_signed => unreachable,
1479 .ptr_stack_offset => unreachable,
1480 .ptr_embedded_in_code => unreachable,
1481 .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?,
1482 .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none),
1483 .stack_offset,
1484 .embedded_in_code,
1485 .memory,
1486 => unreachable,
1487 };
1488
1489 switch (op) {
1490 .add => {
1491 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, op1, operand).toU32());
1492 },
1493 .sub => {
1494 if (swap_lhs_and_rhs) {
1495 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, op1, operand).toU32());
1496 } else {
1497 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, op1, operand).toU32());
1498 }
1499 },
1500 .bool_and, .bit_and => {
1501 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, op1, operand).toU32());
1502 },
1503 .bool_or, .bit_or => {
1504 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, op1, operand).toU32());
1505 },
1506 .not, .xor => {
1507 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, op1, operand).toU32());
1508 },
1509 .cmp_eq => {
1510 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32());
1511 },
1512 .shl => {
1513 assert(!swap_lhs_and_rhs);
1514 const shift_amount = switch (operand) {
1515 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1516 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
1517 };
1518 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amount).toU32());
1519 },
1520 .shr => {
1521 assert(!swap_lhs_and_rhs);
1522 const shift_amount = switch (operand) {
1523 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1524 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
1525 };
1526
1527 const shr = switch (signedness) {
1528 .signed => Instruction.asr,
1529 .unsigned => Instruction.lsr,
1530 };
1531 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amount).toU32());
1532 },
1533 else => unreachable, // not a binary instruction
1534 }
1535}
1536
1537fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
1538 const lhs = try self.resolveInst(op_lhs);
1539 const rhs = try self.resolveInst(op_rhs);
1540
1541 const lhs_is_register = lhs == .register;
1542 const rhs_is_register = rhs == .register;
1543 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1544 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
1545
1546 // Destination must be a register
1547 // LHS must be a register
1548 // RHS must be a register
1549 var dst_mcv: MCValue = undefined;
1550 var lhs_mcv: MCValue = lhs;
1551 var rhs_mcv: MCValue = rhs;
1552
1553 // Allocate registers for operands and/or destination
1554 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1555 if (reuse_lhs) {
1556 // Allocate 0 or 1 registers
1557 if (!rhs_is_register) {
1558 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) };
1559 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1560 }
1561 dst_mcv = lhs;
1562 } else if (reuse_rhs) {
1563 // Allocate 0 or 1 registers
1564 if (!lhs_is_register) {
1565 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) };
1566 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
1567 }
1568 dst_mcv = rhs;
1569 } else {
1570 // Allocate 1 or 2 registers
1571 if (lhs_is_register and rhs_is_register) {
1572 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{ lhs.register, rhs.register }) };
1573 } else if (lhs_is_register) {
1574 // Move RHS to register
1575 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) };
1576 rhs_mcv = dst_mcv;
1577 } else if (rhs_is_register) {
1578 // Move LHS to register
1579 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) };
1580 lhs_mcv = dst_mcv;
1581 } else {
1582 // Move LHS and RHS to register
1583 const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }, &.{});
1584 lhs_mcv = MCValue{ .register = regs[0] };
1585 rhs_mcv = MCValue{ .register = regs[1] };
1586 dst_mcv = lhs_mcv;
1587
1588 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1589 }
1590 }
1591
1592 // Move the operands to the newly allocated registers
1593 if (!lhs_is_register) {
1594 try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs);
1595 }
1596 if (!rhs_is_register) {
1597 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);
1598 }
1599
1600 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32());
1601 return dst_mcv;
1602}
1603
16041339fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
16051340 const ty_str = self.air.instructions.items(.data)[inst].ty_str;
16061341 const zir = &self.mod_fn.owner_decl.getFileScope().zir;
......@@ -1668,7 +1403,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
16681403}
16691404
16701405fn airBreakpoint(self: *Self) !void {
1671 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32());
1406 _ = try self.addInst(.{
1407 .tag = .brk,
1408 .data = .{ .imm16 = 1 },
1409 });
16721410 return self.finishAirBookkeeping();
16731411}
16741412
......@@ -1736,7 +1474,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
17361474
17371475 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
17381476
1739 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
1477 _ = try self.addInst(.{
1478 .tag = .blr,
1479 .data = .{ .reg = .x30 },
1480 });
17401481 } else if (func_value.castTag(.extern_fn)) |_| {
17411482 return self.fail("TODO implement calling extern functions", .{});
17421483 } else {
......@@ -1789,14 +1530,22 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
17891530 .memory = func.owner_decl.link.macho.local_sym_index,
17901531 });
17911532 // blr x30
1792 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
1533 _ = try self.addInst(.{
1534 .tag = .blr,
1535 .data = .{ .reg = .x30 },
1536 });
17931537 } else if (func_value.castTag(.extern_fn)) |func_payload| {
17941538 const decl = func_payload.data;
17951539 const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name));
17961540 const offset = blk: {
1797 const offset = @intCast(u32, self.code.items.len);
1541 // TODO add a pseudo-instruction
1542 const offset = @intCast(u32, self.mir_instructions.len);
17981543 // bl
1799 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32());
1544 // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32());
1545 _ = try self.addInst(.{
1546 .tag = .bl,
1547 .data = .{ .nop = {} },
1548 });
18001549 break :blk offset;
18011550 };
18021551 // Add relocation to the decl.
......@@ -1857,7 +1606,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
18571606
18581607 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
18591608
1860 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
1609 _ = try self.addInst(.{
1610 .tag = .blr,
1611 .data = .{ .reg = .x30 },
1612 });
18611613 } else if (func_value.castTag(.extern_fn)) |_| {
18621614 return self.fail("TODO implement calling extern functions", .{});
18631615 } else {
......@@ -1899,8 +1651,11 @@ fn ret(self: *Self, mcv: MCValue) !void {
18991651 const ret_ty = self.fn_type.fnReturnType();
19001652 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
19011653 // Just add space for an instruction, patch this later
1902 try self.code.resize(self.code.items.len + 4);
1903 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
1654 const index = try self.addInst(.{
1655 .tag = .nop,
1656 .data = .{ .nop = {} },
1657 });
1658 try self.exitlude_jump_relocs.append(self.gpa, index);
19041659}
19051660
19061661fn airRet(self: *Self, inst: Air.Inst.Index) !void {
......@@ -1939,7 +1694,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
19391694
19401695fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
19411696 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
1942 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);
1697 _ = dbg_stmt;
1698 // try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);
19431699 return self.finishAirBookkeeping();
19441700}
19451701
......@@ -2090,19 +1846,18 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
20901846 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
20911847 const loop = self.air.extraData(Air.Block, ty_pl.payload);
20921848 const body = self.air.extra[loop.end..][0..loop.data.body_len];
2093 const start_index = self.code.items.len;
1849 const start_index = @intCast(u32, self.mir_instructions.len);
20941850 try self.genBody(body);
20951851 try self.jump(start_index);
20961852 return self.finishAirBookkeeping();
20971853}
20981854
2099/// Send control flow to the `index` of `self.code`.
2100fn jump(self: *Self, index: usize) !void {
2101 if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {
2102 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32());
2103 } else |_| {
2104 return self.fail("TODO: enable larger branch offset", .{});
2105 }
1855/// Send control flow to `inst`.
1856fn jump(self: *Self, inst: Mir.Inst.Index) !void {
1857 _ = try self.addInst(.{
1858 .tag = .b,
1859 .data = .{ .inst = inst },
1860 });
21061861}
21071862
21081863fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
......@@ -2140,19 +1895,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
21401895
21411896fn performReloc(self: *Self, reloc: Reloc) !void {
21421897 switch (reloc) {
2143 .rel32 => |pos| {
2144 const amt = self.code.items.len - (pos + 4);
2145 // Here it would be tempting to implement testing for amt == 0 and then elide the
2146 // jump. However, that will cause a problem because other jumps may assume that they
2147 // can jump to this code. Or maybe I didn't understand something when I was debugging.
2148 // It could be worth another look. Anyway, that's why that isn't done here. Probably the
2149 // best place to elide jumps will be in semantic analysis, by inlining blocks that only
2150 // only have 1 break instruction.
2151 const s32_amt = math.cast(i32, amt) catch
2152 return self.fail("unable to perform relocation: jump too far", .{});
2153 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
2154 },
2155 .arm_branch => unreachable,
1898 .rel32 => return self.fail("TODO reloc.rel32 for {}", .{self.target.cpu.arch}),
1899 .arm_branch => return self.fail("TODO reloc.arm_branch for {}", .{self.target.cpu.arch}),
21561900 }
21571901}
21581902
......@@ -2244,9 +1988,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
22441988 }
22451989
22461990 if (mem.eql(u8, asm_source, "svc #0")) {
2247 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());
1991 _ = try self.addInst(.{
1992 .tag = .svc,
1993 .data = .{ .imm16 = 0x0 },
1994 });
22481995 } else if (mem.eql(u8, asm_source, "svc #0x80")) {
2249 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());
1996 _ = try self.addInst(.{
1997 .tag = .svc,
1998 .data = .{ .imm16 = 0x80 },
1999 });
22502000 } else {
22512001 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});
22522002 }
......@@ -2333,6 +2083,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
23332083 return self.fail("TODO implement set stack variable from embedded_in_code", .{});
23342084 },
23352085 .register => |reg| {
2086 _ = reg;
2087
23362088 const abi_size = ty.abiSize(self.target.*);
23372089 const adj_off = stack_offset + abi_size;
23382090
......@@ -2347,16 +2099,21 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
23472099 .aarch64_32 => .w29,
23482100 else => unreachable,
23492101 };
2350 const str = switch (abi_size) {
2351 1 => Instruction.strb,
2352 2 => Instruction.strh,
2353 4, 8 => Instruction.str,
2102 const tag: Mir.Inst.Tag = switch (abi_size) {
2103 1 => .strb,
2104 2 => .strh,
2105 4, 8 => .str,
23542106 else => unreachable, // unexpected abi size
23552107 };
23562108
2357 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), str(reg, rn, .{
2358 .offset = offset,
2359 }).toU32());
2109 _ = try self.addInst(.{
2110 .tag = tag,
2111 .data = .{ .load_store_register = .{
2112 .rt = reg,
2113 .rn = rn,
2114 .offset = offset,
2115 } },
2116 });
23602117 },
23612118 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
23622119 }
......@@ -2392,20 +2149,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
23922149 }
23932150 },
23942151 .immediate => |x| {
2395 if (x <= math.maxInt(u16)) {
2396 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @intCast(u16, x), 0).toU32());
2397 } else if (x <= math.maxInt(u32)) {
2398 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());
2399 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 16), 16).toU32());
2400 } else if (x <= math.maxInt(u32)) {
2401 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());
2402 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32());
2403 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 32), 32).toU32());
2404 } else {
2405 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());
2406 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32());
2407 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 32), 32).toU32());
2408 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 48), 48).toU32());
2152 _ = try self.addInst(.{
2153 .tag = .movz,
2154 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } },
2155 });
2156
2157 if (x > math.maxInt(u16)) {
2158 _ = try self.addInst(.{
2159 .tag = .movk,
2160 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } },
2161 });
2162 }
2163 if (x > math.maxInt(u32)) {
2164 _ = try self.addInst(.{
2165 .tag = .movk,
2166 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } },
2167 });
2168 }
2169 if (x > math.maxInt(u48)) {
2170 _ = try self.addInst(.{
2171 .tag = .movk,
2172 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } },
2173 });
24092174 }
24102175 },
24112176 .register => |src_reg| {
......@@ -2414,30 +2179,36 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
24142179 return;
24152180
24162181 // mov reg, src_reg
2417 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
2418 reg,
2419 .xzr,
2420 src_reg,
2421 Instruction.Shift.none,
2422 ).toU32());
2182 _ = try self.addInst(.{
2183 .tag = .mov_register,
2184 .data = .{ .rr = .{ .rd = reg, .rn = src_reg } },
2185 });
24232186 },
24242187 .memory => |addr| {
24252188 if (self.bin_file.options.pie) {
24262189 // PC-relative displacement to the entry in the GOT table.
24272190 // adrp
2428 const offset = @intCast(u32, self.code.items.len);
2429 mem.writeIntLittle(
2430 u32,
2431 try self.code.addManyAsArray(4),
2432 Instruction.adrp(reg, 0).toU32(),
2433 );
2191 // TODO add a pseudo instruction
2192 const offset = @intCast(u32, self.mir_instructions.len);
2193 // mem.writeIntLittle(
2194 // u32,
2195 // try self.code.addManyAsArray(4),
2196 // Instruction.adrp(reg, 0).toU32(),
2197 // );
2198 _ = try self.addInst(.{
2199 .tag = .nop,
2200 .data = .{ .nop = {} },
2201 });
2202
24342203 // ldr reg, reg, offset
2435 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{
2436 .register = .{
2204 _ = try self.addInst(.{
2205 .tag = .ldr,
2206 .data = .{ .load_store_register = .{
2207 .rt = reg,
24372208 .rn = reg,
24382209 .offset = Instruction.LoadStoreOffset.imm(0),
2439 },
2440 }).toU32());
2210 } },
2211 });
24412212
24422213 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
24432214 // TODO I think the reloc might be in the wrong place.
......@@ -2469,7 +2240,14 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
24692240 // The value is in memory at a hard-coded address.
24702241 // If the type is a pointer, it means the pointer address is at this memory location.
24712242 try self.genSetReg(Type.initTag(.usize), reg, .{ .immediate = addr });
2472 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32());
2243 _ = try self.addInst(.{
2244 .tag = .ldr,
2245 .data = .{ .load_store_register = .{
2246 .rt = reg,
2247 .rn = reg,
2248 .offset = Instruction.LoadStoreOffset.none,
2249 } },
2250 });
24732251 }
24742252 },
24752253 .stack_offset => |unadjusted_off| {
......@@ -2489,22 +2267,22 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
24892267 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
24902268
24912269 switch (abi_size) {
2492 1, 2 => {
2493 const ldr = switch (abi_size) {
2494 1 => Instruction.ldrb,
2495 2 => Instruction.ldrh,
2270 1, 2, 4, 8 => {
2271 const tag: Mir.Inst.Tag = switch (abi_size) {
2272 1 => .ldrb,
2273 2 => .ldrh,
2274 4, 8 => .ldr,
24962275 else => unreachable, // unexpected abi size
24972276 };
24982277
2499 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ldr(reg, rn, .{
2500 .offset = offset,
2501 }).toU32());
2502 },
2503 4, 8 => {
2504 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{
2505 .rn = rn,
2506 .offset = offset,
2507 } }).toU32());
2278 _ = try self.addInst(.{
2279 .tag = tag,
2280 .data = .{ .load_store_register = .{
2281 .rt = reg,
2282 .rn = rn,
2283 .offset = offset,
2284 } },
2285 });
25082286 },
25092287 else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}),
25102288 }
src/arch/aarch64/Emit.zig created+199
......@@ -0,0 +1,199 @@
1//! This file contains the functionality for lowering AArch64 MIR into
2//! machine code
3
4const Emit = @This();
5const std = @import("std");
6const Mir = @import("Mir.zig");
7const bits = @import("bits.zig");
8const Instruction = bits.Instruction;
9
10mir: Mir,
11target: *const std.Target,
12code: *std.ArrayList(u8),
13
14pub fn emitMir(
15 emit: *Emit,
16) !void {
17 const mir_tags = emit.mir.instructions.items(.tag);
18
19 for (mir_tags) |tag, index| {
20 const inst = @intCast(u32, index);
21 switch (tag) {
22 .add_immediate => try emit.mirAddSubtractImmediate(inst),
23 .sub_immediate => try emit.mirAddSubtractImmediate(inst),
24
25 .b => try emit.mirBranch(inst),
26 .bl => try emit.mirBranch(inst),
27
28 .blr => try emit.mirUnconditionalBranchRegister(inst),
29 .ret => try emit.mirUnconditionalBranchRegister(inst),
30
31 .brk => try emit.mirExceptionGeneration(inst),
32 .svc => try emit.mirExceptionGeneration(inst),
33
34 .ldp => try emit.mirLoadStoreRegisterPair(inst),
35 .stp => try emit.mirLoadStoreRegisterPair(inst),
36
37 .ldr => try emit.mirLoadStoreRegister(inst),
38 .ldrb => try emit.mirLoadStoreRegister(inst),
39 .ldrh => try emit.mirLoadStoreRegister(inst),
40 .str => try emit.mirLoadStoreRegister(inst),
41 .strb => try emit.mirLoadStoreRegister(inst),
42 .strh => try emit.mirLoadStoreRegister(inst),
43
44 .mov_register => try emit.mirMoveRegister(inst),
45 .mov_to_from_sp => try emit.mirMoveRegister(inst),
46
47 .movk => try emit.mirMoveWideImmediate(inst),
48 .movz => try emit.mirMoveWideImmediate(inst),
49
50 .nop => try emit.mirNop(),
51 }
52 }
53}
54
55fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
56 const endian = emit.target.cpu.arch.endian();
57 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
58}
59
60fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
61 const tag = emit.mir.instructions.items(.tag)[inst];
62 const rr_imm12_sh = emit.mir.instructions.items(.data)[inst].rr_imm12_sh;
63
64 switch (tag) {
65 .add_immediate => try emit.writeInstruction(Instruction.add(
66 rr_imm12_sh.rd,
67 rr_imm12_sh.rn,
68 rr_imm12_sh.imm12,
69 rr_imm12_sh.sh == 1,
70 )),
71 .sub_immediate => try emit.writeInstruction(Instruction.sub(
72 rr_imm12_sh.rd,
73 rr_imm12_sh.rn,
74 rr_imm12_sh.imm12,
75 rr_imm12_sh.sh == 1,
76 )),
77 else => unreachable,
78 }
79}
80
81fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
82 const tag = emit.mir.instructions.items(.tag)[inst];
83 const target_inst = emit.mir.instructions.items(.data)[inst].inst;
84 _ = tag;
85 _ = target_inst;
86
87 switch (tag) {
88 .b => @panic("Implement mirBranch"),
89 .bl => @panic("Implement mirBranch"),
90 else => unreachable,
91 }
92}
93
94fn mirUnconditionalBranchRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
95 const tag = emit.mir.instructions.items(.tag)[inst];
96 const reg = emit.mir.instructions.items(.data)[inst].reg;
97
98 switch (tag) {
99 .blr => try emit.writeInstruction(Instruction.blr(reg)),
100 .ret => try emit.writeInstruction(Instruction.ret(reg)),
101 else => unreachable,
102 }
103}
104
105fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {
106 const tag = emit.mir.instructions.items(.tag)[inst];
107 const imm16 = emit.mir.instructions.items(.data)[inst].imm16;
108
109 switch (tag) {
110 .brk => try emit.writeInstruction(Instruction.brk(imm16)),
111 .svc => try emit.writeInstruction(Instruction.svc(imm16)),
112 else => unreachable,
113 }
114}
115
116fn mirLoadStoreRegisterPair(emit: *Emit, inst: Mir.Inst.Index) !void {
117 const tag = emit.mir.instructions.items(.tag)[inst];
118 const load_store_register_pair = emit.mir.instructions.items(.data)[inst].load_store_register_pair;
119
120 switch (tag) {
121 .stp => try emit.writeInstruction(Instruction.stp(
122 load_store_register_pair.rt,
123 load_store_register_pair.rt2,
124 load_store_register_pair.rn,
125 load_store_register_pair.offset,
126 )),
127 .ldp => try emit.writeInstruction(Instruction.ldp(
128 load_store_register_pair.rt,
129 load_store_register_pair.rt2,
130 load_store_register_pair.rn,
131 load_store_register_pair.offset,
132 )),
133 else => unreachable,
134 }
135}
136
137fn mirLoadStoreRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
138 const tag = emit.mir.instructions.items(.tag)[inst];
139 const load_store_register = emit.mir.instructions.items(.data)[inst].load_store_register;
140
141 switch (tag) {
142 .ldr => try emit.writeInstruction(Instruction.ldr(
143 load_store_register.rt,
144 .{ .register = .{ .rn = load_store_register.rn, .offset = load_store_register.offset } },
145 )),
146 .ldrb => try emit.writeInstruction(Instruction.ldrb(
147 load_store_register.rt,
148 load_store_register.rn,
149 .{ .offset = load_store_register.offset },
150 )),
151 .ldrh => try emit.writeInstruction(Instruction.ldrh(
152 load_store_register.rt,
153 load_store_register.rn,
154 .{ .offset = load_store_register.offset },
155 )),
156 .str => try emit.writeInstruction(Instruction.str(
157 load_store_register.rt,
158 load_store_register.rn,
159 .{ .offset = load_store_register.offset },
160 )),
161 .strb => try emit.writeInstruction(Instruction.strb(
162 load_store_register.rt,
163 load_store_register.rn,
164 .{ .offset = load_store_register.offset },
165 )),
166 .strh => try emit.writeInstruction(Instruction.strh(
167 load_store_register.rt,
168 load_store_register.rn,
169 .{ .offset = load_store_register.offset },
170 )),
171 else => unreachable,
172 }
173}
174
175fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
176 const tag = emit.mir.instructions.items(.tag)[inst];
177 const rr = emit.mir.instructions.items(.data)[inst].rr;
178
179 switch (tag) {
180 .mov_register => try emit.writeInstruction(Instruction.orr(rr.rd, .xzr, rr.rn, Instruction.Shift.none)),
181 .mov_to_from_sp => try emit.writeInstruction(Instruction.add(rr.rd, rr.rn, 0, false)),
182 else => unreachable,
183 }
184}
185
186fn mirMoveWideImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
187 const tag = emit.mir.instructions.items(.tag)[inst];
188 const r_imm16_sh = emit.mir.instructions.items(.data)[inst].r_imm16_sh;
189
190 switch (tag) {
191 .movz => try emit.writeInstruction(Instruction.movz(r_imm16_sh.rd, r_imm16_sh.imm16, @as(u6, r_imm16_sh.hw) << 4)),
192 .movk => try emit.writeInstruction(Instruction.movk(r_imm16_sh.rd, r_imm16_sh.imm16, @as(u6, r_imm16_sh.hw) << 4)),
193 else => unreachable,
194 }
195}
196
197fn mirNop(emit: *Emit) !void {
198 try emit.writeInstruction(Instruction.nop());
199}
src/arch/aarch64/Mir.zig created+158
......@@ -0,0 +1,158 @@
1//! Machine Intermediate Representation.
2//! This data is produced by AArch64 Codegen or AArch64 assembly parsing
3//! These instructions have a 1:1 correspondence with machine code instructions
4//! for the target. MIR can be lowered to source-annotated textual assembly code
5//! instructions, or it can be lowered to machine code.
6//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
7//! so that, for example, the smaller encodings of jump instructions can be used.
8
9const Mir = @This();
10const std = @import("std");
11const builtin = @import("builtin");
12const assert = std.debug.assert;
13
14const bits = @import("bits.zig");
15const Register = bits.Register;
16
17instructions: std.MultiArrayList(Inst).Slice,
18/// The meaning of this data is determined by `Inst.Tag` value.
19extra: []const u32,
20
21pub const Inst = struct {
22 tag: Tag,
23 /// The meaning of this depends on `tag`.
24 data: Data,
25
26 pub const Tag = enum(u16) {
27 /// Add (immediate)
28 add_immediate,
29 /// Branch
30 b,
31 /// Branch with Link
32 bl,
33 /// Branch with Link to Register
34 blr,
35 /// Breakpoint
36 brk,
37 /// Load Pair of Registers
38 ldp,
39 /// Load Register
40 // TODO: split into ldr_immediate and ldr_register
41 ldr,
42 /// Load Register Byte
43 // TODO: split into ldrb_immediate and ldrb_register
44 ldrb,
45 /// Load Register Halfword
46 // TODO: split into ldrh_immediate and ldrh_register
47 ldrh,
48 /// Move (to/from SP)
49 mov_to_from_sp,
50 /// Move (register)
51 mov_register,
52 /// Move wide with keep
53 movk,
54 /// Move wide with zero
55 movz,
56 /// No Operation
57 nop,
58 /// Return from subroutine
59 ret,
60 /// Store Pair of Registers
61 stp,
62 /// Store Register
63 // TODO: split into str_immediate and str_register
64 str,
65 /// Store Register Byte
66 // TODO: split into strb_immediate and strb_register
67 strb,
68 /// Store Register Halfword
69 // TODO: split into strh_immediate and strh_register
70 strh,
71 /// Subtract (immediate)
72 sub_immediate,
73 /// Supervisor Call
74 svc,
75 };
76
77 /// The position of an MIR instruction within the `Mir` instructions array.
78 pub const Index = u32;
79
80 /// All instructions have a 4-byte payload, which is contained within
81 /// this union. `Tag` determines which union field is active, as well as
82 /// how to interpret the data within.
83 pub const Data = union {
84 /// No additional data
85 ///
86 /// Used by e.g. nop
87 nop: void,
88 /// Another instruction.
89 ///
90 /// Used by e.g. b
91 inst: Index,
92 /// A 16-bit immediate value.
93 ///
94 /// Used by e.g. svc
95 imm16: u16,
96 /// Index into `extra`. Meaning of what can be found there is context-dependent.
97 payload: u32,
98 /// A register
99 ///
100 /// Used by e.g. blr
101 reg: Register,
102 /// A register, an unsigned 16-bit immediate, and an optional shift
103 ///
104 /// Used by e.g. movz
105 r_imm16_sh: struct {
106 rd: Register,
107 imm16: u16,
108 hw: u2 = 0,
109 },
110 /// Two registers
111 ///
112 /// Used by e.g. mov_register
113 rr: struct {
114 rd: Register,
115 rn: Register,
116 },
117 /// Two registers, an unsigned 12-bit immediate, and an optional shift
118 ///
119 /// Used by e.g. sub_immediate
120 rr_imm12_sh: struct {
121 rd: Register,
122 rn: Register,
123 imm12: u12,
124 sh: u1 = 0,
125 },
126 /// Three registers and a LoadStoreOffset
127 ///
128 /// Used by e.g. str_register
129 load_store_register: struct {
130 rt: Register,
131 rn: Register,
132 offset: bits.Instruction.LoadStoreOffset,
133 },
134 /// Three registers and a LoadStorePairOffset
135 ///
136 /// Used by e.g. stp
137 load_store_register_pair: struct {
138 rt: Register,
139 rt2: Register,
140 rn: Register,
141 offset: bits.Instruction.LoadStorePairOffset,
142 },
143 };
144
145 // Make sure we don't accidentally make instructions bigger than expected.
146 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
147 // comptime {
148 // if (builtin.mode != .Debug) {
149 // assert(@sizeOf(Inst) == 8);
150 // }
151 // }
152};
153
154pub fn deinit(mir: *Mir, gpa: *std.mem.Allocator) void {
155 mir.instructions.deinit(gpa);
156 gpa.free(mir.extra);
157 mir.* = undefined;
158}
src/codegen.zig+4-3
......@@ -88,9 +88,10 @@ pub fn generateFunction(
8888 .wasm64 => unreachable, // has its own code path
8989 .arm => return Function(.arm).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
9090 .armeb => return Function(.armeb).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
91 .aarch64 => return @import("arch/aarch64/CodeGen.zig").generate(.aarch64, bin_file, src_loc, func, air, liveness, code, debug_output),
92 .aarch64_be => return @import("arch/aarch64/CodeGen.zig").generate(.aarch64_be, bin_file, src_loc, func, air, liveness, code, debug_output),
93 .aarch64_32 => return @import("arch/aarch64/CodeGen.zig").generate(.aarch64_32, bin_file, src_loc, func, air, liveness, code, debug_output),
91 .aarch64,
92 .aarch64_be,
93 .aarch64_32,
94 => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
9495 //.arc => return Function(.arc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
9596 //.avr => return Function(.avr).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
9697 //.bpfel => return Function(.bpfel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),