authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-04-02 18:45:31+07:00
committergravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-04-14 22:18:06+07:00
log1972a2b08063841bdd6dd411b4fb0c1b16225067
treef8bcbedea34c067f2dcac4cf5577e99685fe5984
parentcec48f2cf1009653ac1097328b75beaf0bf198d2

stage2: sparcv9: Add placeholders to generate a minimal program


3 files changed, 708 insertions(+), 136 deletions(-)

src/arch/sparcv9/CodeGen.zig+625-115
......@@ -2,11 +2,14 @@
22//! This lowers AIR into MIR.
33const std = @import("std");
44const assert = std.debug.assert;
5const log = std.log.scoped(.codegen);
6const math = std.math;
57const mem = std.mem;
68const Allocator = mem.Allocator;
79const builtin = @import("builtin");
810const link = @import("../../link.zig");
911const Module = @import("../../Module.zig");
12const TypedValue = @import("../../TypedValue.zig");
1013const ErrorMsg = Module.ErrorMsg;
1114const Air = @import("../../Air.zig");
1215const Mir = @import("Mir.zig");
......@@ -33,6 +36,11 @@ const InnerError = error{
3336 OutOfRegisters,
3437};
3538
39const RegisterView = enum(u1) {
40 caller,
41 callee,
42};
43
3644gpa: Allocator,
3745air: Air,
3846liveness: Liveness,
......@@ -165,7 +173,7 @@ const StackAllocation = struct {
165173};
166174
167175const BlockData = struct {
168 relocs: std.ArrayListUnmanaged(Reloc),
176 relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
169177 /// The first break instruction encounters `null` here and chooses a
170178 /// machine code value for the block result, populating this field.
171179 /// Following break instructions encounter that value and use it for
......@@ -173,18 +181,6 @@ const BlockData = struct {
173181 mcv: MCValue,
174182};
175183
176const Reloc = union(enum) {
177 /// The value is an offset into the `Function` `code` from the beginning.
178 /// To perform the reloc, write 32-bit signed little-endian integer
179 /// which is a relative jump, based on the address following the reloc.
180 rel32: usize,
181 /// A branch in the ARM instruction set
182 arm_branch: struct {
183 pos: usize,
184 cond: @import("../arm/bits.zig").Condition,
185 },
186};
187
188184const CallMCValues = struct {
189185 args: []MCValue,
190186 return_value: MCValue,
......@@ -245,7 +241,7 @@ pub fn generate(
245241 defer function.blocks.deinit(bin_file.allocator);
246242 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
247243
248 var call_info = function.resolveCallingConventionValues(fn_type, false) catch |err| switch (err) {
244 var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) {
249245 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
250246 error.OutOfRegisters => return FnResult{
251247 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
......@@ -298,92 +294,6 @@ pub fn generate(
298294 }
299295}
300296
301/// Caller must call `CallMCValues.deinit`.
302fn resolveCallingConventionValues(self: *Self, fn_ty: Type, is_caller: bool) !CallMCValues {
303 const cc = fn_ty.fnCallingConvention();
304 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
305 defer self.gpa.free(param_types);
306 fn_ty.fnParamTypes(param_types);
307 var result: CallMCValues = .{
308 .args = try self.gpa.alloc(MCValue, param_types.len),
309 // These undefined values must be populated before returning from this function.
310 .return_value = undefined,
311 .stack_byte_count = undefined,
312 .stack_align = undefined,
313 };
314 errdefer self.gpa.free(result.args);
315
316 const ret_ty = fn_ty.fnReturnType();
317
318 switch (cc) {
319 .Naked => {
320 assert(result.args.len == 0);
321 result.return_value = .{ .unreach = {} };
322 result.stack_byte_count = 0;
323 result.stack_align = 1;
324 return result;
325 },
326 .Unspecified, .C => {
327 // SPARC Compliance Definition 2.4.1, Chapter 3
328 // Low-Level System Information (64-bit psABI) - Function Calling Sequence
329
330 var next_register: usize = 0;
331 var next_stack_offset: u32 = 0;
332
333 // The caller puts the argument in %o0-%o5, which becomes %i0-%i5 inside the callee.
334 const argument_registers = if (is_caller) abi.c_abi_int_param_regs_caller_view else abi.c_abi_int_param_regs_callee_view;
335
336 for (param_types) |ty, i| {
337 const param_size = @intCast(u32, ty.abiSize(self.target.*));
338 if (param_size <= 8) {
339 if (next_register < argument_registers.len) {
340 result.args[i] = .{ .register = argument_registers[next_register] };
341 next_register += 1;
342 } else {
343 result.args[i] = .{ .stack_offset = next_stack_offset };
344 next_register += next_stack_offset;
345 }
346 } else if (param_size <= 16) {
347 if (next_register < argument_registers.len - 1) {
348 return self.fail("TODO MCValues with 2 registers", .{});
349 } else if (next_register < argument_registers.len) {
350 return self.fail("TODO MCValues split register + stack", .{});
351 } else {
352 result.args[i] = .{ .stack_offset = next_stack_offset };
353 next_register += next_stack_offset;
354 }
355 } else {
356 result.args[i] = .{ .stack_offset = next_stack_offset };
357 next_register += next_stack_offset;
358 }
359 }
360
361 result.stack_byte_count = next_stack_offset;
362 result.stack_align = 16;
363 },
364 else => return self.fail("TODO implement function parameters for {} on sparcv9", .{cc}),
365 }
366
367 if (ret_ty.zigTypeTag() == .NoReturn) {
368 result.return_value = .{ .unreach = {} };
369 } else if (!ret_ty.hasRuntimeBits()) {
370 result.return_value = .{ .none = {} };
371 } else switch (cc) {
372 .Naked => unreachable,
373 .Unspecified, .C => {
374 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
375 // The callee puts the return values in %i0-%i3, which becomes %o0-%o3 inside the caller.
376 if (ret_ty_size <= 8) {
377 result.return_value = if (is_caller) .{ .register = abi.c_abi_int_return_regs_caller_view[0] } else .{ .register = abi.c_abi_int_return_regs_callee_view[0] };
378 } else {
379 return self.fail("TODO support more return values for sparcv9", .{});
380 }
381 },
382 else => return self.fail("TODO implement function return values for {} on sparcv9", .{cc}),
383 }
384 return result;
385}
386
387297fn gen(self: *Self) !void {
388298 const cc = self.fn_type.fnCallingConvention();
389299 if (cc != .Naked) {
......@@ -519,7 +429,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
519429 .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"),
520430 .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"),
521431
522 .div_float, .div_trunc, .div_floor, .div_exact => @panic("TODO try self.airDiv(inst)"),
432 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
523433
524434 .cmp_lt => @panic("TODO try self.airCmp(inst, .lt)"),
525435 .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"),
......@@ -537,18 +447,18 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
537447 .shr, .shr_exact => @panic("TODO try self.airShr(inst)"),
538448
539449 .alloc => @panic("TODO try self.airAlloc(inst)"),
540 .ret_ptr => @panic("TODO try self.airRetPtr(inst)"),
541 .arg => @panic("TODO try self.airArg(inst)"),
542 .assembly => @panic("TODO try self.airAsm(inst)"),
450 .ret_ptr => try self.airRetPtr(inst),
451 .arg => try self.airArg(inst),
452 .assembly => try self.airAsm(inst),
543453 .bitcast => @panic("TODO try self.airBitCast(inst)"),
544 .block => @panic("TODO try self.airBlock(inst)"),
454 .block => try self.airBlock(inst),
545455 .br => @panic("TODO try self.airBr(inst)"),
546456 .breakpoint => @panic("TODO try self.airBreakpoint()"),
547457 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),
548458 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),
549459 .fence => @panic("TODO try self.airFence()"),
550460 .cond_br => @panic("TODO try self.airCondBr(inst)"),
551 .dbg_stmt => @panic("TODO try self.airDbgStmt(inst)"),
461 .dbg_stmt => try self.airDbgStmt(inst),
552462 .fptrunc => @panic("TODO try self.airFptrunc(inst)"),
553463 .fpext => @panic("TODO try self.airFpext(inst)"),
554464 .intcast => @panic("TODO try self.airIntCast(inst)"),
......@@ -567,8 +477,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
567477 .not => @panic("TODO try self.airNot(inst)"),
568478 .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"),
569479 .ret => @panic("TODO try self.airRet(inst)"),
570 .ret_load => @panic("TODO try self.airRetLoad(inst)"),
571 .store => @panic("TODO try self.airStore(inst)"),
480 .ret_load => try self.airRetLoad(inst),
481 .store => try self.airStore(inst),
572482 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),
573483 .struct_field_val=> @panic("TODO try self.airStructFieldVal(inst)"),
574484 .array_to_slice => @panic("TODO try self.airArrayToSlice(inst)"),
......@@ -600,20 +510,20 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
600510
601511 .dbg_var_ptr,
602512 .dbg_var_val,
603 => @panic("TODO try self.airDbgVar(inst)"),
513 => try self.airDbgVar(inst),
604514
605515 .dbg_inline_begin,
606516 .dbg_inline_end,
607 => @panic("TODO try self.airDbgInline(inst)"),
517 => try self.airDbgInline(inst),
608518
609519 .dbg_block_begin,
610520 .dbg_block_end,
611 => @panic("TODO try self.airDbgBlock(inst)"),
521 => try self.airDbgBlock(inst),
612522
613 .call => @panic("TODO try self.airCall(inst, .auto)"),
523 .call => try self.airCall(inst, .auto),
614524 .call_always_tail => @panic("TODO try self.airCall(inst, .always_tail)"),
615525 .call_never_tail => @panic("TODO try self.airCall(inst, .never_tail)"),
616 .call_never_inline => @panic("TODO try self.airCall(inst, .never_inline)"),
526 .call_never_inline => try self.airCall(inst, .never_inline),
617527
618528 .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"),
619529 .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"),
......@@ -627,7 +537,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
627537
628538 .field_parent_ptr => @panic("TODO try self.airFieldParentPtr(inst)"),
629539
630 .switch_br => @panic("TODO try self.airSwitch(inst)"),
540 .switch_br => try self.airSwitch(inst),
631541 .slice_ptr => @panic("TODO try self.airSlicePtr(inst)"),
632542 .slice_len => @panic("TODO try self.airSliceLen(inst)"),
633543
......@@ -642,7 +552,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
642552
643553 .constant => unreachable, // excluded from function bodies
644554 .const_ty => unreachable, // excluded from function bodies
645 .unreach => @panic("TODO self.finishAirBookkeeping()"),
555 .unreach => self.finishAirBookkeeping(),
646556
647557 .optional_payload => @panic("TODO try self.airOptionalPayload(inst)"),
648558 .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"),
......@@ -670,6 +580,212 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
670580 }
671581}
672582
583fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
584 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
585 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
586 const is_volatile = (extra.data.flags & 0x80000000) != 0;
587 const clobbers_len = @truncate(u31, extra.data.flags);
588 var extra_i: usize = extra.end;
589 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
590 extra_i += outputs.len;
591 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
592 extra_i += inputs.len;
593
594 const dead = !is_volatile and self.liveness.isUnused(inst);
595 _ = dead;
596 _ = clobbers_len;
597
598 return self.fail("TODO implement asm for {}", .{self.target.cpu.arch});
599}
600
601fn airArg(self: *Self, inst: Air.Inst.Index) !void {
602 const arg_index = self.arg_index;
603 self.arg_index += 1;
604
605 const ty = self.air.typeOfIndex(inst);
606 _ = ty;
607
608 const result = self.args[arg_index];
609 // TODO support stack-only arguments
610 // TODO Copy registers to the stack
611 const mcv = result;
612
613 _ = try self.addInst(.{
614 .tag = .dbg_arg,
615 .data = .{
616 .dbg_arg_info = .{
617 .air_inst = inst,
618 .arg_index = arg_index,
619 },
620 },
621 });
622
623 if (self.liveness.isUnused(inst))
624 return self.finishAirBookkeeping();
625
626 switch (mcv) {
627 .register => |reg| {
628 self.register_manager.getRegAssumeFree(reg, inst);
629 },
630 else => {},
631 }
632
633 return self.finishAir(inst, mcv, .{ .none, .none, .none });
634}
635
636fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
637 try self.blocks.putNoClobber(self.gpa, inst, .{
638 // A block is a setup to be able to jump to the end.
639 .relocs = .{},
640 // It also acts as a receptacle for break operands.
641 // Here we use `MCValue.none` to represent a null value so that the first
642 // break instruction will choose a MCValue for the block result and overwrite
643 // this field. Following break instructions will use that MCValue to put their
644 // block results.
645 .mcv = MCValue{ .none = {} },
646 });
647 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
648
649 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
650 const extra = self.air.extraData(Air.Block, ty_pl.payload);
651 const body = self.air.extra[extra.end..][0..extra.data.body_len];
652 try self.genBody(body);
653
654 // relocations for `bpcc` instructions
655 const relocs = &self.blocks.getPtr(inst).?.relocs;
656 if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) {
657 // If the last Mir instruction is the last relocation (which
658 // would just jump one instruction further), it can be safely
659 // removed
660 self.mir_instructions.orderedRemove(relocs.pop());
661 }
662 for (relocs.items) |reloc| {
663 try self.performReloc(reloc);
664 }
665
666 const result = self.blocks.getPtr(inst).?.mcv;
667 return self.finishAir(inst, result, .{ .none, .none, .none });
668}
669
670fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void {
671 if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch});
672
673 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
674 const callee = pl_op.operand;
675 const extra = self.air.extraData(Air.Call, pl_op.payload);
676 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end .. extra.end + extra.data.args_len]);
677 const ty = self.air.typeOf(callee);
678 const fn_ty = switch (ty.zigTypeTag()) {
679 .Fn => ty,
680 .Pointer => ty.childType(),
681 else => unreachable,
682 };
683
684 var info = try self.resolveCallingConventionValues(fn_ty, .caller);
685 defer info.deinit(self);
686 for (info.args) |mc_arg, arg_i| {
687 const arg = args[arg_i];
688 const arg_ty = self.air.typeOf(arg);
689 const arg_mcv = try self.resolveInst(arg);
690
691 switch (mc_arg) {
692 .none => continue,
693 .undef => unreachable,
694 .immediate => unreachable,
695 .unreach => unreachable,
696 .dead => unreachable,
697 .memory => unreachable,
698 .compare_flags_signed => unreachable,
699 .compare_flags_unsigned => unreachable,
700 .got_load => unreachable,
701 .direct_load => unreachable,
702 .register => |reg| {
703 try self.register_manager.getReg(reg, null);
704 try self.genSetReg(arg_ty, reg, arg_mcv);
705 },
706 .stack_offset => {
707 return self.fail("TODO implement calling with parameters in memory", .{});
708 },
709 .ptr_stack_offset => {
710 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
711 },
712 }
713 }
714
715 return self.fail("TODO implement call for {}", .{self.target.cpu.arch});
716}
717
718fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
719 // TODO emit debug info lexical block
720 return self.finishAir(inst, .dead, .{ .none, .none, .none });
721}
722
723fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
724 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
725 const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
726 // TODO emit debug info for function change
727 _ = function;
728 return self.finishAir(inst, .dead, .{ .none, .none, .none });
729}
730
731fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
732 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
733
734 _ = try self.addInst(.{
735 .tag = .dbg_line,
736 .data = .{
737 .dbg_line_column = .{
738 .line = dbg_stmt.line,
739 .column = dbg_stmt.column,
740 },
741 },
742 });
743
744 return self.finishAirBookkeeping();
745}
746
747fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
748 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
749 const name = self.air.nullTerminatedString(pl_op.payload);
750 const operand = pl_op.operand;
751 // TODO emit debug info for this variable
752 _ = name;
753 return self.finishAir(inst, .dead, .{ operand, .none, .none });
754}
755
756fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
757 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
758 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
759 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
760}
761
762fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
763 _ = inst;
764 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
765 //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
766}
767
768fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
769 const stack_offset = try self.allocMemPtr(inst);
770 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
771}
772
773fn airStore(self: *Self, inst: Air.Inst.Index) !void {
774 _ = self;
775 _ = inst;
776
777 return self.fail("TODO implement store for {}", .{self.target.cpu.arch});
778}
779
780fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
781 _ = self;
782 _ = inst;
783
784 return self.fail("TODO implement switch for {}", .{self.target.cpu.arch});
785}
786
787// Common helper functions
788
673789fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
674790 const gpa = self.gpa;
675791
......@@ -680,6 +796,42 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
680796 return result_index;
681797}
682798
799fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
800 if (abi_align > self.stack_align)
801 self.stack_align = abi_align;
802 // TODO find a free slot instead of always appending
803 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
804 self.next_stack_offset = offset + abi_size;
805 if (self.next_stack_offset > self.max_end_stack)
806 self.max_end_stack = self.next_stack_offset;
807 try self.stack.putNoClobber(self.gpa, offset, .{
808 .inst = inst,
809 .size = abi_size,
810 });
811 return offset;
812}
813
814/// Use a pointer instruction as the basis for allocating stack memory.
815fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
816 const elem_ty = self.air.typeOfIndex(inst).elemType();
817
818 if (!elem_ty.hasRuntimeBits()) {
819 // As this stack item will never be dereferenced at runtime,
820 // return the stack offset 0. Stack offset 0 will be where all
821 // zero-sized stack allocations live as non-zero-sized
822 // allocations will always have an offset > 0.
823 return @as(u32, 0);
824 }
825
826 const target = self.target.*;
827 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
828 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
829 };
830 // TODO swap this for inst.ty.ptrAlign
831 const abi_align = elem_ty.abiAlignment(self.target.*);
832 return self.allocMem(inst, abi_size, abi_align);
833}
834
683835fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
684836 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
685837 try table.ensureUnusedCapacity(self.gpa, additional_count);
......@@ -691,3 +843,361 @@ fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
691843 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
692844 return error.CodegenFail;
693845}
846
847/// Called when there are no operands, and the instruction is always unreferenced.
848fn finishAirBookkeeping(self: *Self) void {
849 if (std.debug.runtime_safety) {
850 self.air_bookkeeping += 1;
851 }
852}
853
854fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
855 var tomb_bits = self.liveness.getTombBits(inst);
856 for (operands) |op| {
857 const dies = @truncate(u1, tomb_bits) != 0;
858 tomb_bits >>= 1;
859 if (!dies) continue;
860 const op_int = @enumToInt(op);
861 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
862 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
863 self.processDeath(op_index);
864 }
865 const is_used = @truncate(u1, tomb_bits) == 0;
866 if (is_used) {
867 log.debug("%{d} => {}", .{ inst, result });
868 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
869 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
870
871 switch (result) {
872 .register => |reg| {
873 // In some cases (such as bitcast), an operand
874 // may be the same MCValue as the result. If
875 // that operand died and was a register, it
876 // was freed by processDeath. We have to
877 // "re-allocate" the register.
878 if (self.register_manager.isRegFree(reg)) {
879 self.register_manager.getRegAssumeFree(reg, inst);
880 }
881 },
882 else => {},
883 }
884 }
885 self.finishAirBookkeeping();
886}
887
888fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
889 if (typed_value.val.isUndef())
890 return MCValue{ .undef = {} };
891
892 if (typed_value.val.castTag(.decl_ref)) |payload| {
893 return self.lowerDeclRef(typed_value, payload.data);
894 }
895 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
896 return self.lowerDeclRef(typed_value, payload.data.decl);
897 }
898 const target = self.target.*;
899
900 switch (typed_value.ty.zigTypeTag()) {
901 .Pointer => switch (typed_value.ty.ptrSize()) {
902 .Slice => {
903 return self.lowerUnnamedConst(typed_value);
904 },
905 else => {
906 switch (typed_value.val.tag()) {
907 .int_u64 => {
908 return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
909 },
910 .slice => {
911 return self.lowerUnnamedConst(typed_value);
912 },
913 else => {
914 return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()});
915 },
916 }
917 },
918 },
919 .Int => {
920 const info = typed_value.ty.intInfo(self.target.*);
921 if (info.bits <= 64) {
922 const unsigned = switch (info.signedness) {
923 .signed => blk: {
924 const signed = typed_value.val.toSignedInt();
925 break :blk @bitCast(u64, signed);
926 },
927 .unsigned => typed_value.val.toUnsignedInt(target),
928 };
929
930 return MCValue{ .immediate = unsigned };
931 } else {
932 return self.lowerUnnamedConst(typed_value);
933 }
934 },
935 .Bool => {
936 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
937 },
938 .ComptimeInt => unreachable, // semantic analysis prevents this
939 .ComptimeFloat => unreachable, // semantic analysis prevents this
940 .Optional => {
941 if (typed_value.ty.isPtrLikeOptional()) {
942 if (typed_value.val.isNull())
943 return MCValue{ .immediate = 0 };
944
945 var buf: Type.Payload.ElemType = undefined;
946 return self.genTypedValue(.{
947 .ty = typed_value.ty.optionalChild(&buf),
948 .val = typed_value.val,
949 });
950 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
951 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
952 }
953 return self.fail("TODO non pointer optionals", .{});
954 },
955 .Enum => {
956 if (typed_value.val.castTag(.enum_field_index)) |field_index| {
957 switch (typed_value.ty.tag()) {
958 .enum_simple => {
959 return MCValue{ .immediate = field_index.data };
960 },
961 .enum_full, .enum_nonexhaustive => {
962 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
963 if (enum_full.values.count() != 0) {
964 const tag_val = enum_full.values.keys()[field_index.data];
965 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
966 } else {
967 return MCValue{ .immediate = field_index.data };
968 }
969 },
970 else => unreachable,
971 }
972 } else {
973 var int_tag_buffer: Type.Payload.Bits = undefined;
974 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
975 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
976 }
977 },
978 .ErrorSet => {
979 const err_name = typed_value.val.castTag(.@"error").?.data.name;
980 const module = self.bin_file.options.module.?;
981 const global_error_set = module.global_error_set;
982 const error_index = global_error_set.get(err_name).?;
983 return MCValue{ .immediate = error_index };
984 },
985 .ErrorUnion => {
986 const error_type = typed_value.ty.errorUnionSet();
987 const payload_type = typed_value.ty.errorUnionPayload();
988
989 if (typed_value.val.castTag(.eu_payload)) |pl| {
990 if (!payload_type.hasRuntimeBits()) {
991 // We use the error type directly as the type.
992 return MCValue{ .immediate = 0 };
993 }
994
995 _ = pl;
996 return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()});
997 } else {
998 if (!payload_type.hasRuntimeBits()) {
999 // We use the error type directly as the type.
1000 return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });
1001 }
1002
1003 return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()});
1004 }
1005 },
1006 .Struct => {
1007 return self.lowerUnnamedConst(typed_value);
1008 },
1009 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}),
1010 }
1011}
1012
1013fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
1014 // Treat each stack item as a "layer" on top of the previous one.
1015 var i: usize = self.branch_stack.items.len;
1016 while (true) {
1017 i -= 1;
1018 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1019 assert(mcv != .dead);
1020 return mcv;
1021 }
1022 }
1023}
1024
1025fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
1026 const tag = self.mir_instructions.items(.tag)[inst];
1027 switch (tag) {
1028 .bpcc => self.mir_instructions.items(.data)[inst].branch_predict.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len),
1029 else => unreachable,
1030 }
1031}
1032
1033/// Asserts there is already capacity to insert into top branch inst_table.
1034fn processDeath(self: *Self, inst: Air.Inst.Index) void {
1035 const air_tags = self.air.instructions.items(.tag);
1036 if (air_tags[inst] == .constant) return; // Constants are immortal.
1037 // When editing this function, note that the logic must synchronize with `reuseOperand`.
1038 const prev_value = self.getResolvedInstValue(inst);
1039 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1040 branch.inst_table.putAssumeCapacity(inst, .dead);
1041 switch (prev_value) {
1042 .register => |reg| {
1043 self.register_manager.freeReg(reg);
1044 },
1045 else => {}, // TODO process stack allocation death
1046 }
1047}
1048
1049/// Caller must call `CallMCValues.deinit`.
1050fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView) !CallMCValues {
1051 const cc = fn_ty.fnCallingConvention();
1052 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
1053 defer self.gpa.free(param_types);
1054 fn_ty.fnParamTypes(param_types);
1055 var result: CallMCValues = .{
1056 .args = try self.gpa.alloc(MCValue, param_types.len),
1057 // These undefined values must be populated before returning from this function.
1058 .return_value = undefined,
1059 .stack_byte_count = undefined,
1060 .stack_align = undefined,
1061 };
1062 errdefer self.gpa.free(result.args);
1063
1064 const ret_ty = fn_ty.fnReturnType();
1065
1066 switch (cc) {
1067 .Naked => {
1068 assert(result.args.len == 0);
1069 result.return_value = .{ .unreach = {} };
1070 result.stack_byte_count = 0;
1071 result.stack_align = 1;
1072 return result;
1073 },
1074 .Unspecified, .C => {
1075 // SPARC Compliance Definition 2.4.1, Chapter 3
1076 // Low-Level System Information (64-bit psABI) - Function Calling Sequence
1077
1078 var next_register: usize = 0;
1079 var next_stack_offset: u32 = 0;
1080
1081 // The caller puts the argument in %o0-%o5, which becomes %i0-%i5 inside the callee.
1082 const argument_registers = switch (role) {
1083 .caller => abi.c_abi_int_param_regs_caller_view,
1084 .callee => abi.c_abi_int_param_regs_callee_view,
1085 };
1086
1087 for (param_types) |ty, i| {
1088 const param_size = @intCast(u32, ty.abiSize(self.target.*));
1089 if (param_size <= 8) {
1090 if (next_register < argument_registers.len) {
1091 result.args[i] = .{ .register = argument_registers[next_register] };
1092 next_register += 1;
1093 } else {
1094 result.args[i] = .{ .stack_offset = next_stack_offset };
1095 next_register += next_stack_offset;
1096 }
1097 } else if (param_size <= 16) {
1098 if (next_register < argument_registers.len - 1) {
1099 return self.fail("TODO MCValues with 2 registers", .{});
1100 } else if (next_register < argument_registers.len) {
1101 return self.fail("TODO MCValues split register + stack", .{});
1102 } else {
1103 result.args[i] = .{ .stack_offset = next_stack_offset };
1104 next_register += next_stack_offset;
1105 }
1106 } else {
1107 result.args[i] = .{ .stack_offset = next_stack_offset };
1108 next_register += next_stack_offset;
1109 }
1110 }
1111
1112 result.stack_byte_count = next_stack_offset;
1113 result.stack_align = 16;
1114
1115 if (ret_ty.zigTypeTag() == .NoReturn) {
1116 result.return_value = .{ .unreach = {} };
1117 } else if (!ret_ty.hasRuntimeBits()) {
1118 result.return_value = .{ .none = {} };
1119 } else {
1120 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
1121 // The callee puts the return values in %i0-%i3, which becomes %o0-%o3 inside the caller.
1122 if (ret_ty_size <= 8) {
1123 result.return_value = switch (role) {
1124 .caller => .{ .register = abi.c_abi_int_return_regs_caller_view[0] },
1125 .callee => .{ .register = abi.c_abi_int_return_regs_callee_view[0] },
1126 };
1127 } else {
1128 return self.fail("TODO support more return values for sparcv9", .{});
1129 }
1130 }
1131 },
1132 else => return self.fail("TODO implement function parameters for {} on sparcv9", .{cc}),
1133 }
1134
1135 return result;
1136}
1137
1138fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
1139 // First section of indexes correspond to a set number of constant values.
1140 const ref_int = @enumToInt(inst);
1141 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
1142 const tv = Air.Inst.Ref.typed_value_map[ref_int];
1143 if (!tv.ty.hasRuntimeBits()) {
1144 return MCValue{ .none = {} };
1145 }
1146 return self.genTypedValue(tv);
1147 }
1148
1149 // If the type has no codegen bits, no need to store it.
1150 const inst_ty = self.air.typeOf(inst);
1151 if (!inst_ty.hasRuntimeBits())
1152 return MCValue{ .none = {} };
1153
1154 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
1155 switch (self.air.instructions.items(.tag)[inst_index]) {
1156 .constant => {
1157 // Constants have static lifetimes, so they are always memoized in the outer most table.
1158 const branch = &self.branch_stack.items[0];
1159 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
1160 if (!gop.found_existing) {
1161 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
1162 gop.value_ptr.* = try self.genTypedValue(.{
1163 .ty = inst_ty,
1164 .val = self.air.values[ty_pl.payload],
1165 });
1166 }
1167 return gop.value_ptr.*;
1168 },
1169 .const_ty => unreachable,
1170 else => return self.getResolvedInstValue(inst_index),
1171 }
1172}
1173
1174fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1175 if (!self.liveness.operandDies(inst, op_index))
1176 return false;
1177
1178 switch (mcv) {
1179 .register => |reg| {
1180 // If it's in the registers table, need to associate the register with the
1181 // new instruction.
1182 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
1183 if (!self.register_manager.isRegFree(reg)) {
1184 self.register_manager.registers[index] = inst;
1185 }
1186 }
1187 log.debug("%{d} => {} (reused)", .{ inst, reg });
1188 },
1189 .stack_offset => |off| {
1190 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
1191 },
1192 else => return false,
1193 }
1194
1195 // Prevent the operand deaths processing code from deallocating it.
1196 self.liveness.clearOperandDeath(inst, op_index);
1197
1198 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
1199 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1200 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
1201
1202 return true;
1203}
src/arch/sparcv9/Emit.zig+22-4
......@@ -42,16 +42,23 @@ pub fn emitMir(
4242 for (mir_tags) |tag, index| {
4343 const inst = @intCast(u32, index);
4444 switch (tag) {
45 .dbg_arg => try emit.mirDbgArg(inst),
4546 .dbg_line => try emit.mirDbgLine(inst),
4647 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
4748 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
4849
49 .nop => @panic("TODO implement nop"),
50 .bpcc => @panic("TODO implement sparcv9 bpcc"),
5051
51 .save => @panic("TODO implement save"),
52 .restore => @panic("TODO implement restore"),
52 .call => @panic("TODO implement sparcv9 call"),
5353
54 .@"return" => @panic("TODO implement return"),
54 .jmpl => @panic("TODO implement sparcv9 jmpl"),
55
56 .nop => @panic("TODO implement sparcv9 nop"),
57
58 .@"return" => @panic("TODO implement sparcv9 return"),
59
60 .save => @panic("TODO implement sparcv9 save"),
61 .restore => @panic("TODO implement sparcv9 restore"),
5562 }
5663 }
5764}
......@@ -111,6 +118,17 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
111118 }
112119}
113120
121fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void {
122 const tag = emit.mir.instructions.items(.tag)[inst];
123 const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info;
124 _ = dbg_arg_info;
125
126 switch (tag) {
127 .dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index),
128 else => unreachable,
129 }
130}
131
114132fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
115133 const tag = emit.mir.instructions.items(.tag)[inst];
116134 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
src/arch/sparcv9/Mir.zig+61-17
......@@ -7,9 +7,14 @@
77//! so that, for example, the smaller encodings of jump instructions can be used.
88
99const std = @import("std");
10const builtin = @import("builtin");
11const assert = std.debug.assert;
1012
1113const Mir = @This();
1214const bits = @import("bits.zig");
15const Air = @import("../../Air.zig");
16
17const Instruction = bits.Instruction;
1318const Register = bits.Register;
1419
1520instructions: std.MultiArrayList(Inst).Slice,
......@@ -23,6 +28,8 @@ pub const Inst = struct {
2328 data: Data,
2429
2530 pub const Tag = enum(u16) {
31 /// Pseudo-instruction: Argument
32 dbg_arg,
2633 /// Pseudo-instruction: End of prologue
2734 dbg_prologue_end,
2835 /// Pseudo-instruction: Beginning of epilogue
......@@ -33,6 +40,18 @@ pub const Inst = struct {
3340 // All the real instructions are ordered by their section number
3441 // in The SPARC Architecture Manual, Version 9.
3542
43 /// A.7 Branch on Integer Condition Codes with Prediction (BPcc)
44 /// It uses the branch_predict field.
45 bpcc,
46
47 /// A.8 Call and Link
48 /// It uses the branch_link field.
49 call,
50
51 /// A.24 Jump and Link
52 /// It uses the branch_link field.
53 jmpl,
54
3655 /// A.40 No Operation
3756 /// It uses the nop field.
3857 nop,
......@@ -50,23 +69,33 @@ pub const Inst = struct {
5069 /// The position of an MIR instruction within the `Mir` instructions array.
5170 pub const Index = u32;
5271
53 /// All instructions have a 4-byte payload, which is contained within
72 /// All instructions have a 8-byte payload, which is contained within
5473 /// this union. `Tag` determines which union field is active, as well as
5574 /// how to interpret the data within.
5675 pub const Data = union {
57 /// No additional data
76 /// Debug info: argument
5877 ///
59 /// Used by e.g. flushw
60 nop: void,
78 /// Used by e.g. dbg_arg
79 dbg_arg_info: struct {
80 air_inst: Air.Inst.Index,
81 arg_index: usize,
82 },
6183
62 /// Three operand arithmetic.
84 /// Debug info: line and column
85 ///
86 /// Used by e.g. dbg_line
87 dbg_line_column: struct {
88 line: u32,
89 column: u32,
90 },
91
92 /// Two operand arithmetic.
6393 /// if is_imm true then it uses the imm field of rs2_or_imm,
6494 /// otherwise it uses rs2 field.
6595 ///
66 /// Used by e.g. add, sub
67 arithmetic_3op: struct {
96 /// Used by e.g. return
97 arithmetic_2op: struct {
6898 is_imm: bool,
69 rd: Register,
7099 rs1: Register,
71100 rs2_or_imm: union {
72101 rs2: Register,
......@@ -74,13 +103,14 @@ pub const Inst = struct {
74103 },
75104 },
76105
77 /// Two operand arithmetic.
106 /// Three operand arithmetic.
78107 /// if is_imm true then it uses the imm field of rs2_or_imm,
79108 /// otherwise it uses rs2 field.
80109 ///
81 /// Used by e.g. return
82 arithmetic_2op: struct {
110 /// Used by e.g. add, sub
111 arithmetic_3op: struct {
83112 is_imm: bool,
113 rd: Register,
84114 rs1: Register,
85115 rs2_or_imm: union {
86116 rs2: Register,
......@@ -88,13 +118,27 @@ pub const Inst = struct {
88118 },
89119 },
90120
91 /// Debug info: line and column
92 ///
93 /// Used by e.g. dbg_line
94 dbg_line_column: struct {
95 line: u32,
96 column: u32,
121 /// Branch and link (always unconditional).
122 /// Used by e.g. call
123 branch_link: struct {
124 inst: Index,
125 link: Register,
126 },
127
128 /// Branch with prediction.
129 /// Used by e.g. bpcc
130 branch_predict: struct {
131 annul: bool,
132 pt: bool,
133 ccr: Instruction.CCR,
134 cond: Instruction.Condition,
135 inst: Index,
97136 },
137
138 /// No additional data
139 ///
140 /// Used by e.g. flushw
141 nop: void,
98142 };
99143};
100144