commit
9d6607b460
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -43,3 +43,5 @@ jobs:
|
|||||||
nat/first
|
nat/first
|
||||||
zig build run -- test/stack/main.nat
|
zig build run -- test/stack/main.nat
|
||||||
nat/stack
|
nat/stack
|
||||||
|
zig build run -- test/hello_world/main.nat
|
||||||
|
nat/hello_world
|
||||||
|
@ -10,22 +10,13 @@ const emit = @import("emit.zig");
|
|||||||
const page_size = 0x1000;
|
const page_size = 0x1000;
|
||||||
|
|
||||||
pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, page_size) {
|
pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, page_size) {
|
||||||
var file = try std.ArrayListAlignedUnmanaged(u8, 0x1000).initCapacity(image.allocator, 0x100000);
|
const allocator = image.section_manager.allocator;
|
||||||
_ = try image.insertSection(0, .{
|
|
||||||
.name = "",
|
|
||||||
.size = page_size,
|
|
||||||
.alignment = page_size,
|
|
||||||
.flags = .{
|
|
||||||
.read = true,
|
|
||||||
.write = false,
|
|
||||||
.execute = false,
|
|
||||||
},
|
|
||||||
.type = .loadable_program,
|
|
||||||
});
|
|
||||||
|
|
||||||
const symbol_table_index = try image.addSection(.{
|
try image.section_manager.addNullSection();
|
||||||
|
|
||||||
|
const symbol_table_index = try image.section_manager.addSection(.{
|
||||||
.name = ".symtab",
|
.name = ".symtab",
|
||||||
.size = page_size,
|
.size_guess = 50,
|
||||||
.alignment = @alignOf(SymbolTable.Entry),
|
.alignment = @alignOf(SymbolTable.Entry),
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.read = false,
|
.read = false,
|
||||||
@ -34,9 +25,9 @@ pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, pag
|
|||||||
},
|
},
|
||||||
.type = .symbol_table,
|
.type = .symbol_table,
|
||||||
});
|
});
|
||||||
const string_table_index = try image.addSection(.{
|
const string_table_index = try image.section_manager.addSection(.{
|
||||||
.name = ".strtab",
|
.name = ".strtab",
|
||||||
.size = page_size,
|
.size_guess = 50,
|
||||||
.alignment = 1,
|
.alignment = 1,
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.read = false,
|
.read = false,
|
||||||
@ -45,9 +36,9 @@ pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, pag
|
|||||||
},
|
},
|
||||||
.type = .string_table,
|
.type = .string_table,
|
||||||
});
|
});
|
||||||
const section_header_string_table_index = try image.addSection(.{
|
const section_header_string_table_index = try image.section_manager.addSection(.{
|
||||||
.name = ".shstrtab",
|
.name = ".shstrtab",
|
||||||
.size = page_size,
|
.size_guess = 50,
|
||||||
.alignment = 1,
|
.alignment = 1,
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.read = false,
|
.read = false,
|
||||||
@ -62,7 +53,7 @@ pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, pag
|
|||||||
|
|
||||||
const program_header_count = blk: {
|
const program_header_count = blk: {
|
||||||
var result: usize = 0;
|
var result: usize = 0;
|
||||||
for (image.sections.items) |section| {
|
for (image.section_manager.sections.items) |section| {
|
||||||
result += @intFromBool(switch (section.type) {
|
result += @intFromBool(switch (section.type) {
|
||||||
.null => false,
|
.null => false,
|
||||||
.loadable_program => true,
|
.loadable_program => true,
|
||||||
@ -75,7 +66,7 @@ pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, pag
|
|||||||
|
|
||||||
var symbol_name_offset: u32 = 0;
|
var symbol_name_offset: u32 = 0;
|
||||||
|
|
||||||
image.writeToSection(symbol_table_index, std.mem.asBytes(&SymbolTable.Entry{
|
try image.section_manager.appendToSection(symbol_table_index, std.mem.asBytes(&SymbolTable.Entry{
|
||||||
.name_offset = symbol_name_offset,
|
.name_offset = symbol_name_offset,
|
||||||
.information = 0,
|
.information = 0,
|
||||||
.other = 0,
|
.other = 0,
|
||||||
@ -84,154 +75,172 @@ pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, pag
|
|||||||
.size = 0,
|
.size = 0,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
image.writeToSection(string_table_index, "");
|
try image.section_manager.appendToSection(string_table_index, "");
|
||||||
image.writeByteToSection(string_table_index, 0);
|
try image.section_manager.appendByteToSection(string_table_index, 0);
|
||||||
symbol_name_offset += 1;
|
symbol_name_offset += 1;
|
||||||
|
|
||||||
for (image.sections.items) |section| {
|
for (image.section_manager.sections.items) |section| {
|
||||||
image.writeToSection(section_header_string_table_index, section.name);
|
try image.section_manager.appendToSection(section_header_string_table_index, section.name);
|
||||||
image.writeByteToSection(section_header_string_table_index, 0);
|
try image.section_manager.appendByteToSection(section_header_string_table_index, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
try image.section_manager.appendToSection(0, std.mem.asBytes(&Header{
|
||||||
var program_segment_offset: usize = 0;
|
.endianness = .little,
|
||||||
|
.machine = switch (image.target.cpu.arch) {
|
||||||
|
.x86_64 => .AMD64,
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
.os_abi = switch (image.target.os.tag) {
|
||||||
|
.linux => .systemv,
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
.entry = 0, // overwritten later
|
||||||
|
.section_header_offset = 0, // overwritten later
|
||||||
|
.program_header_count = @intCast(program_header_count),
|
||||||
|
.section_header_count = @intCast(image.section_manager.sections.items.len),
|
||||||
|
.section_header_string_table_index = @intCast(section_header_string_table_index),
|
||||||
|
}));
|
||||||
|
|
||||||
image.writeToSection(0, std.mem.asBytes(&Header{
|
var program_segment_offset: usize = 0;
|
||||||
.endianness = .little,
|
|
||||||
.machine = switch (image.target.cpu.arch) {
|
|
||||||
.x86_64 => .AMD64,
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
.os_abi = switch (image.target.os.tag) {
|
|
||||||
.linux => .systemv,
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
.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),
|
|
||||||
}));
|
|
||||||
|
|
||||||
for (image.sections.items, 0..) |section, section_index| {
|
for (image.section_manager.sections.items, 0..) |section, section_index| {
|
||||||
switch (section.type) {
|
switch (section.type) {
|
||||||
.loadable_program => {
|
.loadable_program => {
|
||||||
program_segment_offset = std.mem.alignForward(usize, program_segment_offset, section.alignment);
|
program_segment_offset = std.mem.alignForward(usize, program_segment_offset, section.alignment);
|
||||||
const virtual_address = base_virtual_address + program_segment_offset;
|
const virtual_address = base_virtual_address + program_segment_offset;
|
||||||
const program_segment_size = switch (section_index) {
|
const program_segment_size = switch (section_index) {
|
||||||
0 => @sizeOf(Header) + @sizeOf(ProgramHeader) * program_header_count,
|
0 => @sizeOf(Header) + @sizeOf(ProgramHeader) * program_header_count,
|
||||||
else => section.index,
|
else => section.bytes.items.len,
|
||||||
};
|
};
|
||||||
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;
|
try image.section_manager.appendToSection(0, std.mem.asBytes(&ProgramHeader{
|
||||||
},
|
.type = .load,
|
||||||
.null,
|
.flags = ProgramHeader.Flags{
|
||||||
.string_table,
|
.executable = section.flags.execute,
|
||||||
.symbol_table,
|
.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,
|
||||||
var section_offset: usize = 0;
|
.size_in_memory = program_segment_size,
|
||||||
var section_headers = try ArrayList(SectionHeader).initCapacity(image.allocator, image.sections.items.len);
|
.alignment = section.alignment,
|
||||||
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);
|
program_segment_offset += program_segment_size;
|
||||||
image.writeByteToSection(string_table_index, 0);
|
},
|
||||||
|
.null,
|
||||||
|
.string_table,
|
||||||
|
.symbol_table,
|
||||||
|
=> {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
symbol_name_offset += @intCast(symbol_name.len + 1);
|
var file = try std.ArrayListAlignedUnmanaged(u8, 0x1000).initCapacity(allocator, 0x100000);
|
||||||
}
|
var section_headers = try ArrayList(SectionHeader).initCapacity(allocator, image.section_manager.sections.items.len);
|
||||||
|
var section_name_offset: u32 = 0;
|
||||||
|
|
||||||
const source = section.content[0..section.index];
|
for (image.section_manager.sections.items, 0..) |section, section_i| {
|
||||||
file.items.len = section_offset + source.len;
|
const section_offset = std.mem.alignForward(usize, file.items.len, section.alignment);
|
||||||
try file.replaceRange(image.allocator, section_offset, source.len, source);
|
const virtual_address = base_virtual_address + section_offset;
|
||||||
|
|
||||||
section_headers.appendAssumeCapacity(SectionHeader{
|
if (file.items.len < section_offset) {
|
||||||
.name_offset = section_name_offset,
|
try file.appendNTimes(allocator, 0, section_offset - file.items.len);
|
||||||
.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 = true,
|
|
||||||
.executable = section.flags.execute,
|
|
||||||
.writable = section.flags.write,
|
|
||||||
},
|
|
||||||
.virtual_address = virtual_address,
|
|
||||||
.file_offset = section_offset,
|
|
||||||
.size = section.index,
|
|
||||||
.link = switch (section.type) {
|
|
||||||
.symbol_table => @intCast(string_table_index),
|
|
||||||
else => 0,
|
|
||||||
},
|
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const section_header_offset = std.mem.alignForward(usize, section_offset, @alignOf(SectionHeader));
|
for (section.symbol_table.keys(), section.symbol_table.values()) |symbol_name, symbol_offset| {
|
||||||
const section_header_bytes = std.mem.sliceAsBytes(section_headers.items);
|
const symbol_address = virtual_address + symbol_offset;
|
||||||
try file.ensureTotalCapacity(image.allocator, section_header_offset + section_header_bytes.len);
|
try image.section_manager.appendToSection(symbol_table_index, std.mem.asBytes(&SymbolTable.Entry{
|
||||||
file.items.len = section_header_offset + section_header_bytes.len;
|
.name_offset = symbol_name_offset,
|
||||||
try file.replaceRange(image.allocator, section_header_offset, section_header_bytes.len, section_header_bytes);
|
.information = 0x10,
|
||||||
|
.other = 0,
|
||||||
|
.section_header_index = @intCast(section_i),
|
||||||
|
.value = symbol_address,
|
||||||
|
.size = 0,
|
||||||
|
}));
|
||||||
|
|
||||||
const _start_offset = blk: {
|
try image.section_manager.appendToSection(string_table_index, symbol_name);
|
||||||
const entry_offset = image.sections.items[text_section_index].symbol_table.values()[image.entry_point];
|
try image.section_manager.appendByteToSection(string_table_index, 0);
|
||||||
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);
|
symbol_name_offset += @intCast(symbol_name.len + 1);
|
||||||
header.section_header_offset = section_header_offset;
|
}
|
||||||
header.entry = _start_offset;
|
|
||||||
|
try file.appendSlice(image.section_manager.allocator, section.bytes.items);
|
||||||
|
|
||||||
|
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 = true,
|
||||||
|
.executable = section.flags.execute,
|
||||||
|
.writable = section.flags.write,
|
||||||
|
},
|
||||||
|
.virtual_address = virtual_address,
|
||||||
|
.file_offset = section_offset,
|
||||||
|
.size = section.bytes.items.len,
|
||||||
|
.link = switch (section.type) {
|
||||||
|
.symbol_table => @intCast(string_table_index),
|
||||||
|
else => 0,
|
||||||
|
},
|
||||||
|
.info = switch (section.type) {
|
||||||
|
.symbol_table => 1,
|
||||||
|
else => 0,
|
||||||
|
},
|
||||||
|
.alignment = 0,
|
||||||
|
.entry_size = switch (section.type) {
|
||||||
|
.symbol_table => @sizeOf(SymbolTable.Entry),
|
||||||
|
else => 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
section_name_offset += @intCast(section.name.len + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const section_header_offset = std.mem.alignForward(usize, file.items.len, @alignOf(SectionHeader));
|
||||||
|
const section_header_bytes = std.mem.sliceAsBytes(section_headers.items);
|
||||||
|
|
||||||
|
try file.ensureTotalCapacity(allocator, section_header_offset + section_header_bytes.len);
|
||||||
|
|
||||||
|
if (file.items.len < section_header_offset) {
|
||||||
|
file.appendNTimesAssumeCapacity(0, section_header_offset - file.items.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
file.appendSliceAssumeCapacity(section_header_bytes);
|
||||||
|
|
||||||
|
// At this point, the file array list is not going to grow, so it's safe to practice relocations
|
||||||
|
for (image.section_manager.linker_relocations.items) |relocation| {
|
||||||
|
const source_section_index = relocation.source.index + @intFromBool(image.section_manager.null);
|
||||||
|
const target_section_index = relocation.target.index + @intFromBool(image.section_manager.null);
|
||||||
|
const source_section_header = §ion_headers.items[source_section_index];
|
||||||
|
const target_section_header = §ion_headers.items[target_section_index];
|
||||||
|
const source_file_offset = source_section_header.file_offset + relocation.source.offset;
|
||||||
|
const source_virtual_address = source_section_header.virtual_address + relocation.source.offset;
|
||||||
|
const target_virtual_address = target_section_header.virtual_address + relocation.target.offset;
|
||||||
|
const displacement: i32 = @intCast(@as(i64, @intCast(target_virtual_address)) - @as(i64, @intCast(source_virtual_address)));
|
||||||
|
const address_file_offset: usize = @intCast(@as(i64, @intCast(source_file_offset)) + relocation.offset);
|
||||||
|
const address_file_pointer: *align(1) i32 = @ptrCast(&file.items[address_file_offset]);
|
||||||
|
address_file_pointer.* = displacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _start_offset = blk: {
|
||||||
|
const entry_offset = image.section_manager.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;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ const ir = @import("intermediate_representation.zig");
|
|||||||
|
|
||||||
const data_structures = @import("../data_structures.zig");
|
const data_structures = @import("../data_structures.zig");
|
||||||
const ArrayList = data_structures.ArrayList;
|
const ArrayList = data_structures.ArrayList;
|
||||||
|
const ArrayListAligned = data_structures.ArrayListAligned;
|
||||||
const AutoHashMap = data_structures.AutoHashMap;
|
const AutoHashMap = data_structures.AutoHashMap;
|
||||||
const mmap = data_structures.mmap;
|
const mmap = data_structures.mmap;
|
||||||
|
|
||||||
@ -22,13 +23,12 @@ const macho = @import("macho.zig");
|
|||||||
const jit_callconv = .SysV;
|
const jit_callconv = .SysV;
|
||||||
|
|
||||||
const Section = struct {
|
const Section = struct {
|
||||||
content: []align(page_size) u8,
|
bytes: ArrayListAligned(u8, page_size),
|
||||||
index: usize = 0,
|
|
||||||
alignment: u32,
|
|
||||||
name: []const u8,
|
|
||||||
flags: Flags,
|
|
||||||
type: Type,
|
|
||||||
symbol_table: std.StringArrayHashMapUnmanaged(u32) = .{},
|
symbol_table: std.StringArrayHashMapUnmanaged(u32) = .{},
|
||||||
|
name: []const u8,
|
||||||
|
alignment: u32,
|
||||||
|
flags: Section.Flags,
|
||||||
|
type: Section.Type,
|
||||||
|
|
||||||
const Type = enum {
|
const Type = enum {
|
||||||
null,
|
null,
|
||||||
@ -44,112 +44,121 @@ const Section = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Result = struct {
|
const SectionCreation = struct {
|
||||||
|
name: []const u8,
|
||||||
|
size_guess: usize,
|
||||||
|
alignment: u32,
|
||||||
|
flags: Section.Flags,
|
||||||
|
type: Section.Type,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Relocation = struct {
|
||||||
|
source: struct {
|
||||||
|
offset: u32,
|
||||||
|
index: u16,
|
||||||
|
},
|
||||||
|
target: struct {
|
||||||
|
offset: u32,
|
||||||
|
index: u16,
|
||||||
|
},
|
||||||
|
offset: i8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SectionManager = struct {
|
||||||
sections: ArrayList(Section) = .{},
|
sections: ArrayList(Section) = .{},
|
||||||
// sections: struct {
|
rodata: ?u16 = null,
|
||||||
// text: Section,
|
null: bool = false,
|
||||||
// rodata: Section,
|
linker_relocations: ArrayList(Relocation) = .{},
|
||||||
// data: Section,
|
|
||||||
// },
|
|
||||||
entry_point: u32,
|
|
||||||
target: std.Target,
|
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
|
||||||
const text_section_index = 0;
|
pub fn addSection(section_manager: *SectionManager, arguments: SectionCreation) !usize {
|
||||||
|
const index = section_manager.sections.items.len;
|
||||||
|
|
||||||
pub fn create(allocator: Allocator, target: std.Target, entry_point_index: u32) !Result {
|
const r = try section_manager.insertSection(index, arguments);
|
||||||
var result = Result{
|
assert(index == r);
|
||||||
// .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(.{
|
return index;
|
||||||
.name = ".text",
|
}
|
||||||
.size = 0x1000,
|
|
||||||
.alignment = 0x1000,
|
pub fn getTextSectionIndex(section_manager: *const SectionManager) u16 {
|
||||||
|
return @intCast(@intFromBool(section_manager.null));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getTextSection(section_manager: *SectionManager) *Section {
|
||||||
|
return §ion_manager.sections.items[section_manager.getTextSectionIndex()];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insertSection(section_manager: *SectionManager, index: usize, arguments: SectionCreation) !usize {
|
||||||
|
try section_manager.sections.insert(section_manager.allocator, index, .{
|
||||||
|
.bytes = try ArrayListAligned(u8, page_size).initCapacity(section_manager.allocator, arguments.size_guess),
|
||||||
|
.alignment = arguments.alignment,
|
||||||
|
.name = arguments.name,
|
||||||
|
.flags = arguments.flags,
|
||||||
|
.type = arguments.type,
|
||||||
|
});
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addNullSection(section_manager: *SectionManager) !void {
|
||||||
|
const index = try section_manager.insertSection(0, .{
|
||||||
|
.name = "",
|
||||||
|
.size_guess = page_size,
|
||||||
|
.alignment = page_size,
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.execute = true,
|
|
||||||
.read = true,
|
.read = true,
|
||||||
.write = false,
|
.write = false,
|
||||||
|
.execute = false,
|
||||||
},
|
},
|
||||||
.type = .loadable_program,
|
.type = .loadable_program,
|
||||||
});
|
});
|
||||||
|
assert(index == 0);
|
||||||
|
|
||||||
|
section_manager.null = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn appendByteToSection(section_manager: *SectionManager, section_index: usize, byte: u8) !void {
|
||||||
|
try section_manager.sections.items[section_index].bytes.append(section_manager.allocator, byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn appendToSection(section_manager: *SectionManager, section_index: usize, bytes: []const u8) !void {
|
||||||
|
try section_manager.sections.items[section_index].bytes.appendSlice(section_manager.allocator, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSectionOffset(section_manager: *SectionManager, section_index: usize) usize {
|
||||||
|
return section_manager.sections.items[section_index].bytes.items.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getCodeOffset(section_manager: *SectionManager) usize {
|
||||||
|
return section_manager.getSectionOffset(text_section_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn appendCode(section_manager: *SectionManager, code: []const u8) !void {
|
||||||
|
try section_manager.appendToSection(text_section_index, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn appendCodeByte(section_manager: *SectionManager, code_byte: u8) !void {
|
||||||
|
try section_manager.appendByteToSection(text_section_index, code_byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
const text_section_index = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Result = struct {
|
||||||
|
section_manager: SectionManager,
|
||||||
|
entry_point: u32,
|
||||||
|
target: std.Target,
|
||||||
|
|
||||||
|
pub fn create(section_manager: SectionManager, target: std.Target, entry_point_index: u32) !Result {
|
||||||
|
var result = Result{
|
||||||
|
.section_manager = section_manager,
|
||||||
|
.target = target,
|
||||||
|
.entry_point = entry_point_index,
|
||||||
|
};
|
||||||
|
|
||||||
return result;
|
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 {
|
|
||||||
image.writeToSection(text_section_index, code);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn appendCodeByte(image: *Result, code_byte: u8) void {
|
|
||||||
image.writeByteToSection(text_section_index, code_byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType {
|
fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType {
|
||||||
if (@import("builtin").cpu.arch == .aarch64 and @import("builtin").os.tag == .macos) {
|
if (@import("builtin").cpu.arch == .aarch64 and @import("builtin").os.tag == .macos) {
|
||||||
data_structures.pthread_jit_write_protect_np(true);
|
data_structures.pthread_jit_write_protect_np(true);
|
||||||
|
@ -15,6 +15,9 @@ const AutoArrayHashMap = data_structures.AutoArrayHashMap;
|
|||||||
const AutoHashMap = data_structures.AutoHashMap;
|
const AutoHashMap = data_structures.AutoHashMap;
|
||||||
const StringKeyMap = data_structures.StringKeyMap;
|
const StringKeyMap = data_structures.StringKeyMap;
|
||||||
|
|
||||||
|
const emit = @import("emit.zig");
|
||||||
|
const SectionManager = emit.SectionManager;
|
||||||
|
|
||||||
pub const Logger = enum {
|
pub const Logger = enum {
|
||||||
function,
|
function,
|
||||||
phi_removal,
|
phi_removal,
|
||||||
@ -40,9 +43,9 @@ pub const Result = struct {
|
|||||||
stack_references: BlockList(StackReference) = .{},
|
stack_references: BlockList(StackReference) = .{},
|
||||||
string_literals: BlockList(StringLiteral) = .{},
|
string_literals: BlockList(StringLiteral) = .{},
|
||||||
casts: BlockList(Cast) = .{},
|
casts: BlockList(Cast) = .{},
|
||||||
readonly_data: ArrayList(u8) = .{},
|
|
||||||
module: *Module,
|
module: *Module,
|
||||||
entry_point: Function.Index = Function.Index.invalid,
|
entry_point: Function.Index = Function.Index.invalid,
|
||||||
|
section_manager: emit.SectionManager,
|
||||||
|
|
||||||
pub fn getFunctionName(ir: *Result, function_index: Function.Declaration.Index) []const u8 {
|
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)).?).?;
|
return ir.module.getName(ir.module.function_name_map.get(@bitCast(function_index)).?).?;
|
||||||
@ -54,10 +57,26 @@ pub fn initialize(compilation: *Compilation, module: *Module) !*Result {
|
|||||||
const builder = try compilation.base_allocator.create(Builder);
|
const builder = try compilation.base_allocator.create(Builder);
|
||||||
builder.* = .{
|
builder.* = .{
|
||||||
.allocator = compilation.base_allocator,
|
.allocator = compilation.base_allocator,
|
||||||
.module = module,
|
.ir = .{
|
||||||
|
.module = module,
|
||||||
|
.section_manager = SectionManager{
|
||||||
|
.allocator = compilation.base_allocator,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.ir.module = module;
|
builder.ir.module = module;
|
||||||
|
_ = try builder.ir.section_manager.addSection(.{
|
||||||
|
.name = ".text",
|
||||||
|
.size_guess = 0,
|
||||||
|
.alignment = 0x1000,
|
||||||
|
.flags = .{
|
||||||
|
.execute = true,
|
||||||
|
.read = true,
|
||||||
|
.write = false,
|
||||||
|
},
|
||||||
|
.type = .loadable_program,
|
||||||
|
});
|
||||||
|
|
||||||
var sema_function_index = function_iterator.getCurrentIndex();
|
var sema_function_index = function_iterator.getCurrentIndex();
|
||||||
while (function_iterator.next()) |sema_function| {
|
while (function_iterator.next()) |sema_function| {
|
||||||
@ -260,7 +279,6 @@ pub const Instruction = union(enum) {
|
|||||||
|
|
||||||
pub const StringLiteral = struct {
|
pub const StringLiteral = struct {
|
||||||
offset: u32,
|
offset: u32,
|
||||||
hash: u32,
|
|
||||||
|
|
||||||
pub const List = BlockList(@This());
|
pub const List = BlockList(@This());
|
||||||
pub const Index = List.Index;
|
pub const Index = List.Index;
|
||||||
@ -395,10 +413,7 @@ pub const Function = struct {
|
|||||||
|
|
||||||
pub const Builder = struct {
|
pub const Builder = struct {
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
ir: Result = .{
|
ir: Result,
|
||||||
.module = undefined,
|
|
||||||
},
|
|
||||||
module: *Module,
|
|
||||||
current_function_index: Function.Index = Function.Index.invalid,
|
current_function_index: Function.Index = Function.Index.invalid,
|
||||||
|
|
||||||
fn currentFunction(builder: *Builder) *Function {
|
fn currentFunction(builder: *Builder) *Function {
|
||||||
@ -406,7 +421,7 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn buildFunction(builder: *Builder, sema_function: Compilation.Function) !Function.Index {
|
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 sema_prototype = builder.ir.module.function_prototypes.get(builder.ir.module.types.get(sema_function.prototype).function);
|
||||||
const function_declaration_allocation = try builder.ir.function_declarations.addOne(builder.allocator);
|
const function_declaration_allocation = try builder.ir.function_declarations.addOne(builder.allocator);
|
||||||
const function_declaration = function_declaration_allocation.ptr;
|
const function_declaration = function_declaration_allocation.ptr;
|
||||||
function_declaration.* = .{
|
function_declaration.* = .{
|
||||||
@ -420,7 +435,7 @@ pub const Builder = struct {
|
|||||||
if (sema_prototype.arguments) |sema_arguments| {
|
if (sema_prototype.arguments) |sema_arguments| {
|
||||||
try function_declaration.arguments.ensureTotalCapacity(builder.allocator, @intCast(sema_arguments.len));
|
try function_declaration.arguments.ensureTotalCapacity(builder.allocator, @intCast(sema_arguments.len));
|
||||||
for (sema_arguments) |sema_argument_declaration_index| {
|
for (sema_arguments) |sema_argument_declaration_index| {
|
||||||
const sema_argument_declaration = builder.module.declarations.get(sema_argument_declaration_index);
|
const sema_argument_declaration = builder.ir.module.declarations.get(sema_argument_declaration_index);
|
||||||
const argument_allocation = try builder.ir.arguments.append(builder.allocator, .{
|
const argument_allocation = try builder.ir.arguments.append(builder.allocator, .{
|
||||||
.type = try builder.translateType(sema_argument_declaration.type),
|
.type = try builder.translateType(sema_argument_declaration.type),
|
||||||
});
|
});
|
||||||
@ -445,7 +460,7 @@ pub const Builder = struct {
|
|||||||
// TODO: arguments
|
// TODO: arguments
|
||||||
function.current_basic_block = try builder.newBlock();
|
function.current_basic_block = try builder.newBlock();
|
||||||
|
|
||||||
const return_type = builder.module.types.get(sema_prototype.return_type);
|
const return_type = builder.ir.module.types.get(sema_prototype.return_type);
|
||||||
const is_noreturn = return_type.* == .noreturn;
|
const is_noreturn = return_type.* == .noreturn;
|
||||||
|
|
||||||
if (!is_noreturn) {
|
if (!is_noreturn) {
|
||||||
@ -486,7 +501,7 @@ pub const Builder = struct {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const sema_block = sema_function.getBodyBlock(builder.module);
|
const sema_block = sema_function.getBodyBlock(builder.ir.module);
|
||||||
try builder.block(sema_block, .{ .emit_exit_block = !is_noreturn });
|
try builder.block(sema_block, .{ .emit_exit_block = !is_noreturn });
|
||||||
|
|
||||||
if (!is_noreturn and sema_block.reaches_end) {
|
if (!is_noreturn and sema_block.reaches_end) {
|
||||||
@ -797,7 +812,7 @@ pub const Builder = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn emitSyscallArgument(builder: *Builder, sema_syscall_argument_value_index: Compilation.Value.Index) !Instruction.Index {
|
fn emitSyscallArgument(builder: *Builder, sema_syscall_argument_value_index: Compilation.Value.Index) !Instruction.Index {
|
||||||
const sema_syscall_argument_value = builder.module.values.get(sema_syscall_argument_value_index);
|
const sema_syscall_argument_value = builder.ir.module.values.get(sema_syscall_argument_value_index);
|
||||||
return switch (sema_syscall_argument_value.*) {
|
return switch (sema_syscall_argument_value.*) {
|
||||||
.integer => |integer| try builder.processInteger(integer),
|
.integer => |integer| try builder.processInteger(integer),
|
||||||
.sign_extend => |cast_index| try builder.processCast(cast_index, .sign_extend),
|
.sign_extend => |cast_index| try builder.processCast(cast_index, .sign_extend),
|
||||||
@ -807,8 +822,8 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn processCast(builder: *Builder, sema_cast_index: Compilation.Cast.Index, cast_type: CastType) !Instruction.Index {
|
fn processCast(builder: *Builder, sema_cast_index: Compilation.Cast.Index, cast_type: CastType) !Instruction.Index {
|
||||||
const sema_cast = builder.module.casts.get(sema_cast_index);
|
const sema_cast = builder.ir.module.casts.get(sema_cast_index);
|
||||||
const sema_source_value = builder.module.values.get(sema_cast.value);
|
const sema_source_value = builder.ir.module.values.get(sema_cast.value);
|
||||||
const source_value = switch (sema_source_value.*) {
|
const source_value = switch (sema_source_value.*) {
|
||||||
.declaration_reference => |declaration_reference| try builder.loadDeclarationReference(declaration_reference.value),
|
.declaration_reference => |declaration_reference| try builder.loadDeclarationReference(declaration_reference.value),
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
@ -827,7 +842,7 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn processDeclarationReferenceRaw(builder: *Builder, declaration_index: Compilation.Declaration.Index) !Instruction.Index {
|
fn processDeclarationReferenceRaw(builder: *Builder, declaration_index: Compilation.Declaration.Index) !Instruction.Index {
|
||||||
const sema_declaration = builder.module.declarations.get(declaration_index);
|
const sema_declaration = builder.ir.module.declarations.get(declaration_index);
|
||||||
const result = switch (sema_declaration.scope_type) {
|
const result = switch (sema_declaration.scope_type) {
|
||||||
.local => builder.currentFunction().stack_map.get(declaration_index).?,
|
.local => builder.currentFunction().stack_map.get(declaration_index).?,
|
||||||
.global => unreachable,
|
.global => unreachable,
|
||||||
@ -853,14 +868,17 @@ pub const Builder = struct {
|
|||||||
.type = try builder.translateType(integer_value.type),
|
.type = try builder.translateType(integer_value.type),
|
||||||
};
|
};
|
||||||
assert(integer.type.isInteger());
|
assert(integer.type.isInteger());
|
||||||
const load_integer = try builder.append(.{
|
const instruction_allocation = try builder.ir.instructions.append(builder.allocator, .{
|
||||||
.load_integer = integer,
|
.load_integer = integer,
|
||||||
});
|
});
|
||||||
return load_integer;
|
// const load_integer = try builder.append(.{
|
||||||
|
// .load_integer = integer,
|
||||||
|
// });
|
||||||
|
return instruction_allocation.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn processSyscall(builder: *Builder, sema_syscall_index: Compilation.Syscall.Index) anyerror!Instruction.Index {
|
fn processSyscall(builder: *Builder, sema_syscall_index: Compilation.Syscall.Index) anyerror!Instruction.Index {
|
||||||
const sema_syscall = builder.module.syscalls.get(sema_syscall_index);
|
const sema_syscall = builder.ir.module.syscalls.get(sema_syscall_index);
|
||||||
var arguments = try ArrayList(Instruction.Index).initCapacity(builder.allocator, sema_syscall.argument_count + 1);
|
var arguments = try ArrayList(Instruction.Index).initCapacity(builder.allocator, sema_syscall.argument_count + 1);
|
||||||
|
|
||||||
const sema_syscall_number = sema_syscall.number;
|
const sema_syscall_number = sema_syscall.number;
|
||||||
@ -886,12 +904,12 @@ pub const Builder = struct {
|
|||||||
|
|
||||||
fn block(builder: *Builder, sema_block: *Compilation.Block, options: BlockOptions) anyerror!void {
|
fn block(builder: *Builder, sema_block: *Compilation.Block, options: BlockOptions) anyerror!void {
|
||||||
for (sema_block.statements.items) |sema_statement_index| {
|
for (sema_block.statements.items) |sema_statement_index| {
|
||||||
const sema_statement = builder.module.values.get(sema_statement_index);
|
const sema_statement = builder.ir.module.values.get(sema_statement_index);
|
||||||
switch (sema_statement.*) {
|
switch (sema_statement.*) {
|
||||||
.loop => |loop_index| {
|
.loop => |loop_index| {
|
||||||
const sema_loop = builder.module.loops.get(loop_index);
|
const sema_loop = builder.ir.module.loops.get(loop_index);
|
||||||
const sema_loop_condition = builder.module.values.get(sema_loop.condition);
|
const sema_loop_condition = builder.ir.module.values.get(sema_loop.condition);
|
||||||
const sema_loop_body = builder.module.values.get(sema_loop.body);
|
const sema_loop_body = builder.ir.module.values.get(sema_loop.body);
|
||||||
const condition: Compilation.Value.Index = switch (sema_loop_condition.*) {
|
const condition: Compilation.Value.Index = switch (sema_loop_condition.*) {
|
||||||
.bool => |bool_value| switch (bool_value) {
|
.bool => |bool_value| switch (bool_value) {
|
||||||
true => Compilation.Value.Index.invalid,
|
true => Compilation.Value.Index.invalid,
|
||||||
@ -917,7 +935,7 @@ pub const Builder = struct {
|
|||||||
.destination = loop_head_block,
|
.destination = loop_head_block,
|
||||||
});
|
});
|
||||||
|
|
||||||
const sema_body_block = builder.module.blocks.get(sema_loop_body.block);
|
const sema_body_block = builder.ir.module.blocks.get(sema_loop_body.block);
|
||||||
builder.currentFunction().current_basic_block = try builder.blockInsideBasicBlock(sema_body_block, loop_body_block);
|
builder.currentFunction().current_basic_block = try builder.blockInsideBasicBlock(sema_body_block, loop_body_block);
|
||||||
if (!loop_prologue_block.invalid) {
|
if (!loop_prologue_block.invalid) {
|
||||||
builder.ir.blocks.get(loop_prologue_block).seal();
|
builder.ir.blocks.get(loop_prologue_block).seal();
|
||||||
@ -947,7 +965,7 @@ pub const Builder = struct {
|
|||||||
.@"unreachable" = {},
|
.@"unreachable" = {},
|
||||||
}),
|
}),
|
||||||
.@"return" => |sema_ret_index| {
|
.@"return" => |sema_ret_index| {
|
||||||
const sema_ret = builder.module.returns.get(sema_ret_index);
|
const sema_ret = builder.ir.module.returns.get(sema_ret_index);
|
||||||
const return_value = try builder.emitReturnValue(sema_ret.value);
|
const return_value = try builder.emitReturnValue(sema_ret.value);
|
||||||
const phi_instruction = builder.ir.instructions.get(builder.currentFunction().return_phi_node);
|
const phi_instruction = builder.ir.instructions.get(builder.currentFunction().return_phi_node);
|
||||||
const phi = switch (phi_instruction.phi.invalid) {
|
const phi = switch (phi_instruction.phi.invalid) {
|
||||||
@ -973,10 +991,10 @@ pub const Builder = struct {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
.declaration => |sema_declaration_index| {
|
.declaration => |sema_declaration_index| {
|
||||||
const sema_declaration = builder.module.declarations.get(sema_declaration_index);
|
const sema_declaration = builder.ir.module.declarations.get(sema_declaration_index);
|
||||||
//logln("Name: {s}\n", .{builder.module.getName(sema_declaration.name).?});
|
//logln("Name: {s}\n", .{builder.module.getName(sema_declaration.name).?});
|
||||||
assert(sema_declaration.scope_type == .local);
|
assert(sema_declaration.scope_type == .local);
|
||||||
const declaration_type = builder.module.types.get(sema_declaration.type);
|
const declaration_type = builder.ir.module.types.get(sema_declaration.type);
|
||||||
switch (declaration_type.*) {
|
switch (declaration_type.*) {
|
||||||
.comptime_int => unreachable,
|
.comptime_int => unreachable,
|
||||||
else => {
|
else => {
|
||||||
@ -1008,7 +1026,7 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emitDeclarationInitValue(builder: *Builder, declaration_init_value_index: Compilation.Value.Index) !Instruction.Index {
|
fn emitDeclarationInitValue(builder: *Builder, declaration_init_value_index: Compilation.Value.Index) !Instruction.Index {
|
||||||
const declaration_init_value = builder.module.values.get(declaration_init_value_index);
|
const declaration_init_value = builder.ir.module.values.get(declaration_init_value_index);
|
||||||
return switch (declaration_init_value.*) {
|
return switch (declaration_init_value.*) {
|
||||||
.call => |call_index| try builder.processCall(call_index),
|
.call => |call_index| try builder.processCall(call_index),
|
||||||
.integer => |integer| try builder.processInteger(integer),
|
.integer => |integer| try builder.processInteger(integer),
|
||||||
@ -1017,7 +1035,7 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emitReturnValue(builder: *Builder, return_value_index: Compilation.Value.Index) !Instruction.Index {
|
fn emitReturnValue(builder: *Builder, return_value_index: Compilation.Value.Index) !Instruction.Index {
|
||||||
const return_value = builder.module.values.get(return_value_index);
|
const return_value = builder.ir.module.values.get(return_value_index);
|
||||||
return switch (return_value.*) {
|
return switch (return_value.*) {
|
||||||
.syscall => |syscall_index| try builder.processSyscall(syscall_index),
|
.syscall => |syscall_index| try builder.processSyscall(syscall_index),
|
||||||
.integer => |integer| try builder.processInteger(integer),
|
.integer => |integer| try builder.processInteger(integer),
|
||||||
@ -1061,7 +1079,7 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emitCallArgument(builder: *Builder, call_argument_value_index: Compilation.Value.Index) !Instruction.Index {
|
fn emitCallArgument(builder: *Builder, call_argument_value_index: Compilation.Value.Index) !Instruction.Index {
|
||||||
const call_argument_value = builder.module.values.get(call_argument_value_index);
|
const call_argument_value = builder.ir.module.values.get(call_argument_value_index);
|
||||||
return switch (call_argument_value.*) {
|
return switch (call_argument_value.*) {
|
||||||
.integer => |integer| try builder.processInteger(integer),
|
.integer => |integer| try builder.processInteger(integer),
|
||||||
.declaration_reference => |declaration_reference| try builder.loadDeclarationReference(declaration_reference.value),
|
.declaration_reference => |declaration_reference| try builder.loadDeclarationReference(declaration_reference.value),
|
||||||
@ -1071,12 +1089,12 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn processCall(builder: *Builder, sema_call_index: Compilation.Call.Index) anyerror!Instruction.Index {
|
fn processCall(builder: *Builder, sema_call_index: Compilation.Call.Index) anyerror!Instruction.Index {
|
||||||
const sema_call = builder.module.calls.get(sema_call_index);
|
const sema_call = builder.ir.module.calls.get(sema_call_index);
|
||||||
const sema_argument_list_index = sema_call.arguments;
|
const sema_argument_list_index = sema_call.arguments;
|
||||||
const argument_list: []const Instruction.Index = switch (sema_argument_list_index.invalid) {
|
const argument_list: []const Instruction.Index = switch (sema_argument_list_index.invalid) {
|
||||||
false => blk: {
|
false => blk: {
|
||||||
var argument_list = ArrayList(Instruction.Index){};
|
var argument_list = ArrayList(Instruction.Index){};
|
||||||
const sema_argument_list = builder.module.argument_lists.get(sema_argument_list_index);
|
const sema_argument_list = builder.ir.module.argument_lists.get(sema_argument_list_index);
|
||||||
try argument_list.ensureTotalCapacity(builder.allocator, sema_argument_list.array.items.len);
|
try argument_list.ensureTotalCapacity(builder.allocator, sema_argument_list.array.items.len);
|
||||||
for (sema_argument_list.array.items) |sema_argument_value_index| {
|
for (sema_argument_list.array.items) |sema_argument_value_index| {
|
||||||
const argument_value_index = try builder.emitCallArgument(sema_argument_value_index);
|
const argument_value_index = try builder.emitCallArgument(sema_argument_value_index);
|
||||||
@ -1088,7 +1106,7 @@ pub const Builder = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const call_index = try builder.call(.{
|
const call_index = try builder.call(.{
|
||||||
.function = switch (builder.module.values.get(sema_call.value).*) {
|
.function = switch (builder.ir.module.values.get(sema_call.value).*) {
|
||||||
.function => |function_index| .{
|
.function => |function_index| .{
|
||||||
.index = function_index.index,
|
.index = function_index.index,
|
||||||
.block = function_index.block,
|
.block = function_index.block,
|
||||||
@ -1106,16 +1124,34 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn processStringLiteral(builder: *Builder, string_literal_hash: u32) !Instruction.Index {
|
fn processStringLiteral(builder: *Builder, string_literal_hash: u32) !Instruction.Index {
|
||||||
const string_literal = builder.module.string_literals.getValue(string_literal_hash).?;
|
const string_literal = builder.ir.module.string_literals.getValue(string_literal_hash).?;
|
||||||
|
|
||||||
const readonly_offset = builder.ir.readonly_data.items.len;
|
if (builder.ir.section_manager.rodata == null) {
|
||||||
try builder.ir.readonly_data.appendSlice(builder.allocator, string_literal);
|
const rodata_index = try builder.ir.section_manager.addSection(.{
|
||||||
try builder.ir.readonly_data.append(builder.allocator, 0);
|
.name = ".rodata",
|
||||||
|
.size_guess = 0,
|
||||||
|
.alignment = 0x1000,
|
||||||
|
.flags = .{
|
||||||
|
.read = true,
|
||||||
|
.write = false,
|
||||||
|
.execute = false,
|
||||||
|
},
|
||||||
|
.type = .loadable_program,
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.ir.section_manager.rodata = @intCast(rodata_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rodata_index = builder.ir.section_manager.rodata orelse unreachable;
|
||||||
|
const rodata_section_offset = builder.ir.section_manager.getSectionOffset(rodata_index);
|
||||||
|
|
||||||
|
try builder.ir.section_manager.appendToSection(rodata_index, string_literal);
|
||||||
|
try builder.ir.section_manager.appendByteToSection(rodata_index, 0);
|
||||||
|
|
||||||
const string_literal_allocation = try builder.ir.string_literals.append(builder.allocator, .{
|
const string_literal_allocation = try builder.ir.string_literals.append(builder.allocator, .{
|
||||||
.offset = @intCast(readonly_offset),
|
.offset = @intCast(rodata_section_offset),
|
||||||
.hash = string_literal_hash,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = try builder.append(.{
|
const result = try builder.append(.{
|
||||||
.load_string_literal = string_literal_allocation.index,
|
.load_string_literal = string_literal_allocation.index,
|
||||||
});
|
});
|
||||||
@ -1123,48 +1159,8 @@ pub const Builder = struct {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn emitValue(builder: *Builder, sema_value_index: Compilation.Value.Index) !Instruction.Index {
|
|
||||||
// const sema_value = builder.module.values.get(sema_value_index).*;
|
|
||||||
// return switch (sema_value) {
|
|
||||||
// .integer => |integer| try builder.append(.{
|
|
||||||
// .integer = integer,
|
|
||||||
// }),
|
|
||||||
// .call => |sema_call_index| try builder.processCall(sema_call_index),
|
|
||||||
// .declaration_reference => |sema_declaration_reference| blk: {
|
|
||||||
// },
|
|
||||||
// .syscall => |sema_syscall_index| try builder.processSyscall(sema_syscall_index),
|
|
||||||
// .string_literal => |string_literal_hash| blk: {
|
|
||||||
// const string_literal = builder.module.string_literals.getValue(string_literal_hash).?;
|
|
||||||
//
|
|
||||||
// const readonly_offset = builder.ir.readonly_data.items.len;
|
|
||||||
// try builder.ir.readonly_data.appendSlice(builder.allocator, string_literal);
|
|
||||||
//
|
|
||||||
// const string_literal_allocation = try builder.ir.string_literals.append(builder.allocator, .{
|
|
||||||
// .offset = @intCast(readonly_offset),
|
|
||||||
// .hash = string_literal_hash,
|
|
||||||
// });
|
|
||||||
// break :blk try builder.append(.{
|
|
||||||
// .string_literal = string_literal_allocation.index,
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// .sign_extend => |sema_cast_index| blk: {
|
|
||||||
// const sema_sign_extend = builder.module.casts.get(sema_cast_index);
|
|
||||||
//
|
|
||||||
// const sign_extend = try builder.ir.casts.append(builder.allocator, .{
|
|
||||||
// .value = try builder.emitValue(sema_sign_extend.value),
|
|
||||||
// .type = try builder.translateType(sema_sign_extend.type),
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// break :blk try builder.append(.{
|
|
||||||
// .sign_extend = sign_extend.index,
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// else => |t| @panic(@tagName(t)),
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn translateType(builder: *Builder, type_index: Compilation.Type.Index) !Type {
|
fn translateType(builder: *Builder, type_index: Compilation.Type.Index) !Type {
|
||||||
const sema_type = builder.module.types.get(type_index);
|
const sema_type = builder.ir.module.types.get(type_index);
|
||||||
return switch (sema_type.*) {
|
return switch (sema_type.*) {
|
||||||
.integer => |integer| switch (integer.bit_count) {
|
.integer => |integer| switch (integer.bit_count) {
|
||||||
8 => .i8,
|
8 => .i8,
|
||||||
|
@ -45,10 +45,10 @@ pub const Logger = enum {
|
|||||||
// .register_allocation_problematic_hint,
|
// .register_allocation_problematic_hint,
|
||||||
// .register_allocation_assignment,
|
// .register_allocation_assignment,
|
||||||
// .register_allocation_reload,
|
// .register_allocation_reload,
|
||||||
// .register_allocation_function_before,
|
.register_allocation_function_before,
|
||||||
// .register_allocation_new_instruction,
|
.register_allocation_new_instruction,
|
||||||
// .register_allocation_new_instruction_function_before,
|
.register_allocation_new_instruction_function_before,
|
||||||
// .register_allocation_instruction_avoid_copy,
|
.register_allocation_instruction_avoid_copy,
|
||||||
.register_allocation_function_after,
|
.register_allocation_function_after,
|
||||||
// .register_allocation_operand_list_verification,
|
// .register_allocation_operand_list_verification,
|
||||||
.encoding,
|
.encoding,
|
||||||
@ -1147,21 +1147,33 @@ const InstructionSelection = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const instruction = mir.ir.instructions.get(ir_instruction_index);
|
const instruction = mir.ir.instructions.get(ir_instruction_index);
|
||||||
if (instruction.* != .stack or !instruction_selection.stack_map.contains(ir_instruction_index)) {
|
const defer_materialization = switch (instruction.*) {
|
||||||
|
.stack => !instruction_selection.stack_map.contains(ir_instruction_index),
|
||||||
|
.load_integer => false,
|
||||||
|
else => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (defer_materialization) {
|
||||||
const ir_type = getIrType(mir.ir, ir_instruction_index);
|
const ir_type = getIrType(mir.ir, ir_instruction_index);
|
||||||
const value_type = resolveType(ir_type);
|
const value_type = resolveType(ir_type);
|
||||||
const register_class = register_classes.get(value_type);
|
const register_class = register_classes.get(value_type);
|
||||||
const new_register = try mir.createVirtualRegister(register_class);
|
const new_register = try mir.createVirtualRegister(register_class);
|
||||||
try instruction_selection.value_map.putNoClobber(mir.allocator, ir_instruction_index, new_register);
|
try instruction_selection.value_map.putNoClobber(mir.allocator, ir_instruction_index, new_register);
|
||||||
return new_register;
|
return new_register;
|
||||||
}
|
} else {
|
||||||
|
const new_register = switch (instruction.*) {
|
||||||
|
.load_integer => try instruction_selection.materializeInteger(mir, ir_instruction_index),
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
try instruction_selection.local_value_map.put(mir.allocator, ir_instruction_index, new_register);
|
||||||
|
|
||||||
unreachable;
|
return new_register;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moving an immediate to a register
|
// Moving an immediate to a register
|
||||||
fn materializeInteger(instruction_selection: *InstructionSelection, mir: *MIR, ir_instruction_index: ir.Instruction.Index) !void {
|
fn materializeInteger(instruction_selection: *InstructionSelection, mir: *MIR, ir_instruction_index: ir.Instruction.Index) !Register {
|
||||||
const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
|
// const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
|
||||||
const integer = mir.ir.instructions.get(ir_instruction_index).load_integer;
|
const integer = mir.ir.instructions.get(ir_instruction_index).load_integer;
|
||||||
const value_type = resolveType(integer.type);
|
const value_type = resolveType(integer.type);
|
||||||
// const destination_register_class = register_classes.get(value_type);
|
// const destination_register_class = register_classes.get(value_type);
|
||||||
@ -1184,6 +1196,8 @@ const InstructionSelection = struct {
|
|||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
};
|
};
|
||||||
const instruction_descriptor = instruction_descriptors.get(instruction_id);
|
const instruction_descriptor = instruction_descriptors.get(instruction_id);
|
||||||
|
const register_class = register_classes.get(value_type);
|
||||||
|
const destination_register = try mir.createVirtualRegister(register_class);
|
||||||
const operand_id = instruction_descriptor.operands[0].id;
|
const operand_id = instruction_descriptor.operands[0].id;
|
||||||
// const register_class = register_classes.get(operand_id);
|
// const register_class = register_classes.get(operand_id);
|
||||||
const destination_operand = Operand{
|
const destination_operand = Operand{
|
||||||
@ -1197,7 +1211,10 @@ const InstructionSelection = struct {
|
|||||||
const xor = try mir.buildInstruction(instruction_selection, instruction_id, &.{
|
const xor = try mir.buildInstruction(instruction_selection, instruction_id, &.{
|
||||||
destination_operand,
|
destination_operand,
|
||||||
});
|
});
|
||||||
|
|
||||||
try instruction_selection.instruction_cache.append(mir.allocator, xor);
|
try instruction_selection.instruction_cache.append(mir.allocator, xor);
|
||||||
|
|
||||||
|
return destination_register;
|
||||||
},
|
},
|
||||||
false => {
|
false => {
|
||||||
const instruction_id: Instruction.Id = switch (value_type) {
|
const instruction_id: Instruction.Id = switch (value_type) {
|
||||||
@ -1216,6 +1233,8 @@ const InstructionSelection = struct {
|
|||||||
|
|
||||||
const instruction_descriptor = instruction_descriptors.get(instruction_id);
|
const instruction_descriptor = instruction_descriptors.get(instruction_id);
|
||||||
const operand_id = instruction_descriptor.operands[0].id;
|
const operand_id = instruction_descriptor.operands[0].id;
|
||||||
|
const register_class = register_classes.get(value_type);
|
||||||
|
const destination_register = try mir.createVirtualRegister(register_class);
|
||||||
|
|
||||||
const destination_operand = Operand{
|
const destination_operand = Operand{
|
||||||
.id = operand_id,
|
.id = operand_id,
|
||||||
@ -1239,6 +1258,8 @@ const InstructionSelection = struct {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try instruction_selection.instruction_cache.append(mir.allocator, instr);
|
try instruction_selection.instruction_cache.append(mir.allocator, instr);
|
||||||
|
|
||||||
|
return destination_register;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1707,19 +1728,9 @@ pub const Operand = struct {
|
|||||||
global_offset: i32 = 0,
|
global_offset: i32 = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const PCRelative = union(enum) {
|
const PCRelative = struct {
|
||||||
function_declaration: MIR.Function.Index,
|
index: u32,
|
||||||
string_literal: ir.StringLiteral.Index,
|
section: u16,
|
||||||
imm32: i32,
|
|
||||||
imm8: i8,
|
|
||||||
|
|
||||||
fn function(ir_function_decl_index: ir.Function.Declaration.Index) Operand {
|
|
||||||
return Operand{
|
|
||||||
.i64i32imm_brtarget = PCRelative{
|
|
||||||
.function_declaration = ir_function_decl_index,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Lea64Mem = struct {
|
const Lea64Mem = struct {
|
||||||
@ -1727,23 +1738,6 @@ pub const Operand = struct {
|
|||||||
scale: u8,
|
scale: u8,
|
||||||
scale_reg: ?Register,
|
scale_reg: ?Register,
|
||||||
displacement: PCRelative,
|
displacement: PCRelative,
|
||||||
|
|
||||||
fn stringLiteral(ir_load_string_literal_index: ir.StringLiteral.Index) Operand {
|
|
||||||
return Operand{
|
|
||||||
.id = .lea64mem,
|
|
||||||
.u = .{
|
|
||||||
.lea64mem = .{
|
|
||||||
.gp64 = null, // rip
|
|
||||||
.scale = 1,
|
|
||||||
.scale_reg = null,
|
|
||||||
.displacement = PCRelative{
|
|
||||||
.string_literal = ir_load_string_literal_index,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.flags = .{},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Immediate = u64;
|
const Immediate = u64;
|
||||||
@ -2137,61 +2131,69 @@ pub const MIR = struct {
|
|||||||
switch (ir_instruction.*) {
|
switch (ir_instruction.*) {
|
||||||
.ret => |ir_ret_index| {
|
.ret => |ir_ret_index| {
|
||||||
const ir_ret = mir.ir.returns.get(ir_ret_index);
|
const ir_ret = mir.ir.returns.get(ir_ret_index);
|
||||||
const value_type = resolveType(getIrType(mir.ir, ir_ret.instruction));
|
switch (ir_ret.instruction.invalid) {
|
||||||
const source_register = try instruction_selection.getRegisterForValue(mir, ir_ret.instruction);
|
true => {
|
||||||
|
const ret = try mir.buildInstruction(instruction_selection, .ret, &.{});
|
||||||
|
try instruction_selection.instruction_cache.append(mir.allocator, ret);
|
||||||
|
},
|
||||||
|
false => {
|
||||||
|
const value_type = resolveType(getIrType(mir.ir, ir_ret.instruction));
|
||||||
|
const source_register = try instruction_selection.getRegisterForValue(mir, ir_ret.instruction);
|
||||||
|
|
||||||
const register_class = register_classes.get(value_type);
|
const register_class = register_classes.get(value_type);
|
||||||
|
|
||||||
const physical_register = Register{
|
const physical_register = Register{
|
||||||
.index = .{
|
.index = .{
|
||||||
.physical = switch (register_class) {
|
.physical = switch (register_class) {
|
||||||
.gp32 => .eax,
|
.gp32 => .eax,
|
||||||
.gp64 => .rax,
|
.gp64 => .rax,
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const operand_id: Operand.Id = switch (register_class) {
|
||||||
|
.gp32 => .gp32,
|
||||||
|
.gp64 => .gp64,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
},
|
};
|
||||||
},
|
|
||||||
};
|
|
||||||
const operand_id: Operand.Id = switch (register_class) {
|
|
||||||
.gp32 => .gp32,
|
|
||||||
.gp64 => .gp64,
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
|
|
||||||
const copy = try mir.buildInstruction(instruction_selection, .copy, &.{
|
const copy = try mir.buildInstruction(instruction_selection, .copy, &.{
|
||||||
Operand{
|
Operand{
|
||||||
.id = operand_id,
|
.id = operand_id,
|
||||||
.u = .{
|
.u = .{
|
||||||
.register = physical_register,
|
.register = physical_register,
|
||||||
},
|
},
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.type = .def,
|
.type = .def,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Operand{
|
Operand{
|
||||||
.id = operand_id,
|
.id = operand_id,
|
||||||
.u = .{
|
.u = .{
|
||||||
.register = source_register,
|
.register = source_register,
|
||||||
},
|
},
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
try instruction_selection.instruction_cache.append(mir.allocator, copy);
|
try instruction_selection.instruction_cache.append(mir.allocator, copy);
|
||||||
|
|
||||||
const ret = try mir.buildInstruction(instruction_selection, .ret, &.{
|
const ret = try mir.buildInstruction(instruction_selection, .ret, &.{
|
||||||
Operand{
|
Operand{
|
||||||
.id = operand_id,
|
.id = operand_id,
|
||||||
.u = .{
|
.u = .{
|
||||||
.register = physical_register,
|
.register = physical_register,
|
||||||
},
|
},
|
||||||
.flags = .{
|
.flags = .{
|
||||||
.implicit = true,
|
.implicit = true,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
try instruction_selection.instruction_cache.append(mir.allocator, ret);
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
try instruction_selection.instruction_cache.append(mir.allocator, ret);
|
|
||||||
},
|
},
|
||||||
.load_integer => try instruction_selection.materializeInteger(mir, ir_instruction_index),
|
.load_integer => unreachable,
|
||||||
.@"unreachable" => try instruction_selection.instruction_cache.append(mir.allocator, try mir.buildInstruction(instruction_selection, .ud2, &.{})),
|
.@"unreachable" => try instruction_selection.instruction_cache.append(mir.allocator, try mir.buildInstruction(instruction_selection, .ud2, &.{})),
|
||||||
.syscall => |ir_syscall_index| {
|
.syscall => |ir_syscall_index| {
|
||||||
const ir_syscall = mir.ir.syscalls.get(ir_syscall_index);
|
const ir_syscall = mir.ir.syscalls.get(ir_syscall_index);
|
||||||
@ -2229,35 +2231,40 @@ pub const MIR = struct {
|
|||||||
try instruction_selection.instruction_cache.append(mir.allocator, argument_copy);
|
try instruction_selection.instruction_cache.append(mir.allocator, argument_copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle syscall return value
|
|
||||||
const syscall = try mir.buildInstruction(instruction_selection, .syscall, &.{});
|
|
||||||
try instruction_selection.instruction_cache.append(mir.allocator, syscall);
|
|
||||||
|
|
||||||
const produce_syscall_return_value = switch (instruction_i == ir_block.instructions.items.len - 2) {
|
const produce_syscall_return_value = switch (instruction_i == ir_block.instructions.items.len - 2) {
|
||||||
true => blk: {
|
true => blk: {
|
||||||
const last_block_instruction = mir.ir.instructions.get(ir_block.instructions.items[ir_block.instructions.items.len - 1]);
|
const last_block_instruction = mir.ir.instructions.get(ir_block.instructions.items[ir_block.instructions.items.len - 1]);
|
||||||
break :blk switch (last_block_instruction.*) {
|
break :blk switch (last_block_instruction.*) {
|
||||||
.@"unreachable" => false,
|
.@"unreachable" => false,
|
||||||
|
.ret => true,
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
false => true,
|
false => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (produce_syscall_return_value) {
|
const physical_return_register = Register{
|
||||||
const physical_return_register = Register{
|
.index = .{
|
||||||
.index = .{
|
.physical = .rax,
|
||||||
.physical = .rax,
|
},
|
||||||
},
|
};
|
||||||
};
|
|
||||||
const physical_return_operand = Operand{
|
const syscall = try mir.buildInstruction(instruction_selection, .syscall, if (produce_syscall_return_value) &.{
|
||||||
|
Operand{
|
||||||
.id = .gp64,
|
.id = .gp64,
|
||||||
.u = .{
|
.u = .{
|
||||||
.register = physical_return_register,
|
.register = physical_return_register,
|
||||||
},
|
},
|
||||||
.flags = .{ .type = .def },
|
.flags = .{
|
||||||
};
|
.type = .def,
|
||||||
|
.implicit = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} else &.{});
|
||||||
|
|
||||||
|
try instruction_selection.instruction_cache.append(mir.allocator, syscall);
|
||||||
|
|
||||||
|
if (produce_syscall_return_value) {
|
||||||
const virtual_return_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
|
const virtual_return_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
|
||||||
const virtual_return_operand = Operand{
|
const virtual_return_operand = Operand{
|
||||||
.id = .gp64,
|
.id = .gp64,
|
||||||
@ -2269,7 +2276,13 @@ pub const MIR = struct {
|
|||||||
|
|
||||||
const syscall_result_copy = try mir.buildInstruction(instruction_selection, .copy, &.{
|
const syscall_result_copy = try mir.buildInstruction(instruction_selection, .copy, &.{
|
||||||
virtual_return_operand,
|
virtual_return_operand,
|
||||||
physical_return_operand,
|
Operand{
|
||||||
|
.id = .gp64,
|
||||||
|
.u = .{
|
||||||
|
.register = physical_return_register,
|
||||||
|
},
|
||||||
|
.flags = .{},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
try instruction_selection.instruction_cache.append(mir.allocator, syscall_result_copy);
|
try instruction_selection.instruction_cache.append(mir.allocator, syscall_result_copy);
|
||||||
}
|
}
|
||||||
@ -2468,12 +2481,16 @@ pub const MIR = struct {
|
|||||||
},
|
},
|
||||||
.call => |ir_call_index| {
|
.call => |ir_call_index| {
|
||||||
const ir_call = mir.ir.calls.get(ir_call_index);
|
const ir_call = mir.ir.calls.get(ir_call_index);
|
||||||
for (ir_call.arguments, 0..) |ir_argument_index, index| {
|
var argument_index = ir_call.arguments.len;
|
||||||
|
while (argument_index > 0) {
|
||||||
|
argument_index -= 1;
|
||||||
|
|
||||||
|
const ir_argument_index = ir_call.arguments[argument_index];
|
||||||
// print("index: {}", .{index});
|
// print("index: {}", .{index});
|
||||||
const source_register = try instruction_selection.getRegisterForValue(mir, ir_argument_index);
|
const source_register = try instruction_selection.getRegisterForValue(mir, ir_argument_index);
|
||||||
const source_value_type = resolveType(getIrType(mir.ir, ir_argument_index));
|
const source_value_type = resolveType(getIrType(mir.ir, ir_argument_index));
|
||||||
const source_register_class = register_classes.get(source_value_type);
|
const source_register_class = register_classes.get(source_value_type);
|
||||||
const argument_register = calling_convention.argument_registers.get(source_register_class)[index];
|
const argument_register = calling_convention.argument_registers.get(source_register_class)[argument_index];
|
||||||
// print("Argument register: {}", .{argument_register});
|
// print("Argument register: {}", .{argument_register});
|
||||||
|
|
||||||
const destination_register = Register{
|
const destination_register = Register{
|
||||||
@ -2487,6 +2504,7 @@ pub const MIR = struct {
|
|||||||
.gp64 => .gp64,
|
.gp64 => .gp64,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
const source_operand = Operand{
|
const source_operand = Operand{
|
||||||
.id = operand_id,
|
.id = operand_id,
|
||||||
.u = .{
|
.u = .{
|
||||||
@ -2494,6 +2512,7 @@ pub const MIR = struct {
|
|||||||
},
|
},
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
const destination_operand = Operand{
|
const destination_operand = Operand{
|
||||||
.id = operand_id,
|
.id = operand_id,
|
||||||
.u = .{
|
.u = .{
|
||||||
@ -2515,7 +2534,8 @@ pub const MIR = struct {
|
|||||||
.id = .i64i32imm_brtarget,
|
.id = .i64i32imm_brtarget,
|
||||||
.u = .{
|
.u = .{
|
||||||
.pc_relative = .{
|
.pc_relative = .{
|
||||||
.function_declaration = mir.function_declaration_map.get(ir_call.function).?,
|
.index = @bitCast(mir.function_declaration_map.get(ir_call.function).?),
|
||||||
|
.section = @intCast(mir.ir.section_manager.getTextSectionIndex()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.flags = .{},
|
.flags = .{},
|
||||||
@ -2577,6 +2597,41 @@ pub const MIR = struct {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.load_string_literal => |ir_load_string_literal_index| {
|
||||||
|
const ir_load_string_literal = mir.ir.string_literals.get(ir_load_string_literal_index);
|
||||||
|
const virtual_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index);
|
||||||
|
const virtual_operand = Operand{
|
||||||
|
.id = .gp64,
|
||||||
|
.u = .{
|
||||||
|
.register = virtual_register,
|
||||||
|
},
|
||||||
|
.flags = .{ .type = .def },
|
||||||
|
};
|
||||||
|
const source_operand = Operand{
|
||||||
|
.id = .lea64mem,
|
||||||
|
.u = .{
|
||||||
|
.lea64mem = .{
|
||||||
|
.gp64 = null,
|
||||||
|
.scale = 1,
|
||||||
|
.scale_reg = null,
|
||||||
|
.displacement = Operand.PCRelative{
|
||||||
|
.index = ir_load_string_literal.offset,
|
||||||
|
.section = mir.ir.section_manager.rodata orelse unreachable,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.flags = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
const lea = try mir.buildInstruction(instruction_selection, .lea64r, &.{
|
||||||
|
virtual_operand,
|
||||||
|
source_operand,
|
||||||
|
});
|
||||||
|
|
||||||
|
try instruction_selection.instruction_cache.append(mir.allocator, lea);
|
||||||
|
|
||||||
|
try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, virtual_register, false);
|
||||||
|
},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3098,6 +3153,10 @@ pub const MIR = struct {
|
|||||||
switch (instruction.id) {
|
switch (instruction.id) {
|
||||||
.mov32rm => {},
|
.mov32rm => {},
|
||||||
.mov32r0 => {},
|
.mov32r0 => {},
|
||||||
|
.mov32ri => {},
|
||||||
|
.mov64rm => {},
|
||||||
|
.lea64r => {},
|
||||||
|
.mov32ri64 => {},
|
||||||
.copy => {
|
.copy => {
|
||||||
const operand_index = instruction.operands.items[1];
|
const operand_index = instruction.operands.items[1];
|
||||||
const operand = mir.operands.get(operand_index);
|
const operand = mir.operands.get(operand_index);
|
||||||
@ -3682,49 +3741,59 @@ pub const MIR = struct {
|
|||||||
const gp_register_encoding: Encoding.GP64 = switch (physical_register) {
|
const gp_register_encoding: Encoding.GP64 = switch (physical_register) {
|
||||||
.rax => .a,
|
.rax => .a,
|
||||||
.rdi => .di,
|
.rdi => .di,
|
||||||
|
.rsi => .si,
|
||||||
|
.rdx => .d,
|
||||||
|
.rcx => .c,
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
};
|
};
|
||||||
|
|
||||||
return gp_register_encoding;
|
return gp_register_encoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn computeStackSize(stack_objects: []const StackObject) u32 {
|
||||||
|
var result: u32 = 0;
|
||||||
|
|
||||||
|
for (stack_objects) |stack_object| {
|
||||||
|
result += @intCast(stack_object.size);
|
||||||
|
result = std.mem.alignForward(u32, result, stack_object.alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn computeStackOffset(stack_objects: []const StackObject) i32 {
|
||||||
|
const stack_size = computeStackSize(stack_objects);
|
||||||
|
return -@as(i32, @intCast(stack_size));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn encode(mir: *MIR) !*emit.Result {
|
pub fn encode(mir: *MIR) !*emit.Result {
|
||||||
const image = try mir.allocator.create(emit.Result);
|
const image = try mir.allocator.create(emit.Result);
|
||||||
image.* = try emit.Result.create(mir.allocator, mir.target, mir.entry_point);
|
image.* = try emit.Result.create(mir.ir.section_manager, mir.target, mir.entry_point);
|
||||||
|
|
||||||
var function_iterator = mir.functions.iterator();
|
var function_iterator = mir.functions.iterator();
|
||||||
|
|
||||||
var function_offsets = std.AutoArrayHashMapUnmanaged(Function.Index, u32){};
|
var function_offsets = std.AutoArrayHashMapUnmanaged(Function.Index, u32){};
|
||||||
try function_offsets.ensureTotalCapacity(mir.allocator, mir.functions.len);
|
try function_offsets.ensureTotalCapacity(mir.allocator, mir.functions.len);
|
||||||
try image.sections.items[0].symbol_table.ensureTotalCapacity(mir.allocator, mir.functions.len);
|
try image.section_manager.getTextSection().symbol_table.ensureTotalCapacity(mir.allocator, mir.functions.len);
|
||||||
|
|
||||||
while (function_iterator.nextPointer()) |function| {
|
while (function_iterator.nextPointer()) |function| {
|
||||||
const function_index = mir.functions.indexOf(function);
|
const function_index = mir.functions.indexOf(function);
|
||||||
logln(.codegen, .encoding, "\n{s}:", .{function.name});
|
logln(.codegen, .encoding, "\n{s}:", .{function.name});
|
||||||
|
|
||||||
const function_offset: u32 = @intCast(image.getTextSection().index);
|
const function_offset: u32 = @intCast(image.section_manager.getCodeOffset());
|
||||||
|
|
||||||
function_offsets.putAssumeCapacityNoClobber(function_index, function_offset);
|
function_offsets.putAssumeCapacityNoClobber(function_index, function_offset);
|
||||||
image.sections.items[0].symbol_table.putAssumeCapacityNoClobber(function.name, function_offset);
|
image.section_manager.getTextSection().symbol_table.putAssumeCapacityNoClobber(function.name, function_offset);
|
||||||
|
|
||||||
const stack_size = blk: {
|
const stack_size = std.mem.alignForward(u32, computeStackSize(function.instruction_selection.stack_objects.items), 0x10);
|
||||||
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) {
|
if (stack_size != 0) {
|
||||||
image.appendCodeByte(0x55); // push rbp
|
try image.section_manager.appendCodeByte(0x55); // push rbp
|
||||||
image.appendCode(&.{ 0x48, 0x89, 0xe5 }); // mov rbp, rsp
|
try image.section_manager.appendCode(&.{ 0x48, 0x89, 0xe5 }); // mov rbp, rsp
|
||||||
|
|
||||||
// sub rsp, stack_offset
|
// sub rsp, stack_offset
|
||||||
if (std.math.cast(u8, stack_size)) |stack_size_u8| {
|
if (std.math.cast(u8, stack_size)) |stack_size_u8| {
|
||||||
image.appendCode(&.{ 0x48, 0x83, 0xec, stack_size_u8 });
|
try image.section_manager.appendCode(&.{ 0x48, 0x83, 0xec, stack_size_u8 });
|
||||||
} else {
|
} else {
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
@ -3735,7 +3804,7 @@ pub const MIR = struct {
|
|||||||
for (block.instructions.items) |instruction_index| {
|
for (block.instructions.items) |instruction_index| {
|
||||||
const instruction = mir.instructions.get(instruction_index);
|
const instruction = mir.instructions.get(instruction_index);
|
||||||
|
|
||||||
const instruction_offset = image.getTextSection().index;
|
const instruction_offset = image.section_manager.getCodeOffset();
|
||||||
|
|
||||||
switch (instruction.id) {
|
switch (instruction.id) {
|
||||||
.mov32r0 => {
|
.mov32r0 => {
|
||||||
@ -3745,14 +3814,14 @@ pub const MIR = struct {
|
|||||||
const new_instruction_id = Instruction.Id.xor32rr;
|
const new_instruction_id = Instruction.Id.xor32rr;
|
||||||
const instruction_descriptor = instruction_descriptors.get(new_instruction_id);
|
const instruction_descriptor = instruction_descriptors.get(new_instruction_id);
|
||||||
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
||||||
image.appendCodeByte(opcode);
|
try image.section_manager.appendCodeByte(opcode);
|
||||||
const direct = true;
|
const direct = true;
|
||||||
const modrm = ModRm{
|
const modrm = ModRm{
|
||||||
.rm = @intCast(@intFromEnum(gp_register_encoding)),
|
.rm = @intCast(@intFromEnum(gp_register_encoding)),
|
||||||
.reg = @intCast(@intFromEnum(gp_register_encoding)),
|
.reg = @intCast(@intFromEnum(gp_register_encoding)),
|
||||||
.mod = @as(u2, @intFromBool(direct)) << 1 | @intFromBool(direct),
|
.mod = @as(u2, @intFromBool(direct)) << 1 | @intFromBool(direct),
|
||||||
};
|
};
|
||||||
image.appendCodeByte(@bitCast(modrm));
|
try image.section_manager.appendCodeByte(@bitCast(modrm));
|
||||||
},
|
},
|
||||||
.ret => {},
|
.ret => {},
|
||||||
.mov32mr => {
|
.mov32mr => {
|
||||||
@ -3765,30 +3834,61 @@ pub const MIR = struct {
|
|||||||
const memory = destination_operand.u.memory;
|
const memory = destination_operand.u.memory;
|
||||||
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||||
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
||||||
image.appendCodeByte(opcode);
|
try image.section_manager.appendCodeByte(opcode);
|
||||||
|
|
||||||
const modrm = ModRm{
|
const modrm = ModRm{
|
||||||
.rm = @intFromEnum(Encoding.GP32.bp),
|
.rm = @intFromEnum(Encoding.GP32.bp),
|
||||||
.reg = @intCast(@intFromEnum(source_gp32)),
|
.reg = @intCast(@intFromEnum(source_gp32)),
|
||||||
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
|
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
|
||||||
};
|
};
|
||||||
image.appendCodeByte(@bitCast(modrm));
|
try image.section_manager.appendCodeByte(@bitCast(modrm));
|
||||||
|
|
||||||
switch (memory.addressing_mode.base) {
|
switch (memory.addressing_mode.base) {
|
||||||
.frame_index => |frame_index| {
|
.frame_index => |frame_index| {
|
||||||
const stack_offset = blk: {
|
const stack_offset = computeStackOffset(function.instruction_selection.stack_objects.items[0 .. frame_index + 1]);
|
||||||
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 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];
|
const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes];
|
||||||
image.appendCode(stack_bytes);
|
try image.section_manager.appendCode(stack_bytes);
|
||||||
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.mov64mr => {
|
||||||
|
assert(instruction.operands.items.len == 2);
|
||||||
|
|
||||||
|
const rex = Rex{
|
||||||
|
.b = false,
|
||||||
|
.x = false,
|
||||||
|
.r = false,
|
||||||
|
.w = true,
|
||||||
|
};
|
||||||
|
try image.section_manager.appendCodeByte(@bitCast(rex));
|
||||||
|
|
||||||
|
const source_operand = mir.operands.get(instruction.operands.items[1]);
|
||||||
|
const source_gp64 = getGP64Encoding(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);
|
||||||
|
try image.section_manager.appendCodeByte(opcode);
|
||||||
|
|
||||||
|
const modrm = ModRm{
|
||||||
|
.rm = @intFromEnum(Encoding.GP64.bp),
|
||||||
|
.reg = @intCast(@intFromEnum(source_gp64)),
|
||||||
|
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
|
||||||
|
};
|
||||||
|
try image.section_manager.appendCodeByte(@bitCast(modrm));
|
||||||
|
|
||||||
|
switch (memory.addressing_mode.base) {
|
||||||
|
.frame_index => |frame_index| {
|
||||||
|
const stack_offset = computeStackOffset(function.instruction_selection.stack_objects.items[0 .. frame_index + 1]);
|
||||||
|
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];
|
||||||
|
try image.section_manager.appendCode(stack_bytes);
|
||||||
},
|
},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
}
|
}
|
||||||
@ -3798,7 +3898,7 @@ pub const MIR = struct {
|
|||||||
|
|
||||||
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||||
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
||||||
image.appendCodeByte(opcode);
|
try image.section_manager.appendCodeByte(opcode);
|
||||||
|
|
||||||
const destination_operand = mir.operands.get(instruction.operands.items[0]);
|
const destination_operand = mir.operands.get(instruction.operands.items[0]);
|
||||||
const destination_gp32 = getGP32Encoding(destination_operand.*);
|
const destination_gp32 = getGP32Encoding(destination_operand.*);
|
||||||
@ -3812,27 +3912,73 @@ pub const MIR = struct {
|
|||||||
.reg = @intCast(@intFromEnum(destination_gp32)),
|
.reg = @intCast(@intFromEnum(destination_gp32)),
|
||||||
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
|
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
|
||||||
};
|
};
|
||||||
image.appendCodeByte(@bitCast(modrm));
|
try image.section_manager.appendCodeByte(@bitCast(modrm));
|
||||||
|
|
||||||
switch (source_memory.addressing_mode.base) {
|
switch (source_memory.addressing_mode.base) {
|
||||||
.frame_index => |frame_index| {
|
.frame_index => |frame_index| {
|
||||||
const stack_offset = blk: {
|
const stack_offset = computeStackOffset(function.instruction_selection.stack_objects.items[0 .. frame_index + 1]);
|
||||||
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 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];
|
const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes];
|
||||||
image.appendCode(stack_bytes);
|
try image.section_manager.appendCode(stack_bytes);
|
||||||
},
|
},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.mov64rm => {
|
||||||
|
assert(instruction.operands.items.len == 2);
|
||||||
|
|
||||||
|
const rex = Rex{
|
||||||
|
.b = false,
|
||||||
|
.x = false,
|
||||||
|
.r = false,
|
||||||
|
.w = true,
|
||||||
|
};
|
||||||
|
try image.section_manager.appendCodeByte(@bitCast(rex));
|
||||||
|
|
||||||
|
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||||
|
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
||||||
|
try image.section_manager.appendCodeByte(opcode);
|
||||||
|
|
||||||
|
const destination_operand = mir.operands.get(instruction.operands.items[0]);
|
||||||
|
const destination_gp64 = getGP64Encoding(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.GP64.bp),
|
||||||
|
.reg = @intCast(@intFromEnum(destination_gp64)),
|
||||||
|
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
|
||||||
|
};
|
||||||
|
try image.section_manager.appendCodeByte(@bitCast(modrm));
|
||||||
|
|
||||||
|
switch (source_memory.addressing_mode.base) {
|
||||||
|
.frame_index => |frame_index| {
|
||||||
|
const stack_offset = computeStackOffset(function.instruction_selection.stack_objects.items[0 .. frame_index + 1]);
|
||||||
|
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];
|
||||||
|
try image.section_manager.appendCode(stack_bytes);
|
||||||
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.mov32ri => {
|
||||||
|
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_gp32 = getGP32Encoding(destination_operand.*);
|
||||||
|
|
||||||
|
const opcode = @as(u8, 0xb8) | @as(u3, @intCast(@intFromEnum(destination_gp32)));
|
||||||
|
try image.section_manager.appendCodeByte(opcode);
|
||||||
|
|
||||||
|
try image.section_manager.appendCode(std.mem.asBytes(&source_immediate));
|
||||||
|
},
|
||||||
.mov32ri64 => {
|
.mov32ri64 => {
|
||||||
assert(instruction.operands.items.len == 2);
|
assert(instruction.operands.items.len == 2);
|
||||||
const source_operand = mir.operands.get(instruction.operands.items[1]);
|
const source_operand = mir.operands.get(instruction.operands.items[1]);
|
||||||
@ -3845,9 +3991,9 @@ pub const MIR = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const opcode = @as(u8, 0xb8) | @as(u3, @intCast(@intFromEnum(destination_gp32)));
|
const opcode = @as(u8, 0xb8) | @as(u3, @intCast(@intFromEnum(destination_gp32)));
|
||||||
image.appendCodeByte(opcode);
|
try image.section_manager.appendCodeByte(opcode);
|
||||||
|
|
||||||
image.appendCode(std.mem.asBytes(&source_immediate));
|
try image.section_manager.appendCode(std.mem.asBytes(&source_immediate));
|
||||||
},
|
},
|
||||||
.movsx64rm32 => {
|
.movsx64rm32 => {
|
||||||
assert(instruction.operands.items.len == 2);
|
assert(instruction.operands.items.len == 2);
|
||||||
@ -3864,52 +4010,44 @@ pub const MIR = struct {
|
|||||||
.r = false,
|
.r = false,
|
||||||
.w = true,
|
.w = true,
|
||||||
};
|
};
|
||||||
image.appendCodeByte(@bitCast(rex));
|
try image.section_manager.appendCodeByte(@bitCast(rex));
|
||||||
|
|
||||||
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||||
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
||||||
image.appendCodeByte(opcode);
|
try image.section_manager.appendCodeByte(opcode);
|
||||||
|
|
||||||
const modrm = ModRm{
|
const modrm = ModRm{
|
||||||
.rm = @intFromEnum(Encoding.GP32.bp),
|
.rm = @intFromEnum(Encoding.GP32.bp),
|
||||||
.reg = @intCast(@intFromEnum(destination_register)),
|
.reg = @intCast(@intFromEnum(destination_register)),
|
||||||
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
|
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true),
|
||||||
};
|
};
|
||||||
image.appendCodeByte(@bitCast(modrm));
|
try image.section_manager.appendCodeByte(@bitCast(modrm));
|
||||||
|
|
||||||
switch (source_memory.addressing_mode.base) {
|
switch (source_memory.addressing_mode.base) {
|
||||||
.frame_index => |frame_index| {
|
.frame_index => |frame_index| {
|
||||||
const stack_offset = blk: {
|
const stack_offset = computeStackOffset(function.instruction_selection.stack_objects.items[0 .. frame_index + 1]);
|
||||||
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 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];
|
const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes];
|
||||||
image.appendCode(stack_bytes);
|
try image.section_manager.appendCode(stack_bytes);
|
||||||
},
|
},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.syscall => image.appendCode(&.{ 0x0f, 0x05 }),
|
.syscall => try image.section_manager.appendCode(&.{ 0x0f, 0x05 }),
|
||||||
.ud2 => image.appendCode(&.{ 0x0f, 0x0b }),
|
.ud2 => try image.section_manager.appendCode(&.{ 0x0f, 0x0b }),
|
||||||
.call64pcrel32 => {
|
.call64pcrel32 => {
|
||||||
// TODO: emit relocation
|
// TODO: emit relocation
|
||||||
assert(instruction.operands.items.len == 1);
|
assert(instruction.operands.items.len == 1);
|
||||||
const operand = mir.operands.get(instruction.operands.items[0]);
|
const operand = mir.operands.get(instruction.operands.items[0]);
|
||||||
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||||
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
||||||
image.appendCodeByte(opcode);
|
try image.section_manager.appendCodeByte(opcode);
|
||||||
|
|
||||||
switch (operand.u) {
|
switch (operand.u) {
|
||||||
.pc_relative => |pc_relative| {
|
.pc_relative => |pc_relative| {
|
||||||
// TODO: fix
|
// TODO: fix
|
||||||
const callee = pc_relative.function_declaration;
|
const callee: Function.Index = @bitCast(pc_relative.index);
|
||||||
const caller = function_index;
|
const caller = function_index;
|
||||||
|
|
||||||
const instruction_len = 5;
|
const instruction_len = 5;
|
||||||
@ -3918,9 +4056,10 @@ pub const MIR = struct {
|
|||||||
const callee_offset: i64 = @intCast(function_offsets.get(callee).?);
|
const callee_offset: i64 = @intCast(function_offsets.get(callee).?);
|
||||||
const caller_offset: i64 = @intCast(instruction_offset + instruction_len);
|
const caller_offset: i64 = @intCast(instruction_offset + instruction_len);
|
||||||
const offset: i32 = @intCast(callee_offset - caller_offset);
|
const offset: i32 = @intCast(callee_offset - caller_offset);
|
||||||
image.appendCode(std.mem.asBytes(&offset));
|
try image.section_manager.appendCode(std.mem.asBytes(&offset));
|
||||||
} else {
|
} else {
|
||||||
image.appendCode(&.{ 0, 0, 0, 0 });
|
// TODO: handle relocation
|
||||||
|
try image.section_manager.appendCode(&.{ 0, 0, 0, 0 });
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3933,12 +4072,9 @@ pub const MIR = struct {
|
|||||||
const source_operand = mir.operands.get(instruction.operands.items[1]);
|
const source_operand = mir.operands.get(instruction.operands.items[1]);
|
||||||
assert(destination_operand.id == source_operand.id);
|
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) {
|
switch (destination_operand.id) {
|
||||||
.gp32 => {
|
.gp32 => {
|
||||||
image.appendCodeByte(0x89);
|
try image.section_manager.appendCodeByte(0x89);
|
||||||
|
|
||||||
const destination_register = getGP32Encoding(destination_operand.*);
|
const destination_register = getGP32Encoding(destination_operand.*);
|
||||||
const source_register = getGP32Encoding(source_operand.*);
|
const source_register = getGP32Encoding(source_operand.*);
|
||||||
@ -3947,7 +4083,79 @@ pub const MIR = struct {
|
|||||||
.reg = @intCast(@intFromEnum(source_register)),
|
.reg = @intCast(@intFromEnum(source_register)),
|
||||||
.mod = @as(u2, @intFromBool(true)) << 1 | @intFromBool(true),
|
.mod = @as(u2, @intFromBool(true)) << 1 | @intFromBool(true),
|
||||||
};
|
};
|
||||||
image.appendCodeByte(@bitCast(modrm));
|
|
||||||
|
try image.section_manager.appendCodeByte(@bitCast(modrm));
|
||||||
|
},
|
||||||
|
.gp64 => {
|
||||||
|
const rex = Rex{
|
||||||
|
.b = false,
|
||||||
|
.x = false,
|
||||||
|
.r = false,
|
||||||
|
.w = true,
|
||||||
|
};
|
||||||
|
try image.section_manager.appendCodeByte(@bitCast(rex));
|
||||||
|
|
||||||
|
try image.section_manager.appendCodeByte(0x89);
|
||||||
|
|
||||||
|
const destination_register = getGP64Encoding(destination_operand.*);
|
||||||
|
const source_register = getGP64Encoding(source_operand.*);
|
||||||
|
const modrm = ModRm{
|
||||||
|
.rm = @intCast(@intFromEnum(destination_register)),
|
||||||
|
.reg = @intCast(@intFromEnum(source_register)),
|
||||||
|
.mod = @as(u2, @intFromBool(true)) << 1 | @intFromBool(true),
|
||||||
|
};
|
||||||
|
try image.section_manager.appendCodeByte(@bitCast(modrm));
|
||||||
|
},
|
||||||
|
else => |t| @panic(@tagName(t)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.lea64r => {
|
||||||
|
assert(instruction.operands.items.len == 2);
|
||||||
|
const rex = Rex{
|
||||||
|
.b = false,
|
||||||
|
.x = false,
|
||||||
|
.r = false,
|
||||||
|
.w = true,
|
||||||
|
};
|
||||||
|
try image.section_manager.appendCodeByte(@bitCast(rex));
|
||||||
|
|
||||||
|
const instruction_descriptor = instruction_descriptors.get(instruction.id);
|
||||||
|
const opcode: u8 = @intCast(instruction_descriptor.opcode);
|
||||||
|
try image.section_manager.appendCodeByte(opcode);
|
||||||
|
|
||||||
|
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 modrm = ModRm{
|
||||||
|
.rm = @intFromEnum(Encoding.GP64.bp),
|
||||||
|
.reg = @intCast(@intFromEnum(destination_register)),
|
||||||
|
.mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(false),
|
||||||
|
};
|
||||||
|
try image.section_manager.appendCodeByte(@bitCast(modrm));
|
||||||
|
|
||||||
|
const source_operand = mir.operands.get(instruction.operands.items[1]);
|
||||||
|
switch (source_operand.u) {
|
||||||
|
.lea64mem => |lea64mem| {
|
||||||
|
assert(lea64mem.gp64 == null);
|
||||||
|
assert(lea64mem.scale == 1);
|
||||||
|
assert(lea64mem.scale_reg == null);
|
||||||
|
|
||||||
|
if (lea64mem.displacement.section == image.section_manager.rodata orelse unreachable) {
|
||||||
|
try image.section_manager.linker_relocations.append(mir.allocator, .{
|
||||||
|
.source = .{
|
||||||
|
.offset = @intCast(image.section_manager.getCodeOffset() + @sizeOf(i32)),
|
||||||
|
.index = image.section_manager.getTextSectionIndex(),
|
||||||
|
},
|
||||||
|
.target = .{
|
||||||
|
.offset = lea64mem.displacement.index,
|
||||||
|
.index = lea64mem.displacement.section,
|
||||||
|
},
|
||||||
|
.offset = -@sizeOf(i32),
|
||||||
|
});
|
||||||
|
try image.section_manager.appendCode(&.{ 0, 0, 0, 0 });
|
||||||
|
} else {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
}
|
}
|
||||||
@ -3955,7 +4163,7 @@ pub const MIR = struct {
|
|||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instruction_offset != image.getTextSection().index) {
|
if (instruction_offset != image.section_manager.getCodeOffset()) {
|
||||||
const print_tags = true;
|
const print_tags = true;
|
||||||
if (print_tags) {
|
if (print_tags) {
|
||||||
var offset = @tagName(instruction.id).len + 2;
|
var offset = @tagName(instruction.id).len + 2;
|
||||||
@ -3965,7 +4173,8 @@ pub const MIR = struct {
|
|||||||
log(.codegen, .encoding, " ", .{});
|
log(.codegen, .encoding, " ", .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (image.getTextSection().content[instruction_offset..image.getTextSection().index]) |byte| {
|
|
||||||
|
for (image.section_manager.getTextSection().bytes.items[instruction_offset..]) |byte| {
|
||||||
log(.codegen, .encoding, "0x{x:0>2} ", .{byte});
|
log(.codegen, .encoding, "0x{x:0>2} ", .{byte});
|
||||||
}
|
}
|
||||||
log(.codegen, .encoding, "\n", .{});
|
log(.codegen, .encoding, "\n", .{});
|
||||||
@ -3982,15 +4191,15 @@ pub const MIR = struct {
|
|||||||
if (stack_size != 0) {
|
if (stack_size != 0) {
|
||||||
// add rsp, stack_offset
|
// add rsp, stack_offset
|
||||||
if (std.math.cast(u8, stack_size)) |stack_size_u8| {
|
if (std.math.cast(u8, stack_size)) |stack_size_u8| {
|
||||||
image.appendCode(&.{ 0x48, 0x83, 0xc4, stack_size_u8 });
|
try image.section_manager.appendCode(&.{ 0x48, 0x83, 0xc4, stack_size_u8 });
|
||||||
} else {
|
} else {
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
image.appendCodeByte(0x5d); // pop rbp
|
try image.section_manager.appendCodeByte(0x5d); // pop rbp
|
||||||
}
|
}
|
||||||
|
|
||||||
image.appendCodeByte(0xc3);
|
try image.section_manager.appendCodeByte(0xc3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4070,11 +4279,18 @@ pub const MIR = struct {
|
|||||||
{
|
{
|
||||||
if (instruction != .copy) {
|
if (instruction != .copy) {
|
||||||
const descriptor = instruction_descriptors.getPtrConst(instruction);
|
const descriptor = instruction_descriptors.getPtrConst(instruction);
|
||||||
if (descriptor.operands.len != operands.len) unreachable;
|
if (descriptor.operands.len == operands.len) {
|
||||||
for (descriptor.operands, operands) |descriptor_operand, operand| {
|
for (descriptor.operands, operands) |descriptor_operand, operand| {
|
||||||
switch (descriptor_operand.id) {
|
switch (descriptor_operand.id) {
|
||||||
.unknown => {},
|
.unknown => {},
|
||||||
else => if (descriptor_operand.id != operand.id) unreachable,
|
else => if (descriptor_operand.id != operand.id) unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (instruction) {
|
||||||
|
.ret => {},
|
||||||
|
.syscall => {},
|
||||||
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ const assert = std.debug.assert;
|
|||||||
pub const Allocator = std.mem.Allocator;
|
pub const Allocator = std.mem.Allocator;
|
||||||
pub const AutoArrayHashMap = std.AutoArrayHashMapUnmanaged;
|
pub const AutoArrayHashMap = std.AutoArrayHashMapUnmanaged;
|
||||||
pub const ArrayList = std.ArrayListUnmanaged;
|
pub const ArrayList = std.ArrayListUnmanaged;
|
||||||
|
pub const ArrayListAligned = std.ArrayListAlignedUnmanaged;
|
||||||
pub const AutoHashMap = std.AutoHashMapUnmanaged;
|
pub const AutoHashMap = std.AutoHashMapUnmanaged;
|
||||||
pub const HashMap = std.HashMapUnmanaged;
|
pub const HashMap = std.HashMapUnmanaged;
|
||||||
pub const SegmentedList = std.SegmentedList;
|
pub const SegmentedList = std.SegmentedList;
|
||||||
|
@ -982,11 +982,37 @@ const Analyzer = struct {
|
|||||||
fn processStringLiteral(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !u32 {
|
fn processStringLiteral(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !u32 {
|
||||||
const string_literal_node = analyzer.getScopeNode(scope_index, node_index);
|
const string_literal_node = analyzer.getScopeNode(scope_index, node_index);
|
||||||
assert(string_literal_node.id == .string_literal);
|
assert(string_literal_node.id == .string_literal);
|
||||||
const string_literal = analyzer.tokenStringLiteral(scope_index, string_literal_node.token);
|
const original_string_literal = analyzer.tokenStringLiteral(scope_index, string_literal_node.token);
|
||||||
|
const string_literal = for (original_string_literal) |ch| {
|
||||||
|
if (ch == '\\') {
|
||||||
|
break try fixupStringLiteral(analyzer.allocator, original_string_literal);
|
||||||
|
}
|
||||||
|
} else original_string_literal;
|
||||||
const string_key = try analyzer.module.addStringLiteral(analyzer.allocator, string_literal);
|
const string_key = try analyzer.module.addStringLiteral(analyzer.allocator, string_literal);
|
||||||
return string_key;
|
return string_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fixupStringLiteral(allocator: Allocator, string_literal: []const u8) ![]const u8 {
|
||||||
|
var result = try ArrayList(u8).initCapacity(allocator, string_literal.len - 1);
|
||||||
|
var i: usize = 0;
|
||||||
|
|
||||||
|
while (i < string_literal.len) : (i += 1) {
|
||||||
|
const ch = string_literal[i];
|
||||||
|
if (ch != '\\') {
|
||||||
|
result.appendAssumeCapacity(ch);
|
||||||
|
} else {
|
||||||
|
const next_ch: u8 = switch (string_literal[i + 1]) {
|
||||||
|
'n' => '\n',
|
||||||
|
else => |next_ch| panic("Unexpected character: {c}, 0x{x}", .{ next_ch, next_ch }),
|
||||||
|
};
|
||||||
|
result.appendAssumeCapacity(next_ch);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.items;
|
||||||
|
}
|
||||||
|
|
||||||
fn functionPrototypeReturnType(analyzer: *Analyzer, function_prototype_index: Function.Prototype.Index) Type.Index {
|
fn functionPrototypeReturnType(analyzer: *Analyzer, function_prototype_index: Function.Prototype.Index) Type.Index {
|
||||||
const function_prototype = analyzer.module.function_prototypes.get(function_prototype_index);
|
const function_prototype = analyzer.module.function_prototypes.get(function_prototype_index);
|
||||||
return function_prototype.return_type;
|
return function_prototype.return_type;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user