| 1 | pub const Mir = @import("loongarch/Mir.zig"); |
| 2 | const Select = @import("loongarch/Select.zig"); |
| 3 | const bits = @import("loongarch/bits.zig"); |
| 4 | pub const Disassemble = @import("loongarch/Disassemble.zig"); |
| 5 | pub const encoding = @import("loongarch/encoding.zig"); |
| 6 | |
| 7 | test { |
| 8 | _ = bits; |
| 9 | _ = Disassemble; |
| 10 | } |
| 11 | |
| 12 | pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { |
| 13 | return comptime &.initMany(&.{ |
| 14 | .expand_bit_cast_safe, |
| 15 | .expand_int_cast_safe, |
| 16 | .expand_int_from_float_safe, |
| 17 | .expand_int_from_float_optimized_safe, |
| 18 | .expand_add_safe, |
| 19 | .expand_sub_safe, |
| 20 | .expand_mul_safe, |
| 21 | .expand_packed_load, |
| 22 | .expand_packed_store, |
| 23 | .expand_packed_agg_field_val, |
| 24 | .expand_packed_aggregate_init, |
| 25 | .soft_f16, |
| 26 | .soft_f32, |
| 27 | .soft_f64, |
| 28 | .soft_f80, |
| 29 | }); |
| 30 | } |
| 31 | |
| 32 | pub fn generate( |
| 33 | _: *link.File, |
| 34 | pt: Zcu.PerThread, |
| 35 | func_index: InternPool.Index, |
| 36 | air: *const Air, |
| 37 | liveness: *const ?Air.Liveness, |
| 38 | ) !Mir { |
| 39 | const zcu = pt.zcu; |
| 40 | const gpa = zcu.gpa; |
| 41 | const ip = &zcu.intern_pool; |
| 42 | const func = zcu.funcInfo(func_index); |
| 43 | const func_zir = func.zir_body_inst.resolveFull(ip).?; |
| 44 | const file = zcu.fileByIndex(func_zir.file); |
| 45 | const named_params_len = file.zir.?.getParamBody(func_zir.inst).len; |
| 46 | const func_type = ip.indexToKey(func.ty).func_type; |
| 47 | assert(liveness.* == null); |
| 48 | |
| 49 | // Initialize ISel |
| 50 | const mod = zcu.navFileScope(func.owner_nav).mod.?; |
| 51 | var isel: Select = .{ |
| 52 | .pt = pt, |
| 53 | .target = &mod.resolved_target.result, |
| 54 | .opt_mode = mod.optimize_mode, |
| 55 | .air = air.*, |
| 56 | .nav_index = zcu.funcInfo(func_index).owner_nav, |
| 57 | }; |
| 58 | defer isel.deinit(); |
| 59 | assert(try isel.active_blocks.fetchPut(gpa, Select.Block.main, .{ .target_label = 0 }) == null); |
| 60 | defer isel.active_blocks.entries.items(.value)[0].deinit(&isel); |
| 61 | |
| 62 | const air_main_body = air.getMainBody(); |
| 63 | |
| 64 | // Calculate parameter & return hints and layouts |
| 65 | var cc_it1: Select.CallAbiIterator = .{ |
| 66 | .cc = &func_type.cc, |
| 67 | .isel = &isel, |
| 68 | .stack_pointer = .fp, |
| 69 | }; |
| 70 | var cc_it2 = cc_it1; |
| 71 | |
| 72 | switch (func_type.cc) { |
| 73 | // naked functions cannot have any arguments |
| 74 | // Otherwise, SP will always be moved to allocate space for the saved FP and _start will be broken. |
| 75 | .naked => {}, |
| 76 | // FP is required to load byval arguments passed on stack now |
| 77 | // TODO: use FP only when necessary |
| 78 | else => isel.saved_registers.insert(.fp), |
| 79 | } |
| 80 | |
| 81 | const ret_layout_vi: ?Select.Value.Index = ret: { |
| 82 | const ret_vi1 = try cc_it1.resolve(.fromInterned(func_type.return_type), true) orelse break :ret null; |
| 83 | const ret_vi2 = try cc_it2.resolve(.fromInterned(func_type.return_type), true) orelse unreachable; |
| 84 | ret_vi2.deref(&isel); |
| 85 | tracking_log.debug("{f} <- %main", .{ret_vi1}); |
| 86 | try isel.live_values.putNoClobber(gpa, Select.Block.main, ret_vi1); |
| 87 | break :ret ret_vi2; |
| 88 | }; |
| 89 | |
| 90 | var arg_layouts: std.ArrayList(Select.Value.Index) = .empty; |
| 91 | defer arg_layouts.deinit(gpa); |
| 92 | for (air_main_body) |air_inst_index| { |
| 93 | if (air.instructions.items(.tag)[@backingInt(air_inst_index)] != .arg) break; |
| 94 | const arg = air.instructions.items(.data)[@backingInt(air_inst_index)].arg; |
| 95 | const param_ty = arg.ty; |
| 96 | if (arg.zir_param_index >= named_params_len) |
| 97 | assert(func_type.is_var_args); |
| 98 | const param_vi1 = try cc_it1.resolve(param_ty, false) orelse unreachable; |
| 99 | const param_vi2 = try cc_it2.resolve(param_ty, false) orelse unreachable; |
| 100 | tracking_log.debug("{f} <- %{d}", .{ param_vi1, @backingInt(air_inst_index) }); |
| 101 | try isel.live_values.putNoClobber(gpa, air_inst_index, param_vi1); |
| 102 | try arg_layouts.append(gpa, param_vi2); |
| 103 | } |
| 104 | if (arg_layouts.items.len != 0) |
| 105 | isel.arg_layouts = try arg_layouts.toOwnedSlice(gpa); |
| 106 | |
| 107 | // Analyze |
| 108 | try isel.analyze(air_main_body); |
| 109 | try isel.finishAnalysis(); |
| 110 | isel.verify(false); |
| 111 | |
| 112 | // Generate body |
| 113 | assert(isel.instructions.items.len == 0); |
| 114 | try isel.body(air_main_body); |
| 115 | if (isel.live_values.fetchRemove(Select.Block.main)) |ret_vi| { |
| 116 | defer ret_vi.value.deref(&isel); |
| 117 | |
| 118 | switch (ret_vi.value.parent(&isel)) { |
| 119 | .none, .value => {}, |
| 120 | .address => |ret_addr_vi| { |
| 121 | tracking_log.debug("live-in by-ref return address", .{}); |
| 122 | try ret_addr_vi.defLiveIn(&isel, ret_layout_vi.?.parent(&isel).address, .{}); |
| 123 | }, |
| 124 | .constant => unreachable, |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | // Generate prologue and epilogue |
| 129 | const prologue = isel.instructions.items.len; |
| 130 | const epilogue = try isel.layout(cc_it1, mod); |
| 131 | |
| 132 | // Verification |
| 133 | isel.verify(true); |
| 134 | try isel.verifyTargetFeatures(); |
| 135 | |
| 136 | // Finalization |
| 137 | const instructions = try isel.instructions.toOwnedSlice(gpa); |
| 138 | var mir: Mir = .{ |
| 139 | .prologue = instructions[prologue..epilogue], |
| 140 | .body = instructions[0..prologue], |
| 141 | .epilogue = instructions[epilogue..], |
| 142 | .nav_relocs = &.{}, |
| 143 | .uav_relocs = &.{}, |
| 144 | .lazy_relocs = &.{}, |
| 145 | .global_relocs = &.{}, |
| 146 | .internal_relocs = &.{}, |
| 147 | }; |
| 148 | errdefer mir.deinit(gpa); |
| 149 | mir.nav_relocs = try isel.nav_relocs.toOwnedSlice(gpa); |
| 150 | mir.uav_relocs = try isel.uav_relocs.toOwnedSlice(gpa); |
| 151 | mir.lazy_relocs = try isel.lazy_relocs.toOwnedSlice(gpa); |
| 152 | mir.global_relocs = try isel.global_relocs.toOwnedSlice(gpa); |
| 153 | mir.internal_relocs = try isel.internal_relocs.toOwnedSlice(gpa); |
| 154 | return mir; |
| 155 | } |
| 156 | |
| 157 | const Air = @import("../Air.zig"); |
| 158 | const assert = std.debug.assert; |
| 159 | const InternPool = @import("../InternPool.zig"); |
| 160 | const link = @import("../link.zig"); |
| 161 | const std = @import("std"); |
| 162 | const tracking_log = std.log.scoped(.tracking); |
| 163 | const Zcu = @import("../Zcu.zig"); |