1pub const abi = @import("aarch64/abi.zig");
2pub const Assemble = @import("aarch64/Assemble.zig");
3pub const Disassemble = @import("aarch64/Disassemble.zig");
4pub const encoding = @import("aarch64/encoding.zig");
5pub const Mir = @import("aarch64/Mir.zig");
6pub const Select = @import("aarch64/Select.zig");
7
8pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
9 return comptime &.initMany(&.{
10 .expand_bit_cast_safe,
11 .expand_array_splat,
12 .expand_array_to_vector,
13 });
14}
15
16pub fn generate(
17 _: *link.File,
18 pt: Zcu.PerThread,
19 func_index: InternPool.Index,
20 air: *const Air,
21 liveness: *const ?Air.Liveness,
22) !Mir {
23 const zcu = pt.zcu;
24 const gpa = zcu.gpa;
25 const ip = &zcu.intern_pool;
26 const func = zcu.funcInfo(func_index);
27 const func_zir = func.zir_body_inst.resolveFull(ip).?;
28 const file = zcu.fileByIndex(func_zir.file);
29 const named_params_len = file.zir.?.getParamBody(func_zir.inst).len;
30 const func_type = ip.indexToKey(func.ty).func_type;
31 assert(liveness.* == null);
32
33 const mod = zcu.navFileScope(func.owner_nav).mod.?;
34 var isel: Select = .{
35 .pt = pt,
36 .target = &mod.resolved_target.result,
37 .air = air.*,
38 .nav_index = zcu.funcInfo(func_index).owner_nav,
39
40 .def_order = .empty,
41 .blocks = .empty,
42 .loops = .empty,
43 .active_loops = .empty,
44 .loop_live = .{
45 .set = .empty,
46 .list = .empty,
47 },
48 .dom_start = 0,
49 .dom_len = 0,
50 .dom = .empty,
51
52 .saved_registers = .empty,
53 .instructions = .empty,
54 .literals = .empty,
55 .nav_relocs = .empty,
56 .uav_relocs = .empty,
57 .lazy_relocs = .empty,
58 .global_relocs = .empty,
59 .literal_relocs = .empty,
60
61 .returns = false,
62 .va_list = undefined,
63 .stack_size = 0,
64 .stack_align = .@"16",
65
66 .live_registers = comptime .initFill(.free),
67 .live_values = .empty,
68 .values = .empty,
69 };
70 defer isel.deinit();
71 const is_sysv = !isel.target.os.tag.isDarwin() and isel.target.os.tag != .windows;
72 const is_sysv_var_args = is_sysv and func_type.is_var_args;
73
74 const air_main_body = air.getMainBody();
75 var param_it: Select.CallAbiIterator = .init;
76 const air_args = for (air_main_body, 0..) |air_inst_index, body_index| {
77 if (air.instructions.items(.tag)[@backingInt(air_inst_index)] != .arg) break air_main_body[0..body_index];
78 const arg = air.instructions.items(.data)[@backingInt(air_inst_index)].arg;
79 const param_ty = arg.ty;
80 const param_vi = param_vi: {
81 if (arg.zir_param_index >= named_params_len) {
82 assert(func_type.is_var_args);
83 if (!is_sysv) break :param_vi try param_it.nonSysvVarArg(&isel, param_ty);
84 }
85 break :param_vi try param_it.param(&isel, param_ty);
86 };
87 tracking_log.debug("${d} <- %{d}", .{ @backingInt(param_vi.?), @backingInt(air_inst_index) });
88 try isel.live_values.putNoClobber(gpa, air_inst_index, param_vi.?);
89 } else unreachable;
90
91 const saved_gra_start = if (mod.strip) param_it.ngrn else Select.CallAbiIterator.ngrn_start;
92 const saved_gra_end = if (is_sysv_var_args) Select.CallAbiIterator.ngrn_end else param_it.ngrn;
93 const saved_gra_len = @backingInt(saved_gra_end) - @backingInt(saved_gra_start);
94
95 const saved_vra_start = if (mod.strip) param_it.nsrn else Select.CallAbiIterator.nsrn_start;
96 const saved_vra_end = if (is_sysv_var_args) Select.CallAbiIterator.nsrn_end else param_it.nsrn;
97 const saved_vra_len = @backingInt(saved_vra_end) - @backingInt(saved_vra_start);
98
99 const frame_record = 2;
100 const named_stack_args: Select.Value.Indirect = .{
101 .base = .fp,
102 .offset = 8 * std.mem.alignForward(u7, frame_record + saved_gra_len, 2),
103 };
104 const stack_var_args = named_stack_args.withOffset(param_it.nsaa);
105 const gr_top = named_stack_args;
106 const vr_top: Select.Value.Indirect = .{ .base = .fp, .offset = 0 };
107 isel.va_list = if (is_sysv) .{ .sysv = .{
108 .__stack = stack_var_args,
109 .__gr_top = gr_top,
110 .__vr_top = vr_top,
111 .__gr_offs = @as(i32, @backingInt(Select.CallAbiIterator.ngrn_end) - @backingInt(param_it.ngrn)) * -8,
112 .__vr_offs = @as(i32, @backingInt(Select.CallAbiIterator.nsrn_end) - @backingInt(param_it.nsrn)) * -16,
113 } } else .{ .other = stack_var_args };
114
115 // translate arg locations from caller-based to callee-based
116 for (air_args) |air_inst_index| {
117 assert(air.instructions.items(.tag)[@backingInt(air_inst_index)] == .arg);
118 const arg_vi = isel.live_values.get(air_inst_index).?;
119 const passed_vi = switch (arg_vi.parent(&isel)) {
120 .unallocated, .stack_slot => arg_vi,
121 .value, .constant => unreachable,
122 .address => |address_vi| address_vi,
123 };
124 switch (passed_vi.parent(&isel)) {
125 .unallocated => if (!mod.strip) {
126 var part_it = passed_vi.parts(&isel);
127 const first_passed_part_vi = part_it.next().?;
128 const hint_ra = first_passed_part_vi.hint(&isel).?;
129 passed_vi.setParent(&isel, .{ .stack_slot = if (hint_ra.isVector())
130 vr_top.withOffset(@as(i8, -16) * (@backingInt(saved_vra_end) - @backingInt(hint_ra)))
131 else
132 gr_top.withOffset(@as(i8, -8) * (@backingInt(saved_gra_end) - @backingInt(hint_ra))) });
133 },
134 .stack_slot => |stack_slot| {
135 assert(stack_slot.base == .sp);
136 passed_vi.changeStackSlot(&isel, named_stack_args.withOffset(stack_slot.offset));
137 },
138 .address, .value, .constant => unreachable,
139 }
140 }
141
142 ret: {
143 var ret_it: Select.CallAbiIterator = .init;
144 const ret_vi = try ret_it.ret(&isel, .fromInterned(func_type.return_type)) orelse break :ret;
145 tracking_log.debug("${d} <- %main", .{@backingInt(ret_vi)});
146 try isel.live_values.putNoClobber(gpa, Select.Block.main, ret_vi);
147 }
148
149 assert(!(try isel.blocks.getOrPut(gpa, Select.Block.main)).found_existing);
150 try isel.analyze(air_main_body);
151 try isel.finishAnalysis();
152 isel.verify(false);
153
154 isel.blocks.values()[0] = .{
155 .live_registers = isel.live_registers,
156 .target_label = @intCast(isel.instructions.items.len),
157 };
158 try isel.body(air_main_body);
159 if (isel.live_values.fetchRemove(Select.Block.main)) |ret_vi| {
160 switch (ret_vi.value.parent(&isel)) {
161 .unallocated, .stack_slot => {},
162 .value, .constant => unreachable,
163 .address => |address_vi| try address_vi.liveIn(
164 &isel,
165 address_vi.hint(&isel).?,
166 comptime &.initFill(.free),
167 ),
168 }
169 ret_vi.value.deref(&isel);
170 }
171 isel.verify(true);
172
173 const prologue = isel.instructions.items.len;
174 const epilogue = try isel.layout(param_it, is_sysv_var_args, saved_gra_len, saved_vra_len, mod);
175
176 try isel.instructions.shrinkToLen(gpa);
177 try isel.literals.shrinkToLen(gpa);
178 try isel.nav_relocs.shrinkToLen(gpa);
179 try isel.uav_relocs.shrinkToLen(gpa);
180 try isel.lazy_relocs.shrinkToLen(gpa);
181 try isel.global_relocs.shrinkToLen(gpa);
182 try isel.literal_relocs.shrinkToLen(gpa);
183
184 const instructions = isel.instructions.toOwnedSliceAssert();
185
186 return .{
187 .prologue = instructions[prologue..epilogue],
188 .body = instructions[0..prologue],
189 .epilogue = instructions[epilogue..],
190 .literals = isel.literals.toOwnedSliceAssert(),
191 .nav_relocs = isel.nav_relocs.toOwnedSliceAssert(),
192 .uav_relocs = isel.uav_relocs.toOwnedSliceAssert(),
193 .lazy_relocs = isel.lazy_relocs.toOwnedSliceAssert(),
194 .global_relocs = isel.global_relocs.toOwnedSliceAssert(),
195 .literal_relocs = isel.literal_relocs.toOwnedSliceAssert(),
196 };
197}
198
199test {
200 _ = Assemble;
201}
202
203const Air = @import("../Air.zig");
204const assert = std.debug.assert;
205const InternPool = @import("../InternPool.zig");
206const link = @import("../link.zig");
207const std = @import("std");
208const tracking_log = std.log.scoped(.tracking);
209const Zcu = @import("../Zcu.zig");