| ... | @@ -8,6 +8,7 @@ const link = @import("../../link.zig"); | ... | @@ -8,6 +8,7 @@ const link = @import("../../link.zig"); |
| 8 | const Module = @import("../../Module.zig"); | 8 | const Module = @import("../../Module.zig"); |
| 9 | const ErrorMsg = Module.ErrorMsg; | 9 | const ErrorMsg = Module.ErrorMsg; |
| 10 | const Liveness = @import("../../Liveness.zig"); | 10 | const Liveness = @import("../../Liveness.zig"); |
| | 11 | const log = std.log.scoped(.sparcv9_emit); |
| 11 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 12 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 12 | const DW = std.dwarf; | 13 | const DW = std.dwarf; |
| 13 | const leb128 = std.leb; | 14 | const leb128 = std.leb; |
| ... | @@ -31,16 +32,42 @@ prev_di_column: u32, | ... | @@ -31,16 +32,42 @@ prev_di_column: u32, |
| 31 | /// Relative to the beginning of `code`. | 32 | /// Relative to the beginning of `code`. |
| 32 | prev_di_pc: usize, | 33 | prev_di_pc: usize, |
| 33 | | 34 | |
| | 35 | /// The branch type of every branch |
| | 36 | branch_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 |
| | 39 | branch_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 |
| | 45 | code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{}, |
| | 46 | |
| 34 | const InnerError = error{ | 47 | const InnerError = error{ |
| 35 | OutOfMemory, | 48 | OutOfMemory, |
| 36 | EmitFail, | 49 | EmitFail, |
| 37 | }; | 50 | }; |
| 38 | | 51 | |
| | 52 | const 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 | |
| 39 | pub fn emitMir( | 62 | pub fn emitMir( |
| 40 | emit: *Emit, | 63 | emit: *Emit, |
| 41 | ) InnerError!void { | 64 | ) InnerError!void { |
| 42 | const mir_tags = emit.mir.instructions.items(.tag); | 65 | const mir_tags = emit.mir.instructions.items(.tag); |
| 43 | | 66 | |
| | 67 | // Convert absolute addresses into offsets and |
| | 68 | // find smallest lowerings for branch instructions |
| | 69 | try emit.lowerBranches(); |
| | 70 | |
| 44 | // Emit machine code | 71 | // Emit machine code |
| 45 | for (mir_tags) |tag, index| { | 72 | for (mir_tags) |tag, index| { |
| 46 | const inst = @intCast(u32, index); | 73 | const inst = @intCast(u32, index); |
| ... | @@ -51,7 +78,7 @@ pub fn emitMir( | ... | @@ -51,7 +78,7 @@ pub fn emitMir( |
| 51 | | 78 | |
| 52 | .add => try emit.mirArithmetic3Op(inst), | 79 | .add => try emit.mirArithmetic3Op(inst), |
| 53 | | 80 | |
| 54 | .bpcc => @panic("TODO implement sparc64 bpcc"), | 81 | .bpcc => try emit.mirConditionalBranch(inst), |
| 55 | | 82 | |
| 56 | .call => @panic("TODO implement sparc64 call"), | 83 | .call => @panic("TODO implement sparc64 call"), |
| 57 | | 84 | |
| ... | @@ -89,6 +116,14 @@ pub fn emitMir( | ... | @@ -89,6 +116,14 @@ pub fn emitMir( |
| 89 | } | 116 | } |
| 90 | | 117 | |
| 91 | pub fn deinit(emit: *Emit) void { | 118 | pub 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); |
| 92 | emit.* = undefined; | 127 | emit.* = undefined; |
| 93 | } | 128 | } |
| 94 | | 129 | |
| ... | @@ -195,6 +230,22 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -195,6 +230,22 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 195 | } | 230 | } |
| 196 | } | 231 | } |
| 197 | | 232 | |
| | 233 | fn 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 | |
| 198 | fn mirNop(emit: *Emit) !void { | 249 | fn mirNop(emit: *Emit) !void { |
| 199 | try emit.writeInstruction(Instruction.nop()); | 250 | try emit.writeInstruction(Instruction.nop()); |
| 200 | } | 251 | } |
| ... | @@ -235,6 +286,15 @@ fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -235,6 +286,15 @@ fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 235 | | 286 | |
| 236 | // Common helper functions | 287 | // Common helper functions |
| 237 | | 288 | |
| | 289 | fn 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 | |
| 238 | fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { | 298 | fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { |
| 239 | const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); | 299 | const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); |
| 240 | const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; | 300 | 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 { | ... | @@ -267,6 +327,155 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { |
| 267 | return error.EmitFail; | 327 | return error.EmitFail; |
| 268 | } | 328 | } |
| 269 | | 329 | |
| | 330 | fn 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 | |
| | 343 | fn isBranch(tag: Mir.Inst.Tag) bool { |
| | 344 | return switch (tag) { |
| | 345 | .bpcc => true, |
| | 346 | else => false, |
| | 347 | }; |
| | 348 | } |
| | 349 | |
| | 350 | fn 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 | |
| | 458 | fn 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 | |
| 270 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { | 479 | fn writeInstruction(emit: *Emit, instruction: Instruction) !void { |
| 271 | // SPARCv9 instructions are always arranged in BE regardless of the | 480 | // SPARCv9 instructions are always arranged in BE regardless of the |
| 272 | // endianness mode the CPU is running in (Section 3.1 of the ISA specification). | 481 | // endianness mode the CPU is running in (Section 3.1 of the ISA specification). |