authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-03-28 20:31:40+07:00
committergravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-04-14 22:18:05+07:00
loga5a89fde1354892c6714c41ea691922bfa10c442
treeb0978df6a580f8b7bc20500e2b6f7d53e12ea73f
parenta30688ef2a136c5a127c706880e8389b9b32e5be

stage2: sparcv9: Add skeleton codegen impl and necessary fields


3 files changed, 362 insertions(+), 11 deletions(-)

src/arch/sparcv9/CodeGen.zig+276-11
......@@ -2,24 +2,198 @@
22//! This lowers AIR into MIR.
33const std = @import("std");
44const assert = std.debug.assert;
5const mem = std.mem;
6const Allocator = mem.Allocator;
57const builtin = @import("builtin");
68const link = @import("../../link.zig");
79const Module = @import("../../Module.zig");
10const ErrorMsg = Module.ErrorMsg;
811const Air = @import("../../Air.zig");
912const Mir = @import("Mir.zig");
1013const Emit = @import("Emit.zig");
1114const Liveness = @import("../../Liveness.zig");
12const build_options = @import("build_options");
13
15const Type = @import("../../type.zig").Type;
1416const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
1517const FnResult = @import("../../codegen.zig").FnResult;
1618const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
1719
20const build_options = @import("build_options");
21
1822const bits = @import("bits.zig");
1923const abi = @import("abi.zig");
24const Register = bits.Register;
2025
2126const Self = @This();
2227
28const InnerError = error{
29 OutOfMemory,
30 CodegenFail,
31 OutOfRegisters,
32};
33
34gpa: Allocator,
35air: Air,
36liveness: Liveness,
37bin_file: *link.File,
38target: *const std.Target,
39mod_fn: *const Module.Fn,
40code: *std.ArrayList(u8),
41debug_output: DebugInfoOutput,
42err_msg: ?*ErrorMsg,
43args: []MCValue,
44ret_mcv: MCValue,
45fn_type: Type,
46arg_index: usize,
47src_loc: Module.SrcLoc,
48stack_align: u32,
49
50/// MIR Instructions
51mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
52/// MIR extra data
53mir_extra: std.ArrayListUnmanaged(u32) = .{},
54
55/// Byte offset within the source file of the ending curly.
56end_di_line: u32,
57end_di_column: u32,
58
59/// The value is an offset into the `Function` `code` from the beginning.
60/// To perform the reloc, write 32-bit signed little-endian integer
61/// which is a relative jump, based on the address following the reloc.
62exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
63
64/// Whenever there is a runtime branch, we push a Branch onto this stack,
65/// and pop it off when the runtime branch joins. This provides an "overlay"
66/// of the table of mappings from instructions to `MCValue` from within the branch.
67/// This way we can modify the `MCValue` for an instruction in different ways
68/// within different branches. Special consideration is needed when a branch
69/// joins with its parent, to make sure all instructions have the same MCValue
70/// across each runtime branch upon joining.
71branch_stack: *std.ArrayList(Branch),
72
73// Key is the block instruction
74blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
75
76/// Maps offset to what is stored there.
77stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
78
79/// Offset from the stack base, representing the end of the stack frame.
80max_end_stack: u32 = 0,
81/// Represents the current end stack offset. If there is no existing slot
82/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
83next_stack_offset: u32 = 0,
84
85/// Debug field, used to find bugs in the compiler.
86air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
87
88const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
89
90const MCValue = union(enum) {
91 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
92 /// TODO Look into deleting this tag and using `dead` instead, since every use
93 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
94 none,
95 /// Control flow will not allow this value to be observed.
96 unreach,
97 /// No more references to this value remain.
98 dead,
99 /// The value is undefined.
100 undef,
101 /// A pointer-sized integer that fits in a register.
102 /// If the type is a pointer, this is the pointer address in virtual address space.
103 immediate: u64,
104 /// The value is in a target-specific register.
105 register: Register,
106 /// The value is in memory at a hard-coded address.
107 /// If the type is a pointer, it means the pointer address is at this memory location.
108 memory: u64,
109 /// The value is one of the stack variables.
110 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
111 stack_offset: u32,
112 /// The value is a pointer to one of the stack variables (payload is stack offset).
113 ptr_stack_offset: u32,
114
115 fn isMemory(mcv: MCValue) bool {
116 return switch (mcv) {
117 .memory, .stack_offset => true,
118 else => false,
119 };
120 }
121
122 fn isImmediate(mcv: MCValue) bool {
123 return switch (mcv) {
124 .immediate => true,
125 else => false,
126 };
127 }
128
129 fn isMutable(mcv: MCValue) bool {
130 return switch (mcv) {
131 .none => unreachable,
132 .unreach => unreachable,
133 .dead => unreachable,
134
135 .immediate,
136 .memory,
137 .ptr_stack_offset,
138 .undef,
139 => false,
140
141 .register,
142 .stack_offset,
143 => true,
144 };
145 }
146};
147
148const Branch = struct {
149 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
150
151 fn deinit(self: *Branch, gpa: Allocator) void {
152 self.inst_table.deinit(gpa);
153 self.* = undefined;
154 }
155};
156
157const StackAllocation = struct {
158 inst: Air.Inst.Index,
159 /// TODO do we need size? should be determined by inst.ty.abiSize()
160 size: u32,
161};
162
163const BlockData = struct {
164 relocs: std.ArrayListUnmanaged(Reloc),
165 /// The first break instruction encounters `null` here and chooses a
166 /// machine code value for the block result, populating this field.
167 /// Following break instructions encounter that value and use it for
168 /// the location to store their block results.
169 mcv: MCValue,
170};
171
172const Reloc = union(enum) {
173 /// The value is an offset into the `Function` `code` from the beginning.
174 /// To perform the reloc, write 32-bit signed little-endian integer
175 /// which is a relative jump, based on the address following the reloc.
176 rel32: usize,
177 /// A branch in the ARM instruction set
178 arm_branch: struct {
179 pos: usize,
180 cond: @import("../arm/bits.zig").Condition,
181 },
182};
183
184const CallMCValues = struct {
185 args: []MCValue,
186 return_value: MCValue,
187 stack_byte_count: u32,
188 stack_align: u32,
189
190 fn deinit(self: *CallMCValues, func: *Self) void {
191 func.gpa.free(self.args);
192 self.* = undefined;
193 }
194};
195
196
23197pub fn generate(
24198 bin_file: *link.File,
25199 src_loc: Module.SrcLoc,
......@@ -29,19 +203,110 @@ pub fn generate(
29203 code: *std.ArrayList(u8),
30204 debug_output: DebugInfoOutput,
31205) GenerateSymbolError!FnResult {
32 _ = bin_file;
33 _ = src_loc;
34 _ = module_fn;
35 _ = air;
36 _ = liveness;
37 _ = code;
38 _ = debug_output;
39
40206 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
41207 @panic("Attempted to compile for architecture that was disabled by build configuration");
42208 }
43209
44210 assert(module_fn.owner_decl.has_tv);
211 const fn_type = module_fn.owner_decl.ty;
212
213 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
214 defer {
215 assert(branch_stack.items.len == 1);
216 branch_stack.items[0].deinit(bin_file.allocator);
217 branch_stack.deinit();
218 }
219 try branch_stack.append(.{});
220
221 var function = Self{
222 .gpa = bin_file.allocator,
223 .air = air,
224 .liveness = liveness,
225 .target = &bin_file.options.target,
226 .bin_file = bin_file,
227 .mod_fn = module_fn,
228 .code = code,
229 .debug_output = debug_output,
230 .err_msg = null,
231 .args = undefined, // populated after `resolveCallingConventionValues`
232 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
233 .fn_type = fn_type,
234 .arg_index = 0,
235 .branch_stack = &branch_stack,
236 .src_loc = src_loc,
237 .stack_align = undefined,
238 .end_di_line = module_fn.rbrace_line,
239 .end_di_column = module_fn.rbrace_column,
240 };
241 defer function.stack.deinit(bin_file.allocator);
242 defer function.blocks.deinit(bin_file.allocator);
243 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
244
245 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
246 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
247 error.OutOfRegisters => return FnResult{
248 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
249 },
250 else => |e| return e,
251 };
252 defer call_info.deinit(&function);
253
254 function.args = call_info.args;
255 function.ret_mcv = call_info.return_value;
256 function.stack_align = call_info.stack_align;
257 function.max_end_stack = call_info.stack_byte_count;
258
259 function.gen() catch |err| switch (err) {
260 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
261 error.OutOfRegisters => return FnResult{
262 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
263 },
264 else => |e| return e,
265 };
266
267 var mir = Mir{
268 .instructions = function.mir_instructions.toOwnedSlice(),
269 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
270 };
271 defer mir.deinit(bin_file.allocator);
272
273 var emit = Emit{
274 .mir = mir,
275 .bin_file = bin_file,
276 .debug_output = debug_output,
277 .target = &bin_file.options.target,
278 .src_loc = src_loc,
279 .code = code,
280 .prev_di_pc = 0,
281 .prev_di_line = module_fn.lbrace_line,
282 .prev_di_column = module_fn.lbrace_column,
283 };
284 defer emit.deinit();
285
286 emit.emitMir() catch |err| switch (err) {
287 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
288 else => |e| return e,
289 };
290
291 if (function.err_msg) |em| {
292 return FnResult{ .fail = em };
293 } else {
294 return FnResult{ .appended = {} };
295 }
296}
297
298/// Caller must call `CallMCValues.deinit`.
299fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
300 _ = self;
301 _ = fn_ty;
302
303 @panic("TODO implement resolveCallingConventionValues");
304}
305
306
307/// Caller must call `CallMCValues.deinit`.
308fn gen(self: *Self) !void {
309 _ = self;
45310
46 @panic("TODO implement SPARCv9 codegen");
311 @panic("TODO implement gen");
47312}
src/arch/sparcv9/Emit.zig+37
......@@ -1,6 +1,43 @@
11//! This file contains the functionality for lowering SPARCv9 MIR into
22//! machine code
33
4const std = @import("std");
5const link = @import("../../link.zig");
6const Module = @import("../../Module.zig");
7const ErrorMsg = Module.ErrorMsg;
8const Liveness = @import("../../Liveness.zig");
9const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
10
411const Emit = @This();
512const Mir = @import("Mir.zig");
613const bits = @import("bits.zig");
14
15mir: Mir,
16bin_file: *link.File,
17debug_output: DebugInfoOutput,
18target: *const std.Target,
19err_msg: ?*ErrorMsg = null,
20src_loc: Module.SrcLoc,
21code: *std.ArrayList(u8),
22
23prev_di_line: u32,
24prev_di_column: u32,
25/// Relative to the beginning of `code`.
26prev_di_pc: usize,
27
28const InnerError = error{
29 OutOfMemory,
30 EmitFail,
31};
32
33pub fn emitMir(
34 emit: *Emit,
35) InnerError!void {
36 _ = emit;
37
38 @panic("TODO implement emitMir");
39}
40
41pub fn deinit(emit: *Emit) void {
42 emit.* = undefined;
43}
src/arch/sparcv9/Mir.zig+49
......@@ -6,6 +6,55 @@
66//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
77//! so that, for example, the smaller encodings of jump instructions can be used.
88
9const std = @import("std");
10
911const Mir = @This();
1012const bits = @import("bits.zig");
1113const Register = bits.Register;
14
15instructions: std.MultiArrayList(Inst).Slice,
16
17/// The meaning of this data is determined by `Inst.Tag` value.
18extra: []const u32,
19
20pub const Inst = struct {
21 tag: Tag,
22 /// The meaning of this depends on `tag`.
23 data: Data,
24
25 pub const Tag = enum(u16) {
26 /// Pseudo-instruction: End of prologue
27 dbg_prologue_end,
28 /// Pseudo-instruction: Beginning of epilogue
29 dbg_epilogue_begin,
30 /// Pseudo-instruction: Update debug line
31 dbg_line,
32 };
33
34 /// The position of an MIR instruction within the `Mir` instructions array.
35 pub const Index = u32;
36
37 /// All instructions have a 4-byte payload, which is contained within
38 /// this union. `Tag` determines which union field is active, as well as
39 /// how to interpret the data within.
40 pub const Data = union {
41 /// No additional data
42 ///
43 /// Used by e.g. flushw
44 nop: void,
45 /// Debug info: line and column
46 ///
47 /// Used by e.g. dbg_line
48 dbg_line_column: struct {
49 line: u32,
50 column: u32,
51 },
52 };
53};
54
55pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
56 mir.instructions.deinit(gpa);
57 gpa.free(mir.extra);
58 mir.* = undefined;
59}
60