x86_64 backend

This commit is contained in:
David Gonzalez Martin 2023-09-22 09:16:39 -06:00
parent 48c3b5e224
commit a2535ac512

View File

@ -71,6 +71,14 @@ const Result = struct {
image.sections.text.index += 1; image.sections.text.index += 1;
} }
fn appendOnlyOpcodeSkipInstructionBytes(image: *Result, instruction: Instruction) void {
const instruction_descriptor = instruction_descriptors.get(instruction);
assert(instruction_descriptor.opcode_byte_count == instruction_descriptor.operand_offset);
image.appendCode(instruction_descriptor.getOpcode());
image.sections.text.index += instruction_descriptor.size - instruction_descriptor.opcode_byte_count;
}
fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType { fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType {
comptime { comptime {
assert(@typeInfo(FunctionType) == .Fn); assert(@typeInfo(FunctionType) == .Fn);
@ -81,15 +89,86 @@ const Result = struct {
} }
}; };
const SimpleRelocation = struct { const Instruction = enum {
source: u32, jmp_rel_8,
write_offset: u8,
instruction_len: u8, const Descriptor = struct {
size: u8, operands: [4]Operand,
operand_count: u3,
operand_offset: u5,
size: u8,
opcode: [2]u8,
opcode_byte_count: u8,
fn getOperands(descriptor: Descriptor) []const Operand {
return descriptor.operands[0..descriptor.operand_count];
}
fn getOpcode(descriptor: Descriptor) []const u8 {
return descriptor.opcode[0..descriptor.opcode_byte_count];
}
fn new(opcode_bytes: []const u8, operands: []const Operand) Descriptor {
// TODO: prefixes
var result = Descriptor{
.operands = undefined,
.operand_count = @intCast(operands.len),
.operand_offset = opcode_bytes.len,
.size = opcode_bytes.len,
.opcode = undefined,
.opcode_byte_count = opcode_bytes.len,
};
for (opcode_bytes, result.opcode[0..opcode_bytes.len]) |opcode_byte, *out_opcode| {
out_opcode.* = opcode_byte;
}
for (operands, result.operands[0..operands.len]) |operand, *out_operand| {
out_operand.* = operand;
result.size += operand.size;
}
return result;
}
};
const Operand = struct {
type: Type,
size: u8,
const Type = enum {
rel,
};
};
}; };
const RelocationManager = struct { const rel8 = Instruction.Operand{
in_function_relocations: data_structures.AutoHashMap(ir.BasicBlock.Index, ArrayList(SimpleRelocation)) = .{}, .type = .rel,
.size = @sizeOf(u8),
};
const instruction_descriptors = blk: {
var result = std.EnumArray(Instruction, Instruction.Descriptor).initUndefined();
result.getPtr(.jmp_rel_8).* = Instruction.Descriptor.new(&.{0xeb}, &[_]Instruction.Operand{rel8});
break :blk result;
};
const InstructionSelector = struct {
functions: ArrayList(Function),
const Function = struct {
instructions: ArrayList(Instruction) = .{},
block_byte_counts: ArrayList(u16),
block_offsets: ArrayList(u32),
byte_count: u32 = 0,
relocations: ArrayList(Relocation) = .{},
block_map: AutoHashMap(ir.BasicBlock.Index, u32) = .{},
const Relocation = struct {
instruction: Instruction,
source: u16,
destination: u16,
block_offset: u16,
};
};
}; };
pub fn get(comptime arch: std.Target.Cpu.Arch) type { pub fn get(comptime arch: std.Target.Cpu.Arch) type {
@ -98,136 +177,104 @@ pub fn get(comptime arch: std.Target.Cpu.Arch) type {
else => @compileError("Architecture not supported"), else => @compileError("Architecture not supported"),
}; };
_ = backend; _ = backend;
const Function = struct {
block_byte_counts: ArrayList(u16),
byte_count: u32 = 0,
relocations: ArrayList(Relocation) = .{},
block_map: AutoHashMap(ir.BasicBlock.Index, u32) = .{},
const Relocation = struct {
source: ir.BasicBlock.Index,
destination: ir.BasicBlock.Index,
offset_offset: u8,
preferred_instruction_len: u8,
};
};
const InstructionSelector = struct {
functions: ArrayList(Function),
};
_ = InstructionSelector;
return struct { return struct {
pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void { pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void {
_ = intermediate; var result = try Result.create();
_ = allocator; var function_iterator = intermediate.functions.iterator();
// var function_iterator = intermediate.functions.iterator(); var instruction_selector = InstructionSelector{
// var instruction_selector = InstructionSelector{ .functions = try ArrayList(InstructionSelector.Function).initCapacity(allocator, intermediate.functions.len),
// .functions = try ArrayList(Function).initCapacity(allocator, intermediate.functions.len), };
// };
// while (function_iterator.next()) |ir_function| { while (function_iterator.next()) |ir_function| {
// const function = instruction_selector.functions.addOneAssumeCapacity(); const function = instruction_selector.functions.addOneAssumeCapacity();
// function.* = .{ function.* = .{
// .block_byte_counts = try ArrayList(u16).initCapacity(allocator, ir_function.blocks.items.len), .block_byte_counts = try ArrayList(u16).initCapacity(allocator, ir_function.blocks.items.len),
// }; .block_offsets = try ArrayList(u32).initCapacity(allocator, ir_function.blocks.items.len),
// try function.block_map.ensureTotalCapacity(allocator, @intCast(ir_function.blocks.items.len)); };
// for (ir_function.blocks.items, 0..) |block_index, index| { try function.block_map.ensureTotalCapacity(allocator, @intCast(ir_function.blocks.items.len));
// function.block_map.putAssumeCapacity(allocator, block_index, @intCast(index)); for (ir_function.blocks.items, 0..) |block_index, index| {
// } function.block_map.putAssumeCapacity(block_index, @intCast(index));
// }
// for (ir_function.blocks.items) |block_index| {
// const block = intermediate.blocks.get(block_index); for (ir_function.blocks.items) |block_index| {
// var block_byte_count: u16 = 0; const block = intermediate.blocks.get(block_index);
// for (block.instructions.items) |instruction_index| { function.block_offsets.appendAssumeCapacity(function.byte_count);
// const instruction = intermediate.instructions.get(instruction_index).*; var block_byte_count: u16 = 0;
// switch (instruction) { for (block.instructions.items) |instruction_index| {
// .phi => unreachable, const instruction = intermediate.instructions.get(instruction_index).*;
// .ret => unreachable, switch (instruction) {
// .jump => { .phi => unreachable,
// block_byte_count += 2; .ret => unreachable,
// }, .jump => |jump_index| {
// } const jump = intermediate.jumps.get(jump_index);
// } const relocation = InstructionSelector.Function.Relocation{
// function.block_byte_counts.appendAssumeCapacity(block_byte_count); .instruction = .jmp_rel_8,
// } .source = @intCast(function.block_map.get(jump.source) orelse unreachable),
// } .destination = @intCast(function.block_map.get(jump.destination) orelse unreachable),
// unreachable; .block_offset = block_byte_count,
};
try function.relocations.append(allocator, relocation);
block_byte_count += instruction_descriptors.get(.jmp_rel_8).size;
try function.instructions.append(allocator, .jmp_rel_8);
},
}
}
function.block_byte_counts.appendAssumeCapacity(block_byte_count);
function.byte_count += block_byte_count;
}
}
for (instruction_selector.functions.items) |function| {
for (function.instructions.items) |instruction| switch (instruction) {
.jmp_rel_8 => result.appendOnlyOpcodeSkipInstructionBytes(instruction),
// else => unreachable,
};
}
for (instruction_selector.functions.items) |function| {
var fix_size: bool = false;
_ = fix_size;
for (function.relocations.items) |relocation| {
std.debug.print("RELOC: {}\n", .{relocation});
const source_block = relocation.source;
const destination_block = relocation.destination;
const source_offset = function.block_offsets.items[source_block];
const destination_offset = function.block_offsets.items[destination_block];
std.debug.print("Source offset: {}. Destination: {}\n", .{ source_offset, destination_offset });
const instruction_descriptor = instruction_descriptors.get(relocation.instruction);
const instruction_offset = source_offset + relocation.block_offset;
const really_source_offset = instruction_offset + instruction_descriptor.size;
const displacement = @as(i64, destination_offset) - @as(i64, really_source_offset);
const operands = instruction_descriptor.getOperands();
switch (operands.len) {
1 => switch (operands[0].size) {
@sizeOf(u8) => {
if (displacement >= std.math.minInt(i8) and displacement <= std.math.maxInt(i8)) {
const writer_index = instruction_offset + instruction_descriptor.operand_offset;
std.debug.print("Instruction offset: {}. Operand offset: {}. Writer index: {}. displacement: {}\n", .{ instruction_offset, instruction_descriptor.operand_offset, writer_index, displacement });
result.sections.text.content[writer_index] = @bitCast(@as(i8, @intCast(displacement)));
} else {
unreachable;
}
},
else => unreachable,
},
else => unreachable,
}
}
}
const text_section = result.sections.text.content[0..result.sections.text.index];
for (text_section) |byte| {
std.debug.print("0x{x}\n", .{byte});
}
} }
}; };
} }
pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void {
_ = allocator;
var result = try Result.create();
_ = result;
var relocation_manager = RelocationManager{};
_ = relocation_manager;
var function_iterator = intermediate.functions.iterator();
_ = function_iterator;
// while (function_iterator.next()) |function| {
// defer relocation_manager.in_function_relocations.clearRetainingCapacity();
//
// for (function.blocks.items) |block_index| {
// if (relocation_manager.in_function_relocations.getPtr(block_index)) |relocations| {
// const current_offset: i64 = @intCast(result.sections.text.index);
// _ = current_offset;
// for (relocations.items) |relocation| switch (relocation.size) {
// inline @sizeOf(u8), @sizeOf(u32) => |relocation_size| {
// const Elem = switch (relocation_size) {
// @sizeOf(u8) => u8,
// @sizeOf(u32) => u32,
// else => unreachable,
// };
// const Ptr = *align(1) Elem;
// _ = Ptr;
// const relocation_slice = result.sections.text.content[relocation.source + relocation.write_offset ..][0..relocation_size];
// _ = relocation_slice;
// // std.math.cast(
// //
// unreachable;
// },
// else => unreachable,
// };
// // const ptr: *align(1) u32 = @ptrCast(&result.sections.text[relocation_source..][0..@sizeOf(u32)]);
// // ptr.* =
// // try relocations.append(allocator, @intCast(result.sections.text[));
// }
//
// const block = intermediate.blocks.get(block_index);
//
// for (block.instructions.items) |instruction_index| {
// const instruction = intermediate.instructions.get(instruction_index);
// switch (instruction.*) {
// .jump => |jump_index| {
// const jump = intermediate.jumps.get(jump_index);
// assert(@as(u32, @bitCast(jump.source)) == @as(u32, @bitCast(block_index)));
// const relocation_index = result.sections.text.index + 1;
// if (@as(u32, @bitCast(jump.destination)) <= @as(u32, @bitCast(jump.source))) {
// unreachable;
// } else {
// result.appendCode(&(.{jmp_rel_32} ++ .{0} ** @sizeOf(u32)));
// const lookup_result = try relocation_manager.in_function_relocations.getOrPut(allocator, jump.destination);
// if (!lookup_result.found_existing) {
// lookup_result.value_ptr.* = .{};
// }
//
// try lookup_result.value_ptr.append(allocator, .{
// .source = @intCast(relocation_index),
// .write_offset = @sizeOf(u8),
// .instruction_len = @sizeOf(u8) + @sizeOf(u32),
// .size = @sizeOf(u32),
// });
// }
// },
// else => |t| @panic(@tagName(t)),
// }
// }
// }
// unreachable;
// }
// unreachable;
}
const Rex = enum(u8) { const Rex = enum(u8) {
b = upper_4_bits | (1 << 0), b = upper_4_bits | (1 << 0),
x = upper_4_bits | (1 << 1), x = upper_4_bits | (1 << 1),