authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-11-16 19:19:16+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-16 19:52:21-05:00
logd94b032e926ef794b422e5abd504a7755d345eff
tree24c7be14ea71f03fa9754533d2812481ebe445c7
parent0e8673f53415e367ee5db9e1384398e0905c5a35

stage2 ARM: Introduce MIR


3 files changed, 1247 insertions(+), 258 deletions(-)

src/arch/arm/CodeGen.zig+496-258
...@@ -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;
...@@ -22,9 +24,9 @@ const log = std.log.scoped(.codegen);...@@ -22,9 +24,9 @@ const log = std.log.scoped(.codegen);
22const build_options = @import("build_options");24const build_options = @import("build_options");
23const RegisterManager = @import("../../register_manager.zig").RegisterManager;25const RegisterManager = @import("../../register_manager.zig").RegisterManager;
2426
25pub const FnResult = @import("../../codegen.zig").FnResult;27const FnResult = @import("../../codegen.zig").FnResult;
26pub const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;28const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
27pub const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;29const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
2830
29const InnerError = error{31const InnerError = error{
30 OutOfMemory,32 OutOfMemory,
...@@ -37,8 +39,6 @@ liveness: Liveness,...@@ -37,8 +39,6 @@ liveness: Liveness,
37bin_file: *link.File,39bin_file: *link.File,
38target: *const std.Target,40target: *const std.Target,
39mod_fn: *const Module.Fn,41mod_fn: *const Module.Fn,
40code: *std.ArrayList(u8),
41debug_output: DebugInfoOutput,
42err_msg: ?*ErrorMsg,42err_msg: ?*ErrorMsg,
43args: []MCValue,43args: []MCValue,
44ret_mcv: MCValue,44ret_mcv: MCValue,
...@@ -47,13 +47,14 @@ arg_index: usize,...@@ -47,13 +47,14 @@ arg_index: usize,
47src_loc: Module.SrcLoc,47src_loc: Module.SrcLoc,
48stack_align: u32,48stack_align: u32,
4949
50prev_di_line: u32,50/// MIR Instructions
51prev_di_column: u32,51mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
52/// MIR extra data
53mir_extra: std.ArrayListUnmanaged(u32) = .{},
54
52/// Byte offset within the source file of the ending curly.55/// Byte offset within the source file of the ending curly.
53end_di_line: u32,56end_di_line: u32,
54end_di_column: u32,57end_di_column: u32,
55/// Relative to the beginning of `code`.
56prev_di_pc: usize,
5758
58/// 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.
59/// To perform the reloc, write 32-bit signed little-endian integer60/// To perform the reloc, write 32-bit signed little-endian integer
...@@ -176,7 +177,7 @@ const StackAllocation = struct {...@@ -176,7 +177,7 @@ const StackAllocation = struct {
176};177};
177178
178const BlockData = struct {179const BlockData = struct {
179 relocs: std.ArrayListUnmanaged(Reloc),180 relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
180 /// The first break instruction encounters `null` here and chooses a181 /// The first break instruction encounters `null` here and chooses a
181 /// machine code value for the block result, populating this field.182 /// machine code value for the block result, populating this field.
182 /// Following break instructions encounter that value and use it for183 /// Following break instructions encounter that value and use it for
...@@ -184,18 +185,6 @@ const BlockData = struct {...@@ -184,18 +185,6 @@ const BlockData = struct {
184 mcv: MCValue,185 mcv: MCValue,
185};186};
186187
187const Reloc = union(enum) {
188 /// The value is an offset into the `Function` `code` from the beginning.
189 /// To perform the reloc, write 32-bit signed little-endian integer
190 /// which is a relative jump, based on the address following the reloc.
191 rel32: usize,
192 /// A branch in the ARM instruction set
193 arm_branch: struct {
194 pos: usize,
195 cond: @import("bits.zig").Condition,
196 },
197};
198
199const BigTomb = struct {188const BigTomb = struct {
200 function: *Self,189 function: *Self,
201 inst: Air.Inst.Index,190 inst: Air.Inst.Index,
...@@ -266,8 +255,6 @@ pub fn generate(...@@ -266,8 +255,6 @@ pub fn generate(
266 .target = &bin_file.options.target,255 .target = &bin_file.options.target,
267 .bin_file = bin_file,256 .bin_file = bin_file,
268 .mod_fn = module_fn,257 .mod_fn = module_fn,
269 .code = code,
270 .debug_output = debug_output,
271 .err_msg = null,258 .err_msg = null,
272 .args = undefined, // populated after `resolveCallingConventionValues`259 .args = undefined, // populated after `resolveCallingConventionValues`
273 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`260 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
...@@ -276,9 +263,6 @@ pub fn generate(...@@ -276,9 +263,6 @@ pub fn generate(
276 .branch_stack = &branch_stack,263 .branch_stack = &branch_stack,
277 .src_loc = src_loc,264 .src_loc = src_loc,
278 .stack_align = undefined,265 .stack_align = undefined,
279 .prev_di_pc = 0,
280 .prev_di_line = module_fn.lbrace_line,
281 .prev_di_column = module_fn.lbrace_column,
282 .end_di_line = module_fn.rbrace_line,266 .end_di_line = module_fn.rbrace_line,
283 .end_di_column = module_fn.rbrace_column,267 .end_di_column = module_fn.rbrace_column,
284 };268 };
...@@ -302,6 +286,30 @@ pub fn generate(...@@ -302,6 +286,30 @@ pub fn generate(
302 else => |e| return e,286 else => |e| return e,
303 };287 };
304288
289 var mir = Mir{
290 .instructions = function.mir_instructions.toOwnedSlice(),
291 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
292 };
293 defer mir.deinit(bin_file.allocator);
294
295 var emit = Emit{
296 .mir = mir,
297 .bin_file = bin_file,
298 .debug_output = debug_output,
299 .target = &bin_file.options.target,
300 .src_loc = src_loc,
301 .code = code,
302 .prev_di_pc = 0,
303 .prev_di_line = module_fn.lbrace_line,
304 .prev_di_column = module_fn.lbrace_column,
305 };
306 defer emit.deinit();
307
308 emit.emitMir() catch |err| switch (err) {
309 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
310 else => |e| return e,
311 };
312
305 if (function.err_msg) |em| {313 if (function.err_msg) |em| {
306 return FnResult{ .fail = em };314 return FnResult{ .fail = em };
307 } else {315 } else {
...@@ -309,17 +317,68 @@ pub fn generate(...@@ -309,17 +317,68 @@ pub fn generate(
309 }317 }
310}318}
311319
320fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
321 const gpa = self.gpa;
322
323 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
324
325 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);
326 self.mir_instructions.appendAssumeCapacity(inst);
327 return result_index;
328}
329
330fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index {
331 return try self.addInst(.{
332 .tag = .nop,
333 .cond = .al,
334 .data = .{ .nop = {} },
335 });
336}
337
338pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
339 const fields = std.meta.fields(@TypeOf(extra));
340 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
341 return self.addExtraAssumeCapacity(extra);
342}
343
344pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
345 const fields = std.meta.fields(@TypeOf(extra));
346 const result = @intCast(u32, self.mir_extra.items.len);
347 inline for (fields) |field| {
348 self.mir_extra.appendAssumeCapacity(switch (field.field_type) {
349 u32 => @field(extra, field.name),
350 i32 => @bitCast(u32, @field(extra, field.name)),
351 else => @compileError("bad field type"),
352 });
353 }
354 return result;
355}
356
312fn gen(self: *Self) !void {357fn gen(self: *Self) !void {
313 const cc = self.fn_type.fnCallingConvention();358 const cc = self.fn_type.fnCallingConvention();
314 if (cc != .Naked) {359 if (cc != .Naked) {
315 // push {fp, lr}360 // push {fp, lr}
361 const push_reloc = try self.addNop();
362
316 // mov fp, sp363 // mov fp, sp
364 _ = try self.addInst(.{
365 .tag = .mov,
366 .cond = .al,
367 .data = .{ .rr_op = .{
368 .rd = .fp,
369 .rn = .r0,
370 .op = Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none),
371 } },
372 });
373
317 // sub sp, sp, #reloc374 // sub sp, sp, #reloc
318 const prologue_reloc = self.code.items.len;375 const sub_reloc = try self.addNop();
319 try self.code.resize(prologue_reloc + 12);
320 self.writeInt(u32, self.code.items[prologue_reloc + 4 ..][0..4], Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32());
321376
322 try self.dbgSetPrologueEnd();377 _ = try self.addInst(.{
378 .tag = .dbg_prologue_end,
379 .cond = undefined,
380 .data = .{ .nop = {} },
381 });
323382
324 try self.genBody(self.air.getMainBody());383 try self.genBody(self.air.getMainBody());
325384
...@@ -333,18 +392,30 @@ fn gen(self: *Self) !void {...@@ -333,18 +392,30 @@ fn gen(self: *Self) !void {
333 @field(saved_regs, @tagName(reg)) = true;392 @field(saved_regs, @tagName(reg)) = true;
334 }393 }
335 }394 }
336 self.writeInt(u32, self.code.items[prologue_reloc..][0..4], Instruction.stmdb(.al, .sp, true, saved_regs).toU32());395 self.mir_instructions.set(push_reloc, .{
396 .tag = .push,
397 .cond = .al,
398 .data = .{ .register_list = saved_regs },
399 });
337400
338 // Backpatch stack offset401 // Backpatch stack offset
339 const stack_end = self.max_end_stack;402 const stack_end = self.max_end_stack;
340 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);403 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
341 if (Instruction.Operand.fromU32(@intCast(u32, aligned_stack_end))) |op| {404 if (Instruction.Operand.fromU32(@intCast(u32, aligned_stack_end))) |op| {
342 self.writeInt(u32, self.code.items[prologue_reloc + 8 ..][0..4], Instruction.sub(.al, .sp, .sp, op).toU32());405 self.mir_instructions.set(sub_reloc, .{
406 .tag = .sub,
407 .cond = .al,
408 .data = .{ .rr_op = .{ .rd = .sp, .rn = .sp, .op = op } },
409 });
343 } else {410 } else {
344 return self.failSymbol("TODO ARM: allow larger stacks", .{});411 return self.failSymbol("TODO ARM: allow larger stacks", .{});
345 }412 }
346413
347 try self.dbgSetEpilogueBegin();414 _ = try self.addInst(.{
415 .tag = .dbg_epilogue_begin,
416 .cond = undefined,
417 .data = .{ .nop = {} },
418 });
348419
349 // exitlude jumps420 // exitlude jumps
350 if (self.exitlude_jump_relocs.items.len == 1) {421 if (self.exitlude_jump_relocs.items.len == 1) {
...@@ -353,23 +424,13 @@ fn gen(self: *Self) !void {...@@ -353,23 +424,13 @@ fn gen(self: *Self) !void {
353 // the code. Therefore, we can just delete424 // the code. Therefore, we can just delete
354 // the space initially reserved for the425 // the space initially reserved for the
355 // jump426 // jump
356 self.code.items.len -= 4;427 self.mir_instructions.len -= 1;
357 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {428 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
358 const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8);429 self.mir_instructions.set(jmp_reloc, .{
359 if (amt == -4) {430 .tag = .b,
360 // This return is at the end of the431 .cond = .al,
361 // code block. We can't just delete432 .data = .{ .inst = @intCast(u32, self.mir_instructions.len) },
362 // the space because there may be433 });
363 // other jumps we already relocated to
364 // the address. Instead, insert a nop
365 self.writeInt(u32, self.code.items[jmp_reloc..][0..4], Instruction.nop().toU32());
366 } else {
367 if (math.cast(i26, amt)) |offset| {
368 self.writeInt(u32, self.code.items[jmp_reloc..][0..4], Instruction.b(.al, offset).toU32());
369 } else |_| {
370 return self.failSymbol("exitlude jump is too large", .{});
371 }
372 }
373 }434 }
374435
375 // Epilogue: pop callee saved registers (swap lr with pc in saved_regs)436 // Epilogue: pop callee saved registers (swap lr with pc in saved_regs)
...@@ -377,17 +438,47 @@ fn gen(self: *Self) !void {...@@ -377,17 +438,47 @@ fn gen(self: *Self) !void {
377 saved_regs.r15 = true; // pc438 saved_regs.r15 = true; // pc
378439
379 // mov sp, fp440 // mov sp, fp
441 _ = try self.addInst(.{
442 .tag = .mov,
443 .cond = .al,
444 .data = .{ .rr_op = .{
445 .rd = .sp,
446 .rn = .r0,
447 .op = Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none),
448 } },
449 });
450
380 // pop {fp, pc}451 // pop {fp, pc}
381 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());452 _ = try self.addInst(.{
382 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldm(.al, .sp, true, saved_regs).toU32());453 .tag = .pop,
454 .cond = .al,
455 .data = .{ .register_list = saved_regs },
456 });
383 } else {457 } else {
384 try self.dbgSetPrologueEnd();458 _ = try self.addInst(.{
459 .tag = .dbg_prologue_end,
460 .cond = undefined,
461 .data = .{ .nop = {} },
462 });
463
385 try self.genBody(self.air.getMainBody());464 try self.genBody(self.air.getMainBody());
386 try self.dbgSetEpilogueBegin();465
466 _ = try self.addInst(.{
467 .tag = .dbg_epilogue_begin,
468 .cond = undefined,
469 .data = .{ .nop = {} },
470 });
387 }471 }
388472
389 // Drop them off at the rbrace.473 // Drop them off at the rbrace.
390 try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);474 _ = try self.addInst(.{
475 .tag = .dbg_line,
476 .cond = undefined,
477 .data = .{ .dbg_line_column = .{
478 .line = self.end_di_line,
479 .column = self.end_di_column,
480 } },
481 });
391}482}
392483
393fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {484fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
...@@ -534,79 +625,6 @@ fn writeInt(self: *Self, comptime T: type, buf: *[@divExact(@typeInfo(T).Int.bit...@@ -534,79 +625,6 @@ fn writeInt(self: *Self, comptime T: type, buf: *[@divExact(@typeInfo(T).Int.bit
534 std.mem.writeInt(T, buf, value, endian);625 std.mem.writeInt(T, buf, value, endian);
535}626}
536627
537fn dbgSetPrologueEnd(self: *Self) InnerError!void {
538 switch (self.debug_output) {
539 .dwarf => |dbg_out| {
540 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
541 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
542 },
543 .plan9 => {},
544 .none => {},
545 }
546}
547
548fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
549 switch (self.debug_output) {
550 .dwarf => |dbg_out| {
551 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
552 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
553 },
554 .plan9 => {},
555 .none => {},
556 }
557}
558
559fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {
560 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
561 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
562 switch (self.debug_output) {
563 .dwarf => |dbg_out| {
564 // TODO Look into using the DWARF special opcodes to compress this data.
565 // It lets you emit single-byte opcodes that add different numbers to
566 // both the PC and the line number at the same time.
567 try dbg_out.dbg_line.ensureUnusedCapacity(11);
568 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
569 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
570 if (delta_line != 0) {
571 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
572 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
573 }
574 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
575 self.prev_di_pc = self.code.items.len;
576 self.prev_di_line = line;
577 self.prev_di_column = column;
578 self.prev_di_pc = self.code.items.len;
579 },
580 .plan9 => |dbg_out| {
581 if (delta_pc <= 0) return; // only do this when the pc changes
582 // we have already checked the target in the linker to make sure it is compatable
583 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
584
585 // increasing the line number
586 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
587 // increasing the pc
588 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
589 if (d_pc_p9 > 0) {
590 // minus one because if its the last one, we want to leave space to change the line which is one quanta
591 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
592 if (dbg_out.pcop_change_index.*) |pci|
593 dbg_out.dbg_line.items[pci] += 1;
594 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
595 } else if (d_pc_p9 == 0) {
596 // we don't need to do anything, because adding the quant does it for us
597 } else unreachable;
598 if (dbg_out.start_line.* == null)
599 dbg_out.start_line.* = self.prev_di_line;
600 dbg_out.end_line.* = line;
601 // only do this if the pc changed
602 self.prev_di_line = line;
603 self.prev_di_column = column;
604 self.prev_di_pc = self.code.items.len;
605 },
606 .none => {},
607 }
608}
609
610/// Asserts there is already capacity to insert into top branch inst_table.628/// Asserts there is already capacity to insert into top branch inst_table.
611fn processDeath(self: *Self, inst: Air.Inst.Index) void {629fn processDeath(self: *Self, inst: Air.Inst.Index) void {
612 const air_tags = self.air.instructions.items(.tag);630 const air_tags = self.air.instructions.items(.tag);
...@@ -1217,7 +1235,15 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1217,7 +1235,15 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
1217 .compare_flags_signed, .compare_flags_unsigned => unreachable,1235 .compare_flags_signed, .compare_flags_unsigned => unreachable,
1218 .embedded_in_code => unreachable,1236 .embedded_in_code => unreachable,
1219 .register => |dst_reg| {1237 .register => |dst_reg| {
1220 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, dst_reg, reg, .{ .offset = Instruction.Offset.none }).toU32());1238 _ = try self.addInst(.{
1239 .tag = .ldr,
1240 .cond = .al,
1241 .data = .{ .rr_offset = .{
1242 .rt = dst_reg,
1243 .rn = reg,
1244 .offset = .{ .offset = Instruction.Offset.none },
1245 } },
1246 });
1221 },1247 },
1222 else => return self.fail("TODO load from register into {}", .{dst_mcv}),1248 else => return self.fail("TODO load from register into {}", .{dst_mcv}),
1223 }1249 }
...@@ -1513,48 +1539,81 @@ fn genArmBinOpCode(...@@ -1513,48 +1539,81 @@ fn genArmBinOpCode(
1513 };1539 };
15141540
1515 switch (op) {1541 switch (op) {
1516 .add => {1542 .add,
1517 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, op1, operand).toU32());1543 .bool_and,
1544 .bit_and,
1545 .bool_or,
1546 .bit_or,
1547 .not,
1548 .xor,
1549 => {
1550 const tag: Mir.Inst.Tag = switch (op) {
1551 .add => .add,
1552 .bool_and, .bit_and => .@"and",
1553 .bool_or, .bit_or => .orr,
1554 .not, .xor => .eor,
1555 else => unreachable,
1556 };
1557
1558 _ = try self.addInst(.{
1559 .tag = tag,
1560 .cond = .al,
1561 .data = .{ .rr_op = .{
1562 .rd = dst_reg,
1563 .rn = op1,
1564 .op = operand,
1565 } },
1566 });
1518 },1567 },
1519 .sub => {1568 .sub => {
1520 if (swap_lhs_and_rhs) {1569 const tag: Mir.Inst.Tag = if (swap_lhs_and_rhs) .rsb else .sub;
1521 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, op1, operand).toU32());1570
1522 } else {1571 _ = try self.addInst(.{
1523 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, op1, operand).toU32());1572 .tag = tag,
1524 }1573 .cond = .al,
1525 },1574 .data = .{ .rr_op = .{
1526 .bool_and, .bit_and => {1575 .rd = dst_reg,
1527 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, op1, operand).toU32());1576 .rn = op1,
1528 },1577 .op = operand,
1529 .bool_or, .bit_or => {1578 } },
1530 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, op1, operand).toU32());1579 });
1531 },
1532 .not, .xor => {
1533 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, op1, operand).toU32());
1534 },1580 },
1535 .cmp_eq => {1581 .cmp_eq => {
1536 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32());1582 _ = try self.addInst(.{
1583 .tag = .cmp,
1584 .cond = .al,
1585 .data = .{ .rr_op = .{
1586 .rd = .r0,
1587 .rn = op1,
1588 .op = operand,
1589 } },
1590 });
1537 },1591 },
1538 .shl => {1592 .shl, .shr => {
1539 assert(!swap_lhs_and_rhs);
1540 const shift_amount = switch (operand) {
1541 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1542 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
1543 };
1544 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amount).toU32());
1545 },
1546 .shr => {
1547 assert(!swap_lhs_and_rhs);1593 assert(!swap_lhs_and_rhs);
1548 const shift_amount = switch (operand) {1594 const shift_amount = switch (operand) {
1549 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),1595 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1550 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),1596 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
1551 };1597 };
15521598
1553 const shr = switch (signedness) {1599 const tag: Mir.Inst.Tag = switch (op) {
1554 .signed => Instruction.asr,1600 .shl => .lsl,
1555 .unsigned => Instruction.lsr,1601 .shr => switch (signedness) {
1602 .signed => Mir.Inst.Tag.asr,
1603 .unsigned => Mir.Inst.Tag.lsr,
1604 },
1605 else => unreachable,
1556 };1606 };
1557 self.writeInt(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amount).toU32());1607
1608 _ = try self.addInst(.{
1609 .tag = tag,
1610 .cond = .al,
1611 .data = .{ .rr_shift = .{
1612 .rd = dst_reg,
1613 .rm = op1,
1614 .shift_amount = shift_amount,
1615 } },
1616 });
1558 },1617 },
1559 else => unreachable, // not a binary instruction1618 else => unreachable, // not a binary instruction
1560 }1619 }
...@@ -1623,7 +1682,15 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai...@@ -1623,7 +1682,15 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai
1623 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);1682 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);
1624 }1683 }
16251684
1626 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32());1685 _ = try self.addInst(.{
1686 .tag = .mul,
1687 .cond = .al,
1688 .data = .{ .rrr = .{
1689 .rd = dst_mcv.register,
1690 .rn = lhs_mcv.register,
1691 .rm = rhs_mcv.register,
1692 } },
1693 });
1627 return dst_mcv;1694 return dst_mcv;
1628}1695}
16291696
...@@ -1707,7 +1774,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1707,7 +1774,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1707 },1774 },
1708 else => result,1775 else => result,
1709 };1776 };
1710 try self.genArgDbgInfo(inst, mcv);1777 // TODO generate debug info
1778 // try self.genArgDbgInfo(inst, mcv);
17111779
1712 if (self.liveness.isUnused(inst))1780 if (self.liveness.isUnused(inst))
1713 return self.finishAirBookkeeping();1781 return self.finishAirBookkeeping();
...@@ -1723,7 +1791,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1723,7 +1791,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1723}1791}
17241792
1725fn airBreakpoint(self: *Self) !void {1793fn airBreakpoint(self: *Self) !void {
1726 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());1794 _ = try self.addInst(.{
1795 .tag = .bkpt,
1796 .cond = .al,
1797 .data = .{ .imm16 = 0 },
1798 });
1727 return self.finishAirBookkeeping();1799 return self.finishAirBookkeeping();
1728}1800}
17291801
...@@ -1794,10 +1866,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -1794,10 +1866,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1794 // TODO: add Instruction.supportedOn1866 // TODO: add Instruction.supportedOn
1795 // function for ARM1867 // function for ARM
1796 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {1868 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
1797 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());1869 _ = try self.addInst(.{
1870 .tag = .blx,
1871 .cond = .al,
1872 .data = .{ .reg = .lr },
1873 });
1798 } else {1874 } else {
1799 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .lr, Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none)).toU32());1875 return self.fail("TODO fix blx emulatio for ARM <v5", .{});
1800 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());1876 // _ = try self.addInst(.{
1877 // .tag = .mov,
1878 // .cond = .al,
1879 // .data = .{ .rr_op = .{
1880 // .rd = .lr,
1881 // .rn = .r0,
1882 // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none),
1883 // } },
1884 // });
1885 // _ = try self.addInst(.{
1886 // .tag = .bx,
1887 // .cond = .al,
1888 // .data = .{ .reg = .lr },
1889 // });
1801 }1890 }
1802 } else if (func_value.castTag(.extern_fn)) |_| {1891 } else if (func_value.castTag(.extern_fn)) |_| {
1803 return self.fail("TODO implement calling extern functions", .{});1892 return self.fail("TODO implement calling extern functions", .{});
...@@ -1845,8 +1934,7 @@ fn ret(self: *Self, mcv: MCValue) !void {...@@ -1845,8 +1934,7 @@ fn ret(self: *Self, mcv: MCValue) !void {
1845 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);1934 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
18461935
1847 // Just add space for an instruction, patch this later1936 // Just add space for an instruction, patch this later
1848 try self.code.resize(self.code.items.len + 4);1937 try self.exitlude_jump_relocs.append(self.gpa, try self.addNop());
1849 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
1850}1938}
18511939
1852fn airRet(self: *Self, inst: Air.Inst.Index) !void {1940fn airRet(self: *Self, inst: Air.Inst.Index) !void {
...@@ -1925,7 +2013,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -1925,7 +2013,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
19252013
1926fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {2014fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
1927 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;2015 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
1928 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);2016
2017 _ = try self.addInst(.{
2018 .tag = .dbg_line,
2019 .cond = undefined,
2020 .data = .{ .dbg_line_column = .{
2021 .line = dbg_stmt.line,
2022 .column = dbg_stmt.column,
2023 } },
2024 });
2025
1929 return self.finishAirBookkeeping();2026 return self.finishAirBookkeeping();
1930}2027}
19312028
...@@ -1937,7 +2034,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1937,7 +2034,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
1937 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];2034 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1938 const liveness_condbr = self.liveness.getCondBr(inst);2035 const liveness_condbr = self.liveness.getCondBr(inst);
19392036
1940 const reloc: Reloc = reloc: {2037 const reloc: Mir.Inst.Index = reloc: {
1941 const condition: Condition = switch (cond) {2038 const condition: Condition = switch (cond) {
1942 .compare_flags_signed => |cmp_op| blk: {2039 .compare_flags_signed => |cmp_op| blk: {
1943 // Here we map to the opposite condition because the jump is to the false branch.2040 // Here we map to the opposite condition because the jump is to the false branch.
...@@ -1952,21 +2049,26 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1952,21 +2049,26 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
1952 .register => |reg| blk: {2049 .register => |reg| blk: {
1953 // cmp reg, 12050 // cmp reg, 1
1954 // bne ...2051 // bne ...
1955 const op = Instruction.Operand.imm(1, 0);2052 _ = try self.addInst(.{
1956 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32());2053 .tag = .cmp,
2054 .cond = .al,
2055 .data = .{ .rr_op = .{
2056 .rd = .r0,
2057 .rn = reg,
2058 .op = Instruction.Operand.imm(1, 0),
2059 } },
2060 });
2061
1957 break :blk .ne;2062 break :blk .ne;
1958 },2063 },
1959 else => return self.fail("TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),2064 else => return self.fail("TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
1960 };2065 };
19612066
1962 const reloc = Reloc{2067 break :reloc try self.addInst(.{
1963 .arm_branch = .{2068 .tag = .b,
1964 .pos = self.code.items.len,2069 .cond = condition,
1965 .cond = condition,2070 .data = .{ .inst = undefined }, // populated later through performReloc
1966 },2071 });
1967 };
1968 try self.code.resize(self.code.items.len + 4);
1969 break :reloc reloc;
1970 };2072 };
19712073
1972 // Capture the state of register and stack allocation state so that we can revert to it.2074 // Capture the state of register and stack allocation state so that we can revert to it.
...@@ -2225,19 +2327,19 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {...@@ -2225,19 +2327,19 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
2225 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2327 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2226 const loop = self.air.extraData(Air.Block, ty_pl.payload);2328 const loop = self.air.extraData(Air.Block, ty_pl.payload);
2227 const body = self.air.extra[loop.end..][0..loop.data.body_len];2329 const body = self.air.extra[loop.end..][0..loop.data.body_len];
2228 const start_index = self.code.items.len;2330 const start_index = @intCast(Mir.Inst.Index, self.mir_instructions.len);
2229 try self.genBody(body);2331 try self.genBody(body);
2230 try self.jump(start_index);2332 try self.jump(start_index);
2231 return self.finishAirBookkeeping();2333 return self.finishAirBookkeeping();
2232}2334}
22332335
2234/// Send control flow to the `index` of `self.code`.2336/// Send control flow to `inst`.
2235fn jump(self: *Self, index: usize) !void {2337fn jump(self: *Self, inst: Mir.Inst.Index) !void {
2236 if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {2338 _ = try self.addInst(.{
2237 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32());2339 .tag = .b,
2238 } else |_| {2340 .cond = .al,
2239 return self.fail("TODO: enable larger branch offset", .{});2341 .data = .{ .inst = inst },
2240 }2342 });
2241}2343}
22422344
2243fn airBlock(self: *Self, inst: Air.Inst.Index) !void {2345fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2273,28 +2375,11 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -2273,28 +2375,11 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
2273 // return self.finishAir(inst, .dead, .{ condition, .none, .none });2375 // return self.finishAir(inst, .dead, .{ condition, .none, .none });
2274}2376}
22752377
2276fn performReloc(self: *Self, reloc: Reloc) !void {2378fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
2277 switch (reloc) {2379 const tag = self.mir_instructions.items(.tag)[inst];
2278 .rel32 => |pos| {2380 switch (tag) {
2279 const amt = self.code.items.len - (pos + 4);2381 .b => self.mir_instructions.items(.data)[inst].inst = @intCast(Air.Inst.Index, self.mir_instructions.len),
2280 // Here it would be tempting to implement testing for amt == 0 and then elide the2382 else => unreachable,
2281 // jump. However, that will cause a problem because other jumps may assume that they
2282 // can jump to this code. Or maybe I didn't understand something when I was debugging.
2283 // It could be worth another look. Anyway, that's why that isn't done here. Probably the
2284 // best place to elide jumps will be in semantic analysis, by inlining blocks that only
2285 // only have 1 break instruction.
2286 const s32_amt = math.cast(i32, amt) catch
2287 return self.fail("unable to perform relocation: jump too far", .{});
2288 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
2289 },
2290 .arm_branch => |info| {
2291 const amt = @intCast(i32, self.code.items.len) - @intCast(i32, info.pos + 8);
2292 if (math.cast(i26, amt)) |delta| {
2293 self.writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32());
2294 } else |_| {
2295 return self.fail("TODO: enable larger branch offset", .{});
2296 }
2297 },
2298 }2383 }
2299}2384}
23002385
...@@ -2334,15 +2419,11 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -2334,15 +2419,11 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
2334 const block_data = self.blocks.getPtr(block).?;2419 const block_data = self.blocks.getPtr(block).?;
23352420
2336 // Emit a jump with a relocation. It will be patched up after the block ends.2421 // Emit a jump with a relocation. It will be patched up after the block ends.
2337 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);2422 try block_data.relocs.append(self.gpa, try self.addInst(.{
23382423 .tag = .b,
2339 try self.code.resize(self.code.items.len + 4);2424 .cond = .al,
2340 block_data.relocs.appendAssumeCapacity(.{2425 .data = .{ .inst = undefined }, // populated later through performReloc
2341 .arm_branch = .{2426 }));
2342 .pos = self.code.items.len - 4,
2343 .cond = .al,
2344 },
2345 });
2346}2427}
23472428
2348fn airAsm(self: *Self, inst: Air.Inst.Index) !void {2429fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2394,7 +2475,11 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -2394,7 +2475,11 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2394 }2475 }
23952476
2396 if (mem.eql(u8, asm_source, "svc #0")) {2477 if (mem.eql(u8, asm_source, "svc #0")) {
2397 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());2478 _ = try self.addInst(.{
2479 .tag = .svc,
2480 .cond = .al,
2481 .data = .{ .imm24 = 0 },
2482 });
2398 } else {2483 } else {
2399 return self.fail("TODO implement support for more arm assembly instructions", .{});2484 return self.fail("TODO implement support for more arm assembly instructions", .{});
2400 }2485 }
...@@ -2490,26 +2575,43 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -2490,26 +2575,43 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
2490 const offset = if (math.cast(u12, adj_off)) |imm| blk: {2575 const offset = if (math.cast(u12, adj_off)) |imm| blk: {
2491 break :blk Instruction.Offset.imm(imm);2576 break :blk Instruction.Offset.imm(imm);
2492 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);2577 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
2493 const str = switch (abi_size) {2578
2494 1 => Instruction.strb,2579 const tag: Mir.Inst.Tag = switch (abi_size) {
2495 4 => Instruction.str,2580 1 => .strb,
2581 4 => .str,
2496 else => unreachable,2582 else => unreachable,
2497 };2583 };
24982584
2499 self.writeInt(u32, try self.code.addManyAsArray(4), str(.al, reg, .fp, .{2585 _ = try self.addInst(.{
2500 .offset = offset,2586 .tag = tag,
2501 .positive = false,2587 .cond = .al,
2502 }).toU32());2588 .data = .{ .rr_offset = .{
2589 .rt = reg,
2590 .rn = .fp,
2591 .offset = .{
2592 .offset = offset,
2593 .positive = false,
2594 },
2595 } },
2596 });
2503 },2597 },
2504 2 => {2598 2 => {
2505 const offset = if (adj_off <= math.maxInt(u8)) blk: {2599 const offset = if (adj_off <= math.maxInt(u8)) blk: {
2506 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));2600 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
2507 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));2601 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
25082602
2509 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{2603 _ = try self.addInst(.{
2510 .offset = offset,2604 .tag = .strh,
2511 .positive = false,2605 .cond = .al,
2512 }).toU32());2606 .data = .{ .rr_extra_offset = .{
2607 .rt = reg,
2608 .rn = .fp,
2609 .offset = .{
2610 .offset = offset,
2611 .positive = false,
2612 },
2613 } },
2614 });
2513 },2615 },
2514 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),2616 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
2515 }2617 }
...@@ -2549,26 +2651,83 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2549,26 +2651,83 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2549 else => unreachable,2651 else => unreachable,
2550 };2652 };
25512653
2552 // mov reg, 0
2553 // moveq reg, 1
2554 const zero = Instruction.Operand.imm(0, 0);2654 const zero = Instruction.Operand.imm(0, 0);
2555 const one = Instruction.Operand.imm(1, 0);2655 const one = Instruction.Operand.imm(1, 0);
2556 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, zero).toU32());2656
2557 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32());2657 // mov reg, 0
2658 _ = try self.addInst(.{
2659 .tag = .mov,
2660 .cond = .al,
2661 .data = .{ .rr_op = .{
2662 .rd = reg,
2663 .rn = .r0,
2664 .op = zero,
2665 } },
2666 });
2667
2668 // moveq reg, 1
2669 _ = try self.addInst(.{
2670 .tag = .mov,
2671 .cond = condition,
2672 .data = .{ .rr_op = .{
2673 .rd = reg,
2674 .rn = .r0,
2675 .op = one,
2676 } },
2677 });
2558 },2678 },
2559 .immediate => |x| {2679 .immediate => |x| {
2560 if (x > math.maxInt(u32)) return self.fail("ARM registers are 32-bit wide", .{});2680 if (x > math.maxInt(u32)) return self.fail("ARM registers are 32-bit wide", .{});
25612681
2562 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {2682 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {
2563 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, op).toU32());2683 _ = try self.addInst(.{
2684 .tag = .mov,
2685 .cond = .al,
2686 .data = .{ .rr_op = .{
2687 .rd = reg,
2688 .rn = .r0,
2689 .op = op,
2690 } },
2691 });
2564 } else if (Instruction.Operand.fromU32(~@intCast(u32, x))) |op| {2692 } else if (Instruction.Operand.fromU32(~@intCast(u32, x))) |op| {
2565 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mvn(.al, reg, op).toU32());2693 _ = try self.addInst(.{
2694 .tag = .mvn,
2695 .cond = .al,
2696 .data = .{ .rr_op = .{
2697 .rd = reg,
2698 .rn = .r0,
2699 .op = op,
2700 } },
2701 });
2566 } else if (x <= math.maxInt(u16)) {2702 } else if (x <= math.maxInt(u16)) {
2567 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) {2703 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) {
2568 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.movw(.al, reg, @intCast(u16, x)).toU32());2704 _ = try self.addInst(.{
2705 .tag = .movw,
2706 .cond = .al,
2707 .data = .{ .r_imm16 = .{
2708 .rd = reg,
2709 .imm16 = @intCast(u16, x),
2710 } },
2711 });
2569 } else {2712 } else {
2570 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());2713 _ = try self.addInst(.{
2571 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());2714 .tag = .mov,
2715 .cond = .al,
2716 .data = .{ .rr_op = .{
2717 .rd = reg,
2718 .rn = .r0,
2719 .op = Instruction.Operand.imm(@truncate(u8, x), 0),
2720 } },
2721 });
2722 _ = try self.addInst(.{
2723 .tag = .orr,
2724 .cond = .al,
2725 .data = .{ .rr_op = .{
2726 .rd = reg,
2727 .rn = reg,
2728 .op = Instruction.Operand.imm(@truncate(u8, x >> 8), 12),
2729 } },
2730 });
2572 }2731 }
2573 } else {2732 } else {
2574 // TODO write constant to code and load2733 // TODO write constant to code and load
...@@ -2577,18 +2736,64 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2577,18 +2736,64 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2577 // immediate: 0xaaaabbbb2736 // immediate: 0xaaaabbbb
2578 // movw reg, #0xbbbb2737 // movw reg, #0xbbbb
2579 // movt reg, #0xaaaa2738 // movt reg, #0xaaaa
2580 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.movw(.al, reg, @truncate(u16, x)).toU32());2739 _ = try self.addInst(.{
2581 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.movt(.al, reg, @truncate(u16, x >> 16)).toU32());2740 .tag = .movw,
2741 .cond = .al,
2742 .data = .{ .r_imm16 = .{
2743 .rd = reg,
2744 .imm16 = @truncate(u16, x),
2745 } },
2746 });
2747 _ = try self.addInst(.{
2748 .tag = .movt,
2749 .cond = .al,
2750 .data = .{ .r_imm16 = .{
2751 .rd = reg,
2752 .imm16 = @truncate(u16, x >> 16),
2753 } },
2754 });
2582 } else {2755 } else {
2583 // immediate: 0xaabbccdd2756 // immediate: 0xaabbccdd
2584 // mov reg, #0xaa2757 // mov reg, #0xaa
2585 // orr reg, reg, #0xbb, 242758 // orr reg, reg, #0xbb, 24
2586 // orr reg, reg, #0xcc, 162759 // orr reg, reg, #0xcc, 16
2587 // orr reg, reg, #0xdd, 82760 // orr reg, reg, #0xdd, 8
2588 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());2761 _ = try self.addInst(.{
2589 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());2762 .tag = .mov,
2590 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());2763 .cond = .al,
2591 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());2764 .data = .{ .rr_op = .{
2765 .rd = reg,
2766 .rn = .r0,
2767 .op = Instruction.Operand.imm(@truncate(u8, x), 0),
2768 } },
2769 });
2770 _ = try self.addInst(.{
2771 .tag = .orr,
2772 .cond = .al,
2773 .data = .{ .rr_op = .{
2774 .rd = reg,
2775 .rn = reg,
2776 .op = Instruction.Operand.imm(@truncate(u8, x >> 8), 12),
2777 } },
2778 });
2779 _ = try self.addInst(.{
2780 .tag = .orr,
2781 .cond = .al,
2782 .data = .{ .rr_op = .{
2783 .rd = reg,
2784 .rn = reg,
2785 .op = Instruction.Operand.imm(@truncate(u8, x >> 16), 8),
2786 } },
2787 });
2788 _ = try self.addInst(.{
2789 .tag = .orr,
2790 .cond = .al,
2791 .data = .{ .rr_op = .{
2792 .rd = reg,
2793 .rn = reg,
2794 .op = Instruction.Operand.imm(@truncate(u8, x >> 24), 4),
2795 } },
2796 });
2592 }2797 }
2593 }2798 }
2594 },2799 },
...@@ -2598,13 +2803,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2598,13 +2803,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2598 return;2803 return;
25992804
2600 // mov reg, src_reg2805 // mov reg, src_reg
2601 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none)).toU32());2806 _ = try self.addInst(.{
2807 .tag = .mov,
2808 .cond = .al,
2809 .data = .{ .rr_op = .{
2810 .rd = reg,
2811 .rn = .r0,
2812 .op = Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none),
2813 } },
2814 });
2602 },2815 },
2603 .memory => |addr| {2816 .memory => |addr| {
2604 // The value is in memory at a hard-coded address.2817 // The value is in memory at a hard-coded address.
2605 // If the type is a pointer, it means the pointer address is at this memory location.2818 // If the type is a pointer, it means the pointer address is at this memory location.
2606 try self.genSetReg(ty, reg, .{ .immediate = addr });2819 try self.genSetReg(ty, reg, .{ .immediate = addr });
2607 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());2820 _ = try self.addInst(.{
2821 .tag = .ldr,
2822 .cond = .al,
2823 .data = .{ .rr_offset = .{
2824 .rt = reg,
2825 .rn = reg,
2826 .offset = .{ .offset = Instruction.Offset.none },
2827 } },
2828 });
2608 },2829 },
2609 .stack_offset => |unadjusted_off| {2830 .stack_offset => |unadjusted_off| {
2610 // TODO: maybe addressing from sp instead of fp2831 // TODO: maybe addressing from sp instead of fp
...@@ -2616,26 +2837,43 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2616,26 +2837,43 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2616 const offset = if (adj_off <= math.maxInt(u12)) blk: {2837 const offset = if (adj_off <= math.maxInt(u12)) blk: {
2617 break :blk Instruction.Offset.imm(@intCast(u12, adj_off));2838 break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
2618 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);2839 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
2619 const ldr = switch (abi_size) {2840
2620 1 => Instruction.ldrb,2841 const tag: Mir.Inst.Tag = switch (abi_size) {
2621 4 => Instruction.ldr,2842 1 => .ldrb,
2843 4 => .ldr,
2622 else => unreachable,2844 else => unreachable,
2623 };2845 };
26242846
2625 self.writeInt(u32, try self.code.addManyAsArray(4), ldr(.al, reg, .fp, .{2847 _ = try self.addInst(.{
2626 .offset = offset,2848 .tag = tag,
2627 .positive = false,2849 .cond = .al,
2628 }).toU32());2850 .data = .{ .rr_offset = .{
2851 .rt = reg,
2852 .rn = .fp,
2853 .offset = .{
2854 .offset = offset,
2855 .positive = false,
2856 },
2857 } },
2858 });
2629 },2859 },
2630 2 => {2860 2 => {
2631 const offset = if (adj_off <= math.maxInt(u8)) blk: {2861 const offset = if (adj_off <= math.maxInt(u8)) blk: {
2632 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));2862 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
2633 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));2863 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
26342864
2635 self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{2865 _ = try self.addInst(.{
2636 .offset = offset,2866 .tag = .ldrh,
2637 .positive = false,2867 .cond = .al,
2638 }).toU32());2868 .data = .{ .rr_extra_offset = .{
2869 .rt = reg,
2870 .rn = .fp,
2871 .offset = .{
2872 .offset = offset,
2873 .positive = false,
2874 },
2875 } },
2876 });
2639 },2877 },
2640 else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}),2878 else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}),
2641 }2879 }
src/arch/arm/Emit.zig created+531
...@@ -0,0 +1,531 @@
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 log = std.log.scoped(.aarch64_emit);
18const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
19
20mir: Mir,
21bin_file: *link.File,
22debug_output: DebugInfoOutput,
23target: *const std.Target,
24err_msg: ?*ErrorMsg = null,
25src_loc: Module.SrcLoc,
26code: *std.ArrayList(u8),
27
28prev_di_line: u32,
29prev_di_column: u32,
30/// Relative to the beginning of `code`.
31prev_di_pc: usize,
32
33/// The branch type of every branch
34branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .{},
35/// For every forward branch, maps the target instruction to a list of
36/// branches which branch to this target instruction
37branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayListUnmanaged(Mir.Inst.Index)) = .{},
38/// For backward branches: stores the code offset of the target
39/// instruction
40///
41/// For forward branches: stores the code offset of the branch
42/// instruction
43code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{},
44
45const InnerError = error{
46 OutOfMemory,
47 EmitFail,
48};
49
50const BranchType = enum {
51 b,
52
53 fn default(tag: Mir.Inst.Tag) BranchType {
54 return switch (tag) {
55 .b => .b,
56 else => unreachable,
57 };
58 }
59};
60
61pub fn emitMir(
62 emit: *Emit,
63) !void {
64 const mir_tags = emit.mir.instructions.items(.tag);
65
66 // Find smallest lowerings for branch instructions
67 try emit.lowerBranches();
68
69 // Emit machine code
70 for (mir_tags) |tag, index| {
71 const inst = @intCast(u32, index);
72 switch (tag) {
73 .add => try emit.mirDataProcessing(inst),
74 .@"and" => try emit.mirDataProcessing(inst),
75 .cmp => try emit.mirDataProcessing(inst),
76 .eor => try emit.mirDataProcessing(inst),
77 .mov => try emit.mirDataProcessing(inst),
78 .mvn => try emit.mirDataProcessing(inst),
79 .orr => try emit.mirDataProcessing(inst),
80 .rsb => try emit.mirDataProcessing(inst),
81 .sub => try emit.mirDataProcessing(inst),
82
83 .asr => try emit.mirShift(inst),
84 .lsl => try emit.mirShift(inst),
85 .lsr => try emit.mirShift(inst),
86
87 .b => try emit.mirBranch(inst),
88
89 .bkpt => try emit.mirExceptionGeneration(inst),
90
91 .blx => try emit.mirBranchExchange(inst),
92 .bx => try emit.mirBranchExchange(inst),
93
94 .dbg_line => try emit.mirDbgLine(inst),
95
96 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
97
98 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
99
100 .ldr => try emit.mirLoadStore(inst),
101 .ldrb => try emit.mirLoadStore(inst),
102 .str => try emit.mirLoadStore(inst),
103 .strb => try emit.mirLoadStore(inst),
104
105 .ldrh => try emit.mirLoadStoreExtra(inst),
106 .strh => try emit.mirLoadStoreExtra(inst),
107
108 .movw => try emit.mirSpecialMove(inst),
109 .movt => try emit.mirSpecialMove(inst),
110
111 .mul => try emit.mirMultiply(inst),
112
113 .nop => try emit.mirNop(),
114
115 .pop => try emit.mirBlockDataTransfer(inst),
116 .push => try emit.mirBlockDataTransfer(inst),
117
118 .svc => try emit.mirSupervisorCall(inst),
119 }
120 }
121}
122
123pub fn deinit(emit: *Emit) void {
124 var iter = emit.branch_forward_origins.valueIterator();
125 while (iter.next()) |origin_list| {
126 origin_list.deinit(emit.bin_file.allocator);
127 }
128
129 emit.branch_types.deinit(emit.bin_file.allocator);
130 emit.branch_forward_origins.deinit(emit.bin_file.allocator);
131 emit.code_offset_mapping.deinit(emit.bin_file.allocator);
132 emit.* = undefined;
133}
134
135fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType {
136 assert(std.mem.isAlignedGeneric(i64, offset, 4)); // misaligned offset
137
138 switch (tag) {
139 .b => {
140 if (std.math.cast(i24, @divExact(offset, 4))) |_| {
141 return BranchType.b;
142 } else |_| {
143 return emit.fail("TODO support larger branches", .{});
144 }
145 },
146 else => unreachable,
147 }
148}
149
150fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
151 const tag = emit.mir.instructions.items(.tag)[inst];
152
153 if (isBranch(tag)) {
154 switch (emit.branch_types.get(inst).?) {
155 .b => return 4,
156 }
157 }
158
159 switch (tag) {
160 .dbg_line,
161 .dbg_epilogue_begin,
162 .dbg_prologue_end,
163 => return 0,
164 else => return 4,
165 }
166}
167
168fn isBranch(tag: Mir.Inst.Tag) bool {
169 return switch (tag) {
170 .b => true,
171 else => false,
172 };
173}
174
175fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index {
176 const tag = emit.mir.instructions.items(.tag)[inst];
177
178 switch (tag) {
179 .b => return emit.mir.instructions.items(.data)[inst].inst,
180 else => unreachable,
181 }
182}
183
184fn lowerBranches(emit: *Emit) !void {
185 const mir_tags = emit.mir.instructions.items(.tag);
186 const allocator = emit.bin_file.allocator;
187
188 // First pass: Note down all branches and their target
189 // instructions, i.e. populate branch_types,
190 // branch_forward_origins, and code_offset_mapping
191 //
192 // TODO optimization opportunity: do this in codegen while
193 // generating MIR
194 for (mir_tags) |tag, index| {
195 const inst = @intCast(u32, index);
196 if (isBranch(tag)) {
197 const target_inst = emit.branchTarget(inst);
198
199 // Remember this branch instruction
200 try emit.branch_types.put(allocator, inst, BranchType.default(tag));
201
202 // Forward branches require some extra stuff: We only
203 // know their offset once we arrive at the target
204 // instruction. Therefore, we need to be able to
205 // access the branch instruction when we visit the
206 // target instruction in order to manipulate its type
207 // etc.
208 if (target_inst > inst) {
209 // Remember the branch instruction index
210 try emit.code_offset_mapping.put(allocator, inst, 0);
211
212 if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| {
213 try origin_list.append(allocator, inst);
214 } else {
215 var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .{};
216 try origin_list.append(allocator, inst);
217 try emit.branch_forward_origins.put(allocator, target_inst, origin_list);
218 }
219 }
220
221 // Remember the target instruction index so that we
222 // update the real code offset in all future passes
223 //
224 // putNoClobber may not be used as the put operation
225 // may clobber the entry when multiple branches branch
226 // to the same target instruction
227 try emit.code_offset_mapping.put(allocator, target_inst, 0);
228 }
229 }
230
231 // Further passes: Until all branches are lowered, interate
232 // through all instructions and calculate new offsets and
233 // potentially new branch types
234 var all_branches_lowered = false;
235 while (!all_branches_lowered) {
236 all_branches_lowered = true;
237 var current_code_offset: usize = 0;
238
239 for (mir_tags) |tag, index| {
240 const inst = @intCast(u32, index);
241
242 // If this instruction contained in the code offset
243 // mapping (when it is a target of a branch or if it is a
244 // forward branch), update the code offset
245 if (emit.code_offset_mapping.getPtr(inst)) |offset| {
246 offset.* = current_code_offset;
247 }
248
249 // If this instruction is a backward branch, calculate the
250 // offset, which may potentially update the branch type
251 if (isBranch(tag)) {
252 const target_inst = emit.branchTarget(inst);
253 if (target_inst < inst) {
254 const target_offset = emit.code_offset_mapping.get(target_inst).?;
255 const offset = @intCast(i64, target_offset) - @intCast(i64, current_code_offset + 8);
256 const branch_type = emit.branch_types.getPtr(inst).?;
257 const optimal_branch_type = try emit.optimalBranchType(tag, offset);
258 if (branch_type.* != optimal_branch_type) {
259 branch_type.* = optimal_branch_type;
260 all_branches_lowered = false;
261 }
262
263 log.debug("lowerBranches: branch {} has offset {}", .{ inst, offset });
264 }
265 }
266
267 // If this instruction is the target of one or more
268 // forward branches, calculate the offset, which may
269 // potentially update the branch type
270 if (emit.branch_forward_origins.get(inst)) |origin_list| {
271 for (origin_list.items) |forward_branch_inst| {
272 const branch_tag = emit.mir.instructions.items(.tag)[forward_branch_inst];
273 const forward_branch_inst_offset = emit.code_offset_mapping.get(forward_branch_inst).?;
274 const offset = @intCast(i64, current_code_offset) - @intCast(i64, forward_branch_inst_offset + 8);
275 const branch_type = emit.branch_types.getPtr(forward_branch_inst).?;
276 const optimal_branch_type = try emit.optimalBranchType(branch_tag, offset);
277 if (branch_type.* != optimal_branch_type) {
278 branch_type.* = optimal_branch_type;
279 all_branches_lowered = false;
280 }
281
282 log.debug("lowerBranches: branch {} has offset {}", .{ forward_branch_inst, offset });
283 }
284 }
285
286 // Increment code offset
287 current_code_offset += emit.instructionSize(inst);
288 }
289 }
290}
291
292fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
293 const endian = emit.target.cpu.arch.endian();
294 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
295}
296
297fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
298 @setCold(true);
299 assert(emit.err_msg == null);
300 emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
301 return error.EmitFail;
302}
303
304fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
305 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
306 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
307 switch (self.debug_output) {
308 .dwarf => |dbg_out| {
309 // TODO Look into using the DWARF special opcodes to compress this data.
310 // It lets you emit single-byte opcodes that add different numbers to
311 // both the PC and the line number at the same time.
312 try dbg_out.dbg_line.ensureUnusedCapacity(11);
313 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
314 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
315 if (delta_line != 0) {
316 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
317 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
318 }
319 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
320 self.prev_di_pc = self.code.items.len;
321 self.prev_di_line = line;
322 self.prev_di_column = column;
323 self.prev_di_pc = self.code.items.len;
324 },
325 .plan9 => |dbg_out| {
326 if (delta_pc <= 0) return; // only do this when the pc changes
327 // we have already checked the target in the linker to make sure it is compatable
328 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
329
330 // increasing the line number
331 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
332 // increasing the pc
333 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
334 if (d_pc_p9 > 0) {
335 // minus one because if its the last one, we want to leave space to change the line which is one quanta
336 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
337 if (dbg_out.pcop_change_index.*) |pci|
338 dbg_out.dbg_line.items[pci] += 1;
339 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
340 } else if (d_pc_p9 == 0) {
341 // we don't need to do anything, because adding the quant does it for us
342 } else unreachable;
343 if (dbg_out.start_line.* == null)
344 dbg_out.start_line.* = self.prev_di_line;
345 dbg_out.end_line.* = line;
346 // only do this if the pc changed
347 self.prev_di_line = line;
348 self.prev_di_column = column;
349 self.prev_di_pc = self.code.items.len;
350 },
351 .none => {},
352 }
353}
354
355fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {
356 const tag = emit.mir.instructions.items(.tag)[inst];
357 const cond = emit.mir.instructions.items(.cond)[inst];
358 const rr_op = emit.mir.instructions.items(.data)[inst].rr_op;
359
360 switch (tag) {
361 .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)),
362 .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)),
363 .cmp => try emit.writeInstruction(Instruction.cmp(cond, rr_op.rn, rr_op.op)),
364 .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)),
365 .mov => try emit.writeInstruction(Instruction.mov(cond, rr_op.rd, rr_op.op)),
366 .mvn => try emit.writeInstruction(Instruction.mvn(cond, rr_op.rd, rr_op.op)),
367 .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)),
368 .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)),
369 .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)),
370 else => unreachable,
371 }
372}
373
374fn mirShift(emit: *Emit, inst: Mir.Inst.Index) !void {
375 const tag = emit.mir.instructions.items(.tag)[inst];
376 const cond = emit.mir.instructions.items(.cond)[inst];
377 const rr_shift = emit.mir.instructions.items(.data)[inst].rr_shift;
378
379 switch (tag) {
380 .asr => try emit.writeInstruction(Instruction.asr(cond, rr_shift.rd, rr_shift.rm, rr_shift.shift_amount)),
381 .lsl => try emit.writeInstruction(Instruction.lsl(cond, rr_shift.rd, rr_shift.rm, rr_shift.shift_amount)),
382 .lsr => try emit.writeInstruction(Instruction.lsr(cond, rr_shift.rd, rr_shift.rm, rr_shift.shift_amount)),
383 else => unreachable,
384 }
385}
386
387fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
388 const tag = emit.mir.instructions.items(.tag)[inst];
389 const cond = emit.mir.instructions.items(.cond)[inst];
390 const target_inst = emit.mir.instructions.items(.data)[inst].inst;
391
392 const offset = @intCast(i64, emit.code_offset_mapping.get(target_inst).?) - @intCast(i64, emit.code.items.len + 8);
393 const branch_type = emit.branch_types.get(inst).?;
394
395 switch (branch_type) {
396 .b => switch (tag) {
397 .b => try emit.writeInstruction(Instruction.b(cond, @intCast(i26, offset))),
398 else => unreachable,
399 },
400 }
401}
402
403fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {
404 const tag = emit.mir.instructions.items(.tag)[inst];
405 const imm16 = emit.mir.instructions.items(.data)[inst].imm16;
406
407 switch (tag) {
408 .bkpt => try emit.writeInstruction(Instruction.bkpt(imm16)),
409 else => unreachable,
410 }
411}
412
413fn mirBranchExchange(emit: *Emit, inst: Mir.Inst.Index) !void {
414 const tag = emit.mir.instructions.items(.tag)[inst];
415 const cond = emit.mir.instructions.items(.cond)[inst];
416 const reg = emit.mir.instructions.items(.data)[inst].reg;
417
418 switch (tag) {
419 .blx => try emit.writeInstruction(Instruction.blx(cond, reg)),
420 .bx => try emit.writeInstruction(Instruction.bx(cond, reg)),
421 else => unreachable,
422 }
423}
424
425fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
426 const tag = emit.mir.instructions.items(.tag)[inst];
427 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
428
429 switch (tag) {
430 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
431 else => unreachable,
432 }
433}
434
435fn mirDebugPrologueEnd(emit: *Emit) !void {
436 switch (emit.debug_output) {
437 .dwarf => |dbg_out| {
438 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
439 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
440 },
441 .plan9 => {},
442 .none => {},
443 }
444}
445
446fn mirDebugEpilogueBegin(emit: *Emit) !void {
447 switch (emit.debug_output) {
448 .dwarf => |dbg_out| {
449 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
450 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
451 },
452 .plan9 => {},
453 .none => {},
454 }
455}
456
457fn mirLoadStore(emit: *Emit, inst: Mir.Inst.Index) !void {
458 const tag = emit.mir.instructions.items(.tag)[inst];
459 const cond = emit.mir.instructions.items(.cond)[inst];
460 const rr_offset = emit.mir.instructions.items(.data)[inst].rr_offset;
461
462 switch (tag) {
463 .ldr => try emit.writeInstruction(Instruction.ldr(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)),
464 .ldrb => try emit.writeInstruction(Instruction.ldrb(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)),
465 .str => try emit.writeInstruction(Instruction.str(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)),
466 .strb => try emit.writeInstruction(Instruction.strb(cond, rr_offset.rt, rr_offset.rn, rr_offset.offset)),
467 else => unreachable,
468 }
469}
470
471fn mirLoadStoreExtra(emit: *Emit, inst: Mir.Inst.Index) !void {
472 const tag = emit.mir.instructions.items(.tag)[inst];
473 const cond = emit.mir.instructions.items(.cond)[inst];
474 const rr_extra_offset = emit.mir.instructions.items(.data)[inst].rr_extra_offset;
475
476 switch (tag) {
477 .ldrh => try emit.writeInstruction(Instruction.ldrh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)),
478 .strh => try emit.writeInstruction(Instruction.strh(cond, rr_extra_offset.rt, rr_extra_offset.rn, rr_extra_offset.offset)),
479 else => unreachable,
480 }
481}
482
483fn mirSpecialMove(emit: *Emit, inst: Mir.Inst.Index) !void {
484 const tag = emit.mir.instructions.items(.tag)[inst];
485 const cond = emit.mir.instructions.items(.cond)[inst];
486 const r_imm16 = emit.mir.instructions.items(.data)[inst].r_imm16;
487
488 switch (tag) {
489 .movw => try emit.writeInstruction(Instruction.movw(cond, r_imm16.rd, r_imm16.imm16)),
490 .movt => try emit.writeInstruction(Instruction.movt(cond, r_imm16.rd, r_imm16.imm16)),
491 else => unreachable,
492 }
493}
494
495fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void {
496 const tag = emit.mir.instructions.items(.tag)[inst];
497 const cond = emit.mir.instructions.items(.cond)[inst];
498 const rrr = emit.mir.instructions.items(.data)[inst].rrr;
499
500 switch (tag) {
501 .mul => try emit.writeInstruction(Instruction.mul(cond, rrr.rd, rrr.rn, rrr.rm)),
502 else => unreachable,
503 }
504}
505
506fn mirNop(emit: *Emit) !void {
507 try emit.writeInstruction(Instruction.nop());
508}
509
510fn mirBlockDataTransfer(emit: *Emit, inst: Mir.Inst.Index) !void {
511 const tag = emit.mir.instructions.items(.tag)[inst];
512 const cond = emit.mir.instructions.items(.cond)[inst];
513 const register_list = emit.mir.instructions.items(.data)[inst].register_list;
514
515 switch (tag) {
516 .pop => try emit.writeInstruction(Instruction.ldm(cond, .sp, true, register_list)),
517 .push => try emit.writeInstruction(Instruction.stmdb(cond, .sp, true, register_list)),
518 else => unreachable,
519 }
520}
521
522fn mirSupervisorCall(emit: *Emit, inst: Mir.Inst.Index) !void {
523 const tag = emit.mir.instructions.items(.tag)[inst];
524 const cond = emit.mir.instructions.items(.cond)[inst];
525 const imm24 = emit.mir.instructions.items(.data)[inst].imm24;
526
527 switch (tag) {
528 .svc => try emit.writeInstruction(Instruction.svc(cond, imm24)),
529 else => unreachable,
530 }
531}
src/arch/arm/Mir.zig created+220
...@@ -0,0 +1,220 @@
1//! Machine Intermediate Representation.
2//! This data is produced by ARM Codegen or ARM 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 cond: bits.Condition,
24 /// The meaning of this depends on `tag`.
25 data: Data,
26
27 pub const Tag = enum(u16) {
28 /// Add
29 add,
30 /// Bitwise AND
31 @"and",
32 /// Arithmetic Shift Right
33 asr,
34 /// Branch
35 b,
36 /// Breakpoint
37 bkpt,
38 /// Branch with Link and Exchange
39 blx,
40 /// Branch and Exchange
41 bx,
42 /// Compare
43 cmp,
44 /// Pseudo-instruction: End of prologue
45 dbg_prologue_end,
46 /// Pseudo-instruction: Beginning of epilogue
47 dbg_epilogue_begin,
48 /// Pseudo-instruction: Update debug line
49 dbg_line,
50 /// Bitwise Exclusive OR
51 eor,
52 /// Load Register
53 ldr,
54 /// Load Register Byte
55 ldrb,
56 /// Load Register Halfword
57 ldrh,
58 /// Logical Shift Left
59 lsl,
60 /// Logical Shift Right
61 lsr,
62 /// Move
63 mov,
64 /// Move
65 movw,
66 /// Move Top
67 movt,
68 /// Multiply
69 mul,
70 /// Bitwise NOT
71 mvn,
72 /// No Operation
73 nop,
74 /// Bitwise OR
75 orr,
76 /// Pop multiple registers from Stack
77 pop,
78 /// Push multiple registers to Stack
79 push,
80 /// Reverse Subtract
81 rsb,
82 /// Store Register
83 str,
84 /// Store Register Byte
85 strb,
86 /// Store Register Halfword
87 strh,
88 /// Subtract
89 sub,
90 /// Supervisor Call
91 svc,
92 };
93
94 /// The position of an MIR instruction within the `Mir` instructions array.
95 pub const Index = u32;
96
97 /// All instructions have a 8-byte payload, which is contained within
98 /// this union. `Tag` determines which union field is active, as well as
99 /// how to interpret the data within.
100 // TODO flatten down Data (remove use of tagged unions) to make it
101 // 8 bytes only
102 pub const Data = union {
103 /// No additional data
104 ///
105 /// Used by e.g. nop
106 nop: void,
107 /// Another instruction
108 ///
109 /// Used by e.g. b
110 inst: Index,
111 /// A 16-bit immediate value.
112 ///
113 /// Used by e.g. bkpt
114 imm16: u16,
115 /// A 24-bit immediate value.
116 ///
117 /// Used by e.g. svc
118 imm24: u24,
119 /// Index into `extra`. Meaning of what can be found there is context-dependent.
120 ///
121 /// Used by e.g. load_memory
122 payload: u32,
123 /// A register
124 ///
125 /// Used by e.g. blx
126 reg: Register,
127 /// A register and a 16-bit unsigned immediate
128 ///
129 /// Used by e.g. movw
130 r_imm16: struct {
131 rd: Register,
132 imm16: u16,
133 },
134 /// Two registers and a shift amount
135 ///
136 /// Used by e.g. lsl
137 rr_shift: struct {
138 rd: Register,
139 rm: Register,
140 shift_amount: bits.Instruction.ShiftAmount,
141 },
142 /// Two registers and an operand
143 ///
144 /// Used by e.g. sub
145 rr_op: struct {
146 rd: Register,
147 rn: Register,
148 op: bits.Instruction.Operand,
149 },
150 /// Two registers and an offset
151 ///
152 /// Used by e.g. ldr
153 rr_offset: struct {
154 rt: Register,
155 rn: Register,
156 offset: bits.Instruction.OffsetArgs,
157 },
158 /// Two registers and an extra load/store offset
159 ///
160 /// Used by e.g. ldrh
161 rr_extra_offset: struct {
162 rt: Register,
163 rn: Register,
164 offset: bits.Instruction.ExtraLoadStoreOffsetArgs,
165 },
166 /// Three registers
167 ///
168 /// Used by e.g. mul
169 rrr: struct {
170 rd: Register,
171 rn: Register,
172 rm: Register,
173 },
174 /// An unordered list of registers
175 ///
176 /// Used by e.g. push
177 register_list: bits.Instruction.RegisterList,
178 /// Debug info: line and column
179 ///
180 /// Used by e.g. dbg_line
181 dbg_line_column: struct {
182 line: u32,
183 column: u32,
184 },
185 };
186
187 // Make sure we don't accidentally make instructions bigger than expected.
188 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
189 // comptime {
190 // if (builtin.mode != .Debug) {
191 // assert(@sizeOf(Data) == 8);
192 // }
193 // }
194};
195
196pub fn deinit(mir: *Mir, gpa: *std.mem.Allocator) void {
197 mir.instructions.deinit(gpa);
198 gpa.free(mir.extra);
199 mir.* = undefined;
200}
201
202/// Returns the requested data, as well as the new index which is at the start of the
203/// trailers for the object.
204pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
205 const fields = std.meta.fields(T);
206 var i: usize = index;
207 var result: T = undefined;
208 inline for (fields) |field| {
209 @field(result, field.name) = switch (field.field_type) {
210 u32 => mir.extra[i],
211 i32 => @bitCast(i32, mir.extra[i]),
212 else => @compileError("bad field type"),
213 };
214 i += 1;
215 }
216 return .{
217 .data = result,
218 .end = i,
219 };
220}