authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-04-14 21:38:35+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-04-14 21:38:35+02:00
log2635e4ca6eec21142ac40e6e16c1860f34dc15ee
tree83c5b20bebe0e8aa1fbfac0e6f339db27c8417f8
parent35171dd3dbb92a0a22733f40f2a1f81daf510409
parentc07213269fe14235d75d8d768984e329cdfcb4fe
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11434 from koachan/sparc64-codegen


14 files changed, 2451 insertions(+), 77 deletions(-)

lib/std/builtin.zig+5-1
...@@ -716,6 +716,9 @@ pub const CompilerBackend = enum(u64) {...@@ -716,6 +716,9 @@ pub const CompilerBackend = enum(u64) {
716 /// The reference implementation self-hosted compiler of Zig, using the716 /// The reference implementation self-hosted compiler of Zig, using the
717 /// riscv64 backend.717 /// riscv64 backend.
718 stage2_riscv64 = 9,718 stage2_riscv64 = 9,
719 /// The reference implementation self-hosted compiler of Zig, using the
720 /// sparcv9 backend.
721 stage2_sparcv9 = 10,
719722
720 _,723 _,
721};724};
...@@ -761,7 +764,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn...@@ -761,7 +764,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
761 builtin.zig_backend == .stage2_aarch64 or764 builtin.zig_backend == .stage2_aarch64 or
762 builtin.zig_backend == .stage2_x86_64 or765 builtin.zig_backend == .stage2_x86_64 or
763 builtin.zig_backend == .stage2_x86 or766 builtin.zig_backend == .stage2_x86 or
764 builtin.zig_backend == .stage2_riscv64)767 builtin.zig_backend == .stage2_riscv64 or
768 builtin.zig_backend == .stage2_sparcv9)
765 {769 {
766 while (true) {770 while (true) {
767 @breakpoint();771 @breakpoint();
lib/std/start.zig+9
...@@ -29,6 +29,7 @@ comptime {...@@ -29,6 +29,7 @@ comptime {
29 builtin.zig_backend == .stage2_aarch64 or29 builtin.zig_backend == .stage2_aarch64 or
30 builtin.zig_backend == .stage2_arm or30 builtin.zig_backend == .stage2_arm or
31 builtin.zig_backend == .stage2_riscv64 or31 builtin.zig_backend == .stage2_riscv64 or
32 builtin.zig_backend == .stage2_sparcv9 or
32 (builtin.zig_backend == .stage2_llvm and native_os != .linux) or33 (builtin.zig_backend == .stage2_llvm and native_os != .linux) or
33 (builtin.zig_backend == .stage2_llvm and native_arch != .x86_64))34 (builtin.zig_backend == .stage2_llvm and native_arch != .x86_64))
34 {35 {
...@@ -165,6 +166,14 @@ fn exit2(code: usize) noreturn {...@@ -165,6 +166,14 @@ fn exit2(code: usize) noreturn {
165 : "rcx", "r11", "memory"166 : "rcx", "r11", "memory"
166 );167 );
167 },168 },
169 .sparcv9 => {
170 asm volatile ("ta 0x6d"
171 :
172 : [number] "{g1}" (1),
173 [arg1] "{o0}" (code),
174 : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory"
175 );
176 },
168 else => @compileError("TODO"),177 else => @compileError("TODO"),
169 },178 },
170 // exits(0)179 // exits(0)
src/Compilation.zig+1
...@@ -4531,6 +4531,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca...@@ -4531,6 +4531,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
4531 .i386 => .stage2_x86,4531 .i386 => .stage2_x86,
4532 .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,4532 .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
4533 .riscv64 => .stage2_riscv64,4533 .riscv64 => .stage2_riscv64,
4534 .sparcv9 => .stage2_sparcv9,
4534 else => .other,4535 else => .other,
4535 };4536 };
4536 };4537 };
src/arch/sparcv9/CodeGen.zig+1648-10
...@@ -1,23 +1,235 @@...@@ -1,23 +1,235 @@
1//! SPARCv9 codegen.1//! SPARCv9 codegen.
2//! This lowers AIR into MIR.2//! This lowers AIR into MIR.
3const std = @import("std");3const std = @import("std");
4const assert = std.debug.assert;
5const log = std.log.scoped(.codegen);
6const math = std.math;
7const mem = std.mem;
8const Allocator = mem.Allocator;
4const builtin = @import("builtin");9const builtin = @import("builtin");
5const link = @import("../../link.zig");10const link = @import("../../link.zig");
6const Module = @import("../../Module.zig");11const Module = @import("../../Module.zig");
12const TypedValue = @import("../../TypedValue.zig");
13const ErrorMsg = Module.ErrorMsg;
7const Air = @import("../../Air.zig");14const Air = @import("../../Air.zig");
8const Mir = @import("Mir.zig");15const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");16const Emit = @import("Emit.zig");
10const Liveness = @import("../../Liveness.zig");17const Liveness = @import("../../Liveness.zig");
1118const Type = @import("../../type.zig").Type;
12const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;19const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
13const FnResult = @import("../../codegen.zig").FnResult;20const FnResult = @import("../../codegen.zig").FnResult;
14const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;21const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
22const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
23const RegisterManager = RegisterManagerFn(Self, Register, &abi.allocatable_regs);
24
25const build_options = @import("build_options");
1526
16const bits = @import("bits.zig");27const bits = @import("bits.zig");
17const abi = @import("abi.zig");28const abi = @import("abi.zig");
29const Register = bits.Register;
1830
19const Self = @This();31const Self = @This();
2032
33const InnerError = error{
34 OutOfMemory,
35 CodegenFail,
36 OutOfRegisters,
37};
38
39const RegisterView = enum(u1) {
40 caller,
41 callee,
42};
43
44gpa: Allocator,
45air: Air,
46liveness: Liveness,
47bin_file: *link.File,
48target: *const std.Target,
49mod_fn: *const Module.Fn,
50code: *std.ArrayList(u8),
51debug_output: DebugInfoOutput,
52err_msg: ?*ErrorMsg,
53args: []MCValue,
54ret_mcv: MCValue,
55fn_type: Type,
56arg_index: usize,
57src_loc: Module.SrcLoc,
58stack_align: u32,
59
60/// MIR Instructions
61mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
62/// MIR extra data
63mir_extra: std.ArrayListUnmanaged(u32) = .{},
64
65/// Byte offset within the source file of the ending curly.
66end_di_line: u32,
67end_di_column: u32,
68
69/// The value is an offset into the `Function` `code` from the beginning.
70/// To perform the reloc, write 32-bit signed little-endian integer
71/// which is a relative jump, based on the address following the reloc.
72exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
73
74/// Whenever there is a runtime branch, we push a Branch onto this stack,
75/// and pop it off when the runtime branch joins. This provides an "overlay"
76/// of the table of mappings from instructions to `MCValue` from within the branch.
77/// This way we can modify the `MCValue` for an instruction in different ways
78/// within different branches. Special consideration is needed when a branch
79/// joins with its parent, to make sure all instructions have the same MCValue
80/// across each runtime branch upon joining.
81branch_stack: *std.ArrayList(Branch),
82
83// Key is the block instruction
84blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
85
86register_manager: RegisterManager = .{},
87
88/// Maps offset to what is stored there.
89stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
90
91/// Offset from the stack base, representing the end of the stack frame.
92max_end_stack: u32 = 0,
93/// Represents the current end stack offset. If there is no existing slot
94/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
95next_stack_offset: u32 = 0,
96
97/// Debug field, used to find bugs in the compiler.
98air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
99
100const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
101
102const MCValue = union(enum) {
103 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
104 /// TODO Look into deleting this tag and using `dead` instead, since every use
105 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
106 none,
107 /// Control flow will not allow this value to be observed.
108 unreach,
109 /// No more references to this value remain.
110 dead,
111 /// The value is undefined.
112 undef,
113 /// A pointer-sized integer that fits in a register.
114 /// If the type is a pointer, this is the pointer address in virtual address space.
115 immediate: u64,
116 /// The value is in a target-specific register.
117 register: Register,
118 /// The value is in memory at a hard-coded address.
119 /// If the type is a pointer, it means the pointer address is at this memory location.
120 memory: u64,
121 /// The value is one of the stack variables.
122 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
123 stack_offset: u32,
124 /// The value is a pointer to one of the stack variables (payload is stack offset).
125 ptr_stack_offset: u32,
126
127 fn isMemory(mcv: MCValue) bool {
128 return switch (mcv) {
129 .memory, .stack_offset => true,
130 else => false,
131 };
132 }
133
134 fn isImmediate(mcv: MCValue) bool {
135 return switch (mcv) {
136 .immediate => true,
137 else => false,
138 };
139 }
140
141 fn isMutable(mcv: MCValue) bool {
142 return switch (mcv) {
143 .none => unreachable,
144 .unreach => unreachable,
145 .dead => unreachable,
146
147 .immediate,
148 .memory,
149 .ptr_stack_offset,
150 .undef,
151 => false,
152
153 .register,
154 .stack_offset,
155 => true,
156 };
157 }
158};
159
160const Branch = struct {
161 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
162
163 fn deinit(self: *Branch, gpa: Allocator) void {
164 self.inst_table.deinit(gpa);
165 self.* = undefined;
166 }
167};
168
169const StackAllocation = struct {
170 inst: Air.Inst.Index,
171 /// TODO do we need size? should be determined by inst.ty.abiSize()
172 size: u32,
173};
174
175const BlockData = struct {
176 relocs: std.ArrayListUnmanaged(Mir.Inst.Index),
177 /// The first break instruction encounters `null` here and chooses a
178 /// machine code value for the block result, populating this field.
179 /// Following break instructions encounter that value and use it for
180 /// the location to store their block results.
181 mcv: MCValue,
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
196const BigTomb = struct {
197 function: *Self,
198 inst: Air.Inst.Index,
199 tomb_bits: Liveness.Bpi,
200 big_tomb_bits: u32,
201 bit_index: usize,
202
203 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
204 const this_bit_index = bt.bit_index;
205 bt.bit_index += 1;
206
207 const op_int = @enumToInt(op_ref);
208 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
209 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
210
211 if (this_bit_index < Liveness.bpi - 1) {
212 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
213 if (!dies) return;
214 } else {
215 const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
216 const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
217 if (!dies) return;
218 }
219 bt.function.processDeath(op_index);
220 }
221
222 fn finishAir(bt: *BigTomb, result: MCValue) void {
223 const is_used = !bt.function.liveness.isUnused(bt.inst);
224 if (is_used) {
225 log.debug("%{d} => {}", .{ bt.inst, result });
226 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
227 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
228 }
229 bt.function.finishAirBookkeeping();
230 }
231};
232
21pub fn generate(233pub fn generate(
22 bin_file: *link.File,234 bin_file: *link.File,
23 src_loc: Module.SrcLoc,235 src_loc: Module.SrcLoc,
...@@ -27,13 +239,1439 @@ pub fn generate(...@@ -27,13 +239,1439 @@ pub fn generate(
27 code: *std.ArrayList(u8),239 code: *std.ArrayList(u8),
28 debug_output: DebugInfoOutput,240 debug_output: DebugInfoOutput,
29) GenerateSymbolError!FnResult {241) GenerateSymbolError!FnResult {
30 _ = bin_file;242 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
31 _ = src_loc;243 @panic("Attempted to compile for architecture that was disabled by build configuration");
32 _ = module_fn;244 }
33 _ = air;245
34 _ = liveness;246 assert(module_fn.owner_decl.has_tv);
35 _ = code;247 const fn_type = module_fn.owner_decl.ty;
36 _ = debug_output;248
37249 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
38 @panic("TODO implement SPARCv9 codegen");250 defer {
251 assert(branch_stack.items.len == 1);
252 branch_stack.items[0].deinit(bin_file.allocator);
253 branch_stack.deinit();
254 }
255 try branch_stack.append(.{});
256
257 var function = Self{
258 .gpa = bin_file.allocator,
259 .air = air,
260 .liveness = liveness,
261 .target = &bin_file.options.target,
262 .bin_file = bin_file,
263 .mod_fn = module_fn,
264 .code = code,
265 .debug_output = debug_output,
266 .err_msg = null,
267 .args = undefined, // populated after `resolveCallingConventionValues`
268 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
269 .fn_type = fn_type,
270 .arg_index = 0,
271 .branch_stack = &branch_stack,
272 .src_loc = src_loc,
273 .stack_align = undefined,
274 .end_di_line = module_fn.rbrace_line,
275 .end_di_column = module_fn.rbrace_column,
276 };
277 defer function.stack.deinit(bin_file.allocator);
278 defer function.blocks.deinit(bin_file.allocator);
279 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
280
281 var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) {
282 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
283 error.OutOfRegisters => return FnResult{
284 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
285 },
286 else => |e| return e,
287 };
288 defer call_info.deinit(&function);
289
290 function.args = call_info.args;
291 function.ret_mcv = call_info.return_value;
292 function.stack_align = call_info.stack_align;
293 function.max_end_stack = call_info.stack_byte_count;
294
295 function.gen() catch |err| switch (err) {
296 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
297 error.OutOfRegisters => return FnResult{
298 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
299 },
300 else => |e| return e,
301 };
302
303 var mir = Mir{
304 .instructions = function.mir_instructions.toOwnedSlice(),
305 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
306 };
307 defer mir.deinit(bin_file.allocator);
308
309 var emit = Emit{
310 .mir = mir,
311 .bin_file = bin_file,
312 .debug_output = debug_output,
313 .target = &bin_file.options.target,
314 .src_loc = src_loc,
315 .code = code,
316 .prev_di_pc = 0,
317 .prev_di_line = module_fn.lbrace_line,
318 .prev_di_column = module_fn.lbrace_column,
319 };
320 defer emit.deinit();
321
322 emit.emitMir() catch |err| switch (err) {
323 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
324 else => |e| return e,
325 };
326
327 if (function.err_msg) |em| {
328 return FnResult{ .fail = em };
329 } else {
330 return FnResult{ .appended = {} };
331 }
332}
333
334fn gen(self: *Self) !void {
335 const cc = self.fn_type.fnCallingConvention();
336 if (cc != .Naked) {
337 // TODO Finish function prologue and epilogue for sparcv9.
338
339 // TODO Backpatch stack offset
340 // save %sp, -176, %sp
341 _ = try self.addInst(.{
342 .tag = .save,
343 .data = .{
344 .arithmetic_3op = .{
345 .is_imm = true,
346 .rd = .sp,
347 .rs1 = .sp,
348 .rs2_or_imm = .{ .imm = -176 },
349 },
350 },
351 });
352
353 _ = try self.addInst(.{
354 .tag = .dbg_prologue_end,
355 .data = .{ .nop = {} },
356 });
357
358 try self.genBody(self.air.getMainBody());
359
360 _ = try self.addInst(.{
361 .tag = .dbg_epilogue_begin,
362 .data = .{ .nop = {} },
363 });
364
365 // exitlude jumps
366 if (self.exitlude_jump_relocs.items.len > 0 and
367 self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2)
368 {
369 // If the last Mir instruction (apart from the
370 // dbg_epilogue_begin) is the last exitlude jump
371 // relocation (which would just jump one instruction
372 // further), it can be safely removed
373 self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop());
374 }
375
376 for (self.exitlude_jump_relocs.items) |jmp_reloc| {
377 _ = jmp_reloc;
378 return self.fail("TODO add branches in sparcv9", .{});
379 }
380
381 // return %i7 + 8
382 _ = try self.addInst(.{
383 .tag = .@"return",
384 .data = .{
385 .arithmetic_2op = .{
386 .is_imm = true,
387 .rs1 = .@"i7",
388 .rs2_or_imm = .{ .imm = 8 },
389 },
390 },
391 });
392
393 // TODO Find a way to fill this slot
394 // nop
395 _ = try self.addInst(.{
396 .tag = .nop,
397 .data = .{ .nop = {} },
398 });
399 } else {
400 _ = try self.addInst(.{
401 .tag = .dbg_prologue_end,
402 .data = .{ .nop = {} },
403 });
404
405 try self.genBody(self.air.getMainBody());
406
407 _ = try self.addInst(.{
408 .tag = .dbg_epilogue_begin,
409 .data = .{ .nop = {} },
410 });
411 }
412
413 // Drop them off at the rbrace.
414 _ = try self.addInst(.{
415 .tag = .dbg_line,
416 .data = .{ .dbg_line_column = .{
417 .line = self.end_di_line,
418 .column = self.end_di_column,
419 } },
420 });
421}
422
423fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
424 const air_tags = self.air.instructions.items(.tag);
425
426 for (body) |inst| {
427 const old_air_bookkeeping = self.air_bookkeeping;
428 try self.ensureProcessDeathCapacity(Liveness.bpi);
429
430 switch (air_tags[inst]) {
431 // zig fmt: off
432 .add, .ptr_add => @panic("TODO try self.airBinOp(inst)"),
433 .addwrap => @panic("TODO try self.airAddWrap(inst)"),
434 .add_sat => @panic("TODO try self.airAddSat(inst)"),
435 .sub, .ptr_sub => @panic("TODO try self.airBinOp(inst)"),
436 .subwrap => @panic("TODO try self.airSubWrap(inst)"),
437 .sub_sat => @panic("TODO try self.airSubSat(inst)"),
438 .mul => @panic("TODO try self.airMul(inst)"),
439 .mulwrap => @panic("TODO try self.airMulWrap(inst)"),
440 .mul_sat => @panic("TODO try self.airMulSat(inst)"),
441 .rem => @panic("TODO try self.airRem(inst)"),
442 .mod => @panic("TODO try self.airMod(inst)"),
443 .shl, .shl_exact => @panic("TODO try self.airShl(inst)"),
444 .shl_sat => @panic("TODO try self.airShlSat(inst)"),
445 .min => @panic("TODO try self.airMin(inst)"),
446 .max => @panic("TODO try self.airMax(inst)"),
447 .slice => @panic("TODO try self.airSlice(inst)"),
448
449 .sqrt,
450 .sin,
451 .cos,
452 .exp,
453 .exp2,
454 .log,
455 .log2,
456 .log10,
457 .fabs,
458 .floor,
459 .ceil,
460 .round,
461 .trunc_float,
462 => @panic("TODO try self.airUnaryMath(inst)"),
463
464 .add_with_overflow => @panic("TODO try self.airAddWithOverflow(inst)"),
465 .sub_with_overflow => @panic("TODO try self.airSubWithOverflow(inst)"),
466 .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"),
467 .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"),
468
469 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
470
471 .cmp_lt => @panic("TODO try self.airCmp(inst, .lt)"),
472 .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"),
473 .cmp_eq => @panic("TODO try self.airCmp(inst, .eq)"),
474 .cmp_gte => @panic("TODO try self.airCmp(inst, .gte)"),
475 .cmp_gt => @panic("TODO try self.airCmp(inst, .gt)"),
476 .cmp_neq => @panic("TODO try self.airCmp(inst, .neq)"),
477 .cmp_vector => @panic("TODO try self.airCmpVector(inst)"),
478 .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"),
479
480 .bool_and => @panic("TODO try self.airBoolOp(inst)"),
481 .bool_or => @panic("TODO try self.airBoolOp(inst)"),
482 .bit_and => @panic("TODO try self.airBitAnd(inst)"),
483 .bit_or => @panic("TODO try self.airBitOr(inst)"),
484 .xor => @panic("TODO try self.airXor(inst)"),
485 .shr, .shr_exact => @panic("TODO try self.airShr(inst)"),
486
487 .alloc => @panic("TODO try self.airAlloc(inst)"),
488 .ret_ptr => try self.airRetPtr(inst),
489 .arg => try self.airArg(inst),
490 .assembly => try self.airAsm(inst),
491 .bitcast => @panic("TODO try self.airBitCast(inst)"),
492 .block => try self.airBlock(inst),
493 .br => @panic("TODO try self.airBr(inst)"),
494 .breakpoint => try self.airBreakpoint(),
495 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),
496 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),
497 .fence => @panic("TODO try self.airFence()"),
498 .cond_br => @panic("TODO try self.airCondBr(inst)"),
499 .dbg_stmt => try self.airDbgStmt(inst),
500 .fptrunc => @panic("TODO try self.airFptrunc(inst)"),
501 .fpext => @panic("TODO try self.airFpext(inst)"),
502 .intcast => @panic("TODO try self.airIntCast(inst)"),
503 .trunc => @panic("TODO try self.airTrunc(inst)"),
504 .bool_to_int => @panic("TODO try self.airBoolToInt(inst)"),
505 .is_non_null => @panic("TODO try self.airIsNonNull(inst)"),
506 .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"),
507 .is_null => @panic("TODO try self.airIsNull(inst)"),
508 .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"),
509 .is_non_err => @panic("TODO try self.airIsNonErr(inst)"),
510 .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"),
511 .is_err => @panic("TODO try self.airIsErr(inst)"),
512 .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"),
513 .load => @panic("TODO try self.airLoad(inst)"),
514 .loop => @panic("TODO try self.airLoop(inst)"),
515 .not => @panic("TODO try self.airNot(inst)"),
516 .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"),
517 .ret => try self.airRet(inst),
518 .ret_load => try self.airRetLoad(inst),
519 .store => try self.airStore(inst),
520 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),
521 .struct_field_val=> @panic("TODO try self.airStructFieldVal(inst)"),
522 .array_to_slice => @panic("TODO try self.airArrayToSlice(inst)"),
523 .int_to_float => @panic("TODO try self.airIntToFloat(inst)"),
524 .float_to_int => @panic("TODO try self.airFloatToInt(inst)"),
525 .cmpxchg_strong => @panic("TODO try self.airCmpxchg(inst)"),
526 .cmpxchg_weak => @panic("TODO try self.airCmpxchg(inst)"),
527 .atomic_rmw => @panic("TODO try self.airAtomicRmw(inst)"),
528 .atomic_load => @panic("TODO try self.airAtomicLoad(inst)"),
529 .memcpy => @panic("TODO try self.airMemcpy(inst)"),
530 .memset => @panic("TODO try self.airMemset(inst)"),
531 .set_union_tag => @panic("TODO try self.airSetUnionTag(inst)"),
532 .get_union_tag => @panic("TODO try self.airGetUnionTag(inst)"),
533 .clz => @panic("TODO try self.airClz(inst)"),
534 .ctz => @panic("TODO try self.airCtz(inst)"),
535 .popcount => @panic("TODO try self.airPopcount(inst)"),
536 .byte_swap => @panic("TODO try self.airByteSwap(inst)"),
537 .bit_reverse => @panic("TODO try self.airBitReverse(inst)"),
538 .tag_name => @panic("TODO try self.airTagName(inst)"),
539 .error_name => @panic("TODO try self.airErrorName(inst)"),
540 .splat => @panic("TODO try self.airSplat(inst)"),
541 .select => @panic("TODO try self.airSelect(inst)"),
542 .shuffle => @panic("TODO try self.airShuffle(inst)"),
543 .reduce => @panic("TODO try self.airReduce(inst)"),
544 .aggregate_init => @panic("TODO try self.airAggregateInit(inst)"),
545 .union_init => @panic("TODO try self.airUnionInit(inst)"),
546 .prefetch => @panic("TODO try self.airPrefetch(inst)"),
547 .mul_add => @panic("TODO try self.airMulAdd(inst)"),
548
549 .dbg_var_ptr,
550 .dbg_var_val,
551 => try self.airDbgVar(inst),
552
553 .dbg_inline_begin,
554 .dbg_inline_end,
555 => try self.airDbgInline(inst),
556
557 .dbg_block_begin,
558 .dbg_block_end,
559 => try self.airDbgBlock(inst),
560
561 .call => try self.airCall(inst, .auto),
562 .call_always_tail => try self.airCall(inst, .always_tail),
563 .call_never_tail => try self.airCall(inst, .never_tail),
564 .call_never_inline => try self.airCall(inst, .never_inline),
565
566 .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"),
567 .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"),
568 .atomic_store_release => @panic("TODO try self.airAtomicStore(inst, .Release)"),
569 .atomic_store_seq_cst => @panic("TODO try self.airAtomicStore(inst, .SeqCst)"),
570
571 .struct_field_ptr_index_0 => @panic("TODO try self.airStructFieldPtrIndex(inst, 0)"),
572 .struct_field_ptr_index_1 => @panic("TODO try self.airStructFieldPtrIndex(inst, 1)"),
573 .struct_field_ptr_index_2 => @panic("TODO try self.airStructFieldPtrIndex(inst, 2)"),
574 .struct_field_ptr_index_3 => @panic("TODO try self.airStructFieldPtrIndex(inst, 3)"),
575
576 .field_parent_ptr => @panic("TODO try self.airFieldParentPtr(inst)"),
577
578 .switch_br => try self.airSwitch(inst),
579 .slice_ptr => @panic("TODO try self.airSlicePtr(inst)"),
580 .slice_len => @panic("TODO try self.airSliceLen(inst)"),
581
582 .ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"),
583 .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"),
584
585 .array_elem_val => @panic("TODO try self.airArrayElemVal(inst)"),
586 .slice_elem_val => @panic("TODO try self.airSliceElemVal(inst)"),
587 .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"),
588 .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"),
589 .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"),
590
591 .constant => unreachable, // excluded from function bodies
592 .const_ty => unreachable, // excluded from function bodies
593 .unreach => self.finishAirBookkeeping(),
594
595 .optional_payload => @panic("TODO try self.airOptionalPayload(inst)"),
596 .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"),
597 .optional_payload_ptr_set => @panic("TODO try self.airOptionalPayloadPtrSet(inst)"),
598 .unwrap_errunion_err => @panic("TODO try self.airUnwrapErrErr(inst)"),
599 .unwrap_errunion_payload => @panic("TODO try self.airUnwrapErrPayload(inst)"),
600 .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),
601 .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"),
602 .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"),
603
604 .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),
605 .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"),
606 .wrap_errunion_err => @panic("TODO try self.airWrapErrUnionErr(inst)"),
607
608 .wasm_memory_size => unreachable,
609 .wasm_memory_grow => unreachable,
610 // zig fmt: on
611 }
612
613 if (std.debug.runtime_safety) {
614 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
615 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] });
616 }
617 }
618 }
619}
620
621fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
622 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
623 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
624 const is_volatile = (extra.data.flags & 0x80000000) != 0;
625 const clobbers_len = @truncate(u31, extra.data.flags);
626 var extra_i: usize = extra.end;
627 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.outputs_len]);
628 extra_i += outputs.len;
629 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.inputs_len]);
630 extra_i += inputs.len;
631
632 const dead = !is_volatile and self.liveness.isUnused(inst);
633 const result: MCValue = if (dead) .dead else result: {
634 if (outputs.len > 1) {
635 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
636 }
637
638 const output_constraint: ?[]const u8 = for (outputs) |output| {
639 if (output != .none) {
640 return self.fail("TODO implement codegen for non-expr asm", .{});
641 }
642 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
643 // This equation accounts for the fact that even if we have exactly 4 bytes
644 // for the string, we still use the next u32 for the null terminator.
645 extra_i += constraint.len / 4 + 1;
646
647 break constraint;
648 } else null;
649
650 for (inputs) |input| {
651 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
652 const constraint = std.mem.sliceTo(input_bytes, 0);
653 const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0);
654 // This equation accounts for the fact that even if we have exactly 4 bytes
655 // for the string, we still use the next u32 for the null terminator.
656 extra_i += (constraint.len + input_name.len + 1) / 4 + 1;
657
658 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
659 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
660 }
661 const reg_name = constraint[1 .. constraint.len - 1];
662 const reg = parseRegName(reg_name) orelse
663 return self.fail("unrecognized register: '{s}'", .{reg_name});
664
665 const arg_mcv = try self.resolveInst(input);
666 try self.register_manager.getReg(reg, null);
667 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
668 }
669
670 {
671 var clobber_i: u32 = 0;
672 while (clobber_i < clobbers_len) : (clobber_i += 1) {
673 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
674 // This equation accounts for the fact that even if we have exactly 4 bytes
675 // for the string, we still use the next u32 for the null terminator.
676 extra_i += clobber.len / 4 + 1;
677
678 // TODO honor these
679 }
680 }
681
682 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
683
684 if (mem.eql(u8, asm_source, "ta 0x6d")) {
685 _ = try self.addInst(.{
686 .tag = .tcc,
687 .data = .{
688 .trap = .{
689 .is_imm = true,
690 .cond = 0b1000, // TODO need to look into changing this into an enum
691 .rs2_or_imm = .{ .imm = 0x6d },
692 },
693 },
694 });
695 } else {
696 return self.fail("TODO implement a full SPARCv9 assembly parsing", .{});
697 }
698
699 if (output_constraint) |output| {
700 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
701 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
702 }
703 const reg_name = output[2 .. output.len - 1];
704 const reg = parseRegName(reg_name) orelse
705 return self.fail("unrecognized register: '{s}'", .{reg_name});
706 break :result MCValue{ .register = reg };
707 } else {
708 break :result MCValue{ .none = {} };
709 }
710 };
711
712 simple: {
713 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
714 var buf_index: usize = 0;
715 for (outputs) |output| {
716 if (output == .none) continue;
717
718 if (buf_index >= buf.len) break :simple;
719 buf[buf_index] = output;
720 buf_index += 1;
721 }
722 if (buf_index + inputs.len > buf.len) break :simple;
723 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
724 return self.finishAir(inst, result, buf);
725 }
726
727 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
728 for (outputs) |output| {
729 if (output == .none) continue;
730
731 bt.feed(output);
732 }
733 for (inputs) |input| {
734 bt.feed(input);
735 }
736 return bt.finishAir(result);
737}
738
739fn airArg(self: *Self, inst: Air.Inst.Index) !void {
740 const arg_index = self.arg_index;
741 self.arg_index += 1;
742
743 const ty = self.air.typeOfIndex(inst);
744 _ = ty;
745
746 const result = self.args[arg_index];
747 // TODO support stack-only arguments
748 // TODO Copy registers to the stack
749 const mcv = result;
750
751 _ = try self.addInst(.{
752 .tag = .dbg_arg,
753 .data = .{
754 .dbg_arg_info = .{
755 .air_inst = inst,
756 .arg_index = arg_index,
757 },
758 },
759 });
760
761 if (self.liveness.isUnused(inst))
762 return self.finishAirBookkeeping();
763
764 switch (mcv) {
765 .register => |reg| {
766 self.register_manager.getRegAssumeFree(reg, inst);
767 },
768 else => {},
769 }
770
771 return self.finishAir(inst, mcv, .{ .none, .none, .none });
772}
773
774fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
775 try self.blocks.putNoClobber(self.gpa, inst, .{
776 // A block is a setup to be able to jump to the end.
777 .relocs = .{},
778 // It also acts as a receptacle for break operands.
779 // Here we use `MCValue.none` to represent a null value so that the first
780 // break instruction will choose a MCValue for the block result and overwrite
781 // this field. Following break instructions will use that MCValue to put their
782 // block results.
783 .mcv = MCValue{ .none = {} },
784 });
785 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
786
787 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
788 const extra = self.air.extraData(Air.Block, ty_pl.payload);
789 const body = self.air.extra[extra.end..][0..extra.data.body_len];
790 try self.genBody(body);
791
792 // relocations for `bpcc` instructions
793 const relocs = &self.blocks.getPtr(inst).?.relocs;
794 if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) {
795 // If the last Mir instruction is the last relocation (which
796 // would just jump one instruction further), it can be safely
797 // removed
798 self.mir_instructions.orderedRemove(relocs.pop());
799 }
800 for (relocs.items) |reloc| {
801 try self.performReloc(reloc);
802 }
803
804 const result = self.blocks.getPtr(inst).?.mcv;
805 return self.finishAir(inst, result, .{ .none, .none, .none });
806}
807
808fn airBreakpoint(self: *Self) !void {
809 // ta 0x01
810 _ = try self.addInst(.{
811 .tag = .tcc,
812 .data = .{
813 .trap = .{
814 .is_imm = true,
815 .cond = 0b1000, // TODO need to look into changing this into an enum
816 .rs2_or_imm = .{ .imm = 0x01 },
817 },
818 },
819 });
820 return self.finishAirBookkeeping();
821}
822
823fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void {
824 if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch});
825
826 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
827 const callee = pl_op.operand;
828 const extra = self.air.extraData(Air.Call, pl_op.payload);
829 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end .. extra.end + extra.data.args_len]);
830 const ty = self.air.typeOf(callee);
831 const fn_ty = switch (ty.zigTypeTag()) {
832 .Fn => ty,
833 .Pointer => ty.childType(),
834 else => unreachable,
835 };
836
837 var info = try self.resolveCallingConventionValues(fn_ty, .caller);
838 defer info.deinit(self);
839 for (info.args) |mc_arg, arg_i| {
840 const arg = args[arg_i];
841 const arg_ty = self.air.typeOf(arg);
842 const arg_mcv = try self.resolveInst(arg);
843
844 switch (mc_arg) {
845 .none => continue,
846 .undef => unreachable,
847 .immediate => unreachable,
848 .unreach => unreachable,
849 .dead => unreachable,
850 .memory => unreachable,
851 .register => |reg| {
852 try self.register_manager.getReg(reg, null);
853 try self.genSetReg(arg_ty, reg, arg_mcv);
854 },
855 .stack_offset => {
856 return self.fail("TODO implement calling with parameters in memory", .{});
857 },
858 .ptr_stack_offset => {
859 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
860 },
861 }
862 }
863
864 // Due to incremental compilation, how function calls are generated depends
865 // on linking.
866 if (self.air.value(callee)) |func_value| {
867 if (self.bin_file.tag == link.File.Elf.base_tag) {
868 if (func_value.castTag(.function)) |func_payload| {
869 const func = func_payload.data;
870 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
871 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
872 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
873 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
874 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
875 } else unreachable;
876
877 try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr });
878
879 _ = try self.addInst(.{
880 .tag = .jmpl,
881 .data = .{ .branch_link_indirect = .{ .reg = .o7 } },
882 });
883 } else if (func_value.castTag(.extern_fn)) |_| {
884 return self.fail("TODO implement calling extern functions", .{});
885 } else {
886 return self.fail("TODO implement calling bitcasted functions", .{});
887 }
888 } else @panic("TODO SPARCv9 currently does not support non-ELF binaries");
889 } else {
890 assert(ty.zigTypeTag() == .Pointer);
891 const mcv = try self.resolveInst(callee);
892 try self.genSetReg(ty, .o7, mcv);
893
894 _ = try self.addInst(.{
895 .tag = .jmpl,
896 .data = .{ .branch_link_indirect = .{ .reg = .o7 } },
897 });
898 }
899
900 const result = info.return_value;
901
902 if (args.len + 1 <= Liveness.bpi - 1) {
903 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
904 buf[0] = callee;
905 std.mem.copy(Air.Inst.Ref, buf[1..], args);
906 return self.finishAir(inst, result, buf);
907 }
908
909 @panic("TODO handle return value with BigTomb");
910}
911
912fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
913 // TODO emit debug info lexical block
914 return self.finishAir(inst, .dead, .{ .none, .none, .none });
915}
916
917fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
918 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
919 const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
920 // TODO emit debug info for function change
921 _ = function;
922 return self.finishAir(inst, .dead, .{ .none, .none, .none });
923}
924
925fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
926 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
927
928 _ = try self.addInst(.{
929 .tag = .dbg_line,
930 .data = .{
931 .dbg_line_column = .{
932 .line = dbg_stmt.line,
933 .column = dbg_stmt.column,
934 },
935 },
936 });
937
938 return self.finishAirBookkeeping();
939}
940
941fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
942 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
943 const name = self.air.nullTerminatedString(pl_op.payload);
944 const operand = pl_op.operand;
945 // TODO emit debug info for this variable
946 _ = name;
947 return self.finishAir(inst, .dead, .{ operand, .none, .none });
948}
949
950fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
951 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
952 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
953 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
954}
955
956fn airRet(self: *Self, inst: Air.Inst.Index) !void {
957 const un_op = self.air.instructions.items(.data)[inst].un_op;
958 const operand = try self.resolveInst(un_op);
959 try self.ret(operand);
960 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
961}
962
963fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
964 const un_op = self.air.instructions.items(.data)[inst].un_op;
965 const ptr = try self.resolveInst(un_op);
966 _ = ptr;
967 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
968 //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
969}
970
971fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
972 const stack_offset = try self.allocMemPtr(inst);
973 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
974}
975
976fn airStore(self: *Self, inst: Air.Inst.Index) !void {
977 _ = self;
978 _ = inst;
979
980 return self.fail("TODO implement store for {}", .{self.target.cpu.arch});
981}
982
983fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
984 _ = self;
985 _ = inst;
986
987 return self.fail("TODO implement switch for {}", .{self.target.cpu.arch});
988}
989
990// Common helper functions
991
992fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
993 const gpa = self.gpa;
994
995 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
996
997 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);
998 self.mir_instructions.appendAssumeCapacity(inst);
999 return result_index;
1000}
1001
1002fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
1003 if (abi_align > self.stack_align)
1004 self.stack_align = abi_align;
1005 // TODO find a free slot instead of always appending
1006 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
1007 self.next_stack_offset = offset + abi_size;
1008 if (self.next_stack_offset > self.max_end_stack)
1009 self.max_end_stack = self.next_stack_offset;
1010 try self.stack.putNoClobber(self.gpa, offset, .{
1011 .inst = inst,
1012 .size = abi_size,
1013 });
1014 return offset;
1015}
1016
1017/// Use a pointer instruction as the basis for allocating stack memory.
1018fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
1019 const elem_ty = self.air.typeOfIndex(inst).elemType();
1020
1021 if (!elem_ty.hasRuntimeBits()) {
1022 // As this stack item will never be dereferenced at runtime,
1023 // return the stack offset 0. Stack offset 0 will be where all
1024 // zero-sized stack allocations live as non-zero-sized
1025 // allocations will always have an offset > 0.
1026 return @as(u32, 0);
1027 }
1028
1029 const target = self.target.*;
1030 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
1031 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
1032 };
1033 // TODO swap this for inst.ty.ptrAlign
1034 const abi_align = elem_ty.abiAlignment(self.target.*);
1035 return self.allocMem(inst, abi_size, abi_align);
1036}
1037
1038fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
1039 const elem_ty = self.air.typeOfIndex(inst);
1040 const target = self.target.*;
1041 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
1042 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
1043 };
1044 const abi_align = elem_ty.abiAlignment(self.target.*);
1045 if (abi_align > self.stack_align)
1046 self.stack_align = abi_align;
1047
1048 if (reg_ok) {
1049 // Make sure the type can fit in a register before we try to allocate one.
1050 if (abi_size <= 8) {
1051 if (self.register_manager.tryAllocReg(inst)) |reg| {
1052 return MCValue{ .register = reg };
1053 }
1054 }
1055 }
1056 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
1057 return MCValue{ .stack_offset = stack_offset };
1058}
1059
1060/// Copies a value to a register without tracking the register. The register is not considered
1061/// allocated. A second call to `copyToTmpRegister` may return the same register.
1062/// This can have a side effect of spilling instructions to the stack to free up a register.
1063fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
1064 const reg = try self.register_manager.allocReg(null);
1065 try self.genSetReg(ty, reg, mcv);
1066 return reg;
1067}
1068
1069fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
1070 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
1071 try table.ensureUnusedCapacity(self.gpa, additional_count);
1072}
1073
1074fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
1075 @setCold(true);
1076 assert(self.err_msg == null);
1077 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
1078 return error.CodegenFail;
1079}
1080
1081/// Called when there are no operands, and the instruction is always unreferenced.
1082fn finishAirBookkeeping(self: *Self) void {
1083 if (std.debug.runtime_safety) {
1084 self.air_bookkeeping += 1;
1085 }
1086}
1087
1088fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
1089 var tomb_bits = self.liveness.getTombBits(inst);
1090 for (operands) |op| {
1091 const dies = @truncate(u1, tomb_bits) != 0;
1092 tomb_bits >>= 1;
1093 if (!dies) continue;
1094 const op_int = @enumToInt(op);
1095 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
1096 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
1097 self.processDeath(op_index);
1098 }
1099 const is_used = @truncate(u1, tomb_bits) == 0;
1100 if (is_used) {
1101 log.debug("%{d} => {}", .{ inst, result });
1102 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1103 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
1104
1105 switch (result) {
1106 .register => |reg| {
1107 // In some cases (such as bitcast), an operand
1108 // may be the same MCValue as the result. If
1109 // that operand died and was a register, it
1110 // was freed by processDeath. We have to
1111 // "re-allocate" the register.
1112 if (self.register_manager.isRegFree(reg)) {
1113 self.register_manager.getRegAssumeFree(reg, inst);
1114 }
1115 },
1116 else => {},
1117 }
1118 }
1119 self.finishAirBookkeeping();
1120}
1121
1122fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void {
1123 assert(off_type == Register or off_type == i13);
1124
1125 const is_imm = (off_type == i13);
1126 const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off };
1127
1128 switch (abi_size) {
1129 1 => {
1130 _ = try self.addInst(.{
1131 .tag = .ldub,
1132 .data = .{
1133 .arithmetic_3op = .{
1134 .is_imm = is_imm,
1135 .rd = value_reg,
1136 .rs1 = addr_reg,
1137 .rs2_or_imm = rs2_or_imm,
1138 },
1139 },
1140 });
1141 },
1142 2 => {
1143 _ = try self.addInst(.{
1144 .tag = .lduh,
1145 .data = .{
1146 .arithmetic_3op = .{
1147 .is_imm = is_imm,
1148 .rd = value_reg,
1149 .rs1 = addr_reg,
1150 .rs2_or_imm = rs2_or_imm,
1151 },
1152 },
1153 });
1154 },
1155 4 => {
1156 _ = try self.addInst(.{
1157 .tag = .lduw,
1158 .data = .{
1159 .arithmetic_3op = .{
1160 .is_imm = is_imm,
1161 .rd = value_reg,
1162 .rs1 = addr_reg,
1163 .rs2_or_imm = rs2_or_imm,
1164 },
1165 },
1166 });
1167 },
1168 8 => {
1169 _ = try self.addInst(.{
1170 .tag = .ldx,
1171 .data = .{
1172 .arithmetic_3op = .{
1173 .is_imm = is_imm,
1174 .rd = value_reg,
1175 .rs1 = addr_reg,
1176 .rs2_or_imm = rs2_or_imm,
1177 },
1178 },
1179 });
1180 },
1181 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}),
1182 else => unreachable,
1183 }
1184}
1185
1186fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
1187 switch (mcv) {
1188 .dead => unreachable,
1189 .unreach, .none => return, // Nothing to do.
1190 .undef => {
1191 if (!self.wantSafety())
1192 return; // The already existing value will do just fine.
1193 // Write the debug undefined value.
1194 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
1195 },
1196 .ptr_stack_offset => |off| {
1197 const simm13 = math.cast(u12, off) catch
1198 return self.fail("TODO larger stack offsets", .{});
1199
1200 _ = try self.addInst(.{
1201 .tag = .add,
1202 .data = .{
1203 .arithmetic_3op = .{
1204 .is_imm = true,
1205 .rd = reg,
1206 .rs1 = .sp,
1207 .rs2_or_imm = .{ .imm = simm13 },
1208 },
1209 },
1210 });
1211 },
1212 .immediate => |x| {
1213 if (x <= math.maxInt(u12)) {
1214 _ = try self.addInst(.{
1215 .tag = .@"or",
1216 .data = .{
1217 .arithmetic_3op = .{
1218 .is_imm = true,
1219 .rd = reg,
1220 .rs1 = .g0,
1221 .rs2_or_imm = .{ .imm = @truncate(u12, x) },
1222 },
1223 },
1224 });
1225 } else if (x <= math.maxInt(u32)) {
1226 _ = try self.addInst(.{
1227 .tag = .sethi,
1228 .data = .{
1229 .sethi = .{
1230 .rd = reg,
1231 .imm = @truncate(u22, x >> 10),
1232 },
1233 },
1234 });
1235
1236 _ = try self.addInst(.{
1237 .tag = .@"or",
1238 .data = .{
1239 .arithmetic_3op = .{
1240 .is_imm = true,
1241 .rd = reg,
1242 .rs1 = reg,
1243 .rs2_or_imm = .{ .imm = @truncate(u10, x) },
1244 },
1245 },
1246 });
1247 } else if (x <= math.maxInt(u44)) {
1248 try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 12) });
1249
1250 _ = try self.addInst(.{
1251 .tag = .sllx,
1252 .data = .{
1253 .shift = .{
1254 .is_imm = true,
1255 .width = .shift64,
1256 .rd = reg,
1257 .rs1 = reg,
1258 .rs2_or_imm = .{ .imm = 12 },
1259 },
1260 },
1261 });
1262
1263 _ = try self.addInst(.{
1264 .tag = .@"or",
1265 .data = .{
1266 .arithmetic_3op = .{
1267 .is_imm = true,
1268 .rd = reg,
1269 .rs1 = reg,
1270 .rs2_or_imm = .{ .imm = @truncate(u12, x) },
1271 },
1272 },
1273 });
1274 } else {
1275 // Need to allocate a temporary register to load 64-bit immediates.
1276 const tmp_reg = try self.register_manager.allocReg(null);
1277
1278 try self.genSetReg(ty, tmp_reg, .{ .immediate = @truncate(u32, x) });
1279 try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 32) });
1280
1281 _ = try self.addInst(.{
1282 .tag = .sllx,
1283 .data = .{
1284 .shift = .{
1285 .is_imm = true,
1286 .width = .shift64,
1287 .rd = reg,
1288 .rs1 = reg,
1289 .rs2_or_imm = .{ .imm = 32 },
1290 },
1291 },
1292 });
1293
1294 _ = try self.addInst(.{
1295 .tag = .@"or",
1296 .data = .{
1297 .arithmetic_3op = .{
1298 .is_imm = false,
1299 .rd = reg,
1300 .rs1 = reg,
1301 .rs2_or_imm = .{ .rs2 = tmp_reg },
1302 },
1303 },
1304 });
1305 }
1306 },
1307 .register => |src_reg| {
1308 // If the registers are the same, nothing to do.
1309 if (src_reg.id() == reg.id())
1310 return;
1311
1312 // or %g0, src, dst (aka mov src, dst)
1313 _ = try self.addInst(.{
1314 .tag = .@"or",
1315 .data = .{
1316 .arithmetic_3op = .{
1317 .is_imm = false,
1318 .rd = reg,
1319 .rs1 = .g0,
1320 .rs2_or_imm = .{ .rs2 = src_reg },
1321 },
1322 },
1323 });
1324 },
1325 .memory => |addr| {
1326 // The value is in memory at a hard-coded address.
1327 // If the type is a pointer, it means the pointer address is at this memory location.
1328 try self.genSetReg(ty, reg, .{ .immediate = addr });
1329 try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*));
1330 },
1331 .stack_offset => |off| {
1332 const simm13 = math.cast(u12, off) catch
1333 return self.fail("TODO larger stack offsets", .{});
1334 try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*));
1335 },
1336 }
1337}
1338
1339fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
1340 const abi_size = ty.abiSize(self.target.*);
1341 switch (mcv) {
1342 .dead => unreachable,
1343 .unreach, .none => return, // Nothing to do.
1344 .undef => {
1345 if (!self.wantSafety())
1346 return; // The already existing value will do just fine.
1347 // TODO Upgrade this to a memset call when we have that available.
1348 switch (ty.abiSize(self.target.*)) {
1349 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
1350 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
1351 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
1352 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
1353 else => return self.fail("TODO implement memset", .{}),
1354 }
1355 },
1356 .immediate,
1357 .ptr_stack_offset,
1358 => {
1359 const reg = try self.copyToTmpRegister(ty, mcv);
1360 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
1361 },
1362 .register => return self.fail("TODO implement storing types abi_size={}", .{abi_size}),
1363 .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}),
1364 }
1365}
1366
1367fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
1368 if (typed_value.val.isUndef())
1369 return MCValue{ .undef = {} };
1370
1371 if (typed_value.val.castTag(.decl_ref)) |payload| {
1372 return self.lowerDeclRef(typed_value, payload.data);
1373 }
1374 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
1375 return self.lowerDeclRef(typed_value, payload.data.decl);
1376 }
1377 const target = self.target.*;
1378
1379 switch (typed_value.ty.zigTypeTag()) {
1380 .Int => {
1381 const info = typed_value.ty.intInfo(self.target.*);
1382 if (info.bits <= 64) {
1383 const unsigned = switch (info.signedness) {
1384 .signed => blk: {
1385 const signed = typed_value.val.toSignedInt();
1386 break :blk @bitCast(u64, signed);
1387 },
1388 .unsigned => typed_value.val.toUnsignedInt(target),
1389 };
1390
1391 return MCValue{ .immediate = unsigned };
1392 } else {
1393 return self.fail("TODO implement int genTypedValue of > 64 bits", .{});
1394 }
1395 },
1396 .ComptimeInt => unreachable, // semantic analysis prevents this
1397 .ComptimeFloat => unreachable, // semantic analysis prevents this
1398 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}),
1399 }
1400}
1401
1402fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
1403 // Treat each stack item as a "layer" on top of the previous one.
1404 var i: usize = self.branch_stack.items.len;
1405 while (true) {
1406 i -= 1;
1407 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1408 assert(mcv != .dead);
1409 return mcv;
1410 }
1411 }
1412}
1413
1414fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
1415 try self.ensureProcessDeathCapacity(operand_count + 1);
1416 return BigTomb{
1417 .function = self,
1418 .inst = inst,
1419 .tomb_bits = self.liveness.getTombBits(inst),
1420 .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
1421 .bit_index = 0,
1422 };
1423}
1424
1425fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue {
1426 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1427 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1428
1429 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
1430 if (tv.ty.zigTypeTag() == .Pointer) blk: {
1431 if (tv.ty.castPtrToFn()) |_| break :blk;
1432 if (!tv.ty.elemType2().hasRuntimeBits()) {
1433 return MCValue.none;
1434 }
1435 }
1436
1437 decl.alive = true;
1438 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
1439 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1440 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
1441 return MCValue{ .memory = got_addr };
1442 } else {
1443 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
1444 }
1445}
1446
1447fn parseRegName(name: []const u8) ?Register {
1448 if (@hasDecl(Register, "parseRegName")) {
1449 return Register.parseRegName(name);
1450 }
1451 return std.meta.stringToEnum(Register, name);
1452}
1453
1454fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
1455 const tag = self.mir_instructions.items(.tag)[inst];
1456 switch (tag) {
1457 .bpcc => self.mir_instructions.items(.data)[inst].branch_predict.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len),
1458 else => unreachable,
1459 }
1460}
1461
1462/// Asserts there is already capacity to insert into top branch inst_table.
1463fn processDeath(self: *Self, inst: Air.Inst.Index) void {
1464 const air_tags = self.air.instructions.items(.tag);
1465 if (air_tags[inst] == .constant) return; // Constants are immortal.
1466 // When editing this function, note that the logic must synchronize with `reuseOperand`.
1467 const prev_value = self.getResolvedInstValue(inst);
1468 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1469 branch.inst_table.putAssumeCapacity(inst, .dead);
1470 switch (prev_value) {
1471 .register => |reg| {
1472 self.register_manager.freeReg(reg);
1473 },
1474 else => {}, // TODO process stack allocation death
1475 }
1476}
1477
1478/// Caller must call `CallMCValues.deinit`.
1479fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView) !CallMCValues {
1480 const cc = fn_ty.fnCallingConvention();
1481 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
1482 defer self.gpa.free(param_types);
1483 fn_ty.fnParamTypes(param_types);
1484 var result: CallMCValues = .{
1485 .args = try self.gpa.alloc(MCValue, param_types.len),
1486 // These undefined values must be populated before returning from this function.
1487 .return_value = undefined,
1488 .stack_byte_count = undefined,
1489 .stack_align = undefined,
1490 };
1491 errdefer self.gpa.free(result.args);
1492
1493 const ret_ty = fn_ty.fnReturnType();
1494
1495 switch (cc) {
1496 .Naked => {
1497 assert(result.args.len == 0);
1498 result.return_value = .{ .unreach = {} };
1499 result.stack_byte_count = 0;
1500 result.stack_align = 1;
1501 return result;
1502 },
1503 .Unspecified, .C => {
1504 // SPARC Compliance Definition 2.4.1, Chapter 3
1505 // Low-Level System Information (64-bit psABI) - Function Calling Sequence
1506
1507 var next_register: usize = 0;
1508 var next_stack_offset: u32 = 0;
1509
1510 // The caller puts the argument in %o0-%o5, which becomes %i0-%i5 inside the callee.
1511 const argument_registers = switch (role) {
1512 .caller => abi.c_abi_int_param_regs_caller_view,
1513 .callee => abi.c_abi_int_param_regs_callee_view,
1514 };
1515
1516 for (param_types) |ty, i| {
1517 const param_size = @intCast(u32, ty.abiSize(self.target.*));
1518 if (param_size <= 8) {
1519 if (next_register < argument_registers.len) {
1520 result.args[i] = .{ .register = argument_registers[next_register] };
1521 next_register += 1;
1522 } else {
1523 result.args[i] = .{ .stack_offset = next_stack_offset };
1524 next_register += next_stack_offset;
1525 }
1526 } else if (param_size <= 16) {
1527 if (next_register < argument_registers.len - 1) {
1528 return self.fail("TODO MCValues with 2 registers", .{});
1529 } else if (next_register < argument_registers.len) {
1530 return self.fail("TODO MCValues split register + stack", .{});
1531 } else {
1532 result.args[i] = .{ .stack_offset = next_stack_offset };
1533 next_register += next_stack_offset;
1534 }
1535 } else {
1536 result.args[i] = .{ .stack_offset = next_stack_offset };
1537 next_register += next_stack_offset;
1538 }
1539 }
1540
1541 result.stack_byte_count = next_stack_offset;
1542 result.stack_align = 16;
1543
1544 if (ret_ty.zigTypeTag() == .NoReturn) {
1545 result.return_value = .{ .unreach = {} };
1546 } else if (!ret_ty.hasRuntimeBits()) {
1547 result.return_value = .{ .none = {} };
1548 } else {
1549 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
1550 // The callee puts the return values in %i0-%i3, which becomes %o0-%o3 inside the caller.
1551 if (ret_ty_size <= 8) {
1552 result.return_value = switch (role) {
1553 .caller => .{ .register = abi.c_abi_int_return_regs_caller_view[0] },
1554 .callee => .{ .register = abi.c_abi_int_return_regs_callee_view[0] },
1555 };
1556 } else {
1557 return self.fail("TODO support more return values for sparcv9", .{});
1558 }
1559 }
1560 },
1561 else => return self.fail("TODO implement function parameters for {} on sparcv9", .{cc}),
1562 }
1563
1564 return result;
1565}
1566
1567fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
1568 // First section of indexes correspond to a set number of constant values.
1569 const ref_int = @enumToInt(inst);
1570 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
1571 const tv = Air.Inst.Ref.typed_value_map[ref_int];
1572 if (!tv.ty.hasRuntimeBits()) {
1573 return MCValue{ .none = {} };
1574 }
1575 return self.genTypedValue(tv);
1576 }
1577
1578 // If the type has no codegen bits, no need to store it.
1579 const inst_ty = self.air.typeOf(inst);
1580 if (!inst_ty.hasRuntimeBits())
1581 return MCValue{ .none = {} };
1582
1583 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
1584 switch (self.air.instructions.items(.tag)[inst_index]) {
1585 .constant => {
1586 // Constants have static lifetimes, so they are always memoized in the outer most table.
1587 const branch = &self.branch_stack.items[0];
1588 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
1589 if (!gop.found_existing) {
1590 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
1591 gop.value_ptr.* = try self.genTypedValue(.{
1592 .ty = inst_ty,
1593 .val = self.air.values[ty_pl.payload],
1594 });
1595 }
1596 return gop.value_ptr.*;
1597 },
1598 .const_ty => unreachable,
1599 else => return self.getResolvedInstValue(inst_index),
1600 }
1601}
1602
1603fn ret(self: *Self, mcv: MCValue) !void {
1604 const ret_ty = self.fn_type.fnReturnType();
1605 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
1606
1607 // Just add space for an instruction, patch this later
1608 const index = try self.addInst(.{
1609 .tag = .nop,
1610 .data = .{ .nop = {} },
1611 });
1612 try self.exitlude_jump_relocs.append(self.gpa, index);
1613}
1614
1615fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1616 if (!self.liveness.operandDies(inst, op_index))
1617 return false;
1618
1619 switch (mcv) {
1620 .register => |reg| {
1621 // If it's in the registers table, need to associate the register with the
1622 // new instruction.
1623 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
1624 if (!self.register_manager.isRegFree(reg)) {
1625 self.register_manager.registers[index] = inst;
1626 }
1627 }
1628 log.debug("%{d} => {} (reused)", .{ inst, reg });
1629 },
1630 .stack_offset => |off| {
1631 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
1632 },
1633 else => return false,
1634 }
1635
1636 // Prevent the operand deaths processing code from deallocating it.
1637 self.liveness.clearOperandDeath(inst, op_index);
1638
1639 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
1640 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1641 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
1642
1643 return true;
1644}
1645
1646/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
1647fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
1648 switch (loc) {
1649 .none => return,
1650 .register => |reg| return self.genSetReg(ty, reg, val),
1651 .stack_offset => |off| return self.genSetStack(ty, off, val),
1652 .memory => {
1653 return self.fail("TODO implement setRegOrMem for memory", .{});
1654 },
1655 else => unreachable,
1656 }
1657}
1658
1659pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
1660 const stack_mcv = try self.allocRegOrMem(inst, false);
1661 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
1662 const reg_mcv = self.getResolvedInstValue(inst);
1663 assert(reg == reg_mcv.register);
1664 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1665 try branch.inst_table.put(self.gpa, inst, stack_mcv);
1666 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
1667}
1668
1669/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
1670fn wantSafety(self: *Self) bool {
1671 return switch (self.bin_file.options.optimize_mode) {
1672 .Debug => true,
1673 .ReleaseSafe => true,
1674 .ReleaseFast => false,
1675 .ReleaseSmall => false,
1676 };
39}1677}
src/arch/sparcv9/Emit.zig+292
...@@ -1,6 +1,298 @@...@@ -1,6 +1,298 @@
1//! This file contains the functionality for lowering SPARCv9 MIR into1//! This file contains the functionality for lowering SPARCv9 MIR into
2//! machine code2//! machine code
33
4const std = @import("std");
5const Endian = std.builtin.Endian;
6const assert = std.debug.assert;
7const link = @import("../../link.zig");
8const Module = @import("../../Module.zig");
9const ErrorMsg = Module.ErrorMsg;
10const Liveness = @import("../../Liveness.zig");
11const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
12const DW = std.dwarf;
13const leb128 = std.leb;
14
4const Emit = @This();15const Emit = @This();
5const Mir = @import("Mir.zig");16const Mir = @import("Mir.zig");
6const bits = @import("bits.zig");17const bits = @import("bits.zig");
18const Instruction = bits.Instruction;
19const Register = bits.Register;
20
21mir: Mir,
22bin_file: *link.File,
23debug_output: DebugInfoOutput,
24target: *const std.Target,
25err_msg: ?*ErrorMsg = null,
26src_loc: Module.SrcLoc,
27code: *std.ArrayList(u8),
28
29prev_di_line: u32,
30prev_di_column: u32,
31/// Relative to the beginning of `code`.
32prev_di_pc: usize,
33
34const InnerError = error{
35 OutOfMemory,
36 EmitFail,
37};
38
39pub fn emitMir(
40 emit: *Emit,
41) InnerError!void {
42 const mir_tags = emit.mir.instructions.items(.tag);
43
44 // Emit machine code
45 for (mir_tags) |tag, index| {
46 const inst = @intCast(u32, index);
47 switch (tag) {
48 .dbg_arg => try emit.mirDbgArg(inst),
49 .dbg_line => try emit.mirDbgLine(inst),
50 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
51 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
52
53 .add => try emit.mirArithmetic3Op(inst),
54
55 .bpcc => @panic("TODO implement sparcv9 bpcc"),
56
57 .call => @panic("TODO implement sparcv9 call"),
58
59 .jmpl => @panic("TODO implement sparcv9 jmpl"),
60 .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"),
61
62 .ldub => try emit.mirArithmetic3Op(inst),
63 .lduh => try emit.mirArithmetic3Op(inst),
64 .lduw => try emit.mirArithmetic3Op(inst),
65 .ldx => try emit.mirArithmetic3Op(inst),
66
67 .@"or" => try emit.mirArithmetic3Op(inst),
68
69 .nop => try emit.mirNop(),
70
71 .@"return" => try emit.mirArithmetic2Op(inst),
72
73 .save => try emit.mirArithmetic3Op(inst),
74 .restore => try emit.mirArithmetic3Op(inst),
75
76 .sethi => try emit.mirSethi(inst),
77
78 .sllx => @panic("TODO implement sparcv9 sllx"),
79
80 .sub => try emit.mirArithmetic3Op(inst),
81
82 .tcc => try emit.mirTrap(inst),
83 }
84 }
85}
86
87pub fn deinit(emit: *Emit) void {
88 emit.* = undefined;
89}
90
91fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void {
92 const tag = emit.mir.instructions.items(.tag)[inst];
93 const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info;
94 _ = dbg_arg_info;
95
96 switch (tag) {
97 .dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index),
98 else => unreachable,
99 }
100}
101
102fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
103 const tag = emit.mir.instructions.items(.tag)[inst];
104 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
105
106 switch (tag) {
107 .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
108 else => unreachable,
109 }
110}
111
112fn mirDebugPrologueEnd(self: *Emit) !void {
113 switch (self.debug_output) {
114 .dwarf => |dbg_out| {
115 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
116 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
117 },
118 .plan9 => {},
119 .none => {},
120 }
121}
122
123fn mirDebugEpilogueBegin(self: *Emit) !void {
124 switch (self.debug_output) {
125 .dwarf => |dbg_out| {
126 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
127 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
128 },
129 .plan9 => {},
130 .none => {},
131 }
132}
133
134fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void {
135 const tag = emit.mir.instructions.items(.tag)[inst];
136 const data = emit.mir.instructions.items(.data)[inst].arithmetic_2op;
137
138 const rs1 = data.rs1;
139
140 if (data.is_imm) {
141 const imm = data.rs2_or_imm.imm;
142 switch (tag) {
143 .@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)),
144 else => unreachable,
145 }
146 } else {
147 const rs2 = data.rs2_or_imm.rs2;
148 switch (tag) {
149 .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)),
150 else => unreachable,
151 }
152 }
153}
154
155fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
156 const tag = emit.mir.instructions.items(.tag)[inst];
157 const data = emit.mir.instructions.items(.data)[inst].arithmetic_3op;
158
159 const rd = data.rd;
160 const rs1 = data.rs1;
161
162 if (data.is_imm) {
163 const imm = data.rs2_or_imm.imm;
164 switch (tag) {
165 .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)),
166 .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)),
167 .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)),
168 .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)),
169 .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)),
170 .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)),
171 .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),
172 .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),
173 .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)),
174 else => unreachable,
175 }
176 } else {
177 const rs2 = data.rs2_or_imm.rs2;
178 switch (tag) {
179 .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)),
180 .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)),
181 .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)),
182 .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)),
183 .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)),
184 .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)),
185 .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),
186 .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),
187 .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)),
188 else => unreachable,
189 }
190 }
191}
192
193fn mirNop(emit: *Emit) !void {
194 try emit.writeInstruction(Instruction.nop());
195}
196
197fn mirSethi(emit: *Emit, inst: Mir.Inst.Index) !void {
198 const tag = emit.mir.instructions.items(.tag)[inst];
199 const data = emit.mir.instructions.items(.data)[inst].sethi;
200
201 const imm = data.imm;
202 const rd = data.rd;
203
204 assert(tag == .sethi);
205 try emit.writeInstruction(Instruction.sethi(imm, rd));
206}
207
208fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void {
209 const tag = emit.mir.instructions.items(.tag)[inst];
210 const data = emit.mir.instructions.items(.data)[inst].trap;
211
212 const cond = data.cond;
213 const ccr = data.ccr;
214 const rs1 = data.rs1;
215
216 if (data.is_imm) {
217 const imm = data.rs2_or_imm.imm;
218 switch (tag) {
219 .tcc => try emit.writeInstruction(Instruction.trap(u7, cond, ccr, rs1, imm)),
220 else => unreachable,
221 }
222 } else {
223 const rs2 = data.rs2_or_imm.rs2;
224 switch (tag) {
225 .tcc => try emit.writeInstruction(Instruction.trap(Register, cond, ccr, rs1, rs2)),
226 else => unreachable,
227 }
228 }
229}
230
231// Common helper functions
232
233fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
234 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
235 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
236 switch (self.debug_output) {
237 .dwarf => |dbg_out| {
238 // TODO Look into using the DWARF special opcodes to compress this data.
239 // It lets you emit single-byte opcodes that add different numbers to
240 // both the PC and the line number at the same time.
241 try dbg_out.dbg_line.ensureUnusedCapacity(11);
242 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
243 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
244 if (delta_line != 0) {
245 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
246 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
247 }
248 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
249 self.prev_di_pc = self.code.items.len;
250 self.prev_di_line = line;
251 self.prev_di_column = column;
252 self.prev_di_pc = self.code.items.len;
253 },
254 .plan9 => |dbg_out| {
255 if (delta_pc <= 0) return; // only do this when the pc changes
256 // we have already checked the target in the linker to make sure it is compatable
257 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
258
259 // increasing the line number
260 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
261 // increasing the pc
262 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
263 if (d_pc_p9 > 0) {
264 // minus one because if its the last one, we want to leave space to change the line which is one quanta
265 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
266 if (dbg_out.pcop_change_index.*) |pci|
267 dbg_out.dbg_line.items[pci] += 1;
268 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
269 } else if (d_pc_p9 == 0) {
270 // we don't need to do anything, because adding the quant does it for us
271 } else unreachable;
272 if (dbg_out.start_line.* == null)
273 dbg_out.start_line.* = self.prev_di_line;
274 dbg_out.end_line.* = line;
275 // only do this if the pc changed
276 self.prev_di_line = line;
277 self.prev_di_column = column;
278 self.prev_di_pc = self.code.items.len;
279 },
280 .none => {},
281 }
282}
283
284fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
285 @setCold(true);
286 assert(emit.err_msg == null);
287 emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
288 return error.EmitFail;
289}
290
291fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
292 // SPARCv9 instructions are always arranged in BE regardless of the
293 // endianness mode the CPU is running in (Section 3.1 of the ISA specification).
294 // This is to ease porting in case someone wants to do a LE SPARCv9 backend.
295 const endian = Endian.Big;
296
297 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
298}
src/arch/sparcv9/Mir.zig+258
...@@ -6,6 +6,264 @@...@@ -6,6 +6,264 @@
6//! The main purpose of MIR is to postpone the assignment of offsets until Isel,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.7//! so that, for example, the smaller encodings of jump instructions can be used.
88
9const std = @import("std");
10const builtin = @import("builtin");
11const assert = std.debug.assert;
12
9const Mir = @This();13const Mir = @This();
10const bits = @import("bits.zig");14const bits = @import("bits.zig");
15const Air = @import("../../Air.zig");
16
17const Instruction = bits.Instruction;
11const Register = bits.Register;18const Register = bits.Register;
19
20instructions: std.MultiArrayList(Inst).Slice,
21
22/// The meaning of this data is determined by `Inst.Tag` value.
23extra: []const u32,
24
25pub const Inst = struct {
26 tag: Tag,
27 /// The meaning of this depends on `tag`.
28 data: Data,
29
30 pub const Tag = enum(u16) {
31 /// Pseudo-instruction: Argument
32 dbg_arg,
33 /// Pseudo-instruction: End of prologue
34 dbg_prologue_end,
35 /// Pseudo-instruction: Beginning of epilogue
36 dbg_epilogue_begin,
37 /// Pseudo-instruction: Update debug line
38 dbg_line,
39
40 // All the real instructions are ordered by their section number
41 // in The SPARC Architecture Manual, Version 9.
42
43 /// A.2 Add
44 /// Those uses the arithmetic_3op field.
45 // TODO add other operations.
46 add,
47
48 /// A.7 Branch on Integer Condition Codes with Prediction (BPcc)
49 /// It uses the branch_predict field.
50 bpcc,
51
52 /// A.8 Call and Link
53 /// It uses the branch_link field.
54 call,
55
56 /// A.24 Jump and Link
57 /// jmpl (far direct jump) uses the branch_link field,
58 /// while jmpl_i (indirect jump) uses the branch_link_indirect field.
59 /// Those two MIR instructions will be lowered into SPARCv9 jmpl instruction.
60 jmpl,
61 jmpl_i,
62
63 /// A.27 Load Integer
64 /// Those uses the arithmetic_3op field.
65 /// Note that the ldd variant of this instruction is deprecated, so do not emit
66 /// it unless specifically requested (e.g. by inline assembly).
67 // TODO add other operations.
68 ldub,
69 lduh,
70 lduw,
71 ldx,
72
73 /// A.31 Logical Operations
74 /// Those uses the arithmetic_3op field.
75 // TODO add other operations.
76 @"or",
77
78 /// A.40 No Operation
79 /// It uses the nop field.
80 nop,
81
82 /// A.45 RETURN
83 /// It uses the arithmetic_2op field.
84 @"return",
85
86 /// A.46 SAVE and RESTORE
87 /// Those uses the arithmetic_3op field.
88 save,
89 restore,
90
91 /// A.48 SETHI
92 /// It uses the sethi field.
93 sethi,
94
95 /// A.49 Shift
96 /// Those uses the shift field.
97 // TODO add other operations.
98 sllx,
99
100 /// A.56 Subtract
101 /// Those uses the arithmetic_3op field.
102 // TODO add other operations.
103 sub,
104
105 /// A.61 Trap on Integer Condition Codes (Tcc)
106 /// It uses the trap field.
107 tcc,
108 };
109
110 /// The position of an MIR instruction within the `Mir` instructions array.
111 pub const Index = u32;
112
113 /// All instructions have a 8-byte payload, which is contained within
114 /// this union. `Tag` determines which union field is active, as well as
115 /// how to interpret the data within.
116 // TODO this is a quick-n-dirty solution that needs to be cleaned up.
117 pub const Data = union {
118 /// Debug info: argument
119 ///
120 /// Used by e.g. dbg_arg
121 dbg_arg_info: struct {
122 air_inst: Air.Inst.Index,
123 arg_index: usize,
124 },
125
126 /// Debug info: line and column
127 ///
128 /// Used by e.g. dbg_line
129 dbg_line_column: struct {
130 line: u32,
131 column: u32,
132 },
133
134 /// Two operand arithmetic.
135 /// if is_imm true then it uses the imm field of rs2_or_imm,
136 /// otherwise it uses rs2 field.
137 ///
138 /// Used by e.g. return
139 arithmetic_2op: struct {
140 is_imm: bool,
141 rs1: Register,
142 rs2_or_imm: union {
143 rs2: Register,
144 imm: i13,
145 },
146 },
147
148 /// Three operand arithmetic.
149 /// if is_imm true then it uses the imm field of rs2_or_imm,
150 /// otherwise it uses rs2 field.
151 ///
152 /// Used by e.g. add, sub
153 arithmetic_3op: struct {
154 is_imm: bool,
155 rd: Register,
156 rs1: Register,
157 rs2_or_imm: union {
158 rs2: Register,
159 imm: i13,
160 },
161 },
162
163 /// Branch and link (always unconditional).
164 /// Used by e.g. call
165 branch_link: struct {
166 inst: Index,
167 link: Register = .o7,
168 },
169
170 /// Indirect branch and link (always unconditional).
171 /// Used by e.g. jmpl_i
172 branch_link_indirect: struct {
173 reg: Register,
174 link: Register = .o7,
175 },
176
177 /// Branch with prediction.
178 /// Used by e.g. bpcc
179 branch_predict: struct {
180 annul: bool = false,
181 pt: bool = true,
182 ccr: Instruction.CCR,
183 cond: Instruction.Condition,
184 inst: Index,
185 },
186
187 /// No additional data
188 ///
189 /// Used by e.g. flushw
190 nop: void,
191
192 /// SETHI operands.
193 ///
194 /// Used by sethi
195 sethi: struct {
196 rd: Register,
197 imm: u22,
198 },
199
200 /// Shift operands.
201 /// if is_imm true then it uses the imm field of rs2_or_imm,
202 /// otherwise it uses rs2 field.
203 ///
204 /// Used by e.g. add, sub
205 shift: struct {
206 is_imm: bool,
207 width: Instruction.ShiftWidth,
208 rd: Register,
209 rs1: Register,
210 rs2_or_imm: union {
211 rs2: Register,
212 imm: u6,
213 },
214 },
215
216 /// Trap.
217 /// if is_imm true then it uses the imm field of rs2_or_imm,
218 /// otherwise it uses rs2 field.
219 ///
220 /// Used by e.g. tcc
221 trap: struct {
222 is_imm: bool = true,
223 cond: Instruction.Condition,
224 ccr: Instruction.CCR = .icc,
225 rs1: Register = .g0,
226 rs2_or_imm: union {
227 rs2: Register,
228 imm: u7,
229 },
230 },
231 };
232
233 // Make sure we don't accidentally make instructions bigger than expected.
234 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
235 comptime {
236 if (builtin.mode != .Debug) {
237 // TODO clean up the definition of Data before enabling this.
238 // I'll do that after the PoC backend can produce usable binaries.
239
240 // assert(@sizeOf(Data) == 8);
241 }
242 }
243};
244
245pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
246 mir.instructions.deinit(gpa);
247 gpa.free(mir.extra);
248 mir.* = undefined;
249}
250
251/// Returns the requested data, as well as the new index which is at the start of the
252/// trailers for the object.
253pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
254 const fields = std.meta.fields(T);
255 var i: usize = index;
256 var result: T = undefined;
257 inline for (fields) |field| {
258 @field(result, field.name) = switch (field.field_type) {
259 u32 => mir.extra[i],
260 i32 => @bitCast(i32, mir.extra[i]),
261 else => @compileError("bad field type"),
262 };
263 i += 1;
264 }
265 return .{
266 .data = result,
267 .end = i,
268 };
269}
src/arch/sparcv9/abi.zig+18-5
...@@ -1,12 +1,25 @@...@@ -1,12 +1,25 @@
1const bits = @import("bits.zig");1const bits = @import("bits.zig");
2const Register = bits.Register;2const Register = bits.Register;
33
4// Register windowing mechanism will take care of preserving registers4// There are no callee-preserved registers since the windowing
5// so no need to do it manually5// mechanism already takes care of them.
6pub const callee_preserved_regs = [_]Register{};6// We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling
7// something, though, as those are shared with the callee and might be
8// thrashed by it.
9pub const caller_preserved_regs = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5, .g1, .g4, .g5 };
10
11// Try to allocate i, l, o, then g sets of registers, in order of priority.
12pub const allocatable_regs = [_]Register{
13 // zig fmt: off
14 .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5",
15 .l0, .l1, .l2, .l3, .l4, .l5, .l6, .l7,
16 .o0, .o1, .o2, .o3, .o4, .o5,
17 .g1, .g4, .g5,
18 // zig fmt: on
19};
720
8pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 };21pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 };
9pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" };22pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" };
1023
11pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 };24pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 };
12pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" };25pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" };
src/arch/sparcv9/bits.zig+168-55
...@@ -164,27 +164,33 @@ pub const Instruction = union(enum) {...@@ -164,27 +164,33 @@ pub const Instruction = union(enum) {
164 // name them with letters since there's no official naming scheme.164 // name them with letters since there's no official naming scheme.
165 // TODO: need to rename the minor formats to a more descriptive name.165 // TODO: need to rename the minor formats to a more descriptive name.
166166
167 // I am using regular structs instead of packed ones to avoid
168 // endianness-dependent behavior when constructing the actual
169 // assembly instructions.
170 // See also: https://github.com/ziglang/zig/issues/10113
171 // TODO: change it back to packed structs once the issue is resolved.
172
167 // Format 1 (op = 1): CALL173 // Format 1 (op = 1): CALL
168 format_1: packed struct {174 format_1: struct {
169 op: u2 = 0b01,175 op: u2 = 0b01,
170 disp30: u30,176 disp30: u30,
171 },177 },
172178
173 // Format 2 (op = 0): SETHI & Branches (Bicc, BPcc, BPr, FBfcc, FBPfcc)179 // Format 2 (op = 0): SETHI & Branches (Bicc, BPcc, BPr, FBfcc, FBPfcc)
174 format_2a: packed struct {180 format_2a: struct {
175 op: u2 = 0b00,181 op: u2 = 0b00,
176 rd: u5,182 rd: u5,
177 op2: u3,183 op2: u3,
178 imm22: u22,184 imm22: u22,
179 },185 },
180 format_2b: packed struct {186 format_2b: struct {
181 op: u2 = 0b00,187 op: u2 = 0b00,
182 a: u1,188 a: u1,
183 cond: u4,189 cond: u4,
184 op2: u3,190 op2: u3,
185 disp22: u22,191 disp22: u22,
186 },192 },
187 format_2c: packed struct {193 format_2c: struct {
188 op: u2 = 0b00,194 op: u2 = 0b00,
189 a: u1,195 a: u1,
190 cond: u4,196 cond: u4,
...@@ -194,7 +200,7 @@ pub const Instruction = union(enum) {...@@ -194,7 +200,7 @@ pub const Instruction = union(enum) {
194 p: u1,200 p: u1,
195 disp19: u19,201 disp19: u19,
196 },202 },
197 format_2d: packed struct {203 format_2d: struct {
198 op: u2 = 0b00,204 op: u2 = 0b00,
199 a: u1,205 a: u1,
200 fixed: u1 = 0b0,206 fixed: u1 = 0b0,
...@@ -207,7 +213,7 @@ pub const Instruction = union(enum) {...@@ -207,7 +213,7 @@ pub const Instruction = union(enum) {
207 },213 },
208214
209 // Format 3 (op = 2 or 3): Arithmetic, Logical, MOVr, MEMBAR, Load, and Store215 // Format 3 (op = 2 or 3): Arithmetic, Logical, MOVr, MEMBAR, Load, and Store
210 format_3a: packed struct {216 format_3a: struct {
211 op: u2,217 op: u2,
212 rd: u5,218 rd: u5,
213 op3: u6,219 op3: u6,
...@@ -224,7 +230,7 @@ pub const Instruction = union(enum) {...@@ -224,7 +230,7 @@ pub const Instruction = union(enum) {
224 i: u1 = 0b1,230 i: u1 = 0b1,
225 simm13: u13,231 simm13: u13,
226 },232 },
227 format_3c: packed struct {233 format_3c: struct {
228 op: u2,234 op: u2,
229 reserved1: u5 = 0b00000,235 reserved1: u5 = 0b00000,
230 op3: u6,236 op3: u6,
...@@ -241,7 +247,7 @@ pub const Instruction = union(enum) {...@@ -241,7 +247,7 @@ pub const Instruction = union(enum) {
241 i: u1 = 0b1,247 i: u1 = 0b1,
242 simm13: u13,248 simm13: u13,
243 },249 },
244 format_3e: packed struct {250 format_3e: struct {
245 op: u2,251 op: u2,
246 rd: u5,252 rd: u5,
247 op3: u6,253 op3: u6,
...@@ -260,7 +266,7 @@ pub const Instruction = union(enum) {...@@ -260,7 +266,7 @@ pub const Instruction = union(enum) {
260 rcond: u3,266 rcond: u3,
261 simm10: u10,267 simm10: u10,
262 },268 },
263 format_3g: packed struct {269 format_3g: struct {
264 op: u2,270 op: u2,
265 rd: u5,271 rd: u5,
266 op3: u6,272 op3: u6,
...@@ -269,7 +275,7 @@ pub const Instruction = union(enum) {...@@ -269,7 +275,7 @@ pub const Instruction = union(enum) {
269 reserved: u8 = 0b00000000,275 reserved: u8 = 0b00000000,
270 rs2: u5,276 rs2: u5,
271 },277 },
272 format_3h: packed struct {278 format_3h: struct {
273 op: u2 = 0b10,279 op: u2 = 0b10,
274 fixed1: u5 = 0b00000,280 fixed1: u5 = 0b00000,
275 op3: u6 = 0b101000,281 op3: u6 = 0b101000,
...@@ -279,7 +285,7 @@ pub const Instruction = union(enum) {...@@ -279,7 +285,7 @@ pub const Instruction = union(enum) {
279 cmask: u3,285 cmask: u3,
280 mmask: u4,286 mmask: u4,
281 },287 },
282 format_3i: packed struct {288 format_3i: struct {
283 op: u2,289 op: u2,
284 rd: u5,290 rd: u5,
285 op3: u6,291 op3: u6,
...@@ -288,13 +294,13 @@ pub const Instruction = union(enum) {...@@ -288,13 +294,13 @@ pub const Instruction = union(enum) {
288 imm_asi: u8,294 imm_asi: u8,
289 rs2: u5,295 rs2: u5,
290 },296 },
291 format_3j: packed struct {297 format_3j: struct {
292 op: u2,298 op: u2,
293 impl_dep1: u5,299 impl_dep1: u5,
294 op3: u6,300 op3: u6,
295 impl_dep2: u19,301 impl_dep2: u19,
296 },302 },
297 format_3k: packed struct {303 format_3k: struct {
298 op: u2,304 op: u2,
299 rd: u5,305 rd: u5,
300 op3: u6,306 op3: u6,
...@@ -304,7 +310,7 @@ pub const Instruction = union(enum) {...@@ -304,7 +310,7 @@ pub const Instruction = union(enum) {
304 reserved: u7 = 0b0000000,310 reserved: u7 = 0b0000000,
305 rs2: u5,311 rs2: u5,
306 },312 },
307 format_3l: packed struct {313 format_3l: struct {
308 op: u2,314 op: u2,
309 rd: u5,315 rd: u5,
310 op3: u6,316 op3: u6,
...@@ -314,7 +320,7 @@ pub const Instruction = union(enum) {...@@ -314,7 +320,7 @@ pub const Instruction = union(enum) {
314 reserved: u7 = 0b0000000,320 reserved: u7 = 0b0000000,
315 shcnt32: u5,321 shcnt32: u5,
316 },322 },
317 format_3m: packed struct {323 format_3m: struct {
318 op: u2,324 op: u2,
319 rd: u5,325 rd: u5,
320 op3: u6,326 op3: u6,
...@@ -324,7 +330,7 @@ pub const Instruction = union(enum) {...@@ -324,7 +330,7 @@ pub const Instruction = union(enum) {
324 reserved: u6 = 0b000000,330 reserved: u6 = 0b000000,
325 shcnt64: u6,331 shcnt64: u6,
326 },332 },
327 format_3n: packed struct {333 format_3n: struct {
328 op: u2,334 op: u2,
329 rd: u5,335 rd: u5,
330 op3: u6,336 op3: u6,
...@@ -332,7 +338,7 @@ pub const Instruction = union(enum) {...@@ -332,7 +338,7 @@ pub const Instruction = union(enum) {
332 opf: u9,338 opf: u9,
333 rs2: u5,339 rs2: u5,
334 },340 },
335 format_3o: packed struct {341 format_3o: struct {
336 op: u2,342 op: u2,
337 fixed: u3 = 0b000,343 fixed: u3 = 0b000,
338 cc1: u1,344 cc1: u1,
...@@ -342,7 +348,7 @@ pub const Instruction = union(enum) {...@@ -342,7 +348,7 @@ pub const Instruction = union(enum) {
342 opf: u9,348 opf: u9,
343 rs2: u5,349 rs2: u5,
344 },350 },
345 format_3p: packed struct {351 format_3p: struct {
346 op: u2,352 op: u2,
347 rd: u5,353 rd: u5,
348 op3: u6,354 op3: u6,
...@@ -350,20 +356,20 @@ pub const Instruction = union(enum) {...@@ -350,20 +356,20 @@ pub const Instruction = union(enum) {
350 opf: u9,356 opf: u9,
351 rs2: u5,357 rs2: u5,
352 },358 },
353 format_3q: packed struct {359 format_3q: struct {
354 op: u2,360 op: u2,
355 rd: u5,361 rd: u5,
356 op3: u6,362 op3: u6,
357 rs1: u5,363 rs1: u5,
358 reserved: u14 = 0b00000000000000,364 reserved: u14 = 0b00000000000000,
359 },365 },
360 format_3r: packed struct {366 format_3r: struct {
361 op: u2,367 op: u2,
362 fcn: u5,368 fcn: u5,
363 op3: u6,369 op3: u6,
364 reserved: u19 = 0b0000000000000000000,370 reserved: u19 = 0b0000000000000000000,
365 },371 },
366 format_3s: packed struct {372 format_3s: struct {
367 op: u2,373 op: u2,
368 rd: u5,374 rd: u5,
369 op3: u6,375 op3: u6,
...@@ -371,7 +377,7 @@ pub const Instruction = union(enum) {...@@ -371,7 +377,7 @@ pub const Instruction = union(enum) {
371 },377 },
372378
373 //Format 4 (op = 2): MOVcc, FMOVr, FMOVcc, and Tcc379 //Format 4 (op = 2): MOVcc, FMOVr, FMOVcc, and Tcc
374 format_4a: packed struct {380 format_4a: struct {
375 op: u2 = 0b10,381 op: u2 = 0b10,
376 rd: u5,382 rd: u5,
377 op3: u6,383 op3: u6,
...@@ -392,7 +398,7 @@ pub const Instruction = union(enum) {...@@ -392,7 +398,7 @@ pub const Instruction = union(enum) {
392 cc0: u1,398 cc0: u1,
393 simm11: u11,399 simm11: u11,
394 },400 },
395 format_4c: packed struct {401 format_4c: struct {
396 op: u2 = 0b10,402 op: u2 = 0b10,
397 rd: u5,403 rd: u5,
398 op3: u6,404 op3: u6,
...@@ -415,7 +421,7 @@ pub const Instruction = union(enum) {...@@ -415,7 +421,7 @@ pub const Instruction = union(enum) {
415 cc0: u1,421 cc0: u1,
416 simm11: u11,422 simm11: u11,
417 },423 },
418 format_4e: packed struct {424 format_4e: struct {
419 op: u2 = 0b10,425 op: u2 = 0b10,
420 rd: u5,426 rd: u5,
421 op3: u6,427 op3: u6,
...@@ -426,7 +432,7 @@ pub const Instruction = union(enum) {...@@ -426,7 +432,7 @@ pub const Instruction = union(enum) {
426 reserved: u4 = 0b0000,432 reserved: u4 = 0b0000,
427 sw_trap: u7,433 sw_trap: u7,
428 },434 },
429 format_4f: packed struct {435 format_4f: struct {
430 op: u2 = 0b10,436 op: u2 = 0b10,
431 rd: u5,437 rd: u5,
432 op3: u6,438 op3: u6,
...@@ -436,7 +442,7 @@ pub const Instruction = union(enum) {...@@ -436,7 +442,7 @@ pub const Instruction = union(enum) {
436 opf_low: u5,442 opf_low: u5,
437 rs2: u5,443 rs2: u5,
438 },444 },
439 format_4g: packed struct {445 format_4g: struct {
440 op: u2 = 0b10,446 op: u2 = 0b10,
441 rd: u5,447 rd: u5,
442 op3: u6,448 op3: u6,
...@@ -512,40 +518,43 @@ pub const Instruction = union(enum) {...@@ -512,40 +518,43 @@ pub const Instruction = union(enum) {
512 pub fn toU32(self: Instruction) u32 {518 pub fn toU32(self: Instruction) u32 {
513 // TODO: Remove this once packed structs work.519 // TODO: Remove this once packed structs work.
514 return switch (self) {520 return switch (self) {
515 .format_1 => |v| @bitCast(u32, v),521 .format_1 => |v| (@as(u32, v.op) << 30) | @as(u32, v.disp30),
516 .format_2a => |v| @bitCast(u32, v),522 .format_2a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.imm22),
517 .format_2b => |v| @bitCast(u32, v),523 .format_2b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.disp22),
518 .format_2c => |v| @bitCast(u32, v),524 .format_2c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.cc1) << 21) | (@as(u32, v.cc0) << 20) | (@as(u32, v.p) << 19) | @as(u32, v.disp19),
519 .format_2d => |v| @bitCast(u32, v),525 .format_2d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.fixed) << 28) | (@as(u32, v.rcond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.d16hi) << 20) | (@as(u32, v.p) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.d16lo),
520 .format_3a => |v| @bitCast(u32, v),526 .format_3a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
521 .format_3b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13),527 .format_3b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13),
522 .format_3c => |v| @bitCast(u32, v),528 .format_3c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved2) << 5) | @as(u32, v.rs2),
523 .format_3d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13),529 .format_3d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13),
524 .format_3e => |v| @bitCast(u32, v),530 .format_3e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
525 .format_3f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | @as(u32, v.simm10),531 .format_3f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | @as(u32, v.simm10),
526 .format_3g => |v| @bitCast(u32, v),532 .format_3g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
527 .format_3h => |v| @bitCast(u32, v),533 .format_3h => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed2) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 7) | (@as(u32, v.cmask) << 4) | @as(u32, v.mmask),
528 .format_3i => |v| @bitCast(u32, v),534 .format_3i => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.imm_asi) << 5) | @as(u32, v.rs2),
529 .format_3j => |v| @bitCast(u32, v),535 .format_3j => |v| (@as(u32, v.op) << 30) | (@as(u32, v.impl_dep1) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.impl_dep2),
530 .format_3k => |v| @bitCast(u32, v),536 .format_3k => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
531 .format_3l => |v| @bitCast(u32, v),537 .format_3l => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.shcnt32),
532 .format_3m => |v| @bitCast(u32, v),538 .format_3m => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 6) | @as(u32, v.shcnt64),
533 .format_3n => |v| @bitCast(u32, v),539 .format_3n => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.reserved) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
534 .format_3o => |v| @bitCast(u32, v),540 .format_3o => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed) << 27) | (@as(u32, v.cc1) << 26) | (@as(u32, v.cc0) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
535 .format_3p => |v| @bitCast(u32, v),541 .format_3p => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2),
536 .format_3q => |v| @bitCast(u32, v),542 .format_3q => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.reserved),
537 .format_3r => |v| @bitCast(u32, v),543 .format_3r => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fcn) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved),
538 .format_3s => |v| @bitCast(u32, v),544 .format_3s => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved),
539 .format_4a => |v| @bitCast(u32, v),545 .format_4a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
540 .format_4b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11),546 .format_4b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11),
541 .format_4c => |v| @bitCast(u32, v),547 .format_4c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2),
542 .format_4d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11),548 .format_4d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11),
543 .format_4e => |v| @bitCast(u32, v),549 .format_4e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 7) | @as(u32, v.sw_trap),
544 .format_4f => |v| @bitCast(u32, v),550 .format_4f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.fixed) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2),
545 .format_4g => |v| @bitCast(u32, v),551 .format_4g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.opf_cc) << 11) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2),
546 };552 };
547 }553 }
548554
555 // SPARCv9 Instruction formats.
556 // See section 6.2 of the SPARCv9 ISA manual.
557
549 fn format1(disp: i32) Instruction {558 fn format1(disp: i32) Instruction {
550 const udisp = @bitCast(u32, disp);559 const udisp = @bitCast(u32, disp);
551560
...@@ -561,7 +570,7 @@ pub const Instruction = union(enum) {...@@ -561,7 +570,7 @@ pub const Instruction = union(enum) {
561 };570 };
562 }571 }
563572
564 fn format2a(op2: u3, rd: Register, imm: u22) Instruction {573 fn format2a(op2: u3, imm: u22, rd: Register) Instruction {
565 return Instruction{574 return Instruction{
566 .format_2a = .{575 .format_2a = .{
567 .rd = rd.enc(),576 .rd = rd.enc(),
...@@ -956,6 +965,106 @@ pub const Instruction = union(enum) {...@@ -956,6 +965,106 @@ pub const Instruction = union(enum) {
956 },965 },
957 };966 };
958 }967 }
968
969 // SPARCv9 Instruction definition.
970 // See appendix A of the SPARCv9 ISA manual.
971
972 pub fn add(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
973 return switch (s2) {
974 Register => format3a(0b10, 0b00_0000, rs1, rs2, rd),
975 i13 => format3b(0b10, 0b00_0000, rs1, rs2, rd),
976 else => unreachable,
977 };
978 }
979
980 pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
981 return switch (s2) {
982 Register => format3a(0b10, 0b00_0010, rs1, rs2, rd),
983 i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd),
984 else => unreachable,
985 };
986 }
987
988 pub fn ldub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
989 return switch (s2) {
990 Register => format3a(0b11, 0b00_0001, rs1, rs2, rd),
991 i13 => format3b(0b11, 0b00_0001, rs1, rs2, rd),
992 else => unreachable,
993 };
994 }
995
996 pub fn lduh(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
997 return switch (s2) {
998 Register => format3a(0b11, 0b00_0010, rs1, rs2, rd),
999 i13 => format3b(0b11, 0b00_0010, rs1, rs2, rd),
1000 else => unreachable,
1001 };
1002 }
1003
1004 pub fn lduw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1005 return switch (s2) {
1006 Register => format3a(0b11, 0b00_0000, rs1, rs2, rd),
1007 i13 => format3b(0b11, 0b00_0000, rs1, rs2, rd),
1008 else => unreachable,
1009 };
1010 }
1011
1012 pub fn ldx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1013 return switch (s2) {
1014 Register => format3a(0b11, 0b00_1011, rs1, rs2, rd),
1015 i13 => format3b(0b11, 0b00_1011, rs1, rs2, rd),
1016 else => unreachable,
1017 };
1018 }
1019
1020 pub fn nop() Instruction {
1021 return sethi(0, .g0);
1022 }
1023
1024 pub fn @"return"(comptime s2: type, rs1: Register, rs2: s2) Instruction {
1025 return switch (s2) {
1026 Register => format3c(0b10, 0b11_1001, rs1, rs2),
1027 i13 => format3d(0b10, 0b11_1001, rs1, rs2),
1028 else => unreachable,
1029 };
1030 }
1031
1032 pub fn save(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1033 return switch (s2) {
1034 Register => format3a(0b10, 0b11_1100, rs1, rs2, rd),
1035 i13 => format3b(0b10, 0b11_1100, rs1, rs2, rd),
1036 else => unreachable,
1037 };
1038 }
1039
1040 pub fn restore(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1041 return switch (s2) {
1042 Register => format3a(0b10, 0b11_1101, rs1, rs2, rd),
1043 i13 => format3b(0b10, 0b11_1101, rs1, rs2, rd),
1044 else => unreachable,
1045 };
1046 }
1047
1048 pub fn sethi(imm: u22, rd: Register) Instruction {
1049 return format2a(0b100, imm, rd);
1050 }
1051
1052 pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1053 return switch (s2) {
1054 Register => format3a(0b10, 0b00_0100, rs1, rs2, rd),
1055 i13 => format3b(0b10, 0b00_0100, rs1, rs2, rd),
1056 else => unreachable,
1057 };
1058 }
1059
1060 pub fn trap(comptime s2: type, cond: Condition, ccr: CCR, rs1: Register, rs2: s2) Instruction {
1061 // Tcc instructions abuse the rd field to store the conditionals.
1062 return switch (s2) {
1063 Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)),
1064 u7 => format4e(0b11_1010, ccr, rs1, @intToEnum(Register, cond), rs2),
1065 else => unreachable,
1066 };
1067 }
959};1068};
9601069
961test "Serialize formats" {1070test "Serialize formats" {
...@@ -973,7 +1082,7 @@ test "Serialize formats" {...@@ -973,7 +1082,7 @@ test "Serialize formats" {
973 .expected = 0b01_000000000000000000000000000001,1082 .expected = 0b01_000000000000000000000000000001,
974 },1083 },
975 .{1084 .{
976 .inst = Instruction.format2a(4, .g0, 0),1085 .inst = Instruction.format2a(4, 0, .g0),
977 .expected = 0b00_00000_100_0000000000000000000000,1086 .expected = 0b00_00000_100_0000000000000000000000,
978 },1087 },
979 .{1088 .{
...@@ -1096,6 +1205,10 @@ test "Serialize formats" {...@@ -1096,6 +1205,10 @@ test "Serialize formats" {
10961205
1097 for (testcases) |case| {1206 for (testcases) |case| {
1098 const actual = case.inst.toU32();1207 const actual = case.inst.toU32();
1099 try testing.expectEqual(case.expected, actual);1208 testing.expectEqual(case.expected, actual) catch |err| {
1209 std.debug.print("error: {x}\n", .{err});
1210 std.debug.print("case: {x}\n", .{case});
1211 return err;
1212 };
1100 }1213 }
1101}1214}
src/link/Elf.zig+8-3
...@@ -65,7 +65,7 @@ phdr_load_rw_index: ?u16 = null,...@@ -65,7 +65,7 @@ phdr_load_rw_index: ?u16 = null,
65phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},65phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
6666
67entry_addr: ?u64 = null,67entry_addr: ?u64 = null,
68page_size: u16,68page_size: u32,
6969
70shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},70shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
71shstrtab_index: ?u16 = null,71shstrtab_index: ?u16 = null,
...@@ -304,7 +304,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -304,7 +304,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
304 };304 };
305 const self = try gpa.create(Elf);305 const self = try gpa.create(Elf);
306 errdefer gpa.destroy(self);306 errdefer gpa.destroy(self);
307 const page_size: u16 = 0x1000; // TODO ppc64le requires 64KB307
308 const page_size: u32 = switch (options.target.cpu.arch) {
309 .powerpc64le => 0x10000,
310 .sparcv9 => 0x2000,
311 else => 0x1000,
312 };
308313
309 var dwarf: ?Dwarf = if (!options.strip and options.module != null)314 var dwarf: ?Dwarf = if (!options.strip and options.module != null)
310 Dwarf.init(gpa, .elf, options.target)315 Dwarf.init(gpa, .elf, options.target)
...@@ -472,7 +477,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {...@@ -472,7 +477,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {
472 return min_pos - start;477 return min_pos - start;
473}478}
474479
475pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {480pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {
476 var start: u64 = 0;481 var start: u64 = 0;
477 while (self.detectAllocCollision(start, object_size)) |item_end| {482 while (self.detectAllocCollision(start, object_size)) |item_end| {
478 start = mem.alignForwardGeneric(u64, item_end, min_alignment);483 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
src/target.zig+1
...@@ -669,6 +669,7 @@ pub fn defaultFunctionAlignment(target: std.Target) u32 {...@@ -669,6 +669,7 @@ pub fn defaultFunctionAlignment(target: std.Target) u32 {
669 return switch (target.cpu.arch) {669 return switch (target.cpu.arch) {
670 .arm, .armeb => 4,670 .arm, .armeb => 4,
671 .aarch64, .aarch64_32, .aarch64_be => 4,671 .aarch64, .aarch64_32, .aarch64_be => 4,
672 .sparc, .sparcel, .sparcv9 => 4,
672 .riscv64 => 2,673 .riscv64 => 2,
673 else => 1,674 else => 1,
674 };675 };
test/cases.zig+1
...@@ -16,6 +16,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -16,6 +16,7 @@ pub fn addCases(ctx: *TestContext) !void {
16 try @import("stage2/riscv64.zig").addCases(ctx);16 try @import("stage2/riscv64.zig").addCases(ctx);
17 try @import("stage2/plan9.zig").addCases(ctx);17 try @import("stage2/plan9.zig").addCases(ctx);
18 try @import("stage2/x86_64.zig").addCases(ctx);18 try @import("stage2/x86_64.zig").addCases(ctx);
19 try @import("stage2/sparcv9.zig").addCases(ctx);
19 // https://github.com/ziglang/zig/issues/1096820 // https://github.com/ziglang/zig/issues/10968
20 //try @import("stage2/nvptx.zig").addCases(ctx);21 //try @import("stage2/nvptx.zig").addCases(ctx);
21}22}
test/stage2/aarch64.zig+1-1
...@@ -159,7 +159,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -159,7 +159,7 @@ pub fn addCases(ctx: *TestContext) !void {
159 {159 {
160 var case = ctx.exe("hello world with updates", macos_aarch64);160 var case = ctx.exe("hello world with updates", macos_aarch64);
161 case.addError("", &[_][]const u8{161 case.addError("", &[_][]const u8{
162 ":108:9: error: struct 'tmp.tmp' has no member named 'main'",162 ":109:9: error: struct 'tmp.tmp' has no member named 'main'",
163 });163 });
164164
165 // Incorrect return type165 // Incorrect return type
test/stage2/sparcv9.zig created+39
...@@ -0,0 +1,39 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3
4const linux_sparcv9 = std.zig.CrossTarget{
5 .cpu_arch = .sparcv9,
6 .os_tag = .linux,
7};
8
9pub fn addCases(ctx: *TestContext) !void {
10 {
11 var case = ctx.exe("sparcv9 hello world", linux_sparcv9);
12 // Regular old hello world
13 case.addCompareOutput(
14 \\const msg = "Hello, World!\n";
15 \\
16 \\pub export fn _start() noreturn {
17 \\ asm volatile ("ta 0x6d"
18 \\ :
19 \\ : [number] "{g1}" (4),
20 \\ [arg1] "{o0}" (1),
21 \\ [arg2] "{o1}" (@ptrToInt(msg)),
22 \\ [arg3] "{o2}" (msg.len)
23 \\ : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory"
24 \\ );
25 \\
26 \\ asm volatile ("ta 0x6d"
27 \\ :
28 \\ : [number] "{g1}" (1),
29 \\ [arg1] "{o0}" (0)
30 \\ : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory"
31 \\ );
32 \\
33 \\ unreachable;
34 \\}
35 ,
36 "Hello, World!\n",
37 );
38 }
39}
test/stage2/x86_64.zig+2-2
...@@ -1925,7 +1925,7 @@ fn addLinuxTestCases(ctx: *TestContext) !void {...@@ -1925,7 +1925,7 @@ fn addLinuxTestCases(ctx: *TestContext) !void {
1925 var case = ctx.exe("hello world with updates", linux_x64);1925 var case = ctx.exe("hello world with updates", linux_x64);
19261926
1927 case.addError("", &[_][]const u8{1927 case.addError("", &[_][]const u8{
1928 ":108:9: error: struct 'tmp.tmp' has no member named 'main'",1928 ":109:9: error: struct 'tmp.tmp' has no member named 'main'",
1929 });1929 });
19301930
1931 // Incorrect return type1931 // Incorrect return type
...@@ -2176,7 +2176,7 @@ fn addMacOsTestCases(ctx: *TestContext) !void {...@@ -2176,7 +2176,7 @@ fn addMacOsTestCases(ctx: *TestContext) !void {
2176 {2176 {
2177 var case = ctx.exe("darwin hello world with updates", macos_x64);2177 var case = ctx.exe("darwin hello world with updates", macos_x64);
2178 case.addError("", &[_][]const u8{2178 case.addError("", &[_][]const u8{
2179 ":108:9: error: struct 'tmp.tmp' has no member named 'main'",2179 ":109:9: error: struct 'tmp.tmp' has no member named 'main'",
2180 });2180 });
21812181
2182 // Incorrect return type2182 // Incorrect return type