mirror of
https://github.com/ziglang/zig.git
synced 2025-12-25 07:33:08 +00:00
riscv: implement basic branching
we use a code offset map in Emit.zig to pre-compute what byte offset each MIR instruction is at. this is important because they can be of different size
This commit is contained in:
parent
28df64cba4
commit
2be3033acd
@ -759,11 +759,6 @@ else
|
||||
pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr: ?usize) noreturn {
|
||||
@setCold(true);
|
||||
|
||||
// stage2_riscv64 backend doesn't support loops yet.
|
||||
if (builtin.zig_backend == .stage2_riscv64) {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
// For backends that cannot handle the language features depended on by the
|
||||
// default panic handler, we have a simpler panic handler:
|
||||
if (builtin.zig_backend == .stage2_wasm or
|
||||
@ -772,7 +767,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
|
||||
builtin.zig_backend == .stage2_x86 or
|
||||
(builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf and builtin.target.ofmt != .macho)) or
|
||||
builtin.zig_backend == .stage2_sparc64 or
|
||||
builtin.zig_backend == .stage2_spirv64)
|
||||
builtin.zig_backend == .stage2_spirv64 or
|
||||
builtin.zig_backend == .stage2_riscv64)
|
||||
{
|
||||
while (true) {
|
||||
@breakpoint();
|
||||
|
||||
@ -301,6 +301,7 @@ pub fn generate(
|
||||
.prev_di_line = func.lbrace_line,
|
||||
.prev_di_column = func.lbrace_column,
|
||||
.stack_size = @max(32, function.max_end_stack),
|
||||
.code_offset_mapping = .{},
|
||||
};
|
||||
defer emit.deinit();
|
||||
|
||||
@ -929,21 +930,16 @@ fn binOpRegister(
|
||||
.cmp_gt => .cmp_gt,
|
||||
else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
|
||||
};
|
||||
const mir_data: Mir.Inst.Data = switch (tag) {
|
||||
.add,
|
||||
.sub,
|
||||
.cmp_eq,
|
||||
=> .{ .r_type = .{
|
||||
.rd = dest_reg,
|
||||
.rs1 = lhs_reg,
|
||||
.rs2 = rhs_reg,
|
||||
} },
|
||||
else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}),
|
||||
};
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = mir_tag,
|
||||
.data = mir_data,
|
||||
.data = .{
|
||||
.r_type = .{
|
||||
.rd = dest_reg,
|
||||
.rs1 = lhs_reg,
|
||||
.rs2 = rhs_reg,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return MCValue{ .register = dest_reg };
|
||||
@ -1636,7 +1632,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
|
||||
|
||||
const dst_mcv = switch (src_mcv) {
|
||||
.register => |src_reg| dst: {
|
||||
self.register_manager.getRegAssumeFree(src_reg, inst);
|
||||
try self.register_manager.getReg(src_reg, inst);
|
||||
break :dst src_mcv;
|
||||
},
|
||||
else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}),
|
||||
@ -1914,6 +1910,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
self.processDeath(operand);
|
||||
}
|
||||
try self.genBody(then_body);
|
||||
// point at the to-be-generated else case
|
||||
try self.performReloc(reloc, @intCast(self.mir_instructions.len));
|
||||
|
||||
// Revert to the previous register and stack allocation state.
|
||||
|
||||
@ -1929,7 +1927,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
self.next_stack_offset = parent_next_stack_offset;
|
||||
self.register_manager.free_registers = parent_free_registers;
|
||||
|
||||
try self.performReloc(reloc);
|
||||
const else_branch = self.branch_stack.addOneAssumeCapacity();
|
||||
else_branch.* = .{};
|
||||
|
||||
@ -2014,8 +2011,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
var item = self.branch_stack.pop();
|
||||
item.deinit(self.gpa);
|
||||
}
|
||||
|
||||
return self.finishAir(inst, .unreach, .{ .none, .none, .none });
|
||||
}
|
||||
|
||||
fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index {
|
||||
@ -2027,12 +2022,12 @@ fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index {
|
||||
};
|
||||
|
||||
return try self.addInst(.{
|
||||
.tag = .beq,
|
||||
.tag = .bne,
|
||||
.data = .{
|
||||
.b_type = .{
|
||||
.rs1 = reg,
|
||||
.rs2 = .zero,
|
||||
.imm12 = 0, // patched later.
|
||||
.inst = undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -2218,7 +2213,13 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
|
||||
try self.genBody(body);
|
||||
|
||||
for (self.blocks.getPtr(inst).?.relocs.items) |reloc| {
|
||||
try self.performReloc(reloc);
|
||||
// here we are relocing to point at the instruction after the block.
|
||||
// [then case]
|
||||
// [jump to end] // this is reloced
|
||||
// [else case]
|
||||
// [jump to end] // this is reloced
|
||||
// [this isn't generated yet] // point to here
|
||||
try self.performReloc(reloc, @intCast(self.mir_instructions.len));
|
||||
}
|
||||
|
||||
const result = self.blocks.getPtr(inst).?.mcv;
|
||||
@ -2233,11 +2234,14 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
|
||||
// return self.finishAir(inst, .dead, .{ condition, .none, .none });
|
||||
}
|
||||
|
||||
fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
|
||||
fn performReloc(self: *Self, inst: Mir.Inst.Index, target: Mir.Inst.Index) !void {
|
||||
const tag = self.mir_instructions.items(.tag)[inst];
|
||||
|
||||
switch (tag) {
|
||||
.beq => self.mir_instructions.items(.data)[inst].b_type.imm12 = @intCast(inst),
|
||||
.bne,
|
||||
.beq,
|
||||
=> self.mir_instructions.items(.data)[inst].b_type.inst = target,
|
||||
.jal => self.mir_instructions.items(.data)[inst].j_type.inst = target,
|
||||
else => return self.fail("TODO: performReloc {s}", .{@tagName(tag)}),
|
||||
}
|
||||
}
|
||||
@ -2283,7 +2287,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
|
||||
.data = .{
|
||||
.j_type = .{
|
||||
.rd = .ra,
|
||||
.imm21 = undefined, // populated later through performReloc
|
||||
.inst = undefined,
|
||||
},
|
||||
},
|
||||
}));
|
||||
@ -2467,6 +2471,9 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) Inner
|
||||
}
|
||||
},
|
||||
.stack_offset, .load_symbol => {
|
||||
if (true)
|
||||
return self.fail("TODO: genSetStack {s}", .{@tagName(src_val)});
|
||||
|
||||
if (abi_size <= 8) {
|
||||
const reg = try self.copyToTmpRegister(ty, src_val);
|
||||
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
|
||||
|
||||
@ -26,8 +26,14 @@ prev_di_line: u32,
|
||||
prev_di_column: u32,
|
||||
/// Relative to the beginning of `code`.
|
||||
prev_di_pc: usize,
|
||||
|
||||
/// Function's stack size. Used for backpatching.
|
||||
stack_size: u32,
|
||||
/// For backward branches: stores the code offset of the target
|
||||
/// instruction
|
||||
///
|
||||
/// For forward branches: stores the code offset of the branch
|
||||
/// instruction
|
||||
code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{},
|
||||
|
||||
const log = std.log.scoped(.emit);
|
||||
|
||||
@ -100,6 +106,10 @@ pub fn emitMir(
|
||||
}
|
||||
|
||||
pub fn deinit(emit: *Emit) void {
|
||||
const comp = emit.bin_file.comp;
|
||||
const gpa = comp.gpa;
|
||||
|
||||
emit.code_offset_mapping.deinit(gpa);
|
||||
emit.* = undefined;
|
||||
}
|
||||
|
||||
@ -118,10 +128,8 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
|
||||
}
|
||||
|
||||
fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
|
||||
log.debug("Line: {} {}\n", .{ line, emit.prev_di_line });
|
||||
const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));
|
||||
const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
|
||||
log.debug("(advance pc={d} and line={d})", .{ delta_pc, delta_line });
|
||||
switch (emit.debug_output) {
|
||||
.dwarf => |dw| {
|
||||
if (column != emit.prev_di_column) try dw.setColumn(column);
|
||||
@ -166,7 +174,7 @@ fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
switch (tag) {
|
||||
.add => try emit.writeInstruction(Instruction.add(r_type.rd, r_type.rs1, r_type.rs2)),
|
||||
.sub => try emit.writeInstruction(Instruction.sub(r_type.rd, r_type.rs1, r_type.rs2)),
|
||||
.cmp_eq => try emit.writeInstruction(Instruction.slt(r_type.rd, r_type.rs1, r_type.rs2)),
|
||||
.cmp_gt => try emit.writeInstruction(Instruction.slt(r_type.rd, r_type.rs1, r_type.rs2)),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -175,8 +183,17 @@ fn mirBType(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
const b_type = emit.mir.instructions.items(.data)[inst].b_type;
|
||||
|
||||
const offset = @as(i64, @intCast(emit.code_offset_mapping.get(b_type.inst).?)) - @as(i64, @intCast(emit.code.items.len));
|
||||
|
||||
switch (tag) {
|
||||
.beq => try emit.writeInstruction(Instruction.beq(b_type.rs1, b_type.rs2, b_type.imm12)),
|
||||
.beq => {
|
||||
log.debug("beq: {} offset={}", .{ inst, offset });
|
||||
try emit.writeInstruction(Instruction.beq(b_type.rs1, b_type.rs2, @intCast(offset)));
|
||||
},
|
||||
.bne => {
|
||||
log.debug("bne: {} offset={}", .{ inst, offset });
|
||||
try emit.writeInstruction(Instruction.bne(b_type.rs1, b_type.rs2, @intCast(offset)));
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -215,9 +232,12 @@ fn mirJType(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
const j_type = emit.mir.instructions.items(.data)[inst].j_type;
|
||||
|
||||
const offset = @as(i64, @intCast(emit.code_offset_mapping.get(j_type.inst).?)) - @as(i64, @intCast(emit.code.items.len));
|
||||
|
||||
switch (tag) {
|
||||
.jal => {
|
||||
try emit.writeInstruction(Instruction.jal(j_type.rd, j_type.imm21));
|
||||
log.debug("jal: {} offset={}", .{ inst, offset });
|
||||
try emit.writeInstruction(Instruction.jal(j_type.rd, @intCast(offset)));
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -304,12 +324,8 @@ fn mirPsuedo(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
},
|
||||
|
||||
.j => {
|
||||
const target = data.inst;
|
||||
const offset: i12 = @intCast(emit.code.items.len);
|
||||
_ = target;
|
||||
|
||||
try emit.writeInstruction(Instruction.jal(.s0, offset));
|
||||
unreachable; // TODO: mirPsuedo j
|
||||
const offset = @as(i64, @intCast(emit.code_offset_mapping.get(data.inst).?)) - @as(i64, @intCast(emit.code.items.len));
|
||||
try emit.writeInstruction(Instruction.jal(.s0, @intCast(offset)));
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
@ -401,7 +417,51 @@ fn isLoad(tag: Mir.Inst.Tag) bool {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isBranch(tag: Mir.Inst.Tag) bool {
|
||||
return switch (tag) {
|
||||
.beq => true,
|
||||
.bne => true,
|
||||
.jal => true,
|
||||
.j => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
const data = emit.mir.instructions.items(.data)[inst];
|
||||
|
||||
switch (tag) {
|
||||
.bne,
|
||||
.beq,
|
||||
=> return data.b_type.inst,
|
||||
.jal => return data.j_type.inst,
|
||||
.j => return data.inst,
|
||||
else => std.debug.panic("branchTarget {s}", .{@tagName(tag)}),
|
||||
}
|
||||
}
|
||||
|
||||
fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
|
||||
return switch (tag) {
|
||||
.dbg_line,
|
||||
.dbg_epilogue_begin,
|
||||
.dbg_prologue_end,
|
||||
=> 0,
|
||||
|
||||
.psuedo_epilogue => 12, // 3 * 4
|
||||
.psuedo_prologue => 16, // 4 * 4
|
||||
|
||||
.abs => 12, // 3 * 4
|
||||
|
||||
else => 4,
|
||||
};
|
||||
}
|
||||
|
||||
fn lowerMir(emit: *Emit) !void {
|
||||
const comp = emit.bin_file.comp;
|
||||
const gpa = comp.gpa;
|
||||
const mir_tags = emit.mir.instructions.items(.tag);
|
||||
const mir_datas = emit.mir.instructions.items(.data);
|
||||
|
||||
@ -419,5 +479,19 @@ fn lowerMir(emit: *Emit) !void {
|
||||
mir_datas[inst].i_type.imm12 = -(casted_size - 12 - offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (isBranch(tag)) {
|
||||
const target_inst = emit.branchTarget(inst);
|
||||
try emit.code_offset_mapping.put(gpa, target_inst, 0);
|
||||
}
|
||||
}
|
||||
var current_code_offset: usize = 0;
|
||||
|
||||
for (0..mir_tags.len) |index| {
|
||||
const inst = @as(u32, @intCast(index));
|
||||
if (emit.code_offset_mapping.getPtr(inst)) |offset| {
|
||||
offset.* = current_code_offset;
|
||||
}
|
||||
current_code_offset += emit.instructionSize(inst);
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,14 +158,14 @@ pub const Inst = struct {
|
||||
b_type: struct {
|
||||
rs1: Register,
|
||||
rs2: Register,
|
||||
imm12: i13,
|
||||
inst: Inst.Index,
|
||||
},
|
||||
/// J-Type
|
||||
///
|
||||
/// Used by e.g. jal
|
||||
j_type: struct {
|
||||
rd: Register,
|
||||
imm21: i21,
|
||||
inst: Inst.Index,
|
||||
},
|
||||
/// U-Type
|
||||
///
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user