authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-11-01 15:48:01+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-01 14:23:38-04:00
log002fbb0af043d90b0ab7d2f2804effc6fa2d690c
tree3342ea9a571b1dfce7ebc5cb4e1bea89a895b3d1
parentf951bf8aeb83c9d0e6528f45d2b2975e50be993a

stage2 AArch64: implement unconditional branches


2 files changed, 186 insertions(+), 6 deletions(-)

src/arch/aarch64/CodeGen.zig+2
......@@ -315,6 +315,8 @@ pub fn generate(
315315 .prev_di_line = module_fn.lbrace_line,
316316 .prev_di_column = module_fn.lbrace_column,
317317 };
318 defer emit.deinit();
319
318320 emit.emitMir() catch |err| switch (err) {
319321 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
320322 else => |e| return e,
src/arch/aarch64/Emit.zig+184-6
......@@ -29,16 +29,38 @@ prev_di_column: u32,
2929/// Relative to the beginning of `code`.
3030prev_di_pc: usize,
3131
32/// The branch type of every branch
33branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .{},
34/// For every forward branch, maps the target instruction to a list of
35/// branches which branch to this target instruction
36branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayListUnmanaged(Mir.Inst.Index)) = .{},
37/// For backward branches: stores the code offset of the target
38/// instruction
39///
40/// For forward branches: stores the code offset of the branch
41/// instruction
42code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{},
43
3244const InnerError = error{
3345 OutOfMemory,
3446 EmitFail,
3547};
3648
49const BranchType = enum {
50 unconditional_branch_immediate,
51
52 const default = BranchType.unconditional_branch_immediate;
53};
54
3755pub fn emitMir(
3856 emit: *Emit,
3957) !void {
4058 const mir_tags = emit.mir.instructions.items(.tag);
4159
60 // Find smallest lowerings for branch instructions
61 try emit.lowerBranches();
62
63 // Emit machine code
4264 for (mir_tags) |tag, index| {
4365 const inst = @intCast(u32, index);
4466 switch (tag) {
......@@ -84,6 +106,159 @@ pub fn emitMir(
84106 }
85107}
86108
109pub fn deinit(emit: *Emit) void {
110 emit.branch_types.deinit(emit.bin_file.allocator);
111 emit.branch_forward_origins.deinit(emit.bin_file.allocator);
112 emit.code_offset_mapping.deinit(emit.bin_file.allocator);
113 emit.* = undefined;
114}
115
116fn optimalBranchType(emit: *Emit, offset: i64) !BranchType {
117 assert(offset & 0b11 == 0);
118
119 // TODO handle conditional branches
120 if (std.math.cast(i26, offset >> 2)) |_| {
121 return BranchType.unconditional_branch_immediate;
122 } else |_| {
123 return emit.fail("TODO support branches larger than +-128 MiB", .{});
124 }
125}
126
127fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
128 const tag = emit.mir.instructions.items(.tag)[inst];
129 switch (tag) {
130 .b, .bl => switch (emit.branch_types.get(inst).?) {
131 .unconditional_branch_immediate => return 4,
132 },
133 .load_memory => {
134 if (emit.bin_file.options.pie) {
135 // adrp, ldr
136 return 2 * 4;
137 } else {
138 const payload = emit.mir.instructions.items(.data)[inst].payload;
139 const load_memory = emit.mir.extraData(Mir.LoadMemory, payload).data;
140 const addr = load_memory.addr;
141
142 // movz, [movk, ...], ldr
143 if (addr <= math.maxInt(u16)) return 2 * 4;
144 if (addr <= math.maxInt(u32)) return 3 * 4;
145 if (addr <= math.maxInt(u48)) return 4 * 4;
146 return 5 * 4;
147 }
148 },
149 else => return 4,
150 }
151}
152
153fn lowerBranches(emit: *Emit) !void {
154 const mir_tags = emit.mir.instructions.items(.tag);
155 const allocator = emit.bin_file.allocator;
156
157 // First pass: Note down all branches and their target
158 // instructions, i.e. populate branch_types,
159 // branch_forward_origins, and code_offset_mapping
160 //
161 // TODO optimization opportunity: do this in codegen while
162 // generating MIR
163 for (mir_tags) |tag, index| {
164 const inst = @intCast(u32, index);
165 switch (tag) {
166 .b, .bl => {
167 const target_inst = emit.mir.instructions.items(.data)[inst].inst;
168
169 // Remember this branch instruction
170 try emit.branch_types.put(allocator, inst, BranchType.default);
171
172 // Forward branches require some extra stuff: We only
173 // know their offset once we arrive at the target
174 // instruction. Therefore, we need to be able to
175 // access the branch instruction when we visit the
176 // target instruction in order to manipulate its type
177 // etc.
178 if (target_inst > inst) {
179 // Remember the branch instruction index
180 try emit.code_offset_mapping.put(allocator, inst, 0);
181
182 if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| {
183 try origin_list.append(allocator, inst);
184 } else {
185 var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .{};
186 try origin_list.append(allocator, inst);
187 try emit.branch_forward_origins.put(allocator, target_inst, origin_list);
188 }
189 }
190
191 // Remember the target instruction index so that we
192 // update the real code offset in all future passes
193 //
194 // putNoClobber may not be used as the put operation
195 // may clobber the entry when multiple branches branch
196 // to the same target instruction
197 try emit.code_offset_mapping.put(allocator, target_inst, 0);
198 },
199 else => {}, // not a branch
200 }
201 }
202
203 // Further passes: Until all branches are lowered, interate
204 // through all instructions and calculate new offsets and
205 // potentially new branch types
206 var all_branches_lowered = false;
207 while (!all_branches_lowered) {
208 all_branches_lowered = true;
209 var current_code_offset: usize = 0;
210
211 for (mir_tags) |tag, index| {
212 const inst = @intCast(u32, index);
213
214 // If this instruction contained in the code offset
215 // mapping (when it is a target of a branch or if it is a
216 // forward branch), update the code offset
217 if (emit.code_offset_mapping.getPtr(inst)) |offset| {
218 offset.* = current_code_offset;
219 }
220
221 // If this instruction is a backward branch, calculate the
222 // offset, which may potentially update the branch type
223 switch (tag) {
224 .b, .bl => {
225 const target_inst = emit.mir.instructions.items(.data)[inst].inst;
226 if (target_inst < inst) {
227 const target_offset = emit.code_offset_mapping.get(target_inst).?;
228 const offset = @intCast(i64, target_offset) - @intCast(i64, current_code_offset + 8);
229 const branch_type = emit.branch_types.getPtr(inst).?;
230 const optimal_branch_type = try emit.optimalBranchType(offset);
231 if (branch_type.* != optimal_branch_type) {
232 branch_type.* = optimal_branch_type;
233 all_branches_lowered = false;
234 }
235 }
236 },
237 else => {},
238 }
239
240 // If this instruction is the target of one or more
241 // forward branches, calculate the offset, which may
242 // potentially update the branch type
243 if (emit.branch_forward_origins.get(inst)) |origin_list| {
244 for (origin_list.items) |forward_branch_inst| {
245 const forward_branch_inst_offset = emit.code_offset_mapping.get(forward_branch_inst).?;
246 const offset = @intCast(i64, forward_branch_inst_offset) - @intCast(i64, current_code_offset + 8);
247 const branch_type = emit.branch_types.getPtr(forward_branch_inst).?;
248 const optimal_branch_type = try emit.optimalBranchType(offset);
249 if (branch_type.* != optimal_branch_type) {
250 branch_type.* = optimal_branch_type;
251 all_branches_lowered = false;
252 }
253 }
254 }
255
256 // Increment code offset
257 current_code_offset += emit.instructionSize(inst);
258 }
259 }
260}
261
87262fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
88263 const endian = emit.target.cpu.arch.endian();
89264 std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
......@@ -185,13 +360,16 @@ fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
185360fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
186361 const tag = emit.mir.instructions.items(.tag)[inst];
187362 const target_inst = emit.mir.instructions.items(.data)[inst].inst;
188 _ = tag;
189 _ = target_inst;
190363
191 switch (tag) {
192 .b => return emit.fail("Implement mirBranch", .{}),
193 .bl => return emit.fail("Implement mirBranch", .{}),
194 else => unreachable,
364 const offset = @intCast(i64, emit.code_offset_mapping.get(target_inst).?) - @intCast(i64, emit.code.items.len + 8);
365 const branch_type = emit.branch_types.get(inst).?;
366
367 switch (branch_type) {
368 .unconditional_branch_immediate => switch (tag) {
369 .b => try emit.writeInstruction(Instruction.b(@intCast(i28, offset))),
370 .bl => try emit.writeInstruction(Instruction.bl(@intCast(i28, offset))),
371 else => unreachable,
372 },
195373 }
196374}
197375