mirror of
https://github.com/ziglang/zig.git
synced 2026-01-10 09:25:11 +00:00
stage2 RISCV64: introduce MIR
This commit is contained in:
parent
2551946a51
commit
0fb26b0369
@ -5,6 +5,8 @@ const math = std.math;
|
||||
const assert = std.debug.assert;
|
||||
const Air = @import("../../Air.zig");
|
||||
const Zir = @import("../../Zir.zig");
|
||||
const Mir = @import("Mir.zig");
|
||||
const Emit = @import("Emit.zig");
|
||||
const Liveness = @import("../../Liveness.zig");
|
||||
const Type = @import("../../type.zig").Type;
|
||||
const Value = @import("../../value.zig").Value;
|
||||
@ -47,13 +49,14 @@ arg_index: usize,
|
||||
src_loc: Module.SrcLoc,
|
||||
stack_align: u32,
|
||||
|
||||
prev_di_line: u32,
|
||||
prev_di_column: u32,
|
||||
/// MIR Instructions
|
||||
mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
|
||||
/// MIR extra data
|
||||
mir_extra: std.ArrayListUnmanaged(u32) = .{},
|
||||
|
||||
/// Byte offset within the source file of the ending curly.
|
||||
end_di_line: u32,
|
||||
end_di_column: u32,
|
||||
/// Relative to the beginning of `code`.
|
||||
prev_di_pc: usize,
|
||||
|
||||
/// The value is an offset into the `Function` `code` from the beginning.
|
||||
/// To perform the reloc, write 32-bit signed little-endian integer
|
||||
@ -276,9 +279,6 @@ pub fn generate(
|
||||
.branch_stack = &branch_stack,
|
||||
.src_loc = src_loc,
|
||||
.stack_align = undefined,
|
||||
.prev_di_pc = 0,
|
||||
.prev_di_line = module_fn.lbrace_line,
|
||||
.prev_di_column = module_fn.lbrace_column,
|
||||
.end_di_line = module_fn.rbrace_line,
|
||||
.end_di_column = module_fn.rbrace_column,
|
||||
};
|
||||
@ -302,6 +302,30 @@ pub fn generate(
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
var mir = Mir{
|
||||
.instructions = function.mir_instructions.toOwnedSlice(),
|
||||
.extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
|
||||
};
|
||||
defer mir.deinit(bin_file.allocator);
|
||||
|
||||
var emit = Emit{
|
||||
.mir = mir,
|
||||
.bin_file = bin_file,
|
||||
.debug_output = debug_output,
|
||||
.target = &bin_file.options.target,
|
||||
.src_loc = src_loc,
|
||||
.code = code,
|
||||
.prev_di_pc = 0,
|
||||
.prev_di_line = module_fn.lbrace_line,
|
||||
.prev_di_column = module_fn.lbrace_column,
|
||||
};
|
||||
defer emit.deinit();
|
||||
|
||||
emit.emitMir() catch |err| switch (err) {
|
||||
error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
if (function.err_msg) |em| {
|
||||
return FnResult{ .fail = em };
|
||||
} else {
|
||||
@ -309,13 +333,56 @@ pub fn generate(
|
||||
}
|
||||
}
|
||||
|
||||
fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
|
||||
const gpa = self.gpa;
|
||||
|
||||
try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
|
||||
|
||||
const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);
|
||||
self.mir_instructions.appendAssumeCapacity(inst);
|
||||
return result_index;
|
||||
}
|
||||
|
||||
pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 {
|
||||
const fields = std.meta.fields(@TypeOf(extra));
|
||||
try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len);
|
||||
return self.addExtraAssumeCapacity(extra);
|
||||
}
|
||||
|
||||
pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
|
||||
const fields = std.meta.fields(@TypeOf(extra));
|
||||
const result = @intCast(u32, self.mir_extra.items.len);
|
||||
inline for (fields) |field| {
|
||||
self.mir_extra.appendAssumeCapacity(switch (field.field_type) {
|
||||
u32 => @field(extra, field.name),
|
||||
i32 => @bitCast(u32, @field(extra, field.name)),
|
||||
else => @compileError("bad field type"),
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn gen(self: *Self) !void {
|
||||
try self.dbgSetPrologueEnd();
|
||||
_ = try self.addInst(.{
|
||||
.tag = .dbg_prologue_end,
|
||||
.data = .{ .nop = {} },
|
||||
});
|
||||
|
||||
try self.genBody(self.air.getMainBody());
|
||||
try self.dbgSetEpilogueBegin();
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = .dbg_epilogue_begin,
|
||||
.data = .{ .nop = {} },
|
||||
});
|
||||
|
||||
// Drop them off at the rbrace.
|
||||
try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);
|
||||
_ = try self.addInst(.{
|
||||
.tag = .dbg_line,
|
||||
.data = .{ .dbg_line_column = .{
|
||||
.line = self.end_di_line,
|
||||
.column = self.end_di_column,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
@ -456,79 +523,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
}
|
||||
}
|
||||
|
||||
fn dbgSetPrologueEnd(self: *Self) InnerError!void {
|
||||
switch (self.debug_output) {
|
||||
.dwarf => |dbg_out| {
|
||||
try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
|
||||
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
|
||||
switch (self.debug_output) {
|
||||
.dwarf => |dbg_out| {
|
||||
try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
|
||||
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {
|
||||
const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
|
||||
const delta_pc: usize = self.code.items.len - self.prev_di_pc;
|
||||
switch (self.debug_output) {
|
||||
.dwarf => |dbg_out| {
|
||||
// TODO Look into using the DWARF special opcodes to compress this data.
|
||||
// It lets you emit single-byte opcodes that add different numbers to
|
||||
// both the PC and the line number at the same time.
|
||||
try dbg_out.dbg_line.ensureUnusedCapacity(11);
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
|
||||
leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
|
||||
if (delta_line != 0) {
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
|
||||
leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
|
||||
}
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
|
||||
self.prev_di_pc = self.code.items.len;
|
||||
self.prev_di_line = line;
|
||||
self.prev_di_column = column;
|
||||
self.prev_di_pc = self.code.items.len;
|
||||
},
|
||||
.plan9 => |dbg_out| {
|
||||
if (delta_pc <= 0) return; // only do this when the pc changes
|
||||
// we have already checked the target in the linker to make sure it is compatable
|
||||
const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
|
||||
|
||||
// increasing the line number
|
||||
try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
|
||||
// increasing the pc
|
||||
const d_pc_p9 = @intCast(i64, delta_pc) - quant;
|
||||
if (d_pc_p9 > 0) {
|
||||
// minus one because if its the last one, we want to leave space to change the line which is one quanta
|
||||
try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
|
||||
if (dbg_out.pcop_change_index.*) |pci|
|
||||
dbg_out.dbg_line.items[pci] += 1;
|
||||
dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
|
||||
} else if (d_pc_p9 == 0) {
|
||||
// we don't need to do anything, because adding the quant does it for us
|
||||
} else unreachable;
|
||||
if (dbg_out.start_line.* == null)
|
||||
dbg_out.start_line.* = self.prev_di_line;
|
||||
dbg_out.end_line.* = line;
|
||||
// only do this if the pc changed
|
||||
self.prev_di_line = line;
|
||||
self.prev_di_column = column;
|
||||
self.prev_di_pc = self.code.items.len;
|
||||
},
|
||||
.none => {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts there is already capacity to insert into top branch inst_table.
|
||||
fn processDeath(self: *Self, inst: Air.Inst.Index) void {
|
||||
const air_tags = self.air.instructions.items(.tag);
|
||||
@ -1291,7 +1285,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
|
||||
}
|
||||
|
||||
fn airBreakpoint(self: *Self) !void {
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());
|
||||
_ = try self.addInst(.{
|
||||
.tag = .ebreak,
|
||||
.data = .{ .nop = {} },
|
||||
});
|
||||
return self.finishAirBookkeeping();
|
||||
}
|
||||
|
||||
@ -1330,7 +1327,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
|
||||
unreachable;
|
||||
|
||||
try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
|
||||
_ = try self.addInst(.{
|
||||
.tag = .jalr,
|
||||
.data = .{ .i_type = .{
|
||||
.rd = .ra,
|
||||
.rs1 = .ra,
|
||||
.imm12 = 0,
|
||||
} },
|
||||
});
|
||||
} else if (func_value.castTag(.extern_fn)) |_| {
|
||||
return self.fail("TODO implement calling extern functions", .{});
|
||||
} else {
|
||||
@ -1375,7 +1379,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
|
||||
fn ret(self: *Self, mcv: MCValue) !void {
|
||||
const ret_ty = self.fn_type.fnReturnType();
|
||||
try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
|
||||
_ = try self.addInst(.{
|
||||
.tag = .jalr,
|
||||
.data = .{ .i_type = .{
|
||||
.rd = .zero,
|
||||
.rs1 = .ra,
|
||||
.imm12 = 0,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
fn airRet(self: *Self, inst: Air.Inst.Index) !void {
|
||||
@ -1414,7 +1425,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
|
||||
|
||||
fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
|
||||
try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);
|
||||
|
||||
_ = try self.addInst(.{
|
||||
.tag = .dbg_line,
|
||||
.data = .{ .dbg_line_column = .{
|
||||
.line = dbg_stmt.line,
|
||||
.column = dbg_stmt.column,
|
||||
} },
|
||||
});
|
||||
|
||||
return self.finishAirBookkeeping();
|
||||
}
|
||||
|
||||
@ -1706,7 +1725,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
}
|
||||
|
||||
if (mem.eql(u8, asm_source, "ecall")) {
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
|
||||
_ = try self.addInst(.{
|
||||
.tag = .ecall,
|
||||
.data = .{ .nop = {} },
|
||||
});
|
||||
} else {
|
||||
return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
|
||||
}
|
||||
@ -1785,29 +1807,54 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||
.immediate => |unsigned_x| {
|
||||
const x = @bitCast(i64, unsigned_x);
|
||||
if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, .zero, @truncate(i12, x)).toU32());
|
||||
return;
|
||||
}
|
||||
if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .addi,
|
||||
.data = .{ .i_type = .{
|
||||
.rd = reg,
|
||||
.rs1 = .zero,
|
||||
.imm12 = @intCast(i12, x),
|
||||
} },
|
||||
});
|
||||
} else if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
|
||||
const lo12 = @truncate(i12, x);
|
||||
const carry: i32 = if (lo12 < 0) 1 else 0;
|
||||
const hi20 = @truncate(i20, (x >> 12) +% carry);
|
||||
|
||||
// TODO: add test case for 32-bit immediate
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lui(reg, hi20).toU32());
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, reg, lo12).toU32());
|
||||
return;
|
||||
_ = try self.addInst(.{
|
||||
.tag = .lui,
|
||||
.data = .{ .u_type = .{
|
||||
.rd = reg,
|
||||
.imm20 = hi20,
|
||||
} },
|
||||
});
|
||||
_ = try self.addInst(.{
|
||||
.tag = .addi,
|
||||
.data = .{ .i_type = .{
|
||||
.rd = reg,
|
||||
.rs1 = reg,
|
||||
.imm12 = lo12,
|
||||
} },
|
||||
});
|
||||
} else {
|
||||
// li rd, immediate
|
||||
// "Myriad sequences"
|
||||
return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
|
||||
}
|
||||
// li rd, immediate
|
||||
// "Myriad sequences"
|
||||
return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
|
||||
},
|
||||
.memory => |addr| {
|
||||
// The value is in memory at a hard-coded address.
|
||||
// If the type is a pointer, it means the pointer address is at this memory location.
|
||||
try self.genSetReg(ty, reg, .{ .immediate = addr });
|
||||
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
|
||||
_ = try self.addInst(.{
|
||||
.tag = .ld,
|
||||
.data = .{ .i_type = .{
|
||||
.rd = reg,
|
||||
.rs1 = reg,
|
||||
.imm12 = 0,
|
||||
} },
|
||||
});
|
||||
// LOAD imm=[i12 offset = 0], rs1 =
|
||||
|
||||
// return self.fail("TODO implement genSetReg memory for riscv64");
|
||||
|
||||
192
src/arch/riscv64/Emit.zig
Normal file
192
src/arch/riscv64/Emit.zig
Normal file
@ -0,0 +1,192 @@
|
||||
//! This file contains the functionality for lowering AArch64 MIR into
|
||||
//! machine code
|
||||
|
||||
const Emit = @This();
|
||||
const std = @import("std");
|
||||
const math = std.math;
|
||||
const Mir = @import("Mir.zig");
|
||||
const bits = @import("bits.zig");
|
||||
const link = @import("../../link.zig");
|
||||
const Module = @import("../../Module.zig");
|
||||
const ErrorMsg = Module.ErrorMsg;
|
||||
const assert = std.debug.assert;
|
||||
const DW = std.dwarf;
|
||||
const leb128 = std.leb;
|
||||
const Instruction = bits.Instruction;
|
||||
const Register = bits.Register;
|
||||
const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
|
||||
|
||||
mir: Mir,
|
||||
bin_file: *link.File,
|
||||
debug_output: DebugInfoOutput,
|
||||
target: *const std.Target,
|
||||
err_msg: ?*ErrorMsg = null,
|
||||
src_loc: Module.SrcLoc,
|
||||
code: *std.ArrayList(u8),
|
||||
|
||||
prev_di_line: u32,
|
||||
prev_di_column: u32,
|
||||
/// Relative to the beginning of `code`.
|
||||
prev_di_pc: usize,
|
||||
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
EmitFail,
|
||||
};
|
||||
|
||||
pub fn emitMir(
|
||||
emit: *Emit,
|
||||
) InnerError!void {
|
||||
const mir_tags = emit.mir.instructions.items(.tag);
|
||||
|
||||
// Emit machine code
|
||||
for (mir_tags) |tag, index| {
|
||||
const inst = @intCast(u32, index);
|
||||
switch (tag) {
|
||||
.addi => try emit.mirIType(inst),
|
||||
.jalr => try emit.mirIType(inst),
|
||||
.ld => try emit.mirIType(inst),
|
||||
|
||||
.ebreak => try emit.mirSystem(inst),
|
||||
.ecall => try emit.mirSystem(inst),
|
||||
|
||||
.dbg_line => try emit.mirDbgLine(inst),
|
||||
|
||||
.dbg_prologue_end => try emit.mirDebugPrologueEnd(),
|
||||
.dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
|
||||
|
||||
.lui => try emit.mirUType(inst),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(emit: *Emit) void {
|
||||
emit.* = undefined;
|
||||
}
|
||||
|
||||
fn writeInstruction(emit: *Emit, instruction: Instruction) !void {
|
||||
const endian = emit.target.cpu.arch.endian();
|
||||
std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian);
|
||||
}
|
||||
|
||||
fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
|
||||
@setCold(true);
|
||||
assert(emit.err_msg == null);
|
||||
emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
|
||||
return error.EmitFail;
|
||||
}
|
||||
|
||||
fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
|
||||
const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
|
||||
const delta_pc: usize = self.code.items.len - self.prev_di_pc;
|
||||
switch (self.debug_output) {
|
||||
.dwarf => |dbg_out| {
|
||||
// TODO Look into using the DWARF special opcodes to compress this data.
|
||||
// It lets you emit single-byte opcodes that add different numbers to
|
||||
// both the PC and the line number at the same time.
|
||||
try dbg_out.dbg_line.ensureUnusedCapacity(11);
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
|
||||
leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
|
||||
if (delta_line != 0) {
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
|
||||
leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
|
||||
}
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
|
||||
self.prev_di_pc = self.code.items.len;
|
||||
self.prev_di_line = line;
|
||||
self.prev_di_column = column;
|
||||
self.prev_di_pc = self.code.items.len;
|
||||
},
|
||||
.plan9 => |dbg_out| {
|
||||
if (delta_pc <= 0) return; // only do this when the pc changes
|
||||
// we have already checked the target in the linker to make sure it is compatable
|
||||
const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
|
||||
|
||||
// increasing the line number
|
||||
try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
|
||||
// increasing the pc
|
||||
const d_pc_p9 = @intCast(i64, delta_pc) - quant;
|
||||
if (d_pc_p9 > 0) {
|
||||
// minus one because if its the last one, we want to leave space to change the line which is one quanta
|
||||
try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
|
||||
if (dbg_out.pcop_change_index.*) |pci|
|
||||
dbg_out.dbg_line.items[pci] += 1;
|
||||
dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
|
||||
} else if (d_pc_p9 == 0) {
|
||||
// we don't need to do anything, because adding the quant does it for us
|
||||
} else unreachable;
|
||||
if (dbg_out.start_line.* == null)
|
||||
dbg_out.start_line.* = self.prev_di_line;
|
||||
dbg_out.end_line.* = line;
|
||||
// only do this if the pc changed
|
||||
self.prev_di_line = line;
|
||||
self.prev_di_column = column;
|
||||
self.prev_di_pc = self.code.items.len;
|
||||
},
|
||||
.none => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
const i_type = emit.mir.instructions.items(.data)[inst].i_type;
|
||||
|
||||
switch (tag) {
|
||||
.addi => try emit.writeInstruction(Instruction.addi(i_type.rd, i_type.rs1, i_type.imm12)),
|
||||
.jalr => try emit.writeInstruction(Instruction.jalr(i_type.rd, i_type.imm12, i_type.rs1)),
|
||||
.ld => try emit.writeInstruction(Instruction.ld(i_type.rd, i_type.imm12, i_type.rs1)),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
|
||||
switch (tag) {
|
||||
.ebreak => try emit.writeInstruction(Instruction.ebreak),
|
||||
.ecall => try emit.writeInstruction(Instruction.ecall),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
|
||||
|
||||
switch (tag) {
|
||||
.dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn mirDebugPrologueEnd(self: *Emit) !void {
|
||||
switch (self.debug_output) {
|
||||
.dwarf => |dbg_out| {
|
||||
try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
|
||||
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn mirDebugEpilogueBegin(self: *Emit) !void {
|
||||
switch (self.debug_output) {
|
||||
.dwarf => |dbg_out| {
|
||||
try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
|
||||
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn mirUType(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||
const u_type = emit.mir.instructions.items(.data)[inst].u_type;
|
||||
|
||||
switch (tag) {
|
||||
.lui => try emit.writeInstruction(Instruction.lui(u_type.rd, u_type.imm20)),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
125
src/arch/riscv64/Mir.zig
Normal file
125
src/arch/riscv64/Mir.zig
Normal file
@ -0,0 +1,125 @@
|
||||
//! Machine Intermediate Representation.
|
||||
//! This data is produced by RISCV64 Codegen or RISCV64 assembly parsing
|
||||
//! These instructions have a 1:1 correspondence with machine code instructions
|
||||
//! for the target. MIR can be lowered to source-annotated textual assembly code
|
||||
//! instructions, or it can be lowered to machine code.
|
||||
//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
|
||||
//! so that, for example, the smaller encodings of jump instructions can be used.
|
||||
|
||||
const Mir = @This();
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const bits = @import("bits.zig");
|
||||
const Register = bits.Register;
|
||||
|
||||
instructions: std.MultiArrayList(Inst).Slice,
|
||||
/// The meaning of this data is determined by `Inst.Tag` value.
|
||||
extra: []const u32,
|
||||
|
||||
pub const Inst = struct {
|
||||
tag: Tag,
|
||||
/// The meaning of this depends on `tag`.
|
||||
data: Data,
|
||||
|
||||
pub const Tag = enum(u16) {
|
||||
addi,
|
||||
/// Pseudo-instruction: End of prologue
|
||||
dbg_prologue_end,
|
||||
/// Pseudo-instruction: Beginning of epilogue
|
||||
dbg_epilogue_begin,
|
||||
/// Pseudo-instruction: Update debug line
|
||||
dbg_line,
|
||||
ebreak,
|
||||
ecall,
|
||||
jalr,
|
||||
ld,
|
||||
lui,
|
||||
};
|
||||
|
||||
/// The position of an MIR instruction within the `Mir` instructions array.
|
||||
pub const Index = u32;
|
||||
|
||||
/// All instructions have a 4-byte payload, which is contained within
|
||||
/// this union. `Tag` determines which union field is active, as well as
|
||||
/// how to interpret the data within.
|
||||
pub const Data = union {
|
||||
/// No additional data
|
||||
///
|
||||
/// Used by e.g. ebreak
|
||||
nop: void,
|
||||
/// Another instruction.
|
||||
///
|
||||
/// Used by e.g. b
|
||||
inst: Index,
|
||||
/// A 16-bit immediate value.
|
||||
///
|
||||
/// Used by e.g. svc
|
||||
imm16: u16,
|
||||
/// Index into `extra`. Meaning of what can be found there is context-dependent.
|
||||
///
|
||||
/// Used by e.g. load_memory
|
||||
payload: u32,
|
||||
/// A register
|
||||
///
|
||||
/// Used by e.g. blr
|
||||
reg: Register,
|
||||
/// I-Type
|
||||
///
|
||||
/// Used by e.g. jalr
|
||||
i_type: struct {
|
||||
rd: Register,
|
||||
rs1: Register,
|
||||
imm12: i12,
|
||||
},
|
||||
/// U-Type
|
||||
///
|
||||
/// Used by e.g. lui
|
||||
u_type: struct {
|
||||
rd: Register,
|
||||
imm20: i20,
|
||||
},
|
||||
/// Debug info: line and column
|
||||
///
|
||||
/// Used by e.g. dbg_line
|
||||
dbg_line_column: struct {
|
||||
line: u32,
|
||||
column: u32,
|
||||
},
|
||||
};
|
||||
|
||||
// Make sure we don't accidentally make instructions bigger than expected.
|
||||
// Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
|
||||
// comptime {
|
||||
// if (builtin.mode != .Debug) {
|
||||
// assert(@sizeOf(Inst) == 8);
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
pub fn deinit(mir: *Mir, gpa: *std.mem.Allocator) void {
|
||||
mir.instructions.deinit(gpa);
|
||||
gpa.free(mir.extra);
|
||||
mir.* = undefined;
|
||||
}
|
||||
|
||||
/// Returns the requested data, as well as the new index which is at the start of the
|
||||
/// trailers for the object.
|
||||
pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } {
|
||||
const fields = std.meta.fields(T);
|
||||
var i: usize = index;
|
||||
var result: T = undefined;
|
||||
inline for (fields) |field| {
|
||||
@field(result, field.name) = switch (field.field_type) {
|
||||
u32 => mir.extra[i],
|
||||
i32 => @bitCast(i32, mir.extra[i]),
|
||||
else => @compileError("bad field type"),
|
||||
};
|
||||
i += 1;
|
||||
}
|
||||
return .{
|
||||
.data = result,
|
||||
.end = i,
|
||||
};
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user