authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-10-31 18:54:19+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-10-31 18:54:19+01:00
logc452d10953366bae4f2c6d2bf689eaaf51da739a
tree90660aff961e4ba42de397a452b09a67b2f25f63
parent969bcb6a59eadb2ef46a9784286728a25d275c24
parent8a55e6b6c4b4954474bc29eccdfda02b052ff71d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10066 from joachimschmidt557/stage2-aarch64-mir

stage2 AArch64: introduce MIR

4 files changed, 877 insertions(+), 570 deletions(-)

src/arch/aarch64/CodeGen.zig+246-567
...@@ -5,6 +5,8 @@ const math = std.math;...@@ -5,6 +5,8 @@ const math = std.math;
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const Air = @import("../../Air.zig");6const Air = @import("../../Air.zig");
7const Zir = @import("../../Zir.zig");7const Zir = @import("../../Zir.zig");
8const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");
8const Liveness = @import("../../Liveness.zig");10const Liveness = @import("../../Liveness.zig");
9const Type = @import("../../type.zig").Type;11const Type = @import("../../type.zig").Type;
10const Value = @import("../../value.zig").Value;12const Value = @import("../../value.zig").Value;
...@@ -31,15 +33,12 @@ const InnerError = error{...@@ -31,15 +33,12 @@ const InnerError = error{
31 CodegenFail,33 CodegenFail,
32};34};
3335
34arch: std.Target.Cpu.Arch,
35gpa: *Allocator,36gpa: *Allocator,
36air: Air,37air: Air,
37liveness: Liveness,38liveness: Liveness,
38bin_file: *link.File,39bin_file: *link.File,
39target: *const std.Target,40target: *const std.Target,
40mod_fn: *const Module.Fn,41mod_fn: *const Module.Fn,
41code: *std.ArrayList(u8),
42debug_output: DebugInfoOutput,
43err_msg: ?*ErrorMsg,42err_msg: ?*ErrorMsg,
44args: []MCValue,43args: []MCValue,
45ret_mcv: MCValue,44ret_mcv: MCValue,
...@@ -48,13 +47,14 @@ arg_index: usize,...@@ -48,13 +47,14 @@ arg_index: usize,
48src_loc: Module.SrcLoc,47src_loc: Module.SrcLoc,
49stack_align: u32,48stack_align: u32,
5049
51prev_di_line: u32,50/// MIR Instructions
52prev_di_column: u32,51mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
52/// MIR extra data
53mir_extra: std.ArrayListUnmanaged(u32) = .{},
54
53/// Byte offset within the source file of the ending curly.55/// Byte offset within the source file of the ending curly.
54end_di_line: u32,56end_di_line: u32,
55end_di_column: u32,57end_di_column: u32,
56/// Relative to the beginning of `code`.
57prev_di_pc: usize,
5858
59/// The value is an offset into the `Function` `code` from the beginning.59/// The value is an offset into the `Function` `code` from the beginning.
60/// To perform the reloc, write 32-bit signed little-endian integer60/// To perform the reloc, write 32-bit signed little-endian integer
...@@ -237,7 +237,6 @@ const BigTomb = struct {...@@ -237,7 +237,6 @@ const BigTomb = struct {
237const Self = @This();237const Self = @This();
238238
239pub fn generate(239pub fn generate(
240 arch: std.Target.Cpu.Arch,
241 bin_file: *link.File,240 bin_file: *link.File,
242 src_loc: Module.SrcLoc,241 src_loc: Module.SrcLoc,
243 module_fn: *Module.Fn,242 module_fn: *Module.Fn,
...@@ -246,7 +245,7 @@ pub fn generate(...@@ -246,7 +245,7 @@ pub fn generate(
246 code: *std.ArrayList(u8),245 code: *std.ArrayList(u8),
247 debug_output: DebugInfoOutput,246 debug_output: DebugInfoOutput,
248) GenerateSymbolError!FnResult {247) GenerateSymbolError!FnResult {
249 if (build_options.skip_non_native and builtin.cpu.arch != arch) {248 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
250 @panic("Attempted to compile for architecture that was disabled by build configuration");249 @panic("Attempted to compile for architecture that was disabled by build configuration");
251 }250 }
252251
...@@ -262,15 +261,12 @@ pub fn generate(...@@ -262,15 +261,12 @@ pub fn generate(
262 try branch_stack.append(.{});261 try branch_stack.append(.{});
263262
264 var function = Self{263 var function = Self{
265 .arch = arch,
266 .gpa = bin_file.allocator,264 .gpa = bin_file.allocator,
267 .air = air,265 .air = air,
268 .liveness = liveness,266 .liveness = liveness,
269 .target = &bin_file.options.target,267 .target = &bin_file.options.target,
270 .bin_file = bin_file,268 .bin_file = bin_file,
271 .mod_fn = module_fn,269 .mod_fn = module_fn,
272 .code = code,
273 .debug_output = debug_output,
274 .err_msg = null,270 .err_msg = null,
275 .args = undefined, // populated after `resolveCallingConventionValues`271 .args = undefined, // populated after `resolveCallingConventionValues`
276 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`272 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
...@@ -279,9 +275,6 @@ pub fn generate(...@@ -279,9 +275,6 @@ pub fn generate(
279 .branch_stack = &branch_stack,275 .branch_stack = &branch_stack,
280 .src_loc = src_loc,276 .src_loc = src_loc,
281 .stack_align = undefined,277 .stack_align = undefined,
282 .prev_di_pc = 0,
283 .prev_di_line = module_fn.lbrace_line,
284 .prev_di_column = module_fn.lbrace_column,
285 .end_di_line = module_fn.rbrace_line,278 .end_di_line = module_fn.rbrace_line,
286 .end_di_column = module_fn.rbrace_column,279 .end_di_column = module_fn.rbrace_column,
287 };280 };
...@@ -305,6 +298,28 @@ pub fn generate(...@@ -305,6 +298,28 @@ pub fn generate(
305 else => |e| return e,298 else => |e| return e,
306 };299 };
307300
301 var mir = Mir{
302 .instructions = function.mir_instructions.toOwnedSlice(),
303 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
304 };
305 defer mir.deinit(bin_file.allocator);
306
307 var emit = Emit{
308 .mir = mir,
309 .bin_file = bin_file,
310 .debug_output = debug_output,
311 .target = &bin_file.options.target,
312 .src_loc = src_loc,
313 .code = code,
314 .prev_di_pc = 0,
315 .prev_di_line = module_fn.lbrace_line,
316 .prev_di_column = module_fn.lbrace_column,
317 };
318 emit.emitMir() catch |err| switch (err) {
319 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
320 else => |e| return e,
321 };
322
308 if (function.err_msg) |em| {323 if (function.err_msg) |em| {
309 return FnResult{ .fail = em };324 return FnResult{ .fail = em };
310 } else {325 } else {
...@@ -312,6 +327,35 @@ pub fn generate(...@@ -312,6 +327,35 @@ pub fn generate(
312 }327 }
313}328}
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
340pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
341 const fields = std.meta.fields(@TypeOf(extra));
342 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
343 return self.addExtraAssumeCapacity(extra);
344}
345
346pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
347 const fields = std.meta.fields(@TypeOf(extra));
348 const result = @intCast(u32, self.mir_extra.items.len);
349 inline for (fields) |field| {
350 self.mir_extra.appendAssumeCapacity(switch (field.field_type) {
351 u32 => @field(extra, field.name),
352 i32 => @bitCast(u32, @field(extra, field.name)),
353 else => @compileError("bad field type"),
354 });
355 }
356 return result;
357}
358
315fn gen(self: *Self) !void {359fn gen(self: *Self) !void {
316 const cc = self.fn_type.fnCallingConvention();360 const cc = self.fn_type.fnCallingConvention();
317 if (cc != .Naked) {361 if (cc != .Naked) {
...@@ -320,17 +364,31 @@ fn gen(self: *Self) !void {...@@ -320,17 +364,31 @@ fn gen(self: *Self) !void {
320 // stp fp, lr, [sp, #-16]!364 // stp fp, lr, [sp, #-16]!
321 // mov fp, sp365 // mov fp, sp
322 // sub sp, sp, #reloc366 // sub sp, sp, #reloc
323 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp(367
324 .x29,368 _ = try self.addInst(.{
325 .x30,369 .tag = .stp,
326 Register.sp,370 .data = .{ .load_store_register_pair = .{
327 Instruction.LoadStorePairOffset.pre_index(-16),371 .rt = .x29,
328 ).toU32());372 .rt2 = .x30,
329 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.x29, .xzr, 0, false).toU32());373 .rn = Register.sp,
330 const backpatch_reloc = self.code.items.len;374 .offset = Instruction.LoadStorePairOffset.pre_index(-16),
331 try self.code.resize(backpatch_reloc + 4);375 } },
332376 });
333 try self.dbgSetPrologueEnd();377
378 _ = try self.addInst(.{
379 .tag = .mov_to_from_sp,
380 .data = .{ .rr = .{ .rd = .x29, .rn = .xzr } },
381 });
382
383 const backpatch_reloc = try self.addInst(.{
384 .tag = .nop,
385 .data = .{ .nop = {} },
386 });
387
388 _ = try self.addInst(.{
389 .tag = .dbg_prologue_end,
390 .data = .{ .nop = {} },
391 });
334392
335 try self.genBody(self.air.getMainBody());393 try self.genBody(self.air.getMainBody());
336394
...@@ -338,12 +396,18 @@ fn gen(self: *Self) !void {...@@ -338,12 +396,18 @@ fn gen(self: *Self) !void {
338 const stack_end = self.max_end_stack;396 const stack_end = self.max_end_stack;
339 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);397 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
340 if (math.cast(u12, aligned_stack_end)) |size| {398 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());399 self.mir_instructions.set(backpatch_reloc, .{
400 .tag = .sub_immediate,
401 .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = size } },
402 });
342 } else |_| {403 } else |_| {
343 return self.failSymbol("TODO AArch64: allow larger stacks", .{});404 return self.failSymbol("TODO AArch64: allow larger stacks", .{});
344 }405 }
345406
346 try self.dbgSetEpilogueBegin();407 _ = try self.addInst(.{
408 .tag = .dbg_epilogue_begin,
409 .data = .{ .nop = {} },
410 });
347411
348 // exitlude jumps412 // exitlude jumps
349 if (self.exitlude_jump_relocs.items.len == 1) {413 if (self.exitlude_jump_relocs.items.len == 1) {
...@@ -352,44 +416,58 @@ fn gen(self: *Self) !void {...@@ -352,44 +416,58 @@ fn gen(self: *Self) !void {
352 // the code. Therefore, we can just delete416 // the code. Therefore, we can just delete
353 // the space initially reserved for the417 // the space initially reserved for the
354 // jump418 // jump
355 self.code.items.len -= 4;419 self.mir_instructions.len -= 1;
356 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {420 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
357 const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8);421 self.mir_instructions.set(jmp_reloc, .{
358 if (amt == -4) {422 .tag = .b,
359 // This return is at the end of the423 .data = .{ .inst = @intCast(u32, self.mir_instructions.len) },
360 // code block. We can't just delete424 });
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 }
372 }425 }
373426
374 // ldp fp, lr, [sp], #16427 // ldp fp, lr, [sp], #16
375 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp(428 _ = try self.addInst(.{
376 .x29,429 .tag = .ldp,
377 .x30,430 .data = .{ .load_store_register_pair = .{
378 Register.sp,431 .rt = .x29,
379 Instruction.LoadStorePairOffset.post_index(16),432 .rt2 = .x30,
380 ).toU32());433 .rn = Register.sp,
434 .offset = Instruction.LoadStorePairOffset.post_index(16),
435 } },
436 });
437
381 // add sp, sp, #stack_size438 // 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());439 _ = try self.addInst(.{
440 .tag = .add_immediate,
441 .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = @intCast(u12, aligned_stack_end) } },
442 });
443
383 // ret lr444 // ret lr
384 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32());445 _ = try self.addInst(.{
446 .tag = .ret,
447 .data = .{ .reg = .x30 },
448 });
385 } else {449 } else {
386 try self.dbgSetPrologueEnd();450 _ = try self.addInst(.{
451 .tag = .dbg_prologue_end,
452 .data = .{ .nop = {} },
453 });
454
387 try self.genBody(self.air.getMainBody());455 try self.genBody(self.air.getMainBody());
388 try self.dbgSetEpilogueBegin();456
457 _ = try self.addInst(.{
458 .tag = .dbg_epilogue_begin,
459 .data = .{ .nop = {} },
460 });
389 }461 }
390462
391 // Drop them off at the rbrace.463 // Drop them off at the rbrace.
392 try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);464 _ = try self.addInst(.{
465 .tag = .dbg_line,
466 .data = .{ .dbg_line_column = .{
467 .line = self.end_di_line,
468 .column = self.end_di_column,
469 } },
470 });
393}471}
394472
395fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {473fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
...@@ -530,79 +608,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -530,79 +608,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
530 }608 }
531}609}
532610
533fn dbgSetPrologueEnd(self: *Self) InnerError!void {
534 switch (self.debug_output) {
535 .dwarf => |dbg_out| {
536 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
537 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
538 },
539 .plan9 => {},
540 .none => {},
541 }
542}
543
544fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
545 switch (self.debug_output) {
546 .dwarf => |dbg_out| {
547 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
548 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
549 },
550 .plan9 => {},
551 .none => {},
552 }
553}
554
555fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {
556 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
557 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
558 switch (self.debug_output) {
559 .dwarf => |dbg_out| {
560 // TODO Look into using the DWARF special opcodes to compress this data.
561 // It lets you emit single-byte opcodes that add different numbers to
562 // both the PC and the line number at the same time.
563 try dbg_out.dbg_line.ensureUnusedCapacity(11);
564 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
565 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
566 if (delta_line != 0) {
567 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
568 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
569 }
570 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
571 self.prev_di_pc = self.code.items.len;
572 self.prev_di_line = line;
573 self.prev_di_column = column;
574 self.prev_di_pc = self.code.items.len;
575 },
576 .plan9 => |dbg_out| {
577 if (delta_pc <= 0) return; // only do this when the pc changes
578 // we have already checked the target in the linker to make sure it is compatable
579 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
580
581 // increasing the line number
582 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
583 // increasing the pc
584 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
585 if (d_pc_p9 > 0) {
586 // minus one because if its the last one, we want to leave space to change the line which is one quanta
587 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
588 if (dbg_out.pcop_change_index.*) |pci|
589 dbg_out.dbg_line.items[pci] += 1;
590 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
591 } else if (d_pc_p9 == 0) {
592 // we don't need to do anything, because adding the quant does it for us
593 } else unreachable;
594 if (dbg_out.start_line.* == null)
595 dbg_out.start_line.* = self.prev_di_line;
596 dbg_out.end_line.* = line;
597 // only do this if the pc changed
598 self.prev_di_line = line;
599 self.prev_di_column = column;
600 self.prev_di_pc = self.code.items.len;
601 },
602 .none => {},
603 }
604}
605
606/// Asserts there is already capacity to insert into top branch inst_table.611/// Asserts there is already capacity to insert into top branch inst_table.
607fn processDeath(self: *Self, inst: Air.Inst.Index) void {612fn processDeath(self: *Self, inst: Air.Inst.Index) void {
608 const air_tags = self.air.instructions.items(.tag);613 const air_tags = self.air.instructions.items(.tag);
...@@ -1297,310 +1302,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -1297,310 +1302,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
1297 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });1302 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
1298}1303}
12991304
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
1604fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {1305fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
1605 const ty_str = self.air.instructions.items(.data)[inst].ty_str;1306 const ty_str = self.air.instructions.items(.data)[inst].ty_str;
1606 const zir = &self.mod_fn.owner_decl.getFileScope().zir;1307 const zir = &self.mod_fn.owner_decl.getFileScope().zir;
...@@ -1652,7 +1353,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1652,7 +1353,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1652 },1353 },
1653 else => result,1354 else => result,
1654 };1355 };
1655 try self.genArgDbgInfo(inst, mcv);1356 // TODO generate debug info
1357 // try self.genArgDbgInfo(inst, mcv);
16561358
1657 if (self.liveness.isUnused(inst))1359 if (self.liveness.isUnused(inst))
1658 return self.finishAirBookkeeping();1360 return self.finishAirBookkeeping();
...@@ -1668,7 +1370,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1668,7 +1370,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1668}1370}
16691371
1670fn airBreakpoint(self: *Self) !void {1372fn airBreakpoint(self: *Self) !void {
1671 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32());1373 _ = try self.addInst(.{
1374 .tag = .brk,
1375 .data = .{ .imm16 = 1 },
1376 });
1672 return self.finishAirBookkeeping();1377 return self.finishAirBookkeeping();
1673}1378}
16741379
...@@ -1736,7 +1441,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -1736,7 +1441,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
17361441
1737 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });1442 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
17381443
1739 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());1444 _ = try self.addInst(.{
1445 .tag = .blr,
1446 .data = .{ .reg = .x30 },
1447 });
1740 } else if (func_value.castTag(.extern_fn)) |_| {1448 } else if (func_value.castTag(.extern_fn)) |_| {
1741 return self.fail("TODO implement calling extern functions", .{});1449 return self.fail("TODO implement calling extern functions", .{});
1742 } else {1450 } else {
...@@ -1789,25 +1497,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -1789,25 +1497,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1789 .memory = func.owner_decl.link.macho.local_sym_index,1497 .memory = func.owner_decl.link.macho.local_sym_index,
1790 });1498 });
1791 // blr x301499 // blr x30
1792 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());1500 _ = try self.addInst(.{
1501 .tag = .blr,
1502 .data = .{ .reg = .x30 },
1503 });
1793 } else if (func_value.castTag(.extern_fn)) |func_payload| {1504 } else if (func_value.castTag(.extern_fn)) |func_payload| {
1794 const decl = func_payload.data;1505 const decl = func_payload.data;
1795 const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name));1506 const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name));
1796 const offset = blk: {1507
1797 const offset = @intCast(u32, self.code.items.len);1508 _ = try self.addInst(.{
1798 // bl1509 .tag = .call_extern,
1799 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32());1510 .data = .{ .extern_fn = n_strx },
1800 break :blk offset;
1801 };
1802 // Add relocation to the decl.
1803 try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{
1804 .offset = offset,
1805 .target = .{ .global = n_strx },
1806 .addend = 0,
1807 .subtractor = null,
1808 .pcrel = true,
1809 .length = 2,
1810 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1811 });1511 });
1812 } else {1512 } else {
1813 return self.fail("TODO implement calling bitcasted functions", .{});1513 return self.fail("TODO implement calling bitcasted functions", .{});
...@@ -1857,7 +1557,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -1857,7 +1557,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
18571557
1858 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });1558 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
18591559
1860 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());1560 _ = try self.addInst(.{
1561 .tag = .blr,
1562 .data = .{ .reg = .x30 },
1563 });
1861 } else if (func_value.castTag(.extern_fn)) |_| {1564 } else if (func_value.castTag(.extern_fn)) |_| {
1862 return self.fail("TODO implement calling extern functions", .{});1565 return self.fail("TODO implement calling extern functions", .{});
1863 } else {1566 } else {
...@@ -1899,8 +1602,11 @@ fn ret(self: *Self, mcv: MCValue) !void {...@@ -1899,8 +1602,11 @@ fn ret(self: *Self, mcv: MCValue) !void {
1899 const ret_ty = self.fn_type.fnReturnType();1602 const ret_ty = self.fn_type.fnReturnType();
1900 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);1603 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
1901 // Just add space for an instruction, patch this later1604 // Just add space for an instruction, patch this later
1902 try self.code.resize(self.code.items.len + 4);1605 const index = try self.addInst(.{
1903 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);1606 .tag = .nop,
1607 .data = .{ .nop = {} },
1608 });
1609 try self.exitlude_jump_relocs.append(self.gpa, index);
1904}1610}
19051611
1906fn airRet(self: *Self, inst: Air.Inst.Index) !void {1612fn airRet(self: *Self, inst: Air.Inst.Index) !void {
...@@ -1939,7 +1645,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -1939,7 +1645,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
19391645
1940fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {1646fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
1941 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;1647 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
1942 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);1648
1649 _ = try self.addInst(.{
1650 .tag = .dbg_line,
1651 .data = .{ .dbg_line_column = .{
1652 .line = dbg_stmt.line,
1653 .column = dbg_stmt.column,
1654 } },
1655 });
1656
1943 return self.finishAirBookkeeping();1657 return self.finishAirBookkeeping();
1944}1658}
19451659
...@@ -2090,19 +1804,18 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {...@@ -2090,19 +1804,18 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
2090 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1804 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2091 const loop = self.air.extraData(Air.Block, ty_pl.payload);1805 const loop = self.air.extraData(Air.Block, ty_pl.payload);
2092 const body = self.air.extra[loop.end..][0..loop.data.body_len];1806 const body = self.air.extra[loop.end..][0..loop.data.body_len];
2093 const start_index = self.code.items.len;1807 const start_index = @intCast(u32, self.mir_instructions.len);
2094 try self.genBody(body);1808 try self.genBody(body);
2095 try self.jump(start_index);1809 try self.jump(start_index);
2096 return self.finishAirBookkeeping();1810 return self.finishAirBookkeeping();
2097}1811}
20981812
2099/// Send control flow to the `index` of `self.code`.1813/// Send control flow to `inst`.
2100fn jump(self: *Self, index: usize) !void {1814fn jump(self: *Self, inst: Mir.Inst.Index) !void {
2101 if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {1815 _ = try self.addInst(.{
2102 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32());1816 .tag = .b,
2103 } else |_| {1817 .data = .{ .inst = inst },
2104 return self.fail("TODO: enable larger branch offset", .{});1818 });
2105 }
2106}1819}
21071820
2108fn airBlock(self: *Self, inst: Air.Inst.Index) !void {1821fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2140,19 +1853,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -2140,19 +1853,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
21401853
2141fn performReloc(self: *Self, reloc: Reloc) !void {1854fn performReloc(self: *Self, reloc: Reloc) !void {
2142 switch (reloc) {1855 switch (reloc) {
2143 .rel32 => |pos| {1856 .rel32 => return self.fail("TODO reloc.rel32 for {}", .{self.target.cpu.arch}),
2144 const amt = self.code.items.len - (pos + 4);1857 .arm_branch => return self.fail("TODO reloc.arm_branch for {}", .{self.target.cpu.arch}),
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,
2156 }1858 }
2157}1859}
21581860
...@@ -2244,9 +1946,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -2244,9 +1946,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2244 }1946 }
22451947
2246 if (mem.eql(u8, asm_source, "svc #0")) {1948 if (mem.eql(u8, asm_source, "svc #0")) {
2247 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());1949 _ = try self.addInst(.{
1950 .tag = .svc,
1951 .data = .{ .imm16 = 0x0 },
1952 });
2248 } else if (mem.eql(u8, asm_source, "svc #0x80")) {1953 } else if (mem.eql(u8, asm_source, "svc #0x80")) {
2249 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());1954 _ = try self.addInst(.{
1955 .tag = .svc,
1956 .data = .{ .imm16 = 0x80 },
1957 });
2250 } else {1958 } else {
2251 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});1959 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});
2252 }1960 }
...@@ -2333,6 +2041,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -2333,6 +2041,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
2333 return self.fail("TODO implement set stack variable from embedded_in_code", .{});2041 return self.fail("TODO implement set stack variable from embedded_in_code", .{});
2334 },2042 },
2335 .register => |reg| {2043 .register => |reg| {
2044 _ = reg;
2045
2336 const abi_size = ty.abiSize(self.target.*);2046 const abi_size = ty.abiSize(self.target.*);
2337 const adj_off = stack_offset + abi_size;2047 const adj_off = stack_offset + abi_size;
23382048
...@@ -2347,16 +2057,21 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -2347,16 +2057,21 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
2347 .aarch64_32 => .w29,2057 .aarch64_32 => .w29,
2348 else => unreachable,2058 else => unreachable,
2349 };2059 };
2350 const str = switch (abi_size) {2060 const tag: Mir.Inst.Tag = switch (abi_size) {
2351 1 => Instruction.strb,2061 1 => .strb,
2352 2 => Instruction.strh,2062 2 => .strh,
2353 4, 8 => Instruction.str,2063 4, 8 => .str,
2354 else => unreachable, // unexpected abi size2064 else => unreachable, // unexpected abi size
2355 };2065 };
23562066
2357 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), str(reg, rn, .{2067 _ = try self.addInst(.{
2358 .offset = offset,2068 .tag = tag,
2359 }).toU32());2069 .data = .{ .load_store_register = .{
2070 .rt = reg,
2071 .rn = rn,
2072 .offset = offset,
2073 } },
2074 });
2360 },2075 },
2361 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),2076 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
2362 }2077 }
...@@ -2392,20 +2107,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2392,20 +2107,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2392 }2107 }
2393 },2108 },
2394 .immediate => |x| {2109 .immediate => |x| {
2395 if (x <= math.maxInt(u16)) {2110 _ = try self.addInst(.{
2396 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @intCast(u16, x), 0).toU32());2111 .tag = .movz,
2397 } else if (x <= math.maxInt(u32)) {2112 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } },
2398 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());2113 });
2399 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 16), 16).toU32());2114
2400 } else if (x <= math.maxInt(u32)) {2115 if (x > math.maxInt(u16)) {
2401 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());2116 _ = try self.addInst(.{
2402 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32());2117 .tag = .movk,
2403 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 32), 32).toU32());2118 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } },
2404 } else {2119 });
2405 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());2120 }
2406 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32());2121 if (x > math.maxInt(u32)) {
2407 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 32), 32).toU32());2122 _ = try self.addInst(.{
2408 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 48), 48).toU32());2123 .tag = .movk,
2124 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } },
2125 });
2126 }
2127 if (x > math.maxInt(u48)) {
2128 _ = try self.addInst(.{
2129 .tag = .movk,
2130 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } },
2131 });
2409 }2132 }
2410 },2133 },
2411 .register => |src_reg| {2134 .register => |src_reg| {
...@@ -2414,63 +2137,19 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2414,63 +2137,19 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2414 return;2137 return;
24152138
2416 // mov reg, src_reg2139 // mov reg, src_reg
2417 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(2140 _ = try self.addInst(.{
2418 reg,2141 .tag = .mov_register,
2419 .xzr,2142 .data = .{ .rr = .{ .rd = reg, .rn = src_reg } },
2420 src_reg,2143 });
2421 Instruction.Shift.none,
2422 ).toU32());
2423 },2144 },
2424 .memory => |addr| {2145 .memory => |addr| {
2425 if (self.bin_file.options.pie) {2146 _ = try self.addInst(.{
2426 // PC-relative displacement to the entry in the GOT table.2147 .tag = .load_memory,
2427 // adrp2148 .data = .{ .payload = try self.addExtra(Mir.LoadMemory{
2428 const offset = @intCast(u32, self.code.items.len);2149 .register = @enumToInt(reg),
2429 mem.writeIntLittle(2150 .addr = @intCast(u32, addr),
2430 u32,2151 }) },
2431 try self.code.addManyAsArray(4),2152 });
2432 Instruction.adrp(reg, 0).toU32(),
2433 );
2434 // ldr reg, reg, offset
2435 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{
2436 .register = .{
2437 .rn = reg,
2438 .offset = Instruction.LoadStoreOffset.imm(0),
2439 },
2440 }).toU32());
2441
2442 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2443 // TODO I think the reloc might be in the wrong place.
2444 const decl = macho_file.active_decl.?;
2445 // Page reloc for adrp instruction.
2446 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
2447 .offset = offset,
2448 .target = .{ .local = @intCast(u32, addr) },
2449 .addend = 0,
2450 .subtractor = null,
2451 .pcrel = true,
2452 .length = 2,
2453 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
2454 });
2455 // Pageoff reloc for adrp instruction.
2456 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
2457 .offset = offset + 4,
2458 .target = .{ .local = @intCast(u32, addr) },
2459 .addend = 0,
2460 .subtractor = null,
2461 .pcrel = false,
2462 .length = 2,
2463 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
2464 });
2465 } else {
2466 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
2467 }
2468 } else {
2469 // The value is in memory at a hard-coded address.
2470 // If the type is a pointer, it means the pointer address is at this memory location.
2471 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());
2473 }
2474 },2153 },
2475 .stack_offset => |unadjusted_off| {2154 .stack_offset => |unadjusted_off| {
2476 // TODO: maybe addressing from sp instead of fp2155 // TODO: maybe addressing from sp instead of fp
...@@ -2489,22 +2168,22 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2489,22 +2168,22 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2489 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));2168 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
24902169
2491 switch (abi_size) {2170 switch (abi_size) {
2492 1, 2 => {2171 1, 2, 4, 8 => {
2493 const ldr = switch (abi_size) {2172 const tag: Mir.Inst.Tag = switch (abi_size) {
2494 1 => Instruction.ldrb,2173 1 => .ldrb,
2495 2 => Instruction.ldrh,2174 2 => .ldrh,
2175 4, 8 => .ldr,
2496 else => unreachable, // unexpected abi size2176 else => unreachable, // unexpected abi size
2497 };2177 };
24982178
2499 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ldr(reg, rn, .{2179 _ = try self.addInst(.{
2500 .offset = offset,2180 .tag = tag,
2501 }).toU32());2181 .data = .{ .load_store_register = .{
2502 },2182 .rt = reg,
2503 4, 8 => {2183 .rn = rn,
2504 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{2184 .offset = offset,
2505 .rn = rn,2185 } },
2506 .offset = offset,2186 });
2507 } }).toU32());
2508 },2187 },
2509 else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}),2188 else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}),
2510 }2189 }
src/arch/aarch64/Emit.zig created+419
...@@ -0,0 +1,419 @@
1//! This file contains the functionality for lowering AArch64 MIR into
2//! machine code
3
4const Emit = @This();
5const std = @import("std");
6const math = std.math;
7const Mir = @import("Mir.zig");
8const bits = @import("bits.zig");
9const link = @import("../../link.zig");
10const Module = @import("../../Module.zig");
11const ErrorMsg = Module.ErrorMsg;
12const assert = std.debug.assert;
13const DW = std.dwarf;
14const leb128 = std.leb;
15const Instruction = bits.Instruction;
16const Register = bits.Register;
17const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
18
19mir: Mir,
20bin_file: *link.File,
21debug_output: DebugInfoOutput,
22target: *const std.Target,
23err_msg: ?*ErrorMsg = null,
24src_loc: Module.SrcLoc,
25code: *std.ArrayList(u8),
26
27prev_di_line: u32,
28prev_di_column: u32,
29/// Relative to the beginning of `code`.
30prev_di_pc: usize,
31
32const InnerError = error{
33 OutOfMemory,
34 EmitFail,
35};
36
37pub fn emitMir(
38 emit: *Emit,
39) !void {
40 const mir_tags = emit.mir.instructions.items(.tag);
41
42 for (mir_tags) |tag, index| {
43 const inst = @intCast(u32, index);
44 switch (tag) {
45 .add_immediate => try emit.mirAddSubtractImmediate(inst),
46 .sub_immediate => try emit.mirAddSubtractImmediate(inst),
47
48 .b => try emit.mirBranch(inst),
49 .bl => try emit.mirBranch(inst),
50
51 .blr => try emit.mirUnconditionalBranchRegister(inst),
52 .ret => try emit.mirUnconditionalBranchRegister(inst),
53
54 .brk => try emit.mirExceptionGeneration(inst),
55 .svc => try emit.mirExceptionGeneration(inst),
56
57 .call_extern => try emit.mirCallExtern(inst),
58
59 .dbg_line => try emit.mirDbgLine(inst),
60
61 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
62 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
63
64 .load_memory => try emit.mirLoadMemory(inst),
65
66 .ldp => try emit.mirLoadStoreRegisterPair(inst),
67 .stp => try emit.mirLoadStoreRegisterPair(inst),
68
69 .ldr => try emit.mirLoadStoreRegister(inst),
70 .ldrb => try emit.mirLoadStoreRegister(inst),
71 .ldrh => try emit.mirLoadStoreRegister(inst),
72 .str => try emit.mirLoadStoreRegister(inst),
73 .strb => try emit.mirLoadStoreRegister(inst),
74 .strh => try emit.mirLoadStoreRegister(inst),
75
76 .mov_register => try emit.mirMoveRegister(inst),
77 .mov_to_from_sp => try emit.mirMoveRegister(inst),
78
79 .movk => try emit.mirMoveWideImmediate(inst),
80 .movz => try emit.mirMoveWideImmediate(inst),
81
82 .nop => try emit.mirNop(),
83 }
84 }
85}
86
87fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
88 const endian = emit.target.cpu.arch.endian();
89 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
90}
91
92fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
93 @setCold(true);
94 assert(emit.err_msg == null);
95 emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
96 return error.EmitFail;
97}
98
99fn moveImmediate(emit: *Emit, reg: Register, imm64: u64) !void {
100 try emit.writeInstruction(Instruction.movz(reg, @truncate(u16, imm64), 0));
101
102 if (imm64 > math.maxInt(u16)) {
103 try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 16), 16));
104 }
105 if (imm64 > math.maxInt(u32)) {
106 try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 32), 32));
107 }
108 if (imm64 > math.maxInt(u48)) {
109 try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 48), 48));
110 }
111}
112
113fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
114 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
115 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
116 switch (self.debug_output) {
117 .dwarf => |dbg_out| {
118 // TODO Look into using the DWARF special opcodes to compress this data.
119 // It lets you emit single-byte opcodes that add different numbers to
120 // both the PC and the line number at the same time.
121 try dbg_out.dbg_line.ensureUnusedCapacity(11);
122 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
123 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
124 if (delta_line != 0) {
125 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
126 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
127 }
128 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
129 self.prev_di_pc = self.code.items.len;
130 self.prev_di_line = line;
131 self.prev_di_column = column;
132 self.prev_di_pc = self.code.items.len;
133 },
134 .plan9 => |dbg_out| {
135 if (delta_pc <= 0) return; // only do this when the pc changes
136 // we have already checked the target in the linker to make sure it is compatable
137 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
138
139 // increasing the line number
140 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
141 // increasing the pc
142 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
143 if (d_pc_p9 > 0) {
144 // minus one because if its the last one, we want to leave space to change the line which is one quanta
145 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
146 if (dbg_out.pcop_change_index.*) |pci|
147 dbg_out.dbg_line.items[pci] += 1;
148 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
149 } else if (d_pc_p9 == 0) {
150 // we don't need to do anything, because adding the quant does it for us
151 } else unreachable;
152 if (dbg_out.start_line.* == null)
153 dbg_out.start_line.* = self.prev_di_line;
154 dbg_out.end_line.* = line;
155 // only do this if the pc changed
156 self.prev_di_line = line;
157 self.prev_di_column = column;
158 self.prev_di_pc = self.code.items.len;
159 },
160 .none => {},
161 }
162}
163
164fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
165 const tag = emit.mir.instructions.items(.tag)[inst];
166 const rr_imm12_sh = emit.mir.instructions.items(.data)[inst].rr_imm12_sh;
167
168 switch (tag) {
169 .add_immediate => try emit.writeInstruction(Instruction.add(
170 rr_imm12_sh.rd,
171 rr_imm12_sh.rn,
172 rr_imm12_sh.imm12,
173 rr_imm12_sh.sh == 1,
174 )),
175 .sub_immediate => try emit.writeInstruction(Instruction.sub(
176 rr_imm12_sh.rd,
177 rr_imm12_sh.rn,
178 rr_imm12_sh.imm12,
179 rr_imm12_sh.sh == 1,
180 )),
181 else => unreachable,
182 }
183}
184
185fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
186 const tag = emit.mir.instructions.items(.tag)[inst];
187 const target_inst = emit.mir.instructions.items(.data)[inst].inst;
188 _ = tag;
189 _ = target_inst;
190
191 switch (tag) {
192 .b => return emit.fail("Implement mirBranch", .{}),
193 .bl => return emit.fail("Implement mirBranch", .{}),
194 else => unreachable,
195 }
196}
197
198fn mirUnconditionalBranchRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
199 const tag = emit.mir.instructions.items(.tag)[inst];
200 const reg = emit.mir.instructions.items(.data)[inst].reg;
201
202 switch (tag) {
203 .blr => try emit.writeInstruction(Instruction.blr(reg)),
204 .ret => try emit.writeInstruction(Instruction.ret(reg)),
205 else => unreachable,
206 }
207}
208
209fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {
210 const tag = emit.mir.instructions.items(.tag)[inst];
211 const imm16 = emit.mir.instructions.items(.data)[inst].imm16;
212
213 switch (tag) {
214 .brk => try emit.writeInstruction(Instruction.brk(imm16)),
215 .svc => try emit.writeInstruction(Instruction.svc(imm16)),
216 else => unreachable,
217 }
218}
219
220fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
221 const tag = emit.mir.instructions.items(.tag)[inst];
222 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
223
224 switch (tag) {
225 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
226 else => unreachable,
227 }
228}
229
230fn mirDebugPrologueEnd(self: *Emit) !void {
231 switch (self.debug_output) {
232 .dwarf => |dbg_out| {
233 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
234 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
235 },
236 .plan9 => {},
237 .none => {},
238 }
239}
240
241fn mirDebugEpilogueBegin(self: *Emit) !void {
242 switch (self.debug_output) {
243 .dwarf => |dbg_out| {
244 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
245 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
246 },
247 .plan9 => {},
248 .none => {},
249 }
250}
251
252fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
253 assert(emit.mir.instructions.items(.tag)[inst] == .call_extern);
254 const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn;
255
256 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
257 const offset = blk: {
258 const offset = @intCast(u32, emit.code.items.len);
259 // bl
260 try emit.writeInstruction(Instruction.bl(0));
261 break :blk offset;
262 };
263 // Add relocation to the decl.
264 try macho_file.active_decl.?.link.macho.relocs.append(emit.bin_file.allocator, .{
265 .offset = offset,
266 .target = .{ .global = n_strx },
267 .addend = 0,
268 .subtractor = null,
269 .pcrel = true,
270 .length = 2,
271 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
272 });
273 } else {
274 return emit.fail("Implement call_extern for linking backends != MachO", .{});
275 }
276}
277
278fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void {
279 assert(emit.mir.instructions.items(.tag)[inst] == .load_memory);
280 const payload = emit.mir.instructions.items(.data)[inst].payload;
281 const load_memory = emit.mir.extraData(Mir.LoadMemory, payload).data;
282 const reg = @intToEnum(Register, load_memory.register);
283 const addr = load_memory.addr;
284
285 if (emit.bin_file.options.pie) {
286 // PC-relative displacement to the entry in the GOT table.
287 // adrp
288 const offset = @intCast(u32, emit.code.items.len);
289 try emit.writeInstruction(Instruction.adrp(reg, 0));
290
291 // ldr reg, reg, offset
292 try emit.writeInstruction(Instruction.ldr(reg, .{
293 .register = .{
294 .rn = reg,
295 .offset = Instruction.LoadStoreOffset.imm(0),
296 },
297 }));
298
299 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
300 // TODO I think the reloc might be in the wrong place.
301 const decl = macho_file.active_decl.?;
302 // Page reloc for adrp instruction.
303 try decl.link.macho.relocs.append(emit.bin_file.allocator, .{
304 .offset = offset,
305 .target = .{ .local = addr },
306 .addend = 0,
307 .subtractor = null,
308 .pcrel = true,
309 .length = 2,
310 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
311 });
312 // Pageoff reloc for adrp instruction.
313 try decl.link.macho.relocs.append(emit.bin_file.allocator, .{
314 .offset = offset + 4,
315 .target = .{ .local = addr },
316 .addend = 0,
317 .subtractor = null,
318 .pcrel = false,
319 .length = 2,
320 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
321 });
322 } else {
323 return emit.fail("TODO implement load_memory for PIE GOT indirection on this platform", .{});
324 }
325 } else {
326 // The value is in memory at a hard-coded address.
327 // If the type is a pointer, it means the pointer address is at this memory location.
328 try emit.moveImmediate(reg, addr);
329 try emit.writeInstruction(Instruction.ldr(
330 reg,
331 .{ .register = .{ .rn = reg, .offset = Instruction.LoadStoreOffset.none } },
332 ));
333 }
334}
335
336fn mirLoadStoreRegisterPair(emit: *Emit, inst: Mir.Inst.Index) !void {
337 const tag = emit.mir.instructions.items(.tag)[inst];
338 const load_store_register_pair = emit.mir.instructions.items(.data)[inst].load_store_register_pair;
339
340 switch (tag) {
341 .stp => try emit.writeInstruction(Instruction.stp(
342 load_store_register_pair.rt,
343 load_store_register_pair.rt2,
344 load_store_register_pair.rn,
345 load_store_register_pair.offset,
346 )),
347 .ldp => try emit.writeInstruction(Instruction.ldp(
348 load_store_register_pair.rt,
349 load_store_register_pair.rt2,
350 load_store_register_pair.rn,
351 load_store_register_pair.offset,
352 )),
353 else => unreachable,
354 }
355}
356
357fn mirLoadStoreRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
358 const tag = emit.mir.instructions.items(.tag)[inst];
359 const load_store_register = emit.mir.instructions.items(.data)[inst].load_store_register;
360
361 switch (tag) {
362 .ldr => try emit.writeInstruction(Instruction.ldr(
363 load_store_register.rt,
364 .{ .register = .{ .rn = load_store_register.rn, .offset = load_store_register.offset } },
365 )),
366 .ldrb => try emit.writeInstruction(Instruction.ldrb(
367 load_store_register.rt,
368 load_store_register.rn,
369 .{ .offset = load_store_register.offset },
370 )),
371 .ldrh => try emit.writeInstruction(Instruction.ldrh(
372 load_store_register.rt,
373 load_store_register.rn,
374 .{ .offset = load_store_register.offset },
375 )),
376 .str => try emit.writeInstruction(Instruction.str(
377 load_store_register.rt,
378 load_store_register.rn,
379 .{ .offset = load_store_register.offset },
380 )),
381 .strb => try emit.writeInstruction(Instruction.strb(
382 load_store_register.rt,
383 load_store_register.rn,
384 .{ .offset = load_store_register.offset },
385 )),
386 .strh => try emit.writeInstruction(Instruction.strh(
387 load_store_register.rt,
388 load_store_register.rn,
389 .{ .offset = load_store_register.offset },
390 )),
391 else => unreachable,
392 }
393}
394
395fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
396 const tag = emit.mir.instructions.items(.tag)[inst];
397 const rr = emit.mir.instructions.items(.data)[inst].rr;
398
399 switch (tag) {
400 .mov_register => try emit.writeInstruction(Instruction.orr(rr.rd, .xzr, rr.rn, Instruction.Shift.none)),
401 .mov_to_from_sp => try emit.writeInstruction(Instruction.add(rr.rd, rr.rn, 0, false)),
402 else => unreachable,
403 }
404}
405
406fn mirMoveWideImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
407 const tag = emit.mir.instructions.items(.tag)[inst];
408 const r_imm16_sh = emit.mir.instructions.items(.data)[inst].r_imm16_sh;
409
410 switch (tag) {
411 .movz => try emit.writeInstruction(Instruction.movz(r_imm16_sh.rd, r_imm16_sh.imm16, @as(u6, r_imm16_sh.hw) << 4)),
412 .movk => try emit.writeInstruction(Instruction.movk(r_imm16_sh.rd, r_imm16_sh.imm16, @as(u6, r_imm16_sh.hw) << 4)),
413 else => unreachable,
414 }
415}
416
417fn mirNop(emit: *Emit) !void {
418 try emit.writeInstruction(Instruction.nop());
419}
src/arch/aarch64/Mir.zig created+208
...@@ -0,0 +1,208 @@
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 /// Pseudo-instruction: Call extern
38 call_extern,
39 /// Pseudo-instruction: End of prologue
40 dbg_prologue_end,
41 /// Pseudo-instruction: Beginning of epilogue
42 dbg_epilogue_begin,
43 /// Pseudo-instruction: Update debug line
44 dbg_line,
45 /// Psuedo-instruction: Load memory
46 ///
47 /// Payload is `LoadMemory`
48 load_memory,
49 /// Load Pair of Registers
50 ldp,
51 /// Load Register
52 // TODO: split into ldr_immediate and ldr_register
53 ldr,
54 /// Load Register Byte
55 // TODO: split into ldrb_immediate and ldrb_register
56 ldrb,
57 /// Load Register Halfword
58 // TODO: split into ldrh_immediate and ldrh_register
59 ldrh,
60 /// Move (to/from SP)
61 mov_to_from_sp,
62 /// Move (register)
63 mov_register,
64 /// Move wide with keep
65 movk,
66 /// Move wide with zero
67 movz,
68 /// No Operation
69 nop,
70 /// Return from subroutine
71 ret,
72 /// Store Pair of Registers
73 stp,
74 /// Store Register
75 // TODO: split into str_immediate and str_register
76 str,
77 /// Store Register Byte
78 // TODO: split into strb_immediate and strb_register
79 strb,
80 /// Store Register Halfword
81 // TODO: split into strh_immediate and strh_register
82 strh,
83 /// Subtract (immediate)
84 sub_immediate,
85 /// Supervisor Call
86 svc,
87 };
88
89 /// The position of an MIR instruction within the `Mir` instructions array.
90 pub const Index = u32;
91
92 /// All instructions have a 4-byte payload, which is contained within
93 /// this union. `Tag` determines which union field is active, as well as
94 /// how to interpret the data within.
95 pub const Data = union {
96 /// No additional data
97 ///
98 /// Used by e.g. nop
99 nop: void,
100 /// Another instruction.
101 ///
102 /// Used by e.g. b
103 inst: Index,
104 /// An extern function
105 ///
106 /// Used by e.g. call_extern
107 extern_fn: u32,
108 /// A 16-bit immediate value.
109 ///
110 /// Used by e.g. svc
111 imm16: u16,
112 /// Index into `extra`. Meaning of what can be found there is context-dependent.
113 ///
114 /// Used by e.g. load_memory
115 payload: u32,
116 /// A register
117 ///
118 /// Used by e.g. blr
119 reg: Register,
120 /// A register, an unsigned 16-bit immediate, and an optional shift
121 ///
122 /// Used by e.g. movz
123 r_imm16_sh: struct {
124 rd: Register,
125 imm16: u16,
126 hw: u2 = 0,
127 },
128 /// Two registers
129 ///
130 /// Used by e.g. mov_register
131 rr: struct {
132 rd: Register,
133 rn: Register,
134 },
135 /// Two registers, an unsigned 12-bit immediate, and an optional shift
136 ///
137 /// Used by e.g. sub_immediate
138 rr_imm12_sh: struct {
139 rd: Register,
140 rn: Register,
141 imm12: u12,
142 sh: u1 = 0,
143 },
144 /// Three registers and a LoadStoreOffset
145 ///
146 /// Used by e.g. str_register
147 load_store_register: struct {
148 rt: Register,
149 rn: Register,
150 offset: bits.Instruction.LoadStoreOffset,
151 },
152 /// Three registers and a LoadStorePairOffset
153 ///
154 /// Used by e.g. stp
155 load_store_register_pair: struct {
156 rt: Register,
157 rt2: Register,
158 rn: Register,
159 offset: bits.Instruction.LoadStorePairOffset,
160 },
161 /// Debug info: line and column
162 ///
163 /// Used by e.g. dbg_line
164 dbg_line_column: struct {
165 line: u32,
166 column: u32,
167 },
168 };
169
170 // Make sure we don't accidentally make instructions bigger than expected.
171 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
172 // comptime {
173 // if (builtin.mode != .Debug) {
174 // assert(@sizeOf(Inst) == 8);
175 // }
176 // }
177};
178
179pub fn deinit(mir: *Mir, gpa: *std.mem.Allocator) void {
180 mir.instructions.deinit(gpa);
181 gpa.free(mir.extra);
182 mir.* = undefined;
183}
184
185/// Returns the requested data, as well as the new index which is at the start of the
186/// trailers for the object.
187pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
188 const fields = std.meta.fields(T);
189 var i: usize = index;
190 var result: T = undefined;
191 inline for (fields) |field| {
192 @field(result, field.name) = switch (field.field_type) {
193 u32 => mir.extra[i],
194 i32 => @bitCast(i32, mir.extra[i]),
195 else => @compileError("bad field type"),
196 };
197 i += 1;
198 }
199 return .{
200 .data = result,
201 .end = i,
202 };
203}
204
205pub const LoadMemory = struct {
206 register: u32,
207 addr: u32,
208};
src/codegen.zig+4-3
...@@ -88,9 +88,10 @@ pub fn generateFunction(...@@ -88,9 +88,10 @@ pub fn generateFunction(
88 .wasm64 => unreachable, // has its own code path88 .wasm64 => unreachable, // has its own code path
89 .arm => return Function(.arm).generate(bin_file, src_loc, func, air, liveness, code, debug_output),89 .arm => return Function(.arm).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
90 .armeb => return Function(.armeb).generate(bin_file, src_loc, func, air, liveness, code, debug_output),90 .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),91 .aarch64,
92 .aarch64_be => return @import("arch/aarch64/CodeGen.zig").generate(.aarch64_be, bin_file, src_loc, func, air, liveness, code, debug_output),92 .aarch64_be,
93 .aarch64_32 => return @import("arch/aarch64/CodeGen.zig").generate(.aarch64_32, bin_file, src_loc, func, air, liveness, code, debug_output),93 .aarch64_32,
94 => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
94 //.arc => return Function(.arc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),95 //.arc => return Function(.arc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
95 //.avr => return Function(.avr).generate(bin_file, src_loc, func, air, liveness, code, debug_output),96 //.avr => return Function(.avr).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
96 //.bpfel => return Function(.bpfel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),97 //.bpfel => return Function(.bpfel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),