1pub const Mir = @import("loongarch/Mir.zig");
2const Select = @import("loongarch/Select.zig");
3const bits = @import("loongarch/bits.zig");
4pub const Disassemble = @import("loongarch/Disassemble.zig");
5pub const encoding = @import("loongarch/encoding.zig");
6
7test {
8 _ = bits;
9 _ = Disassemble;
10}
11
12pub 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
32pub 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
157const Air = @import("../Air.zig");
158const assert = std.debug.assert;
159const InternPool = @import("../InternPool.zig");
160const link = @import("../link.zig");
161const std = @import("std");
162const tracking_log = std.log.scoped(.tracking);
163const Zcu = @import("../Zcu.zig");