| 1 | pub const abi = @import("aarch64/abi.zig"); |
| 2 | pub const Assemble = @import("aarch64/Assemble.zig"); |
| 3 | pub const Disassemble = @import("aarch64/Disassemble.zig"); |
| 4 | pub const encoding = @import("aarch64/encoding.zig"); |
| 5 | pub const Mir = @import("aarch64/Mir.zig"); |
| 6 | pub const Select = @import("aarch64/Select.zig"); |
| 7 | |
| 8 | pub 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 | |
| 16 | pub 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 | |
| 199 | test { |
| 200 | _ = Assemble; |
| 201 | } |
| 202 | |
| 203 | const Air = @import("../Air.zig"); |
| 204 | const assert = std.debug.assert; |
| 205 | const InternPool = @import("../InternPool.zig"); |
| 206 | const link = @import("../link.zig"); |
| 207 | const std = @import("std"); |
| 208 | const tracking_log = std.log.scoped(.tracking); |
| 209 | const Zcu = @import("../Zcu.zig"); |