add encoding and elf linking

This commit is contained in:
David Gonzalez Martin 2023-11-09 23:20:34 -06:00
parent 30baa0b53d
commit 22243d296c
6 changed files with 880 additions and 283 deletions

View File

@ -695,7 +695,7 @@ pub const Module = struct {
casts: BlockList(Cast) = .{},
string_literal_types: data_structures.AutoArrayHashMap(u32, Type.Index) = .{},
array_types: data_structures.AutoArrayHashMap(Array, Type.Index) = .{},
entry_point: ?u32 = null,
entry_point: Function.Index = Function.Index.invalid,
pub const Descriptor = struct {
main_package_path: []const u8,

View File

@ -7,58 +7,96 @@ const Allocator = data_structures.Allocator;
const ArrayList = data_structures.ArrayList;
const emit = @import("emit.zig");
const page_size = 0x1000;
pub const Writer = struct {
bytes: ArrayList(u8),
allocator: Allocator,
pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, page_size) {
var file = try std.ArrayListAlignedUnmanaged(u8, 0x1000).initCapacity(image.allocator, 0x100000);
_ = try image.insertSection(0, .{
.name = "",
.size = page_size,
.alignment = page_size,
.flags = .{
.read = true,
.write = false,
.execute = false,
},
.type = .loadable_program,
});
pub fn init(allocator: Allocator) !Writer {
return .{
.bytes = try ArrayList(u8).initCapacity(allocator, 0x10000),
.allocator = allocator,
};
}
const symbol_table_index = try image.addSection(.{
.name = ".symtab",
.size = page_size,
.alignment = @alignOf(SymbolTable.Entry),
.flags = .{
.read = false,
.write = false,
.execute = false,
},
.type = .symbol_table,
});
const string_table_index = try image.addSection(.{
.name = ".strtab",
.size = page_size,
.alignment = 1,
.flags = .{
.read = false,
.write = false,
.execute = false,
},
.type = .string_table,
});
const section_header_string_table_index = try image.addSection(.{
.name = ".shstrtab",
.size = page_size,
.alignment = 1,
.flags = .{
.read = false,
.write = false,
.execute = false,
},
.type = .string_table,
});
pub fn getHeader(writer: *Writer) *Header {
return @ptrCast(@alignCast(writer.bytes.items.ptr));
}
const base_virtual_address = 0x400000;
const text_section_index = 1;
pub fn writeToMemory(writer: *Writer, image: *const emit.Result) !void {
const section_fields = @typeInfo(@TypeOf(image.sections)).Struct.fields;
const section_count = blk: {
var result: u16 = 0;
inline for (section_fields) |section_field| {
const section_size = @field(image.sections, section_field.name).index;
result += @intFromBool(section_size > 0);
const program_header_count = blk: {
var result: usize = 0;
for (image.sections.items) |section| {
result += @intFromBool(switch (section.type) {
.null => false,
.loadable_program => true,
.string_table => false,
.symbol_table => false,
});
}
break :blk result;
};
const program_header_count = section_count;
const program_start_offset = @sizeOf(Header) + program_header_count * @sizeOf(ProgramHeader);
var symbol_name_offset: u32 = 0;
var section_offsets: [section_fields.len]u32 = undefined;
image.writeToSection(symbol_table_index, std.mem.asBytes(&SymbolTable.Entry{
.name_offset = symbol_name_offset,
.information = 0,
.other = 0,
.section_header_index = 0,
.value = 0,
.size = 0,
}));
const program_end_offset = blk: {
var result: u32 = program_start_offset;
inline for (section_fields, 0..) |section_field, section_index| {
const section = &@field(image.sections, section_field.name);
if (section.index > 0) {
const section_offset = std.mem.alignForward(u32, result, section.alignment);
section_offsets[section_index] = section_offset;
result = std.mem.alignForward(u32, section_offset + @as(u32, @intCast(section.index)), section.alignment);
}
image.writeToSection(string_table_index, "");
image.writeByteToSection(string_table_index, 0);
symbol_name_offset += 1;
for (image.sections.items) |section| {
image.writeToSection(section_header_string_table_index, section.name);
image.writeByteToSection(section_header_string_table_index, 0);
}
break :blk result;
};
{
var program_segment_offset: usize = 0;
const elf_file_end_offset = program_end_offset + @sizeOf(SectionHeader) * section_count;
try writer.bytes.resize(writer.allocator, elf_file_end_offset);
const base_address = 0x200000;
writer.getHeader().* = Header{
image.writeToSection(0, std.mem.asBytes(&Header{
.endianness = .little,
.machine = switch (image.target.cpu.arch) {
.x86_64 => .AMD64,
@ -68,80 +106,135 @@ pub const Writer = struct {
.linux => .systemv,
else => unreachable,
},
.entry = base_address + section_offsets[0] + image.entry_point,
.section_header_offset = program_end_offset,
.program_header_count = program_header_count,
.section_header_count = section_count,
.name_section_header_index = 0,
};
.entry = 0,
.section_header_offset = 0,
.program_header_count = @intCast(program_header_count),
.section_header_count = @intCast(image.sections.items.len),
.section_header_string_table_index = @intCast(section_header_string_table_index),
}));
var program_header_offset: usize = @sizeOf(Header);
var section_header_offset = program_end_offset;
inline for (section_fields, section_offsets) |section_field, section_offset| {
const section_name = section_field.name;
const section = &@field(image.sections, section_name);
if (section.index > 0) {
const program_header: *ProgramHeader = @ptrCast(@alignCast(writer.bytes.items[program_header_offset..].ptr));
program_header.* = .{
.type = .load,
.flags = .{
.executable = equal(u8, section_name, "text"),
.writable = equal(u8, section_name, "data"),
.readable = true,
},
.offset = 0,
.virtual_address = base_address,
.physical_address = base_address,
.size_in_file = section.index,
.size_in_memory = section.index,
.alignment = 0,
for (image.sections.items, 0..) |section, section_index| {
switch (section.type) {
.loadable_program => {
program_segment_offset = std.mem.alignForward(usize, program_segment_offset, section.alignment);
const virtual_address = base_virtual_address + program_segment_offset;
const program_segment_size = switch (section_index) {
0 => @sizeOf(Header) + @sizeOf(ProgramHeader) * program_header_count,
else => section.index,
};
image.writeToSection(0, std.mem.asBytes(&ProgramHeader{
.type = .load,
.flags = ProgramHeader.Flags{
.executable = section.flags.execute,
.writable = section.flags.write,
.readable = section.flags.read,
},
.offset = program_segment_offset,
.virtual_address = virtual_address,
.physical_address = virtual_address,
.size_in_file = program_segment_size,
.size_in_memory = program_segment_size,
.alignment = section.alignment,
}));
program_segment_offset += program_segment_size;
},
.null,
.string_table,
.symbol_table,
=> {},
}
}
}
{
var section_offset: usize = 0;
var section_headers = try ArrayList(SectionHeader).initCapacity(image.allocator, image.sections.items.len);
var section_name_offset: u32 = 0;
for (image.sections.items, 0..) |section, section_i| {
section_offset = std.mem.alignForward(usize, section_offset, section.alignment);
const virtual_address = base_virtual_address + section_offset;
for (section.symbol_table.keys(), section.symbol_table.values()) |symbol_name, symbol_offset| {
const symbol_address = virtual_address + symbol_offset;
image.writeToSection(symbol_table_index, std.mem.asBytes(&SymbolTable.Entry{
.name_offset = symbol_name_offset,
.information = 0x10,
.other = 0,
.section_header_index = @intCast(section_i),
.value = symbol_address,
.size = 0,
}));
image.writeToSection(string_table_index, symbol_name);
image.writeByteToSection(string_table_index, 0);
symbol_name_offset += @intCast(symbol_name.len + 1);
}
const source = section.content[0..section.index];
const destination = writer.bytes.items[section_offset..][0..source.len];
@memcpy(destination, source);
file.items.len = section_offset + source.len;
try file.replaceRange(image.allocator, section_offset, source.len, source);
const section_header: *SectionHeader = @ptrCast(@alignCast(writer.bytes.items[section_header_offset..].ptr));
section_header.* = .{
.name_offset = 0,
.type = .program_data,
section_headers.appendAssumeCapacity(SectionHeader{
.name_offset = section_name_offset,
.type = switch (section_i) {
0 => .null,
else => switch (section.type) {
.loadable_program => .program_data,
.string_table => .string_table,
.symbol_table => .symbol_table,
.null => .null,
},
},
.flags = .{
.alloc = equal(u8, section_name, "text"),
.executable = equal(u8, section_name, "text"),
.writable = equal(u8, section_name, "data"),
.alloc = true,
.executable = section.flags.execute,
.writable = section.flags.write,
},
.address = base_address + section_offset,
.offset = section_offset,
.virtual_address = virtual_address,
.file_offset = section_offset,
.size = section.index,
.link = 0,
.info = 0,
.alignment = 0,
.entry_size = 0,
};
}
}
}
pub fn writeToFile(writer: *const Writer, file_path: []const u8) !void {
std.debug.print("Writing file to {s}\n", .{file_path});
const flags = switch (@import("builtin").os.tag) {
.windows => .{},
else => .{
.mode = 0o777,
.link = switch (section.type) {
.symbol_table => @intCast(string_table_index),
else => 0,
},
};
const file_descriptor = try std.fs.cwd().createFile(file_path, flags);
try file_descriptor.writeAll(writer.bytes.items);
file_descriptor.close();
.info = switch (section.type) {
.symbol_table => 1,
else => 0,
},
.alignment = 0,
.entry_size = switch (section.type) {
.symbol_table => @sizeOf(SymbolTable.Entry),
else => 0,
},
});
section_offset += section.index;
section_name_offset += @intCast(section.name.len + 1);
}
pub fn writeToFileAbsolute(writer: *const Writer, absolute_file_path: []const u8) !void {
const file = try std.fs.createFileAbsolute(absolute_file_path, .{});
defer file.close();
try file.writeAll(writer.bytes.items);
}
const section_header_offset = std.mem.alignForward(usize, section_offset, @alignOf(SectionHeader));
const section_header_bytes = std.mem.sliceAsBytes(section_headers.items);
try file.ensureTotalCapacity(image.allocator, section_header_offset + section_header_bytes.len);
file.items.len = section_header_offset + section_header_bytes.len;
try file.replaceRange(image.allocator, section_header_offset, section_header_bytes.len, section_header_bytes);
const _start_offset = blk: {
const entry_offset = image.sections.items[text_section_index].symbol_table.values()[image.entry_point];
const text_section_virtual_address = section_headers.items[text_section_index].virtual_address;
break :blk text_section_virtual_address + entry_offset;
};
const header: *Header = @ptrCast(file.items.ptr);
header.section_header_offset = section_header_offset;
header.entry = _start_offset;
}
return file;
}
const Header = extern struct {
magic: u8 = 0x7f,
elf_id: [3]u8 = "ELF".*,
@ -163,7 +256,7 @@ const Header = extern struct {
program_header_count: u16 = 1,
section_header_size: u16 = @sizeOf(SectionHeader),
section_header_count: u16,
name_section_header_index: u16,
section_header_string_table_index: u16,
const BitCount = enum(u8) {
@"32" = 1,
@ -197,14 +290,14 @@ const Header = extern struct {
};
const ProgramHeader = extern struct {
type: Type = .load,
type: Type,
flags: Flags,
offset: u64,
virtual_address: u64,
physical_address: u64,
size_in_file: u64,
size_in_memory: u64,
alignment: u64 = 0,
alignment: u64,
const Type = enum(u32) {
null = 0,
@ -232,8 +325,8 @@ const SectionHeader = extern struct {
name_offset: u32,
type: Type,
flags: Flags,
address: u64,
offset: u64,
virtual_address: u64,
file_offset: u64,
size: u64,
// section index
link: u32,
@ -279,3 +372,14 @@ const SectionHeader = extern struct {
_reserved: u53 = 0,
};
};
const SymbolTable = extern struct {
const Entry = extern struct {
name_offset: u32,
information: u8,
other: u8,
section_header_index: u16,
value: u64,
size: u64,
};
};

View File

@ -24,38 +24,130 @@ const jit_callconv = .SysV;
const Section = struct {
content: []align(page_size) u8,
index: usize = 0,
alignment: u32 = 0x10,
alignment: u32,
name: []const u8,
flags: Flags,
type: Type,
symbol_table: std.StringArrayHashMapUnmanaged(u32) = .{},
const Type = enum {
null,
loadable_program,
string_table,
symbol_table,
};
const Flags = packed struct {
read: bool,
write: bool,
execute: bool,
};
};
pub const Result = struct {
sections: struct {
text: Section,
rodata: Section,
data: Section,
},
entry_point: u32 = 0,
sections: ArrayList(Section) = .{},
// sections: struct {
// text: Section,
// rodata: Section,
// data: Section,
// },
entry_point: u32,
target: std.Target,
allocator: Allocator,
pub fn create(target: std.Target) !Result {
return Result{
.sections = .{
.text = .{ .content = try mmap(page_size, .{ .executable = true }) },
.rodata = .{ .content = try mmap(page_size, .{ .executable = false }) },
.data = .{ .content = try mmap(page_size, .{ .executable = false }) },
},
const text_section_index = 0;
pub fn create(allocator: Allocator, target: std.Target, entry_point_index: u32) !Result {
var result = Result{
// .sections = .{
// .text = .{ .content = try mmap(page_size, .{ .executable = true }) },
// .rodata = .{ .content = try mmap(page_size, .{ .executable = false }) },
// .data = .{ .content = try mmap(page_size, .{ .executable = false }) },
// },
.target = target,
.allocator = allocator,
.entry_point = entry_point_index,
};
_ = try result.addSection(.{
.name = ".text",
.size = 0x1000,
.alignment = 0x1000,
.flags = .{
.execute = true,
.read = true,
.write = false,
},
.type = .loadable_program,
});
return result;
}
const SectionCreation = struct {
name: []const u8,
size: usize,
alignment: u32,
flags: Section.Flags,
type: Section.Type,
};
pub fn addSection(result: *Result, arguments: SectionCreation) !usize {
const index = result.sections.items.len;
assert(std.mem.isAligned(arguments.size, page_size));
try result.sections.append(result.allocator, .{
.content = try mmap(arguments.size, .{ .executable = arguments.flags.execute }),
.alignment = arguments.alignment,
.name = arguments.name,
.flags = arguments.flags,
.type = arguments.type,
});
return index;
}
pub fn insertSection(result: *Result, index: usize, arguments: SectionCreation) !usize {
assert(std.mem.isAligned(arguments.size, page_size));
try result.sections.insert(result.allocator, index, .{
.content = try mmap(arguments.size, .{ .executable = arguments.flags.execute }),
.alignment = arguments.alignment,
.name = arguments.name,
.flags = arguments.flags,
.type = arguments.type,
});
return index;
}
pub fn alignSection(result: *Result, index: usize, alignment: usize) void {
const index_ptr = &result.sections.items[index].index;
index_ptr.* = std.mem.alignForward(usize, index_ptr.*, alignment);
}
pub fn writeToSection(image: *Result, section_index: usize, bytes: []const u8) void {
const section = &image.sections.items[section_index];
const destination = section.content[section.index..][0..bytes.len];
@memcpy(destination, bytes);
section.index += bytes.len;
}
pub fn writeByteToSection(image: *Result, section_index: usize, byte: u8) void {
const section = &image.sections.items[section_index];
section.content[section.index] = byte;
section.index += 1;
}
pub fn getTextSection(result: *Result) *Section {
return &result.sections.items[0];
}
pub fn appendCode(image: *Result, code: []const u8) void {
const destination = image.sections.text.content[image.sections.text.index..][0..code.len];
@memcpy(destination, code);
image.sections.text.index += code.len;
image.writeToSection(text_section_index, code);
}
pub fn appendCodeByte(image: *Result, code_byte: u8) void {
image.sections.text.content[image.sections.text.index] = code_byte;
image.sections.text.index += 1;
image.writeByteToSection(text_section_index, code_byte);
}
fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType {
@ -70,16 +162,31 @@ pub const Result = struct {
return @as(*const FunctionType, @ptrCast(&image.sections.text.content[image.entry_point]));
}
fn writeElf(image: *const Result, allocator: Allocator, executable_relative_path: []const u8) !void {
var writer = try elf.Writer.init(allocator);
try writer.writeToMemory(image);
try writer.writeToFile(executable_relative_path);
fn writeElf(image: *Result, executable_relative_path: []const u8) !void {
const file_in_memory = try elf.writeToMemory(image);
try writeFile(file_in_memory.items, executable_relative_path);
}
fn writePe(image: *const Result, allocator: Allocator, executable_relative_path: []const u8) !void {
var writer = try pe.Writer.init(allocator);
try writer.writeToMemory(image);
try writer.writeToFile(executable_relative_path);
fn writeFile(bytes: []const u8, path: []const u8) !void {
const flags = switch (@import("builtin").os.tag) {
.windows => .{},
else => .{
.mode = 0o777,
},
};
const file_descriptor = try std.fs.cwd().createFile(path, flags);
try file_descriptor.writeAll(bytes);
file_descriptor.close();
}
fn writePe(image: *Result, executable_relative_path: []const u8) !void {
_ = executable_relative_path;
_ = image;
// var writer = try pe.Writer.init(allocator);
// try writer.writeToMemory(image);
// try writer.writeToFile(executable_relative_path);
unreachable;
}
};
@ -124,15 +231,13 @@ pub fn get(comptime arch: std.Target.Cpu.Arch) type {
var mir = try backend.MIR.selectInstructions(allocator, intermediate, descriptor.target);
try mir.allocateRegisters();
const os = descriptor.target.os.tag;
_ = os;
const image = try mir.encode();
_ = image;
// switch (os) {
// .linux => try image.writeElf(allocator, descriptor.executable_path),
// .windows => try image.writePe(allocator, descriptor.executable_path),
// else => unreachable,
// }
switch (os) {
.linux => try image.writeElf(descriptor.executable_path),
.windows => try image.writePe(descriptor.executable_path),
else => unreachable,
}
},
else => {
const file = try std.fs.cwd().readFileAlloc(allocator, "main", std.math.maxInt(u64));

View File

@ -42,7 +42,7 @@ pub const Result = struct {
casts: BlockList(Cast) = .{},
readonly_data: ArrayList(u8) = .{},
module: *Module,
entry_point: u32 = 0,
entry_point: Function.Index = Function.Index.invalid,
pub fn getFunctionName(ir: *Result, function_index: Function.Declaration.Index) []const u8 {
return ir.module.getName(ir.module.function_name_map.get(@bitCast(function_index)).?).?;
@ -58,13 +58,20 @@ pub fn initialize(compilation: *Compilation, module: *Module) !*Result {
};
builder.ir.module = module;
builder.ir.entry_point = module.entry_point orelse unreachable;
var sema_function_index = function_iterator.getCurrentIndex();
while (function_iterator.next()) |sema_function| {
const function_index = try builder.buildFunction(sema_function);
_ = function_index;
if (sema_function_index.eq(module.entry_point)) {
assert(!function_index.invalid);
builder.ir.entry_point = function_index;
}
sema_function_index = function_iterator.getCurrentIndex();
}
assert(!builder.ir.entry_point.invalid);
return &builder.ir;
}
@ -398,7 +405,7 @@ pub const Builder = struct {
return builder.ir.function_definitions.get(builder.current_function_index);
}
fn buildFunction(builder: *Builder, sema_function: Compilation.Function) !void {
fn buildFunction(builder: *Builder, sema_function: Compilation.Function) !Function.Index {
const sema_prototype = builder.module.function_prototypes.get(builder.module.types.get(sema_function.prototype).function);
const function_declaration_allocation = try builder.ir.function_declarations.addOne(builder.allocator);
const function_declaration = function_declaration_allocation.ptr;
@ -425,7 +432,7 @@ pub const Builder = struct {
}
switch (sema_prototype.attributes.@"extern") {
true => {},
true => return Function.Index.invalid,
false => {
const function_allocation = try builder.ir.function_definitions.append(builder.allocator, .{
.ir = &builder.ir,
@ -495,6 +502,8 @@ pub const Builder = struct {
builder.currentFunction().current_stack_offset = std.mem.alignForward(usize, builder.currentFunction().current_stack_offset, 0x10);
try builder.optimizeFunction(builder.currentFunction());
return function_allocation.index;
},
}
}

View File

@ -35,20 +35,23 @@ pub const Logger = enum {
register_allocation_instruction_avoid_copy,
register_allocation_function_after,
register_allocation_operand_list_verification,
encoding,
pub var bitset = std.EnumSet(Logger).initMany(&.{
.instruction_selection_ir_function,
.instruction_selection_mir_function,
// .instruction_selection_register_operand_list,
.register_allocation_block,
// .register_allocation_block,
// .register_allocation_problematic_hint,
// .register_allocation_assignment,
// .register_allocation_reload,
.register_allocation_function_before,
// .register_allocation_function_before,
// .register_allocation_new_instruction,
// .register_allocation_new_instruction_function_before,
// .register_allocation_instruction_avoid_copy,
.register_allocation_function_after,
.register_allocation_operand_list_verification,
// .register_allocation_operand_list_verification,
.encoding,
});
};
@ -1238,33 +1241,6 @@ const InstructionSelection = struct {
try instruction_selection.instruction_cache.append(mir.allocator, instr);
},
}
// const instruction_descriptor = instruction_descriptors.getPtrConst(instruction_id);
//
// switch (integer.value.unsigned == 0) {
// true => switch (value_type) {
// .i32 => .mov32r0,
// else => |t| @panic(@tagName(t)),
// },
// false => blk: {
//
// const destination_register = try mir.createVirtualRegister(destination_register_class);
// const destination_operand = mir.constrainOperandRegisterClass(instruction_descriptor, destination_register, 0, .{ .type = .def });
//
// const instr = try mir.buildInstruction(instruction_selection, instruction_id, &.{
// destination_operand,
// Operand{
// .id = .immediate,
// .u = .{
// .immediate = integer.value.unsigned,
// },
// .flags = .{},
// },
// });
// try instruction_selection.instruction_cache.append(mir.allocator, instr);
//
// break :blk destination_register;
// },
// }
}
fn getAddressingModeFromIr(instruction_selection: *InstructionSelection, mir: *MIR, ir_instruction_index: ir.Instruction.Index) AddressingMode {
@ -1293,17 +1269,6 @@ const InstructionSelection = struct {
unreachable;
}
}
// const gop = try instruction_selection.local_value_map.getOrPut(allocator, ir_instruction_index);
// if (gop.found_existing) {
// const stored_register = gop.value_ptr.*;
// if (std.meta.eql(stored_register, register)) {
// unreachable;
// } else {
// std.debug.panic("Register mismatch: Stored: {} Got: {}", .{ stored_register, register });
// }
// } else {
// gop.value_ptr.* = register;
// }
}
fn lowerArguments(instruction_selection: *InstructionSelection, mir: *MIR, ir_function: *ir.Function) !void {
@ -1478,20 +1443,24 @@ const Instruction = struct {
mov64mr,
mov32ri,
mov32ri64,
mov32rr,
movsx64rm32,
movsx64rr32,
ret,
syscall,
ud2,
xor32rr,
};
pub const Descriptor = struct {
operands: []const Operand.Reference = &.{},
opcode: u16 = 0,
opcode: u16,
format: Format = .pseudo,
flags: Flags,
flags: Flags = .{},
const Flags = packed struct {
implicit_def: bool,
implicit_def: bool = false,
two_byte_prefix: bool = false,
};
const Format = enum {
@ -1501,6 +1470,7 @@ const Instruction = struct {
mrm_dest_mem,
mrm_source_mem,
mrm_source_reg,
mrm_dest_reg,
};
};
@ -1738,7 +1708,7 @@ pub const Operand = struct {
};
const PCRelative = union(enum) {
function_declaration: ir.Function.Declaration.Index,
function_declaration: MIR.Function.Index,
string_literal: ir.StringLiteral.Index,
imm32: i32,
imm8: i8,
@ -1794,18 +1764,17 @@ const register_class_operand_matcher = std.EnumArray(Operand.Id, Register.Class)
const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descriptor).init(.{
.call64pcrel32 = .{
.format = .no_operands,
.opcode = 0xe8,
.operands = &.{
.{
.id = .i64i32imm_brtarget,
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.copy = .{
.format = .pseudo,
.opcode = 0,
.operands = &.{
.{
.id = .unknown,
@ -1816,12 +1785,10 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.lea64r = .{
.format = .mrm_source_mem,
.opcode = 0x8d,
.operands = &.{
.{
.id = .gp64,
@ -1832,24 +1799,20 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.mov32r0 = .{
.format = .pseudo,
.opcode = 0,
.operands = &.{
.{
.id = .gp32,
.kind = .dst,
},
},
.flags = .{
.implicit_def = false,
},
},
.mov32rm = .{
.format = .mrm_source_mem,
.opcode = 0x8b,
.operands = &.{
.{
.id = .gp32,
@ -1860,12 +1823,10 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.mov64rm = .{
.format = .mrm_source_mem,
.opcode = 0x8b,
.operands = &.{
.{
.id = .gp64,
@ -1876,12 +1837,24 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
.mov32rr = .{
.format = .mrm_dest_reg,
.opcode = 0x89,
.operands = &.{
.{
.id = .gp32,
.kind = .dst,
},
.{
.id = .gp32,
.kind = .src,
},
},
},
.mov32mr = .{
.format = .mrm_dest_mem,
.opcode = 0x89,
.operands = &.{
.{
.id = .i32mem,
@ -1892,12 +1865,10 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.mov64mr = .{
.format = .mrm_dest_mem,
.opcode = 0x89,
.operands = &.{
.{
.id = .i64mem,
@ -1908,12 +1879,10 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.mov32ri = .{
.format = .add_reg,
.opcode = 0xb8,
.operands = &.{
.{
.id = .gp32,
@ -1924,12 +1893,10 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.mov32ri64 = .{
.format = .pseudo,
.opcode = 0,
.operands = &.{
.{
.id = .gp64,
@ -1940,12 +1907,10 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.movsx64rm32 = .{
.format = .mrm_source_reg,
.format = .mrm_source_mem,
.opcode = 0x63,
.operands = &.{
.{
.id = .gp64,
@ -1956,12 +1921,10 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.movsx64rr32 = .{
.format = .mrm_source_reg,
.opcode = 0x63,
.operands = &.{
.{
.id = .gp64,
@ -1972,34 +1935,45 @@ const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descri
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.ret = .{
.format = .no_operands,
.opcode = 0xc3,
.operands = &.{
.{
.id = .unknown,
.kind = .src,
},
},
.flags = .{
.implicit_def = false,
},
},
.syscall = .{
.format = .no_operands,
.opcode = 0x05,
.operands = &.{},
.flags = .{
.implicit_def = false,
.two_byte_prefix = true,
},
},
.ud2 = .{
.format = .no_operands,
.opcode = 0x0b,
.operands = &.{},
.flags = .{
.implicit_def = false,
.two_byte_prefix = true,
},
},
.xor32rr = .{
.format = .mrm_dest_reg,
.opcode = 0x31,
.operands = &.{
.{
.id = .gp32,
.kind = .dst,
},
.{
.id = .gp32,
.kind = .src,
},
},
},
});
@ -2052,6 +2026,8 @@ pub const MIR = struct {
operands: BlockList(Operand) = .{},
instruction_selections: ArrayList(InstructionSelection) = .{},
virtual_registers: BlockList(Register.Virtual) = .{},
function_declaration_map: std.AutoHashMapUnmanaged(ir.Function.Declaration.Index, Function.Index) = .{},
entry_point: u32 = 0,
pub fn selectInstructions(allocator: Allocator, intermediate: *ir.Result, target: std.Target) !*MIR {
logln(.codegen, .instruction_selection_block, "\n[INSTRUCTION SELECTION]\n", .{});
@ -2066,15 +2042,16 @@ pub const MIR = struct {
try mir.functions.ensureCapacity(allocator, intermediate.function_definitions.len);
try mir.instruction_selections.ensureUnusedCapacity(allocator, intermediate.function_definitions.len);
var function_definition_iterator = intermediate.function_definitions.iterator();
var ir_function_definition_iterator = intermediate.function_definitions.iterator();
try mir.function_declaration_map.ensureTotalCapacity(mir.allocator, @intCast(intermediate.function_definitions.len));
while (function_definition_iterator.nextPointer()) |ir_function| {
while (ir_function_definition_iterator.nextPointer()) |ir_function| {
const fn_name = mir.ir.getFunctionName(ir_function.declaration);
logln(.codegen, .instruction_selection_ir_function, "Selecting instructions for {}", .{ir_function});
const instruction_selection = mir.instruction_selections.addOneAssumeCapacity();
const function_allocation = try mir.functions.addOne(mir.allocator);
const function = function_allocation.ptr;
mir.function_declaration_map.putAssumeCapacityNoClobber(ir_function.declaration, function_allocation.index);
function.* = .{
.mir = mir,
.instruction_selection = instruction_selection,
@ -2083,12 +2060,29 @@ pub const MIR = struct {
instruction_selection.* = .{
.function = function,
};
}
var function_iterator = mir.functions.iterator();
ir_function_definition_iterator = intermediate.function_definitions.iterator();
var entry_point: ?u32 = null;
var ir_function_index = ir_function_definition_iterator.getCurrentIndex();
while (ir_function_definition_iterator.nextPointer()) |ir_function| {
const function_index = function_iterator.getCurrentIndex();
const function = function_iterator.nextPointer() orelse unreachable;
logln(.codegen, .instruction_selection_ir_function, "Selecting instructions for {}", .{ir_function});
const instruction_selection = function.instruction_selection;
if (ir_function_index.eq(intermediate.entry_point)) {
entry_point = function_index.uniqueInteger();
}
const ir_function_declaration = mir.ir.function_declarations.get(ir_function.declaration);
const calling_convention = calling_conventions.get(ir_function_declaration.calling_convention);
try instruction_selection.block_map.ensureUnusedCapacity(allocator, @intCast(ir_function.blocks.items.len));
try function.blocks.ensureTotalCapacity(allocator, ir_function.blocks.items.len);
for (ir_function.blocks.items) |block| {
const block_allocation = try mir.blocks.append(allocator, .{});
instruction_selection.block_map.putAssumeCapacity(block, block_allocation.index);
@ -2521,7 +2515,7 @@ pub const MIR = struct {
.id = .i64i32imm_brtarget,
.u = .{
.pc_relative = .{
.function_declaration = ir_call.function,
.function_declaration = mir.function_declaration_map.get(ir_call.function).?,
},
},
.flags = .{},
@ -2605,8 +2599,12 @@ pub const MIR = struct {
try instruction_selection.emitLiveInCopies(mir, function.blocks.items[0]);
logln(.codegen, .instruction_selection_ir_function, "Selected instructions for {}", .{function});
ir_function_index = ir_function_definition_iterator.getCurrentIndex();
}
mir.entry_point = entry_point orelse unreachable;
return mir;
}
@ -3666,10 +3664,337 @@ pub const MIR = struct {
unreachable;
}
pub fn encode(mir: *MIR) !emit.Result {
_ = mir;
// unreachable;
return undefined;
fn getGP32Encoding(operand: Operand) Encoding.GP32 {
assert(operand.id == .gp32);
const physical_register = operand.u.register.index.physical;
const gp_register_encoding: Encoding.GP32 = switch (physical_register) {
.eax => .a,
.edi => .di,
else => |t| @panic(@tagName(t)),
};
return gp_register_encoding;
}
fn getGP64Encoding(operand: Operand) Encoding.GP64 {
assert(operand.id == .gp64);
const physical_register = operand.u.register.index.physical;
const gp_register_encoding: Encoding.GP64 = switch (physical_register) {
.rax => .a,
.rdi => .di,
else => |t| @panic(@tagName(t)),
};
return gp_register_encoding;
}
pub fn encode(mir: *MIR) !*emit.Result {
const image = try mir.allocator.create(emit.Result);
image.* = try emit.Result.create(mir.allocator, mir.target, mir.entry_point);
var function_iterator = mir.functions.iterator();
var function_offsets = std.AutoArrayHashMapUnmanaged(Function.Index, u32){};
try function_offsets.ensureTotalCapacity(mir.allocator, mir.functions.len);
try image.sections.items[0].symbol_table.ensureTotalCapacity(mir.allocator, mir.functions.len);
while (function_iterator.nextPointer()) |function| {
const function_index = mir.functions.indexOf(function);
logln(.codegen, .encoding, "\n{s}:", .{function.name});
const function_offset: u32 = @intCast(image.getTextSection().index);
function_offsets.putAssumeCapacityNoClobber(function_index, function_offset);
image.sections.items[0].symbol_table.putAssumeCapacityNoClobber(function.name, function_offset);
const stack_size = blk: {
var result: u32 = 0;
for (function.instruction_selection.stack_objects.items) |stack_object| {
assert(std.mem.isAligned(result, stack_object.alignment));
result += @intCast(stack_object.size);
}
break :blk result;
};
if (stack_size != 0) {
image.appendCodeByte(0x55); // push rbp
image.appendCode(&.{ 0x48, 0x89, 0xe5 }); // mov rbp, rsp
// sub rsp, stack_offset
if (std.math.cast(u8, stack_size)) |stack_size_u8| {
image.appendCode(&.{ 0x48, 0x83, 0xec, stack_size_u8 });
} else {
unreachable;
}
}
for (function.blocks.items) |block_index| {
const block = mir.blocks.get(block_index);
for (block.instructions.items) |instruction_index| {
const instruction = mir.instructions.get(instruction_index);
const instruction_offset = image.getTextSection().index;
switch (instruction.id) {
.mov32r0 => {
assert(instruction.operands.items.len == 1);
const operand = mir.operands.get(instruction.operands.items[0]);
const gp_register_encoding = getGP32Encoding(operand.*);
const new_instruction_id = Instruction.Id.xor32rr;
const instruction_descriptor = instruction_descriptors.get(new_instruction_id);
const opcode: u8 = @intCast(instruction_descriptor.opcode);
image.appendCodeByte(opcode);
const direct = true;
const modrm = ModRm{
.rm = @intCast(@intFromEnum(gp_register_encoding)),
.reg = @intCast(@intFromEnum(gp_register_encoding)),
.mod = @as(u2, @intFromBool(direct)) << 1 | @intFromBool(direct),
};
image.appendCodeByte(@bitCast(modrm));
},
.ret => {},
.mov32mr => {
assert(instruction.operands.items.len == 2);
const source_operand = mir.operands.get(instruction.operands.items[1]);
const source_gp32 = getGP32Encoding(source_operand.*);
const destination_operand = mir.operands.get(instruction.operands.items[0]);
assert(destination_operand.u == .memory);
const memory = destination_operand.u.memory;
const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode: u8 = @intCast(instruction_descriptor.opcode);
image.appendCodeByte(opcode);
const modrm = ModRm{
.rm = @intFromEnum(Encoding.GP32.bp),
.reg = @intCast(@intFromEnum(source_gp32)),
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
};
image.appendCodeByte(@bitCast(modrm));
switch (memory.addressing_mode.base) {
.frame_index => |frame_index| {
const stack_offset = blk: {
var computed_stack_offset: usize = 0;
for (function.instruction_selection.stack_objects.items[0 .. frame_index + 1]) |stack_object| {
assert(std.mem.isAligned(computed_stack_offset, stack_object.alignment));
computed_stack_offset += stack_object.size;
}
break :blk -@as(i64, @intCast(computed_stack_offset));
};
const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable;
const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes];
image.appendCode(stack_bytes);
},
else => |t| @panic(@tagName(t)),
}
},
.mov32rm => {
assert(instruction.operands.items.len == 2);
const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode: u8 = @intCast(instruction_descriptor.opcode);
image.appendCodeByte(opcode);
const destination_operand = mir.operands.get(instruction.operands.items[0]);
const destination_gp32 = getGP32Encoding(destination_operand.*);
const source_operand = mir.operands.get(instruction.operands.items[1]);
assert(source_operand.u == .memory);
const source_memory = source_operand.u.memory;
const modrm = ModRm{
.rm = @intFromEnum(Encoding.GP32.bp),
.reg = @intCast(@intFromEnum(destination_gp32)),
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
};
image.appendCodeByte(@bitCast(modrm));
switch (source_memory.addressing_mode.base) {
.frame_index => |frame_index| {
const stack_offset = blk: {
var computed_stack_offset: usize = 0;
for (function.instruction_selection.stack_objects.items[0 .. frame_index + 1]) |stack_object| {
assert(std.mem.isAligned(computed_stack_offset, stack_object.alignment));
computed_stack_offset += stack_object.size;
}
break :blk -@as(i64, @intCast(computed_stack_offset));
};
const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable;
const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes];
image.appendCode(stack_bytes);
},
else => |t| @panic(@tagName(t)),
}
},
.mov32ri64 => {
assert(instruction.operands.items.len == 2);
const source_operand = mir.operands.get(instruction.operands.items[1]);
const source_immediate: u32 = @intCast(source_operand.u.immediate);
const destination_operand = mir.operands.get(instruction.operands.items[0]);
const destination_gp64 = getGP64Encoding(destination_operand.*);
const destination_gp32 = switch (destination_gp64) {
inline else => |gp64| @field(Encoding.GP32, @tagName(gp64)),
};
const opcode = @as(u8, 0xb8) | @as(u3, @intCast(@intFromEnum(destination_gp32)));
image.appendCodeByte(opcode);
image.appendCode(std.mem.asBytes(&source_immediate));
},
.movsx64rm32 => {
assert(instruction.operands.items.len == 2);
const destination_operand = mir.operands.get(instruction.operands.items[0]);
const destination_register = getGP64Encoding(destination_operand.*);
const source_operand = mir.operands.get(instruction.operands.items[1]);
const source_memory = source_operand.u.memory;
const rex = Rex{
.b = false,
.x = false,
.r = false,
.w = true,
};
image.appendCodeByte(@bitCast(rex));
const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode: u8 = @intCast(instruction_descriptor.opcode);
image.appendCodeByte(opcode);
const modrm = ModRm{
.rm = @intFromEnum(Encoding.GP32.bp),
.reg = @intCast(@intFromEnum(destination_register)),
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
};
image.appendCodeByte(@bitCast(modrm));
switch (source_memory.addressing_mode.base) {
.frame_index => |frame_index| {
const stack_offset = blk: {
var computed_stack_offset: usize = 0;
for (function.instruction_selection.stack_objects.items[0 .. frame_index + 1]) |stack_object| {
assert(std.mem.isAligned(computed_stack_offset, stack_object.alignment));
computed_stack_offset += stack_object.size;
}
break :blk -@as(i64, @intCast(computed_stack_offset));
};
const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable;
const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes];
image.appendCode(stack_bytes);
},
else => |t| @panic(@tagName(t)),
}
},
.syscall => image.appendCode(&.{ 0x0f, 0x05 }),
.ud2 => image.appendCode(&.{ 0x0f, 0x0b }),
.call64pcrel32 => {
// TODO: emit relocation
assert(instruction.operands.items.len == 1);
const operand = mir.operands.get(instruction.operands.items[0]);
const instruction_descriptor = instruction_descriptors.get(instruction.id);
const opcode: u8 = @intCast(instruction_descriptor.opcode);
image.appendCodeByte(opcode);
switch (operand.u) {
.pc_relative => |pc_relative| {
// TODO: fix
const callee = pc_relative.function_declaration;
const caller = function_index;
const instruction_len = 5;
if (callee.uniqueInteger() <= caller.uniqueInteger()) {
const callee_offset: i64 = @intCast(function_offsets.get(callee).?);
const caller_offset: i64 = @intCast(instruction_offset + instruction_len);
const offset: i32 = @intCast(callee_offset - caller_offset);
image.appendCode(std.mem.asBytes(&offset));
} else {
image.appendCode(&.{ 0, 0, 0, 0 });
unreachable;
}
},
else => |t| @panic(@tagName(t)),
}
},
.copy => {
assert(instruction.operands.items.len == 2);
const destination_operand = mir.operands.get(instruction.operands.items[0]);
const source_operand = mir.operands.get(instruction.operands.items[1]);
assert(destination_operand.id == source_operand.id);
// const destination_physical_register = destination_operand.u.register.index.physical;
// _ = destination_physical_register;
// const source_physical_register = source_operand.u.register.index.physical;
switch (destination_operand.id) {
.gp32 => {
image.appendCodeByte(0x89);
const destination_register = getGP32Encoding(destination_operand.*);
const source_register = getGP32Encoding(source_operand.*);
const modrm = ModRm{
.rm = @intCast(@intFromEnum(destination_register)),
.reg = @intCast(@intFromEnum(source_register)),
.mod = @as(u2, @intFromBool(true)) << 1 | @intFromBool(true),
};
image.appendCodeByte(@bitCast(modrm));
},
else => |t| @panic(@tagName(t)),
}
},
else => |t| @panic(@tagName(t)),
}
if (instruction_offset != image.getTextSection().index) {
const print_tags = true;
if (print_tags) {
var offset = @tagName(instruction.id).len + 2;
log(.codegen, .encoding, "{s}: ", .{@tagName(instruction.id)});
const margin = 16;
while (offset < margin) : (offset += 1) {
log(.codegen, .encoding, " ", .{});
}
}
for (image.getTextSection().content[instruction_offset..image.getTextSection().index]) |byte| {
log(.codegen, .encoding, "0x{x:0>2} ", .{byte});
}
log(.codegen, .encoding, "\n", .{});
}
}
}
const last_block_index = function.blocks.items[function.blocks.items.len - 1];
const last_block = mir.blocks.get(last_block_index);
const last_block_last_instruction_index = last_block.instructions.items[last_block.instructions.items.len - 1];
const last_block_last_instruction = mir.instructions.get(last_block_last_instruction_index);
if (last_block_last_instruction.id == .ret) {
if (stack_size != 0) {
// add rsp, stack_offset
if (std.math.cast(u8, stack_size)) |stack_size_u8| {
image.appendCode(&.{ 0x48, 0x83, 0xc4, stack_size_u8 });
} else {
unreachable;
}
image.appendCodeByte(0x5d); // pop rbp
}
image.appendCodeByte(0xc3);
}
}
return image;
}
fn getRegisterListHead(mir: *MIR, instruction_selection: *InstructionSelection, register: Register) *Operand.Index {
@ -3691,6 +4016,10 @@ pub const MIR = struct {
mir: *MIR,
name: []const u8,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
pub fn format(function: *const Function, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
const function_name = function.name;
try writer.print("{s}:\n", .{function_name});
@ -3820,19 +4149,30 @@ pub const MIR = struct {
}
};
// const ModRm = packed struct(u8) {
// rm: u3,
// reg: u3,
// mod: u2,
// };
const ModRm = packed struct(u8) {
rm: u3,
reg: u3,
mod: u2,
};
const Rex = packed struct(u8) {
b: bool,
x: bool,
r: bool,
w: bool,
fixed: u4 = 0b0100,
// fn create32RR(args: struct {
// rm: Encoding.GP32,
// reg: Encoding.GP32,
// sib: bool = false,
// }) ?Rex {
// if (args.sib) {
// unreachable;
// } else {
// }
// }
// const Rex = packed struct(u8) {
// b: bool,
// x: bool,
// r: bool,
// w: bool,
// fixed: u4 = 0b0100,
//
// fn create(args: struct {
// rm: ?GPRegister = null,
// reg: ?GPRegister = null,
@ -3852,7 +4192,7 @@ pub const MIR = struct {
// return null;
// }
// }
// };
};
fn getIrType(intermediate: *ir.Result, ir_instruction_index: ir.Instruction.Index) ir.Type {
const ir_instruction = intermediate.instructions.get(ir_instruction_index);
@ -3903,6 +4243,45 @@ fn getSubregistersRecursive(allocator: Allocator, set: *RegisterSet, reg: Regist
}
}
const Encoding = struct {
const GP32 = enum(u4) {
a = 0,
c = 1,
d = 2,
b = 3,
sp = 4,
bp = 5,
si = 6,
di = 7,
r8 = 8,
r9 = 9,
r10 = 10,
r11 = 11,
r12 = 12,
r13 = 13,
r14 = 14,
r15 = 15,
};
const GP64 = enum(u4) {
a = 0,
c = 1,
d = 2,
b = 3,
sp = 4,
bp = 5,
si = 6,
di = 7,
r8 = 8,
r9 = 9,
r10 = 10,
r11 = 11,
r12 = 12,
r13 = 13,
r14 = 14,
r15 = 15,
};
};
const LiveRegister = struct {
last_use: Instruction.Index = Instruction.Index.invalid,
virtual: Register.Virtual.Index,

View File

@ -1492,7 +1492,7 @@ pub fn initialize(compilation: *Compilation, module: *Module, package: *Package,
if (equal(u8, declaration_name, "_start")) {
const value = module.values.get(decl.init_value);
module.entry_point = switch (value.*) {
.function => |function_index| function_index.uniqueInteger(),
.function => |function_index| function_index,
.unresolved => panic("Unresolved declaration: {s}\n", .{declaration_name}),
else => |t| @panic(@tagName(t)),
};