authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-06 21:59:47+07:00
committergravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-16 22:48:49+07:00
log339b0517b3a1e33f2b348ac9764687847054bea2
tree6294de8ea2fbc6782ca33cffdf9b131a022d2609
parent5d260eb573132194e802680803f225404a06b00e

stage2: sparc64: Implement SPARCv9 bpcc


2 files changed, 214 insertions(+), 1 deletions(-)

src/arch/sparc64/Emit.zig+210-1
......@@ -8,6 +8,7 @@ const link = @import("../../link.zig");
88const Module = @import("../../Module.zig");
99const ErrorMsg = Module.ErrorMsg;
1010const Liveness = @import("../../Liveness.zig");
11const log = std.log.scoped(.sparcv9_emit);
1112const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
1213const DW = std.dwarf;
1314const leb128 = std.leb;
......@@ -31,16 +32,42 @@ prev_di_column: u32,
3132/// Relative to the beginning of `code`.
3233prev_di_pc: usize,
3334
35/// The branch type of every branch
36branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .{},
37/// For every forward branch, maps the target instruction to a list of
38/// branches which branch to this target instruction
39branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayListUnmanaged(Mir.Inst.Index)) = .{},
40/// For backward branches: stores the code offset of the target
41/// instruction
42///
43/// For forward branches: stores the code offset of the branch
44/// instruction
45code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{},
46
3447const InnerError = error{
3548 OutOfMemory,
3649 EmitFail,
3750};
3851
52const BranchType = enum {
53 bpcc,
54 fn default(tag: Mir.Inst.Tag) BranchType {
55 return switch (tag) {
56 .bpcc => .bpcc,
57 else => unreachable,
58 };
59 }
60};
61
3962pub fn emitMir(
4063 emit: *Emit,
4164) InnerError!void {
4265 const mir_tags = emit.mir.instructions.items(.tag);
4366
67 // Convert absolute addresses into offsets and
68 // find smallest lowerings for branch instructions
69 try emit.lowerBranches();
70
4471 // Emit machine code
4572 for (mir_tags) |tag, index| {
4673 const inst = @intCast(u32, index);
......@@ -51,7 +78,7 @@ pub fn emitMir(
5178
5279 .add => try emit.mirArithmetic3Op(inst),
5380
54 .bpcc => @panic("TODO implement sparc64 bpcc"),
81 .bpcc => try emit.mirConditionalBranch(inst),
5582
5683 .call => @panic("TODO implement sparc64 call"),
5784
......@@ -89,6 +116,14 @@ pub fn emitMir(
89116}
90117
91118pub fn deinit(emit: *Emit) void {
119 var iter = emit.branch_forward_origins.valueIterator();
120 while (iter.next()) |origin_list| {
121 origin_list.deinit(emit.bin_file.allocator);
122 }
123
124 emit.branch_types.deinit(emit.bin_file.allocator);
125 emit.branch_forward_origins.deinit(emit.bin_file.allocator);
126 emit.code_offset_mapping.deinit(emit.bin_file.allocator);
92127 emit.* = undefined;
93128}
94129
......@@ -195,6 +230,22 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
195230 }
196231}
197232
233fn mirConditionalBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
234 const tag = emit.mir.instructions.items(.tag)[inst];
235 const branch_predict_int = emit.mir.instructions.items(.data)[inst].branch_predict_int;
236
237 const offset = @intCast(i64, emit.code_offset_mapping.get(branch_predict_int.inst).?) - @intCast(i64, emit.code.items.len);
238 const branch_type = emit.branch_types.get(inst).?;
239 log.debug("mirConditionalBranchImmediate: {} offset={}", .{ inst, offset });
240
241 switch (branch_type) {
242 .bpcc => switch (tag) {
243 .bpcc => try emit.writeInstruction(Instruction.bpcc(branch_predict_int.cond, branch_predict_int.annul, branch_predict_int.pt, branch_predict_int.ccr, @intCast(i21, offset))),
244 else => unreachable,
245 },
246 }
247}
248
198249fn mirNop(emit: *Emit) !void {
199250 try emit.writeInstruction(Instruction.nop());
200251}
......@@ -235,6 +286,15 @@ fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void {
235286
236287// Common helper functions
237288
289fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index {
290 const tag = emit.mir.instructions.items(.tag)[inst];
291
292 switch (tag) {
293 .bpcc => return emit.mir.instructions.items(.data)[inst].branch_predict_int.inst,
294 else => unreachable,
295 }
296}
297
238298fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
239299 const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
240300 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
......@@ -267,6 +327,155 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
267327 return error.EmitFail;
268328}
269329
330fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
331 const tag = emit.mir.instructions.items(.tag)[inst];
332
333 switch (tag) {
334 .dbg_line,
335 .dbg_epilogue_begin,
336 .dbg_prologue_end,
337 => return 0,
338 // Currently Mir instructions always map to single machine instruction.
339 else => return 4,
340 }
341}
342
343fn isBranch(tag: Mir.Inst.Tag) bool {
344 return switch (tag) {
345 .bpcc => true,
346 else => false,
347 };
348}
349
350fn lowerBranches(emit: *Emit) !void {
351 const mir_tags = emit.mir.instructions.items(.tag);
352 const allocator = emit.bin_file.allocator;
353
354 // First pass: Note down all branches and their target
355 // instructions, i.e. populate branch_types,
356 // branch_forward_origins, and code_offset_mapping
357 //
358 // TODO optimization opportunity: do this in codegen while
359 // generating MIR
360 for (mir_tags) |tag, index| {
361 const inst = @intCast(u32, index);
362 if (isBranch(tag)) {
363 const target_inst = emit.branchTarget(inst);
364
365 // Remember this branch instruction
366 try emit.branch_types.put(allocator, inst, BranchType.default(tag));
367
368 // Forward branches require some extra stuff: We only
369 // know their offset once we arrive at the target
370 // instruction. Therefore, we need to be able to
371 // access the branch instruction when we visit the
372 // target instruction in order to manipulate its type
373 // etc.
374 if (target_inst > inst) {
375 // Remember the branch instruction index
376 try emit.code_offset_mapping.put(allocator, inst, 0);
377
378 if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| {
379 try origin_list.append(allocator, inst);
380 } else {
381 var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .{};
382 try origin_list.append(allocator, inst);
383 try emit.branch_forward_origins.put(allocator, target_inst, origin_list);
384 }
385 }
386
387 // Remember the target instruction index so that we
388 // update the real code offset in all future passes
389 //
390 // putNoClobber may not be used as the put operation
391 // may clobber the entry when multiple branches branch
392 // to the same target instruction
393 try emit.code_offset_mapping.put(allocator, target_inst, 0);
394 }
395 }
396
397 // Further passes: Until all branches are lowered, interate
398 // through all instructions and calculate new offsets and
399 // potentially new branch types
400 var all_branches_lowered = false;
401 while (!all_branches_lowered) {
402 all_branches_lowered = true;
403 var current_code_offset: usize = 0;
404
405 for (mir_tags) |tag, index| {
406 const inst = @intCast(u32, index);
407
408 // If this instruction contained in the code offset
409 // mapping (when it is a target of a branch or if it is a
410 // forward branch), update the code offset
411 if (emit.code_offset_mapping.getPtr(inst)) |offset| {
412 offset.* = current_code_offset;
413 }
414
415 // If this instruction is a backward branch, calculate the
416 // offset, which may potentially update the branch type
417 if (isBranch(tag)) {
418 const target_inst = emit.branchTarget(inst);
419 if (target_inst < inst) {
420 const target_offset = emit.code_offset_mapping.get(target_inst).?;
421 const offset = @intCast(i64, target_offset) - @intCast(i64, current_code_offset);
422 const branch_type = emit.branch_types.getPtr(inst).?;
423 const optimal_branch_type = try emit.optimalBranchType(tag, offset);
424 if (branch_type.* != optimal_branch_type) {
425 branch_type.* = optimal_branch_type;
426 all_branches_lowered = false;
427 }
428
429 log.debug("lowerBranches: branch {} has offset {}", .{ inst, offset });
430 }
431 }
432
433 // If this instruction is the target of one or more
434 // forward branches, calculate the offset, which may
435 // potentially update the branch type
436 if (emit.branch_forward_origins.get(inst)) |origin_list| {
437 for (origin_list.items) |forward_branch_inst| {
438 const branch_tag = emit.mir.instructions.items(.tag)[forward_branch_inst];
439 const forward_branch_inst_offset = emit.code_offset_mapping.get(forward_branch_inst).?;
440 const offset = @intCast(i64, current_code_offset) - @intCast(i64, forward_branch_inst_offset);
441 const branch_type = emit.branch_types.getPtr(forward_branch_inst).?;
442 const optimal_branch_type = try emit.optimalBranchType(branch_tag, offset);
443 if (branch_type.* != optimal_branch_type) {
444 branch_type.* = optimal_branch_type;
445 all_branches_lowered = false;
446 }
447
448 log.debug("lowerBranches: branch {} has offset {}", .{ forward_branch_inst, offset });
449 }
450 }
451
452 // Increment code offset
453 current_code_offset += emit.instructionSize(inst);
454 }
455 }
456}
457
458fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType {
459 assert(offset & 0b11 == 0);
460
461 switch (tag) {
462 .bpcc => {
463 if (std.math.cast(i21, offset)) |_| {
464 return BranchType.bpcc;
465 } else |_| {
466 // TODO use the following strategy to implement long branches:
467 // - Negate the conditional and target of the original BPcc;
468 // - In the space immediately after the branch, load
469 // the address of the original target, preferrably in
470 // a PC-relative way, into %o7; and
471 // - jmpl %o7 + %g0, %g0
472 return emit.fail("TODO support BPcc branches larger than +-1 MiB", .{});
473 }
474 },
475 else => unreachable,
476 }
477}
478
270479fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
271480 // SPARCv9 instructions are always arranged in BE regardless of the
272481 // endianness mode the CPU is running in (Section 3.1 of the ISA specification).
src/arch/sparc64/bits.zig+4
......@@ -1141,6 +1141,10 @@ pub const Instruction = union(enum) {
11411141 };
11421142 }
11431143
1144 pub fn bpcc(cond: ICondition, annul: bool, pt: bool, ccr: CCR, disp: i21) Instruction {
1145 return format2c(0b001, .{ .icond = cond }, annul, pt, ccr, disp);
1146 }
1147
11441148 pub fn jmpl(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
11451149 return switch (s2) {
11461150 Register => format3a(0b10, 0b11_1000, rs1, rs2, rd),