From 31185e677945c01338c723cce05219bbb99e31d4 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 10 Nov 2023 17:05:03 -0600 Subject: [PATCH] hello_world: finish producing a hello world exe Section manager redesign Linker relocations Using global strings String literal fixup in sema --- .github/workflows/ci.yml | 2 + src/backend/elf.zig | 309 ++++++++++---------- src/backend/emit.zig | 203 +++++++------ src/backend/intermediate_representation.zig | 147 +++++----- src/backend/x86_64.zig | 164 +++++------ src/data_structures.zig | 1 + src/frontend/semantic_analyzer.zig | 28 +- 7 files changed, 442 insertions(+), 412 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0df45d..9c93eaa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,3 +43,5 @@ jobs: nat/first zig build run -- test/stack/main.nat nat/stack + zig build run -- test/hello_world/main.nat + nat/hello_world diff --git a/src/backend/elf.zig b/src/backend/elf.zig index 7d5d188..06e91ec 100644 --- a/src/backend/elf.zig +++ b/src/backend/elf.zig @@ -10,22 +10,13 @@ const emit = @import("emit.zig"); const page_size = 0x1000; pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, page_size) { - var file = try std.ArrayListAlignedUnmanaged(u8, 0x1000).initCapacity(image.allocator, 0x100000); - _ = try image.insertSection(0, .{ - .name = "", - .size = page_size, - .alignment = page_size, - .flags = .{ - .read = true, - .write = false, - .execute = false, - }, - .type = .loadable_program, - }); + const allocator = image.section_manager.allocator; - const symbol_table_index = try image.addSection(.{ + try image.section_manager.addNullSection(); + + const symbol_table_index = try image.section_manager.addSection(.{ .name = ".symtab", - .size = page_size, + .size_guess = 50, .alignment = @alignOf(SymbolTable.Entry), .flags = .{ .read = false, @@ -34,9 +25,9 @@ pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, pag }, .type = .symbol_table, }); - const string_table_index = try image.addSection(.{ + const string_table_index = try image.section_manager.addSection(.{ .name = ".strtab", - .size = page_size, + .size_guess = 50, .alignment = 1, .flags = .{ .read = false, @@ -45,9 +36,9 @@ pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, pag }, .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", - .size = page_size, + .size_guess = 50, .alignment = 1, .flags = .{ .read = false, @@ -62,7 +53,7 @@ pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, pag const program_header_count = blk: { var result: usize = 0; - for (image.sections.items) |section| { + for (image.section_manager.sections.items) |section| { result += @intFromBool(switch (section.type) { .null => false, .loadable_program => true, @@ -75,7 +66,7 @@ pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, pag 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, .information = 0, .other = 0, @@ -84,154 +75,172 @@ pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, pag .size = 0, })); - image.writeToSection(string_table_index, ""); - image.writeByteToSection(string_table_index, 0); + try image.section_manager.appendToSection(string_table_index, ""); + try image.section_manager.appendByteToSection(string_table_index, 0); symbol_name_offset += 1; - for (image.sections.items) |section| { - image.writeToSection(section_header_string_table_index, section.name); - image.writeByteToSection(section_header_string_table_index, 0); + for (image.section_manager.sections.items) |section| { + try image.section_manager.appendToSection(section_header_string_table_index, section.name); + try image.section_manager.appendByteToSection(section_header_string_table_index, 0); } - { - var program_segment_offset: usize = 0; + try image.section_manager.appendToSection(0, std.mem.asBytes(&Header{ + .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{ - .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), - })); + var program_segment_offset: usize = 0; - for (image.sections.items, 0..) |section, section_index| { - switch (section.type) { - .loadable_program => { - program_segment_offset = std.mem.alignForward(usize, program_segment_offset, section.alignment); - const virtual_address = base_virtual_address + program_segment_offset; - const program_segment_size = switch (section_index) { - 0 => @sizeOf(Header) + @sizeOf(ProgramHeader) * program_header_count, - else => section.index, - }; - image.writeToSection(0, std.mem.asBytes(&ProgramHeader{ - .type = .load, - .flags = ProgramHeader.Flags{ - .executable = section.flags.execute, - .writable = section.flags.write, - .readable = section.flags.read, - }, - .offset = program_segment_offset, - .virtual_address = virtual_address, - .physical_address = virtual_address, - .size_in_file = program_segment_size, - .size_in_memory = program_segment_size, - .alignment = section.alignment, - })); + for (image.section_manager.sections.items, 0..) |section, section_index| { + switch (section.type) { + .loadable_program => { + program_segment_offset = std.mem.alignForward(usize, program_segment_offset, section.alignment); + const virtual_address = base_virtual_address + program_segment_offset; + const program_segment_size = switch (section_index) { + 0 => @sizeOf(Header) + @sizeOf(ProgramHeader) * program_header_count, + else => section.bytes.items.len, + }; - program_segment_offset += program_segment_size; - }, - .null, - .string_table, - .symbol_table, - => {}, - } - } - } - - { - var section_offset: usize = 0; - var section_headers = try ArrayList(SectionHeader).initCapacity(image.allocator, image.sections.items.len); - var section_name_offset: u32 = 0; - - for (image.sections.items, 0..) |section, section_i| { - section_offset = std.mem.alignForward(usize, section_offset, section.alignment); - const virtual_address = base_virtual_address + section_offset; - - for (section.symbol_table.keys(), section.symbol_table.values()) |symbol_name, symbol_offset| { - const symbol_address = virtual_address + symbol_offset; - image.writeToSection(symbol_table_index, std.mem.asBytes(&SymbolTable.Entry{ - .name_offset = symbol_name_offset, - .information = 0x10, - .other = 0, - .section_header_index = @intCast(section_i), - .value = symbol_address, - .size = 0, + try image.section_manager.appendToSection(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, })); - image.writeToSection(string_table_index, symbol_name); - image.writeByteToSection(string_table_index, 0); + program_segment_offset += program_segment_size; + }, + .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]; - file.items.len = section_offset + source.len; - try file.replaceRange(image.allocator, section_offset, source.len, source); + for (image.section_manager.sections.items, 0..) |section, section_i| { + const section_offset = std.mem.alignForward(usize, file.items.len, section.alignment); + const virtual_address = base_virtual_address + section_offset; - 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.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); + if (file.items.len < section_offset) { + try file.appendNTimes(allocator, 0, section_offset - file.items.len); } - const section_header_offset = std.mem.alignForward(usize, section_offset, @alignOf(SectionHeader)); - const section_header_bytes = std.mem.sliceAsBytes(section_headers.items); - try file.ensureTotalCapacity(image.allocator, section_header_offset + section_header_bytes.len); - file.items.len = section_header_offset + section_header_bytes.len; - try file.replaceRange(image.allocator, section_header_offset, section_header_bytes.len, section_header_bytes); + for (section.symbol_table.keys(), section.symbol_table.values()) |symbol_name, symbol_offset| { + const symbol_address = virtual_address + symbol_offset; + try image.section_manager.appendToSection(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, + })); - const _start_offset = blk: { - const entry_offset = image.sections.items[text_section_index].symbol_table.values()[image.entry_point]; - const text_section_virtual_address = section_headers.items[text_section_index].virtual_address; - break :blk text_section_virtual_address + entry_offset; - }; + try image.section_manager.appendToSection(string_table_index, symbol_name); + try image.section_manager.appendByteToSection(string_table_index, 0); - const header: *Header = @ptrCast(file.items.ptr); - header.section_header_offset = section_header_offset; - header.entry = _start_offset; + symbol_name_offset += @intCast(symbol_name.len + 1); + } + + 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; } diff --git a/src/backend/emit.zig b/src/backend/emit.zig index e1395fa..aa2850f 100644 --- a/src/backend/emit.zig +++ b/src/backend/emit.zig @@ -12,6 +12,7 @@ const ir = @import("intermediate_representation.zig"); const data_structures = @import("../data_structures.zig"); const ArrayList = data_structures.ArrayList; +const ArrayListAligned = data_structures.ArrayListAligned; const AutoHashMap = data_structures.AutoHashMap; const mmap = data_structures.mmap; @@ -22,13 +23,12 @@ const macho = @import("macho.zig"); const jit_callconv = .SysV; const Section = struct { - content: []align(page_size) u8, - index: usize = 0, - alignment: u32, - name: []const u8, - flags: Flags, - type: Type, + bytes: ArrayListAligned(u8, page_size), symbol_table: std.StringArrayHashMapUnmanaged(u32) = .{}, + name: []const u8, + alignment: u32, + flags: Section.Flags, + type: Section.Type, const Type = enum { 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: struct { - // text: Section, - // rodata: Section, - // data: Section, - // }, - entry_point: u32, - target: std.Target, + rodata: ?u16 = null, + null: bool = false, + linker_relocations: ArrayList(Relocation) = .{}, 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 { - var result = Result{ - // .sections = .{ - // .text = .{ .content = try mmap(page_size, .{ .executable = true }) }, - // .rodata = .{ .content = try mmap(page_size, .{ .executable = false }) }, - // .data = .{ .content = try mmap(page_size, .{ .executable = false }) }, - // }, - .target = target, - .allocator = allocator, - .entry_point = entry_point_index, - }; + const r = try section_manager.insertSection(index, arguments); + assert(index == r); - _ = try result.addSection(.{ - .name = ".text", - .size = 0x1000, - .alignment = 0x1000, + return index; + } + + 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 = .{ - .execute = true, .read = true, .write = false, + .execute = false, }, .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; } - 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 { if (@import("builtin").cpu.arch == .aarch64 and @import("builtin").os.tag == .macos) { data_structures.pthread_jit_write_protect_np(true); diff --git a/src/backend/intermediate_representation.zig b/src/backend/intermediate_representation.zig index f833d48..91825ad 100644 --- a/src/backend/intermediate_representation.zig +++ b/src/backend/intermediate_representation.zig @@ -15,6 +15,9 @@ const AutoArrayHashMap = data_structures.AutoArrayHashMap; const AutoHashMap = data_structures.AutoHashMap; const StringKeyMap = data_structures.StringKeyMap; +const emit = @import("emit.zig"); +const SectionManager = emit.SectionManager; + pub const Logger = enum { function, phi_removal, @@ -40,9 +43,9 @@ pub const Result = struct { stack_references: BlockList(StackReference) = .{}, string_literals: BlockList(StringLiteral) = .{}, casts: BlockList(Cast) = .{}, - readonly_data: ArrayList(u8) = .{}, module: *Module, entry_point: Function.Index = Function.Index.invalid, + section_manager: emit.SectionManager, 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)).?).?; @@ -54,10 +57,26 @@ pub fn initialize(compilation: *Compilation, module: *Module) !*Result { const builder = try compilation.base_allocator.create(Builder); builder.* = .{ .allocator = compilation.base_allocator, - .module = module, + .ir = .{ + .module = module, + .section_manager = SectionManager{ + .allocator = compilation.base_allocator, + }, + }, }; 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(); while (function_iterator.next()) |sema_function| { @@ -260,7 +279,6 @@ pub const Instruction = union(enum) { pub const StringLiteral = struct { offset: u32, - hash: u32, pub const List = BlockList(@This()); pub const Index = List.Index; @@ -395,10 +413,7 @@ pub const Function = struct { pub const Builder = struct { allocator: Allocator, - ir: Result = .{ - .module = undefined, - }, - module: *Module, + ir: Result, current_function_index: Function.Index = Function.Index.invalid, fn currentFunction(builder: *Builder) *Function { @@ -406,7 +421,7 @@ pub const Builder = struct { } 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 = function_declaration_allocation.ptr; function_declaration.* = .{ @@ -420,7 +435,7 @@ pub const Builder = struct { if (sema_prototype.arguments) |sema_arguments| { try function_declaration.arguments.ensureTotalCapacity(builder.allocator, @intCast(sema_arguments.len)); 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, .{ .type = try builder.translateType(sema_argument_declaration.type), }); @@ -445,7 +460,7 @@ pub const Builder = struct { // TODO: arguments 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; 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 }); 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 { - 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.*) { .integer => |integer| try builder.processInteger(integer), .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 { - const sema_cast = builder.module.casts.get(sema_cast_index); - const sema_source_value = builder.module.values.get(sema_cast.value); + const sema_cast = builder.ir.module.casts.get(sema_cast_index); + const sema_source_value = builder.ir.module.values.get(sema_cast.value); const source_value = switch (sema_source_value.*) { .declaration_reference => |declaration_reference| try builder.loadDeclarationReference(declaration_reference.value), else => |t| @panic(@tagName(t)), @@ -827,7 +842,7 @@ pub const Builder = struct { } 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) { .local => builder.currentFunction().stack_map.get(declaration_index).?, .global => unreachable, @@ -863,7 +878,7 @@ pub const Builder = struct { } 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); const sema_syscall_number = sema_syscall.number; @@ -889,12 +904,12 @@ pub const Builder = struct { fn block(builder: *Builder, sema_block: *Compilation.Block, options: BlockOptions) anyerror!void { 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.*) { .loop => |loop_index| { - const sema_loop = builder.module.loops.get(loop_index); - const sema_loop_condition = builder.module.values.get(sema_loop.condition); - const sema_loop_body = builder.module.values.get(sema_loop.body); + const sema_loop = builder.ir.module.loops.get(loop_index); + const sema_loop_condition = builder.ir.module.values.get(sema_loop.condition); + const sema_loop_body = builder.ir.module.values.get(sema_loop.body); const condition: Compilation.Value.Index = switch (sema_loop_condition.*) { .bool => |bool_value| switch (bool_value) { true => Compilation.Value.Index.invalid, @@ -920,7 +935,7 @@ pub const Builder = struct { .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); if (!loop_prologue_block.invalid) { builder.ir.blocks.get(loop_prologue_block).seal(); @@ -950,7 +965,7 @@ pub const Builder = struct { .@"unreachable" = {}, }), .@"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 phi_instruction = builder.ir.instructions.get(builder.currentFunction().return_phi_node); const phi = switch (phi_instruction.phi.invalid) { @@ -976,10 +991,10 @@ pub const Builder = struct { }); }, .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).?}); 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.*) { .comptime_int => unreachable, else => { @@ -1011,7 +1026,7 @@ pub const Builder = struct { } 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.*) { .call => |call_index| try builder.processCall(call_index), .integer => |integer| try builder.processInteger(integer), @@ -1020,7 +1035,7 @@ pub const Builder = struct { } 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.*) { .syscall => |syscall_index| try builder.processSyscall(syscall_index), .integer => |integer| try builder.processInteger(integer), @@ -1064,7 +1079,7 @@ pub const Builder = struct { } 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.*) { .integer => |integer| try builder.processInteger(integer), .declaration_reference => |declaration_reference| try builder.loadDeclarationReference(declaration_reference.value), @@ -1074,12 +1089,12 @@ pub const Builder = struct { } 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 argument_list: []const Instruction.Index = switch (sema_argument_list_index.invalid) { false => blk: { 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); for (sema_argument_list.array.items) |sema_argument_value_index| { const argument_value_index = try builder.emitCallArgument(sema_argument_value_index); @@ -1091,7 +1106,7 @@ pub const Builder = struct { }; 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| .{ .index = function_index.index, .block = function_index.block, @@ -1109,16 +1124,34 @@ pub const Builder = struct { } 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; - try builder.ir.readonly_data.appendSlice(builder.allocator, string_literal); - try builder.ir.readonly_data.append(builder.allocator, 0); + if (builder.ir.section_manager.rodata == null) { + const rodata_index = try builder.ir.section_manager.addSection(.{ + .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, .{ - .offset = @intCast(readonly_offset), - .hash = string_literal_hash, + .offset = @intCast(rodata_section_offset), }); + const result = try builder.append(.{ .load_string_literal = string_literal_allocation.index, }); @@ -1126,48 +1159,8 @@ pub const Builder = struct { 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 { - const sema_type = builder.module.types.get(type_index); + const sema_type = builder.ir.module.types.get(type_index); return switch (sema_type.*) { .integer => |integer| switch (integer.bit_count) { 8 => .i8, diff --git a/src/backend/x86_64.zig b/src/backend/x86_64.zig index 7c5a2e2..902e781 100644 --- a/src/backend/x86_64.zig +++ b/src/backend/x86_64.zig @@ -1728,19 +1728,9 @@ pub const Operand = struct { global_offset: i32 = 0, }; - const PCRelative = union(enum) { - function_declaration: MIR.Function.Index, - string_literal: ir.StringLiteral.Index, - 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 PCRelative = struct { + index: u32, + section: u16, }; const Lea64Mem = struct { @@ -1748,23 +1738,6 @@ pub const Operand = struct { scale: u8, scale_reg: ?Register, 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; @@ -2561,7 +2534,8 @@ pub const MIR = struct { .id = .i64i32imm_brtarget, .u = .{ .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 = .{}, @@ -2624,6 +2598,7 @@ 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, @@ -2640,7 +2615,8 @@ pub const MIR = struct { .scale = 1, .scale_reg = null, .displacement = Operand.PCRelative{ - .string_literal = ir_load_string_literal_index, + .index = ir_load_string_literal.offset, + .section = mir.ir.section_manager.rodata orelse unreachable, }, }, }, @@ -3792,32 +3768,32 @@ pub const MIR = struct { pub fn encode(mir: *MIR) !*emit.Result { const image = try mir.allocator.create(emit.Result); - image.* = try emit.Result.create(mir.allocator, mir.target, mir.entry_point); + image.* = try emit.Result.create(mir.ir.section_manager, mir.target, mir.entry_point); var function_iterator = mir.functions.iterator(); var function_offsets = std.AutoArrayHashMapUnmanaged(Function.Index, u32){}; try function_offsets.ensureTotalCapacity(mir.allocator, mir.functions.len); - try image.sections.items[0].symbol_table.ensureTotalCapacity(mir.allocator, mir.functions.len); + try image.section_manager.getTextSection().symbol_table.ensureTotalCapacity(mir.allocator, mir.functions.len); while (function_iterator.nextPointer()) |function| { const function_index = mir.functions.indexOf(function); logln(.codegen, .encoding, "\n{s}:", .{function.name}); - const function_offset: u32 = @intCast(image.getTextSection().index); + const function_offset: u32 = @intCast(image.section_manager.getCodeOffset()); 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 = std.mem.alignForward(u32, computeStackSize(function.instruction_selection.stack_objects.items), 0x10); if (stack_size != 0) { - image.appendCodeByte(0x55); // push rbp - image.appendCode(&.{ 0x48, 0x89, 0xe5 }); // mov rbp, rsp + try image.section_manager.appendCodeByte(0x55); // push rbp + try image.section_manager.appendCode(&.{ 0x48, 0x89, 0xe5 }); // mov rbp, rsp // sub rsp, stack_offset if (std.math.cast(u8, stack_size)) |stack_size_u8| { - image.appendCode(&.{ 0x48, 0x83, 0xec, stack_size_u8 }); + try image.section_manager.appendCode(&.{ 0x48, 0x83, 0xec, stack_size_u8 }); } else { unreachable; } @@ -3828,7 +3804,7 @@ pub const MIR = struct { for (block.instructions.items) |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) { .mov32r0 => { @@ -3838,14 +3814,14 @@ pub const MIR = struct { const new_instruction_id = Instruction.Id.xor32rr; const instruction_descriptor = instruction_descriptors.get(new_instruction_id); const opcode: u8 = @intCast(instruction_descriptor.opcode); - image.appendCodeByte(opcode); + try image.section_manager.appendCodeByte(opcode); const direct = true; const modrm = ModRm{ .rm = @intCast(@intFromEnum(gp_register_encoding)), .reg = @intCast(@intFromEnum(gp_register_encoding)), .mod = @as(u2, @intFromBool(direct)) << 1 | @intFromBool(direct), }; - image.appendCodeByte(@bitCast(modrm)); + try image.section_manager.appendCodeByte(@bitCast(modrm)); }, .ret => {}, .mov32mr => { @@ -3858,14 +3834,14 @@ pub const MIR = struct { const memory = destination_operand.u.memory; const instruction_descriptor = instruction_descriptors.get(instruction.id); const opcode: u8 = @intCast(instruction_descriptor.opcode); - image.appendCodeByte(opcode); + try image.section_manager.appendCodeByte(opcode); const modrm = ModRm{ .rm = @intFromEnum(Encoding.GP32.bp), .reg = @intCast(@intFromEnum(source_gp32)), .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true), }; - image.appendCodeByte(@bitCast(modrm)); + try image.section_manager.appendCodeByte(@bitCast(modrm)); switch (memory.addressing_mode.base) { .frame_index => |frame_index| { @@ -3873,7 +3849,7 @@ pub const MIR = struct { const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable; const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes]; - image.appendCode(stack_bytes); + try image.section_manager.appendCode(stack_bytes); }, else => |t| @panic(@tagName(t)), } @@ -3887,7 +3863,7 @@ pub const MIR = struct { .r = false, .w = true, }; - image.appendCodeByte(@bitCast(rex)); + try image.section_manager.appendCodeByte(@bitCast(rex)); const source_operand = mir.operands.get(instruction.operands.items[1]); const source_gp64 = getGP64Encoding(source_operand.*); @@ -3897,14 +3873,14 @@ pub const MIR = struct { const memory = destination_operand.u.memory; const instruction_descriptor = instruction_descriptors.get(instruction.id); const opcode: u8 = @intCast(instruction_descriptor.opcode); - image.appendCodeByte(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), }; - image.appendCodeByte(@bitCast(modrm)); + try image.section_manager.appendCodeByte(@bitCast(modrm)); switch (memory.addressing_mode.base) { .frame_index => |frame_index| { @@ -3912,7 +3888,7 @@ pub const MIR = struct { const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable; const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes]; - image.appendCode(stack_bytes); + try image.section_manager.appendCode(stack_bytes); }, else => |t| @panic(@tagName(t)), } @@ -3922,7 +3898,7 @@ pub const MIR = struct { const instruction_descriptor = instruction_descriptors.get(instruction.id); 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_gp32 = getGP32Encoding(destination_operand.*); @@ -3936,7 +3912,7 @@ pub const MIR = struct { .reg = @intCast(@intFromEnum(destination_gp32)), .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) { .frame_index => |frame_index| { @@ -3944,7 +3920,7 @@ pub const MIR = struct { const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable; const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes]; - image.appendCode(stack_bytes); + try image.section_manager.appendCode(stack_bytes); }, else => |t| @panic(@tagName(t)), } @@ -3958,11 +3934,11 @@ pub const MIR = struct { .r = false, .w = true, }; - image.appendCodeByte(@bitCast(rex)); + try image.section_manager.appendCodeByte(@bitCast(rex)); const instruction_descriptor = instruction_descriptors.get(instruction.id); 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_gp64 = getGP64Encoding(destination_operand.*); @@ -3976,7 +3952,7 @@ pub const MIR = struct { .reg = @intCast(@intFromEnum(destination_gp64)), .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) { .frame_index => |frame_index| { @@ -3984,7 +3960,7 @@ pub const MIR = struct { const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable; const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes]; - image.appendCode(stack_bytes); + try image.section_manager.appendCode(stack_bytes); }, else => |t| @panic(@tagName(t)), } @@ -3999,9 +3975,9 @@ pub const MIR = struct { const destination_gp32 = getGP32Encoding(destination_operand.*); 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)); }, .mov32ri64 => { assert(instruction.operands.items.len == 2); @@ -4015,9 +3991,9 @@ pub const MIR = struct { }; 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 => { assert(instruction.operands.items.len == 2); @@ -4034,18 +4010,18 @@ pub const MIR = struct { .r = false, .w = true, }; - image.appendCodeByte(@bitCast(rex)); + try image.section_manager.appendCodeByte(@bitCast(rex)); const instruction_descriptor = instruction_descriptors.get(instruction.id); const opcode: u8 = @intCast(instruction_descriptor.opcode); - image.appendCodeByte(opcode); + try image.section_manager.appendCodeByte(opcode); const modrm = ModRm{ .rm = @intFromEnum(Encoding.GP32.bp), .reg = @intCast(@intFromEnum(destination_register)), .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true), }; - image.appendCodeByte(@bitCast(modrm)); + try image.section_manager.appendCodeByte(@bitCast(modrm)); switch (source_memory.addressing_mode.base) { .frame_index => |frame_index| { @@ -4053,25 +4029,25 @@ pub const MIR = struct { const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable; const stack_bytes = std.mem.asBytes(&stack_offset)[0..displacement_bytes]; - image.appendCode(stack_bytes); + try image.section_manager.appendCode(stack_bytes); }, else => |t| @panic(@tagName(t)), } }, - .syscall => image.appendCode(&.{ 0x0f, 0x05 }), - .ud2 => image.appendCode(&.{ 0x0f, 0x0b }), + .syscall => try image.section_manager.appendCode(&.{ 0x0f, 0x05 }), + .ud2 => try image.section_manager.appendCode(&.{ 0x0f, 0x0b }), .call64pcrel32 => { // TODO: emit relocation assert(instruction.operands.items.len == 1); const operand = mir.operands.get(instruction.operands.items[0]); const instruction_descriptor = instruction_descriptors.get(instruction.id); const opcode: u8 = @intCast(instruction_descriptor.opcode); - image.appendCodeByte(opcode); + try image.section_manager.appendCodeByte(opcode); switch (operand.u) { .pc_relative => |pc_relative| { // TODO: fix - const callee = pc_relative.function_declaration; + const callee: Function.Index = @bitCast(pc_relative.index); const caller = function_index; const instruction_len = 5; @@ -4080,9 +4056,10 @@ pub const MIR = struct { const callee_offset: i64 = @intCast(function_offsets.get(callee).?); const caller_offset: i64 = @intCast(instruction_offset + instruction_len); const offset: i32 = @intCast(callee_offset - caller_offset); - image.appendCode(std.mem.asBytes(&offset)); + try image.section_manager.appendCode(std.mem.asBytes(&offset)); } else { - image.appendCode(&.{ 0, 0, 0, 0 }); + // TODO: handle relocation + try image.section_manager.appendCode(&.{ 0, 0, 0, 0 }); unreachable; } }, @@ -4097,7 +4074,7 @@ pub const MIR = struct { switch (destination_operand.id) { .gp32 => { - image.appendCodeByte(0x89); + try image.section_manager.appendCodeByte(0x89); const destination_register = getGP32Encoding(destination_operand.*); const source_register = getGP32Encoding(source_operand.*); @@ -4106,7 +4083,8 @@ pub const MIR = struct { .reg = @intCast(@intFromEnum(source_register)), .mod = @as(u2, @intFromBool(true)) << 1 | @intFromBool(true), }; - image.appendCodeByte(@bitCast(modrm)); + + try image.section_manager.appendCodeByte(@bitCast(modrm)); }, .gp64 => { const rex = Rex{ @@ -4115,9 +4093,9 @@ pub const MIR = struct { .r = false, .w = true, }; - image.appendCodeByte(@bitCast(rex)); + try image.section_manager.appendCodeByte(@bitCast(rex)); - image.appendCodeByte(0x89); + try image.section_manager.appendCodeByte(0x89); const destination_register = getGP64Encoding(destination_operand.*); const source_register = getGP64Encoding(source_operand.*); @@ -4126,7 +4104,7 @@ pub const MIR = struct { .reg = @intCast(@intFromEnum(source_register)), .mod = @as(u2, @intFromBool(true)) << 1 | @intFromBool(true), }; - image.appendCodeByte(@bitCast(modrm)); + try image.section_manager.appendCodeByte(@bitCast(modrm)); }, else => |t| @panic(@tagName(t)), } @@ -4139,11 +4117,11 @@ pub const MIR = struct { .r = false, .w = true, }; - image.appendCodeByte(@bitCast(rex)); + try image.section_manager.appendCodeByte(@bitCast(rex)); const instruction_descriptor = instruction_descriptors.get(instruction.id); 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_register = getGP64Encoding(destination_operand.*); @@ -4153,7 +4131,7 @@ pub const MIR = struct { .reg = @intCast(@intFromEnum(destination_register)), .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(false), }; - image.appendCodeByte(@bitCast(modrm)); + try image.section_manager.appendCodeByte(@bitCast(modrm)); const source_operand = mir.operands.get(instruction.operands.items[1]); switch (source_operand.u) { @@ -4162,19 +4140,30 @@ pub const MIR = struct { assert(lea64mem.scale == 1); assert(lea64mem.scale_reg == null); - switch (lea64mem.displacement) { - .string_literal => unreachable, - else => unreachable, + 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)), } - unreachable; }, else => |t| @panic(@tagName(t)), } - if (instruction_offset != image.getTextSection().index) { + if (instruction_offset != image.section_manager.getCodeOffset()) { const print_tags = true; if (print_tags) { var offset = @tagName(instruction.id).len + 2; @@ -4184,7 +4173,8 @@ pub const MIR = struct { 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, "\n", .{}); @@ -4201,15 +4191,15 @@ pub const MIR = struct { if (stack_size != 0) { // add rsp, stack_offset if (std.math.cast(u8, stack_size)) |stack_size_u8| { - image.appendCode(&.{ 0x48, 0x83, 0xc4, stack_size_u8 }); + try image.section_manager.appendCode(&.{ 0x48, 0x83, 0xc4, stack_size_u8 }); } else { unreachable; } - image.appendCodeByte(0x5d); // pop rbp + try image.section_manager.appendCodeByte(0x5d); // pop rbp } - image.appendCodeByte(0xc3); + try image.section_manager.appendCodeByte(0xc3); } } diff --git a/src/data_structures.zig b/src/data_structures.zig index 85ea8d0..df8043d 100644 --- a/src/data_structures.zig +++ b/src/data_structures.zig @@ -4,6 +4,7 @@ const assert = std.debug.assert; pub const Allocator = std.mem.Allocator; pub const AutoArrayHashMap = std.AutoArrayHashMapUnmanaged; pub const ArrayList = std.ArrayListUnmanaged; +pub const ArrayListAligned = std.ArrayListAlignedUnmanaged; pub const AutoHashMap = std.AutoHashMapUnmanaged; pub const HashMap = std.HashMapUnmanaged; pub const SegmentedList = std.SegmentedList; diff --git a/src/frontend/semantic_analyzer.zig b/src/frontend/semantic_analyzer.zig index 82ed488..35edc47 100644 --- a/src/frontend/semantic_analyzer.zig +++ b/src/frontend/semantic_analyzer.zig @@ -982,11 +982,37 @@ const Analyzer = struct { fn processStringLiteral(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) !u32 { const string_literal_node = analyzer.getScopeNode(scope_index, node_index); 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); 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 { const function_prototype = analyzer.module.function_prototypes.get(function_prototype_index); return function_prototype.return_type;