From c6ef35aeed3f51cab804e90877064705ff845680 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Thu, 30 Nov 2023 17:33:45 -0600 Subject: [PATCH] implement current executable path --- bootstrap/Compilation.zig | 331 +- bootstrap/backend/aarch64.zig | 0 bootstrap/backend/c_transpiler.zig | 302 +- bootstrap/backend/elf.zig | 394 -- bootstrap/backend/emit.zig | 270 - .../backend/intermediate_representation.zig | 1269 ---- bootstrap/backend/macho.zig | 696 -- bootstrap/backend/pe.zig | 267 - bootstrap/backend/x86_64.zig | 6174 ----------------- bootstrap/data_structures.zig | 32 +- bootstrap/frontend/lexical_analyzer.zig | 72 +- bootstrap/frontend/semantic_analyzer.zig | 1265 ++-- bootstrap/frontend/syntactic_analyzer.zig | 125 +- build.zig | 3 +- ci.sh | 34 +- lib/std/os.nat | 67 +- lib/std/os/linux.nat | 24 +- lib/std/std.nat | 16 + test/self_exe_path/main.nat | 13 + 19 files changed, 1374 insertions(+), 9980 deletions(-) delete mode 100644 bootstrap/backend/aarch64.zig delete mode 100644 bootstrap/backend/elf.zig delete mode 100644 bootstrap/backend/emit.zig delete mode 100644 bootstrap/backend/intermediate_representation.zig delete mode 100644 bootstrap/backend/macho.zig delete mode 100644 bootstrap/backend/pe.zig delete mode 100644 bootstrap/backend/x86_64.zig create mode 100644 test/self_exe_path/main.nat diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index c431dac..b2c1758 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -21,9 +21,7 @@ const Token = lexical_analyzer.Token; const syntactic_analyzer = @import("frontend/syntactic_analyzer.zig"); const Node = syntactic_analyzer.Node; const semantic_analyzer = @import("frontend/semantic_analyzer.zig"); -const intermediate_representation = @import("backend/intermediate_representation.zig"); const c_transpiler = @import("backend/c_transpiler.zig"); -const emit = @import("backend/emit.zig"); test { _ = lexical_analyzer; @@ -55,7 +53,7 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor { var maybe_executable_path: ?[]const u8 = null; var maybe_main_package_path: ?[]const u8 = null; var target_triplet: []const u8 = "x86_64-linux-gnu"; - var transpile_to_c: ?bool = null; + var should_transpile_to_c: ?bool = null; var i: usize = 0; while (i < arguments.len) : (i += 1) { @@ -121,9 +119,9 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor { const arg = arguments[i]; if (std.mem.eql(u8, arg, "true")) { - transpile_to_c = true; + should_transpile_to_c = true; } else if (std.mem.eql(u8, arg, "false")) { - transpile_to_c = false; + should_transpile_to_c = false; } else { unreachable; } @@ -135,23 +133,33 @@ fn parseArguments(allocator: Allocator) !Compilation.Module.Descriptor { } } - const main_package_path = maybe_main_package_path orelse return error.main_package_path_not_specified; + const cross_target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = target_triplet }); + const target = cross_target.toTarget(); + const transpile_to_c = should_transpile_to_c orelse true; + + var is_build = false; + const main_package_path = if (maybe_main_package_path) |path| path else blk: { + const build_file = "build.nat"; + const file = std.fs.cwd().openFile(build_file, .{}) catch return error.main_package_path_not_specified; + file.close(); + is_build = true; + + break :blk build_file; + }; const executable_path = maybe_executable_path orelse blk: { - const executable_name = std.fs.path.basename(main_package_path[0 .. main_package_path.len - "/main.nat".len]); + const executable_name = if (is_build) "build" else std.fs.path.basename(main_package_path[0 .. main_package_path.len - "/main.nat".len]); assert(executable_name.len > 0); const result = try std.mem.concat(allocator, u8, &.{ "nat/", executable_name }); break :blk result; }; - const cross_target = try std.zig.CrossTarget.parse(.{ .arch_os_abi = target_triplet }); - const target = cross_target.toTarget(); - return .{ .main_package_path = main_package_path, .executable_path = executable_path, .target = target, - .transpile_to_c = transpile_to_c orelse true, + .transpile_to_c = transpile_to_c, + .is_build = is_build, }; } @@ -183,7 +191,6 @@ pub const Struct = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const ContainerField = struct { @@ -194,7 +201,6 @@ pub const ContainerField = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const ContainerInitialization = struct { @@ -203,7 +209,6 @@ pub const ContainerInitialization = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Enum = struct { @@ -218,21 +223,10 @@ pub const Enum = struct { pub const List = BlockList(@This()); pub const Index = Enum.Field.List.Index; - pub const Allocation = Enum.Field.List.Allocation; }; pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const Array = struct { - element_type: Type.Index, - element_count: u32, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Type = union(enum) { @@ -255,6 +249,11 @@ pub const Type = union(enum) { element_type: Type.Index, }; + pub const Array = struct { + element_type: Type.Index, + element_count: u32, + }; + pub const Slice = struct { element_type: Type.Index, @"const": bool, @@ -268,7 +267,6 @@ pub const Type = union(enum) { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; pub const Integer = struct { bit_count: u16, @@ -434,7 +432,6 @@ pub const Scope = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const ScopeType = enum(u1) { @@ -463,7 +460,6 @@ pub const Declaration = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Function = struct { @@ -500,7 +496,6 @@ pub const Function = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Block = struct { @@ -508,7 +503,6 @@ pub const Block = struct { reaches_end: bool, pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Loop = struct { @@ -520,7 +514,6 @@ pub const Loop = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; const Unresolved = struct { @@ -539,7 +532,6 @@ pub const Assignment = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Syscall = struct { @@ -553,7 +545,6 @@ pub const Syscall = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Call = struct { @@ -563,21 +554,18 @@ pub const Call = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const ArgumentList = struct { array: ArrayList(Value.Index), pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Return = struct { value: Value.Index, pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Cast = struct { @@ -586,7 +574,6 @@ pub const Cast = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const BinaryOperation = struct { @@ -597,7 +584,6 @@ pub const BinaryOperation = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; pub const Id = enum { add, @@ -624,7 +610,6 @@ pub const UnaryOperation = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; pub const Id = enum { boolean_not, @@ -647,7 +632,6 @@ pub const Branch = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const FieldAccess = struct { @@ -656,7 +640,6 @@ pub const FieldAccess = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Range = struct { @@ -676,7 +659,6 @@ pub const Slice = struct { pub const List = BlockList(@This()); pub const Index = Slice.Access.List.Index; - pub const Allocation = Slice.Access.List.Allocation; }; pub const Field = enum { @@ -686,7 +668,6 @@ pub const Slice = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const IndexedAccess = struct { @@ -695,7 +676,6 @@ pub const IndexedAccess = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const OptionalCheck = struct { @@ -703,7 +683,6 @@ pub const OptionalCheck = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const OptionalUnwrap = struct { @@ -711,7 +690,6 @@ pub const OptionalUnwrap = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Assembly = struct { @@ -721,7 +699,6 @@ pub const Assembly = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const Operand = union(enum) { @@ -735,7 +712,6 @@ pub const Assembly = struct { pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; }; pub const x86_64 = struct { @@ -752,6 +728,11 @@ pub const Assembly = struct { }; }; +pub const StringLiteral = struct { + hash: u32, + type: Type.Index, +}; + pub const Value = union(enum) { void, bool: bool, @@ -774,7 +755,7 @@ pub const Value = union(enum) { argument_list: ArgumentList, @"return": Return.Index, argument: Declaration.Index, - string_literal: u32, + string_literal: StringLiteral, enum_field: Enum.Field.Index, extern_function: Function.Prototype.Index, sign_extend: Cast.Index, @@ -789,12 +770,13 @@ pub const Value = union(enum) { indexed_access: IndexedAccess.Index, optional_check: OptionalCheck.Index, optional_unwrap: OptionalUnwrap.Index, + optional_cast: Cast.Index, + array_coerce_to_slice: Cast.Index, slice: Slice.Index, assembly_block: Assembly.Block.Index, pub const List = BlockList(@This()); pub const Index = List.Index; - pub const Allocation = List.Allocation; pub const Integer = struct { value: u64, @@ -809,7 +791,7 @@ pub const Value = union(enum) { pub fn isComptime(value: *Value, module: *Module) bool { return switch (value.*) { .integer => |integer| integer.type.eq(Type.comptime_int), - .declaration_reference => |declaration_reference| module.declarations.get(declaration_reference.value).mutability == .@"const" and isComptime(module.values.get(module.declarations.get(declaration_reference.value).init_value), module), + .declaration_reference => |declaration_reference| module.values.declarations.get(declaration_reference.value).mutability == .@"const" and isComptime(module.values.array.get(module.values.declarations.get(declaration_reference.value).init_value), module), .bool, .void, .undefined, .function_definition, .type, .enum_field => true, // TODO: .call, @@ -820,6 +802,7 @@ pub const Value = union(enum) { .optional_unwrap, .pointer_null_literal, .indexed_access, + .slice, => false, // TODO: else => |t| @panic(@tagName(t)), @@ -828,31 +811,34 @@ pub const Value = union(enum) { pub fn getType(value: Value, module: *Module) Type.Index { const result = switch (value) { - .call => |call_index| module.calls.get(call_index).type, + .call => |call_index| module.values.calls.get(call_index).type, .integer => |integer| integer.type, .declaration_reference => |declaration_reference| declaration_reference.type, - .string_literal => |string_literal_hash| module.string_literal_types.get(@intCast(module.getStringLiteral(string_literal_hash).?.len)).?, + .string_literal => |string_literal| string_literal.type, .type => Type.type, - .enum_field => |enum_field_index| module.enums.get(module.enum_fields.get(enum_field_index).parent).type, - .function_definition => |function_index| module.function_definitions.get(function_index).prototype, - .function_declaration => |function_index| module.function_declarations.get(function_index).prototype, - .binary_operation => |binary_operation| module.binary_operations.get(binary_operation).type, + .enum_field => |enum_field_index| module.types.enums.get(module.types.enum_fields.get(enum_field_index).parent).type, + .function_definition => |function_index| module.types.function_definitions.get(function_index).prototype, + .function_declaration => |function_index| module.types.function_declarations.get(function_index).prototype, + .binary_operation => |binary_operation| module.values.binary_operations.get(binary_operation).type, .bool => Type.boolean, .declaration => Type.void, - .container_initialization => |container_initialization| module.container_initializations.get(container_initialization).type, + .container_initialization => |container_initialization| module.values.container_initializations.get(container_initialization).type, .syscall => Type.usize, - .unary_operation => |unary_operation_index| module.unary_operations.get(unary_operation_index).type, + .unary_operation => |unary_operation_index| module.values.unary_operations.get(unary_operation_index).type, .pointer_null_literal => semantic_analyzer.optional_pointer_to_any_type, .optional_null_literal => semantic_analyzer.optional_any, - .field_access => |field_access_index| module.container_fields.get(module.field_accesses.get(field_access_index).field).type, - .cast => |cast_index| module.casts.get(cast_index).type, - .slice => |slice_index| module.slices.get(slice_index).type, - .slice_access => |slice_access_index| module.slice_accesses.get(slice_access_index).type, + .field_access => |field_access_index| module.types.container_fields.get(module.values.field_accesses.get(field_access_index).field).type, + .cast, + .optional_cast, + .array_coerce_to_slice, + => |cast_index| module.values.casts.get(cast_index).type, + .slice => |slice_index| module.values.slices.get(slice_index).type, + .slice_access => |slice_access_index| module.values.slice_accesses.get(slice_access_index).type, .optional_check => Type.boolean, .indexed_access => |indexed_access_index| blk: { - const indexed_expression = module.values.get(module.indexed_accesses.get(indexed_access_index).indexed_expression); + const indexed_expression = module.values.array.get(module.values.indexed_accesses.get(indexed_access_index).indexed_expression); const indexed_expression_type_index = indexed_expression.getType(module); - const indexed_expression_type = module.types.get(indexed_expression_type_index); + const indexed_expression_type = module.types.array.get(indexed_expression_type_index); break :blk switch (indexed_expression_type.*) { .slice => |slice| slice.element_type, else => |t| @panic(@tagName(t)), @@ -872,51 +858,55 @@ pub const Value = union(enum) { }; pub const Module = struct { + values: struct { + field_accesses: BlockList(FieldAccess) = .{}, + array: BlockList(Value) = .{}, + declarations: BlockList(Declaration) = .{}, + scopes: BlockList(Scope) = .{}, + files: BlockList(File) = .{}, + blocks: BlockList(Block) = .{}, + loops: BlockList(Loop) = .{}, + assignments: BlockList(Assignment) = .{}, + syscalls: BlockList(Syscall) = .{}, + calls: BlockList(Call) = .{}, + argument_lists: BlockList(ArgumentList) = .{}, + returns: BlockList(Return) = .{}, + container_initializations: BlockList(ContainerInitialization) = .{}, + casts: BlockList(Cast) = .{}, + branches: BlockList(Branch) = .{}, + binary_operations: BlockList(BinaryOperation) = .{}, + unary_operations: BlockList(UnaryOperation) = .{}, + slices: BlockList(Slice) = .{}, + slice_accesses: BlockList(Slice.Access) = .{}, + indexed_accesses: BlockList(IndexedAccess) = .{}, + optional_checks: BlockList(OptionalCheck) = .{}, + optional_unwraps: BlockList(OptionalUnwrap) = .{}, + assembly_blocks: BlockList(Assembly.Block) = .{}, + assembly_instructions: BlockList(Assembly.Instruction) = .{}, + } = .{}, + types: struct { + array: BlockList(Type) = .{}, + enums: BlockList(Enum) = .{}, + arrays: BlockList(Type.Array) = .{}, + structs: BlockList(Struct) = .{}, + container_fields: BlockList(ContainerField) = .{}, + enum_fields: BlockList(Enum.Field) = .{}, + function_definitions: BlockList(Function) = .{}, + function_declarations: BlockList(Function) = .{}, + function_prototypes: BlockList(Function.Prototype) = .{}, + } = .{}, + map: struct { + functions: data_structures.AutoArrayHashMap(Function.Index, Declaration.Index) = .{}, + strings: StringKeyMap([]const u8) = .{}, + imports: StringArrayHashMap(File.Index) = .{}, + types: data_structures.AutoArrayHashMap(Type.Index, Declaration.Index) = .{}, + non_primitive_integer: data_structures.AutoArrayHashMap(Type.Integer, Type.Index) = .{}, + slices: data_structures.AutoArrayHashMap(Type.Slice, Type.Index) = .{}, + pointers: data_structures.AutoArrayHashMap(Type.Pointer, Type.Index) = .{}, + optionals: data_structures.AutoArrayHashMap(Type.Index, Type.Index) = .{}, + arrays: data_structures.AutoArrayHashMap(Type.Array, Type.Index) = .{}, + } = .{}, main_package: *Package, - import_table: StringArrayHashMap(*File) = .{}, - string_table: StringKeyMap([]const u8) = .{}, - declarations: BlockList(Declaration) = .{}, - structs: BlockList(Struct) = .{}, - scopes: BlockList(Scope) = .{}, - files: BlockList(File) = .{}, - values: BlockList(Value) = .{}, - function_definitions: BlockList(Function) = .{}, - function_declarations: BlockList(Function) = .{}, - function_prototypes: BlockList(Function.Prototype) = .{}, - types: BlockList(Type) = .{}, - blocks: BlockList(Block) = .{}, - loops: BlockList(Loop) = .{}, - assignments: BlockList(Assignment) = .{}, - syscalls: BlockList(Syscall) = .{}, - calls: BlockList(Call) = .{}, - argument_lists: BlockList(ArgumentList) = .{}, - returns: BlockList(Return) = .{}, - string_literals: StringKeyMap([]const u8) = .{}, - enums: BlockList(Enum) = .{}, - enum_fields: BlockList(Enum.Field) = .{}, - container_fields: BlockList(ContainerField) = .{}, - container_initializations: BlockList(ContainerInitialization) = .{}, - function_map: data_structures.AutoArrayHashMap(Function.Index, Declaration.Index) = .{}, - type_map: data_structures.AutoArrayHashMap(Type.Index, Declaration.Index) = .{}, - arrays: BlockList(Array) = .{}, - casts: BlockList(Cast) = .{}, - binary_operations: BlockList(BinaryOperation) = .{}, - unary_operations: BlockList(UnaryOperation) = .{}, - branches: BlockList(Branch) = .{}, - field_accesses: BlockList(FieldAccess) = .{}, - slices: BlockList(Slice) = .{}, - slice_accesses: BlockList(Slice.Access) = .{}, - indexed_accesses: BlockList(IndexedAccess) = .{}, - optional_checks: BlockList(OptionalCheck) = .{}, - optional_unwraps: BlockList(OptionalUnwrap) = .{}, - assembly_blocks: BlockList(Assembly.Block) = .{}, - assembly_instructions: BlockList(Assembly.Instruction) = .{}, - non_primitive_integer_types: data_structures.AutoArrayHashMap(Type.Integer, Type.Index) = .{}, - string_literal_types: data_structures.AutoArrayHashMap(u32, Type.Index) = .{}, - slice_types: data_structures.AutoArrayHashMap(Type.Slice, Type.Index) = .{}, - pointer_types: data_structures.AutoArrayHashMap(Type.Pointer, Type.Index) = .{}, - optional_types: data_structures.AutoArrayHashMap(Type.Index, Type.Index) = .{}, - array_types: data_structures.AutoArrayHashMap(Array, Type.Index) = .{}, entry_point: Function.Index = Function.Index.invalid, descriptor: Descriptor, @@ -925,10 +915,10 @@ pub const Module = struct { executable_path: []const u8, target: std.Target, transpile_to_c: bool, + is_build: bool, }; const ImportFileResult = struct { - ptr: *File, index: File.Index, is_new: bool, }; @@ -952,7 +942,7 @@ pub const Module = struct { return module.importPackage(allocator, module.main_package); } - const current_file = module.files.get(current_file_index); + const current_file = module.values.files.get(current_file_index); if (current_file.package.dependencies.get(import_name)) |package| { return module.importPackage(allocator, package); } @@ -968,7 +958,7 @@ pub const Module = struct { const package = current_file.package; const import_file = try module.getFile(allocator, full_path, file_relative_path, package); - try import_file.ptr.addFileReference(allocator, current_file); + try module.values.files.get(import_file.index).file_references.append(allocator, current_file); const result = ImportPackageResult{ .file = import_file, @@ -979,33 +969,22 @@ pub const Module = struct { } fn getFile(module: *Module, allocator: Allocator, full_path: []const u8, relative_path: []const u8, package: *Package) !ImportFileResult { - const path_lookup = try module.import_table.getOrPut(allocator, full_path); - const file, const index = switch (path_lookup.found_existing) { - true => blk: { - const result = path_lookup.value_ptr.*; - const index = module.files.indexOf(result); - break :blk .{ - result, - index, - }; - }, + const path_lookup = try module.map.imports.getOrPut(allocator, full_path); + const index = switch (path_lookup.found_existing) { + true => path_lookup.value_ptr.*, false => blk: { - const file_allocation = try module.files.append(allocator, File{ + const file_index = try module.values.files.append(allocator, File{ .relative_path = relative_path, .package = package, }); - logln(.compilation, .new_file, "Adding file #{}: {s}\n", .{ file_allocation.index.uniqueInteger(), full_path }); - path_lookup.value_ptr.* = file_allocation.ptr; + logln(.compilation, .new_file, "Adding file #{}: {s}\n", .{ file_index.uniqueInteger(), full_path }); + path_lookup.value_ptr.* = file_index; // break :blk file; - break :blk .{ - file_allocation.ptr, - file_allocation.index, - }; + break :blk file_index; }, }; return .{ - .ptr = file, .index = index, .is_new = !path_lookup.found_existing, }; @@ -1015,7 +994,8 @@ pub const Module = struct { const full_path = try std.fs.path.resolve(allocator, &.{ package.directory.path, package.source_path }); logln(.compilation, .import, "Import full path: {s}\n", .{full_path}); const import_file = try module.getFile(allocator, full_path, package.source_path, package); - try import_file.ptr.addPackageReference(allocator, package); + const file = module.values.files.get(import_file.index); + try file.addPackageReference(allocator, package); return .{ .file = import_file, @@ -1024,7 +1004,7 @@ pub const Module = struct { } pub fn generateAbstractSyntaxTreeForFile(module: *Module, allocator: Allocator, file_index: File.Index) !void { - const file = module.files.get(file_index); + const file = module.values.files.get(file_index); const source_file = file.package.directory.handle.openFile(file.relative_path, .{}) catch |err| { std.debug.panic("Can't find file {s} in directory {s} for error {s}", .{ file.relative_path, file.package.directory.path, @errorName(err) }); }; @@ -1048,54 +1028,17 @@ pub const Module = struct { return map.getValue(key); } - fn addString(map: *StringKeyMap([]const u8), allocator: Allocator, string: []const u8) !u32 { + pub fn addString(map: *StringKeyMap([]const u8), allocator: Allocator, string: []const u8) !u32 { const lookup_result = try map.getOrPut(allocator, string, string); return lookup_result.key; } pub fn getName(module: *Module, key: u32) ?[]const u8 { - return getString(&module.string_table, key); + return getString(&module.map.strings, key); } pub fn addName(module: *Module, allocator: Allocator, name: []const u8) !u32 { - return addString(&module.string_table, allocator, name); - } - - pub fn getStringLiteral(module: *Module, key: u32) ?[]const u8 { - return getString(&module.string_literals, key); - } - - pub fn addStringLiteral(module: *Module, allocator: Allocator, string_literal: []const u8) !u32 { - const result = addString(&module.string_literals, allocator, string_literal); - - const len: u32 = @intCast(string_literal.len); - // try analyzer.module. - const string_literal_type_gop = try module.string_literal_types.getOrPut(allocator, len); - if (!string_literal_type_gop.found_existing) { - const array = Array{ - .element_type = Type.u8, - .element_count = len, - }; - const array_type_gop = try module.array_types.getOrPut(allocator, array); - if (!array_type_gop.found_existing) { - const array_type_allocation = try module.types.append(allocator, .{ - .array = array, - }); - array_type_gop.value_ptr.* = array_type_allocation.index; - } - - const array_type_index = array_type_gop.value_ptr.*; - const pointer_type_allocation = try module.types.append(allocator, .{ - .pointer = .{ - .@"const" = true, - .many = true, - .element_type = array_type_index, - }, - }); - string_literal_type_gop.value_ptr.* = pointer_type_allocation.index; - } - - return result; + return addString(&module.map.strings, allocator, name); } }; @@ -1135,7 +1078,10 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! module.* = Module{ .main_package = blk: { const result = try compilation.base_allocator.create(Package); - const main_package_absolute_directory_path = try compilation.pathFromCwd(std.fs.path.dirname(descriptor.main_package_path).?); + const main_package_absolute_directory_path = b: { + const relative_path = if (std.fs.path.dirname(descriptor.main_package_path)) |dirname| dirname else "."; + break :b try compilation.pathFromCwd(relative_path); + }; result.* = .{ .directory = .{ .handle = try std.fs.openDirAbsolute(main_package_absolute_directory_path, .{}), @@ -1188,12 +1134,12 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! _ = try module.importPackage(compilation.base_allocator, module.main_package.dependencies.get("std").?); - for (module.import_table.values()) |import| { - try module.generateAbstractSyntaxTreeForFile(compilation.base_allocator, module.files.indexOf(import)); + for (module.map.imports.values()) |import| { + try module.generateAbstractSyntaxTreeForFile(compilation.base_allocator, import); } inline for (@typeInfo(FixedTypeKeyword).Enum.fields) |enum_field| { - _ = try module.types.append(compilation.base_allocator, switch (@field(FixedTypeKeyword, enum_field.name)) { + _ = try module.types.array.append(compilation.base_allocator, switch (@field(FixedTypeKeyword, enum_field.name)) { .usize => @unionInit(Type, "integer", .{ .bit_count = 64, .signedness = .unsigned, @@ -1207,7 +1153,7 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! } inline for (@typeInfo(HardwareUnsignedIntegerType).Enum.fields) |enum_field| { - _ = try module.types.append(compilation.base_allocator, .{ + _ = try module.types.array.append(compilation.base_allocator, .{ .integer = .{ .signedness = .unsigned, .bit_count = switch (@field(HardwareUnsignedIntegerType, enum_field.name)) { @@ -1221,7 +1167,7 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! } inline for (@typeInfo(HardwareSignedIntegerType).Enum.fields) |enum_field| { - _ = try module.types.append(compilation.base_allocator, .{ + _ = try module.types.array.append(compilation.base_allocator, .{ .integer = .{ .signedness = .signed, .bit_count = switch (@field(HardwareSignedIntegerType, enum_field.name)) { @@ -1235,37 +1181,38 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! } for (extra_common_type_data) |type_data| { - _ = try module.types.append(compilation.base_allocator, type_data); + _ = try module.types.array.append(compilation.base_allocator, type_data); } - semantic_analyzer.pointer_to_any_type = (try module.types.append(compilation.base_allocator, .{ + semantic_analyzer.pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{ .pointer = .{ .element_type = Type.any, .many = false, .@"const" = true, }, - })).index; - semantic_analyzer.optional_pointer_to_any_type = (try module.types.append(compilation.base_allocator, .{ + }); + semantic_analyzer.optional_pointer_to_any_type = try module.types.array.append(compilation.base_allocator, .{ .optional = .{ .element_type = semantic_analyzer.pointer_to_any_type, }, - })).index; - semantic_analyzer.optional_any = (try module.types.append(compilation.base_allocator, .{ + }); + semantic_analyzer.optional_any = try module.types.array.append(compilation.base_allocator, .{ .optional = .{ .element_type = Type.any, }, - })).index; + }); - semantic_analyzer.unreachable_index = (try module.values.append(compilation.base_allocator, .@"unreachable")).index; - semantic_analyzer.pointer_null_index = (try module.values.append(compilation.base_allocator, .pointer_null_literal)).index; - semantic_analyzer.optional_null_index = (try module.values.append(compilation.base_allocator, .optional_null_literal)).index; + semantic_analyzer.unreachable_index = try module.values.array.append(compilation.base_allocator, .@"unreachable"); + semantic_analyzer.pointer_null_index = try module.values.array.append(compilation.base_allocator, .pointer_null_literal); + semantic_analyzer.optional_null_index = try module.values.array.append(compilation.base_allocator, .optional_null_literal); + semantic_analyzer.undefined_index = try module.values.array.append(compilation.base_allocator, .undefined); - const value_allocation = try module.values.append(compilation.base_allocator, .{ + const value_index = try module.values.array.append(compilation.base_allocator, .{ .unresolved = .{ .node_index = .{ .value = 0 }, }, }); - try semantic_analyzer.initialize(compilation, module, packages[0], value_allocation.ptr); + try semantic_analyzer.initialize(compilation, module, packages[0], value_index); if (descriptor.transpile_to_c) { try c_transpiler.initialize(compilation, module, descriptor); @@ -1355,8 +1302,6 @@ const LoggerScope = enum { lexer, parser, sema, - ir, - codegen, c, }; @@ -1374,8 +1319,6 @@ fn getLoggerScopeType(comptime logger_scope: LoggerScope) type { .lexer => lexical_analyzer, .parser => syntactic_analyzer, .sema => semantic_analyzer, - .ir => intermediate_representation, - .codegen => emit, .c => c_transpiler, }; } diff --git a/bootstrap/backend/aarch64.zig b/bootstrap/backend/aarch64.zig deleted file mode 100644 index e69de29..0000000 diff --git a/bootstrap/backend/c_transpiler.zig b/bootstrap/backend/c_transpiler.zig index e0ef40a..b6ad7e3 100644 --- a/bootstrap/backend/c_transpiler.zig +++ b/bootstrap/backend/c_transpiler.zig @@ -34,6 +34,7 @@ pub const TranslationUnit = struct { optional_type_set: AutoArrayHashMap(Compilation.Type.Index, []const u8) = .{}, function_set: AutoArrayHashMap(Compilation.Function.Index, []const u8) = .{}, slice_type_set: AutoArrayHashMap(Compilation.Type.Index, []const u8) = .{}, + array_type_set: AutoArrayHashMap(Compilation.Type.Index, []const u8) = .{}, declaration_set: AutoArrayHashMap(Compilation.Declaration.Index, []const u8) = .{}, const SyscallBitset = std.StaticBitSet(6); @@ -65,7 +66,7 @@ pub const TranslationUnit = struct { ); { - var function_definitions = module.function_definitions.iterator(); + var function_definitions = module.types.function_definitions.iterator(); while (function_definitions.nextIndex()) |function_definition_index| { _ = try unit.writeFunctionDefinition(module, allocator, function_definition_index); } @@ -78,9 +79,9 @@ pub const TranslationUnit = struct { const gop = try unit.function_set.getOrPut(allocator, function_definition_index); if (!gop.found_existing) { - const function_definition = module.function_definitions.get(function_definition_index); + const function_definition = module.types.function_definitions.get(function_definition_index); const function_prototype_type = function_definition.prototype; - const function_prototype = module.function_prototypes.get(module.types.get(function_prototype_type).function); + const function_prototype = module.types.function_prototypes.get(module.types.array.get(function_prototype_type).function); const function_name = try unit.writeFunctionHeader(module, &unit.function_declarations, allocator, function_definition_index); gop.value_ptr.* = function_name; @@ -97,13 +98,13 @@ pub const TranslationUnit = struct { } fn writeDeclaration(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, declaration_index: Compilation.Declaration.Index, indentation: usize) !void { - const declaration = module.declarations.get(declaration_index); + const declaration = module.values.declarations.get(declaration_index); const mangle = false; const name = try unit.renderDeclarationName(module, allocator, declaration_index, mangle); if (declaration.mutability == .@"const") { - switch (module.types.get(declaration.type).*) { - .optional => |optional| switch (module.types.get(optional.element_type).*) { + switch (module.types.array.get(declaration.type).*) { + .optional => |optional| switch (module.types.array.get(optional.element_type).*) { .pointer => {}, else => try list.appendSlice(allocator, "const "), }, @@ -133,8 +134,8 @@ pub const TranslationUnit = struct { } fn writeAssignment(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, assignment_index: Compilation.Assignment.Index, function_return_type: Compilation.Type.Index, indentation: usize) !void { - const assignment = module.assignments.get(assignment_index); - const left_type = module.values.get(assignment.source).getType(module); + const assignment = module.values.assignments.get(assignment_index); + const left_type = module.values.array.get(assignment.source).getType(module); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = assignment.destination, .type_index = left_type, @@ -153,14 +154,14 @@ pub const TranslationUnit = struct { fn writeBlock(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, block_index: Compilation.Block.Index, function_return_type: Compilation.Type.Index, old_indentation: usize) !void { try list.appendSlice(allocator, "{\n"); - const block = module.blocks.get(block_index); + const block = module.values.blocks.get(block_index); const indentation = old_indentation + 1; for (block.statements.items) |statement_index| { try list.appendNTimes(allocator, ' ', indentation * margin_width); - const statement = module.values.get(statement_index); + const statement = module.values.array.get(statement_index); switch (statement.*) { .declaration => |declaration_index| { try unit.writeDeclaration(module, list, allocator, declaration_index, indentation); @@ -171,13 +172,13 @@ pub const TranslationUnit = struct { try list.append(allocator, ';'); }, .@"return" => |return_index| { - const return_expr = module.returns.get(return_index); + const return_expr = module.values.returns.get(return_index); try list.appendSlice(allocator, "return "); - const return_value = module.values.get(return_expr.value); + const return_value = module.values.array.get(return_expr.value); const return_value_type_index = return_value.getType(module); // _ = return_value_type_index; - switch (module.types.get(function_return_type).*) { - .optional => switch (module.types.get(return_value_type_index).*) { + switch (module.types.array.get(function_return_type).*) { + .optional => switch (module.types.array.get(return_value_type_index).*) { .optional => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = return_expr.value, .type_index = function_return_type, @@ -221,7 +222,7 @@ pub const TranslationUnit = struct { try list.append(allocator, ';'); }, .branch => |branch_index| { - const branch = module.branches.get(branch_index); + const branch = module.values.branches.get(branch_index); try list.appendSlice(allocator, "if ("); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = branch.expression, @@ -234,7 +235,7 @@ pub const TranslationUnit = struct { }); if (!branch.not_taken_expression.invalid) { - if (module.values.get(branch.taken_expression).* == .block) { + if (module.values.array.get(branch.taken_expression).* == .block) { _ = list.pop(); try list.appendSlice(allocator, " else "); } else { @@ -245,7 +246,7 @@ pub const TranslationUnit = struct { .type_index = function_return_type, }); - if (module.values.get(branch.not_taken_expression).* == .block) { + if (module.values.array.get(branch.not_taken_expression).* == .block) { continue; } } @@ -255,7 +256,7 @@ pub const TranslationUnit = struct { try list.append(allocator, ';'); }, .loop => |loop_index| { - const loop = module.loops.get(loop_index); + const loop = module.values.loops.get(loop_index); try list.appendSlice(allocator, "for ("); if (!loop.pre.invalid) { try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ @@ -286,6 +287,9 @@ pub const TranslationUnit = struct { .type_index = Compilation.Type.Index.invalid, }); }, + .block => |new_block_index| { + try unit.writeBlock(module, list, allocator, new_block_index, function_return_type, indentation); + }, else => |t| @panic(@tagName(t)), } @@ -302,19 +306,19 @@ pub const TranslationUnit = struct { }; fn renderTypeName(unit: *TranslationUnit, module: *Module, allocator: Allocator, type_index: Compilation.Type.Index) ![]const u8 { - const declaration_index = module.type_map.get(type_index).?; + const declaration_index = module.map.types.get(type_index).?; const mangle = true; const result = try unit.renderDeclarationName(module, allocator, declaration_index, mangle); return result; } fn renderFunctionName(unit: *TranslationUnit, module: *Module, allocator: Allocator, function_index: Compilation.Function.Index) ![]const u8 { - const function_definition = module.function_definitions.get(function_index); - const function_prototype_type = module.types.get(function_definition.prototype); + const function_definition = module.types.function_definitions.get(function_index); + const function_prototype_type = module.types.array.get(function_definition.prototype); const function_prototype_index = function_prototype_type.function; - const function_prototype = module.function_prototypes.get(function_prototype_index); + const function_prototype = module.types.function_prototypes.get(function_prototype_index); const mangle = !(function_prototype.attributes.@"export" or function_prototype.attributes.@"extern"); - const function_declaration_index = module.function_map.get(function_index).?; + const function_declaration_index = module.map.functions.get(function_index).?; const name = try unit.renderDeclarationName(module, allocator, function_declaration_index, mangle); return name; } @@ -323,7 +327,7 @@ pub const TranslationUnit = struct { const gop = try unit.declaration_set.getOrPut(allocator, declaration_index); if (!gop.found_existing) { - const declaration = module.declarations.get(declaration_index); + const declaration = module.values.declarations.get(declaration_index); const base_declaration_name = module.getName(declaration.name).?; var list = ArrayList(u8){}; @@ -336,10 +340,10 @@ pub const TranslationUnit = struct { switch (declaration.scope_type) { .global => { while (!scope_index.invalid) { - const scope = module.scopes.get(scope_index); + const scope = module.values.scopes.get(scope_index); - if (module.type_map.get(scope.type)) |type_declaration| { - const scope_type_declaration = module.declarations.get(type_declaration); + if (module.map.types.get(scope.type)) |type_declaration| { + const scope_type_declaration = module.values.declarations.get(type_declaration); const scope_type_declaration_name = module.getName(scope_type_declaration.name).?; try list.insert(allocator, 0, '_'); try list.insertSlice(allocator, 0, scope_type_declaration_name); @@ -361,7 +365,7 @@ pub const TranslationUnit = struct { gop.value_ptr.* = list.items; switch (declaration.scope_type) { - .global => switch (module.types.get(declaration.type).*) { + .global => switch (module.types.array.get(declaration.type).*) { .function, .type, => {}, @@ -383,7 +387,7 @@ pub const TranslationUnit = struct { } fn writeFunctionPrototype(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, function_prototype_index: Compilation.Function.Prototype.Index, name: []const u8) !void { - const function_prototype = module.function_prototypes.get(function_prototype_index); + const function_prototype = module.types.function_prototypes.get(function_prototype_index); switch (function_prototype.attributes.calling_convention) { .system_v => {}, .naked => try list.appendSlice(allocator, "[[gnu::naked]] "), @@ -399,7 +403,7 @@ pub const TranslationUnit = struct { if (function_prototype.arguments) |function_arguments| { for (function_arguments) |argument_index| { - const arg_declaration = module.declarations.get(argument_index); + const arg_declaration = module.values.declarations.get(argument_index); try unit.writeType(module, list, allocator, arg_declaration.type); try list.append(allocator, ' '); const arg_name = module.getName(arg_declaration.name).?; @@ -415,8 +419,8 @@ pub const TranslationUnit = struct { fn writeFunctionHeader(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, function_index: Compilation.Function.Index) ![]const u8 { const name = try unit.renderFunctionName(module, allocator, function_index); - const function_definition = module.function_definitions.get(function_index); - const function_prototype_type = module.types.get(function_definition.prototype); + const function_definition = module.types.function_definitions.get(function_index); + const function_prototype_type = module.types.array.get(function_definition.prototype); const function_prototype_index = function_prototype_type.function; try unit.writeFunctionPrototype(module, list, allocator, function_prototype_index, name); @@ -424,7 +428,7 @@ pub const TranslationUnit = struct { } fn writeType(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, type_index: Compilation.Type.Index) anyerror!void { - const sema_type = module.types.get(type_index); + const sema_type = module.types.array.get(type_index); switch (sema_type.*) { .void => try list.appendSlice(allocator, "void"), @@ -438,7 +442,7 @@ pub const TranslationUnit = struct { try list.writer(allocator).print("{}", .{integer.bit_count}); }, .pointer => |pointer| { - switch (module.types.get(pointer.element_type).*) { + switch (module.types.array.get(pointer.element_type).*) { .function => { @panic("This should be unreachable"); }, @@ -463,16 +467,20 @@ pub const TranslationUnit = struct { const name = try unit.cacheSliceType(module, allocator, type_index); try list.appendSlice(allocator, name); }, + .array => { + const name = try unit.cacheArrayType(module, allocator, type_index); + try list.appendSlice(allocator, name); + }, .any => @panic("Internal compiler error: 'any' made it to the backend"), else => |t| @panic(@tagName(t)), } } fn writeCDeclaration(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, name: []const u8, type_index: Compilation.Type.Index) !void { - const declaration_type = module.types.get(type_index); + const declaration_type = module.types.array.get(type_index); switch (declaration_type.*) { .pointer => |pointer| { - switch (module.types.get(pointer.element_type).*) { + switch (module.types.array.get(pointer.element_type).*) { .function => |function| return try unit.writeFunctionPrototype(module, list, allocator, function, try std.mem.concat(allocator, u8, &.{ "(*", name, ")" })), else => |t| @panic(@tagName(t)), } @@ -486,10 +494,10 @@ pub const TranslationUnit = struct { } fn writeAssembly(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, assembly_block_index: Compilation.Assembly.Block.Index, indentation: usize) !void { - const assembly_block = module.assembly_blocks.get(assembly_block_index); + const assembly_block = module.values.assembly_blocks.get(assembly_block_index); try list.appendSlice(allocator, "__asm__ __volatile__(\n"); for (assembly_block.instructions) |instruction_index| { - const generic_instruction = module.assembly_instructions.get(instruction_index); + const generic_instruction = module.values.assembly_instructions.get(instruction_index); try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width); try list.append(allocator, '"'); switch (module.descriptor.target.cpu.arch) { @@ -547,12 +555,12 @@ pub const TranslationUnit = struct { } fn cacheStructType(unit: *TranslationUnit, module: *Module, allocator: Allocator, type_index: Compilation.Type.Index) ![]const u8 { - const t = module.types.get(type_index); + const t = module.types.array.get(type_index); assert(t.* == .@"struct"); const gop = try unit.struct_type_set.getOrPut(allocator, type_index); if (!gop.found_existing) { - const struct_type = module.structs.get(t.@"struct"); + const struct_type = module.types.structs.get(t.@"struct"); const type_name = try unit.renderTypeName(module, allocator, type_index); gop.value_ptr.* = type_name; // Forward declare the struct @@ -573,7 +581,7 @@ pub const TranslationUnit = struct { for (struct_type.fields.items) |struct_field_index| { try unit.type_declarations.appendNTimes(allocator, ' ', margin_width); - const struct_field = module.container_fields.get(struct_field_index); + const struct_field = module.types.container_fields.get(struct_field_index); const struct_field_name = module.getName(struct_field.name).?; switch (struct_type.backing_type.invalid) { @@ -582,7 +590,7 @@ pub const TranslationUnit = struct { try unit.type_declarations.append(allocator, ' '); try unit.type_declarations.appendSlice(allocator, struct_field_name); try unit.type_declarations.appendSlice(allocator, " : "); - try unit.type_declarations.writer(allocator).print("{}", .{module.types.get(struct_field.type).getBitSize()}); + try unit.type_declarations.writer(allocator).print("{}", .{module.types.array.get(struct_field.type).getBitSize()}); }, true => try unit.writeCDeclaration(module, &unit.type_declarations, allocator, struct_field_name, struct_field.type), } @@ -602,7 +610,7 @@ pub const TranslationUnit = struct { } fn cacheOptionalType(unit: *TranslationUnit, module: *Module, allocator: Allocator, type_index: Compilation.Type.Index) ![]const u8 { - const optional_type = module.types.get(type_index); + const optional_type = module.types.array.get(type_index); assert(optional_type.* == .optional); const optional = optional_type.optional; @@ -610,7 +618,7 @@ pub const TranslationUnit = struct { if (!gop.found_existing) { var type_name = ArrayList(u8){}; - const optional_element_type = module.types.get(optional.element_type); + const optional_element_type = module.types.array.get(optional.element_type); switch (optional_element_type.*) { .pointer => { @@ -652,7 +660,7 @@ pub const TranslationUnit = struct { } fn cacheSliceType(unit: *TranslationUnit, module: *Module, allocator: Allocator, type_index: Compilation.Type.Index) ![]const u8 { - const slice = module.types.get(type_index).slice; + const slice = module.types.array.get(type_index).slice; const gop = try unit.slice_type_set.getOrPut(allocator, slice.element_type); @@ -687,8 +695,44 @@ pub const TranslationUnit = struct { return gop.value_ptr.*; } + fn cacheArrayType(unit: *TranslationUnit, module: *Module, allocator: Allocator, type_index: Compilation.Type.Index) ![]const u8 { + const array = module.types.array.get(type_index).array; + + const gop = try unit.array_type_set.getOrPut(allocator, array.element_type); + + if (!gop.found_existing) { + var type_name = ArrayList(u8){}; + try type_name.appendSlice(allocator, "Array_"); + try unit.writeType(module, &type_name, allocator, array.element_type); + try type_name.writer(allocator).print("_{}", .{array.element_count}); + gop.value_ptr.* = type_name.items; + + try unit.type_forward_declarations.appendSlice(allocator, "typedef struct "); + try unit.type_forward_declarations.appendSlice(allocator, type_name.items); + try unit.type_forward_declarations.append(allocator, ' '); + try unit.type_forward_declarations.appendSlice(allocator, type_name.items); + try unit.type_forward_declarations.appendSlice(allocator, ";\n"); + + try unit.type_declarations.appendSlice(allocator, "typedef struct "); + try unit.type_declarations.appendSlice(allocator, type_name.items); + try unit.type_declarations.appendSlice(allocator, " {\n"); + + try unit.type_declarations.appendNTimes(allocator, ' ', margin_width); + try unit.writeType(module, &unit.type_declarations, allocator, array.element_type); + try unit.type_declarations.appendSlice(allocator, " value\n"); + + try unit.type_declarations.writer(allocator).print("[{}];\n", .{array.element_count}); + + try unit.type_declarations.appendSlice(allocator, "} "); + try unit.type_declarations.appendSlice(allocator, type_name.items); + try unit.type_declarations.appendSlice(allocator, ";\n\n"); + } + + return gop.value_ptr.*; + } + fn writeSyscall(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, syscall_index: Compilation.Syscall.Index, function_return_type: Compilation.Type.Index, indentation: usize) !void { - const syscall = module.syscalls.get(syscall_index); + const syscall = module.values.syscalls.get(syscall_index); const arguments = syscall.getArguments(); if (!unit.syscall_bitset.isSet(arguments.len - 1)) { @@ -774,8 +818,8 @@ pub const TranslationUnit = struct { } fn writeCall(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, call_index: Compilation.Call.Index, function_return_type: Compilation.Type.Index, indentation: usize) !void { - const call = module.calls.get(call_index); - const call_value = module.values.get(call.value); + const call = module.values.calls.get(call_index); + const call_value = module.values.array.get(call.value); switch (call_value.*) { .function_definition => |function_definition_index| { const name = try unit.renderFunctionName(module, allocator, function_definition_index); @@ -783,13 +827,13 @@ pub const TranslationUnit = struct { try list.append(allocator, '('); }, .field_access => |field_access_index| { - const field_access = module.field_accesses.get(field_access_index); + const field_access = module.values.field_accesses.get(field_access_index); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = field_access.declaration_reference, .type_index = function_return_type, }); - const left_type = module.types.get(module.values.get(field_access.declaration_reference).declaration_reference.type); + const left_type = module.types.array.get(module.values.array.get(field_access.declaration_reference).declaration_reference.type); const is_pointer = switch (left_type.*) { .pointer => true, else => false, @@ -801,7 +845,7 @@ pub const TranslationUnit = struct { try list.append(allocator, '.'); } - const field = module.container_fields.get(field_access.field); + const field = module.types.container_fields.get(field_access.field); const field_name = module.getName(field.name).?; try list.appendSlice(allocator, field_name); try list.append(allocator, '('); @@ -810,7 +854,7 @@ pub const TranslationUnit = struct { } if (!call.arguments.invalid) { - const argument_list = module.argument_lists.get(call.arguments); + const argument_list = module.values.argument_lists.get(call.arguments); if (argument_list.array.items.len > 0) { for (argument_list.array.items) |argument_index| { @@ -838,7 +882,7 @@ pub const TranslationUnit = struct { const value_index = arguments.value_index; const type_index = arguments.type_index; _ = type_index; - const value = module.values.get(value_index); + const value = module.values.array.get(value_index); switch (value.*) { .declaration => |declaration_index| { try unit.writeDeclaration(module, list, allocator, declaration_index, indentation); @@ -855,7 +899,7 @@ pub const TranslationUnit = struct { try list.appendSlice(allocator, name); }, .binary_operation => |binary_operation_index| { - const binary_operation = module.binary_operations.get(binary_operation_index); + const binary_operation = module.values.binary_operations.get(binary_operation_index); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = binary_operation.left, .type_index = binary_operation.type, @@ -886,23 +930,23 @@ pub const TranslationUnit = struct { }); }, .sign_extend => |cast_index| { - const sign_extend = module.casts.get(cast_index); + const sign_extend = module.values.casts.get(cast_index); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = sign_extend.value, .type_index = sign_extend.type, }); }, .cast => |cast_index| { - const cast = module.casts.get(cast_index); + const cast = module.values.casts.get(cast_index); try list.append(allocator, '('); try unit.writeType(module, list, allocator, cast.type); try list.append(allocator, ')'); - const cast_value = module.values.get(cast.value); - const cast_value_type = module.types.get(cast_value.getType(module)); + const cast_value = module.values.array.get(cast.value); + const cast_value_type = module.types.array.get(cast_value.getType(module)); switch (cast_value_type.*) { .@"struct" => |struct_index| { - const struct_type = module.structs.get(struct_index); + const struct_type = module.types.structs.get(struct_index); switch (struct_type.backing_type.invalid) { false => { try list.appendSlice(allocator, "*("); @@ -923,9 +967,9 @@ pub const TranslationUnit = struct { }), } }, - .string_literal => |string_literal_hash| { + .string_literal => |string_literal_descriptor| { try list.appendSlice(allocator, "(const u8*)"); - const string_literal = module.string_literals.getValue(string_literal_hash).?; + const string_literal = module.getName(string_literal_descriptor.hash) orelse unreachable; try list.append(allocator, '"'); try list.appendSlice(allocator, string_literal); try list.append(allocator, '"'); @@ -938,7 +982,7 @@ pub const TranslationUnit = struct { .bool => |boolean| try list.appendSlice(allocator, if (boolean) "true" else "false"), .block => |block_index| try unit.writeBlock(module, list, allocator, block_index, function_return_type, indentation), .unary_operation => |unary_operation_index| { - const unary_operation = module.unary_operations.get(unary_operation_index); + const unary_operation = module.values.unary_operations.get(unary_operation_index); const expression_character: u8 = switch (unary_operation.id) { .boolean_not => '!', .negation => '-', @@ -955,21 +999,21 @@ pub const TranslationUnit = struct { try list.append(allocator, ')'); }, .container_initialization => |container_initialization_index| { - const container_initialization = module.container_initializations.get(container_initialization_index); + const container_initialization = module.values.container_initializations.get(container_initialization_index); try list.append(allocator, '('); try unit.writeType(module, list, allocator, container_initialization.type); try list.appendSlice(allocator, ") {\n"); - const container_type = module.types.get(container_initialization.type); + const container_type = module.types.array.get(container_initialization.type); const container_fields = switch (container_type.*) { - .@"struct" => module.structs.get(container_type.@"struct").fields, + .@"struct" => module.types.structs.get(container_type.@"struct").fields, else => |t| @panic(@tagName(t)), }; for (container_initialization.field_initializations.items, container_fields.items) |field_initialization_index, container_field_index| { try list.appendNTimes(allocator, ' ', (indentation + 1) * margin_width); try list.append(allocator, '.'); - const container_field = module.container_fields.get(container_field_index); + const container_field = module.types.container_fields.get(container_field_index); const field_name = module.getName(container_field.name).?; try list.appendSlice(allocator, field_name); try list.appendSlice(allocator, " = "); @@ -984,10 +1028,10 @@ pub const TranslationUnit = struct { try list.append(allocator, '}'); }, .field_access => |field_access_index| { - const field_access = module.field_accesses.get(field_access_index); - const left = module.values.get(field_access.declaration_reference); - const left_type = module.types.get(left.getType(module)); - const right_field = module.container_fields.get(field_access.field); + const field_access = module.values.field_accesses.get(field_access_index); + const left = module.values.array.get(field_access.declaration_reference); + const left_type = module.types.array.get(left.getType(module)); + const right_field = module.types.container_fields.get(field_access.field); const right_field_name = module.getName(right_field.name).?; const is_pointer = switch (left_type.*) { .@"struct" => false, @@ -1015,16 +1059,17 @@ pub const TranslationUnit = struct { try list.appendSlice(allocator, ") { .is_null = true }"); }, .slice => |slice_index| { - const slice = module.slices.get(slice_index); - const sliceable = module.values.get(slice.sliceable); + const slice = module.values.slices.get(slice_index); + const sliceable = module.values.array.get(slice.sliceable); const sliceable_type_index = switch (sliceable.*) { .declaration_reference => |declaration_reference| declaration_reference.type, else => |t| @panic(@tagName(t)), }; - const sliceable_type = module.types.get(sliceable_type_index); + const sliceable_type = module.types.array.get(sliceable_type_index); const sliceable_element_type = switch (sliceable_type.*) { .pointer => |pointer| pointer.element_type, + .slice => |slice_type| slice_type.element_type, else => |t| @panic(@tagName(t)), }; @@ -1049,6 +1094,19 @@ pub const TranslationUnit = struct { }); try list.appendSlice(allocator, "),\n"); }, + .slice => { + try list.append(allocator, '('); + try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{ + .value_index = slice.sliceable, + .type_index = sliceable_type_index, + }); + try list.appendSlice(allocator, ").ptr + ("); + try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{ + .value_index = slice.range.start, + .type_index = Compilation.Type.Index.invalid, + }); + try list.appendSlice(allocator, "),\n"); + }, else => |t| @panic(@tagName(t)), } @@ -1076,6 +1134,26 @@ pub const TranslationUnit = struct { }, } }, + .slice => { + switch (slice.range.end.invalid) { + false => { + try list.append(allocator, '('); + try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{ + .value_index = slice.range.end, + .type_index = Compilation.Type.Index.invalid, + }); + try list.appendSlice(allocator, ") - ("); + try unit.writeValue(module, list, allocator, function_return_type, indentation + 1, .{ + .value_index = slice.range.start, + .type_index = Compilation.Type.Index.invalid, + }); + try list.appendSlice(allocator, ")\n"); + }, + true => { + unreachable; + }, + } + }, else => |t| @panic(@tagName(t)), } @@ -1087,10 +1165,10 @@ pub const TranslationUnit = struct { try list.appendSlice(allocator, function_name); }, .optional_check => |optional_check_index| { - const optional_check = module.optional_checks.get(optional_check_index); - const optional_type = module.types.get(module.values.get(optional_check.value).getType(module)); + const optional_check = module.values.optional_checks.get(optional_check_index); + const optional_type = module.types.array.get(module.values.array.get(optional_check.value).getType(module)); assert(optional_type.* == .optional); - const optional_element_type = module.types.get(optional_type.optional.element_type); + const optional_element_type = module.types.array.get(optional_type.optional.element_type); const is_null_suffix_expression = switch (optional_element_type.*) { .pointer => false, else => true, @@ -1111,12 +1189,12 @@ pub const TranslationUnit = struct { } }, .optional_unwrap => |optional_unwrap_index| { - const optional_unwrap = module.optional_unwraps.get(optional_unwrap_index); - const optional_value = module.values.get(optional_unwrap.value); - const optional_type = module.types.get(optional_value.getType(module)); + const optional_unwrap = module.values.optional_unwraps.get(optional_unwrap_index); + const optional_value = module.values.array.get(optional_unwrap.value); + const optional_type = module.types.array.get(optional_value.getType(module)); assert(optional_type.* == .optional); const optional_element_type_index = optional_type.optional.element_type; - const optional_element_type = module.types.get(optional_element_type_index); + const optional_element_type = module.types.array.get(optional_element_type_index); try list.append(allocator, '('); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ @@ -1131,7 +1209,7 @@ pub const TranslationUnit = struct { } }, .slice_access => |slice_access_index| { - const slice_access = module.slice_accesses.get(slice_access_index); + const slice_access = module.values.slice_accesses.get(slice_access_index); try list.append(allocator, '('); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = slice_access.value, @@ -1141,17 +1219,17 @@ pub const TranslationUnit = struct { try list.appendSlice(allocator, @tagName(slice_access.field)); }, .indexed_access => |indexed_access_index| { - const indexed_access = module.indexed_accesses.get(indexed_access_index); + const indexed_access = module.values.indexed_accesses.get(indexed_access_index); try list.append(allocator, '('); const indexed_expression_index = indexed_access.indexed_expression; - const indexed_expression = module.values.get(indexed_expression_index); + const indexed_expression = module.values.array.get(indexed_expression_index); const indexed_expression_type_index = indexed_expression.getType(module); try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ .value_index = indexed_expression_index, .type_index = indexed_expression_type_index, }); - const indexed_expression_type = module.types.get(indexed_expression_type_index); + const indexed_expression_type = module.types.array.get(indexed_expression_type_index); switch (indexed_expression_type.*) { .slice => { try list.appendSlice(allocator, ".ptr"); @@ -1166,6 +1244,62 @@ pub const TranslationUnit = struct { }); try list.append(allocator, ']'); }, + .optional_cast => |cast_index| { + const optional_cast = module.values.casts.get(cast_index); + const optional_type = module.types.array.get(optional_cast.type); + switch (optional_type.*) { + .optional => |optional| { + const optional_element_type = module.types.array.get(optional.element_type); + switch (optional_element_type.*) { + .pointer => try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = optional_cast.value, + .type_index = optional.element_type, + }), + else => { + try list.append(allocator, '('); + try unit.writeType(module, list, allocator, optional_cast.type); + try list.appendSlice(allocator, ") {\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, ".value = "); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = optional_cast.value, + .type_index = Compilation.Type.Index.invalid, + }); + try list.appendSlice(allocator, ",\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, ".is_null = false,\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.append(allocator, '}'); + }, + } + }, + else => |t| @panic(@tagName(t)), + } + }, + .undefined => try list.appendSlice(allocator, "{}"), + .array_coerce_to_slice => |cast_index| { + const array_coerce_to_slice = module.values.casts.get(cast_index); + try list.append(allocator, '('); + try unit.writeType(module, list, allocator, array_coerce_to_slice.type); + try list.appendSlice(allocator, ") {\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.appendSlice(allocator, ".ptr = "); + try unit.writeValue(module, list, allocator, function_return_type, indentation, .{ + .value_index = array_coerce_to_slice.value, + .type_index = Compilation.Type.Index.invalid, + }); + try list.appendSlice(allocator, ".value,\n"); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + const array_value = module.values.array.get(array_coerce_to_slice.value); + const array_type = module.types.array.get(array_value.getType(module)); + const array_length = switch (array_type.*) { + .array => |array| array.element_count, + else => |t| @panic(@tagName(t)), + }; + try list.writer(allocator).print(".len = {},\n", .{array_length}); + try list.appendNTimes(allocator, ' ', indentation * margin_width); + try list.append(allocator, '}'); + }, else => |t| @panic(@tagName(t)), } } diff --git a/bootstrap/backend/elf.zig b/bootstrap/backend/elf.zig deleted file mode 100644 index 06e91ec..0000000 --- a/bootstrap/backend/elf.zig +++ /dev/null @@ -1,394 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const equal = std.mem.eql; - -const data_structures = @import("../data_structures.zig"); -const Allocator = data_structures.Allocator; -const ArrayList = data_structures.ArrayList; - -const emit = @import("emit.zig"); -const page_size = 0x1000; - -pub fn writeToMemory(image: *emit.Result) !std.ArrayListAlignedUnmanaged(u8, page_size) { - const allocator = image.section_manager.allocator; - - try image.section_manager.addNullSection(); - - const symbol_table_index = try image.section_manager.addSection(.{ - .name = ".symtab", - .size_guess = 50, - .alignment = @alignOf(SymbolTable.Entry), - .flags = .{ - .read = false, - .write = false, - .execute = false, - }, - .type = .symbol_table, - }); - const string_table_index = try image.section_manager.addSection(.{ - .name = ".strtab", - .size_guess = 50, - .alignment = 1, - .flags = .{ - .read = false, - .write = false, - .execute = false, - }, - .type = .string_table, - }); - const section_header_string_table_index = try image.section_manager.addSection(.{ - .name = ".shstrtab", - .size_guess = 50, - .alignment = 1, - .flags = .{ - .read = false, - .write = false, - .execute = false, - }, - .type = .string_table, - }); - - const base_virtual_address = 0x400000; - const text_section_index = 1; - - const program_header_count = blk: { - var result: usize = 0; - for (image.section_manager.sections.items) |section| { - result += @intFromBool(switch (section.type) { - .null => false, - .loadable_program => true, - .string_table => false, - .symbol_table => false, - }); - } - break :blk result; - }; - - var symbol_name_offset: u32 = 0; - - try image.section_manager.appendToSection(symbol_table_index, std.mem.asBytes(&SymbolTable.Entry{ - .name_offset = symbol_name_offset, - .information = 0, - .other = 0, - .section_header_index = 0, - .value = 0, - .size = 0, - })); - - try image.section_manager.appendToSection(string_table_index, ""); - try image.section_manager.appendByteToSection(string_table_index, 0); - symbol_name_offset += 1; - - 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); - } - - 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), - })); - - var program_segment_offset: usize = 0; - - 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, - }; - - 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, - })); - - program_segment_offset += program_segment_size; - }, - .null, - .string_table, - .symbol_table, - => {}, - } - } - - 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; - - 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; - - if (file.items.len < section_offset) { - try file.appendNTimes(allocator, 0, section_offset - file.items.len); - } - - 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, - })); - - try image.section_manager.appendToSection(string_table_index, symbol_name); - try image.section_manager.appendByteToSection(string_table_index, 0); - - 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; -} - -const Header = extern struct { - magic: u8 = 0x7f, - elf_id: [3]u8 = "ELF".*, - bit_count: BitCount = .@"64", - endianness: Endianness = .little, - header_version: u8 = 1, - os_abi: ABI, - abi_version: u8 = 0, - padding: [7]u8 = [_]u8{0} ** 7, - object_type: ObjectFileType = .executable, // e_type - machine: Machine, - version: u32 = 1, - entry: u64, - program_header_offset: u64 = std.mem.alignForward(u16, @sizeOf(Header), @alignOf(ProgramHeader)), - section_header_offset: u64, - flags: u32 = 0, - header_size: u16 = 0x40, - program_header_size: u16 = @sizeOf(ProgramHeader), - program_header_count: u16 = 1, - section_header_size: u16 = @sizeOf(SectionHeader), - section_header_count: u16, - section_header_string_table_index: u16, - - const BitCount = enum(u8) { - @"32" = 1, - @"64" = 2, - }; - - const ABI = enum(u8) { - systemv = 0, - }; - - const ObjectFileType = enum(u16) { - none = 0, - relocatable = 1, - executable = 2, - dynamic = 3, - core = 4, - lo_os = 0xfe00, - hi_os = 0xfeff, - lo_proc = 0xff00, - hi_proc = 0xffff, - }; - - const Machine = enum(u16) { - AMD64 = 0x3e, - }; - - const Endianness = enum(u8) { - little = 1, - big = 2, - }; -}; - -const ProgramHeader = extern struct { - type: Type, - flags: Flags, - offset: u64, - virtual_address: u64, - physical_address: u64, - size_in_file: u64, - size_in_memory: u64, - alignment: u64, - - const Type = enum(u32) { - null = 0, - load = 1, - dynamic = 2, - interpreter = 3, - note = 4, - shlib = 5, // reserved - program_header = 6, - tls = 7, - lo_os = 0x60000000, - hi_os = 0x6fffffff, - lo_proc = 0x70000000, - hi_proc = 0x7fffffff, - }; - - const Flags = packed struct(u32) { - executable: bool, - writable: bool, - readable: bool, - reserved: u29 = 0, - }; -}; -const SectionHeader = extern struct { - name_offset: u32, - type: Type, - flags: Flags, - virtual_address: u64, - file_offset: u64, - size: u64, - // section index - link: u32, - info: u32, - alignment: u64, - entry_size: u64, - - // type - const Type = enum(u32) { - null = 0, - program_data = 1, - symbol_table = 2, - string_table = 3, - relocation_entries_addends = 4, - symbol_hash_table = 5, - dynamic_linking_info = 6, - notes = 7, - program_space_no_data = 8, - relocation_entries = 9, - reserved = 10, - dynamic_linker_symbol_table = 11, - array_of_constructors = 14, - array_of_destructors = 15, - array_of_pre_constructors = 16, - section_group = 17, - extended_section_indices = 18, - number_of_defined_types = 19, - start_os_specific = 0x60000000, - }; - - const Flags = packed struct(u64) { - writable: bool, - alloc: bool, - executable: bool, - reserved: bool = false, - mergeable: bool = false, - contains_null_terminated_strings: bool = false, - info_link: bool = false, - link_order: bool = false, - os_non_conforming: bool = false, - section_group: bool = false, - tls: bool = false, - _reserved: u53 = 0, - }; -}; - -const SymbolTable = extern struct { - const Entry = extern struct { - name_offset: u32, - information: u8, - other: u8, - section_header_index: u16, - value: u64, - size: u64, - }; -}; diff --git a/bootstrap/backend/emit.zig b/bootstrap/backend/emit.zig deleted file mode 100644 index c38e066..0000000 --- a/bootstrap/backend/emit.zig +++ /dev/null @@ -1,270 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const log = std.log; -const page_size = std.mem.page_size; -const assert = std.debug.assert; -const expect = std.testing.expect; -const expectEqual = std.testing.expectEqual; - -const Compilation = @import("../Compilation.zig"); - -const ir = @import("intermediate_representation.zig"); -const IR = ir.IR; - -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; - -const elf = @import("elf.zig"); -const pe = @import("pe.zig"); -const macho = @import("macho.zig"); - -const jit_callconv = .SysV; - -const Section = struct { - 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, - loadable_program, - string_table, - symbol_table, - }; - - const Flags = packed struct { - read: bool, - write: bool, - execute: bool, - }; -}; - -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) = .{}, - rodata: ?u16 = null, - null: bool = false, - linker_relocations: ArrayList(Relocation) = .{}, - allocator: Allocator, - - pub fn addSection(section_manager: *SectionManager, arguments: SectionCreation) !usize { - const index = section_manager.sections.items.len; - - const r = try section_manager.insertSection(index, arguments); - assert(index == r); - - 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 = .{ - .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 { - const result = Result{ - .section_manager = section_manager, - .target = target, - .entry_point = entry_point_index, - }; - - return result; - } - - 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); - } - comptime { - assert(@typeInfo(FunctionType) == .Fn); - } - - assert(image.sections.text.content.len > 0); - return @as(*const FunctionType, @ptrCast(&image.sections.text.content[image.entry_point])); - } - - fn writeElf(image: *Result, executable_relative_path: []const u8) !void { - const file_in_memory = try elf.writeToMemory(image); - try writeFile(file_in_memory.items, executable_relative_path); - } - - fn writeFile(bytes: []const u8, path: []const u8) !void { - const flags = switch (@import("builtin").os.tag) { - .windows => .{}, - else => .{ - .mode = 0o777, - }, - }; - - const file_descriptor = try std.fs.cwd().createFile(path, flags); - try file_descriptor.writeAll(bytes); - file_descriptor.close(); - } - - fn writePe(image: *Result, executable_relative_path: []const u8) !void { - _ = executable_relative_path; - _ = image; - // var writer = try pe.Writer.init(allocator); - // try writer.writeToMemory(image); - // try writer.writeToFile(executable_relative_path); - unreachable; - } -}; - -pub fn InstructionSelector(comptime Instruction: type) type { - return struct { - functions: ArrayList(Function), - allocator: Allocator, - - pub const Function = struct { - instructions: ArrayList(Instruction) = .{}, - relocations: ArrayList(u32) = .{}, - block_map: AutoHashMap(ir.BasicBlock.Index, u32) = .{}, - - pub fn addInstruction(function: *Function, allocator: Allocator, instruction: Instruction) !u32 { - const index = function.instructions.items.len; - try function.instructions.append(allocator, instruction); - - return @intCast(index); - } - }; - - const Selector = @This(); - }; -} - -const x86_64 = @import("x86_64.zig"); -const aarch64 = @import("aarch64.zig"); - -pub const Logger = x86_64.Logger; - -pub fn get(comptime arch: std.Target.Cpu.Arch) type { - const backend = switch (arch) { - .x86_64 => x86_64, - .aarch64 => aarch64, - else => {}, - }; - - return struct { - pub fn initialize(allocator: Allocator, intermediate: *IR, descriptor: Compilation.Module.Descriptor) !void { - switch (arch) { - .x86_64 => { - var mir = try backend.MIR.selectInstructions(allocator, intermediate, descriptor.target); - try mir.allocateRegisters(); - const os = descriptor.target.os.tag; - const image = try mir.encode(); - - switch (os) { - .linux => try image.writeElf(descriptor.executable_path), - .windows => try image.writePe(descriptor.executable_path), - else => unreachable, - } - }, - else => { - const file = try std.fs.cwd().readFileAlloc(allocator, "main", std.math.maxInt(u64)); - try macho.interpretFile(allocator, descriptor, file); - }, - } - - // switch (@import("builtin").os.tag) { - // .linux => switch (@import("builtin").cpu.arch == arch) { - // true => { - // const entryPoint = result.getEntryPoint(fn () callconv(.SysV) noreturn); - // entryPoint(); - // }, - // false => {}, - // }, - // else => {}, - // } - } - }; -} diff --git a/bootstrap/backend/intermediate_representation.zig b/bootstrap/backend/intermediate_representation.zig deleted file mode 100644 index 3430e43..0000000 --- a/bootstrap/backend/intermediate_representation.zig +++ /dev/null @@ -1,1269 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; - -const Compilation = @import("../Compilation.zig"); -const log = Compilation.log; -const logln = Compilation.logln; -const Module = Compilation.Module; -const Package = Compilation.Package; - -const data_structures = @import("../data_structures.zig"); -const ArrayList = data_structures.ArrayList; -const BlockList = data_structures.BlockList; -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, - function_name, - phi_removal, - weird_bug, - - pub var bitset = std.EnumSet(Logger).initMany(&.{ - .function, - .weird_bug, - .function_name, - }); -}; - -pub const IR = struct { - arguments: BlockList(Argument) = .{}, - basic_blocks: BlockList(BasicBlock) = .{}, - binary_operations: BlockList(BinaryOperation) = .{}, - branches: BlockList(Branch) = .{}, - calls: BlockList(Call) = .{}, - casts: BlockList(Cast) = .{}, - function_definitions: BlockList(FunctionDefinition) = .{}, - instructions: BlockList(Instruction) = .{}, - jumps: BlockList(Jump) = .{}, - loads: BlockList(Load) = .{}, - phis: BlockList(Phi) = .{}, - returns: BlockList(Return) = .{}, - stack_slots: BlockList(StackSlot) = .{}, - string_literals: BlockList(StringLiteral) = .{}, - stores: BlockList(Store) = .{}, - syscalls: BlockList(Syscall) = .{}, - - section_manager: SectionManager, - module: *Module, - entry_point: FunctionDefinition.Index, - - pub fn getFunctionName(ir: *IR, function_index: FunctionDefinition.Index) []const u8 { - return ir.module.getName(ir.module.function_name_map.get(@bitCast(function_index)).?).?; - } -}; - -pub const StringLiteral = struct { - offset: u32, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const BinaryOperation = struct { - left: Instruction.Index, - right: Instruction.Index, - id: Id, - type: Type, - - const Id = enum { - add, - sub, - logical_and, - logical_xor, - logical_or, - signed_multiply, - signed_divide, - shift_left, - shift_right, - integer_compare_equal, - }; - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -const Cast = struct { - value: Instruction.Index, - type: Type, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; -const Syscall = struct { - arguments: ArrayList(Instruction.Index), - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const Jump = struct { - target: BasicBlock.Index, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const Branch = struct { - condition: Instruction.Index, - true_jump: Jump.Index, - false_jump: Jump.Index, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const Load = struct { - value: Instruction.Index, - ordering: ?AtomicOrder = null, - @"volatile": bool = false, - - pub fn isUnordered(load: *const Load) bool { - return (load.ordering == null or load.ordering == .unordered) and !load.@"volatile"; - } - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -const ConstantInteger = struct { - value: extern union { - signed: i64, - unsigned: u64, - }, - type: Type.Scalar, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const AtomicOrder = enum { - unordered, - monotonic, - acquire, - release, - acquire_release, - sequentially_consistent, -}; - -pub const Store = struct { - source: Instruction.Index, - destination: Instruction.Index, - ordering: ?AtomicOrder = null, - @"volatile": bool = false, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; -pub const StackSlot = struct { - type: Type, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const Return = struct { - value: Instruction.Index, - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const Argument = struct { - type: Type, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; - - pub const Map = AutoArrayHashMap(Compilation.Declaration.Index, Instruction.Index); -}; - -pub const BasicBlock = struct { - instructions: ArrayList(Instruction.Index) = .{}, - parent: FunctionDefinition.Index = FunctionDefinition.Index.invalid, - /// This variable `filled` is set to true when local value numbering is finished for a basic block, - /// that is, whenever the block is not going to receive more instructions - filled: bool = false, - sealed: bool = false, - predecessors: ArrayList(BasicBlock.Index) = .{}, - incomplete_phis: ArrayList(Instruction.Index) = .{}, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -const Builder = struct { - allocator: Allocator, - ir: IR, - current_function_index: FunctionDefinition.Index = FunctionDefinition.Index.invalid, - current_basic_block_index: BasicBlock.Index = BasicBlock.Index.invalid, - return_phi_index: Instruction.Index = Instruction.Index.invalid, - return_basic_block_index: BasicBlock.Index = BasicBlock.Index.invalid, - - fn translateType(builder: *Builder, type_index: Compilation.Type.Index) !?Type { - const sema_type = builder.ir.module.types.get(type_index); - return switch (sema_type.*) { - .integer => |integer| switch (integer.bit_count) { - 8 => Type.i8, - 16 => Type.i16, - 32 => Type.i32, - 64 => Type.i64, - else => unreachable, - }, - // TODO - .pointer => Type.i64, - .bool => Type.i1, - .void, - .noreturn, - => null, - else => |t| @panic(@tagName(t)), - }; - } - - fn allocateBlock(builder: *Builder) !BasicBlock.Allocation { - const current_function_index = builder.current_function_index; - assert(!current_function_index.invalid); - const basic_block = try builder.ir.basic_blocks.append(builder.allocator, .{}); - basic_block.ptr.parent = current_function_index; - - return basic_block; - } - - fn appendAndSetCurrentBlock(builder: *Builder) !BasicBlock.Allocation { - const basic_block = try builder.allocateBlock(); - builder.current_basic_block_index = basic_block.index; - return basic_block; - } - - fn createStackSlot(builder: *Builder, arguments: struct { - type: Type, - sema: Compilation.Declaration.Index, - }) !Instruction.Index { - const current_function = builder.ir.function_definitions.get(builder.current_function_index); - const stack_reference_allocation = try builder.ir.stack_slots.append(builder.allocator, .{ - .type = arguments.type, - }); - - const instruction_index = try builder.createInstructionAndAppendToCurrentBlock(.{ - .stack_slot = stack_reference_allocation.index, - }); - - try current_function.stack_map.put(builder.allocator, arguments.sema, instruction_index); - - return instruction_index; - } - - fn createInstructionAndAppendToCurrentBlock(builder: *Builder, instruction_union: Instruction.U) !Instruction.Index { - const current_function_index = builder.current_function_index; - assert(!current_function_index.invalid); - const current_basic_block_index = builder.current_basic_block_index; - assert(!current_basic_block_index.invalid); - const current_basic_block = builder.ir.basic_blocks.get(current_basic_block_index); - assert(current_basic_block.parent.eq(current_function_index)); - const instruction = try builder.ir.instructions.append(builder.allocator, .{ - .u = instruction_union, - }); - - try builder.appendToBlock(current_basic_block_index, instruction.index); - - return instruction.index; - } - - fn appendToBlock(builder: *Builder, basic_block_index: BasicBlock.Index, instruction_index: Instruction.Index) !void { - const basic_block = builder.ir.basic_blocks.get(basic_block_index); - const instruction = builder.ir.instructions.get(instruction_index); - assert(instruction.parent.invalid); - instruction.parent = basic_block_index; - try basic_block.instructions.append(builder.allocator, instruction_index); - } - - fn emitConstantInteger(builder: *Builder, constant_integer: ConstantInteger) !Instruction.Index { - // TODO: should we emit integer constants to the block? - assert(constant_integer.type.getKind() == .integer); - const load_integer = try builder.createInstructionAndAppendToCurrentBlock(.{ - .constant_integer = constant_integer, - }); - return load_integer; - } - - fn emitValue(builder: *Builder, sema_value_index: Compilation.Value.Index) !Instruction.Index { - const sema_value = builder.ir.module.values.get(sema_value_index); - const result = switch (sema_value.*) { - .bool => |boolean| try builder.emitConstantInteger(ConstantInteger{ - .value = .{ - .unsigned = @intFromBool(boolean), - }, - .type = .i8, - }), - .integer => |integer| try builder.emitConstantInteger(ConstantInteger{ - .value = .{ - .unsigned = integer.value, - }, - .type = (builder.translateType(integer.type) catch unreachable orelse unreachable).scalar, - }), - .declaration_reference => |sema_declaration_reference| blk: { - const sema_declaration_index = sema_declaration_reference.value; - const sema_declaration = builder.ir.module.declarations.get(sema_declaration_index); - // TODO: substitute stack slot with a precise name - const stack_slot = switch (sema_declaration.scope_type) { - .local => local: { - const current_function = builder.ir.function_definitions.get(builder.current_function_index); - const stack = current_function.stack_map.get(sema_declaration_index).?; - break :local stack; - }, - .global => unreachable, - }; - const load = try builder.ir.loads.append(builder.allocator, .{ - .value = stack_slot, - }); - const instruction = try builder.createInstructionAndAppendToCurrentBlock(.{ - .load = load.index, - }); - - break :blk instruction; - }, - .sign_extend => |sema_cast_index| blk: { - const cast_type: CastType = switch (sema_value.*) { - .sign_extend => .sign_extend, - else => unreachable, - }; - const sema_cast = builder.ir.module.casts.get(sema_cast_index); - const source_value = try builder.emitValue(sema_cast.value); - - const cast_allocation = try builder.ir.casts.append(builder.allocator, .{ - .value = source_value, - .type = try builder.translateType(sema_cast.type) orelse unreachable, - }); - - break :blk try builder.createInstructionAndAppendToCurrentBlock(switch (cast_type) { - inline else => |ct| @unionInit(Instruction.U, @tagName(ct), cast_allocation.index), - }); - }, - .call => |sema_call_index| try builder.emitCall(sema_call_index), - .binary_operation => |sema_binary_operation_index| try builder.emitBinaryOperation(sema_binary_operation_index), - .syscall => |sema_syscall_index| try builder.emitSyscall(sema_syscall_index), - .string_literal => |sema_string_literal_index| blk: { - const string_literal = builder.ir.module.string_literals.getValue(sema_string_literal_index).?; - - 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(rodata_section_offset), - }); - - break :blk try builder.createInstructionAndAppendToCurrentBlock(.{ - .constant_string_literal = string_literal_allocation.index, - }); - }, - else => |t| @panic(@tagName(t)), - }; - - return result; - } - - fn emitCall(builder: *Builder, sema_call_index: Compilation.Call.Index) anyerror!Instruction.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.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.emitValue(sema_argument_value_index); - argument_list.appendAssumeCapacity(argument_value_index); - } - break :blk argument_list.items; - }, - true => &.{}, - }; - - const call = try builder.ir.calls.append(builder.allocator, .{ - .callable = switch (builder.ir.module.values.get(sema_call.value).*) { - .function_definition => |sema_function_definition_index| .{ - .function_definition = .{ - .element = sema_function_definition_index.element, - .block = sema_function_definition_index.block, - }, - }, - // .function => |function_index| .{ - // .index = function_index.index, - // .block = function_index.block, - // }, - else => |t| @panic(@tagName(t)), - }, - .arguments = argument_list, - }); - - const instruction_index = try builder.createInstructionAndAppendToCurrentBlock(.{ - .call = call.index, - }); - - return instruction_index; - } - - fn emitBinaryOperation(builder: *Builder, sema_binary_operation_index: Compilation.BinaryOperation.Index) anyerror!Instruction.Index { - const sema_binary_operation = builder.ir.module.binary_operations.get(sema_binary_operation_index); - - const left = try builder.emitValue(sema_binary_operation.left); - const right = try builder.emitValue(sema_binary_operation.right); - - const sema_type = builder.ir.module.types.get(sema_binary_operation.type).*; - const binary_operation_type = try builder.translateType(sema_binary_operation.type); - - const binary_operation = try builder.ir.binary_operations.append(builder.allocator, .{ - .left = left, - .right = right, - .id = switch (sema_binary_operation.id) { - .add => .add, - .sub => .sub, - .logical_and => .logical_and, - .logical_xor => .logical_xor, - .logical_or => .logical_or, - .multiply => switch (sema_type) { - .integer => |integer| switch (integer.signedness) { - .signed => .signed_multiply, - else => |t| @panic(@tagName(t)), - }, - else => |t| @panic(@tagName(t)), - }, - .divide => switch (sema_type) { - .integer => |integer| switch (integer.signedness) { - .signed => .signed_divide, - else => |t| @panic(@tagName(t)), - }, - else => |t| @panic(@tagName(t)), - }, - .shift_left => .shift_left, - .shift_right => .shift_right, - .compare_equal => switch (sema_type) { - .integer => .integer_compare_equal, - else => unreachable, - }, - }, - .type = binary_operation_type orelse unreachable, - }); - - const instruction = try builder.createInstructionAndAppendToCurrentBlock(.{ - .binary_operation = binary_operation.index, - }); - - return instruction; - } - - fn emitBlock(builder: *Builder, sema_block_index: Compilation.Block.Index) anyerror!void { - const sema_block = builder.ir.module.blocks.get(sema_block_index); - - for (sema_block.statements.items) |sema_statement_index| { - const sema_statement = builder.ir.module.values.get(sema_statement_index); - switch (sema_statement.*) { - .declaration => |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.ir.module.types.get(sema_declaration.type); - switch (declaration_type.*) { - .comptime_int => unreachable, - else => { - const ir_type = try builder.translateType(sema_declaration.type); - const stack_slot = try builder.createStackSlot(.{ - .type = ir_type orelse unreachable, - .sema = sema_declaration_index, - }); - - _ = try builder.emitAssignment(.{ - .destination = stack_slot, - .sema_source = sema_declaration.init_value, - }); - }, - } - }, - .branch => |sema_branch_index| { - const sema_branch = builder.ir.module.branches.get(sema_branch_index); - const condition = try builder.emitValue(sema_branch.condition); - const true_expression = builder.ir.module.values.get(sema_branch.true_expression); - const false_expression = builder.ir.module.values.get(sema_branch.false_expression); - - const true_block = try builder.allocateBlock(); - const false_block = try builder.allocateBlock(); - - const current_basic_block_index = builder.current_basic_block_index; - assert(!current_basic_block_index.invalid); - - const branch = try builder.ir.branches.append(builder.allocator, .{ - .condition = condition, - .true_jump = try builder.createJump(.{ - .source = current_basic_block_index, - .target = true_block.index, - }), - .false_jump = try builder.createJump(.{ - .source = current_basic_block_index, - .target = false_block.index, - }), - }); - - _ = try builder.createInstructionAndAppendToCurrentBlock(.{ - .branch = branch.index, - }); - builder.ir.basic_blocks.get(current_basic_block_index).filled = true; - try builder.sealBlock(true_block.index); - try builder.sealBlock(false_block.index); - - const exit_block = try builder.allocateBlock(); - - const sema_true_block = builder.ir.module.blocks.get(true_expression.block); - try builder.pushBlockAndEmit(true_block.index, true_expression.block); - if (sema_true_block.reaches_end) { - const jump_index = try builder.createJump(.{ - .source = builder.current_basic_block_index, - .target = exit_block.index, - }); - _ = try builder.createInstructionAndAppendToCurrentBlock(.{ - .jump = jump_index, - }); - } - builder.ir.basic_blocks.get(builder.current_basic_block_index).filled = true; - - const sema_false_block = builder.ir.module.blocks.get(false_expression.block); - try builder.pushBlockAndEmit(false_block.index, false_expression.block); - if (sema_false_block.reaches_end) { - const jump_index = try builder.createJump(.{ - .source = builder.current_basic_block_index, - .target = exit_block.index, - }); - _ = try builder.createInstructionAndAppendToCurrentBlock(.{ - .jump = jump_index, - }); - } - builder.ir.basic_blocks.get(builder.current_basic_block_index).filled = true; - - try builder.sealBlock(exit_block.index); - - builder.current_basic_block_index = exit_block.index; - }, - .@"return" => |sema_return_index| { - const sema_return = builder.ir.module.returns.get(sema_return_index); - assert(!builder.return_basic_block_index.invalid); - const jump_index = try builder.createJump(.{ - .source = builder.current_basic_block_index, - .target = builder.return_basic_block_index, - }); - - if (!sema_return.value.invalid) { - const return_value = try builder.emitValue(sema_return.value); - const return_phi_instruction = builder.ir.instructions.get(builder.return_phi_index); - assert(return_phi_instruction.parent.eq(builder.return_basic_block_index)); - const return_phi = builder.ir.phis.get(return_phi_instruction.u.phi); - try return_phi.operands.append(builder.allocator, .{ - .jump = jump_index, - .value = return_value, - }); - } - - _ = try builder.createInstructionAndAppendToCurrentBlock(.{ - .jump = jump_index, - }); - }, - .syscall => |sema_syscall_index| _ = try builder.emitSyscall(sema_syscall_index), - .@"unreachable" => _ = try builder.createInstructionAndAppendToCurrentBlock(.@"unreachable"), - .call => |sema_call_index| _ = try builder.emitCall(sema_call_index), - .assign => |sema_assignment_index| { - const sema_assignment = builder.ir.module.assignments.get(sema_assignment_index); - const current_function = builder.ir.function_definitions.get(builder.current_function_index); - const sema_declaration = builder.ir.module.values.get(sema_assignment.destination).declaration_reference.value; - const destination = current_function.stack_map.get(sema_declaration).?; - _ = try builder.emitAssignment(.{ - .destination = destination, - .sema_source = sema_assignment.source, - }); - }, - else => |t| @panic(@tagName(t)), - } - } - } - - fn emitAssignment(builder: *Builder, arguments: struct { - destination: Instruction.Index, - sema_source: Compilation.Value.Index, - }) !Instruction.Index { - const value_index = try builder.emitValue(arguments.sema_source); - - const store = try builder.ir.stores.append(builder.allocator, .{ - .destination = arguments.destination, - .source = value_index, - }); - - return try builder.createInstructionAndAppendToCurrentBlock(.{ - .store = store.index, - }); - } - - fn emitSyscall(builder: *Builder, sema_syscall_index: Compilation.Syscall.Index) anyerror!Instruction.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; - assert(!sema_syscall_number.invalid); - const number_value_index = try builder.emitValue(sema_syscall_number); - - arguments.appendAssumeCapacity(number_value_index); - - for (sema_syscall.getArguments()) |sema_syscall_argument| { - assert(!sema_syscall_argument.invalid); - const argument_value_index = try builder.emitValue(sema_syscall_argument); - arguments.appendAssumeCapacity(argument_value_index); - } - - const syscall = try builder.ir.syscalls.append(builder.allocator, .{ - .arguments = arguments, - }); - - return try builder.createInstructionAndAppendToCurrentBlock(.{ - .syscall = syscall.index, - }); - } - - fn createJump(builder: *Builder, arguments: struct { - source: BasicBlock.Index, - target: BasicBlock.Index, - }) !Jump.Index { - assert(!arguments.source.invalid); - assert(!arguments.target.invalid); - - const target_block = builder.ir.basic_blocks.get(arguments.target); - assert(!target_block.sealed); - const jump = try builder.ir.jumps.append(builder.allocator, .{ - .target = arguments.target, - }); - try target_block.predecessors.append(builder.allocator, arguments.source); - // TODO: predecessors - return jump.index; - } - - fn sealBlock(builder: *Builder, basic_block_index: BasicBlock.Index) !void { - const block = builder.ir.basic_blocks.get(basic_block_index); - for (block.incomplete_phis.items) |_| { - unreachable; - } - block.sealed = true; - } - - fn pushBlockAndEmit(builder: *Builder, basic_block_index: BasicBlock.Index, sema_block_index: Compilation.Block.Index) !void { - builder.current_basic_block_index = basic_block_index; - try builder.emitBlock(sema_block_index); - } -}; -pub const CastType = enum { - sign_extend, -}; - -pub fn findReachableBlocks(arguments: struct { - allocator: Allocator, - ir: *IR, - first: BasicBlock.Index, - traverse_functions: bool, -}) !ArrayList(BasicBlock.Index) { - const allocator = arguments.allocator; - const ir = arguments.ir; - const first = arguments.first; - const traverse_functions = arguments.traverse_functions; - - const BlockSearcher = struct { - to_visit: ArrayList(BasicBlock.Index) = .{}, - visited: AutoArrayHashMap(BasicBlock.Index, void) = .{}, - - fn visit(searcher: *@This(), a: Allocator, basic_block: BasicBlock.Index) !void { - if (searcher.visited.get(basic_block) == null) { - try searcher.to_visit.append(a, basic_block); - try searcher.visited.put(a, basic_block, {}); - } - } - }; - - var searcher = BlockSearcher{}; - try searcher.to_visit.append(allocator, first); - try searcher.visited.put(allocator, first, {}); - - while (searcher.to_visit.items.len > 0) { - const block_index = searcher.to_visit.swapRemove(0); - const block_to_visit = ir.basic_blocks.get(block_index); - const last_instruction_index = block_to_visit.instructions.items[block_to_visit.instructions.items.len - 1]; - const last_instruction = ir.instructions.get(last_instruction_index); - switch (last_instruction.u) { - .jump => |jump_index| { - const ir_jump = ir.jumps.get(jump_index); - const new_block = ir_jump.target; - try searcher.visit(allocator, new_block); - }, - .call => |call_index| { - if (traverse_functions) { - const ir_call = ir.calls.get(call_index); - switch (ir_call.callable) { - .function_definition => |definition_index| { - switch (definition_index.invalid) { - false => { - const function = ir.function_definitions.get(definition_index); - try searcher.visit(allocator, function.entry_block); - }, - true => {}, - } - }, - // else => unreachable, - } - } - }, - .branch => |branch_index| { - const branch = ir.branches.get(branch_index); - const true_jump = ir.jumps.get(branch.true_jump); - const false_jump = ir.jumps.get(branch.false_jump); - try searcher.visit(allocator, true_jump.target); - try searcher.visit(allocator, false_jump.target); - }, - .@"unreachable", - .ret, - .store, - => {}, - else => |t| @panic(@tagName(t)), - } - } - - var list = try ArrayList(BasicBlock.Index).initCapacity(allocator, searcher.visited.keys().len); - list.appendSliceAssumeCapacity(searcher.visited.keys()); - - return list; -} - -const Callable = struct { - argument_map: AutoArrayHashMap(Compilation.Declaration.Index, Instruction.Index), - calling_convention: Compilation.CallingConvention, - return_type: ?Type, - attributes: Attributes, - - const Attributes = struct { - returns: bool, - }; - - pub const Index = union(enum) { - function_definition: FunctionDefinition.Index, - }; -}; - -pub const Type = union(enum) { - scalar: Scalar, - vector: Vector, - aggregate: Aggregate.Index, - - pub const Vector = struct { - count: u16, - scalar: Scalar, - alignment: u16 = Vector.default_alignment, - - const default_alignment = std.math.log2_int(u16, 16); - }; - - pub const Scalar = enum { - i1, - i8, - i16, - i32, - i64, - - pub const Kind = enum { - integer, - float, - }; - - pub fn getKind(scalar: Type.Scalar) Kind { - return switch (scalar) { - .i1, - .i8, - .i16, - .i32, - .i64, - => .integer, - }; - } - }; - - const Aggregate = struct { - kind: Kind, - const Kind = enum { - @"struct", - @"union", - }; - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; - }; - - pub const @"i1" = Type{ - .scalar = .i1, - }; - pub const @"i8" = Type{ - .scalar = .i8, - }; - pub const @"i16" = Type{ - .scalar = .i16, - }; - pub const @"i32" = Type{ - .scalar = .i32, - }; - pub const @"i64" = Type{ - .scalar = .i64, - }; - - pub fn getSize(t: Type) usize { - const result: usize = switch (t) { - .scalar => switch (t.scalar) { - .i1 => @sizeOf(i1), - .i8 => @sizeOf(i8), - .i16 => @sizeOf(i16), - .i32 => @sizeOf(i32), - .i64 => @sizeOf(i64), - }, - else => |tg| @panic(@tagName(tg)), - }; - - return result; - } - - pub fn getAlignment(t: Type) u16 { - const result: u16 = switch (t) { - .scalar => switch (t.scalar) { - .i1 => @alignOf(i1), - .i8 => @alignOf(i8), - .i16 => @alignOf(i16), - .i32 => @alignOf(i32), - .i64 => @alignOf(i64), - }, - else => |tag| @panic(@tagName(tag)), - }; - return result; - } -}; - -pub const FunctionDefinition = struct { - callable: Callable, - entry_block: BasicBlock.Index = BasicBlock.Index.invalid, - stack_map: AutoHashMap(Compilation.Declaration.Index, Instruction.Index) = .{}, - - fn formatter(allocator: Allocator, function_definition: FunctionDefinition.Index, ir: *IR) FunctionDefinition.Formatter { - return .{ - .function = function_definition, - .ir = ir, - .allocator = allocator, - }; - } - - pub const Formatter = struct { - function: FunctionDefinition.Index, - ir: *IR, - allocator: Allocator, - - pub fn format(function_formatter: *const Formatter, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - const function_index = function_formatter.function; - const ir = function_formatter.ir; - const function = ir.function_definitions.get(function_index); - const sema_function_index: Compilation.Function.Index = @bitCast(function_index); - const function_name_hash = ir.module.function_name_map.get(sema_function_index).?; - const function_name = ir.module.getName(function_name_hash).?; - try writer.print("Function #{} \"{s}\"\n", .{ function_index.uniqueInteger(), function_name }); - - const reachable_blocks = findReachableBlocks(.{ - .allocator = function_formatter.allocator, - .ir = ir, - .first = function.entry_block, - .traverse_functions = false, - }) catch unreachable; - - for (reachable_blocks.items, 0..) |basic_block_index, function_block_index| { - const basic_block = ir.basic_blocks.get(basic_block_index); - try writer.print("\nBLOCK #{} (${}):\n", .{ function_block_index, basic_block_index.uniqueInteger() }); - - for (basic_block.instructions.items, 0..) |instruction_index, block_instruction_index| { - try writer.print("%{} (${}): ", .{ block_instruction_index, instruction_index.uniqueInteger() }); - - const instruction = ir.instructions.get(instruction_index); - switch (instruction.u) { - // .binary_operation => {}, @tagName(binary_operation.type) - else => try writer.writeAll(@tagName(instruction.u)), - } - - try writer.writeByte(' '); - - switch (instruction.u) { - .syscall => |syscall_index| { - const syscall = ir.syscalls.get(syscall_index); - try writer.writeAll(" ("); - for (syscall.arguments.items, 0..) |arg_index, i| { - const arg_value = ir.instructions.get(arg_index); - - try writer.print("${}: {s}", .{ i, @tagName(arg_value.u) }); - - if (i < syscall.arguments.items.len - 1) { - try writer.writeAll(", "); - } - } - try writer.writeAll(")"); - }, - .jump => |jump_index| { - const jump = ir.jumps.get(jump_index); - try writer.print("${}", .{jump.target.uniqueInteger()}); - }, - .phi => |phi_index| { - const phi = ir.phis.get(phi_index); - for (phi.operands.items, 0..) |phi_operand, i| { - const arg_value = ir.instructions.get(phi_operand.value); - - try writer.print("%{} (#{}): {s}", .{ i, phi_operand.value.uniqueInteger(), @tagName(arg_value.u) }); - - if (i < phi.operands.items.len - 1) { - try writer.writeAll(", "); - } - } - try writer.writeAll(")"); - }, - .ret => |ret_index| { - const ret = ir.returns.get(ret_index); - switch (ret.value.invalid) { - false => { - const ret_value = ir.instructions.get(ret.value); - try writer.print("{s}", .{@tagName(ret_value.u)}); - }, - true => try writer.writeAll("void"), - } - }, - // .load => |load_index| { - // const load = ir.loads.get(load_index); - // try writer.print("{s}", .{@tagName(ir.values.get(load.value).*)}); - // }, - .store => |store_index| { - const store = ir.stores.get(store_index); - const source = ir.instructions.get(store.source); - const destination = ir.instructions.get(store.destination); - try writer.print("{s}, {s}", .{ @tagName(destination.u), @tagName(source.u) }); - }, - .call => |call_index| { - const call = ir.calls.get(call_index); - - switch (call.callable) { - .function_definition => |definition_index| try writer.print("${} {s}(", .{ definition_index.uniqueInteger(), ir.getFunctionName(definition_index) }), - } - - for (call.arguments, 0..) |arg_index, i| { - const arg_value = ir.instructions.get(arg_index); - - try writer.print("${}: {s}", .{ i, @tagName(arg_value.u) }); - - if (i < call.arguments.len - 1) { - try writer.writeAll(", "); - } - } - try writer.writeAll(")"); - }, - .constant_integer => |integer| { - try writer.print("{s} (unsigned: 0x{x}, signed {})", .{ @tagName(integer.type), integer.value.unsigned, integer.value.signed }); - }, - .@"unreachable" => {}, - .constant_string_literal => |string_literal_index| { - const string_literal = ir.string_literals.get(string_literal_index); - try writer.print("at 0x{x}", .{string_literal.offset}); - }, - .stack_slot => |stack_index| { - const stack = ir.stack_slots.get(stack_index); - try writer.print("size: {}. alignment: {}", .{ stack.type.getSize(), stack.type.getAlignment() }); - }, - .argument => |argument_index| { - const argument = ir.arguments.get(argument_index); - try writer.print("${}, size: {}. alignment: {}", .{ argument_index, argument.type.getSize(), argument.type.getAlignment() }); - }, - .sign_extend => |cast_index| { - const cast = ir.casts.get(cast_index); - try writer.print("{s} ${}", .{ @tagName(cast.type), cast.value.uniqueInteger() }); - }, - .load => |load_index| { - const load = ir.loads.get(load_index); - try writer.print("${}", .{load.value.uniqueInteger()}); - }, - .binary_operation => |binary_operation_index| { - const binary_operation = ir.binary_operations.get(binary_operation_index); - try writer.writeAll(@tagName(binary_operation.id)); - try writer.print("${}, ${}", .{ binary_operation.left.uniqueInteger(), binary_operation.right.uniqueInteger() }); - }, - .branch => |branch_index| { - const branch = ir.branches.get(branch_index); - try writer.print("${}, #{}, #{}", .{ branch.condition.uniqueInteger(), branch.true_jump.uniqueInteger(), branch.false_jump.uniqueInteger() }); - }, - // else => |t| @panic(@tagName(t)), - } - - try writer.writeByte('\n'); - } - } - _ = options; - _ = fmt; - } - }; - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -const Phi = struct { - operands: ArrayList(Phi.Operand) = .{}, - - const Operand = struct { - jump: Jump.Index, - value: Instruction.Index, - }; - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const Call = struct { - callable: Callable.Index, - arguments: []const Instruction.Index, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const Instruction = struct { - u: U, - parent: BasicBlock.Index = BasicBlock.Index.invalid, - - pub const U = union(enum) { - argument: Argument.Index, - binary_operation: BinaryOperation.Index, - branch: Branch.Index, - call: Call.Index, - constant_integer: ConstantInteger, - constant_string_literal: StringLiteral.Index, - jump: Jump.Index, - load: Load.Index, - phi: Phi.Index, - ret: Return.Index, - sign_extend: Cast.Index, - stack_slot: StackSlot.Index, - store: Store.Index, - syscall: Syscall.Index, - @"unreachable": void, - }; - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -const ArgumentMap = AutoArrayHashMap(Compilation.Declaration.Index, Instruction.Index); - -pub fn initialize(compilation: *Compilation, module: *Module) !*IR { - const builder = try compilation.base_allocator.create(Builder); - const allocator = compilation.base_allocator; - builder.* = .{ - .allocator = allocator, - .ir = .{ - .module = module, - .section_manager = SectionManager{ - .allocator = allocator, - }, - .entry_point = @bitCast(module.entry_point), - }, - }; - - _ = 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_definition_iterator = module.function_definitions.iterator(); - - while (sema_function_definition_iterator.nextIndex()) |sema_function_definition_index| { - builder.return_basic_block_index = BasicBlock.Index.invalid; - builder.current_function_index = FunctionDefinition.Index.invalid; - builder.current_basic_block_index = BasicBlock.Index.invalid; - builder.return_phi_index = Instruction.Index.invalid; - // const function_decl_name = builder.ir.getFunctionName(function_declaration_allocation.index); - - const function_name = module.getName(module.function_name_map.get(sema_function_definition_index).?).?; - const sema_function_definition = module.function_definitions.get(sema_function_definition_index); - const sema_prototype = builder.ir.module.function_prototypes.get(builder.ir.module.types.get(sema_function_definition.prototype).function); - const function_calling_convention = sema_prototype.attributes.calling_convention; - const returns = !sema_prototype.return_type.eq(Compilation.Type.noreturn); - const function_return_type = try builder.translateType(sema_prototype.return_type); - // arguments: - const function_argument_map = if (sema_prototype.arguments) |sema_arguments| blk: { - var arg_map = ArgumentMap{}; - try arg_map.ensureTotalCapacity(builder.allocator, @intCast(sema_arguments.len)); - - for (sema_arguments) |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) orelse unreachable, - }); - const value_allocation = try builder.ir.instructions.append(builder.allocator, .{ - .u = .{ - .argument = argument_allocation.index, - }, - }); - arg_map.putAssumeCapacity(sema_argument_declaration_index, value_allocation.index); - } - - break :blk arg_map; - } else ArgumentMap{}; - - const function_definition_allocation = try builder.ir.function_definitions.addOne(builder.allocator); - function_definition_allocation.ptr.* = .{ - .callable = .{ - .argument_map = function_argument_map, - .calling_convention = function_calling_convention, - .return_type = function_return_type, - .attributes = .{ - .returns = returns, - }, - }, - }; - const function_definition = function_definition_allocation.ptr; - builder.current_function_index = function_definition_allocation.index; - - builder.return_basic_block_index = if (returns) blk: { - const exit_block = try builder.ir.basic_blocks.append(builder.allocator, .{}); - const is_void = false; - builder.return_phi_index = if (is_void) ret_value: { - break :ret_value Instruction.Index.invalid; - } else ret_value: { - const phi = try builder.ir.phis.append(builder.allocator, .{}); - const phi_instruction = try builder.ir.instructions.append(builder.allocator, .{ - .u = .{ - .phi = phi.index, - }, - }); - - try builder.appendToBlock(exit_block.index, phi_instruction.index); - - break :ret_value phi_instruction.index; - }; - const ret = try builder.ir.returns.append(builder.allocator, .{ - .value = builder.return_phi_index, - }); - const ret_instruction = try builder.ir.instructions.append(builder.allocator, .{ - .u = .{ - .ret = ret.index, - }, - }); - - try builder.appendToBlock(exit_block.index, ret_instruction.index); - - break :blk exit_block.index; - } else BasicBlock.Index.invalid; - - const function_body = module.blocks.get(sema_function_definition.body); - - if (function_body.statements.items.len > 0) { - // Create the entry block and assign it to the function - const entry_block = try builder.appendAndSetCurrentBlock(); - function_definition.entry_block = entry_block.index; - - // Process arguments. TODO: Currently we spill them to the stack just like LLVM, but surely there must be a better way - try function_definition.stack_map.ensureUnusedCapacity(builder.allocator, @intCast(function_definition.callable.argument_map.keys().len)); - - for (function_definition.callable.argument_map.keys(), function_definition.callable.argument_map.values()) |sema_argument_index, ir_argument_instruction_index| { - const ir_argument_instruction = builder.ir.instructions.get(ir_argument_instruction_index); - const ir_argument = builder.ir.arguments.get(ir_argument_instruction.u.argument); - - _ = try builder.createStackSlot(.{ - .type = ir_argument.type, - .sema = sema_argument_index, - }); - } - - for (function_definition.callable.argument_map.keys(), function_definition.callable.argument_map.values()) |sema_argument_index, ir_argument_instruction_index| { - const stack_slot = function_definition.stack_map.get(sema_argument_index).?; - - const store = try builder.ir.stores.append(builder.allocator, .{ - .destination = stack_slot, - .source = ir_argument_instruction_index, - }); - - _ = try builder.createInstructionAndAppendToCurrentBlock(.{ - .store = store.index, - }); - } - // End processing arguments - - try builder.emitBlock(sema_function_definition.body); - } - - if (function_body.reaches_end) { - assert(returns); - if (!function_definition.entry_block.invalid) { - const jump_index = try builder.createJump(.{ - .source = builder.current_basic_block_index, - .target = builder.return_basic_block_index, - }); - _ = try builder.createInstructionAndAppendToCurrentBlock(.{ - .jump = jump_index, - }); - } else { - function_definition.entry_block = builder.return_basic_block_index; - } - } - - assert(!function_definition.entry_block.invalid); - - if (std.mem.eql(u8, function_name, "main")) { - logln(.ir, .function, "{}", .{FunctionDefinition.formatter(builder.allocator, function_definition_allocation.index, &builder.ir)}); - } - } - - //unreachable; - return &builder.ir; -} diff --git a/bootstrap/backend/macho.zig b/bootstrap/backend/macho.zig deleted file mode 100644 index 00e2ff8..0000000 --- a/bootstrap/backend/macho.zig +++ /dev/null @@ -1,696 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const equal = std.mem.eql; - -const Compilation = @import("../Compilation.zig"); - -const data_structures = @import("../data_structures.zig"); -const ArrayList = data_structures.ArrayList; -const mmap = data_structures.mmap; - -const Header = extern struct { - magic: u32 = magic, - cpu_type: CpuType, - cpu_subtype: extern union { - arm: ArmSubType, - x86: X86SubType, - }, - file_type: FileType, - load_command_count: u32, - load_command_size: u32, - flags: Flags, - reserved: u32 = 0, - - const magic = 0xfeedfacf; - - const CpuType = enum(u32) { - VAX = 0x00000001, - ROMP = 0x00000002, - NS32032 = 0x00000004, - NS32332 = 0x00000005, - MC680x0 = 0x00000006, - x86 = 0x00000007, - MIPS = 0x00000008, - NS32352 = 0x00000009, - MC98000 = 0x0000000A, - HPPA = 0x0000000B, - ARM = 0x0000000C, - MC88000 = 0x0000000D, - SPARC = 0x0000000E, - i860be = 0x0000000F, - i860_le = 0x00000010, - RS6000 = 0x00000011, - PowerPC = 0x00000012, - arm64 = 0x0000000C | abi64, - x86_64 = 0x00000007 | abi64, - - const abi64 = 0x01000000; - }; - - const ArmSubType = enum(u32) { - all = 0x00000000, - ARM_A500_ARCH = 0x00000001, - ARM_A500 = 0x00000002, - ARM_A440 = 0x00000003, - ARM_M4 = 0x00000004, - ARM_V4T = 0x00000005, - ARM_V6 = 0x00000006, - ARM_V5TEJ = 0x00000007, - ARM_XSCALE = 0x00000008, - ARM_V7 = 0x00000009, - ARM_V7F = 0x0000000A, - ARM_V7S = 0x0000000B, - ARM_V7K = 0x0000000C, - ARM_V8 = 0x0000000D, - ARM_V6M = 0x0000000E, - ARM_V7M = 0x0000000F, - ARM_V7EM = 0x00000010, - _, - }; - - const X86SubType = enum(u32) { - All = 0x00000003, - @"486" = 0x00000004, - @"486SX" = 0x00000084, - Pentium_M5 = 0x00000056, - Celeron = 0x00000067, - Celeron_Mobile = 0x00000077, - Pentium_3 = 0x00000008, - Pentium_3_M = 0x00000018, - Pentium_3_XEON = 0x00000028, - Pentium_4 = 0x0000000A, - Itanium = 0x0000000B, - Itanium_2 = 0x0000001B, - XEON = 0x0000000C, - XEON_MP = 0x0000001C, - _, - }; - - const FileType = enum(u32) { - relocatable_object = 0x00000001, - demand_paged_executable = 0x00000002, - fixed_vm_shared_library = 0x00000003, - core = 0x00000004, - preloaded_executable = 0x00000005, - dynamic_shared_library = 0x00000006, - dynamic_link_editor = 0x00000007, - dynamic_bundle = 0x00000008, - shared_library_stub = 0x00000009, - debug_companion = 0x0000000A, - x86_64_kext = 0x0000000B, - archive = 0x0000000C, - }; - - const Flags = packed struct(u32) { - no_undefined_references: bool = true, - incrementally_linked: bool = false, - dynamic_linker_input: bool = true, - dynamic_linker_bound_undefined_references: bool = false, - prebound_dynamic_undefined_references: bool = false, - split_ro_and_rw_segments: bool = false, - _: bool = false, - two_level_namespace_bindings: bool = true, - no_symbol_multiple_definition_in_subimages: bool = false, - no_dyld_prebinding_agent_notification: bool = false, - can_redo_prebinding: bool = false, - bind_two_level_namespaces_to_libraries: bool = false, - safe_to_split_sections_for_dead_code_stripping: bool = false, - canonicalized_by_unprebinding: bool = false, - final_external_weak_symbols: bool = false, - final_weak_symbols: bool = false, - all_stacks_execute_protected: bool = false, - safe_for_zero_uid: bool = false, - safe_for_ugid: bool = false, - no_check_dependent_dylibs_for_reexport: bool = false, - load_at_random_address: bool = false, - no_load_command_for_unreferenced_dylib: bool = true, - thread_local_variable_section: bool = false, - run_with_non_executable_heap: bool = false, - code_linked_for_application_use: bool = false, - nlist_external_symbols_not_all_dyld_info_symbols: bool = false, - allow_lc_min_version_macos_lc_build_version: bool = false, - reserved: u4 = 0, - dylib_only: bool = false, - }; -}; - -const UniversalHeader = extern struct { - magic: u32 = magic, - binary_count: u32, - - const magic = 0xcafebabe; -}; - -const LoadCommand = extern struct { - type: Type, - size: u32, - - const Type = enum(u32) { - segment32 = 0x01, - symbol_table = 0x02, - symbol_table_information = 0x0b, - load_dylib = 0x0c, - id_dylib = 0x0d, - load_dylinker = 0x0e, - id_dylinker = 0x0f, - optional_dynamic_library = 0x18, - segment64 = 0x19, - uuid_number = 0x1b, - code_signature = 0x1d, - compressed_linkedit_table = 0x22, - function_starts = 0x26, - data_in_code = 0x29, - source_version = 0x2a, - minimum_os_version = 0x32, - dyld_exports_trie = 0x80000033, - dyld_chained_fixups = 0x80000034, - dyld_main_entry_point = 0x80000028, - }; - - const Segment64 = extern struct { - type: Type = .segment64, - size: u32, - name: [16]u8, - address: u64, - address_size: u64, - file_offset: u64, - file_size: u64, - maximum_virtual_memory_protections: VirtualMemoryProtection, - initial_virtual_memory_protections: VirtualMemoryProtection, - section_count: u32, - flags: Flags, - - const VirtualMemoryProtection = packed struct(u32) { - read: bool, - write: bool, - execute: bool, - reserved: u29 = 0, - }; - - const Flags = packed struct(u32) { - vm_space_high_part: bool = false, - vm_fixed_library: bool = false, - no_relocation: bool = false, - protected_segment: bool = false, - read_only_after_relocations: bool = false, - reserved: u27 = 0, - }; - - const Section = extern struct { - name: [16]u8, - segment_name: [16]u8, - address: u64, - size: u64, - file_offset: u32, - alignment: u32, - relocation_file_offset: u32, - relocation_count: u32, - type: Section.Type, - reserved: u8 = 0, - flags: Section.Flags, - reserved0: u32 = 0, - reserved1: u32 = 0, - reserved2: u32 = 0, - - comptime { - assert(@sizeOf(Section) == 80); - } - - const Type = enum(u8) { - regular = 0, - only_non_lazy_symbol_pointers = 0b110, - only_lazy_symbol_pointers_only_symbol_stubs = 0b111, - zero_fill_on_demand_section = 0b1100, - only_lazy_pointers_to_lazy_loaded_dylibs = 0b10000, - }; - - const Flags = packed struct(u16) { - local_relocations: bool = false, - external_relocations: bool = false, - some_machine_instructions: bool = false, - reserved: u5 = 0, - reserved2: u1 = 0, - debug_section: bool = false, - i386_code_stubs: bool = false, - live_blocks_if_reference_live_blocks: bool = false, - no_dead_stripping: bool = false, - strip_static_symbols_dyldlink_flag: bool = false, - coalesced_symbols: bool = false, - only_machine_instructions: bool = false, - }; - }; - - fn getSize(section_count: u32) u32 { - return @sizeOf(LoadCommand.Segment64) + section_count * @sizeOf(LoadCommand.Segment64.Section); - } - }; - - const LinkeditData = extern struct { - type: Type, - size: u32 = 16, - data_offset: u32, - data_size: u32, - }; - - const SymbolTable = extern struct { - type: Type, - size: u32 = 24, - symbol_offset: u32, - symbol_count: u32, - string_table_offset: u32, - string_table_size: u32, - }; - - const SymbolTableInformation = extern struct { - type: Type, - size: u32 = 80, - local_symbol_index: u32, - local_symbol_count: u32, - external_symbol_index: u32, - external_symbol_count: u32, - undefined_symbol_index: u32, - undefined_symbol_count: u32, - content_table_offset: u32, - content_table_entry_count: u32, - module_table_offset: u32, - module_table_entry_count: u32, - referenced_symbol_table_offset: u32, - referenced_symbol_table_entry_count: u32, - indirect_symbol_table_offset: u32, - indirect_symbol_table_entry_count: u32, - external_relocation_offset: u32, - external_relocation_entry_count: u32, - local_relocation_offset: u32, - local_relocation_entry_count: u32, - }; - - const Dylinker = extern struct { - type: Type, - size: u32, - name_offset: u32 = 12, - }; - - const Dylib = extern struct { - type: Type, - size: u32, - name_offset: u32, - timestamp: u32, - current_version: u32, - compatibility_version: u32, - }; - - const Uuid = extern struct { - type: Type, - size: u32, - uuid: [16]u8, - }; - - const MinimumVersion = extern struct { - type: Type, - size: u32, - version: u32, - sdk: u32, - }; - - const SourceVersion = extern struct { - type: Type, - size: u32, - version: u64, - }; - - const EntryPoint = extern struct { - type: Type, - size: u32, - entry_offset: u64, - stack_size: u64, - }; -}; - -const Writer = struct { - items: []u8, - index: usize = 0, - address_offset: usize = 0, - file_offset: usize = 0, - load_command_size: u32, - segment_count: u16, - segment_index: u16 = 0, - segment_offset: u16 = @sizeOf(Header), - linkedit_segment_address_offset: u64 = 0, - linkedit_segment_file_offset: u64 = 0, - linkedit_segment_size: u32 = 0, - - fn getWrittenBytes(writer: *const Writer) []const u8 { - return writer.items[0..writer.index]; - } - - fn append(writer: *Writer, bytes: []const u8) void { - writer.writeBytesAt(bytes, writer.index); - writer.index += bytes.len; - } - - fn writeBytesAt(writer: *Writer, bytes: []const u8, offset: usize) void { - @memcpy(writer.items[offset..][0..bytes.len], bytes); - } - - const SegmentCreation = struct { - name: []const u8, - sections: []const SectionCreation, - protection: LoadCommand.Segment64.VirtualMemoryProtection, - }; - - const SectionCreation = struct { - name: []const u8, - bytes: []const u8, - alignment: u32 = 1, - flags: LoadCommand.Segment64.Section.Flags, - }; - - fn writeSegment(writer: *Writer, descriptor: SegmentCreation) void { - assert(writer.segment_index < writer.segment_count); - defer writer.segment_index += 1; - - const segment_name = blk: { - var result = [1]u8{0} ** 16; - @memcpy(result[0..descriptor.name.len], descriptor.name); - break :blk result; - }; - - if (equal(u8, descriptor.name, "__PAGEZERO")) { - assert(writer.segment_offset == @sizeOf(Header)); - const address_size = 4 * 1024 * 1024 * 1024; - writer.writeBytesAt(std.mem.asBytes(&LoadCommand.Segment64{ - .size = @sizeOf(LoadCommand.Segment64), - .name = segment_name, - .address = 0, - .address_size = address_size, - .file_offset = 0, - .file_size = 0, - .maximum_virtual_memory_protections = descriptor.protection, - .initial_virtual_memory_protections = descriptor.protection, - .section_count = @intCast(descriptor.sections.len), - .flags = .{}, - }), writer.segment_offset); - - writer.address_offset += address_size; - writer.segment_offset += @sizeOf(LoadCommand.Segment64); - } else if (equal(u8, descriptor.name, "__TEXT")) { - const original_offset = writer.segment_offset; - assert(original_offset == @sizeOf(Header) + @sizeOf(LoadCommand.Segment64)); - writer.segment_offset += @sizeOf(LoadCommand.Segment64); - - const text_metadata_offset = @sizeOf(Header) + writer.load_command_size; - var section_address_offset = writer.address_offset + text_metadata_offset; - var section_file_offset = writer.file_offset + text_metadata_offset; - - for (descriptor.sections) |section| { - section_address_offset = std.mem.alignForward(usize, section_address_offset, section.alignment); - section_file_offset = std.mem.alignForward(usize, section_file_offset, section.alignment); - - writer.writeBytesAt(std.mem.asBytes(&LoadCommand.Segment64.Section{ - .name = blk: { - var result = [1]u8{0} ** 16; - @memcpy(result[0..section.name.len], section.name); - break :blk result; - }, - .segment_name = segment_name, - .address = section_address_offset, - .size = section.bytes.len, - .file_offset = @intCast(section_file_offset), - .alignment = std.math.log2(section.alignment), - .relocation_file_offset = 0, - .relocation_count = 0, - .type = .regular, - .flags = section.flags, - }), writer.segment_offset); - - @memcpy(writer.items[section_file_offset..][0..section.bytes.len], section.bytes); - - section_address_offset += section.bytes.len; - section_file_offset += section.bytes.len; - - writer.segment_offset += @sizeOf(LoadCommand.Segment64.Section); - } - - const end_segment_offset = writer.segment_offset; - writer.segment_offset = original_offset; - - const size = end_segment_offset - writer.file_offset; - const aligned_size = std.mem.alignForward(usize, size, 16 * 1024); - - writer.append(std.mem.asBytes(&LoadCommand.Segment64{ - .size = @sizeOf(LoadCommand.Segment64), - .name = segment_name, - .address = writer.address_offset, - .address_size = aligned_size, - .file_offset = writer.file_offset, - .file_size = aligned_size, - .maximum_virtual_memory_protections = descriptor.protection, - .initial_virtual_memory_protections = descriptor.protection, - .section_count = @intCast(descriptor.sections.len), - .flags = .{}, - })); - - writer.segment_offset = end_segment_offset; - - writer.address_offset += aligned_size; - writer.file_offset += aligned_size; - } else { - unreachable; - } - } - - fn writeLinkeditData(writer: *Writer, bytes: []const u8, load_command_type: LoadCommand.Type) void { - if (writer.linkedit_segment_size == 0) { - writer.linkedit_segment_address_offset = writer.address_offset; - writer.linkedit_segment_file_offset = writer.file_offset; - } - - const data_size: u32 = @intCast(bytes.len); - @memcpy(writer.items[writer.file_offset..][0..data_size], bytes); - - writer.append(std.mem.asBytes(&LoadCommand.LinkeditData{ - .type = load_command_type, - .data_offset = @intCast(writer.linkedit_segment_file_offset), - .data_size = data_size, - })); - - writer.address_offset += data_size; - writer.file_offset += data_size; - - writer.linkedit_segment_size += data_size; - } -}; - -pub fn interpretFile(allocator: Allocator, descriptor: Compilation.Module.Descriptor, file: []const u8) !void { - _ = allocator; - _ = descriptor; - const header: *const Header = @ptrCast(@alignCast(file.ptr)); - //print("Header : {}", .{header}); - assert(header.magic == Header.magic); - - var text_segment: LoadCommand.Segment64 = undefined; - const load_command_start: *const LoadCommand = @ptrCast(@alignCast(file[@sizeOf(Header)..].ptr)); - var load_command_ptr = load_command_start; - - for (0..header.load_command_count) |_| { - const load_command = load_command_ptr.*; - switch (load_command.type) { - .segment64 => { - const segment_load_command: *const LoadCommand.Segment64 = @ptrCast(@alignCast(load_command_ptr)); - const text_segment_name = "__TEXT"; - if (equal(u8, segment_load_command.name[0..text_segment_name.len], text_segment_name)) { - text_segment = segment_load_command.*; - } - //print("SLC: {}", .{segment_load_command}); - //print("segment name: {s}", .{segment_load_command.name}); - const section_ptr: [*]const LoadCommand.Segment64.Section = @ptrFromInt(@intFromPtr(segment_load_command) + @sizeOf(LoadCommand.Segment64)); - const sections = section_ptr[0..segment_load_command.section_count]; - for (sections) |section| { - _ = section; - //print("{}", .{section}); - //print("Section name: {s}. Segment name: {s}", .{ section.name, section.segment_name }); - } - }, - .dyld_chained_fixups => { - const command: *const LoadCommand.LinkeditData = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - .dyld_exports_trie => { - const command: *const LoadCommand.LinkeditData = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - .symbol_table => { - const command: *const LoadCommand.SymbolTable = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - .symbol_table_information => { - const command: *const LoadCommand.SymbolTableInformation = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - .load_dylinker => { - const command: *const LoadCommand.Dylinker = @ptrCast(@alignCast(load_command_ptr)); - //print("command: {}", .{command}); - const name: [*:0]const u8 = @ptrFromInt(@intFromPtr(command) + command.name_offset); - _ = name; - //print("Name: {s}", .{name}); - }, - .uuid_number => { - const command: *const LoadCommand.Uuid = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - .minimum_os_version => { - const command: *const LoadCommand.MinimumVersion = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - .source_version => { - const command: *const LoadCommand.SourceVersion = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - .dyld_main_entry_point => { - const command: *const LoadCommand.EntryPoint = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - .load_dylib => { - const command: *const LoadCommand.Dylib = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - //print("Dylib: {s}", .{@as([*:0]const u8, @ptrFromInt(@intFromPtr(load_command_ptr) + @sizeOf(LoadCommand.Dylib)))}); - }, - .function_starts => { - const command: *const LoadCommand.LinkeditData = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - .data_in_code => { - const command: *const LoadCommand.LinkeditData = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - .code_signature => { - const command: *const LoadCommand.LinkeditData = @ptrCast(@alignCast(load_command_ptr)); - _ = command; - //print("command: {}", .{command}); - }, - else => |t| @panic(@tagName(t)), - } - - load_command_ptr = @ptrFromInt(@intFromPtr(load_command_ptr) + load_command.size); - } - - // const load_command_end = load_command_ptr; - // const load_command_size = @intFromPtr(load_command_end) - @intFromPtr(load_command_start); - // assert(load_command_size == header.load_command_size); - - const segment_count = 3; - var writer = Writer{ - .items = try mmap(0x100000, .{}), - .load_command_size = segment_count * @sizeOf(LoadCommand.Segment64) + - 2 * @sizeOf(LoadCommand.Segment64.Section) + - @sizeOf(LoadCommand.LinkeditData) + - @sizeOf(LoadCommand.LinkeditData) + - @sizeOf(LoadCommand.SymbolTable) + - @sizeOf(LoadCommand.SymbolTableInformation) + - @sizeOf(LoadCommand.Dylinker) + std.mem.alignForward(u32, "/usr/lib/dyld".len, 8) + - @sizeOf(LoadCommand.Uuid) + - @sizeOf(LoadCommand.MinimumVersion) + - @sizeOf(LoadCommand.EntryPoint) + - @sizeOf(LoadCommand.Dylib) + std.mem.alignForward(u32, "/usr/lib/libSystem.B.dylib".len, 8) + - 3 * @sizeOf(LoadCommand.LinkeditData), - .segment_count = segment_count, - }; - writer.index = @sizeOf(Header); - writer.writeSegment(.{ - .name = "__PAGEZERO", - .sections = &.{}, - .protection = .{ - .read = false, - .write = false, - .execute = false, - }, - }); - writer.writeSegment(.{ - .name = "__TEXT", - .sections = &.{ - .{ - .name = "__text", - .bytes = &.{ - 0x00, 0x00, 0x80, 0x52, - 0xc0, 0x03, 0x5f, 0xd6, - }, - .alignment = 4, - .flags = .{ - .only_machine_instructions = true, - }, - }, - .{ - .name = "__unwind_info", - .bytes = &.{ - 0x01, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, - 0xb0, 0x3f, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, - 0xb9, 0x3f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x01, 0x00, - 0x10, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, - }, - .alignment = 4, - .flags = .{}, - }, - }, - .protection = .{ - .read = true, - .write = false, - .execute = true, - }, - }); - - // TODO: write this later - - // writer.writeSegment(.{ - // .name = "__LINKEDIT", - // .sections = &.{}, - // .protection = .{ - // .read = true, - // .write = false, - // .execute = false, - // }, - // }); - assert(writer.segment_index == writer.segment_count - 1); - writer.index = writer.segment_offset + @sizeOf(LoadCommand.Segment64); - - for (file[16384 + 56 ..][0..48]) |b| { - _ = b; - //print("0x{x}, ", .{b}); - } - - const chained_fixup_bytes = &.{ 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; - writer.writeLinkeditData(chained_fixup_bytes, .dyld_chained_fixups); - const export_trie_bytes = &.{ 0x0, 0x1, 0x5f, 0x0, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x2, 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0, 0x5, 0x6d, 0x61, 0x69, 0x6e, 0x0, 0x25, 0x3, 0x0, 0xb0, 0x7f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; - writer.writeLinkeditData(export_trie_bytes, .dyld_exports_trie); - unreachable; - // writer.writeSymbolTable( -} - -// .bytes = &.{ -// 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5f, 0x0, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x2, 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0, 0x5, 0x6d, 0x61, 0x69, 0x6e, 0x0, 0x25, 0x3, 0x0, 0xb0, 0x7f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb0, 0x7f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xf, 0x1, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0xf, 0x1, 0x0, 0x0, 0xb0, 0x3f, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x20, 0x0, 0x5f, 0x5f, 0x6d, 0x68, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfa, 0xde, 0xc, 0xc0, 0x0, 0x0, 0x1, 0x11, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0xfa, 0xde, 0xc, 0x2, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x2, 0x4, 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5d, 0x0, 0x0, 0x0, 0x58, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x40, 0xb0, 0x20, 0x2, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x6d, 0x61, 0x69, 0x6e, 0x0, 0xb2, 0x2a, 0x3, 0x79, 0x1b, 0x82, 0xf4, 0x71, 0xf1, 0xae, 0xfa, 0x44, 0x53, 0xe0, 0xc2, 0x78, 0x1e, 0x56, 0xd1, 0x9b, 0x36, 0x37, 0x7b, 0x7e, 0x61, 0xf5, 0x8a, 0x59, 0xc4, 0xf0, 0x64, 0x56, 0xad, 0x7f, 0xac, 0xb2, 0x58, 0x6f, 0xc6, 0xe9, 0x66, 0xc0, 0x4, 0xd7, 0xd1, 0xd1, 0x6b, 0x2, 0x4f, 0x58, 0x5, 0xff, 0x7c, 0xb4, 0x7c, 0x7a, 0x85, 0xda, 0xbd, 0x8b, 0x48, 0x89, 0x2c, 0xa7, 0xad, 0x7f, 0xac, 0xb2, 0x58, 0x6f, 0xc6, 0xe9, 0x66, 0xc0, 0x4, 0xd7, 0xd1, 0xd1, 0x6b, 0x2, 0x4f, 0x58, 0x5, 0xff, 0x7c, 0xb4, 0x7c, 0x7a, 0x85, 0xda, 0xbd, 0x8b, 0x48, 0x89, 0x2c, 0xa7, 0x8, 0xdb, 0xee, 0xf5, 0x95, 0x71, 0x3e, 0xcb, 0x29, 0xff, 0x3f, 0x28, 0x46, 0xf0, 0xdc, 0x97, 0xbf, 0x2d, 0x3, 0xf2, 0xec, 0xc, 0x84, 0xa, 0x44, 0x90, 0xf, 0xe0, 0xf4, 0xea, 0x67, 0x97, 0x6b, 0xb0, 0x22, 0x2, 0x0, 0xa7, 0xed, 0x94, 0xb2, 0x3d, 0x86, 0x4d, 0x13, 0xd6, 0xa4, 0xe, 0x1c, 0x1a, 0x6b, 0x9b, 0x82, 0xa0, 0xeb, 0x28, 0x23, 0xfe, 0x8a, 0x51, 0x2a, 0xe5, 0xf9, 0x39, -// }, diff --git a/bootstrap/backend/pe.zig b/bootstrap/backend/pe.zig deleted file mode 100644 index e6d0424..0000000 --- a/bootstrap/backend/pe.zig +++ /dev/null @@ -1,267 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const Allocator = std.mem.Allocator; - -const data_structures = @import("../data_structures.zig"); -const ArrayList = data_structures.ArrayList; -const emit = @import("emit.zig"); -pub const Writer = struct { - in_file: []const u8, - items: []u8, - index: usize = 0, - allocator: Allocator, - pub fn init(allocator: Allocator) !Writer { - const file = try std.fs.cwd().readFileAlloc(allocator, "main.exe", 0xfffffffffffff); - const len = std.mem.alignForward(usize, file.len, 0x1000); - return Writer{ - .in_file = file, - .items = try data_structures.mmap(len, .{}), - .allocator = allocator, - }; - } - - pub fn writeToMemory(writer: *Writer, image: *const emit.Result) !void { - //print("File len: {}", .{writer.in_file.len}); - const dos_header: *const ImageDosHeader = @ptrCast(@alignCast(writer.in_file.ptr)); - //print("File address: {}", .{dos_header.file_address_of_new_exe_header}); - //print("File: {s}", .{writer.in_file[0x40..]}); - for (writer.in_file[0x40..], 0..) |byte, index| { - _ = index; - if (byte == 'T') { - //print("Index: {}", .{index}); - break; - } - } - assert(dos_header.magic_number == ImageDosHeader.magic); - // assert(dos_header.file_address_of_new_exe_header == @sizeOf(ImageDosHeader)); - //print("{}", .{dos_header}); - const file_header: *const ImageFileHeader = @ptrCast(@alignCast(writer.in_file[dos_header.file_address_of_new_exe_header + 4 ..].ptr)); - _ = file_header; - //print("File header: {}", .{file_header}); - - writer.append(std.mem.asBytes(&ImageDosHeader{ - .file_address_of_new_exe_header = 208, - })); - while (writer.index < 208) : (writer.index += 1) { - writer.append(&.{0}); - } - writer.append(std.mem.asBytes(&image_NT_signature)); - writer.append(std.mem.asBytes(&ImageFileHeader{ - .machine = switch (image.target.cpu.arch) { - .x86_64 => .amd64, - .aarch64 => .arm64, - else => @panic("Architecture"), - }, - .section_count = 3, - .time_date_stamp = @intCast(std.time.timestamp()), - })); - - const kernel32 = blk: { - var library = Library{ - .name = "KERNEL32.DLL", - }; - try library.symbols.append(writer.allocator, Symbol{ - .name = "ExitProcess", - }); - - break :blk library; - }; - - const libraries = &[_]Library{kernel32}; - _ = libraries; - - const code = &.{ - 0x48, 0x83, 0xec, 0x28, //subq $40, %rsp - 0xb9, 0x2a, 0x00, 0x00, 0x00, //movl $42, %ecx - 0xff, 0x15, 0xf1, 0x0f, 0x00, 0x00, //callq *4081(%rip) # 0x140002000 - 0xcc, - }; - _ = code; - - const pdata = &.{ - 0x00, 0x10, - 0x00, 0x00, - 0x10, 0x10, - 0x00, 0x00, - 0x28, 0x21, - 0x00, 0x00, - }; - _ = pdata; - - // TODO - // writer.append(std.mem.asBytes(ImageOptionalHeader{ - // .magic = ImageOptionalHeader.magic, - // .size_of_code = code.len, - // })); - - unreachable; - } - - fn append(writer: *Writer, bytes: []const u8) void { - const destination = writer.items[writer.index..][0..bytes.len]; - const source = bytes; - @memcpy(destination, source); - writer.index += bytes.len; - } - - pub fn writeToFile(writer: *Writer, executable_relative_path: []const u8) !void { - _ = writer; - _ = executable_relative_path; - unreachable; - } -}; - -const ImageDosHeader = extern struct { - magic_number: u16 = magic, - bytes_last_page_of_file: u16 = 0, - pages_in_file: u16 = 0, - relocations: u16 = 0, - size_of_header_in_paragraphs: u16 = 0, - minimum_extra_paragraphs: u16 = 0, - maximum_extra_paragraphs: u16 = 0, - initial_ss_value: u16 = 0, - initial_sp_value: u16 = 0, - cheksum: u16 = 0, - initial_ip_value: u16 = 0, - initial_cs_value: u16 = 0, - file_address_of_relocation_table: u16 = 0, - overlay_number: u16 = 0, - reserved_words: [4]u16 = .{0} ** 4, - oem_id: u16 = 0, - oem_info: u16 = 0, - reserved_words2: [10]u16 = .{0} ** 10, - file_address_of_new_exe_header: u32 = @sizeOf(ImageDosHeader), - - const magic = 0x5a4d; - - comptime { - assert(@sizeOf(ImageDosHeader) == 64); - } -}; -const image_NT_signature: u32 = 0x00004550; - -/// COFF header format -const ImageFileHeader = extern struct { - machine: ImageFileMachine, - section_count: u16, - time_date_stamp: u32, - symbol_table_offset: u32 = 0, - symbol_count: u32 = 0, - size_of_optional_header: u16 = @sizeOf(ImageOptionalHeader), - characteristics: Characteristics = .{}, - - const Characteristics = packed struct(u16) { - relocations_stripped: bool = false, - executable_image: bool = true, - stripped_line_count: bool = false, - stripped_local_symbols: bool = false, - aggressive_ws_trim: bool = false, - large_address_aware: bool = true, - reserved: u1 = 0, - bytes_reversed_lo: bool = false, - machine_32bit: bool = false, - stripped_debug: bool = false, - removable_run_from_swap: bool = false, - net_run_from_swap: bool = false, - system: bool = false, - dll: bool = false, - up_systems_only: bool = false, - bytes_reversed_hi: bool = false, - }; -}; - -const ImageFileMachine = enum(u16) { - unknown = 0, - target_host = 0x0001, // Useful for indicating we want to interact with the host and not a WoW guest. - i386 = 0x014c, // Intel 386. - r3000 = 0x0162, // MIPS little-endian, 0x160 big-endian - r4000 = 0x0166, // MIPS little-endian - r10000 = 0x0168, // MIPS little-endian - wcemipsv2 = 0x0169, // MIPS little-endian WCE v2 - alpha = 0x0184, // Alpha_AXP - sh3 = 0x01a2, // SH3 little-endian - sh3dsp = 0x01a3, - sh3e = 0x01a4, // SH3E little-endian - sh4 = 0x01a6, // SH4 little-endian - sh5 = 0x01a8, // SH5 - arm = 0x01c0, // ARM Little-Endian - thumb = 0x01c2, // ARM Thumb/Thumb-2 Little-Endian - armnt = 0x01c4, // ARM Thumb-2 Little-Endian - am33 = 0x01d3, - powerpc = 0x01F0, // IBM PowerPC Little-Endian - powerpcfp = 0x01f1, - ia64 = 0x0200, // Intel 64 - mips16 = 0x0266, // MIPS - alpha64 = 0x0284, // ALPHA64 - mipsfpu = 0x0366, // MIPS - mipsfpu16 = 0x0466, // MIPS - tricore = 0x0520, // Infineon - cef = 0x0CEF, - ebc = 0x0EBC, // EFI Byte Code - amd64 = 0x8664, // AMD64 (K8) - m32r = 0x9041, // M32R little-endian - arm64 = 0xAA64, // ARM64 Little-Endian - cee = 0xC0EE, - - const axp64 = ImageFileMachine.alpha64; -}; - -const ImageOptionalHeader = extern struct { - magic: u16 = magic, - major_linker_version: u8 = 0, - minor_linker_version: u8 = 0, - size_of_code: u32, - size_of_initialized_data: u32, - size_of_uninitialized_data: u32, - address_of_entry_point: u32, - base_of_code: u32, - image_base: u64, - section_alignment: u32, - file_alignment: u32, - major_os_version: u16, - minor_os_version: u16, - major_image_version: u16, - minor_image_version: u16, - major_subsystem_version: u16, - minor_subsystem_version: u16, - win32_version_value: u32, - size_of_image: u32, - size_of_headers: u32, - checksum: u32, - subsystem: u16, - dll_characteristics: u16, - size_of_stack_reserve: u64, - size_of_stack_commit: u64, - size_of_heap_reserve: u64, - size_of_heap_commit: u64, - loader_flags: u32, - number_of_RVA_and_sizes: u32, - data_directory: [image_number_of_directory_entries]ImageDataDirectory, - - const magic = 0x20b; - - comptime { - assert(@sizeOf(ImageOptionalHeader) == 0xf0); - } -}; - -const ImageDataDirectory = extern struct { - virtual_address: u32, - size: u32, -}; - -const image_number_of_directory_entries = 0x10; - -const Library = struct { - symbols: ArrayList(Symbol) = .{}, - name: []const u8, - name_virtual_address: u32 = 0, - virtual_address: u32 = 0, - image_thunk_virtual_address: u32 = 0, -}; - -const Symbol = struct { - name: []const u8, - name_virtual_address: u32 = 0, - offset_in_data: u32 = 0, -}; diff --git a/bootstrap/backend/x86_64.zig b/bootstrap/backend/x86_64.zig deleted file mode 100644 index 6f69a25..0000000 --- a/bootstrap/backend/x86_64.zig +++ /dev/null @@ -1,6174 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const panic = std.debug.panic; -const emit = @import("emit.zig"); -const ir = @import("intermediate_representation.zig"); -const IR = ir.IR; - -const Compilation = @import("../Compilation.zig"); - -const data_structures = @import("../data_structures.zig"); -const ArrayList = data_structures.ArrayList; -const AutoArrayHashMap = data_structures.AutoArrayHashMap; -const BlockList = data_structures.BlockList; - -const log = Compilation.log; -const logln = Compilation.logln; - -const x86_64 = @This(); - -pub const Logger = enum { - register_allocation_new_instructions, - instruction_selection_block, - instruction_selection_ir_function, - instruction_selection_new_instruction, - instruction_selection_cache_flush, - instruction_selection_mir_function, - instruction_selection_register_operand_list, - register_allocation_block, - register_allocation_problematic_hint, - register_allocation_assignment, - register_allocation_reload, - register_allocation_function_before, - register_allocation_new_instruction, - register_allocation_new_instruction_function_before, - register_allocation_instruction_avoid_copy, - register_allocation_function_after, - register_allocation_operand_list_verification, - encoding, - - pub var bitset = std.EnumSet(Logger).initMany(&.{ - .instruction_selection_ir_function, - .instruction_selection_mir_function, - .instruction_selection_register_operand_list, - .instruction_selection_new_instruction, - .register_allocation_block, - .register_allocation_problematic_hint, - .register_allocation_assignment, - .register_allocation_reload, - .register_allocation_function_before, - .register_allocation_new_instruction, - .register_allocation_new_instruction_function_before, - .register_allocation_instruction_avoid_copy, - .register_allocation_function_after, - .register_allocation_operand_list_verification, - .encoding, - }); -}; - -const Register = struct { - list: List = .{}, - index: Index, - - const invalid = Register{ - .index = .{ - .physical = .no_register, - }, - }; - - fn isValid(register: Register) bool { - return switch (register.index) { - .physical => |physical| physical != .no_register, - .virtual => true, - }; - } - - const Index = union(enum) { - physical: Register.Physical, - virtual: Register.Virtual.Index, - }; - - const State = union(enum) { - virtual: Virtual.Index, - free, - preassigned, - livein, - }; - const Class = enum { - not_a_register, - any, - gp8, - gp16, - gp32, - gp64, - gp64_nosp, - ccr, - - pub const Descriptor = struct { - size: u16, - spill_size: u16, - spill_alignment: u16, - }; - }; - - const Physical = enum(u9) { - no_register = 0, - ah = 1, - al = 2, - ax = 3, - bh = 4, - bl = 5, - bp = 6, - bph = 7, - bpl = 8, - bx = 9, - ch = 10, - cl = 11, - cs = 12, - cx = 13, - df = 14, - dh = 15, - di = 16, - dih = 17, - dil = 18, - dl = 19, - ds = 20, - dx = 21, - eax = 22, - ebp = 23, - ebx = 24, - ecx = 25, - edi = 26, - edx = 27, - eflags = 28, - eip = 29, - eiz = 30, - es = 31, - esi = 32, - esp = 33, - fpcw = 34, - fpsw = 35, - fs = 36, - fs_base = 37, - gs = 38, - gs_base = 39, - hax = 40, - hbp = 41, - hbx = 42, - hcx = 43, - hdi = 44, - hdx = 45, - hip = 46, - hsi = 47, - hsp = 48, - ip = 49, - mxcsr = 50, - rax = 51, - rbp = 52, - rbx = 53, - rcx = 54, - rdi = 55, - rdx = 56, - rflags = 57, - rip = 58, - riz = 59, - rsi = 60, - rsp = 61, - si = 62, - sih = 63, - sil = 64, - sp = 65, - sph = 66, - spl = 67, - ss = 68, - ssp = 69, - tmmcfg = 70, - _eflags = 71, - cr0 = 72, - cr1 = 73, - cr2 = 74, - cr3 = 75, - cr4 = 76, - cr5 = 77, - cr6 = 78, - cr7 = 79, - cr8 = 80, - cr9 = 81, - cr10 = 82, - cr11 = 83, - cr12 = 84, - cr13 = 85, - cr14 = 86, - cr15 = 87, - dr0 = 88, - dr1 = 89, - dr2 = 90, - dr3 = 91, - dr4 = 92, - dr5 = 93, - dr6 = 94, - dr7 = 95, - dr8 = 96, - dr9 = 97, - dr10 = 98, - dr11 = 99, - dr12 = 100, - dr13 = 101, - dr14 = 102, - dr15 = 103, - fp0 = 104, - fp1 = 105, - fp2 = 106, - fp3 = 107, - fp4 = 108, - fp5 = 109, - fp6 = 110, - fp7 = 111, - k0 = 112, - k1 = 113, - k2 = 114, - k3 = 115, - k4 = 116, - k5 = 117, - k6 = 118, - k7 = 119, - mm0 = 120, - mm1 = 121, - mm2 = 122, - mm3 = 123, - mm4 = 124, - mm5 = 125, - mm6 = 126, - mm7 = 127, - r8 = 128, - r9 = 129, - r10 = 130, - r11 = 131, - r12 = 132, - r13 = 133, - r14 = 134, - r15 = 135, - st0 = 136, - st1 = 137, - st2 = 138, - st3 = 139, - st4 = 140, - st5 = 141, - st6 = 142, - st7 = 143, - tmm0 = 144, - tmm1 = 145, - tmm2 = 146, - tmm3 = 147, - tmm4 = 148, - tmm5 = 149, - tmm6 = 150, - tmm7 = 151, - xmm0 = 152, - xmm1 = 153, - xmm2 = 154, - xmm3 = 155, - xmm4 = 156, - xmm5 = 157, - xmm6 = 158, - xmm7 = 159, - xmm8 = 160, - xmm9 = 161, - xmm10 = 162, - xmm11 = 163, - xmm12 = 164, - xmm13 = 165, - xmm14 = 166, - xmm15 = 167, - xmm16 = 168, - xmm17 = 169, - xmm18 = 170, - xmm19 = 171, - xmm20 = 172, - xmm21 = 173, - xmm22 = 174, - xmm23 = 175, - xmm24 = 176, - xmm25 = 177, - xmm26 = 178, - xmm27 = 179, - xmm28 = 180, - xmm29 = 181, - xmm30 = 182, - xmm31 = 183, - ymm0 = 184, - ymm1 = 185, - ymm2 = 186, - ymm3 = 187, - ymm4 = 188, - ymm5 = 189, - ymm6 = 190, - ymm7 = 191, - ymm8 = 192, - ymm9 = 193, - ymm10 = 194, - ymm11 = 195, - ymm12 = 196, - ymm13 = 197, - ymm14 = 198, - ymm15 = 199, - ymm16 = 200, - ymm17 = 201, - ymm18 = 202, - ymm19 = 203, - ymm20 = 204, - ymm21 = 205, - ymm22 = 206, - ymm23 = 207, - ymm24 = 208, - ymm25 = 209, - ymm26 = 210, - ymm27 = 211, - ymm28 = 212, - ymm29 = 213, - ymm30 = 214, - ymm31 = 215, - zmm0 = 216, - zmm1 = 217, - zmm2 = 218, - zmm3 = 219, - zmm4 = 220, - zmm5 = 221, - zmm6 = 222, - zmm7 = 223, - zmm8 = 224, - zmm9 = 225, - zmm10 = 226, - zmm11 = 227, - zmm12 = 228, - zmm13 = 229, - zmm14 = 230, - zmm15 = 231, - zmm16 = 232, - zmm17 = 233, - zmm18 = 234, - zmm19 = 235, - zmm20 = 236, - zmm21 = 237, - zmm22 = 238, - zmm23 = 239, - zmm24 = 240, - zmm25 = 241, - zmm26 = 242, - zmm27 = 243, - zmm28 = 244, - zmm29 = 245, - zmm30 = 246, - zmm31 = 247, - r8b = 248, - r9b = 249, - r10b = 250, - r11b = 251, - r12b = 252, - r13b = 253, - r14b = 254, - r15b = 255, - r8bh = 256, - r9bh = 257, - r10bh = 258, - r11bh = 259, - r12bh = 260, - r13bh = 261, - r14bh = 262, - r15bh = 263, - r8d = 264, - r9d = 265, - r10d = 266, - r11d = 267, - r12d = 268, - r13d = 269, - r14d = 270, - r15d = 271, - r8w = 272, - r9w = 273, - r10w = 274, - r11w = 275, - r12w = 276, - r13w = 277, - r14w = 278, - r15w = 279, - r8wh = 280, - r9wh = 281, - r10wh = 282, - r11wh = 283, - r12wh = 284, - r13wh = 285, - r14wh = 286, - r15wh = 287, - k0_k1 = 288, - k2_k3 = 289, - k4_k5 = 290, - k6_k7 = 291, - - const Descriptor = struct { - subregisters: []const Register.Physical = &.{}, - }; - }; - - const Virtual = struct { - register_class: Register.Class, - use_def_list_head: Operand.Index = Operand.Index.invalid, - - pub const List = BlockList(@This()); - pub const Index = Virtual.List.Index; - pub const Allocation = Virtual.List.Allocation; - }; - - const List = struct { - previous: Operand.Index = Operand.Index.invalid, - next: Operand.Index = Operand.Index.invalid, - }; -}; - -const register_descriptors = std.EnumArray(Register.Physical, Register.Physical.Descriptor).init(.{ - .no_register = .{}, - .ah = .{}, - .al = .{}, - .ax = .{}, - .bh = .{}, - .bl = .{}, - .bp = .{}, - .bph = .{}, - .bpl = .{}, - .bx = .{}, - .ch = .{}, - .cl = .{}, - .cs = .{}, - .cx = .{}, - .df = .{}, - .dh = .{}, - .di = .{}, - .dih = .{}, - .dil = .{}, - .dl = .{}, - .ds = .{}, - .dx = .{}, - .eax = .{}, - .ebp = .{}, - .ebx = .{}, - .ecx = .{}, - .edi = .{}, - .edx = .{}, - .eflags = .{}, - .eip = .{ - .subregisters = &.{ .ip, .hip }, - }, - .eiz = .{}, - .es = .{}, - .esi = .{}, - .esp = .{}, - .fpcw = .{}, - .fpsw = .{}, - .fs = .{}, - .fs_base = .{}, - .gs = .{}, - .gs_base = .{}, - .hax = .{}, - .hbp = .{}, - .hbx = .{}, - .hcx = .{}, - .hdi = .{}, - .hdx = .{}, - .hip = .{}, - .hsi = .{}, - .hsp = .{}, - .ip = .{}, - .mxcsr = .{}, - .rax = .{}, - .rbp = .{}, - .rbx = .{}, - .rcx = .{}, - .rdi = .{}, - .rdx = .{}, - .rflags = .{}, - .rip = .{ - .subregisters = &.{.eip}, - }, - .riz = .{}, - .rsi = .{}, - .rsp = .{}, - .si = .{}, - .sih = .{}, - .sil = .{}, - .sp = .{}, - .sph = .{}, - .spl = .{}, - .ss = .{}, - .ssp = .{}, - .tmmcfg = .{}, - ._eflags = .{}, - .cr0 = .{}, - .cr1 = .{}, - .cr2 = .{}, - .cr3 = .{}, - .cr4 = .{}, - .cr5 = .{}, - .cr6 = .{}, - .cr7 = .{}, - .cr8 = .{}, - .cr9 = .{}, - .cr10 = .{}, - .cr11 = .{}, - .cr12 = .{}, - .cr13 = .{}, - .cr14 = .{}, - .cr15 = .{}, - .dr0 = .{}, - .dr1 = .{}, - .dr2 = .{}, - .dr3 = .{}, - .dr4 = .{}, - .dr5 = .{}, - .dr6 = .{}, - .dr7 = .{}, - .dr8 = .{}, - .dr9 = .{}, - .dr10 = .{}, - .dr11 = .{}, - .dr12 = .{}, - .dr13 = .{}, - .dr14 = .{}, - .dr15 = .{}, - .fp0 = .{}, - .fp1 = .{}, - .fp2 = .{}, - .fp3 = .{}, - .fp4 = .{}, - .fp5 = .{}, - .fp6 = .{}, - .fp7 = .{}, - .k0 = .{}, - .k1 = .{}, - .k2 = .{}, - .k3 = .{}, - .k4 = .{}, - .k5 = .{}, - .k6 = .{}, - .k7 = .{}, - .mm0 = .{}, - .mm1 = .{}, - .mm2 = .{}, - .mm3 = .{}, - .mm4 = .{}, - .mm5 = .{}, - .mm6 = .{}, - .mm7 = .{}, - .r8 = .{}, - .r9 = .{}, - .r10 = .{}, - .r11 = .{}, - .r12 = .{}, - .r13 = .{}, - .r14 = .{}, - .r15 = .{}, - .st0 = .{}, - .st1 = .{}, - .st2 = .{}, - .st3 = .{}, - .st4 = .{}, - .st5 = .{}, - .st6 = .{}, - .st7 = .{}, - .tmm0 = .{}, - .tmm1 = .{}, - .tmm2 = .{}, - .tmm3 = .{}, - .tmm4 = .{}, - .tmm5 = .{}, - .tmm6 = .{}, - .tmm7 = .{}, - .xmm0 = .{}, - .xmm1 = .{}, - .xmm2 = .{}, - .xmm3 = .{}, - .xmm4 = .{}, - .xmm5 = .{}, - .xmm6 = .{}, - .xmm7 = .{}, - .xmm8 = .{}, - .xmm9 = .{}, - .xmm10 = .{}, - .xmm11 = .{}, - .xmm12 = .{}, - .xmm13 = .{}, - .xmm14 = .{}, - .xmm15 = .{}, - .xmm16 = .{}, - .xmm17 = .{}, - .xmm18 = .{}, - .xmm19 = .{}, - .xmm20 = .{}, - .xmm21 = .{}, - .xmm22 = .{}, - .xmm23 = .{}, - .xmm24 = .{}, - .xmm25 = .{}, - .xmm26 = .{}, - .xmm27 = .{}, - .xmm28 = .{}, - .xmm29 = .{}, - .xmm30 = .{}, - .xmm31 = .{}, - .ymm0 = .{}, - .ymm1 = .{}, - .ymm2 = .{}, - .ymm3 = .{}, - .ymm4 = .{}, - .ymm5 = .{}, - .ymm6 = .{}, - .ymm7 = .{}, - .ymm8 = .{}, - .ymm9 = .{}, - .ymm10 = .{}, - .ymm11 = .{}, - .ymm12 = .{}, - .ymm13 = .{}, - .ymm14 = .{}, - .ymm15 = .{}, - .ymm16 = .{}, - .ymm17 = .{}, - .ymm18 = .{}, - .ymm19 = .{}, - .ymm20 = .{}, - .ymm21 = .{}, - .ymm22 = .{}, - .ymm23 = .{}, - .ymm24 = .{}, - .ymm25 = .{}, - .ymm26 = .{}, - .ymm27 = .{}, - .ymm28 = .{}, - .ymm29 = .{}, - .ymm30 = .{}, - .ymm31 = .{}, - .zmm0 = .{}, - .zmm1 = .{}, - .zmm2 = .{}, - .zmm3 = .{}, - .zmm4 = .{}, - .zmm5 = .{}, - .zmm6 = .{}, - .zmm7 = .{}, - .zmm8 = .{}, - .zmm9 = .{}, - .zmm10 = .{}, - .zmm11 = .{}, - .zmm12 = .{}, - .zmm13 = .{}, - .zmm14 = .{}, - .zmm15 = .{}, - .zmm16 = .{}, - .zmm17 = .{}, - .zmm18 = .{}, - .zmm19 = .{}, - .zmm20 = .{}, - .zmm21 = .{}, - .zmm22 = .{}, - .zmm23 = .{}, - .zmm24 = .{}, - .zmm25 = .{}, - .zmm26 = .{}, - .zmm27 = .{}, - .zmm28 = .{}, - .zmm29 = .{}, - .zmm30 = .{}, - .zmm31 = .{}, - .r8b = .{}, - .r9b = .{}, - .r10b = .{}, - .r11b = .{}, - .r12b = .{}, - .r13b = .{}, - .r14b = .{}, - .r15b = .{}, - .r8bh = .{}, - .r9bh = .{}, - .r10bh = .{}, - .r11bh = .{}, - .r12bh = .{}, - .r13bh = .{}, - .r14bh = .{}, - .r15bh = .{}, - .r8d = .{}, - .r9d = .{}, - .r10d = .{}, - .r11d = .{}, - .r12d = .{}, - .r13d = .{}, - .r14d = .{}, - .r15d = .{}, - .r8w = .{}, - .r9w = .{}, - .r10w = .{}, - .r11w = .{}, - .r12w = .{}, - .r13w = .{}, - .r14w = .{}, - .r15w = .{}, - .r8wh = .{}, - .r9wh = .{}, - .r10wh = .{}, - .r11wh = .{}, - .r12wh = .{}, - .r13wh = .{}, - .r14wh = .{}, - .r15wh = .{}, - .k0_k1 = .{}, - .k2_k3 = .{}, - .k4_k5 = .{}, - .k6_k7 = .{}, -}); - -// const SubregisterIndex = struct { -// size: u16, -// offset: u16 = 0, -// }; -// -// const SubRegisterIndexType = enum { -// sub_8bit, -// sub_8bit_hi, -// sub_16bit_, -// sub_16bit_hi, -// sub_32bit, -// }; - -// const subregister_indices = std.EnumArray(SubRegisterIndexType, []const SubregisterIndex).init(.{ -// }); - -// const Sub8Bit = enum{ -// ax = 0, -// cx = 1, -// dx = 2, -// bx = 3, -// }; - -const GP32 = enum(u3) { - a = 0, - c = 1, - d = 2, - b = 3, - sp = 4, - bp = 5, - si = 6, - di = 7, -}; - -const GP64 = enum(u4) { - a = 0, - c = 1, - d = 2, - b = 3, - sp = 4, - bp = 5, - si = 6, - di = 7, - r8 = 8, - r9 = 9, - r10 = 10, - r11 = 11, - r12 = 12, - r13 = 13, - r14 = 14, - r15 = 15, -}; - -const GP64NOSP = enum(u4) { - a = 0, - c = 1, - d = 2, - b = 3, - bp = 5, - si = 6, - di = 7, - r8 = 8, - r9 = 9, - r10 = 10, - r11 = 11, - r12 = 12, - r13 = 13, - r14 = 14, - r15 = 15, -}; - -const XMMRegister = u4; - -const CallingConvention = struct { - argument_registers: RegisterGroupMap, - syscall_registers: []const Register.Physical, - - const Id = Compilation.CallingConvention; -}; - -const RegisterGroupMap = std.EnumArray(Register.Class, []const Register.Physical); - -const zero_register_class_descriptor = Register.Class.Descriptor{ - .size = 0, - .spill_size = 0, - .spill_alignment = 0, -}; -const register_class_descriptors = std.EnumArray(Register.Class, Register.Class.Descriptor).init(.{ - .not_a_register = zero_register_class_descriptor, - .any = zero_register_class_descriptor, - .gp8 = .{ - .size = @sizeOf(u8), - .spill_size = @sizeOf(u8), - .spill_alignment = @sizeOf(u8), - }, - .gp16 = .{ - .size = @sizeOf(u16), - .spill_size = @sizeOf(u16), - .spill_alignment = @sizeOf(u16), - }, - .gp32 = .{ - .size = @sizeOf(u32), - .spill_size = @sizeOf(u32), - .spill_alignment = @sizeOf(u32), - }, - .gp64 = .{ - .size = @sizeOf(u64), - .spill_size = @sizeOf(u64), - .spill_alignment = @sizeOf(u64), - }, - .gp64_nosp = .{ - .size = @sizeOf(u64), - .spill_size = @sizeOf(u64), - .spill_alignment = @sizeOf(u64), - }, - .ccr = .{ - .size = @sizeOf(u32), - .spill_size = @sizeOf(u32), - .spill_alignment = @sizeOf(u32), - }, -}); - -const registers_by_class = RegisterGroupMap.init(.{ - .not_a_register = &.{}, - .any = &.{}, - .gp8 = &.{ - .al, - .cl, - .dl, - .ah, - .ch, - .dh, - .bl, - .bh, - .sil, - .dil, - .bpl, - .spl, - .r8b, - .r9b, - .r10b, - .r11b, - .r14b, - .r15b, - .r12b, - .r13b, - }, - .gp16 = &.{ - .ax, - .cx, - .dx, - .si, - .di, - .bx, - .bp, - .sp, - .r8w, - .r9w, - .r10w, - .r11w, - .r14w, - .r15w, - .r12w, - .r13w, - }, - .gp32 = &.{ - .eax, - .ecx, - .edx, - .esi, - .edi, - .ebx, - .ebp, - .esp, - .r8d, - .r9d, - .r10d, - .r11d, - .r14d, - .r15d, - .r12d, - .r13d, - }, - .gp64 = &.{ - .rax, - .rcx, - .rdx, - .rsi, - .rdi, - .r8, - .r9, - .r10, - .r11, - .rbx, - .r14, - .r15, - .r12, - .r13, - .rbp, - .rsp, - }, - .ccr = &.{ - .eflags, - }, - .gp64_nosp = &.{}, -}); - -// TODO: fix this -const system_v_gp32_argument_registers = [4]Register.Physical{ .edi, .esi, .edx, .ecx }; -const system_v_gp64_argument_registers = [6]Register.Physical{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; -const system_v_xmm_argument_registers = [8]Register.Physical{ .xmm0, .xmm1, .xmm2, .xmm3, .xmm4, .xmm5, .xmm6, .xmm7 }; -const system_v_syscall_registers = [7]Register.Physical{ .rax, .rdi, .rsi, .rdx, .r10, .r8, .r9 }; - -const system_v = CallingConvention{ - .argument_registers = RegisterGroupMap.init(.{ - .not_a_register = &.{}, - .any = &.{}, - .gp8 = &.{}, - .gp16 = &.{}, - .gp32 = &system_v_gp32_argument_registers, - .gp64 = &system_v_gp64_argument_registers, - .gp64_nosp = &.{}, - .ccr = &.{}, - }), - .syscall_registers = &system_v_syscall_registers, -}; - -const calling_conventions = std.EnumArray(CallingConvention.Id, CallingConvention).init(.{ - .system_v = system_v, -}); - -const ValueType = struct { - size: u16, - element_count: u16, - element_type: u32, - data_type: DataType, - scalarness: Scalarness, - - fn getSize(value_type: ValueType) usize { - return value_type.size * value_type.element_count; - } - - const DataType = enum(u1) { - integer = 0, - float = 1, - }; - const Scalarness = enum(u1) { - scalar = 0, - vector = 1, - }; - - const Id = enum(u32) { - any = 0, - // other = 1, - // i1 = 2, - i8 = 3, - i16 = 4, - i32 = 5, - i64 = 6, - // i128 = 7, - ccr = 15, - }; -}; - -const value_types = std.EnumArray(ValueType.Id, ValueType).init(.{ - .any = .{ - .size = 0, - .element_count = 1, - .element_type = @intFromEnum(ValueType.Id.any), - .data_type = .integer, - .scalarness = .scalar, - }, - .i8 = .{ - .size = @sizeOf(u8), - .element_count = 1, - .element_type = @intFromEnum(ValueType.Id.i8), - .data_type = .integer, - .scalarness = .scalar, - }, - .i16 = .{ - .size = @sizeOf(u16), - .element_count = 1, - .element_type = @intFromEnum(ValueType.Id.i16), - .data_type = .integer, - .scalarness = .scalar, - }, - .i32 = .{ - .size = @sizeOf(u32), - .element_count = 1, - .element_type = @intFromEnum(ValueType.Id.i32), - .data_type = .integer, - .scalarness = .scalar, - }, - .i64 = .{ - .size = @sizeOf(u64), - .element_count = 1, - .element_type = @intFromEnum(ValueType.Id.i64), - .data_type = .integer, - .scalarness = .scalar, - }, - .ccr = .{ - .size = @sizeOf(u32), - .element_count = 1, - .element_type = @intFromEnum(ValueType.Id.i32), - .data_type = .integer, - .scalarness = .scalar, - }, -}); - -const register_classes = std.EnumArray(ValueType.Id, Register.Class).init(.{ - .any = .any, - .i8 = .gp8, - .i16 = .gp16, - .i32 = .gp32, - .i64 = .gp64, - .ccr = .ccr, -}); - -const Memory = struct { - alignment: u64, - // low_level_type: LowLevelType, - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -const LowLevelType = packed struct(u64) { - u: packed union { - vector: Vector, - scalar: Scalar, - }, - scalar: bool, - pointer: bool, - - const Vector = packed struct(u62) { - foo: u62 = 0, - }; - - const Scalar = packed struct {}; -}; - -const AddressingMode = struct { - base: AddressingMode.Base, - scale: u32 = 1, - displacement: i32 = 0, - index_register: u32 = 0, - const Base = union(enum) { - register_base: u32, - frame_index: u32, - }; -}; - -const StackObject = struct { - size: u64, - alignment: u32, - spill_slot: bool, - ir: ir.Instruction.Index, -}; - -const InstructionSelection = struct { - local_value_map: data_structures.AutoArrayHashMap(ir.Instruction.Index, Register) = .{}, - value_map: data_structures.AutoArrayHashMap(ir.Instruction.Index, Register) = .{}, - block_map: data_structures.AutoHashMap(ir.BasicBlock.Index, BasicBlock.Index) = .{}, - liveins: data_structures.AutoArrayHashMap(Register.Physical, Register.Virtual.Index) = .{}, - memory_map: data_structures.AutoArrayHashMap(ir.Instruction.Index, Memory.Index) = .{}, - stack_map: data_structures.AutoArrayHashMap(ir.Instruction.Index, u32) = .{}, - physical_register_use_or_definition_list: std.EnumArray(Register.Physical, Operand.Index) = std.EnumArray(Register.Physical, Operand.Index).initFill(Operand.Index.invalid), - current_block: BasicBlock.Index = BasicBlock.Index.invalid, - stack_objects: ArrayList(StackObject) = .{}, - function: *MIR.Function, - instruction_cache: ArrayList(Instruction.Index) = .{}, - register_fixups: data_structures.AutoArrayHashMap(Register.Index, Register.Index) = .{}, - registers_with_fixups: data_structures.AutoArrayHashMap(Register.Index, void) = .{}, - folded_loads: data_structures.AutoArrayHashMap(ir.Instruction.Index, void) = .{}, - - fn storeRegisterToStackSlot(instruction_selection: *InstructionSelection, mir: *MIR, insert_before_instruction_index: usize, source_register: Register.Physical, kill: bool, frame_index: u32, register_class: Register.Class, virtual_register: Register.Virtual.Index) !void { - _ = virtual_register; - const stack_object = instruction_selection.stack_objects.items[frame_index]; - switch (@divExact(stack_object.size, 8)) { - @sizeOf(u64) => { - switch (register_class) { - .gp64 => { - const instruction_id = Instruction.Id.mov64mr; - const instruction_descriptor = comptime instruction_descriptors.get(instruction_id); - const source_operand_id = instruction_descriptor.operands[1].id; - const addressing_mode = AddressingMode{ - .base = .{ - .frame_index = frame_index, - }, - }; - - const destination_operand_id = instruction_descriptor.operands[0].id; - const destination_operand = Operand{ - .id = destination_operand_id, - .u = .{ - .memory = .{ .addressing_mode = addressing_mode }, - }, - .flags = .{}, - }; - const source_operand = Operand{ - .id = source_operand_id, - .u = .{ - .register = .{ - .index = .{ - .physical = source_register, - }, - }, - }, - .flags = .{ - .dead_or_kill = kill, - }, - }; - - const instruction_index = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - source_operand, - }); - - try mir.blocks.get(instruction_selection.current_block).instructions.insert(mir.allocator, insert_before_instruction_index, instruction_index); - }, - else => |t| @panic(@tagName(t)), - } - }, - else => std.debug.panic("Stack object size: {}", .{stack_object.size}), - } - } - - fn loadRegisterFromStackSlot(instruction_selection: *InstructionSelection, mir: *MIR, insert_before_instruction_index: usize, destination_register: Register.Physical, frame_index: u32, register_class: Register.Class, virtual_register: Register.Virtual.Index) !void { - _ = virtual_register; - const stack_object = instruction_selection.stack_objects.items[frame_index]; - switch (stack_object.size) { - @sizeOf(u64) => { - switch (register_class) { - .gp64 => { - const instruction_id = Instruction.Id.mov64rm; - const instruction_descriptor = comptime instruction_descriptors.get(instruction_id); - const source_operand_id = instruction_descriptor.operands[1].id; - const addressing_mode = AddressingMode{ - .base = .{ - .frame_index = frame_index, - }, - }; - const source_operand = Operand{ - .id = source_operand_id, - .u = .{ - .memory = .{ .addressing_mode = addressing_mode }, - }, - .flags = .{}, - }; - const destination_operand = Operand{ - .id = .gp64, - .u = .{ - .register = .{ - .index = .{ - .physical = destination_register, - }, - }, - }, - .flags = .{ .type = .def }, - }; - const instruction_index = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - source_operand, - }); - logln(.codegen, .register_allocation_new_instructions, "Inserting instruction at index {}", .{insert_before_instruction_index}); - try mir.blocks.get(instruction_selection.current_block).instructions.insert(mir.allocator, insert_before_instruction_index, instruction_index); - }, - else => |t| @panic(@tagName(t)), - } - }, - @sizeOf(u32) => switch (register_class) { - .gp32 => { - const instruction_id = Instruction.Id.mov32rm; - const instruction_descriptor = comptime instruction_descriptors.get(instruction_id); - const source_operand_id = instruction_descriptor.operands[1].id; - const addressing_mode = AddressingMode{ - .base = .{ - .frame_index = frame_index, - }, - }; - const source_operand = Operand{ - .id = source_operand_id, - .u = .{ - .memory = .{ .addressing_mode = addressing_mode }, - }, - .flags = .{}, - }; - const destination_operand = Operand{ - .id = .gp32, - .u = .{ - .register = .{ - .index = .{ - .physical = destination_register, - }, - }, - }, - .flags = .{ .type = .def }, - }; - const instruction_index = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - source_operand, - }); - logln(.codegen, .register_allocation_new_instructions, "Inserting instruction at index {}", .{insert_before_instruction_index}); - try mir.blocks.get(instruction_selection.current_block).instructions.insert(mir.allocator, insert_before_instruction_index, instruction_index); - }, - else => |t| @panic(@tagName(t)), - }, - else => panic("Stack object size: {} bits", .{stack_object.size}), - } - } - - // TODO: add value map on top of local value map? - fn lookupRegisterForValue(instruction_selection: *InstructionSelection, mir: *MIR, ir_instruction_index: ir.Instruction.Index) !Register { - if (instruction_selection.value_map.get(ir_instruction_index)) |register| { - return register; - } - - const gop = try instruction_selection.local_value_map.getOrPutValue(mir.allocator, ir_instruction_index, Register.invalid); - return gop.value_ptr.*; - } - - fn getRegisterForValue(instruction_selection: *InstructionSelection, mir: *MIR, ir_instruction_index: ir.Instruction.Index) !Register { - const register = try instruction_selection.lookupRegisterForValue(mir, ir_instruction_index); - if (register.isValid()) { - return register; - } - - const instruction = mir.ir.instructions.get(ir_instruction_index); - const defer_materialization = switch (instruction.u) { - .stack_slot => !instruction_selection.stack_map.contains(ir_instruction_index), - .constant_integer => false, - else => true, - }; - - if (defer_materialization) { - const ir_type = getIrType(mir.ir, ir_instruction_index) orelse unreachable; - const value_type = resolveType(ir_type); - const register_class = register_classes.get(value_type); - const new_register = try mir.createVirtualRegister(register_class); - try instruction_selection.value_map.putNoClobber(mir.allocator, ir_instruction_index, new_register); - return new_register; - } else { - const new_register = switch (instruction.u) { - .constant_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); - - return new_register; - } - } - - // Moving an immediate to a register - 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 integer = mir.ir.instructions.get(ir_instruction_index).u.constant_integer; - const value_type = resolveScalarType(integer.type); - switch (integer.value.unsigned == 0) { - true => { - const instruction_id: Instruction.Id = switch (value_type) { - // .i8 => unreachable, - // .i16 => unreachable, - .i32 => .mov32r0, - // .i64 => b: { - // if (std.math.cast(u32, integer.value.unsigned)) |_| { - // break :b .mov32ri64; - // } else if (std.math.cast(i32, integer.value.signed)) |_| { - // unreachable; - // } else { - // unreachable; - // } - // }, - else => |t| @panic(@tagName(t)), - }; - 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 register_class = register_classes.get(operand_id); - const destination_operand = Operand{ - .id = operand_id, - .u = .{ - .register = destination_register, - }, - .flags = .{ .type = .def }, - }; - - const xor = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, xor); - - return destination_register; - }, - false => { - const instruction_id: Instruction.Id = switch (value_type) { - .i32 => .mov32ri, - .i64 => b: { - if (std.math.cast(u32, integer.value.unsigned)) |_| { - break :b .mov32ri64; - } else if (std.math.cast(i32, integer.value.signed)) |_| { - unreachable; - } else { - unreachable; - } - }, - else => |t| @panic(@tagName(t)), - }; - - const instruction_descriptor = instruction_descriptors.get(instruction_id); - const destination_operand_id = instruction_descriptor.operands[0].id; - const source_operand_id = instruction_descriptor.operands[1].id; - const register_class = register_classes.get(value_type); - const destination_register = try mir.createVirtualRegister(register_class); - - const destination_operand = Operand{ - .id = destination_operand_id, - .u = .{ - .register = destination_register, - }, - .flags = .{ .type = .def }, - }; - - const source_operand = Operand{ - .id = source_operand_id, - .u = .{ - .immediate = integer.value.unsigned, - }, - .flags = .{}, - }; - - const instr = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - source_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, instr); - - return destination_register; - }, - } - } - - fn getAddressingModeFromIr(instruction_selection: *InstructionSelection, mir: *MIR, ir_instruction_index: ir.Instruction.Index) AddressingMode { - const instruction = mir.ir.instructions.get(ir_instruction_index); - switch (instruction.u) { - .stack_slot => { - const frame_index: u32 = @intCast(instruction_selection.stack_map.getIndex(ir_instruction_index).?); - return AddressingMode{ - .base = .{ - .frame_index = frame_index, - }, - }; - }, - else => |t| @panic(@tagName(t)), - } - } - - fn updateValueMap(instruction_selection: *InstructionSelection, allocator: Allocator, ir_instruction_index: ir.Instruction.Index, register: Register, local: bool) !void { - if (local) { - try instruction_selection.local_value_map.putNoClobber(allocator, ir_instruction_index, register); - } else { - const gop = try instruction_selection.value_map.getOrPutValue(allocator, ir_instruction_index, Register.invalid); - if (!gop.value_ptr.isValid()) { - gop.value_ptr.* = register; - } else if (!std.meta.eql(gop.value_ptr.index, register.index)) { - logln(.codegen, .instruction_selection_new_instruction, "Already found register: {}\ngot: {}", .{ gop.value_ptr.index, register.index }); - unreachable; - } - } - } - - fn lowerArguments(instruction_selection: *InstructionSelection, mir: *MIR, ir_function: *ir.FunctionDefinition) !void { - const ir_arguments = ir_function.callable.argument_map.values(); - const calling_convention = calling_conventions.get(ir_function.callable.calling_convention); - - try instruction_selection.local_value_map.ensureUnusedCapacity(mir.allocator, ir_arguments.len); - - var gp_count: u8 = 0; - - for (ir_arguments) |ir_argument_instruction_index| { - const ir_argument_instruction = mir.ir.instructions.get(ir_argument_instruction_index); - const ir_argument = mir.ir.arguments.get(ir_argument_instruction.u.argument); - switch (ir_argument.type) { - .scalar => |scalar_type| switch (scalar_type) { - .i8, .i16, .i32, .i64 => gp_count += 1, - else => unreachable, - }, - else => unreachable, - } - } - - if (gp_count >= 8) { - @panic("Cannot lower arguments"); - } - - var gp_i: u8 = 0; - - for (ir_arguments) |ir_argument_instruction_index| { - const ir_argument_instruction = mir.ir.instructions.get(ir_argument_instruction_index); - const ir_argument = mir.ir.arguments.get(ir_argument_instruction.u.argument); - const value_type = resolveType(ir_argument.type); - const register_class = register_classes.get(value_type); - const argument_registers = calling_convention.argument_registers.get(register_class); - const physical_register = argument_registers[gp_i]; - const operand_id: Operand.Id = switch (register_class) { - inline .gp32, - .gp64, - => |gp| blk: { - gp_i += 1; - break :blk switch (gp) { - .gp32 => .gp32, - .gp64 => .gp64, - else => unreachable, - }; - }, - else => unreachable, - }; - - // const operand_register_class = register_class_operand_matcher.get(operand_reference.id); - - const virtual_register_index = try instruction_selection.createLiveIn(mir, physical_register, register_class); - const result_register = try mir.createVirtualRegister(register_class); - try mir.append(instruction_selection, .copy, &.{ - Operand{ - .id = operand_id, - .u = .{ - .register = result_register, - }, - .flags = .{ - .dead_or_kill = true, - .type = .def, - }, - }, - Operand{ - .id = operand_id, - .u = .{ - .register = .{ - .index = .{ - .virtual = virtual_register_index, - }, - }, - }, - .flags = .{}, - }, - }); - - mir.blocks.get(instruction_selection.current_block).current_stack_index += 1; - - try instruction_selection.updateValueMap(mir.allocator, ir_argument_instruction_index, result_register, true); - try instruction_selection.value_map.putNoClobber(mir.allocator, ir_argument_instruction_index, result_register); - } - } - - fn addLiveIn(instruction_selection: *InstructionSelection, mir: *MIR, register: Register, register_class: Register.Class.Id) !void { - _ = mir; - _ = register_class; - _ = register; - _ = instruction_selection; - unreachable; - } - - fn addExistingLiveIn(instruction_selection: *InstructionSelection, mir: *MIR, physical_register: Register.Physical.Index, virtual_register: Register) !void { - _ = mir; - _ = virtual_register; - _ = physical_register; - _ = instruction_selection; - unreachable; - } - - fn createLiveIn(instruction_selection: *InstructionSelection, mir: *MIR, physical_register: Register.Physical, register_class: Register.Class) !Register.Virtual.Index { - const virtual_register_index = try mir.createVirtualRegisterIndexed(register_class); - try instruction_selection.liveins.putNoClobber(mir.allocator, physical_register, virtual_register_index); - - return virtual_register_index; - } - - fn emitLiveInCopies(instruction_selection: *InstructionSelection, mir: *MIR, entry_block_index: BasicBlock.Index) !void { - const entry_block = mir.blocks.get(entry_block_index); - for (instruction_selection.liveins.keys(), instruction_selection.liveins.values()) |livein_physical_register, livein_virtual_register| { - const vr = mir.virtual_registers.get(livein_virtual_register); - const destination_operand = Operand{ - .id = switch (vr.register_class) { - .gp32 => .gp32, - .gp64 => .gp64, - else => |t| @panic(@tagName(t)), - }, - .u = .{ - .register = .{ - .index = .{ - .virtual = livein_virtual_register, - }, - }, - }, - .flags = .{ - .type = .def, - }, - }; - const source_operand = Operand{ - .id = destination_operand.id, - .u = .{ - .register = .{ - .index = .{ - .physical = livein_physical_register, - }, - }, - }, - .flags = .{}, - }; - - const instruction_index = try mir.buildInstruction(instruction_selection, .copy, &.{ - destination_operand, - source_operand, - }); - - try entry_block.instructions.insert(mir.allocator, 0, instruction_index); - - // TODO: addLiveIn MachineBasicBlock ? unreachable; - } - } - - fn tryToFoldLoad(instruction_selection: *InstructionSelection, mir: *MIR, ir_load_instruction_index: ir.Instruction.Index, ir_current: ir.Instruction.Index) !bool { - _ = ir_current; - // TODO: assert load one use - // TODO: check users - const ir_load_instruction = mir.ir.instructions.get(ir_load_instruction_index); - assert(ir_load_instruction.* == .load); - const ir_load = mir.ir.loads.get(ir_load_instruction.load); - - if (!ir_load.@"volatile") { - const load_register = try instruction_selection.getRegisterForValue(mir, ir_load_instruction_index); - if (load_register.isValid()) { - const register_has_one_use = true; - if (register_has_one_use) { - if (!instruction_selection.registers_with_fixups.contains(load_register.index)) { - // TODO: architecture dependent part - const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_load.instruction); - _ = addressing_mode; - unreachable; - } - } - } - } - - return false; - } -}; - -const Instruction = struct { - id: Id, - operands: ArrayList(Operand.Index), - parent: BasicBlock.Index, - - const Id = enum { - add32rr, - add32rm, - add32mr, - and8ri, - and32rm, - and32mr, - and32rr, - call64pcrel32, - cmp32mi, - cmp32mi8, - copy, - idiv32r, - idiv32m, - imul32mr, - imul32rm, - imul32rr, - jump_pcrel32, - lea64r, - mov32r0, - mov32rm, - mov64rm, - mov32mr, - mov64mr, - mov32ri, - mov32ri64, - mov32rr, - mov32mi, - movsx64rm32, - movsx64rr32, - or32rm, - or32mr, - or32rr, - ret, - seter, - shl32mi, - shl32ri, - shr32mi, - shr32ri, - sub32mr, - sub32rr, - sub32rm, - syscall, - ud2, - xor32mr, - xor32rr, - xor32rm, - }; - - pub const Descriptor = struct { - operands: []const Operand.Reference = &.{}, - opcode: u16, - // format: Format = .pseudo, - flags: Flags = .{}, - implicit_definitions: []const Register.Physical = &.{}, - implicit_uses: []const Register.Physical = &.{}, - - const Flags = packed struct { - implicit_def: bool = false, - two_byte_prefix: bool = false, - }; - - const Format = enum { - pseudo, - no_operands, - add_reg, - mrm_dest_mem, - mrm_source_mem, - mrm_source_reg, - mrm_dest_reg, - }; - }; - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; - - pub const Iterator = struct { - pub const Arguments = packed struct { - use: bool, - def: bool, - element: Iterator.Element, - }; - pub const Element = enum(u1) { - operand = 0, - instruction = 1, - }; - - fn Get(comptime arguments: Arguments) type { - return struct { - index: Operand.Index, - mir: *MIR, - - const I = @This(); - - fn new(mir: *MIR, index: Operand.Index) I { - var it = I{ - .index = index, - .mir = mir, - }; - - if (!index.invalid) { - const operand = mir.operands.get(index); - if ((!arguments.use and operand.flags.type == .use) or (!arguments.def and operand.flags.type == .def)) { - it.advance(); - } - } - - return it; - } - - const ReturnValue = switch (arguments.element) { - .instruction => Instruction, - .operand => Operand, - }; - - fn next(it: *I) ?ReturnValue.Index { - const original_operand_index = it.index; - - switch (it.index.invalid) { - false => switch (arguments.element) { - .instruction => { - const original_operand = it.mir.operands.get(original_operand_index); - const instruction = original_operand.parent; - // const i_desc = it.mir.instructions.get(instruction); - // print("Instruction: {}", .{i_desc.id}); - while (true) { - it.advance(); - - if (it.index.invalid) { - break; - } - - const it_operand = it.mir.operands.get(it.index); - if (!it_operand.parent.eq(instruction)) { - break; - } - } - - return instruction; - }, - .operand => { - it.advance(); - return original_operand_index; - }, - }, - true => return null, - } - } - - fn nextPointer(it: *I) ?*ReturnValue { - if (it.next()) |next_index| { - const result = switch (arguments.element) { - .instruction => it.mir.instructions.get(next_index), - .operand => it.mir.operands.get(next_index), - }; - return result; - } else return null; - } - - fn advance(it: *I) void { - assert(!it.index.invalid); - it.advanceRaw(); - - switch (arguments.use) { - true => { - while (!it.index.invalid) { - const operand = it.mir.operands.get(it.index); - if (!arguments.def and operand.flags.type == .def) { - it.advanceRaw(); - } else { - break; - } - } - }, - false => { - if (!it.index.invalid) { - const operand = it.mir.operands.get(it.index); - if (operand.flags.type == .use) { - it.index = Operand.Index.invalid; - } else { - //TODO: assert that is not debug - } - } - }, - } - } - - fn advanceRaw(it: *I) void { - assert(!it.index.invalid); - const old_index = it.index; - const current_operand = it.mir.operands.get(old_index); - assert(current_operand.u == .register); - const next_index = current_operand.u.register.list.next; - it.index = next_index; - - if (it.index.invalid) { - logln(.codegen, .register_allocation_problematic_hint, "[ITERATOR] O{} -> NULL operand index", .{old_index.uniqueInteger()}); - } else { - const operand = it.mir.operands.get(it.index); - logln(.codegen, .register_allocation_problematic_hint, "[ITERATOR] O{} -> O{}: {}", .{ old_index.uniqueInteger(), it.index.uniqueInteger(), operand.flags }); - } - } - }; - } - }; -}; - -pub const Operand = struct { - id: Operand.Id, - u: union(enum) { - register: Register, - memory: Operand.Memory, - immediate: Operand.Immediate, - pc_relative: PCRelative, - lea64mem: Lea64Mem, - }, - flags: Flags, - parent: Instruction.Index = Instruction.Index.invalid, - - pub const List = BlockList(@This()); - pub const Index = Operand.List.Index; - pub const Allocation = Operand.List.Allocation; - - fn readsRegister(operand: Operand) bool { - return !operand.flags.undef and !operand.flags.internal_read and (operand.flags.type == .use or operand.flags.subreg); - } - - fn isOnRegisterUseList(operand: *const Operand) bool { - assert(operand.u == .register); - return !operand.u.register.list.previous.invalid; - } - - const Id = enum { - unknown, - i32mem, - i64mem, - gp8, - gp32, - gp64, - gp64_nosp, - imm8, - imm16, - imm32, - imm64, - i64i32imm_brtarget, - lea64mem, - ccr, - }; - - pub const Type = enum(u1) { - use = 0, - def = 1, - }; - - const Flags = packed struct { - type: Type = .use, - dead_or_kill: bool = false, - undef: bool = false, - early_clobber: bool = false, - internal_read: bool = false, - subreg: bool = false, - renamable: bool = false, - implicit: bool = false, - - fn isDead(flags: Flags) bool { - return flags.dead_or_kill and flags.type == .def; - } - - fn isKill(flags: Flags) bool { - return flags.dead_or_kill and flags.type != .def; - } - }; - - // fn mapOperandIdToPayloadType(comptime id: Operand.Id) type { - // } - fn mapOperandIdToPayloadName(comptime id: Operand.Id) []const u8 { - return switch (id) { - .unknown => @compileError("unsupported"), - .i32mem, - .i64mem, - => "memory", - .gp32, - .gp64, - .gp64_nosp, - => "register", - .immediate => "immediate", - .i64i32imm_brtarget => "pc_relative", - - .lea64mem => "lea64mem", - }; - } - - fn operandUnionPayloadType(comptime id: Operand.Id) type { - const dumb_union = @field(@as(Operand, undefined), "u"); - return @TypeOf(@field(dumb_union, mapOperandIdToPayloadName(id))); - } - - const Reference = struct { - id: Operand.Id, - kind: Operand.Kind, - }; - - const Kind = enum { - src, - dst, - }; - - const Memory = struct { - addressing_mode: AddressingMode, - global_offset: i32 = 0, - }; - - const PCRelative = struct { - index: u32, - section: u16, - kind: enum { - function, - block, - rodata, - }, - }; - - const Lea64Mem = struct { - gp64: ?Register, // null means RIP, as this register is mandatory - scale: u8, - scale_reg: ?Register, - displacement: PCRelative, - }; - - const Immediate = u64; -}; - -const register_class_operand_matcher = std.EnumArray(Operand.Id, Register.Class).init(.{ - .unknown = .any, - .i64i32imm_brtarget = .not_a_register, - .i32mem = .not_a_register, - .i64mem = .not_a_register, - .gp8 = .gp8, - .gp32 = .gp32, - .gp64 = .gp64, - .gp64_nosp = .gp64_nosp, - .imm8 = .not_a_register, - .imm16 = .not_a_register, - .imm32 = .not_a_register, - .imm64 = .not_a_register, - .lea64mem = .not_a_register, - .ccr = .ccr, -}); - -const instruction_descriptors = std.EnumArray(Instruction.Id, Instruction.Descriptor).init(.{ - .add32rr = .{ - // .format = .mrm_dest_reg, // right? - .opcode = 0x01, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .add32mr = .{ - // .format = .mrm_dest_reg, // right? - .opcode = 0x01, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .dst, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .add32rm = .{ - // .format = .mrm_dest_reg, // right? - .opcode = 0x03, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .and8ri = .{ - // .format = .mrm_dest_reg, // right? - .opcode = 0x21, - .operands = &.{ - .{ - .id = .gp8, - .kind = .dst, - }, - .{ - .id = .gp8, - .kind = .src, - }, - .{ - .id = .imm8, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .and32mr = .{ - // .format = .mrm_dest_reg, // right? - .opcode = 0x21, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .dst, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .and32rr = .{ - // .format = .mrm_dest_reg, // right? - .opcode = 0x21, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .and32rm = .{ - // .format = .mrm_dest_reg, // right? - .opcode = 0x23, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .call64pcrel32 = .{ - // .format = .no_operands, - .opcode = 0xe8, - .operands = &.{ - .{ - .id = .i64i32imm_brtarget, - .kind = .src, - }, - }, - .implicit_definitions = &.{}, - .implicit_uses = &.{}, - }, - .cmp32mi8 = .{ - .opcode = 0x83, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .imm8, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .cmp32mi = .{ - .opcode = 0x81, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .imm32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .copy = .{ - // .format = .pseudo, - .opcode = 0, - .operands = &.{ - .{ - .id = .unknown, - .kind = .dst, - }, - .{ - .id = .unknown, - .kind = .src, - }, - }, - }, - .idiv32r = .{ - .opcode = 0xf7, - .operands = &.{ - .{ - .id = .gp32, - .kind = .src, - }, - }, - }, - .idiv32m = .{ - .opcode = 0xf7, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .src, - }, - }, - .implicit_definitions = &.{ - .eax, - .edx, - .eflags, - }, - .implicit_uses = &.{ - .eax, - .edx, - }, - }, - .imul32mr = .{ - .opcode = 0x6b, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .dst, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .imul32rr = .{ - .opcode = 0x6b, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .imul32rm = .{ - .opcode = 0x6b, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .jump_pcrel32 = .{ - // .format = .mrm_source_mem, - .opcode = 0xe9, - .operands = &.{ - .{ - .id = .i64i32imm_brtarget, - .kind = .src, - }, - }, - }, - .lea64r = .{ - // .format = .mrm_source_mem, - .opcode = 0x8d, - .operands = &.{ - .{ - .id = .gp64, - .kind = .dst, - }, - .{ - .id = .lea64mem, - .kind = .src, - }, - }, - }, - .mov32r0 = .{ - // .format = .pseudo, - .opcode = 0, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - }, - }, - .mov32rm = .{ - // .format = .mrm_source_mem, - .opcode = 0x8b, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - }, - }, - .mov64rm = .{ - // .format = .mrm_source_mem, - .opcode = 0x8b, - .operands = &.{ - .{ - .id = .gp64, - .kind = .dst, - }, - .{ - .id = .i64mem, - .kind = .src, - }, - }, - }, - .mov32rr = .{ - // .format = .mrm_dest_reg, - .opcode = 0x89, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - }, - .mov32mr = .{ - // .format = .mrm_dest_mem, - .opcode = 0x89, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - }, - .mov64mr = .{ - // .format = .mrm_dest_mem, - .opcode = 0x89, - .operands = &.{ - .{ - .id = .i64mem, - .kind = .dst, - }, - .{ - .id = .gp64, - .kind = .src, - }, - }, - }, - .mov32ri = .{ - // .format = .add_reg, - .opcode = 0xb8, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .imm32, - .kind = .src, - }, - }, - }, - .mov32ri64 = .{ - // .format = .pseudo, - .opcode = 0, - .operands = &.{ - .{ - .id = .gp64, - .kind = .dst, - }, - .{ - .id = .imm64, - .kind = .src, - }, - }, - }, - .mov32mi = .{ - .opcode = 0xc7, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .dst, - }, - .{ - .id = .imm32, - .kind = .src, - }, - }, - }, - .movsx64rm32 = .{ - // .format = .mrm_source_mem, - .opcode = 0x63, - .operands = &.{ - .{ - .id = .gp64, - .kind = .dst, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - }, - }, - .movsx64rr32 = .{ - // .format = .mrm_source_reg, - .opcode = 0x63, - .operands = &.{ - .{ - .id = .gp64, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - }, - .or32rm = .{ - // .format = .mrm_dest_reg, - .opcode = 0x0b, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .or32mr = .{ - // .format = .mrm_dest_reg, - .opcode = 0x09, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .dst, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .or32rr = .{ - // .format = .mrm_dest_reg, - .opcode = 0x09, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .ret = .{ - // .format = .no_operands, - .opcode = 0xc3, - .operands = &.{ - .{ - .id = .unknown, - .kind = .src, - }, - }, - }, - .seter = .{ - .opcode = 0xc3, - .operands = &.{ - .{ - .id = .gp8, - .kind = .dst, - }, - }, - .implicit_uses = &.{.eflags}, - }, - .shl32ri = .{ - .opcode = 0xc1, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .imm8, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .shr32ri = .{ - .opcode = 0xc1, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .imm8, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .shl32mi = .{ - .opcode = 0xc1, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .dst, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - .{ - .id = .imm8, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .shr32mi = .{ - .opcode = 0xc1, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .dst, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - .{ - .id = .imm8, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .sub32mr = .{ - // .format = .mrm_dest_reg, // right? - .opcode = 0x29, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .dst, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .sub32rr = .{ - // .format = .mrm_dest_reg, // right? - .opcode = 0x29, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .sub32rm = .{ - // .format = .mrm_dest_reg, // right? - .opcode = 0x2b, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .syscall = .{ - // .format = .no_operands, - .opcode = 0x05, - .operands = &.{}, - .flags = .{ - .two_byte_prefix = true, - }, - }, - .ud2 = .{ - // .format = .no_operands, - .opcode = 0x0b, - .operands = &.{}, - .flags = .{ - .two_byte_prefix = true, - }, - }, - .xor32rm = .{ - // .format = .mrm_dest_reg, - .opcode = 0x33, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .xor32mr = .{ - // .format = .mrm_dest_reg, - .opcode = 0x31, - .operands = &.{ - .{ - .id = .i32mem, - .kind = .dst, - }, - .{ - .id = .i32mem, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, - .xor32rr = .{ - // .format = .mrm_dest_reg, - .opcode = 0x31, - .operands = &.{ - .{ - .id = .gp32, - .kind = .dst, - }, - .{ - .id = .gp32, - .kind = .src, - }, - .{ - .id = .gp32, - .kind = .src, - }, - }, - .implicit_definitions = &.{.eflags}, - }, -}); - -const Size = enum(u2) { - one = 0, - two = 1, - four = 2, - eight = 3, - - fn fromByteCount(byte_count: u8) Size { - return @enumFromInt(@as(u2, @intCast(std.math.log2(byte_count)))); - } - - fn fromBitCount(bit_count: u16) Size { - assert(bit_count % @bitSizeOf(u8) == 0); - const byte_count: u8 = @intCast(bit_count >> 3); - return fromByteCount(byte_count); - } - - fn toInteger(comptime size: Size) type { - return switch (size) { - .one => u8, - .two => u16, - .four => u32, - .eight => u64, - }; - } - - fn fromType(t: ir.Type) Size { - return fromByteCount(@intCast(t.getSize())); - } -}; - -const BasicBlock = struct { - instructions: ArrayList(Instruction.Index) = .{}, - current_stack_index: usize = 0, - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; -}; - -pub const MIR = struct { - allocator: Allocator, - ir: *IR, - target: std.Target, - instructions: BlockList(Instruction) = .{}, - functions: BlockList(Function) = .{}, - blocks: BlockList(BasicBlock) = .{}, - operands: BlockList(Operand) = .{}, - instruction_selections: ArrayList(InstructionSelection) = .{}, - virtual_registers: BlockList(Register.Virtual) = .{}, - function_definition_map: std.AutoHashMapUnmanaged(ir.FunctionDefinition.Index, Function.Index) = .{}, - entry_point: u32 = 0, - - pub fn selectInstructions(allocator: Allocator, intermediate: *IR, target: std.Target) !*MIR { - logln(.codegen, .instruction_selection_block, "\n[INSTRUCTION SELECTION]\n", .{}); - const mir = try allocator.create(MIR); - mir.* = .{ - .allocator = allocator, - .ir = intermediate, - .target = target, - }; - - try mir.blocks.ensureCapacity(allocator, intermediate.basic_blocks.len); - try mir.functions.ensureCapacity(allocator, intermediate.function_definitions.len); - try mir.instruction_selections.ensureUnusedCapacity(allocator, intermediate.function_definitions.len); - - var ir_function_definition_iterator = intermediate.function_definitions.iterator(); - try mir.function_definition_map.ensureTotalCapacity(mir.allocator, @intCast(intermediate.function_definitions.len)); - - var entry_point: ?u32 = null; - while (ir_function_definition_iterator.nextIndex()) |ir_function_index| { - const fn_name = mir.ir.getFunctionName(ir_function_index); - - const instruction_selection = mir.instruction_selections.addOneAssumeCapacity(); - const function_allocation = try mir.functions.addOne(mir.allocator); - const function = function_allocation.ptr; - mir.function_definition_map.putAssumeCapacityNoClobber(ir_function_index, function_allocation.index); - function.* = .{ - .mir = mir, - .instruction_selection = instruction_selection, - .name = fn_name, - }; - instruction_selection.* = .{ - .function = function, - }; - - if (ir_function_index.eq(intermediate.entry_point)) { - entry_point = function_allocation.index.uniqueInteger(); - } - } - - var function_iterator = mir.functions.iterator(); - ir_function_definition_iterator = intermediate.function_definitions.iterator(); - - while (ir_function_definition_iterator.nextIndex()) |ir_function_index| { - const ir_function = intermediate.function_definitions.get(ir_function_index); - const function_index = function_iterator.nextIndex() orelse unreachable; - const function = mir.functions.get(function_index); - logln(.codegen, .instruction_selection_ir_function, "Selecting instructions for {}", .{ir_function}); - const instruction_selection = function.instruction_selection; - - const calling_convention = calling_conventions.get(ir_function.callable.calling_convention); - - const ir_basic_blocks = try ir.findReachableBlocks(.{ - .allocator = allocator, - .ir = mir.ir, - .first = ir_function.entry_block, - .traverse_functions = false, - }); - - try instruction_selection.block_map.ensureUnusedCapacity(allocator, @intCast(ir_basic_blocks.items.len)); - try function.blocks.ensureTotalCapacity(allocator, ir_basic_blocks.items.len); - - for (ir_basic_blocks.items) |ir_block_index| { - const block_allocation = try mir.blocks.append(allocator, .{}); - instruction_selection.block_map.putAssumeCapacity(ir_block_index, block_allocation.index); - function.blocks.appendAssumeCapacity(block_allocation.index); - - const ir_block = mir.ir.basic_blocks.get(ir_block_index); - for (ir_block.instructions.items) |ir_instruction_index| { - const ir_instruction = mir.ir.instructions.get(ir_instruction_index); - - // TODO: take into account exceptions, dynamic allocas? - if (ir_instruction.u == .stack_slot) { - const ir_type = getIrType(mir.ir, ir_instruction_index) orelse unreachable; - const size = ir_type.getSize(); - const alignment = ir_type.getAlignment(); - - assert(size <= 8 and alignment <= 8); - const frame_index = try mir.createStackObject(instruction_selection, size, alignment, ir_instruction_index, false); - try instruction_selection.stack_map.putNoClobber(allocator, ir_instruction_index, frame_index); - } - - // TODO: handle stack references outside blocks - } - } - - instruction_selection.current_block = function.blocks.items[0]; - - try instruction_selection.lowerArguments(mir, ir_function); - - var block_i: usize = function.blocks.items.len; - - while (block_i > 0) { - block_i -= 1; - - const ir_block_index = ir_basic_blocks.items[block_i]; - const ir_block = mir.ir.basic_blocks.get(ir_block_index); - instruction_selection.current_block = instruction_selection.block_map.get(ir_block_index).?; - - var instruction_i: usize = ir_block.instructions.items.len; - - while (instruction_i > 0) { - instruction_i -= 1; - - const ir_instruction_index = ir_block.instructions.items[instruction_i]; - const ir_instruction = mir.ir.instructions.get(ir_instruction_index); - - instruction_selection.local_value_map.clearRetainingCapacity(); - - logln(.codegen, .instruction_selection_new_instruction, "Instruction #{}", .{instruction_i}); - - switch (ir_instruction.u) { - .ret => |ir_ret_index| { - const ir_ret = mir.ir.returns.get(ir_ret_index); - switch (ir_ret.value.invalid) { - 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.value) orelse unreachable); - const source_register = try instruction_selection.getRegisterForValue(mir, ir_ret.value); - - const register_class = register_classes.get(value_type); - - const physical_register = Register{ - .index = .{ - .physical = switch (register_class) { - .gp32 => .eax, - .gp64 => .rax, - else => unreachable, - }, - }, - }; - const operand_id: Operand.Id = switch (register_class) { - .gp32 => .gp32, - .gp64 => .gp64, - else => unreachable, - }; - - const copy = try mir.buildInstruction(instruction_selection, .copy, &.{ - Operand{ - .id = operand_id, - .u = .{ - .register = physical_register, - }, - .flags = .{ - .type = .def, - }, - }, - Operand{ - .id = operand_id, - .u = .{ - .register = source_register, - }, - .flags = .{}, - }, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, copy); - - const ret = try mir.buildInstruction(instruction_selection, .ret, &.{ - Operand{ - .id = operand_id, - .u = .{ - .register = physical_register, - }, - .flags = .{ - .implicit = true, - }, - }, - }); - try instruction_selection.instruction_cache.append(mir.allocator, ret); - }, - } - }, - .constant_integer => {}, - .@"unreachable" => try instruction_selection.instruction_cache.append(mir.allocator, try mir.buildInstruction(instruction_selection, .ud2, &.{})), - .syscall => |ir_syscall_index| { - const ir_syscall = mir.ir.syscalls.get(ir_syscall_index); - const syscall_register_list = calling_convention.syscall_registers[0..ir_syscall.arguments.items.len]; - - for (ir_syscall.arguments.items, syscall_register_list) |ir_argument_index, syscall_register| { - //print("index: {}", .{index}); - const source_register = try instruction_selection.getRegisterForValue(mir, ir_argument_index); - const destination_register = Register{ - .index = .{ - .physical = syscall_register, - }, - }; - - const source_operand = Operand{ - .id = .gp64, - .u = .{ - .register = source_register, - }, - .flags = .{}, - }; - const destination_operand = Operand{ - .id = .gp64, - .u = .{ - .register = destination_register, - }, - .flags = .{ .type = .def }, - }; - - const argument_copy = try mir.buildInstruction(instruction_selection, .copy, &.{ - destination_operand, - source_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, argument_copy); - } - - const produce_syscall_return_value = switch (instruction_i == ir_block.instructions.items.len - 2) { - true => blk: { - const last_block_instruction = mir.ir.instructions.get(ir_block.instructions.items[ir_block.instructions.items.len - 1]); - break :blk switch (last_block_instruction.u) { - .@"unreachable" => false, - .ret => true, - else => |t| @panic(@tagName(t)), - }; - }, - false => true, - }; - - const physical_return_register = Register{ - .index = .{ - .physical = .rax, - }, - }; - - const syscall = try mir.buildInstruction(instruction_selection, .syscall, if (produce_syscall_return_value) &.{ - Operand{ - .id = .gp64, - .u = .{ - .register = physical_return_register, - }, - .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_operand = Operand{ - .id = .gp64, - .u = .{ - .register = virtual_return_register, - }, - .flags = .{ .type = .def }, - }; - - const syscall_result_copy = try mir.buildInstruction(instruction_selection, .copy, &.{ - virtual_return_operand, - Operand{ - .id = .gp64, - .u = .{ - .register = physical_return_register, - }, - .flags = .{}, - }, - }); - try instruction_selection.instruction_cache.append(mir.allocator, syscall_result_copy); - } - }, - .sign_extend => |ir_cast_index| { - const ir_sign_extend = mir.ir.casts.get(ir_cast_index); - const fold_load = blk: { - const source_instruction = mir.ir.instructions.get(ir_sign_extend.value); - const result = switch (source_instruction.u) { - .load => true, - else => false, - }; - break :blk result; - }; - - const destination_type = resolveType(ir_sign_extend.type); - - if (fold_load) { - const ir_load_instruction_index = ir_sign_extend.value; - const ir_load_instruction = mir.ir.instructions.get(ir_sign_extend.value); - const ir_load = mir.ir.loads.get(ir_load_instruction.u.load); - const ir_source = ir_load.value; - const source_type = resolveType(getIrType(mir.ir, ir_source) orelse unreachable); - - if (destination_type != source_type) { - try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_load_instruction_index, {}); - - const instruction_id: Instruction.Id = switch (source_type) { - .i32 => switch (destination_type) { - .i64 => .movsx64rm32, - else => unreachable, - }, - else => |t| @panic(@tagName(t)), - }; - - const instruction_descriptor = instruction_descriptors.getPtrConst(instruction_id); - assert(instruction_descriptor.operands.len == 2); - const destination_operand_index = 0; - const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); - const destination_operand = mir.constrainOperandRegisterClass(instruction_descriptor, destination_register, destination_operand_index, .{ .type = .def }); - - const source_operand = blk: { - const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_source); - const memory_id: Operand.Id = switch (source_type) { - .i32 => .i32mem, - .i64 => .i64mem, - else => |t| @panic(@tagName(t)), - }; - const operand = Operand{ - .id = memory_id, - .u = .{ - .memory = .{ - .addressing_mode = addressing_mode, - }, - }, - .flags = .{}, - }; - break :blk operand; - }; - - const sign_extend = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - source_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, sign_extend); - - try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false); - } else { - unreachable; - } - } else { - // const source_register = try instruction_selection.getRegisterForValue(mir, ir_source_instruction); - // const source_operand = mir.constrainOperandRegisterClass(instruction_descriptor, source_register, source_operand_index, .{}); - unreachable; - } - }, - .load => |ir_load_index| { - if (!instruction_selection.folded_loads.swapRemove(ir_instruction_index)) { - const ir_load = mir.ir.loads.get(ir_load_index); - const ir_source = ir_load.value; - const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_source); - const value_type = resolveType(getIrType(mir.ir, ir_source) orelse unreachable); - - switch (value_type) { - inline .i32, - .i64, - => |vt| { - const instruction_id: Instruction.Id = switch (vt) { - .i32 => .mov32rm, - .i64 => .mov64rm, - else => |t| @panic(@tagName(t)), - }; - const memory_id: Operand.Id = switch (vt) { - .i32 => .i32mem, - .i64 => .i64mem, - else => |t| @panic(@tagName(t)), - }; - - const instruction_descriptor = instruction_descriptors.getPtrConst(instruction_id); - - const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); - const destination_operand_index = 0; - const destination_operand_id = instruction_descriptor.operands[destination_operand_index].id; - const destination_operand = Operand{ - .id = destination_operand_id, - .u = .{ - .register = destination_register, - }, - .flags = .{ .type = .def }, - }; - - const source_operand = Operand{ - .id = memory_id, - .u = .{ - .memory = .{ - .addressing_mode = addressing_mode, - }, - }, - .flags = .{}, - }; - - const load = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - source_operand, - }); - try instruction_selection.instruction_cache.append(mir.allocator, load); - - try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false); - }, - else => |t| @panic(@tagName(t)), - } - } - }, - .store => |ir_store_index| { - const ir_store = mir.ir.stores.get(ir_store_index); - const ir_source_index = ir_store.source; - - const ir_destination = ir_store.destination; - const addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_destination); - const ir_source = mir.ir.instructions.get(ir_source_index); - - const value_type = resolveType(getIrType(mir.ir, ir_source_index) orelse unreachable); - - if (ir_source.u == .constant_integer and value_types.get(value_type).getSize() <= @sizeOf(u32)) { - const instruction_id: Instruction.Id = switch (value_type) { - .i32 => .mov32mi, - else => unreachable, - }; - - const source_immediate = ir_source.u.constant_integer; - const instruction_descriptor = instruction_descriptors.getPtrConst(instruction_id); - - const source_operand_index = 1; - const source_operand_id = instruction_descriptor.operands[source_operand_index].id; - const source_operand = Operand{ - .id = source_operand_id, - .u = .{ - .immediate = source_immediate.value.unsigned, - }, - .flags = .{}, - }; - const destination_operand_id = instruction_descriptor.operands[0].id; - const destination_operand = Operand{ - .id = destination_operand_id, - .u = .{ - .memory = .{ - .addressing_mode = addressing_mode, - }, - }, - .flags = .{}, - }; - const store = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - source_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, store); - } else { - const source_register = try instruction_selection.getRegisterForValue(mir, ir_source_index); - - switch (value_type) { - .i32, .i64 => |vt| { - const instruction_id: Instruction.Id = switch (vt) { - // TODO, non-temporal SSE2 MOVNT - .i32 => .mov32mr, - .i64 => .mov64mr, - else => |t| @panic(@tagName(t)), - }; - - const instruction_descriptor = instruction_descriptors.getPtrConst(instruction_id); - const source_operand_index = 1; - const source_operand_id = instruction_descriptor.operands[source_operand_index].id; - const source_operand = Operand{ - .id = source_operand_id, - .u = .{ - .register = source_register, - }, - .flags = .{}, - }; - - const destination_operand_id = instruction_descriptor.operands[0].id; - const destination_operand = Operand{ - .id = destination_operand_id, - .u = .{ - .memory = .{ - .addressing_mode = addressing_mode, - }, - }, - .flags = .{}, - }; - - const store = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - source_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, store); - }, - else => |t| @panic(@tagName(t)), - } - } - }, - .stack_slot => { - assert(instruction_selection.stack_map.get(ir_instruction_index) != null); - }, - .call => |ir_call_index| { - const ir_call = mir.ir.calls.get(ir_call_index); - var argument_virtual_registers = try ArrayList(Register).initCapacity(mir.allocator, ir_call.arguments.len); - for (ir_call.arguments) |ir_argument_index| { - const register = try instruction_selection.getRegisterForValue(mir, ir_argument_index); - argument_virtual_registers.appendAssumeCapacity(register); - } - - for (argument_virtual_registers.items, 0..) |argument_virtual_register, argument_index| { - const source_register_class = mir.virtual_registers.get(argument_virtual_register.index.virtual).register_class; - const argument_register = calling_convention.argument_registers.get(source_register_class)[argument_index]; - const argument_physical_register = Register{ - .index = .{ - .physical = argument_register, - }, - }; - - const operand_id: Operand.Id = switch (source_register_class) { - .gp32 => .gp32, - .gp64 => .gp64, - else => unreachable, - }; - - const source_operand = Operand{ - .id = operand_id, - .u = .{ - .register = argument_virtual_register, - }, - .flags = .{}, - }; - - const destination_operand = Operand{ - .id = operand_id, - .u = .{ - .register = argument_physical_register, - }, - .flags = .{ - .type = .def, - }, - }; - - const copy = try mir.buildInstruction(instruction_selection, .copy, &.{ - destination_operand, - source_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, copy); - } - - const call = try mir.buildInstruction(instruction_selection, .call64pcrel32, &.{ - Operand{ - .id = .i64i32imm_brtarget, - .u = .{ - .pc_relative = .{ - .index = @bitCast(mir.function_definition_map.get(ir_call.callable.function_definition).?), - .section = @intCast(mir.ir.section_manager.getTextSectionIndex()), - .kind = .function, - }, - }, - .flags = .{}, - }, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, call); - - if (mir.ir.function_definitions.get(ir_call.callable.function_definition).callable.return_type) |ir_return_type| { - const return_type = resolveType(ir_return_type); - switch (return_type) { - inline .i64, .i32 => |rt| { - const register_operand_id = switch (rt) { - .i32 => .gp32, - .i64 => .gp64, - else => unreachable, - }; - const physical_return_register = Register{ - .index = .{ - .physical = switch (rt) { - .i32 => .eax, - .i64 => .rax, - else => unreachable, - }, - }, - }; - - const physical_return_operand = Operand{ - .id = register_operand_id, - .u = .{ - .register = physical_return_register, - }, - .flags = .{}, - }; - - const virtual_return_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); - const virtual_return_operand = Operand{ - .id = register_operand_id, - .u = .{ - .register = virtual_return_register, - }, - .flags = .{ .type = .def }, - }; - - const call_result_copy = try mir.buildInstruction(instruction_selection, .copy, &.{ - virtual_return_operand, - physical_return_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, call_result_copy); - }, - else => |t| @panic(@tagName(t)), - } - } - }, - .constant_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, - .kind = .rodata, - }, - }, - }, - .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); - }, - .binary_operation => |ir_binary_operation_index| { - const ir_binary_operation = mir.ir.binary_operations.get(ir_binary_operation_index); - const value_type = resolveType(ir_binary_operation.type); - - const BinaryOperandKind = enum { - load, - immediate, - rest, - }; - - const left_kind: BinaryOperandKind = switch (mir.ir.instructions.get(ir_binary_operation.left).u) { - .load => .load, - .constant_integer => .immediate, - else => .rest, - }; - const right_kind: BinaryOperandKind = switch (mir.ir.instructions.get(ir_binary_operation.right).u) { - .load => .load, - .constant_integer => .immediate, - else => .rest, - }; - - switch (ir_binary_operation.id) { - .signed_divide => { - const operand_id: Operand.Id = switch (value_type) { - .i32 => .gp32, - else => unreachable, - }; - const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); - - const low_physical_register: Register.Physical = switch (value_type) { - .i32 => .eax, - else => unreachable, - }; - const high_physical_register: Register.Physical = switch (value_type) { - .i32 => .edx, - else => unreachable, - }; - const left_operand = Operand{ - .id = operand_id, - .u = .{ - .register = left_register, - }, - .flags = .{}, - }; - const low_operand = Operand{ - .id = operand_id, - .u = .{ - .register = .{ - .index = .{ - .physical = low_physical_register, - }, - }, - }, - .flags = .{ - .type = .def, - }, - }; - const copy_low = try mir.buildInstruction(instruction_selection, .copy, &.{ - low_operand, - left_operand, - }); - try instruction_selection.instruction_cache.append(mir.allocator, copy_low); - - if (right_kind == .load) { - try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_binary_operation.right, {}); - - const instruction_id: Instruction.Id = switch (value_type) { - .i32 => .idiv32m, - else => |t| @panic(@tagName(t)), - }; - const instruction_descriptor = instruction_descriptors.get(instruction_id); - const right_operand_id = instruction_descriptor.operands[0].id; - const ir_load = mir.ir.loads.get(mir.ir.instructions.get(ir_binary_operation.right).u.load); - const right_operand_addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_load.value); - const right_operand = Operand{ - .id = right_operand_id, - .u = .{ - .memory = .{ - .addressing_mode = right_operand_addressing_mode, - }, - }, - .flags = .{}, - }; - const div = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - right_operand, - }); - try instruction_selection.instruction_cache.append(mir.allocator, div); - } else { - const instruction_id: Instruction.Id = switch (value_type) { - .i32 => .idiv32r, - else => |t| @panic(@tagName(t)), - }; - const instruction_descriptor = instruction_descriptors.get(instruction_id); - _ = instruction_descriptor; - const right_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.right); - _ = high_physical_register; - - const right_operand = Operand{ - .id = operand_id, - .u = .{ - .register = right_register, - }, - .flags = .{}, - }; - - const div = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - right_operand, - }); - try instruction_selection.instruction_cache.append(mir.allocator, div); - } - - const register_class = register_classes.get(value_type); - _ = register_class; - const result_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); - const result_operand = Operand{ - .id = operand_id, - .u = .{ - .register = result_register, - }, - .flags = .{ - .type = .def, - }, - }; - const division_result_register = Register{ - .index = .{ - .physical = switch (value_type) { - .i32 => .eax, - else => unreachable, - }, - }, - }; - - const division_result_operand = Operand{ - .id = operand_id, - .u = .{ - .register = division_result_register, - }, - .flags = .{}, - }; - const result_copy = try mir.buildInstruction(instruction_selection, .copy, &.{ - result_operand, - division_result_operand, - }); - try instruction_selection.instruction_cache.append(mir.allocator, result_copy); - - try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, result_register, false); - }, - else => { - - // TODO: optimize if the result is going to be stored? - // for (ir_instruction.use_list.items) |use_index| { - // const use = mir.ir.instructions.get(use_index); - // std.debug.print("Use: {s}\n", .{@tagName(use.u)}); - // } - switch (left_kind) { - .load => switch (right_kind) { - .load => { - // If both operands come from memory (both operands are loads), load the left one into a register and operate from the stack with the right one, when possible - const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); - const instruction_id: Instruction.Id = switch (ir_binary_operation.id) { - .add => switch (value_type) { - .i32 => .add32rm, - else => unreachable, - }, - .sub => switch (value_type) { - .i32 => .sub32rm, - else => unreachable, - }, - .logical_and => switch (value_type) { - .i32 => .and32rm, - else => unreachable, - }, - .logical_xor => switch (value_type) { - .i32 => .xor32rm, - else => unreachable, - }, - .logical_or => switch (value_type) { - .i32 => .or32rm, - else => unreachable, - }, - .signed_multiply => switch (value_type) { - .i32 => .imul32rm, - else => unreachable, - }, - .signed_divide => unreachable, - .shift_left => unreachable, - .shift_right => unreachable, - .integer_compare_equal => unreachable, - }; - - try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_binary_operation.right, {}); - - const instruction_descriptor = instruction_descriptors.get(instruction_id); - const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); - const destination_operand_id = instruction_descriptor.operands[0].id; - const left_operand_id = instruction_descriptor.operands[1].id; - const right_operand_id = instruction_descriptor.operands[2].id; - const ir_load = mir.ir.loads.get(mir.ir.instructions.get(ir_binary_operation.right).u.load); - const right_operand_addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_load.value); - - const destination_operand = Operand{ - .id = destination_operand_id, - .u = .{ - .register = destination_register, - }, - .flags = .{ - .type = .def, - }, - }; - - const left_operand = Operand{ - .id = left_operand_id, - .u = .{ - .register = left_register, - }, - .flags = .{}, - }; - - const right_operand = Operand{ - .id = right_operand_id, - .u = .{ - .memory = .{ .addressing_mode = right_operand_addressing_mode }, - }, - .flags = .{}, - }; - - const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - left_operand, - right_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, binary_op_instruction); - - try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false); - }, - .immediate => { - switch (ir_binary_operation.id) { - .shift_left, .shift_right => { - const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); - const instruction_id: Instruction.Id = switch (ir_binary_operation.id) { - .shift_left => .shl32ri, - .shift_right => .shr32ri, - else => |t| @panic(@tagName(t)), - }; - const instruction_descriptor = instruction_descriptors.get(instruction_id); - // const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); - const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); - const destination_operand_id = instruction_descriptor.operands[0].id; - const left_operand_id = instruction_descriptor.operands[1].id; - - const destination_operand = Operand{ - .id = destination_operand_id, - .u = .{ - .register = destination_register, - }, - .flags = .{ - .type = .def, - }, - }; - - const left_operand = Operand{ - .id = left_operand_id, - .u = .{ - .register = left_register, - }, - .flags = .{}, - }; - - const right_immediate = mir.ir.instructions.get(ir_binary_operation.right).u.constant_integer; - const right_value_type: ValueType.Id = switch (right_immediate.type) { - .i8 => .i8, - else => unreachable, - }; - _ = right_value_type; - const right_operand = Operand{ - .id = .imm8, - .u = .{ - .immediate = right_immediate.value.unsigned, - }, - .flags = .{}, - }; - - const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - left_operand, - right_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, binary_op_instruction); - - try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false); - }, - .integer_compare_equal => { - const result = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); - const right_immediate = mir.ir.instructions.get(ir_binary_operation.right).u.constant_integer; - const instruction_id: Instruction.Id = switch (ir_binary_operation.id) { - .integer_compare_equal => blk: { - const instruction_id: Instruction.Id = switch (right_immediate.type) { - .i32 => if (std.math.cast(i8, right_immediate.value.signed)) |_| .cmp32mi8 else .cmp32mi, - else => |t| @panic(@tagName(t)), - }; - break :blk instruction_id; - }, - else => unreachable, - }; - try instruction_selection.folded_loads.putNoClobber(mir.allocator, ir_binary_operation.left, {}); - // - const instruction_descriptor = instruction_descriptors.get(instruction_id); - // const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); - // const destination_operand_id = instruction_descriptor.operands[0].id; - const right_operand_id = instruction_descriptor.operands[1].id; - const left_operand_id = instruction_descriptor.operands[0].id; - const ir_load = mir.ir.loads.get(mir.ir.instructions.get(ir_binary_operation.left).u.load); - const left_operand_addressing_mode = instruction_selection.getAddressingModeFromIr(mir, ir_load.value); - const left_operand = Operand{ - .id = left_operand_id, - .u = .{ - .memory = .{ .addressing_mode = left_operand_addressing_mode }, - }, - .flags = .{}, - }; - const right_operand = Operand{ - .id = right_operand_id, - .u = .{ - .immediate = right_immediate.value.unsigned, - }, - .flags = .{}, - }; - const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - left_operand, - right_operand, - }); - try instruction_selection.instruction_cache.append(mir.allocator, binary_op_instruction); - - const set_instruction: Instruction.Id = switch (ir_binary_operation.id) { - .integer_compare_equal => .seter, - else => unreachable, - }; - const flag1_register = try mir.createVirtualRegister(.gp8); - const flag1_operand = Operand{ - .id = .gp8, - .u = .{ - .register = flag1_register, - }, - .flags = .{ - .type = .def, - }, - }; - const setcc = try mir.buildInstruction(instruction_selection, set_instruction, &.{ - flag1_operand, - }); - try instruction_selection.instruction_cache.append(mir.allocator, setcc); - // TODO: parity? - - const select_instruction: Instruction.Id = switch (ir_binary_operation.id) { - .integer_compare_equal => .and8ri, - else => unreachable, - }; - - const result_operand = Operand{ - .id = .gp8, - .u = .{ - .register = result, - }, - .flags = .{ - .type = .def, - }, - }; - const flag1_src_operand = Operand{ - .id = flag1_operand.id, - .u = flag1_operand.u, - .flags = .{}, - }; - const select = try mir.buildInstruction(instruction_selection, select_instruction, &.{ - result_operand, - flag1_src_operand, - Operand{ - .id = .imm8, - .u = .{ - .immediate = 0x01, - }, - .flags = .{}, - }, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, select); - try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, result, false); - }, - else => unreachable, - } - }, - .rest => { - const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); - const instruction_id: Instruction.Id = switch (ir_binary_operation.id) { - .add => switch (value_type) { - .i32 => .add32rr, - else => unreachable, - }, - .sub => switch (value_type) { - .i32 => .sub32rr, - else => unreachable, - }, - .logical_and => switch (value_type) { - .i32 => .and32rr, - else => unreachable, - }, - .logical_xor => switch (value_type) { - .i32 => .xor32rr, - else => unreachable, - }, - .logical_or => switch (value_type) { - .i32 => .or32rr, - else => unreachable, - }, - .signed_multiply => switch (value_type) { - .i32 => .imul32rr, - else => unreachable, - }, - .signed_divide => unreachable, - .shift_left => unreachable, - .shift_right => unreachable, - .integer_compare_equal => unreachable, - }; - - const instruction_descriptor = instruction_descriptors.get(instruction_id); - const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); - const right_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.right); - const destination_operand_id = instruction_descriptor.operands[0].id; - const left_operand_id = instruction_descriptor.operands[1].id; - - const right_operand_id = instruction_descriptor.operands[2].id; - - const destination_operand = Operand{ - .id = destination_operand_id, - .u = .{ - .register = destination_register, - }, - .flags = .{ - .type = .def, - }, - }; - - const left_operand = Operand{ - .id = left_operand_id, - .u = .{ - .register = left_register, - }, - .flags = .{}, - }; - - const right_operand = Operand{ - .id = right_operand_id, - .u = .{ - .register = right_register, - }, - .flags = .{}, - }; - - const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - left_operand, - right_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, binary_op_instruction); - - try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false); - }, - }, - .rest => switch (right_kind) { - .load => unreachable, - .immediate => unreachable, - .rest => { - const destination_register = try instruction_selection.getRegisterForValue(mir, ir_instruction_index); - const instruction_id: Instruction.Id = switch (ir_binary_operation.id) { - .add => switch (value_type) { - .i32 => .add32rr, - else => unreachable, - }, - .sub => switch (value_type) { - .i32 => .sub32rr, - else => unreachable, - }, - .logical_and => switch (value_type) { - .i32 => .and32rr, - else => unreachable, - }, - .logical_xor => switch (value_type) { - .i32 => .xor32rr, - else => unreachable, - }, - .logical_or => switch (value_type) { - .i32 => .or32rr, - else => unreachable, - }, - .signed_multiply => switch (value_type) { - .i32 => .imul32rr, - else => unreachable, - }, - .signed_divide => unreachable, - .shift_left => unreachable, - .shift_right => unreachable, - .integer_compare_equal => unreachable, - }; - - const instruction_descriptor = instruction_descriptors.get(instruction_id); - const left_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.left); - const right_register = try instruction_selection.getRegisterForValue(mir, ir_binary_operation.right); - const destination_operand_id = instruction_descriptor.operands[0].id; - const left_operand_id = instruction_descriptor.operands[1].id; - - const right_operand_id = instruction_descriptor.operands[2].id; - - const destination_operand = Operand{ - .id = destination_operand_id, - .u = .{ - .register = destination_register, - }, - .flags = .{ - .type = .def, - }, - }; - - const left_operand = Operand{ - .id = left_operand_id, - .u = .{ - .register = left_register, - }, - .flags = .{}, - }; - - const right_operand = Operand{ - .id = right_operand_id, - .u = .{ - .register = right_register, - }, - .flags = .{}, - }; - - const binary_op_instruction = try mir.buildInstruction(instruction_selection, instruction_id, &.{ - destination_operand, - left_operand, - right_operand, - }); - - try instruction_selection.instruction_cache.append(mir.allocator, binary_op_instruction); - - try instruction_selection.updateValueMap(mir.allocator, ir_instruction_index, destination_register, false); - }, - }, - .immediate => switch (right_kind) { - .load => unreachable, - .immediate => switch (ir_binary_operation.id) { - else => |t| @panic(@tagName(t)), - }, - .rest => unreachable, - }, - } - }, - } - }, - .phi => |ir_phi_index| { - const ir_phi = mir.ir.phis.get(ir_phi_index); - - // TODO: check if we should something else here - const virtual_register = instruction_selection.value_map.get(ir_instruction_index).?; - for (ir_phi.operands.items) |phi_operand| { - try instruction_selection.updateValueMap(mir.allocator, phi_operand.value, virtual_register, false); - } - }, - .jump => |ir_jump_index| { - const ir_jump = mir.ir.jumps.get(ir_jump_index); - const ir_target_block = ir_jump.target; - const target_block = instruction_selection.block_map.get(ir_target_block).?; - - const jump = try mir.buildInstruction(instruction_selection, .jump_pcrel32, &.{ - Operand{ - .id = .i64i32imm_brtarget, - .u = .{ - .pc_relative = .{ - .index = @bitCast(target_block), - .section = @intCast(mir.ir.section_manager.getTextSectionIndex()), - .kind = .block, - }, - }, - .flags = .{}, - }, - }); - try instruction_selection.instruction_cache.append(mir.allocator, jump); - }, - else => |t| @panic(@tagName(t)), - } - - var i: usize = instruction_selection.instruction_cache.items.len; - const block = mir.blocks.get(instruction_selection.current_block); - - while (i > 0) { - i -= 1; - - const instruction_index = instruction_selection.instruction_cache.items[i]; - const instruction = mir.instructions.get(instruction_index); - logln(.codegen, .instruction_selection_cache_flush, "Inserting instruction #{} ({s}) into index {} (instruction count: {})", .{ instruction_index.uniqueInteger(), @tagName(instruction.id), block.current_stack_index, block.instructions.items.len }); - try block.instructions.insert(mir.allocator, block.current_stack_index, instruction_index); - } - - instruction_selection.instruction_cache.clearRetainingCapacity(); - } - } - - try instruction_selection.emitLiveInCopies(mir, function.blocks.items[0]); - - logln(.codegen, .instruction_selection_ir_function, "Selected instructions for {}", .{function}); - } - - mir.entry_point = entry_point orelse unreachable; - - return mir; - } - - fn getNextInstructionIndex(mir: *MIR, instruction_index: Instruction.Index) usize { - const instruction = mir.instructions.get(instruction_index); - const parent_block = mir.blocks.get(instruction.parent); - const next = for (parent_block.instructions.items, 0..) |index, i| { - if (index.eq(instruction_index)) break i + 1; - } else unreachable; - return next; - } - - fn setPhysicalRegister(mir: *MIR, instruction_selection: *InstructionSelection, operand_index: Operand.Index, register: Register.Physical) bool { - const operand = mir.operands.get(operand_index); - if (!operand.flags.subreg) { - mir.setRegisterInOperand(instruction_selection, operand_index, .{ - .physical = register, - }); - operand.flags.renamable = true; - return false; - } - - unreachable; - } - - fn setRegisterInOperand(mir: *MIR, instruction_selection: *InstructionSelection, operand_index: Operand.Index, register: Register.Index) void { - const operand = mir.operands.get(operand_index); - assert(operand.u == .register); - assert(!std.meta.eql(operand.u.register.index, register)); - operand.flags.renamable = false; - mir.removeRegisterOperandFromUseList(instruction_selection, operand); - operand.u.register.index = register; - mir.addRegisterOperandFromUseList(instruction_selection, operand_index); - } - - fn addRegisterOperandFromUseList(mir: *MIR, instruction_selection: *InstructionSelection, operand_index: Operand.Index) void { - const operand = mir.operands.get(operand_index); - assert(!operand.isOnRegisterUseList()); - const head_index_ptr = mir.getRegisterListHead(instruction_selection, operand.u.register); - const head_index = head_index_ptr.*; - - logln(.codegen, .instruction_selection_register_operand_list, "Adding register list metadata to operand #{}", .{operand_index.uniqueInteger()}); - - switch (head_index.invalid) { - false => { - const head_operand = mir.operands.get(head_index); - assert(std.meta.eql(head_operand.u.register.index, operand.u.register.index)); - - const last_operand_index = head_operand.u.register.list.previous; - const last_operand = mir.operands.get(last_operand_index); - assert(std.meta.eql(last_operand.u.register.index, operand.u.register.index)); - - head_operand.u.register.list.previous = operand_index; - operand.u.register.list.previous = last_operand_index; - - switch (operand.flags.type) { - .def => { - operand.u.register.list.next = head_index; - head_index_ptr.* = operand_index; - }, - .use => { - operand.u.register.list.next = Operand.Index.invalid; - last_operand.u.register.list.next = operand_index; - }, - } - }, - true => { - logln(.codegen, .instruction_selection_register_operand_list, "List is empty, adding it to the top of the list", .{}); - - operand.u.register.list.previous = operand_index; - operand.u.register.list.next = Operand.Index.invalid; - head_index_ptr.* = operand_index; - }, - } - } - - fn removeRegisterOperandFromUseList(mir: *MIR, instruction_selection: *InstructionSelection, operand: *Operand) void { - assert(operand.isOnRegisterUseList()); - const head_index_ptr = mir.getRegisterListHead(instruction_selection, operand.u.register); - const head_index = head_index_ptr.*; - assert(!head_index.invalid); - - const operand_previous = operand.u.register.list.previous; - const operand_next = operand.u.register.list.next; - - const head = mir.operands.get(head_index); - if (operand == head) { - head_index_ptr.* = operand_next; - } else { - const previous = mir.operands.get(operand_previous); - previous.u.register.list.next = operand_next; - } - - const next = switch (operand_next.invalid) { - false => mir.operands.get(operand_next), - true => head, - }; - - next.u.register.list.previous = operand_previous; - - operand.u.register.list.previous = Operand.Index.invalid; - operand.u.register.list.next = Operand.Index.invalid; - } - - fn constrainRegisterClass(mir: *MIR, register: Register, old_register_class: Register.Class) ?Register.Class { - const new_register_class = switch (register.index) { - .virtual => |virtual_register_index| mir.virtual_registers.get(virtual_register_index).register_class, - else => unreachable, - }; - - // print("Old: {}. New: {}", .{ old_register_class, new_register_class }); - switch (old_register_class == new_register_class) { - true => return new_register_class, - false => unreachable, - } - unreachable; - } - - fn constrainOperandRegisterClass(mir: *MIR, instruction_descriptor: *const Instruction.Descriptor, register: Register, operand_index: usize, flags: Operand.Flags) Operand { - assert(register.index == .virtual); - const operand_reference = instruction_descriptor.operands[operand_index]; - const operand_register_class = register_class_operand_matcher.get(operand_reference.id); - // print("Constraint operand #{} with {} (out of {})", .{ operand_index, operand_register_class, operand_reference.id }); - - // const register_class = op - if (mir.constrainRegisterClass(register, operand_register_class) == null) { - unreachable; - } - - return Operand{ - .id = operand_reference.id, - .u = .{ - .register = register, - }, - .flags = flags, - }; - } - - fn createVirtualRegister(mir: *MIR, register_class: Register.Class) !Register { - const virtual_register_index = try mir.createVirtualRegisterIndexed(register_class); - return Register{ - .index = .{ - .virtual = virtual_register_index, - }, - }; - } - - fn createVirtualRegisterIndexed(mir: *MIR, register_class: Register.Class) !Register.Virtual.Index { - const allocation = try mir.virtual_registers.append(mir.allocator, .{ - .register_class = register_class, - }); - return allocation.index; - } - - const RegisterBitset = std.EnumSet(Register.Physical); - - const RegisterAllocator = struct { - reserved: RegisterBitset = RegisterBitset.initEmpty(), - register_states: std.EnumArray(Register.Physical, Register.State) = std.EnumArray(Register.Physical, Register.State).initFill(.free), - used_in_instruction: RegisterBitset = RegisterBitset.initEmpty(), - may_live_across_blocks: std.DynamicBitSetUnmanaged, - live_virtual_registers: std.AutoArrayHashMapUnmanaged(Register.Virtual.Index, LiveRegister) = .{}, - stack_slots: std.AutoHashMapUnmanaged(Register.Virtual.Index, u32) = .{}, - coalesced: ArrayList(Instruction.Index) = .{}, - - fn init(mir: *MIR, instruction_selection: *InstructionSelection) !RegisterAllocator { - var result = RegisterAllocator{ - .may_live_across_blocks = try std.DynamicBitSetUnmanaged.initEmpty(mir.allocator, mir.virtual_registers.len), - }; - - result.reserved.setPresent(.fpcw, true); - result.reserved.setPresent(.fpsw, true); - result.reserved.setPresent(.mxcsr, true); - - for ((try getSubregisters(mir.allocator, .rsp)).keys()) |rsp_subreg| { - result.reserved.setPresent(rsp_subreg, true); - } - - result.reserved.setPresent(.ssp, true); - - for ((try getSubregisters(mir.allocator, .rip)).keys()) |rip_subreg| { - result.reserved.setPresent(rip_subreg, true); - } - - // TODO: complete - const has_frame_pointer = instruction_selection.stack_map.entries.len > 0; - if (has_frame_pointer) { - for ((try getSubregisters(mir.allocator, .rbp)).keys()) |rbp_subreg| { - result.reserved.setPresent(rbp_subreg, true); - } - } - - // TODO: complete - const has_base_pointer = false; - if (has_base_pointer) { - // TODO - } - - result.reserved.setPresent(.cs, true); - result.reserved.setPresent(.ss, true); - result.reserved.setPresent(.ds, true); - result.reserved.setPresent(.es, true); - result.reserved.setPresent(.fs, true); - result.reserved.setPresent(.gs, true); - - inline for ([8]Register.Physical{ .st0, .st1, .st2, .st3, .st4, .st5, .st6, .st7 }) |st_reg| { - result.reserved.setPresent(st_reg, true); - } - - const has_avx512 = false; - if (!has_avx512) { - // TODO xmm alias - } - - // TODO: callee saved registers (CSR) - - // TODO: more setup - - return result; - } - - fn useVirtualRegister(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, instruction_index: Instruction.Index, virtual_register: Register.Virtual.Index, instruction_operand_index: u8) !bool { - const instruction = mir.instructions.get(instruction_index); - const operand_index = instruction.operands.items[instruction_operand_index]; - const operand = mir.operands.get(operand_index); - const gop = try register_allocator.live_virtual_registers.getOrPut(mir.allocator, virtual_register); - const live_register = gop.value_ptr; - switch (gop.found_existing) { - true => { - // TODO: asserts - const assert_result = !operand.flags.isKill() or live_register.last_use.eq(instruction_index); - if (assert_result) { - // logln("Existing live register at instruction #{}: {}", .{ instruction_index.uniqueInteger(), live_register }); - // logln("Function until now: {}", .{instruction_selection.function}); - assert(assert_result); - } - }, - false => { - if (!operand.flags.isKill()) { - // TODO some logic - // unreachable; - if (register_allocator.mayLiveOut(mir, instruction_selection, virtual_register)) { - unreachable; - } else { - operand.flags.dead_or_kill = true; - } - } - - live_register.* = .{ - .virtual = virtual_register, - }; - }, - } - - if (live_register.physical == .no_register) { - const hint: ?Register = blk: { - if (instruction.id == .copy) { - const source_operand = mir.operands.get(instruction.operands.items[1]); - assert(source_operand.u == .register); - if (!source_operand.flags.subreg) { - const destination_operand = mir.operands.get(instruction.operands.items[0]); - const hint_register = destination_operand.u.register; - assert(hint_register.index == .physical); - break :blk hint_register; - } - } - break :blk null; - }; - // TODO: handle allocation error here - register_allocator.allocateVirtualRegister(mir, instruction_selection, instruction_index, live_register, hint, false) catch unreachable; - } - - live_register.last_use = instruction_index; - - register_allocator.setRegisterUsedInInstruction(live_register.physical, true); - return mir.setPhysicalRegister(instruction_selection, operand_index, live_register.physical); - } - - fn isRegisterInClass(register: Register.Physical, register_class: Register.Class) bool { - const result = std.mem.indexOfScalar(Register.Physical, registers_by_class.get(register_class), register) != null; - return result; - } - - fn allocateVirtualRegister(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, instruction_index: Instruction.Index, live_register: *LiveRegister, maybe_hint: ?Register, look_at_physical_register_uses: bool) !void { - assert(live_register.physical == .no_register); - const virtual_register = live_register.virtual; - const register_class = mir.virtual_registers.get(live_register.virtual).register_class; - - logln(.codegen, .register_allocation_problematic_hint, "Hint 1: {?}", .{maybe_hint}); - if (maybe_hint) |hint_register| { - logln(.codegen, .register_allocation_problematic_hint, "Hint register 1: {s}", .{@tagName(hint_register.index.physical)}); - if (hint_register.index == .physical) { - const hint_physical_register = hint_register.index.physical; - if (isRegisterInClass(hint_physical_register, register_class)) { - logln(.codegen, .register_allocation_problematic_hint, "Hint register 1 {s} is in register class {s}", .{ @tagName(hint_physical_register), @tagName(register_class) }); - const is_used_in_instruction = register_allocator.isRegisterUsedInInstruction(hint_physical_register, look_at_physical_register_uses); - logln(.codegen, .register_allocation_problematic_hint, "Hint register 1 {s} is {s} in instruction", .{ @tagName(hint_physical_register), if (is_used_in_instruction) "used" else "unused" }); - if (!is_used_in_instruction) { - logln(.codegen, .register_allocation_problematic_hint, "Register {s} used in instruction: false", .{@tagName(hint_physical_register)}); - if (register_allocator.register_states.get(hint_physical_register) == .free) { - register_allocator.assignVirtualToPhysicalRegister(live_register, hint_physical_register); - return; - } - } - } - } - // TODO : and isAllocatable - } - - logln(.codegen, .register_allocation_problematic_hint, "Tracing copies for VR{} in instruction #{}", .{ virtual_register.uniqueInteger(), instruction_index.uniqueInteger() }); - - const maybe_hint2 = register_allocator.traceCopies(mir, instruction_selection, virtual_register); - logln(.codegen, .register_allocation_problematic_hint, "Hint 2: {?}", .{maybe_hint2}); - if (maybe_hint2) |hint| { - // TODO - const allocatable = true; - logln(.codegen, .register_allocation_problematic_hint, "Hint register 2: {}. Register class: {s}", .{ hint, @tagName(register_class) }); - - if (hint == .physical and allocatable and isRegisterInClass(hint.physical, register_class) and !register_allocator.isRegisterUsedInInstruction(hint.physical, look_at_physical_register_uses)) { - const physical_register = hint.physical; - - if (register_allocator.register_states.get(physical_register) == .free) { - register_allocator.assignVirtualToPhysicalRegister(live_register, physical_register); - return; - } else { - logln(.codegen, .register_allocation_problematic_hint, "Second hint {s} not free", .{@tagName(physical_register)}); - } - } else { - unreachable; - } - } else { - logln(.codegen, .register_allocation_problematic_hint, "Can't take hint for VR{} for instruction #{}", .{ virtual_register.uniqueInteger(), instruction_index.uniqueInteger() }); - } - - const register_class_members = registers_by_class.get(register_class); - assert(register_class_members.len > 0); - - var best_cost: u32 = SpillCost.impossible; - var best_register = Register.Physical.no_register; - // print("Candidates for {s}: ", .{@tagName(register_class)}); - // for (register_class_members) |candidate_register| { - // print("{s}, ", .{@tagName(candidate_register)}); - // } - // print("", .{}); - for (register_class_members) |candidate_register| { - if (!register_allocator.isRegisterUsedInInstruction(candidate_register, look_at_physical_register_uses)) { - const spill_cost = register_allocator.computeSpillCost(candidate_register); - logln(.codegen, .register_allocation_problematic_hint, "Candidate: {s}. Spill cost: {}", .{ @tagName(candidate_register), spill_cost }); - - if (spill_cost == 0) { - register_allocator.assignVirtualToPhysicalRegister(live_register, candidate_register); - return; - } - - if (maybe_hint) |hint| { - if (hint.index.physical == candidate_register) { - unreachable; - } - } - - if (maybe_hint2) |hint| { - if (hint.physical == candidate_register) { - unreachable; - } - } - - if (spill_cost < best_cost) { - best_register = candidate_register; - best_cost = spill_cost; - } - } - } - - assert(best_register != .no_register); - - unreachable; - } - - fn computeSpillCost(register_allocator: *RegisterAllocator, physical_register: Register.Physical) u32 { - const register_state = register_allocator.register_states.get(physical_register); - return switch (register_state) { - .free => 0, - .preassigned => SpillCost.impossible, - .virtual => |virtual_register_index| blk: { - const stack_slot = register_allocator.stack_slots.get(virtual_register_index) != null; - const live_out = register_allocator.live_virtual_registers.get(virtual_register_index).?.live_out; - logln(.codegen, .register_allocation_problematic_hint, "Register {s} has stack slot: {}. Live out: {}", .{ @tagName(physical_register), stack_slot, live_out }); - const sure_spill = stack_slot or live_out; - break :blk if (sure_spill) SpillCost.clean else SpillCost.dirty; - }, - .livein => unreachable, - }; - } - - const SpillCost = struct { - const clean = 50; - const dirty = 100; - const pref_bonus = 20; - const impossible = std.math.maxInt(u32); - }; - - fn isRegisterUsedInInstruction(register_allocator: *RegisterAllocator, physical_register: Register.Physical, look_at_physical_register_uses: bool) bool { - _ = look_at_physical_register_uses; - - // TODO: register masks - // if (register_allocator.used_in_instruction.contains(physical_register)) { - // return true; - // } - // // TODO - // else { - // return false; - // } - - const result = register_allocator.used_in_instruction.contains(physical_register); - return result; - } - - fn traceCopyChain(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, given_register: Register) ?Register.Index { - _ = register_allocator; - const chain_length_limit = 3; - var chain_try_count: u32 = 0; - - var register = given_register; - - while (true) { - switch (register.index) { - .physical => return register.index, - .virtual => |vri| { - logln(.codegen, .register_allocation_problematic_hint, "[traceCopyChain] Operand: VR{}", .{vri.uniqueInteger()}); - - const virtual_head_index_ptr = mir.getRegisterListHead(instruction_selection, .{ - .index = .{ - .virtual = vri, - }, - }); - - logln(.codegen, .register_allocation_problematic_hint, "[traceCopyChain] Head operand for VR{}: O{}", .{ vri.uniqueInteger(), virtual_head_index_ptr.uniqueInteger() }); - - var vdef = Instruction.Iterator.Get(.{ - .use = false, - .def = true, - .element = .instruction, - }).new(mir, virtual_head_index_ptr.*); - - const operand_index = vdef.index; - - const vdef_instruction = vdef.next() orelse break; - logln(.codegen, .register_allocation_problematic_hint, "[traceCopyChain] VR{} defined in operand #{} of instruction #{}", .{ vri.uniqueInteger(), operand_index.uniqueInteger(), vdef_instruction.uniqueInteger() }); - - const next_operand = vdef.index; - - if (vdef.next()) |unexpected_next_instruction| { - logln(.codegen, .register_allocation_problematic_hint, "[traceCopyChain] VR{} also defined in operand #{} unexpected next instruction #{}. Breaking...", .{ vri.uniqueInteger(), next_operand.uniqueInteger(), unexpected_next_instruction.uniqueInteger() }); - break; - } - - const instruction = mir.instructions.get(vdef_instruction); - switch (instruction.id) { - .copy => { - const copy_source_operand_index = instruction.operands.items[1]; - const copy_source_operand = mir.operands.get(copy_source_operand_index); - register = copy_source_operand.u.register; - }, - else => |t| @panic(@tagName(t)), - } - }, - } - - chain_try_count += 1; - if (chain_try_count >= chain_length_limit) break; - } - - return null; - } - - fn traceCopies(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, virtual_register_index: Register.Virtual.Index) ?Register.Index { - const head_index_ptr = mir.getRegisterListHead(instruction_selection, .{ - .index = .{ - .virtual = virtual_register_index, - }, - }); - - logln(.codegen, .register_allocation_problematic_hint, "[traceCopies] Tracing copies for VR{}. Head operand: #{}", .{ virtual_register_index.uniqueInteger(), head_index_ptr.uniqueInteger() }); - - var define_instructions = Instruction.Iterator.Get(.{ - .use = false, - .def = true, - .element = .instruction, - }).new(mir, head_index_ptr.*); - - if (!define_instructions.index.invalid) { - logln(.codegen, .register_allocation_problematic_hint, "[traceCopies] Next operand before loop: #{}", .{define_instructions.index.uniqueInteger()}); - } - - const definition_limit = 3; - var try_count: u32 = 0; - while (define_instructions.next()) |instruction_index| { - logln(.codegen, .register_allocation_problematic_hint, "[traceCopies] Current instruction: #{}", .{instruction_index.uniqueInteger()}); - if (!define_instructions.index.invalid) { - logln(.codegen, .register_allocation_problematic_hint, "[traceCopies] Next operand: #{}", .{define_instructions.index.uniqueInteger()}); - } else { - // logln(.codegen, .register_allocation_problematic_hint, "[traceCopies] Next operand: invalid", .{}); - } - - const instruction = mir.instructions.get(instruction_index); - switch (instruction.id) { - .copy => { - const operand_index = instruction.operands.items[1]; - const operand = mir.operands.get(operand_index); - - if (register_allocator.traceCopyChain(mir, instruction_selection, operand.u.register)) |register| { - return register; - } - - logln(.codegen, .register_allocation_problematic_hint, "[traceCopies] Missed oportunity for register allocation tracing copy chain for VR{}", .{virtual_register_index.uniqueInteger()}); - }, - else => {}, - } - - try_count += 1; - if (try_count >= definition_limit) break; - } - - return null; - } - - fn assignVirtualToPhysicalRegister(register_allocator: *RegisterAllocator, live_register: *LiveRegister, register: Register.Physical) void { - const virtual_register = live_register.virtual; - assert(live_register.physical == .no_register); - assert(register != .no_register); - live_register.physical = register; - register_allocator.register_states.set(register, .{ - .virtual = virtual_register, - }); - - logln(.codegen, .register_allocation_assignment, "Assigning V{} to {s}", .{ virtual_register.uniqueInteger(), @tagName(register) }); - // TODO: debug info - } - - fn usePhysicalRegister(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, instruction_index: Instruction.Index, physical_register: Register.Physical) !bool { - const displaced_any = try register_allocator.displacePhysicalRegister(mir, instruction_selection, instruction_index, physical_register); - register_allocator.register_states.set(physical_register, .preassigned); - register_allocator.setRegisterUsedInInstruction(physical_register, true); - return displaced_any; - } - - fn displacePhysicalRegister(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, instruction_index: Instruction.Index, physical_register: Register.Physical) !bool { - const state = register_allocator.register_states.getPtr(physical_register); - // print("Trying to displace register {s} with state {s}", .{ @tagName(physical_register), @tagName(state.*) }); - return switch (state.*) { - .free => false, - .preassigned => blk: { - logln(.codegen, .register_allocation_problematic_hint, "Freeing preassigned {s} at displacePhysicalRegister", .{@tagName(physical_register)}); - state.* = .free; - break :blk true; - }, - .virtual => |virtual_register| blk: { - logln(.codegen, .register_allocation_problematic_hint, "Freeing assigned {s} at displacePhysicalRegister", .{@tagName(physical_register)}); - const live_reg = register_allocator.live_virtual_registers.getPtr(virtual_register).?; - const before = mir.getNextInstructionIndex(instruction_index); - try register_allocator.reload(mir, instruction_selection, before, virtual_register, physical_register); - state.* = .free; - live_reg.physical = .no_register; - live_reg.reloaded = true; - break :blk true; - }, - .livein => unreachable, - }; - } - - fn reload(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, before_index: usize, virtual_register: Register.Virtual.Index, physical_register: Register.Physical) !void { - const frame_index = try register_allocator.getStackSpaceFor(mir, instruction_selection, virtual_register); - const register_class = mir.virtual_registers.get(virtual_register).register_class; - logln(.codegen, .register_allocation_reload, "Frame index: {}", .{frame_index}); - - try instruction_selection.loadRegisterFromStackSlot(mir, before_index, physical_register, frame_index, register_class, virtual_register); - } - - fn getStackSpaceFor(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, virtual_register: Register.Virtual.Index) !u32 { - if (register_allocator.stack_slots.get(virtual_register)) |frame_index| { - return frame_index; - } else { - const register_class = mir.virtual_registers.get(virtual_register).register_class; - const register_class_descriptor = register_class_descriptors.get(register_class); - assert(register_class_descriptor.spill_size > 0); - assert(register_class_descriptor.spill_alignment > 0); - const frame_index = try mir.createSpillStackObject(instruction_selection, register_class_descriptor.spill_size, register_class_descriptor.spill_alignment); - - try register_allocator.stack_slots.put(mir.allocator, virtual_register, frame_index); - return frame_index; - } - } - - fn freePhysicalRegister(register_allocator: *RegisterAllocator, physical_register: Register.Physical) void { - const state = register_allocator.register_states.getPtr(physical_register); - switch (state.*) { - .free => unreachable, - .preassigned => { - logln(.codegen, .register_allocation_problematic_hint, "Freeing preassigned {s} at freePhysicalRegister", .{@tagName(physical_register)}); - state.* = .free; - }, - .virtual => |virtual_register_index| { - const live_register = register_allocator.live_virtual_registers.getPtr(virtual_register_index).?; - assert(live_register.physical == physical_register); - register_allocator.register_states.set(physical_register, .free); - live_register.physical = .no_register; - }, - .livein => unreachable, - } - } - - fn setRegisterUsedInInstruction(register_allocator: *RegisterAllocator, physical_register: Register.Physical, value: bool) void { - logln(.codegen, .register_allocation_problematic_hint, "Marked {s} {s} in instruction", .{ @tagName(physical_register), if (value) "used" else "unused" }); - register_allocator.used_in_instruction.setPresent(physical_register, value); - } - - // fn markUsedRegisterInInstruction(register_allocator: *RegisterAllocator, physical_register: Register.Physical) void { - // register_allocator.used_in_instruction.setPresent(physical_register, true); - // } - // - // fn unmarkUsedRegisterInInstruction(register_allocator: *RegisterAllocator, physical_register: Register.Physical) void { - // register_allocator.used_in_instruction.setPresent(physical_register, false); - // } - - fn definePhysicalRegister(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, instruction_index: Instruction.Index, physical_register: Register.Physical) !bool { - const displaced_any = try register_allocator.displacePhysicalRegister(mir, instruction_selection, instruction_index, physical_register); - register_allocator.register_states.set(physical_register, .preassigned); - return displaced_any; - } - - fn defineVirtualRegister(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, instruction_index: Instruction.Index, operand_index: Operand.Index, virtual_register: Register.Virtual.Index, look_at_physical_register_uses: bool) !bool { - const instruction = mir.instructions.get(instruction_index); - const operand = mir.operands.get(operand_index); - const gop = try register_allocator.live_virtual_registers.getOrPut(mir.allocator, virtual_register); - const live_register = gop.value_ptr; - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .virtual = virtual_register, - }; - if (!operand.flags.dead_or_kill) { - if (live_register.live_out) { - // TODO - unreachable; - } else { - operand.flags.dead_or_kill = true; - } - } - } - - if (live_register.physical == .no_register) { - try register_allocator.allocateVirtualRegister(mir, instruction_selection, instruction_index, live_register, null, look_at_physical_register_uses); - } else { - assert(!register_allocator.isRegisterUsedInInstruction(live_register.physical, look_at_physical_register_uses)); - } - - const physical_register = live_register.physical; - assert(physical_register != .no_register); - if (live_register.reloaded or live_register.live_out) { - logln(.codegen, .register_allocation_problematic_hint, "Live register: {}", .{live_register}); - const instruction_descriptor = instruction_descriptors.get(instruction.id); - if (!instruction_descriptor.flags.implicit_def) { - const spill_before = mir.getNextInstructionIndex(instruction_index); - const kill = live_register.last_use.invalid; - try register_allocator.spill(mir, instruction_selection, spill_before, virtual_register, physical_register, kill, live_register.live_out); - - live_register.last_use = Instruction.Index.invalid; - } - - live_register.live_out = false; - live_register.reloaded = false; - } - - // bundle? - - register_allocator.setRegisterUsedInInstruction(physical_register, true); - return mir.setPhysicalRegister(instruction_selection, operand_index, physical_register); - } - - fn spill(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, spill_before: usize, virtual_register: Register.Virtual.Index, physical_register: Register.Physical, kill: bool, live_out: bool) !void { - _ = live_out; - const frame_index = try register_allocator.getStackSpaceFor(mir, instruction_selection, virtual_register); - const register_class = mir.virtual_registers.get(virtual_register).register_class; - try instruction_selection.storeRegisterToStackSlot(mir, spill_before, physical_register, kill, frame_index, register_class, virtual_register); - // TODO: debug operands - } - - fn mayLiveIn(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, virtual_register_index: Register.Virtual.Index) bool { - if (register_allocator.may_live_across_blocks.isSet(virtual_register_index.uniqueInteger())) { - unreachable; - } else { - const head_index_ptr = mir.getRegisterListHead(instruction_selection, .{ - .index = .{ - .virtual = virtual_register_index, - }, - }); - - // TODO: setup iterator - var define_instructions = Instruction.Iterator.Get(.{ - .use = false, - .def = true, - .element = .instruction, - }).new(mir, head_index_ptr.*); - while (define_instructions.next()) |_| { - unreachable; - } - - return false; - } - } - - fn mayLiveOut(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, virtual_register_index: Register.Virtual.Index) bool { - if (register_allocator.may_live_across_blocks.isSet(virtual_register_index.uniqueInteger())) { - unreachable; - } else { - if (false) { - // TODO: FIXME if block loops - } - - const head_index_ptr = mir.getRegisterListHead(instruction_selection, .{ - .index = .{ - .virtual = virtual_register_index, - }, - }); - var iterator = Instruction.Iterator.Get(.{ - .use = true, - .def = false, - .element = .instruction, - }).new(mir, head_index_ptr.*); - - const limit = 8; - var count: u32 = 0; - while (iterator.nextPointer()) |use_instruction| { - if (!use_instruction.parent.eq(instruction_selection.current_block)) { - register_allocator.may_live_across_blocks.set(virtual_register_index.uniqueInteger()); - // TODO: return !basic_block.successorsEmpty() - return false; - } - - count += 1; - if (count >= limit) { - register_allocator.may_live_across_blocks.set(virtual_register_index.uniqueInteger()); - // TODO: return !basic_block.successorsEmpty() - return false; - } - - // self loop def - if (false) { - unreachable; - } - } - - return false; - } - } - - fn reloadAtBegin(register_allocator: *RegisterAllocator, mir: *MIR, instruction_selection: *InstructionSelection, basic_block: BasicBlock.Index) !void { - _ = basic_block; - if (register_allocator.live_virtual_registers.entries.len > 0) { - // TODO: basic block liveins (regmasks?) - - const live_registers = register_allocator.live_virtual_registers.values(); - // print("Live register count: {}", .{live_registers.len}); - - for (live_registers) |live_register| { - const physical_register = live_register.physical; - if (physical_register == .no_register) { - continue; - } - - if (register_allocator.register_states.get(physical_register) == .livein) { - unreachable; - } - - // assert? - - const virtual_register = live_register.virtual; - if (false) { - unreachable; - } else { - try register_allocator.reload(mir, instruction_selection, 0, virtual_register, physical_register); - } - } - - register_allocator.live_virtual_registers.clearRetainingCapacity(); - } - } - }; - - fn getRegisters(operand: *const Operand, register_buffer: []Register) []const Register { - var registers: []Register = register_buffer; - registers.len = 0; - switch (operand.u) { - .register => |register| { - registers.len += 1; - registers[registers.len - 1] = register; - }, - .lea64mem => |lea64mem| { - if (lea64mem.gp64) |register| { - registers.len += 1; - registers[registers.len - 1] = register; - } - - if (lea64mem.scale_reg) |register| { - registers.len += 1; - registers[registers.len - 1] = register; - } - }, - .memory, - .immediate, - .pc_relative, - => {}, - // else => |t| @panic(@tagName(t)), - } - - return registers; - } - - pub fn allocateRegisters(mir: *MIR) !void { - logln(.codegen, .register_allocation_block, "\n[REGISTER ALLOCATION]\n", .{}); - const function_count = mir.functions.len; - var function_iterator = mir.functions.iterator(); - const register_count = @typeInfo(Register.Physical).Enum.fields.len; - _ = register_count; - const register_unit_count = 173; - _ = register_unit_count; - - for (0..function_count) |function_index| { - const function = function_iterator.nextPointer().?; - const instruction_selection = &mir.instruction_selections.items[function_index]; - logln(.codegen, .register_allocation_function_before, "Allocating registers for {}", .{function}); - - var block_i: usize = function.blocks.items.len; - var register_allocator = try RegisterAllocator.init(mir, instruction_selection); - - while (block_i > 0) { - block_i -= 1; - - const block_index = function.blocks.items[block_i]; - const block = mir.blocks.get(block_index); - - var instruction_i: usize = block.instructions.items.len; - - while (instruction_i > 0) { - instruction_i -= 1; - - const instruction_index = block.instructions.items[instruction_i]; - const instruction = mir.instructions.get(instruction_index); - logln(.codegen, .register_allocation_new_instruction, "===============\nInstruction {} (#{})", .{ instruction_i, instruction_index.uniqueInteger() }); - logln(.codegen, .register_allocation_new_instruction_function_before, "{}", .{function}); - - register_allocator.used_in_instruction = RegisterBitset.initEmpty(); - - var physical_register_use = false; - var virtual_register_definition = false; - var register_definition = false; - var early_clobber = false; - var assign_live_throughs = false; - - for (instruction.operands.items) |operand_index| { - const operand = mir.operands.get(operand_index); - switch (operand.u) { - .register => |register| switch (register.index) { - .virtual => { - if (operand.flags.type == .def) { - register_definition = true; - virtual_register_definition = true; - if (operand.flags.early_clobber) { - early_clobber = true; - assign_live_throughs = true; - } - - // TODO - } - }, - .physical => |physical_register| { - if (!register_allocator.reserved.contains(physical_register)) { - if (operand.flags.type == .def) { - register_definition = true; - - const displaced_any = try register_allocator.definePhysicalRegister(mir, instruction_selection, instruction_index, physical_register); - if (operand.flags.early_clobber) { - early_clobber = true; - } - if (!displaced_any) { - operand.flags.dead_or_kill = true; - } - } - - if (operand.readsRegister()) { - physical_register_use = true; - } - } - }, - }, - else => {}, - } - } - - if (register_definition) { - if (virtual_register_definition) { - var rearranged_implicit_operands = true; - if (assign_live_throughs) { - unreachable; - } else { - while (rearranged_implicit_operands) { - rearranged_implicit_operands = false; - - for (instruction.operands.items) |operand_index| { - const operand = mir.operands.get(operand_index); - switch (operand.u) { - .register => |register| switch (operand.flags.type) { - .def => switch (register.index) { - .virtual => |virtual_register| { - rearranged_implicit_operands = try register_allocator.defineVirtualRegister(mir, instruction_selection, instruction_index, operand_index, virtual_register, false); - if (rearranged_implicit_operands) { - break; - } - }, - .physical => {}, - }, - else => {}, - }, - .lea64mem => |lea64mem| { - assert(lea64mem.gp64 == null); - assert(lea64mem.scale_reg == null); - }, - else => {}, - } - } - } - } - } - - var operand_i = instruction.operands.items.len; - while (operand_i > 0) { - operand_i -= 1; - - const operand_index = instruction.operands.items[operand_i]; - const operand = mir.operands.get(operand_index); - switch (operand.u) { - .register => |register| switch (operand.flags.type) { - .def => switch (register.index) { - .physical => |physical_register| { - register_allocator.freePhysicalRegister(physical_register); - register_allocator.setRegisterUsedInInstruction(physical_register, false); - }, - .virtual => {}, - }, - .use => {}, - }, - else => {}, - } - } - } - - // if (register_mask) { - // unreachable; - // } - - // Physical register use - if (physical_register_use) { - for (instruction.operands.items) |operand_index| { - const operand = mir.operands.get(operand_index); - - switch (operand.flags.type) { - .def => {}, - .use => switch (operand.u) { - .register => |register| switch (register.index) { - .physical => |physical_register| { - if (!register_allocator.reserved.contains(physical_register)) { - const displaced_any = try register_allocator.usePhysicalRegister(mir, instruction_selection, instruction_index, physical_register); - if (!displaced_any) { - operand.flags.dead_or_kill = true; - } - } - }, - .virtual => {}, - }, - else => {}, - }, - } - } - } - - var rearranged_implicit_operands = true; - while (rearranged_implicit_operands) { - rearranged_implicit_operands = false; - for (instruction.operands.items, 0..) |operand_index, operand_i| { - const operand = mir.operands.get(operand_index); - switch (operand.u) { - .register => |register| switch (operand.flags.type) { - .def => {}, - .use => switch (register.index) { - .physical => {}, - .virtual => |virtual_register_index| { - if (operand.flags.undef) { - unreachable; - } - rearranged_implicit_operands = try register_allocator.useVirtualRegister(mir, instruction_selection, instruction_index, virtual_register_index, @intCast(operand_i)); - if (rearranged_implicit_operands) break; - }, - }, - }, - else => {}, - } - } - } - - if (instruction.id == .copy and instruction.operands.items.len == 2) { - const dst_register = mir.operands.get(instruction.operands.items[0]).u.register.index; - const src_register = mir.operands.get(instruction.operands.items[1]).u.register.index; - - if (std.meta.eql(dst_register, src_register)) { - try register_allocator.coalesced.append(mir.allocator, instruction_index); - logln(.codegen, .register_allocation_instruction_avoid_copy, "Avoiding copy...", .{}); - } - } - } - - try register_allocator.reloadAtBegin(mir, instruction_selection, block_index); - - for (register_allocator.coalesced.items) |coalesced| { - for (block.instructions.items, 0..) |instruction_index, i| { - if (coalesced.eq(instruction_index)) { - const result = block.instructions.orderedRemove(i); - assert(result.eq(coalesced)); - break; - } - } else unreachable; - } - - logln(.codegen, .register_allocation_function_after, "Allocated registers for {}\n============", .{function}); - } - } - - const clear_virtual_registers = true; - if (clear_virtual_registers) { - mir.clearVirtualRegisters(); - } - } - - fn clearVirtualRegisters(mir: *MIR) void { - var vr_it = mir.virtual_registers.iterator(); - var verified_virtual_register_count: usize = 0; - var skipped: usize = 0; - while (vr_it.nextIndex()) |virtual_register_index| { - const virtual_register = mir.virtual_registers.get(virtual_register_index); - verified_virtual_register_count += 1; - if (virtual_register.use_def_list_head.invalid) { - skipped += 1; - continue; - } - - mir.verifyUseList(virtual_register.use_def_list_head, virtual_register_index); - } - - logln(.codegen, .register_allocation_operand_list_verification, "Verified {} virtual registers ({} skipped)", .{ verified_virtual_register_count, skipped }); - } - - fn verifyUseList(mir: *MIR, start_operand_index: Operand.Index, register: Register.Virtual.Index) void { - var iterator = Instruction.Iterator.Get(.{ - .use = true, - .def = true, - .element = .operand, - }).new(mir, start_operand_index); - - while (iterator.nextPointer()) |operand| { - const instruction_index = operand.parent; - assert(!instruction_index.invalid); - const instruction = mir.instructions.get(instruction_index); - logln(.codegen, .register_allocation_operand_list_verification, "Verifying instruction #{}, operand #{}", .{ instruction_index.uniqueInteger(), mir.operands.indexOf(operand).uniqueInteger() }); - _ = instruction; - assert(operand.u == .register); - assert(operand.u.register.index == .virtual and operand.u.register.index.virtual.eq(register)); - } - - unreachable; - } - - fn getGP32Encoding(operand: Operand) Encoding.GP32 { - assert(operand.id == .gp32); - const physical_register = operand.u.register.index.physical; - const gp_register_encoding: Encoding.GP32 = switch (physical_register) { - .eax => .a, - .edi => .di, - .ecx => .c, - else => |t| @panic(@tagName(t)), - }; - - return gp_register_encoding; - } - - fn getGP64Encoding(operand: Operand) Encoding.GP64 { - assert(operand.id == .gp64); - const physical_register = operand.u.register.index.physical; - const gp_register_encoding: Encoding.GP64 = switch (physical_register) { - .rax => .a, - .rdi => .di, - .rsi => .si, - .rdx => .d, - .rcx => .c, - else => |t| @panic(@tagName(t)), - }; - - 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); - // logln(.codegen, .register_allocation_problematic_hint, "Stack size: {} after Stack object: {}", .{ result, stack_object }); - } - - 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 { - const image = try mir.allocator.create(emit.Result); - 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.section_manager.getTextSection().symbol_table.ensureTotalCapacity(mir.allocator, mir.functions.len); - - while (function_iterator.nextIndex()) |function_index| { - const function = mir.functions.get(function_index); - logln(.codegen, .encoding, "\n{s}:", .{function.name}); - - const function_offset: u32 = @intCast(image.section_manager.getCodeOffset()); - - function_offsets.putAssumeCapacityNoClobber(function_index, 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) { - 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| { - try image.section_manager.appendCode(&.{ 0x48, 0x83, 0xec, stack_size_u8 }); - } else { - unreachable; - } - } - - for (function.blocks.items) |block_index| { - const block = mir.blocks.get(block_index); - for (block.instructions.items) |instruction_index| { - const instruction = mir.instructions.get(instruction_index); - - const instruction_offset = image.section_manager.getCodeOffset(); - - switch (instruction.id) { - .mov32r0 => { - const operand = mir.operands.get(instruction.operands.items[0]); - const gp_register_encoding = getGP32Encoding(operand.*); - const new_instruction_id = Instruction.Id.xor32rr; - const instruction_descriptor = instruction_descriptors.get(new_instruction_id); - const opcode: u8 = @intCast(instruction_descriptor.opcode); - 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), - }; - try image.section_manager.appendCodeByte(@bitCast(modrm)); - }, - .ret => {}, - .mov32mr => { - const source_operand = mir.operands.get(instruction.operands.items[1]); - const source_gp32 = getGP32Encoding(source_operand.*); - - const destination_operand = mir.operands.get(instruction.operands.items[0]); - assert(destination_operand.u == .memory); - const memory = destination_operand.u.memory; - const instruction_descriptor = instruction_descriptors.get(instruction.id); - const opcode: u8 = @intCast(instruction_descriptor.opcode); - 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), - }; - 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)), - } - }, - .mov64mr => { - 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)), - } - }, - .mov32rm => { - 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_gp32 = getGP32Encoding(destination_operand.*); - - const source_operand = mir.operands.get(instruction.operands.items[1]); - assert(source_operand.u == .memory); - const source_memory = source_operand.u.memory; - - const modrm = ModRm{ - .rm = @intFromEnum(Encoding.GP32.bp), - .reg = @intCast(@intFromEnum(destination_gp32)), - .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true), - }; - 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)), - } - }, - .mov64rm => { - 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 => { - 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 => { - const source_operand = mir.operands.get(instruction.operands.items[1]); - const source_immediate: u32 = @intCast(source_operand.u.immediate); - - const destination_operand = mir.operands.get(instruction.operands.items[0]); - const destination_gp64 = getGP64Encoding(destination_operand.*); - const destination_gp32 = switch (destination_gp64) { - inline else => |gp64| @field(Encoding.GP32, @tagName(gp64)), - }; - - const opcode = @as(u8, 0xb8) | @as(u3, @intCast(@intFromEnum(destination_gp32))); - try image.section_manager.appendCodeByte(opcode); - - try image.section_manager.appendCode(std.mem.asBytes(&source_immediate)); - }, - .movsx64rm32 => { - const destination_operand = mir.operands.get(instruction.operands.items[0]); - const destination_register = getGP64Encoding(destination_operand.*); - - const source_operand = mir.operands.get(instruction.operands.items[1]); - const source_memory = source_operand.u.memory; - - const rex = Rex{ - .b = false, - .x = false, - .r = false, - .w = true, - }; - 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 modrm = ModRm{ - .rm = @intFromEnum(Encoding.GP32.bp), - .reg = @intCast(@intFromEnum(destination_register)), - .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)), - } - }, - .syscall => try image.section_manager.appendCode(&.{ 0x0f, 0x05 }), - .ud2 => try image.section_manager.appendCode(&.{ 0x0f, 0x0b }), - .call64pcrel32 => { - // TODO: emit relocation - const operand = mir.operands.get(instruction.operands.items[0]); - const instruction_descriptor = instruction_descriptors.get(instruction.id); - const opcode: u8 = @intCast(instruction_descriptor.opcode); - try image.section_manager.appendCodeByte(opcode); - - switch (operand.u) { - .pc_relative => |pc_relative| { - // TODO: fix - const callee: Function.Index = @bitCast(pc_relative.index); - const caller = function_index; - - const instruction_len = 5; - - if (callee.uniqueInteger() <= caller.uniqueInteger()) { - const callee_offset: i64 = @intCast(function_offsets.get(callee).?); - const caller_offset: i64 = @intCast(instruction_offset + instruction_len); - const offset: i32 = @intCast(callee_offset - caller_offset); - try image.section_manager.appendCode(std.mem.asBytes(&offset)); - } else { - // TODO: handle relocation - try image.section_manager.appendCode(&.{ 0, 0, 0, 0 }); - unreachable; - } - }, - else => |t| @panic(@tagName(t)), - } - }, - .copy => { - const destination_operand = mir.operands.get(instruction.operands.items[0]); - const source_operand = mir.operands.get(instruction.operands.items[1]); - assert(destination_operand.id == source_operand.id); - - switch (destination_operand.id) { - .gp32 => { - try image.section_manager.appendCodeByte(0x89); - - const destination_register = getGP32Encoding(destination_operand.*); - const source_register = getGP32Encoding(source_operand.*); - const modrm = ModRm{ - .rm = @intCast(@intFromEnum(destination_register)), - .reg = @intCast(@intFromEnum(source_register)), - .mod = @as(u2, @intFromBool(true)) << 1 | @intFromBool(true), - }; - - 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 => { - 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)), - } - }, - .add32rr, - .sub32rr, - .imul32rr, - => { - const instruction_descriptor = instruction_descriptors.get(instruction.id); - const opcode: u8 = @intCast(instruction_descriptor.opcode); - try image.section_manager.appendCodeByte(opcode); - - const destination_operand_index = instruction.operands.items[0]; - const left_operand_index = instruction.operands.items[1]; - const right_operand_index = instruction.operands.items[2]; - - const destination_operand = mir.operands.get(destination_operand_index); - const left_operand = mir.operands.get(left_operand_index); - const right_operand = mir.operands.get(right_operand_index); - - const destination_register = getGP32Encoding(destination_operand.*); - const left_register = getGP32Encoding(left_operand.*); - const right_register = getGP32Encoding(right_operand.*); - - assert(destination_register == left_register); - - const modrm = ModRm{ - .rm = @intCast(@intFromEnum(destination_register)), - .reg = @intCast(@intFromEnum(right_register)), - .mod = 0b11, - }; - try image.section_manager.appendCodeByte(@bitCast(modrm)); - }, - .mov32mi => { - const instruction_descriptor = instruction_descriptors.get(instruction.id); - const opcode: u8 = @intCast(instruction_descriptor.opcode); - try image.section_manager.appendCodeByte(opcode); - - const destination_operand_index = instruction.operands.items[0]; - const destination_operand = mir.operands.get(destination_operand_index); - switch (destination_operand.u.memory.addressing_mode.base) { - .register_base => unreachable, - .frame_index => |frame_index| { - const modrm = ModRm{ - .rm = @intFromEnum(Encoding.GP64.bp), - .reg = 0, - .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true), - }; - try image.section_manager.appendCodeByte(@bitCast(modrm)); - - 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); - }, - } - - const source_operand_index = instruction.operands.items[1]; - const source_operand = mir.operands.get(source_operand_index); - const source_immediate: u32 = @intCast(source_operand.u.immediate); - - try image.section_manager.appendCode(std.mem.asBytes(&source_immediate)); - }, - .idiv32m => { - const instruction_descriptor = instruction_descriptors.get(instruction.id); - const opcode = @as(u8, @intCast(instruction_descriptor.opcode)); - try image.section_manager.appendCodeByte(opcode); - - const destination_operand_index = instruction.operands.items[0]; - const destination_operand = mir.operands.get(destination_operand_index); - switch (destination_operand.u.memory.addressing_mode.base) { - .register_base => unreachable, - .frame_index => |frame_index| { - const modrm = ModRm{ - .rm = @intFromEnum(Encoding.GP64.bp), - .reg = 7, - .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true), - }; - try image.section_manager.appendCodeByte(@bitCast(modrm)); - - 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); - }, - } - }, - .add32rm, - .sub32rm, - .and32rm, - .xor32rm, - .or32rm, - .imul32rm, - => { - const instruction_descriptor = instruction_descriptors.get(instruction.id); - const opcode: u8 = @intCast(instruction_descriptor.opcode); - try image.section_manager.appendCodeByte(opcode); - - const destination_operand_index = instruction.operands.items[0]; - const left_operand_index = instruction.operands.items[1]; - const right_operand_index = instruction.operands.items[2]; - - const destination_operand = mir.operands.get(destination_operand_index); - const left_operand = mir.operands.get(left_operand_index); - const right_operand = mir.operands.get(right_operand_index); - - const destination_register = getGP32Encoding(destination_operand.*); - const left_register = getGP32Encoding(left_operand.*); - assert(destination_register == left_register); - - assert(right_operand.id == .i32mem); - const right_memory = right_operand.u.memory; - - switch (right_memory.addressing_mode.base) { - .register_base => unreachable, - .frame_index => |frame_index| { - const modrm = ModRm{ - .rm = @intFromEnum(Encoding.GP64.bp), - .reg = @intCast(@intFromEnum(destination_register)), - .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true), - }; - try image.section_manager.appendCodeByte(@bitCast(modrm)); - - 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); - }, - } - }, - .sub32mr => { - const instruction_descriptor = instruction_descriptors.get(instruction.id); - const opcode: u8 = @intCast(instruction_descriptor.opcode); - try image.section_manager.appendCodeByte(opcode); - - const destination_operand_index = instruction.operands.items[0]; - const left_operand_index = instruction.operands.items[1]; - const right_operand_index = instruction.operands.items[2]; - - const destination_operand = mir.operands.get(destination_operand_index); - const left_operand = mir.operands.get(left_operand_index); - const right_operand = mir.operands.get(right_operand_index); - - const right_register = getGP32Encoding(right_operand.*); - - assert(left_operand.id == .i32mem); - assert(destination_operand.id == .i32mem); - const left_memory = left_operand.u.memory; - - switch (left_memory.addressing_mode.base) { - .register_base => unreachable, - .frame_index => |frame_index| { - const modrm = ModRm{ - .rm = @intFromEnum(Encoding.GP64.bp), - .reg = @intCast(@intFromEnum(right_register)), - .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true), - }; - try image.section_manager.appendCodeByte(@bitCast(modrm)); - - 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); - }, - } - }, - .shl32ri, - .shr32ri, - .shl32mi, - .shr32mi, - => { - const instruction_descriptor = instruction_descriptors.get(instruction.id); - const opcode: u8 = @intCast(instruction_descriptor.opcode); - try image.section_manager.appendCodeByte(opcode); - - const destination_operand_index = instruction.operands.items[0]; - const destination_operand = mir.operands.get(destination_operand_index); - - const left_operand_index = instruction.operands.items[1]; - const left_operand = mir.operands.get(left_operand_index); - - switch (destination_operand.u) { - .memory => switch (destination_operand.u.memory.addressing_mode.base) { - .register_base => unreachable, - .frame_index => |frame_index| { - assert(left_operand.u.memory.addressing_mode.base.frame_index == frame_index); - const modrm = ModRm{ - .rm = @intFromEnum(Encoding.GP64.bp), - .reg = switch (instruction.id) { - .shl32mi => 4, - .shr32mi => 5, - else => unreachable, - }, - .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true), - }; - try image.section_manager.appendCodeByte(@bitCast(modrm)); - - 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); - }, - }, - .register => { - const register = getGP32Encoding(destination_operand.*); - // std.debug.print("Register: {}\n", .{register}); - const modrm = ModRm{ - .rm = @intCast(@intFromEnum(register)), - .reg = switch (instruction.id) { - .shl32ri => 4, - .shr32ri => 5, - else => unreachable, - }, - .mod = @as(u2, @intFromBool(true)) << 1 | @intFromBool(true), - }; - try image.section_manager.appendCodeByte(@bitCast(modrm)); - }, - else => unreachable, - } - - const source_operand_index = instruction.operands.items[2]; - const source_operand = mir.operands.get(source_operand_index); - assert(source_operand.id == .imm8); - const source_immediate: u8 = @intCast(source_operand.u.immediate); - - try image.section_manager.appendCodeByte(source_immediate); - }, - .cmp32mi8 => { - const left_operand_index = instruction.operands.items[0]; - const left_operand = mir.operands.get(left_operand_index); - - switch (left_operand.u) { - .memory => |memory| switch (memory.addressing_mode.base) { - .register_base => unreachable, - .frame_index => |frame_index| { - const modrm = ModRm{ - .rm = @intFromEnum(Encoding.GP64.bp), - .reg = switch (instruction.id) { - .cmp32mi8 => 7, - else => unreachable, - }, - .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true), - }; - try image.section_manager.appendCodeByte(@bitCast(modrm)); - - 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 => unreachable, - } - - const source_operand_index = instruction.operands.items[1]; - const source_operand = mir.operands.get(source_operand_index); - assert(source_operand.id == .imm8); - const source_immediate: u8 = @intCast(source_operand.u.immediate); - - try image.section_manager.appendCodeByte(source_immediate); - }, - .seter => { - const operand_index = instruction.operands.items[0]; - const operand = mir.operands.get(operand_index); - assert(operand.u == .register); - const instruction_descriptor = instruction_descriptors.get(instruction.id); - const opcode = &[_]u8{ 0x0f, @intCast(instruction_descriptor.opcode) }; - try image.section_manager.appendCode(opcode); - const modrm = ModRm{ - .rm = @intFromEnum(Encoding.GP64.bp), - .reg = 0, - .mod = @as(u2, @intFromBool(false)) << 1 | @intFromBool(true), - }; - try image.section_manager.appendCodeByte(@bitCast(modrm)); - }, - .and8ri => { - const destination_register_index = instruction.operands.items[0]; - const destination_register_operand = mir.operands.get(destination_register_index); - switch (destination_register_operand.u.register.index.physical) { - .al => { - unreachable; - }, - else => |t| @panic(@tagName(t)), - } - }, - else => |t| @panic(@tagName(t)), - } - - if (instruction_offset != image.section_manager.getCodeOffset()) { - const print_tags = true; - if (print_tags) { - var offset = @tagName(instruction.id).len + 2; - log(.codegen, .encoding, "{s}: ", .{@tagName(instruction.id)}); - const margin = 16; - while (offset < margin) : (offset += 1) { - log(.codegen, .encoding, " ", .{}); - } - } - - for (image.section_manager.getTextSection().bytes.items[instruction_offset..]) |byte| { - log(.codegen, .encoding, "0x{x:0>2} ", .{byte}); - } - log(.codegen, .encoding, "\n", .{}); - } - } - } - - const last_block_index = function.blocks.items[function.blocks.items.len - 1]; - const last_block = mir.blocks.get(last_block_index); - const last_block_last_instruction_index = last_block.instructions.items[last_block.instructions.items.len - 1]; - const last_block_last_instruction = mir.instructions.get(last_block_last_instruction_index); - - if (last_block_last_instruction.id == .ret) { - if (stack_size != 0) { - // add rsp, stack_offset - if (std.math.cast(u8, stack_size)) |stack_size_u8| { - try image.section_manager.appendCode(&.{ 0x48, 0x83, 0xc4, stack_size_u8 }); - } else { - unreachable; - } - - try image.section_manager.appendCodeByte(0x5d); // pop rbp - } - - try image.section_manager.appendCodeByte(0xc3); - } - } - - return image; - } - - fn getRegisterListHead(mir: *MIR, instruction_selection: *InstructionSelection, register: Register) *Operand.Index { - switch (register.index) { - .physical => |physical| { - const operand_index = instruction_selection.physical_register_use_or_definition_list.getPtr(physical); - return operand_index; - }, - .virtual => |virtual_register_index| { - const virtual_register = mir.virtual_registers.get(virtual_register_index); - return &virtual_register.use_def_list_head; - }, - } - } - - const Function = struct { - blocks: ArrayList(BasicBlock.Index) = .{}, - instruction_selection: *InstructionSelection, - mir: *MIR, - name: []const u8, - - pub const List = BlockList(@This()); - pub const Index = List.Index; - pub const Allocation = List.Allocation; - - pub fn format(function: *const Function, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - const function_name = function.name; - try writer.print("{s}:\n", .{function_name}); - for (function.blocks.items, 0..) |block_index, function_block_index| { - try writer.print("#{}: ({})\n", .{ function_block_index, block_index.uniqueInteger() }); - const block = function.mir.blocks.get(block_index); - for (block.instructions.items, 0..) |instruction_index, block_instruction_index| { - try writer.print("%{} (${}): ", .{ block_instruction_index, instruction_index.uniqueInteger() }); - const instruction = function.mir.instructions.get(instruction_index).*; - try writer.print("{s}", .{@tagName(instruction.id)}); - for (instruction.operands.items, 0..) |operand_index, i| { - const operand = function.mir.operands.get(operand_index); - try writer.print(" O{} ", .{operand_index.uniqueInteger()}); - switch (operand.u) { - .register => |register| { - switch (register.index) { - .physical => |physical| try writer.writeAll(@tagName(physical)), - .virtual => |virtual| try writer.print("VR{}", .{virtual.uniqueInteger()}), - } - }, - .memory => |memory| { - const base = memory.addressing_mode.base; - switch (base) { - .register_base => unreachable, - .frame_index => |frame_index| try writer.print("SF{}", .{frame_index}), - } - }, - else => try writer.writeAll(@tagName(operand.u)), - } - - if (i < instruction.operands.items.len - 1) { - try writer.writeByte(','); - } - } - - try writer.writeByte('\n'); - } - - try writer.writeByte('\n'); - } - _ = options; - _ = fmt; - } - }; - - fn buildInstruction(mir: *MIR, instruction_selection: *InstructionSelection, instruction: Instruction.Id, operands: []const Operand) !Instruction.Index { - // Some sanity check - const descriptor = instruction_descriptors.getPtrConst(instruction); - { - if (instruction != .copy) { - if (descriptor.operands.len == operands.len) { - for (descriptor.operands, operands) |descriptor_operand, operand| { - switch (descriptor_operand.id) { - .unknown => {}, - else => if (descriptor_operand.id != operand.id) unreachable, - } - } - } else { - switch (instruction) { - .ret => {}, - .syscall => {}, - else => |t| @panic(@tagName(t)), - } - } - } - } - - var list = try ArrayList(Operand.Index).initCapacity(mir.allocator, operands.len + descriptor.implicit_definitions.len + descriptor.implicit_uses.len); - const instruction_allocation = try mir.instructions.addOne(mir.allocator); - // TODO: MachineRegisterInfo::addRegOperandToUseList - for (operands) |operand_value| { - const operand_allocation = try mir.operands.append(mir.allocator, operand_value); - list.appendAssumeCapacity(operand_allocation.index); - const operand = operand_allocation.ptr; - const operand_index = operand_allocation.index; - operand_allocation.ptr.parent = instruction_allocation.index; - - switch (operand.u) { - .register => mir.addRegisterOperandFromUseList(instruction_selection, operand_index), - .lea64mem => |lea64mem| { - // TODO - assert(lea64mem.gp64 == null); - assert(lea64mem.scale_reg == null); - }, - .memory, - .immediate, - .pc_relative, - => {}, - } - } - - for (descriptor.implicit_definitions) |implicitly_defined_register| { - const operand_allocation = try mir.operands.append(mir.allocator, Operand{ - .id = switch (implicitly_defined_register) { - .eflags => .ccr, - .eax => .gp32, - .edx => .gp32, - else => |t| @panic(@tagName(t)), - }, - .u = .{ - .register = .{ - .index = .{ - .physical = implicitly_defined_register, - }, - }, - }, - .flags = .{ - .type = .def, - .implicit = true, - }, - }); - list.appendAssumeCapacity(operand_allocation.index); - const operand_index = operand_allocation.index; - operand_allocation.ptr.parent = instruction_allocation.index; - mir.addRegisterOperandFromUseList(instruction_selection, operand_index); - } - - for (descriptor.implicit_uses) |implicitly_used_register| { - const operand_allocation = try mir.operands.append(mir.allocator, Operand{ - .id = switch (implicitly_used_register) { - .eax => .gp32, - .edx => .gp32, - .eflags => .ccr, - else => |t| @panic(@tagName(t)), - }, - .u = .{ - .register = .{ - .index = .{ - .physical = implicitly_used_register, - }, - }, - }, - .flags = .{ - .type = .use, - .implicit = true, - }, - }); - list.appendAssumeCapacity(operand_allocation.index); - const operand_index = operand_allocation.index; - operand_allocation.ptr.parent = instruction_allocation.index; - mir.addRegisterOperandFromUseList(instruction_selection, operand_index); - } - - instruction_allocation.ptr.* = .{ - .id = instruction, - .operands = list, - .parent = instruction_selection.current_block, - }; - - return instruction_allocation.index; - } - - fn append(mir: *MIR, instruction_selection: *InstructionSelection, id: Instruction.Id, operands: []const Operand) !void { - const instruction = try mir.buildInstruction(instruction_selection, id, operands); - const current_block = mir.blocks.get(instruction_selection.current_block); - try current_block.instructions.append(mir.allocator, instruction); - } - - fn createSpillStackObject(mir: *MIR, instruction_selection: *InstructionSelection, spill_size: u32, spill_alignment: u32) !u32 { - const frame_index = try mir.createStackObject(instruction_selection, spill_size, spill_alignment, ir.Instruction.Index.invalid, true); - return frame_index; - } - - fn createStackObject(mir: *MIR, instruction_selection: *InstructionSelection, size: u64, asked_alignment: u32, ir_instruction: ir.Instruction.Index, is_spill_slot: bool) !u32 { - const stack_realignable = false; - const alignment = clampStackAlignment(!stack_realignable, asked_alignment, 16); - const index: u32 = @intCast(instruction_selection.stack_objects.items.len); - try instruction_selection.stack_objects.append(mir.allocator, .{ - .size = size, - .alignment = alignment, - .spill_slot = is_spill_slot, - .ir = ir_instruction, - }); - return index; - } - - fn clampStackAlignment(clamp: bool, alignment: u32, stack_alignment: u32) u32 { - if (!clamp or alignment <= stack_alignment) return alignment; - return stack_alignment; - } - - fn isFoldedOrDeadInstruction(mir: *MIR, instruction_index: ir.Instruction.Index) bool { - const result = !mir.mayWriteToMemory(instruction_index) and !mir.isTerminator(instruction_index); - return result; - } - - fn mayWriteToMemory(mir: *MIR, instruction_index: ir.Instruction.Index) bool { - const ir_instruction = mir.ir.instructions.get(instruction_index); - const result = switch (ir_instruction.*) { - .load => !mir.ir.loads.get(ir_instruction.load).isUnordered(), - .store => true, - else => |t| @panic(@tagName(t)), - }; - - return result; - } - - fn isTerminator(mir: *MIR, instruction_index: ir.Instruction.Index) bool { - const ir_instruction = mir.ir.instructions.get(instruction_index); - const result = switch (ir_instruction.*) { - .load => false, - else => |t| @panic(@tagName(t)), - }; - - return result; - } -}; - -const Rex = packed struct(u8) { - b: bool, - x: bool, - r: bool, - w: bool, - fixed: u4 = 0b0100, - - // fn create32RR(args: struct { - // rm: Encoding.GP32, - // reg: Encoding.GP32, - // sib: bool = false, - // }) ?Rex { - // if (args.sib) { - // unreachable; - // } else { - // } - // } - - // fn create(args: struct { - // rm: ?GPRegister = null, - // reg: ?GPRegister = null, - // sib: bool = false, - // rm_size: ?Size = null, - // }) ?Rex { - // const rex_byte = Rex{ - // .b = if (args.rm) |rm| @intFromEnum(rm) > std.math.maxInt(u3) else false, - // .x = args.sib, - // .r = if (args.reg) |reg| @intFromEnum(reg) > std.math.maxInt(u3) else false, - // .w = if (args.rm_size) |rm_size| rm_size == .eight else false, - // }; - // - // if (@as(u4, @truncate(@as(u8, @bitCast(rex_byte)))) != 0) { - // return rex_byte; - // } else { - // return null; - // } - // } -}; - -const Sib = packed struct(u8) { - base: u3, - index: u3, - scale: u2, -}; - -const ModRm = packed struct(u8) { - rm: u3, - reg: u3, - mod: u2, -}; - -fn getIrType(intermediate: *IR, ir_instruction_index: ir.Instruction.Index) ?ir.Type { - const ir_instruction = intermediate.instructions.get(ir_instruction_index); - return switch (ir_instruction.u) { - .argument => |argument_index| intermediate.arguments.get(argument_index).type, - .stack_slot => |stack_index| intermediate.stack_slots.get(stack_index).type, - .load => |load_index| getIrType(intermediate, intermediate.loads.get(load_index).value), - .syscall => |_| .{ - .scalar = .i64, - }, - .constant_integer => |integer| .{ - .scalar = integer.type, - }, - .constant_string_literal => .{ - .scalar = .i64, - }, - .call => |call_index| intermediate.function_definitions.get(intermediate.calls.get(call_index).callable.function_definition).callable.return_type, - .sign_extend => |cast_index| intermediate.casts.get(cast_index).type, - .binary_operation => |binary_operation_index| intermediate.binary_operations.get(binary_operation_index).type, - .phi => |phi_index| getIrType(intermediate, intermediate.phis.get(phi_index).operands.items[0].value), - else => |t| @panic(@tagName(t)), - }; -} - -fn resolveScalarType(ir_scalar_type: ir.Type.Scalar) ValueType.Id { - return switch (ir_scalar_type) { - .i1 => unreachable, - inline .i8, - .i16, - .i32, - .i64, - => |ir_type_ct| @field(ValueType.Id, @typeInfo(ir.Type.Scalar).Enum.fields[@intFromEnum(ir_type_ct)].name), - }; -} - -fn resolveType(ir_type: ir.Type) ValueType.Id { - return switch (ir_type) { - .scalar => |ir_scalar_type| resolveScalarType(ir_scalar_type), - else => unreachable, - }; -} - -const RegisterSet = AutoArrayHashMap(Register.Physical, void); - -fn getSubregisters(allocator: Allocator, reg: Register.Physical) !RegisterSet { - var result = RegisterSet{}; - - try getSubregistersRecursive(allocator, &result, reg); - - return result; -} - -fn getSubregistersRecursive(allocator: Allocator, set: *RegisterSet, reg: Register.Physical) !void { - if (set.get(reg) == null) { - try set.putNoClobber(allocator, reg, {}); - const register_descriptor = register_descriptors.getPtrConst(reg); - for (register_descriptor.subregisters) |subreg| { - try getSubregistersRecursive(allocator, set, subreg); - } - } -} - -const Encoding = struct { - const GP32 = enum(u4) { - a = 0, - c = 1, - d = 2, - b = 3, - sp = 4, - bp = 5, - si = 6, - di = 7, - r8 = 8, - r9 = 9, - r10 = 10, - r11 = 11, - r12 = 12, - r13 = 13, - r14 = 14, - r15 = 15, - }; - const GP64 = enum(u4) { - a = 0, - c = 1, - d = 2, - b = 3, - sp = 4, - bp = 5, - si = 6, - di = 7, - r8 = 8, - r9 = 9, - r10 = 10, - r11 = 11, - r12 = 12, - r13 = 13, - r14 = 14, - r15 = 15, - }; -}; - -const LiveRegister = struct { - last_use: Instruction.Index = Instruction.Index.invalid, - virtual: Register.Virtual.Index, - physical: Register.Physical = Register.Physical.no_register, - live_out: bool = false, - reloaded: bool = false, -}; diff --git a/bootstrap/data_structures.zig b/bootstrap/data_structures.zig index 131f1a8..d13932c 100644 --- a/bootstrap/data_structures.zig +++ b/bootstrap/data_structures.zig @@ -110,11 +110,6 @@ pub fn BlockList(comptime T: type) type { } }; - pub const Allocation = struct { - ptr: *T, - index: Index, - }; - pub fn iterator(list: *List) Iterator { return .{ .index = Index{ @@ -130,26 +125,22 @@ pub fn BlockList(comptime T: type) type { return &list.blocks.items[index.block].items[index.element]; } - pub fn append(list: *List, allocator: Allocator, element: T) !Allocation { + pub fn append(list: *List, allocator: Allocator, element: T) !Index { const result = try list.addOne(allocator); - result.ptr.* = element; + list.get(result).* = element; return result; } - pub fn addOne(list: *List, allocator: Allocator) !Allocation { + pub fn addOne(list: *List, allocator: Allocator) !Index { try list.ensureCapacity(allocator, list.len + 1); const max_allocation = list.blocks.items.len * item_count; const result = switch (list.len < max_allocation) { true => blk: { const block = &list.blocks.items[list.first_block]; if (block.allocateIndex()) |element_index| { - const ptr = &block.items[element_index]; - break :blk Allocation{ - .ptr = ptr, - .index = .{ - .element = element_index, - .block = @intCast(list.first_block), - }, + break :blk Index{ + .element = element_index, + .block = @intCast(list.first_block), }; } else |_| { @panic("TODO"); @@ -161,13 +152,12 @@ pub fn BlockList(comptime T: type) type { new_block.* = .{}; const element_index = new_block.allocateIndex() catch unreachable; const ptr = &new_block.items[element_index]; + _ = ptr; list.first_block += @intFromBool(block_index != 0); - break :blk Allocation{ - .ptr = ptr, - .index = .{ - .element = element_index, - .block = @intCast(block_index), - }, + + break :blk Index{ + .element = element_index, + .block = @intCast(block_index), }; }, }; diff --git a/bootstrap/frontend/lexical_analyzer.zig b/bootstrap/frontend/lexical_analyzer.zig index d39f5c0..071ecd1 100644 --- a/bootstrap/frontend/lexical_analyzer.zig +++ b/bootstrap/frontend/lexical_analyzer.zig @@ -21,38 +21,12 @@ pub const Token = packed struct(u64) { pub const Id = enum(u8) { eof = 0x00, - identifier = 0x01, - number_literal = 0x02, - string_literal = 0x03, - fixed_keyword_function = 0x04, - fixed_keyword_const = 0x05, - fixed_keyword_var = 0x06, - fixed_keyword_void = 0x07, - fixed_keyword_noreturn = 0x08, - fixed_keyword_comptime = 0x09, - fixed_keyword_while = 0x0a, - fixed_keyword_bool = 0x0b, - fixed_keyword_true = 0x0c, - fixed_keyword_false = 0x0d, - fixed_keyword_fn = 0x0e, - fixed_keyword_unreachable = 0x0f, - fixed_keyword_return = 0x10, - fixed_keyword_ssize = 0x11, - fixed_keyword_usize = 0x12, - fixed_keyword_switch = 0x13, - fixed_keyword_if = 0x14, - fixed_keyword_else = 0x15, - fixed_keyword_struct = 0x16, - fixed_keyword_enum = 0x17, - fixed_keyword_union = 0x18, - fixed_keyword_extern = 0x19, - fixed_keyword_null = 0x1a, - fixed_keyword_align = 0x1b, - fixed_keyword_export = 0x1c, - fixed_keyword_cc = 0x1d, - fixed_keyword_for = 0x1e, - keyword_unsigned_integer = 0x1f, - keyword_signed_integer = 0x20, + keyword_unsigned_integer = 0x01, + keyword_signed_integer = 0x02, + identifier = 0x03, + number_literal = 0x04, + string_literal = 0x05, + discard = 0x06, bang = '!', // 0x21 hash = '#', // 0x23 dollar_sign = '$', // 0x24 @@ -83,6 +57,34 @@ pub const Token = packed struct(u64) { vertical_bar = '|', // 0x7c right_brace = '}', // 0x7d tilde = '~', // 0x7e + fixed_keyword_function = 0x7f, + fixed_keyword_const = 0x80, + fixed_keyword_var = 0x81, + fixed_keyword_void = 0x82, + fixed_keyword_noreturn = 0x83, + fixed_keyword_comptime = 0x84, + fixed_keyword_while = 0x85, + fixed_keyword_bool = 0x86, + fixed_keyword_true = 0x87, + fixed_keyword_false = 0x88, + fixed_keyword_fn = 0x89, + fixed_keyword_unreachable = 0x8a, + fixed_keyword_return = 0x8b, + fixed_keyword_ssize = 0x8c, + fixed_keyword_usize = 0x8d, + fixed_keyword_switch = 0x8e, + fixed_keyword_if = 0x8f, + fixed_keyword_else = 0x90, + fixed_keyword_struct = 0x91, + fixed_keyword_enum = 0x92, + fixed_keyword_union = 0x93, + fixed_keyword_extern = 0x94, + fixed_keyword_null = 0x95, + fixed_keyword_align = 0x96, + fixed_keyword_export = 0x97, + fixed_keyword_cc = 0x98, + fixed_keyword_for = 0x99, + fixed_keyword_undefined = 0x9a, }; pub const Index = u32; @@ -116,6 +118,7 @@ pub const FixedKeyword = enum { @"export", cc, @"for", + undefined, }; pub const Result = struct { @@ -175,9 +178,10 @@ pub fn analyze(allocator: Allocator, text: []const u8, file_index: File.Index) ! } } - break :blk if (enumFromString(FixedKeyword, text[start_index..][0 .. index - start_index])) |fixed_keyword| switch (fixed_keyword) { + const string = text[start_index..][0 .. index - start_index]; + break :blk if (enumFromString(FixedKeyword, string)) |fixed_keyword| switch (fixed_keyword) { inline else => |comptime_fixed_keyword| @field(Token.Id, "fixed_keyword_" ++ @tagName(comptime_fixed_keyword)), - } else .identifier; + } else if (equal(u8, string, "_")) .discard else .identifier; }, '0'...'9' => blk: { // Detect other non-decimal literals diff --git a/bootstrap/frontend/semantic_analyzer.zig b/bootstrap/frontend/semantic_analyzer.zig index 895c332..3f86ea7 100644 --- a/bootstrap/frontend/semantic_analyzer.zig +++ b/bootstrap/frontend/semantic_analyzer.zig @@ -8,6 +8,7 @@ const File = Compilation.File; const Module = Compilation.Module; const Package = Compilation.Package; +const ArrayType = Compilation.ArrayType; const ArgumentList = Compilation.ArgumentList; const Assignment = Compilation.Assignment; const Block = Compilation.Block; @@ -23,6 +24,7 @@ const Scope = Compilation.Scope; const ScopeType = Compilation.ScopeType; const Slice = Compilation.Slice; const Struct = Compilation.Struct; +const StringLiteral = Compilation.StringLiteral; const Type = Compilation.Type; const Value = Compilation.Value; @@ -42,6 +44,7 @@ pub const Logger = enum { scope_lookup, debug, fn_return_type, + address_of, pub var bitset = std.EnumSet(Logger).initMany(&.{ // .type, @@ -52,10 +55,11 @@ pub const Logger = enum { // .typecheck, // .@"switch", // .block, - // .call, + .call, // // .scope_lookup, .debug, .fn_return_type, + .address_of, }); }; @@ -75,6 +79,7 @@ const ExpectType = union(enum) { none, type_index: Type.Index, flexible_integer: FlexibleInteger, + addressable: Type.Index, pub const none = ExpectType{ .none = {}, @@ -96,6 +101,7 @@ const ExpectType = union(enum) { pub var unreachable_index = Value.Index.invalid; pub var optional_null_index = Value.Index.invalid; pub var pointer_null_index = Value.Index.invalid; +pub var undefined_index = Value.Index.invalid; pub var pointer_to_any_type = Type.Index.invalid; pub var optional_pointer_to_any_type = Type.Index.invalid; @@ -112,47 +118,47 @@ const Analyzer = struct { for_count: usize = 0, fn getScopeSourceFile(analyzer: *Analyzer, scope_index: Scope.Index) []const u8 { - const scope = analyzer.module.scopes.get(scope_index); - const file = analyzer.module.files.get(scope.file); + const scope = analyzer.module.values.scopes.get(scope_index); + const file = analyzer.module.values.files.get(scope.file); return file.source_code; } fn getScopeNode(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) Node { - const scope = analyzer.module.scopes.get(scope_index); - const file = analyzer.module.files.get(scope.file); + const scope = analyzer.module.values.scopes.get(scope_index); + const file = analyzer.module.values.files.get(scope.file); const result = &file.syntactic_analyzer_result.nodes.items[node_index.unwrap()]; // logln(.sema, .scope_node, "Fetching node #{} (0x{x}) from scope #{} from file #{} with id: {s}", .{ node_index.uniqueInteger(), @intFromPtr(result), scope_index.uniqueInteger(), scope.file.uniqueInteger(), @tagName(result.id) }); return result.*; } fn getScopeToken(analyzer: *Analyzer, scope_index: Scope.Index, token_index: Token.Index) Token { - const scope = analyzer.module.scopes.get(scope_index); - const file = analyzer.module.files.get(scope.file); + const scope = analyzer.module.values.scopes.get(scope_index); + const file = analyzer.module.values.files.get(scope.file); const result = file.lexical_analyzer_result.tokens.items[token_index]; return result; } fn getScopeNodeList(analyzer: *Analyzer, scope_index: Scope.Index, node: Node) ArrayList(Node.Index) { - const scope = analyzer.module.scopes.get(scope_index); + const scope = analyzer.module.values.scopes.get(scope_index); return getFileNodeList(analyzer, scope.file, node); } fn getFileNodeList(analyzer: *Analyzer, file_index: File.Index, node: Node) ArrayList(Node.Index) { assert(node.id == .node_list); - const file = analyzer.module.files.get(file_index); + const file = analyzer.module.values.files.get(file_index); const list_index = node.left; return file.syntactic_analyzer_result.node_lists.items[list_index.uniqueInteger()]; } fn getFileToken(analyzer: *Analyzer, file_index: File.Index, token: Token.Index) Token { - const file = analyzer.module.files.get(file_index); + const file = analyzer.module.values.files.get(file_index); const result = file.lexical_analyzer_result.tokens.items[token]; return result; } fn getFileNode(analyzer: *Analyzer, file_index: File.Index, node_index: Node.Index) Node { - const file = analyzer.module.files.get(file_index); + const file = analyzer.module.values.files.get(file_index); const result = file.syntactic_analyzer_result.nodes.items[node_index.unwrap()]; return result; } @@ -161,50 +167,48 @@ const Analyzer = struct { const comptime_node = analyzer.getScopeNode(scope_index, node_index); const comptime_block = try analyzer.block(scope_index, .{ .none = {} }, comptime_node.left); - const value_allocation = try analyzer.module.values.append(analyzer.allocator, .{ + const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .block = comptime_block, }); - return value_allocation.index; + return value_index; } - fn unresolvedAllocate(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value.Allocation { - const value_allocation = try analyzer.module.values.addOne(analyzer.allocator); - value_allocation.ptr.* = .{ + fn unresolvedAllocate(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value.Index { + const value_index = try analyzer.module.values.array.addOne(analyzer.allocator); + analyzer.module.values.array.get(value_index).* = .{ .unresolved = .{ .node_index = node_index, }, }; - try analyzer.resolveNode(value_allocation.ptr, scope_index, expect_type, node_index); - return value_allocation; + try analyzer.resolveNode(value_index, scope_index, expect_type, node_index); + return value_index; } fn block(analyzer: *Analyzer, parent_scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) anyerror!Block.Index { - logln(.sema, .block, "Resolving block from scope #{} in file #{}", .{ parent_scope_index.uniqueInteger(), analyzer.module.scopes.get(parent_scope_index).file.uniqueInteger() }); + logln(.sema, .block, "Resolving block from scope #{} in file #{}", .{ parent_scope_index.uniqueInteger(), analyzer.module.values.scopes.get(parent_scope_index).file.uniqueInteger() }); var reaches_end = true; const block_node = analyzer.getScopeNode(parent_scope_index, node_index); const statement_nodes = analyzer.getScopeNodeList(parent_scope_index, analyzer.getScopeNode(parent_scope_index, block_node.left)); - const new_scope = try analyzer.allocateScope(.{ + const scope_index = try analyzer.module.values.scopes.append(analyzer.allocator, .{ .parent = parent_scope_index, - .file = analyzer.module.scopes.get(parent_scope_index).file, + .file = analyzer.module.values.scopes.get(parent_scope_index).file, .token = block_node.token, }); - const scope_index = new_scope.index; - const block_allocation = try analyzer.module.blocks.append(analyzer.allocator, .{ + const block_index = try analyzer.module.values.blocks.append(analyzer.allocator, .{ .statements = ArrayList(Value.Index){}, .reaches_end = true, }); - const block_index = block_allocation.index; const previous_block = analyzer.current_block; analyzer.current_block = block_index; for (analyzer.payloads.items) |payload| { const declaration_index = try analyzer.declarationCommon(scope_index, .local, payload.mutability, payload.name, payload.type, payload.value, null); - const statement_value_allocation = try analyzer.module.values.append(analyzer.allocator, .{ + const statement_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .declaration = declaration_index, }); - try analyzer.module.blocks.get(block_index).statements.append(analyzer.allocator, statement_value_allocation.index); + try analyzer.module.values.blocks.get(block_index).statements.append(analyzer.allocator, statement_value_index); } analyzer.payloads.clearRetainingCapacity(); @@ -226,23 +230,23 @@ const Analyzer = struct { logln(.sema, .node, "Trying to resolve statement of id {s}", .{@tagName(statement_node.id)}); const statement_value_index = switch (statement_node.id) { - .assign, .add_assign => (try analyzer.module.values.append(analyzer.allocator, try analyzer.processAssignment(scope_index, statement_node_index))).index, + .assign, .add_assign => try analyzer.module.values.array.append(analyzer.allocator, try analyzer.processAssignment(scope_index, statement_node_index)), .@"unreachable" => blk: { reaches_end = false; break :blk unreachable_index; }, .simple_symbol_declaration => blk: { const declaration_index = try analyzer.symbolDeclaration(scope_index, statement_node_index, .local); - const declaration = analyzer.module.declarations.get(declaration_index); - const init_value = analyzer.module.values.get(declaration.init_value); + const declaration = analyzer.module.values.declarations.get(declaration_index); + const init_value = analyzer.module.values.array.get(declaration.init_value); switch (init_value.isComptime(analyzer.module) and declaration.mutability == .@"const") { // Dont add comptime declaration statements true => continue, false => { - const statement_value_allocation = try analyzer.module.values.append(analyzer.allocator, .{ + const statement_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .declaration = declaration_index, }); - break :blk statement_value_allocation.index; + break :blk statement_value_index; }, } }, @@ -250,19 +254,19 @@ const Analyzer = struct { reaches_end = false; const return_expresssion = try analyzer.processReturn(scope_index, expect_type, statement_node_index); - const return_value_allocation = try analyzer.module.values.append(analyzer.allocator, return_expresssion); + const return_value_index = try analyzer.module.values.array.append(analyzer.allocator, return_expresssion); - break :blk return_value_allocation.index; + break :blk return_value_index; }, .call => blk: { const call_index = try analyzer.processCall(scope_index, statement_node_index); - const call_statement = try analyzer.module.values.append(analyzer.allocator, .{ + const call_statement_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .call = call_index, }); - if (call_statement.ptr.getType(analyzer.module).eq(Type.noreturn)) { + if (analyzer.getValueType(call_statement_index).eq(Type.noreturn)) { reaches_end = false; } - break :blk call_statement.index; + break :blk call_statement_index; }, // TODO: reaches end switch statement .@"switch" => blk: { @@ -271,21 +275,21 @@ const Analyzer = struct { .@"return" => reaches_end = false, else => {}, } - const switch_value_allocation = try analyzer.module.values.append(analyzer.allocator, switch_value); + const switch_value_index = try analyzer.module.values.array.append(analyzer.allocator, switch_value); - break :blk switch_value_allocation.index; + break :blk switch_value_index; }, .if_else => blk: { const if_else_node_index = statement_node_index; const payload_node_index = Node.Index.invalid; const if_else_value = try analyzer.processIfElse(scope_index, expect_type, if_else_node_index, payload_node_index); - const branch = analyzer.module.branches.get(if_else_value.branch); + const branch = analyzer.module.values.branches.get(if_else_value.branch); reaches_end = branch.reaches_end; assert(if_else_value.maybe_payload_declaration_index == null); - const branch_value = try analyzer.module.values.append(analyzer.allocator, .{ + const branch_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .branch = if_else_value.branch, }); - break :blk branch_value.index; + break :blk branch_value_index; }, .if_else_payload => blk: { const if_else_node_index = statement_node.left; @@ -293,15 +297,15 @@ const Analyzer = struct { const if_else_value = try analyzer.processIfElse(scope_index, expect_type, if_else_node_index, payload_node_index); if (if_else_value.maybe_payload_declaration_index) |maybe_payload_declaration| { - try analyzer.module.blocks.get(block_index).statements.append(analyzer.allocator, maybe_payload_declaration); + try analyzer.module.values.blocks.get(block_index).statements.append(analyzer.allocator, maybe_payload_declaration); } - const branch = analyzer.module.branches.get(if_else_value.branch); + const branch = analyzer.module.values.branches.get(if_else_value.branch); reaches_end = branch.reaches_end; - const branch_statement = try analyzer.module.values.append(analyzer.allocator, .{ + const branch_statement_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .branch = if_else_value.branch, }); - break :blk branch_statement.index; + break :blk branch_statement_index; }, .@"if", .if_payload, @@ -321,44 +325,44 @@ const Analyzer = struct { const if_expression = try analyzer.processIf(scope_index, expect_type, if_statement_node_index, payload_node_index); if (if_expression.maybe_payload_declaration_value) |maybe_payload_declaration| { - try analyzer.module.blocks.get(block_index).statements.append(analyzer.allocator, maybe_payload_declaration); + try analyzer.module.values.blocks.get(block_index).statements.append(analyzer.allocator, maybe_payload_declaration); } - const branch = try analyzer.module.branches.append(analyzer.allocator, .{ + const branch_index = try analyzer.module.values.branches.append(analyzer.allocator, .{ .expression = if_expression.expression, .taken_expression = if_expression.taken_expression, .not_taken_expression = Value.Index.invalid, .reaches_end = true, // The else branch, as it doesnt exist, always reaches the end }); - const branch_statement = try analyzer.module.values.append(analyzer.allocator, .{ - .branch = branch.index, + const branch_statement_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .branch = branch_index, }); - break :blk branch_statement.index; + break :blk branch_statement_index; }, .compiler_intrinsic => blk: { const intrinsic_value = try analyzer.compilerIntrinsic(scope_index, ExpectType.none, statement_node_index); - const value = try analyzer.module.values.append(analyzer.allocator, intrinsic_value); - break :blk value.index; + const value_index = try analyzer.module.values.array.append(analyzer.allocator, intrinsic_value); + break :blk value_index; }, .assembly_block => blk: { const assembly_value = try analyzer.assembly(scope_index, ExpectType.none, statement_node_index); - const value = try analyzer.module.values.append(analyzer.allocator, assembly_value); - break :blk value.index; + const value_index = try analyzer.module.values.array.append(analyzer.allocator, assembly_value); + break :blk value_index; }, .for_loop => blk: { const loop_index = try analyzer.forLoop(scope_index, expect_type, statement_node_index); - const value = try analyzer.module.values.append(analyzer.allocator, .{ + const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .loop = loop_index, }); - break :blk value.index; + break :blk value_index; }, else => |t| @panic(@tagName(t)), }; - try analyzer.module.blocks.get(block_index).statements.append(analyzer.allocator, statement_value_index); + try analyzer.module.values.blocks.get(block_index).statements.append(analyzer.allocator, statement_value_index); } - analyzer.module.blocks.get(block_index).reaches_end = reaches_end; + analyzer.module.values.blocks.get(block_index).reaches_end = reaches_end; analyzer.current_block = previous_block; @@ -410,11 +414,11 @@ const Analyzer = struct { operand_list.appendAssumeCapacity(operand); } - const assembly_instruction = try analyzer.module.assembly_instructions.append(analyzer.allocator, .{ + const assembly_instruction_index = try analyzer.module.values.assembly_instructions.append(analyzer.allocator, .{ .id = @intFromEnum(instruction), .operands = operand_list.items, }); - instructions.appendAssumeCapacity(assembly_instruction.index); + instructions.appendAssumeCapacity(assembly_instruction_index); } return instructions.items; @@ -431,42 +435,43 @@ const Analyzer = struct { .x86_64 => try analyzer.processAssemblyStatements(Compilation.Assembly.x86_64, scope_index, assembly_statement_nodes.items), else => unreachable, }; - const assembly_block = try analyzer.module.assembly_blocks.append(analyzer.allocator, .{ + const assembly_block_index = try analyzer.module.values.assembly_blocks.append(analyzer.allocator, .{ .instructions = assembly_statements, }); return .{ - .assembly_block = assembly_block.index, + .assembly_block = assembly_block_index, }; } fn processCallToFunctionPrototype(analyzer: *Analyzer, scope_index: Scope.Index, function_prototype_index: Function.Prototype.Index, call_argument_node_list: []const Node.Index, method_object: Value.Index) !ArrayList(Value.Index) { - const function_prototype = analyzer.module.function_prototypes.get(function_prototype_index); + const function_prototype = analyzer.module.types.function_prototypes.get(function_prototype_index); const method_object_count = @intFromBool(!method_object.invalid); const call_argument_count = call_argument_node_list.len + method_object_count; var argument_array = try ArrayList(Value.Index).initCapacity(analyzer.allocator, call_argument_count); + logln(.sema, .call, "Method object valid: {}", .{!method_object.invalid}); if (!method_object.invalid) { const first_argument_index = function_prototype.arguments.?[0]; - const first_argument = analyzer.module.declarations.get(first_argument_index); + const first_argument = analyzer.module.values.declarations.get(first_argument_index); const first_argument_type = first_argument.type; - const method_object_value = analyzer.module.values.get(method_object); + const method_object_value = analyzer.module.values.array.get(method_object); const method_object_type = method_object_value.getType(analyzer.module); // TODO: further typecheck - const method_object_argument = switch (analyzer.module.types.get(first_argument_type).*) { - .pointer => switch (analyzer.module.types.get(method_object_type).*) { + const method_object_argument = switch (analyzer.module.types.array.get(first_argument_type).*) { + .pointer => switch (analyzer.module.types.array.get(method_object_type).*) { .pointer => method_object, else => blk: { - const unary = try analyzer.module.unary_operations.append(analyzer.allocator, .{ + const unary_index = try analyzer.module.values.unary_operations.append(analyzer.allocator, .{ .id = .address_of, .value = method_object, .type = first_argument_type, }); - const address_of = try analyzer.module.values.append(analyzer.allocator, .{ - .unary_operation = unary.index, + const address_of_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .unary_operation = unary_index, }); - break :blk address_of.index; + break :blk address_of_index; }, }, else => method_object, @@ -481,7 +486,7 @@ const Analyzer = struct { if (argument_declarations.len == call_argument_count) { for (argument_declarations[method_object_count..], call_argument_node_list, 0..) |argument_declaration_index, argument_node_index, _index| { const index = _index + method_object_count; - const argument_declaration = analyzer.module.declarations.get(argument_declaration_index); + const argument_declaration = analyzer.module.values.declarations.get(argument_declaration_index); const argument_node = analyzer.getScopeNode(scope_index, argument_node_index); const value_node_index = switch (argument_node.id) { .identifier => blk: { @@ -493,7 +498,7 @@ const Analyzer = struct { } else { const call_site_name = analyzer.module.getName(identifier_hash).?; const definition_site_name = analyzer.module.getName(argument_declaration.name).?; - // const function_name = analyzer.module.getName(analyzer.module.function_name_map.get(function_index).?).?; + // const function_name = analyzer.module.getName(analyzer.module.types.function_name_map.get(function_index).?).?; std.debug.panic("At function call, argument #{} must be named the same way. Call site was name '{s}' while function definition has it named as '{s}'", .{ index, call_site_name, definition_site_name }); } }, @@ -510,27 +515,27 @@ const Analyzer = struct { } else { const call_site_name = analyzer.module.getName(identifier_hash).?; const definition_site_name = analyzer.module.getName(argument_declaration.name).?; - // const function_name = analyzer.module.getName(analyzer.module.function_name_map.get(function_index).?).?; + // const function_name = analyzer.module.getName(analyzer.module.types.function_name_map.get(function_index).?).?; std.debug.panic("At function call, argument #{} must be named the same way. Call site was name '{s}' while function definition has it named as '{s}'", .{ index, call_site_name, definition_site_name }); } }, else => |node_id| { const definition_site_name = analyzer.module.getName(argument_declaration.name).?; - // const function_name = analyzer.module.getName(analyzer.module.function_name_map.get(function_index).?).?; + // const function_name = analyzer.module.getName(analyzer.module.types.function_name_map.get(function_index).?).?; std.debug.panic("Argument #{} of call to function of type {s} must be named as '{s}'", .{ index, @tagName(node_id), definition_site_name }); }, }; - const call_argument_allocation = try analyzer.unresolvedAllocate(scope_index, ExpectType{ + const call_argument_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType{ .type_index = argument_declaration.type, }, value_node_index); - const call_site_type = call_argument_allocation.ptr.getType(analyzer.module); + const call_site_type = analyzer.getValueType(call_argument_value_index); const result = try analyzer.typeCheck(ExpectType{ .type_index = argument_declaration.type, }, call_site_type); assert(result == .success); - argument_array.appendAssumeCapacity(call_argument_allocation.index); + argument_array.appendAssumeCapacity(call_argument_value_index); } } else { panic("{s} call has argument count mismatch: call has {}, function declaration has {}", .{ switch (method_object.invalid) { @@ -558,25 +563,25 @@ const Analyzer = struct { .field_access => true, else => false, }; - const this_value_allocation = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, member_or_namespace_node_index); - break :blk this_value_allocation.index; + const this_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, member_or_namespace_node_index); + break :blk this_value_index; }, false => unreachable, //Value.Index.invalid, }; const left_type = switch (left_value_index.invalid) { - false => switch (analyzer.module.values.get(left_value_index).*) { - .function_definition => |function_index| analyzer.module.function_prototypes.get(analyzer.module.types.get(analyzer.module.function_definitions.get(function_index).prototype).function).return_type, + false => switch (analyzer.module.values.array.get(left_value_index).*) { + .function_definition => |function_index| analyzer.module.types.function_prototypes.get(analyzer.module.types.array.get(analyzer.module.types.function_definitions.get(function_index).prototype).function).return_type, .field_access => |field_access_index| blk: { - const field_access_type_index = analyzer.module.container_fields.get(analyzer.module.field_accesses.get(field_access_index).field).type; - const field_access_type = analyzer.module.types.get(field_access_type_index); + const field_access_type_index = analyzer.module.types.container_fields.get(analyzer.module.values.field_accesses.get(field_access_index).field).type; + const field_access_type = analyzer.module.types.array.get(field_access_type_index); break :blk switch (field_access_type.*) { .pointer => |pointer| b: { assert(!pointer.many); assert(pointer.@"const"); - const appointee_type = analyzer.module.types.get(pointer.element_type); + const appointee_type = analyzer.module.types.array.get(pointer.element_type); break :b switch (appointee_type.*) { - .function => |function_prototype_index| analyzer.module.function_prototypes.get(function_prototype_index).return_type, + .function => |function_prototype_index| analyzer.module.types.function_prototypes.get(function_prototype_index).return_type, else => |t| @panic(@tagName(t)), }; }, @@ -589,27 +594,32 @@ const Analyzer = struct { }; const arguments_index = switch (node.id) { - .call => (try analyzer.module.argument_lists.append(analyzer.allocator, .{ + .call => try analyzer.module.values.argument_lists.append(analyzer.allocator, .{ .array = b: { const argument_list_node_index = node.right; const call_argument_node_list = analyzer.getScopeNodeList(scope_index, analyzer.getScopeNode(scope_index, argument_list_node_index)); - switch (analyzer.module.values.get(left_value_index).*) { + switch (analyzer.module.values.array.get(left_value_index).*) { .function_definition => |function_index| { - const function_definition = analyzer.module.function_definitions.get(function_index); - const function_prototype_index = analyzer.module.types.get(function_definition.prototype).function; + const function_definition = analyzer.module.types.function_definitions.get(function_index); + const function_prototype_index = analyzer.module.types.array.get(function_definition.prototype).function; - const method_object = switch (is_field_access) { + logln(.sema, .call, "Is field access: {}", .{is_field_access}); + const method_object: Value.Index = switch (is_field_access) { true => mob: { const field_access_node = analyzer.getScopeNode(scope_index, node.left); assert(field_access_node.id == .field_access); - const maybe_left_value = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, field_access_node.left); - break :mob switch (analyzer.module.types.get(maybe_left_value.ptr.getType(analyzer.module)).*) { + const maybe_left_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, field_access_node.left); + const left_value_type_index = analyzer.getValueType(maybe_left_value_index); + const left_value_type = analyzer.module.types.array.get(left_value_type_index); + logln(.sema, .call, "Left value type: {}", .{left_value_type}); + break :mob switch (left_value_type.*) { .type => Value.Index.invalid, - .@"struct" => switch (analyzer.module.values.get(left_value_index).*) { - .function_definition => maybe_left_value.index, + .@"struct" => switch (analyzer.module.values.array.get(left_value_index).*) { + .function_definition => maybe_left_value_index, else => |t| @panic(@tagName(t)), }, + .pointer => maybe_left_value_index, // .field_access => maybe_left_value.index, else => |t| @panic(@tagName(t)), }; @@ -619,9 +629,9 @@ const Analyzer = struct { break :b try analyzer.processCallToFunctionPrototype(scope_index, function_prototype_index, call_argument_node_list.items, method_object); }, .field_access => |field_access_index| { - const field_access = analyzer.module.field_accesses.get(field_access_index); - const container_field = analyzer.module.container_fields.get(field_access.field); - const container_field_type = analyzer.module.types.get(container_field.type); + const field_access = analyzer.module.values.field_accesses.get(field_access_index); + const container_field = analyzer.module.types.container_fields.get(field_access.field); + const container_field_type = analyzer.module.types.array.get(container_field.type); switch (container_field_type.*) { .pointer => |function_pointer| { if (!function_pointer.@"const") { @@ -632,7 +642,7 @@ const Analyzer = struct { unreachable; } - const appointee_type = analyzer.module.types.get(function_pointer.element_type); + const appointee_type = analyzer.module.types.array.get(function_pointer.element_type); switch (appointee_type.*) { .function => |function_prototype_index| { break :b try analyzer.processCallToFunctionPrototype(scope_index, function_prototype_index, call_argument_node_list.items, Value.Index.invalid); @@ -646,18 +656,18 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), } }, - })).index, + }), else => |t| @panic(@tagName(t)), }; - const call_allocation = try analyzer.module.calls.append(analyzer.allocator, .{ + const call_index = try analyzer.module.values.calls.append(analyzer.allocator, .{ .value = left_value_index, .arguments = arguments_index, .type = left_type, }); - return call_allocation.index; + return call_index; } fn typeCheckEnumLiteral(analyzer: *Analyzer, scope_index: Scope.Index, token_index: Token.Index, enum_type: *const Enum) !?Enum.Field.Index { @@ -665,7 +675,7 @@ const Analyzer = struct { const enum_name_hash = try analyzer.processIdentifier(enum_name); for (enum_type.fields.items) |enum_field_index| { - const enum_field = analyzer.module.enum_fields.get(enum_field_index); + const enum_field = analyzer.module.types.enum_fields.get(enum_field_index); const existing = analyzer.module.getName(enum_field.name).?; if (enum_field.name == enum_name_hash) { return enum_field_index; @@ -678,23 +688,22 @@ const Analyzer = struct { } fn processSwitch(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value { - _ = expect_type; const node = analyzer.getScopeNode(scope_index, node_index); assert(node.id == .@"switch"); // analyzer.debugNode(scope_index, node_index); - const switch_expr = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); + const switch_expression_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); const switch_case_list_node = analyzer.getScopeNode(scope_index, node.right); const switch_case_node_list = switch (switch_case_list_node.id) { .node_list => analyzer.getScopeNodeList(scope_index, switch_case_list_node).items, else => |t| @panic(@tagName(t)), }; - switch (switch_expr.ptr.*) { + switch (analyzer.module.values.array.get(switch_expression_value_index).*) { .enum_field => |e_field_index| { - const e_field = analyzer.module.enum_fields.get(e_field_index); - const enum_type = analyzer.module.enums.get(e_field.parent); + const e_field = analyzer.module.types.enum_fields.get(e_field_index); + const enum_type = analyzer.module.types.enums.get(e_field.parent); const enum_field_name = analyzer.module.getName(e_field.name); _ = enum_field_name; @@ -779,15 +788,9 @@ const Analyzer = struct { logln(.sema, .@"switch", "Index: {}", .{group_index}); const true_switch_case_node = analyzer.getScopeNode(scope_index, switch_case_node_list[group_index]); - var result = Value{ - .unresolved = .{ - .node_index = true_switch_case_node.right, - }, - }; + const result_index = try analyzer.unresolvedAllocate(scope_index, expect_type, true_switch_case_node.right); - try analyzer.resolveNode(&result, scope_index, ExpectType.none, true_switch_case_node.right); - - return result; + return analyzer.module.values.array.get(result_index).*; }, else => |t| @panic(@tagName(t)), } @@ -803,25 +806,35 @@ const Analyzer = struct { .type_index = Type.usize, }; - const range_start = try analyzer.unresolvedAllocate(scope_index, expect_type, range_node.left); - const range_end = try analyzer.unresolvedAllocate(scope_index, expect_type, range_node.right); + const range_start_index = try analyzer.unresolvedAllocate(scope_index, expect_type, range_node.left); + const range_end_index = try analyzer.unresolvedAllocate(scope_index, expect_type, range_node.right); return Range{ - .start = range_start.index, - .end = range_end.index, + .start = range_start_index, + .end = range_end_index, }; } + fn getPayloadName(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index) ?[]const u8 { + const payload_node = analyzer.getScopeNode(scope_index, node_index); + const maybe_payload_name: ?[]const u8 = switch (payload_node.id) { + .identifier => analyzer.tokenIdentifier(scope_index, payload_node.token), + .discard => null, + else => |t| @panic(@tagName(t)), + }; + return maybe_payload_name; + } + fn forLoop(analyzer: *Analyzer, parent_scope_index: Scope.Index, expect_type: ExpectType, for_node_index: Node.Index) !Loop.Index { const for_loop_node = analyzer.getScopeNode(parent_scope_index, for_node_index); assert(for_loop_node.id == .for_loop); - const scope_allocation = try analyzer.allocateScope(.{ + const scope_index = try analyzer.module.values.scopes.append(analyzer.allocator, .{ .token = for_loop_node.token, - .file = analyzer.module.scopes.get(parent_scope_index).file, + .file = analyzer.module.values.scopes.get(parent_scope_index).file, .parent = parent_scope_index, }); - const scope_index = scope_allocation.index; + const for_condition_node = analyzer.getScopeNode(scope_index, for_loop_node.left); assert(for_condition_node.id == .for_condition); @@ -832,23 +845,23 @@ const Analyzer = struct { const for_range = try analyzer.range(scope_index, for_condition_node.left); const for_loop_payload_node = analyzer.getScopeNode(scope_index, for_condition_node.right); - const payload_name = switch (for_loop_payload_node.id) { + const maybe_payload_name = switch (for_loop_payload_node.id) { .node_list => b: { const nodes = analyzer.getScopeNodeList(scope_index, for_loop_payload_node); assert(nodes.items.len == 1); - const payload_node = analyzer.getScopeNode(scope_index, nodes.items[0]); - const result = analyzer.tokenIdentifier(scope_index, payload_node.token); - break :b result; + const payload_node_index = nodes.items[0]; + break :b analyzer.getPayloadName(scope_index, payload_node_index); }, else => |t| @panic(@tagName(t)), }; + const payload_name = if (maybe_payload_name) |name| name else "_"; const declaration_index = try analyzer.declarationCommon(scope_index, .local, .@"var", payload_name, Type.usize, for_range.start, null); - const declaration_value = try analyzer.module.values.append(analyzer.allocator, .{ + const declaration_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .declaration = declaration_index, }); - pre = declaration_value.index; + pre = declaration_value_index; - const binary_condition = try analyzer.module.binary_operations.append(analyzer.allocator, .{ + const binary_condition_index = try analyzer.module.values.binary_operations.append(analyzer.allocator, .{ .id = .compare_less_than, .type = Type.boolean, .left = try analyzer.doIdentifierString(scope_index, ExpectType{ @@ -857,24 +870,24 @@ const Analyzer = struct { .right = for_range.end, }); - const condition = try analyzer.module.values.append(analyzer.allocator, .{ - .binary_operation = binary_condition.index, + const condition_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .binary_operation = binary_condition_index, }); - break :blk condition.index; + break :blk condition_index; }, else => |t| @panic(@tagName(t)), }; - const for_loop_body = try analyzer.unresolvedAllocate(scope_index, expect_type, for_loop_node.right); + const for_loop_body_index = try analyzer.unresolvedAllocate(scope_index, expect_type, for_loop_node.right); var post = Value.Index.invalid; switch (for_loop_element_node.id) { .range => { - const for_condition_value = analyzer.module.values.get(for_condition); + const for_condition_value = analyzer.module.values.array.get(for_condition); switch (for_condition_value.*) { .binary_operation => |binary_operation_index| { - const binary_operation = analyzer.module.binary_operations.get(binary_operation_index); - const left = binary_operation.left; - const right = try analyzer.module.values.append(analyzer.allocator, .{ + const binary_operation = analyzer.module.values.binary_operations.get(binary_operation_index); + const left_index = binary_operation.left; + const right_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .integer = .{ .value = 1, .type = Type.usize, @@ -882,16 +895,16 @@ const Analyzer = struct { }, }); - const assignment = try analyzer.module.assignments.append(analyzer.allocator, .{ + const assignment_index = try analyzer.module.values.assignments.append(analyzer.allocator, .{ .operation = .add, - .destination = left, - .source = right.index, + .destination = left_index, + .source = right_index, }); - const assignment_value = try analyzer.module.values.append(analyzer.allocator, .{ - .assign = assignment.index, + const assignment_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .assign = assignment_index, }); - post = assignment_value.index; + post = assignment_value_index; }, else => |t| @panic(@tagName(t)), } @@ -899,20 +912,20 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), } - const reaches_end = switch (for_loop_body.ptr.*) { - .block => |block_index| analyzer.module.blocks.get(block_index).reaches_end, + const reaches_end = switch (analyzer.module.values.array.get(for_loop_body_index).*) { + .block => |block_index| analyzer.module.values.blocks.get(block_index).reaches_end, else => |t| @panic(@tagName(t)), }; - const loop = try analyzer.module.loops.append(analyzer.allocator, .{ + const loop_index = try analyzer.module.values.loops.append(analyzer.allocator, .{ .pre = pre, .condition = for_condition, - .body = for_loop_body.index, + .body = for_loop_body_index, .reaches_end = reaches_end, .post = post, }); - return loop.index; + return loop_index; } const If = struct { @@ -933,7 +946,7 @@ const Analyzer = struct { const if_branch_node = analyzer.getScopeNode(scope_index, if_node_index); // analyzer.debugNode(scope_index, if_node_index); assert(if_branch_node.id == .@"if"); - var if_expression = try analyzer.unresolvedAllocate(scope_index, ExpectType{ + var if_expression_index = try analyzer.unresolvedAllocate(scope_index, ExpectType{ .type_index = switch (payload_node_index.invalid) { true => Type.boolean, false => optional_any, @@ -941,80 +954,79 @@ const Analyzer = struct { }, if_branch_node.left); const maybe_payload_declaration_value_index: ?Value.Index = if (!payload_node_index.invalid) blk: { - const if_type_index = if_expression.ptr.getType(analyzer.module); + const if_type_index = analyzer.getValueType(if_expression_index); logln(.sema, .fn_return_type, "If condition expression has type #{}", .{if_type_index.uniqueInteger()}); - const if_type = analyzer.module.types.get(if_type_index); + const if_type = analyzer.module.types.array.get(if_type_index); assert(if_type.* == .optional); const payload_type_index = if_type.optional.element_type; - const payload_node = analyzer.getScopeNode(scope_index, payload_node_index); - const payload_name = analyzer.tokenIdentifier(scope_index, payload_node.token); + const maybe_payload_name = analyzer.getPayloadName(scope_index, payload_node_index); - switch (analyzer.module.types.get(if_expression.ptr.getType(analyzer.module)).*) { + switch (analyzer.module.types.array.get(if_type_index).*) { .optional => {}, else => |t| @panic(@tagName(t)), } - const is_discard = equal(u8, payload_name, "_"); - const result: ?Value.Index = if (is_discard) null else b: { + const result: ?Value.Index = if (maybe_payload_name) |payload_name| b: { // TODO: const maybe_payload_declaration_index = try analyzer.declarationCommon(scope_index, .local, .@"const", try std.fmt.allocPrint(analyzer.allocator, "maybe_{}_{s}", .{ maybe: { const r = analyzer.maybe_count; analyzer.maybe_count += 1; break :maybe r; - }, payload_name }), if_type_index, if_expression.index, null); - const maybe_payload_declaration_value = try analyzer.module.values.append(analyzer.allocator, .{ + }, payload_name }), if_type_index, if_expression_index, null); + const maybe_payload_declaration_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .declaration = maybe_payload_declaration_index, }); - if_expression = try analyzer.module.values.append(analyzer.allocator, .{ + if_expression_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .declaration_reference = .{ .value = maybe_payload_declaration_index, .type = if_type_index, }, }); - break :b maybe_payload_declaration_value.index; - }; + break :b maybe_payload_declaration_value_index; + } else null; - const if_expression_before_optional_check = if_expression.index; + const if_expression_before_optional_check = if_expression_index; - const optional_check = try analyzer.module.optional_checks.append(analyzer.allocator, .{ - .value = if_expression.index, - }); - if_expression = try analyzer.module.values.append(analyzer.allocator, .{ - .optional_check = optional_check.index, + const optional_check_index = try analyzer.module.values.optional_checks.append(analyzer.allocator, .{ + .value = if_expression_index, }); - if (!is_discard) { - const optional_unwrap = try analyzer.module.optional_unwraps.append(analyzer.allocator, .{ + if_expression_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .optional_check = optional_check_index, + }); + + if (maybe_payload_name) |payload_name| { + const optional_unwrap_index = try analyzer.module.values.optional_unwraps.append(analyzer.allocator, .{ .value = if_expression_before_optional_check, }); - const payload_value = try analyzer.module.values.append(analyzer.allocator, .{ - .optional_unwrap = optional_unwrap.index, + const payload_value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .optional_unwrap = optional_unwrap_index, }); try analyzer.payloads.append(analyzer.allocator, Payload{ .name = payload_name, .mutability = .@"const", .type = payload_type_index, - .value = payload_value.index, + .value = payload_value_index, }); } break :blk result; } else null; - const taken_expression = try analyzer.unresolvedAllocate(scope_index, expect_type, if_branch_node.right); + const taken_expression_index = try analyzer.unresolvedAllocate(scope_index, expect_type, if_branch_node.right); - const true_reaches_end = switch (taken_expression.ptr.*) { - .block => |block_index| analyzer.module.blocks.get(block_index).reaches_end, + const true_reaches_end = switch (analyzer.module.values.array.get(taken_expression_index).*) { + .block => |block_index| analyzer.module.values.blocks.get(block_index).reaches_end, else => |t| @panic(@tagName(t)), }; const if_result = If{ .maybe_payload_declaration_value = maybe_payload_declaration_value_index, - .expression = if_expression.index, - .taken_expression = taken_expression.index, + .expression = if_expression_index, + .taken_expression = taken_expression_index, .reaches_end = true_reaches_end, }; return if_result; @@ -1032,23 +1044,23 @@ const Analyzer = struct { assert(!node.right.invalid); const if_result = try analyzer.processIf(scope_index, expect_type, node.left, payload_node_index); - const not_taken_expression = try analyzer.unresolvedAllocate(scope_index, expect_type, node.right); - const false_reaches_end = switch (not_taken_expression.ptr.*) { - .block => |block_index| analyzer.module.blocks.get(block_index).reaches_end, + const not_taken_expression_index = try analyzer.unresolvedAllocate(scope_index, expect_type, node.right); + const false_reaches_end = switch (analyzer.module.values.array.get(not_taken_expression_index).*) { + .block => |block_index| analyzer.module.values.blocks.get(block_index).reaches_end, else => |t| @panic(@tagName(t)), }; const reaches_end = if_result.reaches_end and false_reaches_end; - const branch = try analyzer.module.branches.append(analyzer.allocator, .{ + const branch_index = try analyzer.module.values.branches.append(analyzer.allocator, .{ .expression = if_result.expression, .taken_expression = if_result.taken_expression, - .not_taken_expression = not_taken_expression.index, + .not_taken_expression = not_taken_expression_index, .reaches_end = reaches_end, }); return IfElseResult{ .maybe_payload_declaration_index = if_result.maybe_payload_declaration_value, - .branch = branch.index, + .branch = branch_index, }; } @@ -1059,30 +1071,25 @@ const Analyzer = struct { switch (left_node.id) { .discard => { assert(node.id == .assign); - var result = Value{ - .unresolved = .{ - .node_index = node.right, - }, - }; - try analyzer.resolveNode(&result, scope_index, ExpectType.none, node.right); + const result = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.right); - return result; + return analyzer.module.values.array.get(result).*; }, else => { // const id = analyzer.tokenIdentifier(.token); // logln("id: {s}", .{id}); const left = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); const right = try analyzer.unresolvedAllocate(scope_index, ExpectType{ - .type_index = left.ptr.getType(analyzer.module), + .type_index = analyzer.getValueType(left), }, node.right); - if (left.ptr.isComptime(analyzer.module) and right.ptr.isComptime(analyzer.module)) { + if (analyzer.module.values.array.get(left).isComptime(analyzer.module) and analyzer.module.values.array.get(right).isComptime(analyzer.module)) { unreachable; } else { - const assignment = try analyzer.module.assignments.append(analyzer.allocator, .{ - .destination = left.index, - .source = right.index, + const assignment_index = try analyzer.module.values.assignments.append(analyzer.allocator, .{ + .destination = left, + .source = right, .operation = switch (node.id) { .assign => .none, .add_assign => .add, @@ -1091,7 +1098,7 @@ const Analyzer = struct { }); return Value{ - .assign = assignment.index, + .assign = assignment_index, }; } }, @@ -1100,27 +1107,21 @@ const Analyzer = struct { fn processReturn(analyzer: *Analyzer, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) !Value { const node = analyzer.getScopeNode(scope_index, node_index); - const return_expression: Value.Index = switch (node_index.invalid) { + const return_value: Value.Index = switch (node_index.invalid) { // TODO: expect type false => ret: { - const return_value_allocation = try analyzer.module.values.addOne(analyzer.allocator); - return_value_allocation.ptr.* = .{ - .unresolved = .{ - .node_index = node.left, - }, - }; - try analyzer.resolveNode(return_value_allocation.ptr, scope_index, expect_type, node.left); - break :ret return_value_allocation.index; + const return_value_index = try analyzer.unresolvedAllocate(scope_index, expect_type, node.left); + break :ret return_value_index; }, true => @panic("TODO: ret void"), }; - const return_value_allocation = try analyzer.module.returns.append(analyzer.allocator, .{ - .value = return_expression, + const return_expression_index = try analyzer.module.values.returns.append(analyzer.allocator, .{ + .value = return_value, }); return .{ - .@"return" = return_value_allocation.index, + .@"return" = return_expression_index, }; } @@ -1153,7 +1154,7 @@ const Analyzer = struct { else => expect_type, }; - const left_allocation = try analyzer.unresolvedAllocate(scope_index, left_expect_type, node.left); + const left_index = try analyzer.unresolvedAllocate(scope_index, left_expect_type, node.left); const right_expect_type: ExpectType = switch (binary_operation_id) { .add, .sub, @@ -1174,17 +1175,15 @@ const Analyzer = struct { .compare_greater_than, .compare_less_or_equal, => ExpectType{ - .type_index = left_allocation.ptr.getType(analyzer.module), + .type_index = analyzer.getValueType(left_index), }, }; - const right_allocation = try analyzer.unresolvedAllocate(scope_index, right_expect_type, node.right); - const left_type = left_allocation.ptr.getType(analyzer.module); - // const right_type = right_allocation.ptr.getType(analyzer.module); - // _ = right_type; + const left_type = analyzer.getValueType(left_index); + const right_index = try analyzer.unresolvedAllocate(scope_index, right_expect_type, node.right); - const binary_operation = try analyzer.module.binary_operations.append(analyzer.allocator, .{ - .left = left_allocation.index, - .right = right_allocation.index, + const binary_operation_index = try analyzer.module.values.binary_operations.append(analyzer.allocator, .{ + .left = left_index, + .right = right_index, .type = switch (expect_type) { .none => switch (binary_operation_id) { .bit_and => left_type, @@ -1197,7 +1196,7 @@ const Analyzer = struct { }); return .{ - .binary_operation = binary_operation.index, + .binary_operation = binary_operation_index, }; } @@ -1207,22 +1206,22 @@ const Analyzer = struct { }; fn getScopeSlice(analyzer: *Analyzer, scope_index: Scope.Index) []const u8 { - const scope = analyzer.module.scopes.get(scope_index); + const scope = analyzer.module.values.scopes.get(scope_index); return analyzer.getScopeSliceCustomToken(scope_index, scope.token); } fn getScopeSliceCustomToken(analyzer: *Analyzer, scope_index: Scope.Index, custom_token_index: Token.Index) []const u8 { - const scope = analyzer.module.scopes.get(scope_index); + const scope = analyzer.module.values.scopes.get(scope_index); const token = analyzer.getFileToken(scope.file, custom_token_index); - const scope_slice = analyzer.module.files.get(scope.file).source_code[token.start..]; + const scope_slice = analyzer.module.values.files.get(scope.file).source_code[token.start..]; return scope_slice; } fn lookupDeclarationInCurrentAndParentScopes(analyzer: *Analyzer, scope_index: Scope.Index, identifier_hash: u32) ?DeclarationLookup { var scope_iterator = scope_index; while (!scope_iterator.invalid) { - const scope = analyzer.module.scopes.get(scope_iterator); + const scope = analyzer.module.values.scopes.get(scope_iterator); if (Logger.bitset.contains(.scope_lookup)) { const scope_slice = analyzer.getScopeSlice(scope_iterator); logln(.sema, .scope_lookup, "Searching for identifier 0x{x} in scope #{}:\n```{s}\n```", .{ identifier_hash, scope_iterator.uniqueInteger(), scope_slice }); @@ -1249,30 +1248,29 @@ const Analyzer = struct { if (analyzer.lookupDeclarationInCurrentAndParentScopes(scope_index, identifier_hash)) |lookup| { const declaration_index = lookup.declaration; - const declaration = analyzer.module.declarations.get(declaration_index); + const declaration = analyzer.module.values.declarations.get(declaration_index); // Up until now, only arguments have no initialization value const typecheck_result = switch (declaration.init_value.invalid) { false => blk: { - const init_value = analyzer.module.values.get(declaration.init_value); // logln(.sema, .identifier, "Declaration found: {}", .{init_value}); - switch (init_value.*) { - .unresolved => { + switch (analyzer.module.values.array.get(declaration.init_value).*) { + .unresolved => |unresolved| { const previous_declaration = analyzer.current_declaration; analyzer.current_declaration = declaration_index; - try analyzer.resolveNode(init_value, lookup.scope, expect_type, init_value.unresolved.node_index); + try analyzer.resolveNode(declaration.init_value, lookup.scope, expect_type, unresolved.node_index); analyzer.current_declaration = previous_declaration; - switch (init_value.*) { + switch (analyzer.module.values.array.get(declaration.init_value).*) { .function_definition => |function_index| { - const function_definition = analyzer.module.function_definitions.get(function_index); - const function_prototype = analyzer.module.types.get(function_definition.prototype); + const function_definition = analyzer.module.types.function_definitions.get(function_index); + const function_prototype = analyzer.module.types.array.get(function_definition.prototype); const return_type_index = analyzer.functionPrototypeReturnType(function_prototype.function); logln(.sema, .fn_return_type, "Function {s} has return type #{}", .{ analyzer.module.getName(declaration.name).?, return_type_index.uniqueInteger() }); - try analyzer.module.function_map.put(analyzer.allocator, function_index, declaration_index); + try analyzer.module.map.functions.put(analyzer.allocator, function_index, declaration_index); }, .type => |type_index| { - try analyzer.module.type_map.put(analyzer.allocator, type_index, declaration_index); + try analyzer.module.map.types.put(analyzer.allocator, type_index, declaration_index); }, else => {}, } @@ -1281,7 +1279,7 @@ const Analyzer = struct { } if (declaration.type.invalid) { - declaration.type = init_value.getType(analyzer.module); + declaration.type = analyzer.module.values.array.get(declaration.init_value).getType(analyzer.module); } // logln(.sema, .identifier, "Declaration resolved as: {}", .{init_value}); @@ -1296,7 +1294,7 @@ const Analyzer = struct { assert(!declaration.type.eq(optional_pointer_to_any_type)); assert(!declaration.type.eq(optional_any)); - if (init_value.isComptime(analyzer.module) and declaration.mutability == .@"const") { + if (analyzer.module.values.array.get(declaration.init_value).isComptime(analyzer.module) and declaration.mutability == .@"const") { assert(!declaration.init_value.invalid); assert(typecheck_result == .success); return declaration.init_value; @@ -1307,19 +1305,26 @@ const Analyzer = struct { true => try analyzer.typeCheck(expect_type, declaration.type), }; - const ref_allocation = try analyzer.module.values.append(analyzer.allocator, .{ + const reference_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .declaration_reference = .{ .value = declaration_index, .type = switch (expect_type) { .none => declaration.type, .type_index => switch (typecheck_result) { - .success => expect_type.type_index, - else => declaration.type, + .success, + .cast_to_optional, + => expect_type.type_index, + .array_coerce_to_slice, + .zero_extend, + .sign_extend, + .take_source, + => declaration.type, }, .flexible_integer => blk: { assert(!declaration.type.invalid); break :blk declaration.type; }, + .addressable => declaration.type, }, }, }); @@ -1327,10 +1332,10 @@ const Analyzer = struct { return switch (typecheck_result) { .success, .take_source, - => ref_allocation.index, + => reference_index, inline .zero_extend, .sign_extend => |extend| blk: { - const cast_allocation = try analyzer.module.casts.append(analyzer.allocator, .{ - .value = ref_allocation.index, + const cast_index = try analyzer.module.values.casts.append(analyzer.allocator, .{ + .value = reference_index, .type = switch (expect_type) { .flexible_integer => |flexible_integer| t: { const cast_type = Type.Integer.getIndex(.{ @@ -1346,12 +1351,43 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), }, }); - const value_allocation = try analyzer.module.values.append(analyzer.allocator, @unionInit(Value, @tagName(extend), cast_allocation.index)); - break :blk value_allocation.index; + + const value_index = try analyzer.module.values.array.append(analyzer.allocator, @unionInit(Value, @tagName(extend), cast_index)); + break :blk value_index; + }, + .cast_to_optional => blk: { + const cast_type = switch (expect_type) { + .type_index => |type_index| type_index, + else => |t| @panic(@tagName(t)), + }; + const cast_index = try analyzer.module.values.casts.append(analyzer.allocator, .{ + .value = reference_index, + .type = cast_type, + }); + + const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .optional_cast = cast_index, + }); + break :blk value_index; + }, + .array_coerce_to_slice => blk: { + const cast_type = switch (expect_type) { + .type_index => |type_index| type_index, + else => |t| @panic(@tagName(t)), + }; + const cast_index = try analyzer.module.values.casts.append(analyzer.allocator, .{ + .value = reference_index, + .type = cast_type, + }); + + const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ + .array_coerce_to_slice = cast_index, + }); + + break :blk value_index; }, }; } else { - // const scope = analyzer.module.scopes.get(scope_index); panic("Identifier \"{s}\" not found in scope", .{identifier}); } } @@ -1361,16 +1397,30 @@ const Analyzer = struct { return try analyzer.doIdentifierString(scope_index, expect_type, identifier); } - fn resolveNode(analyzer: *Analyzer, value: *Value, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) anyerror!void { + fn resolveInteger(analyzer: *Analyzer, scope_index: Scope.Index, value_index: Value.Index) usize { + _ = scope_index; + + const value = analyzer.module.values.array.get(value_index); + return switch (value.*) { + .declaration_reference => |declaration_reference| blk: { + const declaration = analyzer.module.values.declarations.get(declaration_reference.value); + break :blk analyzer.resolveInteger(declaration.scope, declaration.init_value); + }, + .integer => |integer| integer.value, + else => |t| @panic(@tagName(t)), + }; + } + + fn resolveNode(analyzer: *Analyzer, value_index: Value.Index, scope_index: Scope.Index, expect_type: ExpectType, node_index: Node.Index) anyerror!void { const node = analyzer.getScopeNode(scope_index, node_index); - // logln(.sema, .node, "Resolving node #{} in scope #{} from file #{}: {}", .{ node_index.uniqueInteger(), scope_index.uniqueInteger(), analyzer.module.scopes.get(scope_index).file.uniqueInteger(), node }); + // logln(.sema, .node, "Resolving node #{} in scope #{} from file #{}: {}", .{ node_index.uniqueInteger(), scope_index.uniqueInteger(), analyzer.module.values.scopes.get(scope_index).file.uniqueInteger(), node }); - assert(value.* == .unresolved); + assert(analyzer.module.values.array.get(value_index).* == .unresolved); - value.* = switch (node.id) { + const new_value: Value = switch (node.id) { .identifier => blk: { - const value_index = try analyzer.doIdentifier(scope_index, expect_type, node.token, scope_index); - const value_ref = analyzer.module.values.get(value_index); + const identifier_value_index = try analyzer.doIdentifier(scope_index, expect_type, node.token, scope_index); + const value_ref = analyzer.module.values.array.get(identifier_value_index); break :blk value_ref.*; }, .keyword_true, .keyword_false => blk: { @@ -1394,52 +1444,53 @@ const Analyzer = struct { }, .compiler_intrinsic => try analyzer.compilerIntrinsic(scope_index, expect_type, node_index), .function_definition => blk: { - const function_scope_allocation = try analyzer.allocateScope(.{ + const function_scope_index = try analyzer.module.values.scopes.append(analyzer.allocator, .{ .parent = scope_index, - .file = analyzer.module.scopes.get(scope_index).file, + .file = analyzer.module.values.scopes.get(scope_index).file, .token = node.token, }); - const function_prototype_index = try analyzer.functionPrototype(function_scope_allocation.index, node.left); - const function_prototype = analyzer.module.function_prototypes.get(function_prototype_index); + const function_prototype_index = try analyzer.functionPrototype(function_scope_index, node.left); + const function_prototype = analyzer.module.types.function_prototypes.get(function_prototype_index); assert(!function_prototype.attributes.@"extern"); const expected_type = ExpectType{ .type_index = analyzer.functionPrototypeReturnType(function_prototype_index), }; logln(.sema, .fn_return_type, "Return type: #{}", .{expected_type.type_index.uniqueInteger()}); - const function_body = try analyzer.block(function_scope_allocation.index, expected_type, node.right); + const function_body = try analyzer.block(function_scope_index, expected_type, node.right); - const prototype_type = try analyzer.module.types.append(analyzer.allocator, .{ + const prototype_type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ .function = function_prototype_index, }); - const function_allocation = try analyzer.module.function_definitions.append(analyzer.allocator, .{ - .prototype = prototype_type.index, + const function_index = try analyzer.module.types.function_definitions.append(analyzer.allocator, .{ + .prototype = prototype_type_index, .body = function_body, - .scope = function_scope_allocation.index, + .scope = function_scope_index, }); - break :blk .{ - .function_definition = function_allocation.index, + const result = Value{ + .function_definition = function_index, }; + break :blk result; }, .function_prototype => blk: { const function_prototype_index = try analyzer.functionPrototype(scope_index, node_index); - const function_prototype = analyzer.module.function_prototypes.get(function_prototype_index); + const function_prototype = analyzer.module.types.function_prototypes.get(function_prototype_index); break :blk switch (function_prototype.attributes.@"extern") { true => b: { - const prototype_type = try analyzer.module.types.append(analyzer.allocator, .{ + const prototype_type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ .function = function_prototype_index, }); - const function_declaration = try analyzer.module.function_declarations.append(analyzer.allocator, .{ - .prototype = prototype_type.index, + const function_declaration_index = try analyzer.module.types.function_declarations.append(analyzer.allocator, .{ + .prototype = prototype_type_index, .body = Block.Index.invalid, .scope = Scope.Index.invalid, }); - break :b .{ - .function_declaration = function_declaration.index, + break :b Value{ + .function_declaration = function_declaration_index, }; }, false => unreachable, @@ -1448,12 +1499,12 @@ const Analyzer = struct { .simple_while => unreachable, .block => blk: { const block_index = try analyzer.block(scope_index, expect_type, node_index); - break :blk .{ + break :blk Value{ .block = block_index, }; }, .number_literal => switch (std.zig.parseNumberLiteral(analyzer.numberBytes(scope_index, node.token))) { - .int => |integer| .{ + .int => |integer| Value{ .integer = .{ .value = integer, .type = switch (expect_type) { @@ -1464,7 +1515,7 @@ const Analyzer = struct { .signedness = .unsigned, }), .type_index => |type_index| a: { - const type_info = analyzer.module.types.get(type_index); + const type_info = analyzer.module.types.array.get(type_index); break :a switch (type_info.*) { .integer => type_index, else => |t| @panic(@tagName(t)), @@ -1472,6 +1523,7 @@ const Analyzer = struct { }, else => unreachable, }, + else => unreachable, }, .signedness = .unsigned, }, @@ -1487,17 +1539,17 @@ const Analyzer = struct { const identifier_hash = try analyzer.processIdentifier(identifier); logln(.sema, .node, "Field access identifier for RHS: \"{s}\"", .{identifier}); // analyzer.debugNode(scope_index, node_index); - const left_allocation = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); + const left_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); - switch (left_allocation.ptr.*) { + switch (analyzer.module.values.array.get(left_value_index).*) { .type => |type_index| { if (!type_index.invalid) { - const left_type = analyzer.module.types.get(type_index); + const left_type = analyzer.module.types.array.get(type_index); switch (left_type.*) { .@"struct" => |struct_index| { - const struct_type = analyzer.module.structs.get(struct_index); + const struct_type = analyzer.module.types.structs.get(struct_index); const right_index = try analyzer.doIdentifier(struct_type.scope, ExpectType.none, node.right.value, scope_index); - const right_value = analyzer.module.values.get(right_index); + const right_value = analyzer.module.values.array.get(right_index); switch (right_value.*) { .function_definition, .type, .enum_field => break :blk right_value.*, @@ -1511,20 +1563,20 @@ const Analyzer = struct { unreachable; }, .@"enum" => |enum_index| { - const enum_type = analyzer.module.enums.get(enum_index); + const enum_type = analyzer.module.types.enums.get(enum_index); const result = for (enum_type.fields.items) |enum_field_index| { - const enum_field = analyzer.module.enum_fields.get(enum_field_index); + const enum_field = analyzer.module.types.enum_fields.get(enum_field_index); if (enum_field.name == identifier_hash) { break enum_field_index; } } else { @panic("No enum found"); }; - const enum_field = analyzer.module.enum_fields.get(result); + const enum_field = analyzer.module.types.enum_fields.get(result); const enum_field_name = analyzer.module.getName(enum_field.name).?; logln(.sema, .node, "Enum field name resolution: {s}", .{enum_field_name}); - break :blk .{ + break :blk Value{ .enum_field = result, }; }, @@ -1536,56 +1588,54 @@ const Analyzer = struct { } }, .enum_field => |enum_field_index| { - const enum_field = analyzer.module.enum_fields.get(enum_field_index); + const enum_field = analyzer.module.types.enum_fields.get(enum_field_index); const enum_field_name = analyzer.module.getName(enum_field.name).?; std.debug.panic("LEFT: enum {s}. RIGHT: {s}", .{ enum_field_name, identifier }); }, .declaration_reference => |declaration_reference| { - const declaration_type = analyzer.module.types.get(declaration_reference.type); + const declaration_type = analyzer.module.types.array.get(declaration_reference.type); switch (declaration_type.*) { .@"struct" => |struct_index| { - const struct_type = analyzer.module.structs.get(struct_index); + const struct_type = analyzer.module.types.structs.get(struct_index); for (struct_type.fields.items) |struct_field_index| { - const struct_field = analyzer.module.container_fields.get(struct_field_index); + const struct_field = analyzer.module.types.container_fields.get(struct_field_index); if (struct_field.name == identifier_hash) { - const field_access = try analyzer.module.field_accesses.append(analyzer.allocator, .{ - .declaration_reference = left_allocation.index, + const field_access_index = try analyzer.module.values.field_accesses.append(analyzer.allocator, .{ + .declaration_reference = left_value_index, .field = struct_field_index, }); - break :blk .{ - .field_access = field_access.index, + break :blk Value{ + .field_access = field_access_index, }; } } else { - const right_node = analyzer.getScopeNode(scope_index, node.right); - _ = right_node; const declaration_value = try analyzer.doIdentifier(struct_type.scope, ExpectType.none, node.right.value, scope_index); - const value_ref = analyzer.module.values.get(declaration_value); + const value_ref = analyzer.module.values.array.get(declaration_value); break :blk value_ref.*; } }, .pointer => |pointer| { - const pointer_element_type = analyzer.module.types.get(pointer.element_type); + const pointer_element_type = analyzer.module.types.array.get(pointer.element_type); switch (pointer_element_type.*) { .@"struct" => |struct_index| { - const struct_type = analyzer.module.structs.get(struct_index); + const struct_type = analyzer.module.types.structs.get(struct_index); for (struct_type.fields.items) |struct_field_index| { - const struct_field = analyzer.module.container_fields.get(struct_field_index); + const struct_field = analyzer.module.types.container_fields.get(struct_field_index); if (struct_field.name == identifier_hash) { - const field_access = try analyzer.module.field_accesses.append(analyzer.allocator, .{ - .declaration_reference = left_allocation.index, + const field_access_index = try analyzer.module.values.field_accesses.append(analyzer.allocator, .{ + .declaration_reference = left_value_index, .field = struct_field_index, }); - break :blk .{ - .field_access = field_access.index, + + break :blk Value{ + .field_access = field_access_index, }; } } else { const declaration_value = try analyzer.doIdentifier(struct_type.scope, ExpectType.none, node.right.value, scope_index); - _ = declaration_value; - - unreachable; + const value_ref = analyzer.module.values.array.get(declaration_value); + break :blk value_ref.*; } }, else => |t| @panic(@tagName(t)), @@ -1601,8 +1651,8 @@ const Analyzer = struct { } else unreachable; const slice_access_type = switch (slice_field) { .ptr => t: { - const slice_type_index = left_allocation.ptr.getType(analyzer.module); - const slice_type = analyzer.module.types.get(slice_type_index); + const slice_type_index = analyzer.getValueType(left_value_index); + const slice_type = analyzer.module.types.array.get(slice_type_index); const slice_type_slice = slice_type.slice; const pointer_type = try analyzer.getPointerType(.{ .element_type = slice_type_slice.element_type, @@ -1613,38 +1663,47 @@ const Analyzer = struct { }, .len => Type.usize, }; - const field_access = try analyzer.module.slice_accesses.append(analyzer.allocator, .{ - .value = left_allocation.index, + + const field_access_index = try analyzer.module.values.slice_accesses.append(analyzer.allocator, .{ + .value = left_value_index, .field = slice_field, .type = slice_access_type, }); - break :blk .{ - .slice_access = field_access.index, + + break :blk Value{ + .slice_access = field_access_index, }; }, + .array => |array| break :blk Value{ + .integer = .{ + .value = array.element_count, + .type = expect_type.type_index, + .signedness = .unsigned, + }, + }, else => |t| @panic(@tagName(t)), } unreachable; }, .field_access => |field_access| { - const left_field_access = analyzer.module.field_accesses.get(field_access); - const left_field = analyzer.module.container_fields.get(left_field_access.field); - const left_field_type = analyzer.module.types.get(left_field.type); + const left_field_access = analyzer.module.values.field_accesses.get(field_access); + const left_field = analyzer.module.types.container_fields.get(left_field_access.field); + const left_field_type = analyzer.module.types.array.get(left_field.type); switch (left_field_type.*) { .@"struct" => |struct_index| { - const struct_type = analyzer.module.structs.get(struct_index); + const struct_type = analyzer.module.types.structs.get(struct_index); for (struct_type.fields.items) |struct_field_index| { - const struct_field = analyzer.module.container_fields.get(struct_field_index); + const struct_field = analyzer.module.types.container_fields.get(struct_field_index); if (struct_field.name == identifier_hash) { - const new_field_access = try analyzer.module.field_accesses.append(analyzer.allocator, .{ - .declaration_reference = left_allocation.index, + const field_access_index = try analyzer.module.values.field_accesses.append(analyzer.allocator, .{ + .declaration_reference = left_value_index, .field = struct_field_index, }); - break :blk .{ - .field_access = new_field_access.index, + break :blk Value{ + .field_access = field_access_index, }; } } else { @@ -1652,7 +1711,7 @@ const Analyzer = struct { const scope2 = scope_index; const declaration_value = try analyzer.doIdentifier(scope1, ExpectType.none, node.right.value, scope2); - const value_ref = analyzer.module.values.get(declaration_value); + const value_ref = analyzer.module.values.array.get(declaration_value); break :blk value_ref.*; } @@ -1677,9 +1736,9 @@ const Analyzer = struct { }; var field_list = try ArrayList(Enum.Field.Index).initCapacity(analyzer.allocator, field_node_list.items.len); - const enum_allocation = try analyzer.module.enums.addOne(analyzer.allocator); - const type_allocation = try analyzer.module.types.append(analyzer.allocator, .{ - .@"enum" = enum_allocation.index, + const enum_index = try analyzer.module.types.enums.addOne(analyzer.allocator); + const type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ + .@"enum" = enum_index, }); for (field_node_list.items) |field_node_index| { @@ -1690,23 +1749,23 @@ const Analyzer = struct { const enum_hash_name = try analyzer.processIdentifier(identifier); - const enum_field_allocation = try analyzer.module.enum_fields.append(analyzer.allocator, .{ + const enum_field_index = try analyzer.module.types.enum_fields.append(analyzer.allocator, .{ .name = enum_hash_name, .value = Value.Index.invalid, - .parent = enum_allocation.index, + .parent = enum_index, }); - field_list.appendAssumeCapacity(enum_field_allocation.index); + field_list.appendAssumeCapacity(enum_field_index); } - enum_allocation.ptr.* = .{ + analyzer.module.types.enums.get(enum_index).* = .{ .scope = Scope.Index.invalid, .fields = field_list, - .type = type_allocation.index, + .type = type_index, }; break :blk .{ - .type = type_allocation.index, + .type = type_index, }; }, .assign => try analyzer.processAssignment(scope_index, node_index), @@ -1732,7 +1791,7 @@ const Analyzer = struct { .compare_less_than, .compare_less_or_equal, => try analyzer.processBinaryOperation(scope_index, expect_type, node_index), - .expression_group => return try analyzer.resolveNode(value, scope_index, expect_type, node.left), //unreachable, + .expression_group => return try analyzer.resolveNode(value_index, scope_index, expect_type, node.left), //unreachable, .container_literal => blk: { const literal_type = try analyzer.resolveType(.{ .scope_index = scope_index, @@ -1746,8 +1805,8 @@ const Analyzer = struct { .struct_type => blk: { const left_node = analyzer.getScopeNode(scope_index, node.left); const nodes = analyzer.getScopeNodeList(scope_index, left_node); - const scope = analyzer.module.scopes.get(scope_index); - const struct_type = try analyzer.structType(value, scope_index, nodes.items, scope.file, node_index); + const scope = analyzer.module.values.scopes.get(scope_index); + const struct_type = try analyzer.structType(value_index, scope_index, nodes.items, scope.file, node_index); break :blk .{ .type = struct_type, }; @@ -1762,70 +1821,102 @@ const Analyzer = struct { .boolean_not => blk: { const typecheck_result = try analyzer.typeCheck(expect_type, Type.boolean); assert(typecheck_result == .success); - const not_value = try analyzer.unresolvedAllocate(scope_index, ExpectType.boolean, node.left); - const unary = try analyzer.module.unary_operations.append(analyzer.allocator, .{ + const not_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.boolean, node.left); + const unary_index = try analyzer.module.values.unary_operations.append(analyzer.allocator, .{ .id = .boolean_not, - .value = not_value.index, + .value = not_value_index, .type = Type.boolean, }); break :blk .{ - .unary_operation = unary.index, + .unary_operation = unary_index, }; }, .null_literal => switch (expect_type) { - .type_index => |type_index| switch (analyzer.module.types.get(type_index).*) { - .optional => |optional| switch (analyzer.module.types.get(optional.element_type).*) { - .pointer => .pointer_null_literal, - else => .optional_null_literal, + .type_index => |type_index| switch (analyzer.module.types.array.get(type_index).*) { + .optional => |optional| switch (analyzer.module.types.array.get(optional.element_type).*) { + .pointer => Value.pointer_null_literal, + else => Value.optional_null_literal, }, else => |t| @panic(@tagName(t)), }, else => |t| @panic(@tagName(t)), }, .negation => blk: { - const negation_value = try analyzer.unresolvedAllocate(scope_index, expect_type, node.left); - const unary = try analyzer.module.unary_operations.append(analyzer.allocator, .{ + const negation_value_index = try analyzer.unresolvedAllocate(scope_index, expect_type, node.left); + const unary_index = try analyzer.module.values.unary_operations.append(analyzer.allocator, .{ .id = .negation, - .value = negation_value.index, - .type = negation_value.ptr.getType(analyzer.module), + .value = negation_value_index, + .type = analyzer.getValueType(negation_value_index), }); break :blk .{ - .unary_operation = unary.index, + .unary_operation = unary_index, }; }, .address_of => blk: { + var many = false; + var is_const = false; const new_expect_type = switch (expect_type) { - .none => expect_type, - .type_index => |type_index| switch (analyzer.module.types.get(type_index).*) { - .pointer => |pointer| ExpectType{ - .type_index = pointer.element_type, + // .none => expect_type, + .type_index => |type_index| ExpectType{ + .addressable = switch (analyzer.module.types.array.get(type_index).*) { + .pointer => |pointer| b: { + is_const = pointer.@"const"; + many = pointer.many; + break :b pointer.element_type; + }, + .slice => |slice| b: { + is_const = slice.@"const"; + many = true; + break :b slice.element_type; + }, + else => |t| @panic(@tagName(t)), }, - else => |t| @panic(@tagName(t)), }, .flexible_integer => unreachable, + else => unreachable, }; + logln(.sema, .address_of, "New expect type: {}", .{new_expect_type}); - const appointee_value = try analyzer.unresolvedAllocate(scope_index, new_expect_type, node.left); - const unary = try analyzer.module.unary_operations.append(analyzer.allocator, .{ + const appointee_value_index = try analyzer.unresolvedAllocate(scope_index, new_expect_type, node.left); + const unary_type_index: Type.Index = try analyzer.getPointerType(.{ + .element_type = analyzer.module.values.array.get(appointee_value_index).getType(analyzer.module), + .many = many, + .@"const" = is_const, + }); + const unary_index = try analyzer.module.values.unary_operations.append(analyzer.allocator, .{ .id = .address_of, - .value = appointee_value.index, - .type = switch (expect_type) { - .none => unreachable, - .type_index => |type_index| type_index, - .flexible_integer => unreachable, - }, + .value = appointee_value_index, + .type = unary_type_index, }); - break :blk .{ - .unary_operation = unary.index, + const value: Value = switch (expect_type) { + .none => unreachable, + .type_index => |type_index| switch (analyzer.module.types.array.get(type_index).*) { + .slice => b: { + const array_coerce_to_slice = try analyzer.module.values.casts.append(analyzer.allocator, .{ + .value = appointee_value_index, + .type = type_index, + }); + break :b .{ + .array_coerce_to_slice = array_coerce_to_slice, + }; + }, + else => .{ + .unary_operation = unary_index, + }, + }, + .flexible_integer => unreachable, + else => unreachable, }; + + break :blk value; }, .pointer_dereference => blk: { const new_expect_type = switch (expect_type) { .none => expect_type, - .type_index => |type_index| switch (analyzer.module.types.get(type_index).*) { + .type_index => |type_index| switch (analyzer.module.types.array.get(type_index).*) { .pointer => unreachable, else => ExpectType{ .type_index = try analyzer.getPointerType(.{ @@ -1836,31 +1927,33 @@ const Analyzer = struct { }, }, .flexible_integer => unreachable, + else => unreachable, }; - const pointer_value = try analyzer.unresolvedAllocate(scope_index, new_expect_type, node.left); - const pointer_type = analyzer.module.types.get(pointer_value.ptr.getType(analyzer.module)); + const pointer_value_index = try analyzer.unresolvedAllocate(scope_index, new_expect_type, node.left); + const pointer_type = analyzer.module.types.array.get(analyzer.getValueType(pointer_value_index)); assert(pointer_type.* == .pointer); const element_type = pointer_type.pointer.element_type; - const unary = try analyzer.module.unary_operations.append(analyzer.allocator, .{ + const unary_index = try analyzer.module.values.unary_operations.append(analyzer.allocator, .{ .id = .pointer_dereference, - .value = pointer_value.index, + .value = pointer_value_index, .type = element_type, }); break :blk .{ - .unary_operation = unary.index, + .unary_operation = unary_index, }; }, .slice => blk: { - const expression_to_slice = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); - const expression_to_slice_type = expression_to_slice.ptr.getType(analyzer.module); - const element_type = switch (analyzer.module.types.get(expression_to_slice_type).*) { + const expression_to_slice_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); + const expression_to_slice_type = analyzer.getValueType(expression_to_slice_index); + const element_type = switch (analyzer.module.types.array.get(expression_to_slice_type).*) { .pointer => |pointer| pointer.element_type, + .slice => |slice| slice.element_type, else => |t| @panic(@tagName(t)), }; - const slice = try analyzer.module.slices.append(analyzer.allocator, .{ - .sliceable = expression_to_slice.index, + const slice_index = try analyzer.module.values.slices.append(analyzer.allocator, .{ + .sliceable = expression_to_slice_index, .range = try analyzer.range(scope_index, node.right), .type = try analyzer.getSliceType(.{ .element_type = element_type, @@ -1869,36 +1962,36 @@ const Analyzer = struct { }); break :blk .{ - .slice = slice.index, + .slice = slice_index, }; }, .indexed_access => blk: { - const indexable_expression = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); - const indexable_expression_type = indexable_expression.ptr.getType(analyzer.module); - switch (analyzer.module.types.get(indexable_expression_type).*) { + const indexable_expression_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node.left); + const indexable_expression_type = analyzer.getValueType(indexable_expression_index); + switch (analyzer.module.types.array.get(indexable_expression_type).*) { .slice => {}, else => |t| @panic(@tagName(t)), } - const index_expression = try analyzer.unresolvedAllocate(scope_index, ExpectType{ + const index_expression_index = try analyzer.unresolvedAllocate(scope_index, ExpectType{ .type_index = Type.usize, }, node.right); - const indexed_access = try analyzer.module.indexed_accesses.append(analyzer.allocator, .{ - .indexed_expression = indexable_expression.index, - .index_expression = index_expression.index, + const indexed_access_index = try analyzer.module.values.indexed_accesses.append(analyzer.allocator, .{ + .indexed_expression = indexable_expression_index, + .index_expression = index_expression_index, }); break :blk .{ - .indexed_access = indexed_access.index, + .indexed_access = indexed_access_index, }; }, .enum_literal => blk: { const enum_literal_identifier_token = node.token + 1; switch (expect_type) { .type_index => |type_index| { - switch (analyzer.module.types.get(type_index).*) { + switch (analyzer.module.types.array.get(type_index).*) { .@"enum" => |enum_index| { - const enum_type = analyzer.module.enums.get(enum_index); + const enum_type = analyzer.module.types.enums.get(enum_index); const enum_field_index = try analyzer.typeCheckEnumLiteral(scope_index, enum_literal_identifier_token, enum_type) orelse unreachable; break :blk .{ @@ -1911,39 +2004,28 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), } }, + .undefined => .undefined, else => |t| @panic(@tagName(t)), }; - } - fn getSliceType(analyzer: *Analyzer, slice: Type.Slice) !Type.Index { - const gop = try analyzer.module.slice_types.getOrPut(analyzer.allocator, slice); - - if (!gop.found_existing) { - const type_allocation = try analyzer.module.types.append(analyzer.allocator, .{ - .slice = slice, - }); - gop.value_ptr.* = type_allocation.index; - } - - const result = gop.value_ptr.*; - return result; + analyzer.module.values.array.get(value_index).* = new_value; } fn analyzeContainerLiteral(analyzer: *Analyzer, scope_index: Scope.Index, expected_type_index: Type.Index, node_list_node_index: Node.Index) !Compilation.ContainerInitialization.Index { const field_initialization_node_list = analyzer.getScopeNode(scope_index, node_list_node_index); const field_nodes = analyzer.getScopeNodeList(scope_index, field_initialization_node_list); assert(!expected_type_index.invalid); - const expected_type = analyzer.module.types.get(expected_type_index); + const expected_type = analyzer.module.types.array.get(expected_type_index); switch (expected_type.*) { .@"struct" => |struct_index| { - const struct_type = analyzer.module.structs.get(struct_index); + const struct_type = analyzer.module.types.structs.get(struct_index); var bitset = try std.DynamicBitSetUnmanaged.initEmpty(analyzer.allocator, field_nodes.items.len); var list = try ArrayList(Value.Index).initCapacity(analyzer.allocator, struct_type.fields.items.len); for (struct_type.fields.items) |struct_field_index| { - const struct_field = analyzer.module.container_fields.get(struct_field_index); + const struct_field = analyzer.module.types.container_fields.get(struct_field_index); var value_index = Value.Index.invalid; @@ -1960,9 +2042,9 @@ const Analyzer = struct { bitset.set(index); - value_index = (try analyzer.unresolvedAllocate(scope_index, ExpectType{ + value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType{ .type_index = struct_field.type, - }, field_node.right)).index; + }, field_node.right); } } @@ -1982,11 +2064,11 @@ const Analyzer = struct { @panic("Some field name in struct initialization is wrong"); } - const container_initialization = try analyzer.module.container_initializations.append(analyzer.allocator, .{ + const container_initialization_index = try analyzer.module.values.container_initializations.append(analyzer.allocator, .{ .field_initializations = list, .type = expected_type_index, }); - return container_initialization.index; + return container_initialization_index; }, else => |t| @panic(@tagName(t)), } @@ -2005,7 +2087,7 @@ const Analyzer = struct { logln(.sema, .debug, "Debugging:\n\n```\n{s}\n```", .{source_file[token.start..]}); } - 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) !StringLiteral { const string_literal_node = analyzer.getScopeNode(scope_index, node_index); assert(string_literal_node.id == .string_literal); const original_string_literal = analyzer.tokenStringLiteral(scope_index, string_literal_node.token); @@ -2020,8 +2102,24 @@ const Analyzer = struct { break :blk original_string_literal; }; - const string_key = try analyzer.module.addStringLiteral(analyzer.allocator, string_literal); - return string_key; + const len: u32 = @intCast(string_literal.len); + const array_type = try analyzer.getArrayType(.{ + .element_type = Type.u8, + .element_count = len, + }); + + const pointer_type = try analyzer.getPointerType(.{ + .many = true, + .@"const" = true, + .element_type = array_type, + }); + + const hash = try Module.addString(&analyzer.module.map.strings, analyzer.allocator, string_literal); + + return StringLiteral{ + .hash = hash, + .type = pointer_type, + }; } fn fixupStringLiteral(allocator: Allocator, string_literal: []const u8) ![]const u8 { @@ -2046,7 +2144,7 @@ const Analyzer = struct { } 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.types.function_prototypes.get(function_prototype_index); return function_prototype.return_type; } @@ -2066,7 +2164,7 @@ const Analyzer = struct { const identifier = tokenBytes(token, source_file); logln(.sema, .type, "Identifier: \"{s}\"", .{identifier}); const resolved_value_index = try analyzer.doIdentifier(scope_index, ExpectType.type, type_node.token, scope_index); - const resolved_value = analyzer.module.values.get(resolved_value_index); + const resolved_value = analyzer.module.values.array.get(resolved_value_index); break :blk switch (resolved_value.*) { .type => |type_index| type_index, else => |t| @panic(@tagName(t)), @@ -2095,23 +2193,30 @@ const Analyzer = struct { else => @compileError("OOO"), }, }; - const gop = try analyzer.module.non_primitive_integer_types.getOrPut(analyzer.allocator, integer); + const gop = try analyzer.module.map.non_primitive_integer.getOrPut(analyzer.allocator, integer); if (!gop.found_existing) { - const new_type = try analyzer.module.types.append(analyzer.allocator, .{ + const type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ .integer = integer, }); - gop.value_ptr.* = new_type.index; + gop.value_ptr.* = type_index; } - break :b gop.value_ptr.*; + const result = gop.value_ptr.*; + break :b result; }, false => @panic("non primitive size not allowed"), }, }; }, - .const_single_pointer_type, .single_pointer_type, .const_many_pointer_type, .many_pointer_type => blk: { + .const_single_pointer_type, + .single_pointer_type, + .const_many_pointer_type, + .many_pointer_type, + .zero_terminated_const_many_pointer_type, + .zero_terminated_many_pointer_type, + => blk: { const element_type = try resolveType(analyzer, .{ .scope_index = scope_index, .node_index = type_node.left, @@ -2119,6 +2224,8 @@ const Analyzer = struct { const many = switch (type_node.id) { .const_many_pointer_type, .many_pointer_type, + .zero_terminated_const_many_pointer_type, + .zero_terminated_many_pointer_type, => true, .const_single_pointer_type, .single_pointer_type, @@ -2128,7 +2235,9 @@ const Analyzer = struct { const is_const = switch (type_node.id) { .const_many_pointer_type, .const_single_pointer_type, + .zero_terminated_const_many_pointer_type, => true, + .zero_terminated_many_pointer_type, .many_pointer_type, .single_pointer_type, => false, @@ -2158,16 +2267,16 @@ const Analyzer = struct { .scope_index = scope_index, .node_index = type_node.left, }); - const gop = try analyzer.module.optional_types.getOrPut(analyzer.allocator, element_type); + const gop = try analyzer.module.map.optionals.getOrPut(analyzer.allocator, element_type); const result = switch (gop.found_existing) { true => gop.value_ptr.*, false => b: { - const type_allocation = try analyzer.module.types.append(analyzer.allocator, .{ + const type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ .optional = .{ .element_type = element_type, }, }); - gop.value_ptr.* = type_allocation.index; + gop.value_ptr.* = type_index; break :b gop.value_ptr.*; }, @@ -2180,17 +2289,35 @@ const Analyzer = struct { .usize_type => Type.usize, .bool_type => Type.boolean, .simple_function_prototype => blk: { - const function_prototype = try analyzer.module.function_prototypes.append(analyzer.allocator, try analyzer.processSimpleFunctionPrototype(scope_index, node_index)); + const function_prototype_index = try analyzer.module.types.function_prototypes.append(analyzer.allocator, try analyzer.processSimpleFunctionPrototype(scope_index, node_index)); - const function_type = try analyzer.module.types.append(analyzer.allocator, .{ - .function = function_prototype.index, + const function_type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ + .function = function_prototype_index, }); - break :blk function_type.index; + break :blk function_type_index; }, .field_access => blk: { - const type_resolution = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node_index); - assert(type_resolution.ptr.* == .type); - break :blk type_resolution.ptr.type; + const type_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, node_index); + const type_value_ptr = analyzer.module.values.array.get(type_value_index); + assert(type_value_ptr.* == .type); + break :blk type_value_ptr.type; + }, + .array_type => blk: { + const array_element_type_value_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.type, type_node.right); + const array_element_type_value = analyzer.module.values.array.get(array_element_type_value_index); + assert(array_element_type_value.* == .type); + const array_element_type_index = array_element_type_value.type; + + const length_expression_index = try analyzer.unresolvedAllocate(scope_index, ExpectType{ + .type_index = Type.usize, + }, type_node.left); + const length: usize = analyzer.resolveInteger(scope_index, length_expression_index); + + const array_type = try analyzer.getArrayType(.{ + .element_type = array_element_type_index, + .element_count = @intCast(length), + }); + break :blk array_type; }, else => |t| @panic(@tagName(t)), }; @@ -2216,8 +2343,7 @@ const Analyzer = struct { assert(argument_node_list.items.len > 0); if (argument_node_list.items.len > 0) { var arguments = try ArrayList(Declaration.Index).initCapacity(analyzer.allocator, argument_node_list.items.len); - const scope = analyzer.module.scopes.get(scope_index); - _ = scope; + for (argument_node_list.items, 0..) |argument_node_index, index| { const argument_node = analyzer.getScopeNode(scope_index, argument_node_index); switch (argument_node.id) { @@ -2257,9 +2383,9 @@ const Analyzer = struct { const function_prototype_node = analyzer.getScopeNode(scope_index, node_index); switch (function_prototype_node.id) { .simple_function_prototype => { - const function_prototype_allocation = try analyzer.module.function_prototypes.append(analyzer.allocator, try analyzer.processSimpleFunctionPrototype(scope_index, node_index)); + const function_prototype_index = try analyzer.module.types.function_prototypes.append(analyzer.allocator, try analyzer.processSimpleFunctionPrototype(scope_index, node_index)); - return function_prototype_allocation.index; + return function_prototype_index; }, .function_prototype => { var function_prototype = try analyzer.processSimpleFunctionPrototype(scope_index, function_prototype_node.left); @@ -2275,17 +2401,17 @@ const Analyzer = struct { .export_qualifier => function_prototype.attributes.@"export" = true, .calling_convention => { const calling_convention_type_declaration = try analyzer.forceDeclarationAnalysis(scope_index, "std.builtin.CallingConvention"); - const calling_convention_type = switch (analyzer.module.values.get(calling_convention_type_declaration).*) { + const calling_convention_type = switch (analyzer.module.values.array.get(calling_convention_type_declaration).*) { .type => |type_index| type_index, else => |t| @panic(@tagName(t)), }; const cc_value = try analyzer.unresolvedAllocate(scope_index, ExpectType{ .type_index = calling_convention_type, }, attribute_node.left); - const enum_field_value = cc_value.ptr; - switch (enum_field_value.*) { + + switch (analyzer.module.values.array.get(cc_value).*) { .enum_field => |enum_field_index| { - const enum_field = analyzer.module.enum_fields.get(enum_field_index); + const enum_field = analyzer.module.types.enum_fields.get(enum_field_index); const enum_field_name = analyzer.module.getName(enum_field.name).?; calling_convention = inline for (@typeInfo(Compilation.CallingConvention).Enum.fields) |cc_enum_field| { @@ -2303,8 +2429,8 @@ const Analyzer = struct { function_prototype.attributes.calling_convention = calling_convention orelse Compilation.CallingConvention.system_v; - const function_prototype_allocation = try analyzer.module.function_prototypes.append(analyzer.allocator, function_prototype); - return function_prototype_allocation.index; + const function_prototype_index = try analyzer.module.types.function_prototypes.append(analyzer.allocator, function_prototype); + return function_prototype_index; }, else => |t| @panic(@tagName(t)), } @@ -2319,17 +2445,16 @@ const Analyzer = struct { const result = switch (before_expression.invalid) { true => try analyzer.doIdentifierString(scope_index, ExpectType.type, expression_name), false => blk: { - const before_expression_value = analyzer.module.values.get(before_expression); const expression_name_hash = try analyzer.processIdentifier(expression_name); - switch (before_expression_value.*) { + switch (analyzer.module.values.array.get(before_expression).*) { .type => |type_index| { - const expression_type = analyzer.module.types.get(type_index); + const expression_type = analyzer.module.types.array.get(type_index); switch (expression_type.*) { .@"struct" => |struct_index| { - const struct_type = analyzer.module.structs.get(struct_index); - const struct_type_scope = analyzer.module.scopes.get(struct_type.scope); + const struct_type = analyzer.module.types.structs.get(struct_index); + const struct_type_scope = analyzer.module.values.scopes.get(struct_type.scope); const declaration_index = struct_type_scope.declarations.get(expression_name_hash).?; - const declaration = analyzer.module.declarations.get(declaration_index); + const declaration = analyzer.module.values.declarations.get(declaration_index); assert(declaration.name == expression_name_hash); last_scope = declaration.scope; @@ -2339,16 +2464,16 @@ const Analyzer = struct { } }, .unresolved => |unresolved| { - try analyzer.resolveNode(before_expression_value, last_scope, ExpectType.none, unresolved.node_index); - switch (before_expression_value.*) { + try analyzer.resolveNode(before_expression, last_scope, ExpectType.none, unresolved.node_index); + switch (analyzer.module.values.array.get(before_expression).*) { .type => |type_index| { - const expression_type = analyzer.module.types.get(type_index); + const expression_type = analyzer.module.types.array.get(type_index); switch (expression_type.*) { .@"struct" => |struct_index| { - const struct_type = analyzer.module.structs.get(struct_index); - const struct_type_scope = analyzer.module.scopes.get(struct_type.scope); + const struct_type = analyzer.module.types.structs.get(struct_index); + const struct_type_scope = analyzer.module.values.scopes.get(struct_type.scope); const declaration_index = struct_type_scope.declarations.get(expression_name_hash).?; - const declaration = analyzer.module.declarations.get(declaration_index); + const declaration = analyzer.module.values.declarations.get(declaration_index); assert(declaration.name == expression_name_hash); last_scope = declaration.scope; @@ -2368,10 +2493,9 @@ const Analyzer = struct { before_expression = result; } - const expression_value = analyzer.module.values.get(before_expression); - switch (expression_value.*) { + switch (analyzer.module.values.array.get(before_expression).*) { .unresolved => |unresolved| { - try analyzer.resolveNode(expression_value, last_scope, ExpectType.none, unresolved.node_index); + try analyzer.resolveNode(before_expression, last_scope, ExpectType.none, unresolved.node_index); }, else => {}, } @@ -2379,17 +2503,15 @@ const Analyzer = struct { return before_expression; } - fn structType(analyzer: *Analyzer, value: *Value, parent_scope_index: Scope.Index, struct_nodes: []const Node.Index, struct_file_index: File.Index, struct_node_index: Node.Index) !Type.Index { + fn structType(analyzer: *Analyzer, value_index: Value.Index, parent_scope_index: Scope.Index, struct_nodes: []const Node.Index, struct_file_index: File.Index, struct_node_index: Node.Index) !Type.Index { const struct_node = analyzer.getFileNode(struct_file_index, struct_node_index); if (struct_nodes.len > 0) { - const new_scope = try analyzer.allocateScope(.{ + const scope_index = try analyzer.module.values.scopes.append(analyzer.allocator, .{ .parent = parent_scope_index, .file = struct_file_index, .token = struct_node.token, }); - const scope_index = new_scope.index; - const is_file = parent_scope_index.invalid; const backing_type = blk: { if (!is_file) { @@ -2408,27 +2530,27 @@ const Analyzer = struct { break :blk Type.Index.invalid; }; - const struct_allocation = try analyzer.module.structs.append(analyzer.allocator, .{ - .scope = new_scope.index, + const struct_index = try analyzer.module.types.structs.append(analyzer.allocator, .{ + .scope = scope_index, .backing_type = backing_type, }); - const type_allocation = try analyzer.module.types.append(analyzer.allocator, .{ - .@"struct" = struct_allocation.index, + const struct_type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ + .@"struct" = struct_index, }); if (is_file) { - const file = analyzer.module.files.get(struct_file_index); - file.type = type_allocation.index; + const file = analyzer.module.values.files.get(struct_file_index); + file.type = struct_type_index; } - analyzer.module.scopes.get(scope_index).type = type_allocation.index; - value.* = .{ - .type = type_allocation.index, + analyzer.module.values.scopes.get(scope_index).type = struct_type_index; + analyzer.module.values.array.get(value_index).* = .{ + .type = struct_type_index, }; if (!analyzer.current_declaration.invalid) { - const current_declaration = analyzer.module.declarations.get(analyzer.current_declaration); + const current_declaration = analyzer.module.values.declarations.get(analyzer.current_declaration); assert(current_declaration.type.invalid); current_declaration.type = Type.type; } @@ -2482,11 +2604,11 @@ const Analyzer = struct { } } - struct_allocation.ptr.fields = try ArrayList(Compilation.ContainerField.Index).initCapacity(analyzer.allocator, field_nodes.items.len); + analyzer.module.types.structs.get(struct_index).fields = try ArrayList(Compilation.ContainerField.Index).initCapacity(analyzer.allocator, field_nodes.items.len); if (field_nodes.items.len > 0) { // This is done in order for the names inside fields not to collision with the declaration ones - const field_scope = try analyzer.allocateScope(.{ + const field_scope_index = try analyzer.module.values.scopes.append(analyzer.allocator, .{ .token = analyzer.getScopeNode(scope_index, field_nodes.items[0]).token, .file = struct_file_index, .parent = scope_index, @@ -2494,33 +2616,33 @@ const Analyzer = struct { for (field_nodes.items) |field_index| { const field_node = analyzer.getFileNode(struct_file_index, field_index); - const identifier = analyzer.tokenIdentifier(field_scope.index, field_node.token); + const identifier = analyzer.tokenIdentifier(field_scope_index, field_node.token); const identifier_index = try analyzer.processIdentifier(identifier); const type_index = try analyzer.resolveType(.{ - .scope_index = field_scope.index, + .scope_index = field_scope_index, .node_index = field_node.left, .allow_non_primitive_size = !backing_type.invalid, }); const default_value = if (field_node.right.invalid) Value.Index.invalid else blk: { - const v = try analyzer.unresolvedAllocate(field_scope.index, ExpectType{ + const index = try analyzer.unresolvedAllocate(field_scope_index, ExpectType{ .type_index = type_index, }, field_node.right); - break :blk v.index; + break :blk index; }; - const container_field = try analyzer.module.container_fields.append(analyzer.allocator, .{ + const container_field_index = try analyzer.module.types.container_fields.append(analyzer.allocator, .{ .name = identifier_index, .type = type_index, .default_value = default_value, - .parent = type_allocation.index, + .parent = struct_type_index, }); - struct_allocation.ptr.fields.appendAssumeCapacity(container_field.index); + analyzer.module.types.structs.get(struct_index).fields.appendAssumeCapacity(container_field_index); } } - return type_allocation.index; + return struct_type_index; } else { return Type.Index.invalid; } @@ -2530,14 +2652,14 @@ const Analyzer = struct { const identifier_index = try analyzer.processIdentifier(name); if (analyzer.lookupDeclarationInCurrentAndParentScopes(scope_index, identifier_index)) |lookup| { - const declaration = analyzer.module.declarations.get(lookup.declaration); + const declaration = analyzer.module.values.declarations.get(lookup.declaration); const declaration_name = analyzer.module.getName(declaration.name).?; panic("Existing name in lookup: {s}.\nSource scope: #{}. Lookup scope: #{}", .{ declaration_name, scope_index.uniqueInteger(), lookup.scope.uniqueInteger() }); } // Check if the symbol name is already occupied in the same scope - const scope = analyzer.module.scopes.get(scope_index); - const declaration_allocation = try analyzer.module.declarations.append(analyzer.allocator, .{ + const scope = analyzer.module.values.scopes.get(scope_index); + const declaration_index = try analyzer.module.values.declarations.append(analyzer.allocator, .{ .name = identifier_index, .scope_type = scope_type, .mutability = mutability, @@ -2547,9 +2669,9 @@ const Analyzer = struct { .scope = scope_index, }); - try scope.declarations.putNoClobber(analyzer.allocator, identifier_index, declaration_allocation.index); + try scope.declarations.putNoClobber(analyzer.allocator, identifier_index, declaration_index); - return declaration_allocation.index; + return declaration_index; } fn symbolDeclaration(analyzer: *Analyzer, scope_index: Scope.Index, node_index: Node.Index, scope_type: ScopeType) !Declaration.Index { @@ -2588,9 +2710,9 @@ const Analyzer = struct { const argument = null; assert(argument == null); - const init_value_allocation = switch (scope_type) { + const init_value_index = switch (scope_type) { .local => try analyzer.unresolvedAllocate(scope_index, expect_type, declaration_node.right), - .global => try analyzer.module.values.append(analyzer.allocator, .{ + .global => try analyzer.module.values.array.append(analyzer.allocator, .{ .unresolved = .{ .node_index = declaration_node.right, }, @@ -2601,12 +2723,12 @@ const Analyzer = struct { const type_index = switch (scope_type) { .local => switch (expect_type) { .type_index => |type_index| type_index, - else => init_value_allocation.ptr.getType(analyzer.module), + else => analyzer.module.values.array.get(init_value_index).getType(analyzer.module), }, .global => Type.Index.invalid, }; - const result = try analyzer.declarationCommon(scope_index, scope_type, mutability, identifier, type_index, init_value_allocation.index, argument); + const result = try analyzer.declarationCommon(scope_index, scope_type, mutability, identifier, type_index, init_value_index, argument); return result; } @@ -2662,35 +2784,33 @@ const Analyzer = struct { return string_literal; } - fn allocateScope(analyzer: *Analyzer, scope_value: Scope) !Scope.Allocation { - return analyzer.module.scopes.append(analyzer.allocator, scope_value); - } - const TypeCheckResult = enum { success, zero_extend, sign_extend, take_source, + cast_to_optional, + array_coerce_to_slice, }; fn canCast(analyzer: *Analyzer, expect_type: ExpectType, source: Type.Index) !TypeCheckResult { return switch (expect_type) { .none => unreachable, .flexible_integer => |flexible_integer| blk: { - const source_type = analyzer.module.types.get(source); + const source_type = analyzer.module.types.array.get(source); break :blk switch (source_type.*) { .pointer => .success, .optional => b: { - const optional_element_type = analyzer.module.types.get(source_type.optional.element_type); + const optional_element_type = analyzer.module.types.array.get(source_type.optional.element_type); break :b switch (optional_element_type.*) { .pointer => .success, else => |t| @panic(@tagName(t)), }; }, .@"struct" => |struct_type_index| { - const struct_type = analyzer.module.structs.get(struct_type_index); + const struct_type = analyzer.module.types.structs.get(struct_type_index); if (!struct_type.backing_type.invalid) { - const backing_integer_type = analyzer.module.types.get(struct_type.backing_type); + const backing_integer_type = analyzer.module.types.array.get(struct_type.backing_type); if (backing_integer_type.integer.bit_count >> 3 <= flexible_integer.byte_count) { return .success; } @@ -2706,18 +2826,20 @@ const Analyzer = struct { unreachable; } - const destination_type = analyzer.module.types.get(type_index); - const source_type = analyzer.module.types.get(source); + const destination_type = analyzer.module.types.array.get(type_index); + const source_type = analyzer.module.types.array.get(source); break :blk switch (source_type.*) { .integer => |integer| switch (destination_type.*) { - .optional => |optional| switch (analyzer.module.types.get(optional.element_type).*) { + .optional => |optional| switch (analyzer.module.types.array.get(optional.element_type).*) { .pointer => if (integer.bit_count == 64) .success else unreachable, else => |t| @panic(@tagName(t)), }, + .integer => .success, + .pointer => .success, else => |t| @panic(@tagName(t)), }, .pointer => switch (destination_type.*) { - .optional => |destination_optional| switch (analyzer.module.types.get(destination_optional.element_type).*) { + .optional => |destination_optional| switch (analyzer.module.types.array.get(destination_optional.element_type).*) { .pointer => .success, else => |t| @panic(@tagName(t)), }, @@ -2726,6 +2848,7 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), }; }, + else => unreachable, }; } @@ -2737,8 +2860,8 @@ const Analyzer = struct { return TypeCheckResult.success; } - const destination_type = analyzer.module.types.get(expected_type_index); - const source_type = analyzer.module.types.get(source); + const destination_type = analyzer.module.types.array.get(expected_type_index); + const source_type = analyzer.module.types.array.get(source); switch (destination_type.*) { .type => switch (source_type.* == .type) { @@ -2768,7 +2891,7 @@ const Analyzer = struct { if (source_pointer.element_type.eq(destination_pointer.element_type)) { return .success; } else { - switch (analyzer.module.types.get(source_pointer.element_type).*) { + switch (analyzer.module.types.array.get(source_pointer.element_type).*) { .array => |array| { if (array.element_type.eq(destination_pointer.element_type)) { return .success; @@ -2801,8 +2924,8 @@ const Analyzer = struct { if (destination_optional.element_type.eq(source_optional.element_type)) { return .success; } else { - const destination_optional_element_type = analyzer.module.types.get(destination_optional.element_type); - const source_optional_element_type = analyzer.module.types.get(source_optional.element_type); + const destination_optional_element_type = analyzer.module.types.array.get(destination_optional.element_type); + const source_optional_element_type = analyzer.module.types.array.get(source_optional.element_type); switch (destination_optional_element_type.*) { .pointer => |destination_pointer| switch (source_optional_element_type.*) { @@ -2853,24 +2976,24 @@ const Analyzer = struct { }, .pointer => |source_pointer| { if (destination_optional.element_type.eq(source)) { - return .success; + return .cast_to_optional; } else { - const destination_optional_element_type = analyzer.module.types.get(destination_optional.element_type); + const destination_optional_element_type = analyzer.module.types.array.get(destination_optional.element_type); switch (destination_optional_element_type.*) { .pointer => |destination_pointer| { if (source.eq(optional_pointer_to_any_type)) { - return .success; + return .cast_to_optional; } if (expected_type_index.eq(optional_pointer_to_any_type)) { - return .take_source; + return .cast_to_optional; } if (destination_pointer.many == source_pointer.many) { if (destination_pointer.@"const" or destination_pointer.@"const" == source_pointer.@"const") { if (destination_pointer.element_type.eq(source_pointer.element_type)) { - return .success; + return .cast_to_optional; } } } @@ -2891,6 +3014,26 @@ const Analyzer = struct { unreachable; } }, + // TODO + .integer => if (destination_optional.element_type.eq(source)) { + return .cast_to_optional; + } else { + unreachable; + }, + .slice => |source_slice| if (destination_optional.element_type.eq(source)) { + return .cast_to_optional; + } else { + switch (analyzer.module.types.array.get(destination_optional.element_type).*) { + .slice => |destination_slice| { + if (destination_slice.element_type.eq(source_slice.element_type)) { + return .cast_to_optional; + } else { + unreachable; + } + }, + else => |t| @panic(@tagName(t)), + } + }, else => |t| @panic(@tagName(t)), }, .function => |destination_function| switch (source_type.*) { @@ -2907,7 +3050,7 @@ const Analyzer = struct { } }, .flexible_integer => |expected_flexible_integer| { - const source_type = analyzer.module.types.get(source); + const source_type = analyzer.module.types.array.get(source); switch (source_type.*) { .integer => |source_integer| { const source_size = source_integer.getSize(); @@ -2933,21 +3076,84 @@ const Analyzer = struct { else => |t| @panic(@tagName(t)), } }, + .addressable => |element_type_index| { + const destination_type = analyzer.module.types.array.get(element_type_index); + const source_type = analyzer.module.types.array.get(source); + + switch (source_type.*) { + .array => |array| { + if (array.element_type.eq(element_type_index)) { + return .success; + } else { + switch (destination_type.*) { + else => |t| @panic(@tagName(t)), + } + } + }, + .function => |source_function| { + if (element_type_index.eq(source)) { + return .success; + } else { + switch (destination_type.*) { + .function => |destination_function| { + if (source_function.eq(destination_function)) { + return .success; + } else { + // TODO: FIXME + return .success; + } + }, + else => |t| @panic(@tagName(t)), + } + unreachable; + } + }, + else => |t| @panic(@tagName(t)), + } + }, // else => |t| @panic(@tagName(t)), }; } fn getPointerType(analyzer: *Analyzer, pointer: Type.Pointer) !Type.Index { - const gop = try analyzer.module.pointer_types.getOrPut(analyzer.allocator, pointer); + const gop = try analyzer.module.map.pointers.getOrPut(analyzer.allocator, pointer); if (!gop.found_existing) { - const type_allocation = try analyzer.module.types.append(analyzer.allocator, .{ + const type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ .pointer = .{ .element_type = pointer.element_type, .many = pointer.many, .@"const" = pointer.@"const", }, }); - gop.value_ptr.* = type_allocation.index; + gop.value_ptr.* = type_index; + } + + const result = gop.value_ptr.*; + return result; + } + + fn getSliceType(analyzer: *Analyzer, slice: Type.Slice) !Type.Index { + const gop = try analyzer.module.map.slices.getOrPut(analyzer.allocator, slice); + + if (!gop.found_existing) { + const type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ + .slice = slice, + }); + gop.value_ptr.* = type_index; + } + + const result = gop.value_ptr.*; + return result; + } + + fn getArrayType(analyzer: *Analyzer, array: Type.Array) !Type.Index { + const gop = try analyzer.module.map.arrays.getOrPut(analyzer.allocator, array); + + if (!gop.found_existing) { + const type_index = try analyzer.module.types.array.append(analyzer.allocator, .{ + .array = array, + }); + gop.value_ptr.* = type_index; } const result = gop.value_ptr.*; @@ -2976,10 +3182,10 @@ const Analyzer = struct { if (import_file.file.is_new) { const new_file_index = import_file.file.index; try analyzer.module.generateAbstractSyntaxTreeForFile(analyzer.allocator, new_file_index); - var value = Value{ + const value_index = try analyzer.module.values.array.append(analyzer.allocator, .{ .unresolved = undefined, - }; - const analyze_result = try analyzeFile(&value, analyzer.allocator, analyzer.module, new_file_index); + }); + const analyze_result = try analyzeFile(value_index, analyzer.allocator, analyzer.module, new_file_index); logln(.sema, .node, "Done analyzing {s}!", .{import_name}); const result = Value{ .type = analyze_result, @@ -2987,7 +3193,7 @@ const Analyzer = struct { break :blk result; } else { const result = Value{ - .type = import_file.file.ptr.type, + .type = analyzer.module.values.files.get(import_file.file.index).type, }; assert(!result.type.invalid); @@ -3004,13 +3210,12 @@ const Analyzer = struct { .byte_count = 8, }, }; - const number_allocation = try analyzer.unresolvedAllocate(scope_index, argument_expect_type, intrinsic_argument_node_list.items[0]); - const number = number_allocation.index; + const number = try analyzer.unresolvedAllocate(scope_index, argument_expect_type, intrinsic_argument_node_list.items[0]); assert(!number.invalid); var arguments = std.mem.zeroes([6]Value.Index); for (intrinsic_argument_node_list.items[1..], 0..) |argument_node_index, argument_index| { - const argument_allocation = try analyzer.unresolvedAllocate(scope_index, argument_expect_type, argument_node_index); - arguments[argument_index] = argument_allocation.index; + const argument_value_index = try analyzer.unresolvedAllocate(scope_index, argument_expect_type, argument_node_index); + arguments[argument_index] = argument_value_index; } // TODO: typecheck for usize @@ -3019,11 +3224,11 @@ const Analyzer = struct { // } break :blk Value{ - .syscall = (try analyzer.module.syscalls.append(analyzer.allocator, .{ + .syscall = try analyzer.module.values.syscalls.append(analyzer.allocator, .{ .number = number, .arguments = arguments, .argument_count = @intCast(intrinsic_argument_node_list.items.len - 1), - })).index, + }), }; } else { unreachable; @@ -3040,14 +3245,14 @@ const Analyzer = struct { }, .cast => blk: { assert(intrinsic_argument_node_list.items.len == 1); - const value_to_cast = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, intrinsic_argument_node_list.items[0]); - const value_type = value_to_cast.ptr.getType(analyzer.module); + const value_to_cast_index = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, intrinsic_argument_node_list.items[0]); + const value_type = analyzer.getValueType(value_to_cast_index); assert(expect_type != .none); const cast_result = try analyzer.canCast(expect_type, value_type); if (cast_result == .success) { - const cast = try analyzer.module.casts.append(analyzer.allocator, .{ - .value = value_to_cast.index, + const cast_index = try analyzer.module.values.casts.append(analyzer.allocator, .{ + .value = value_to_cast_index, .type = switch (expect_type) { .none => unreachable, .flexible_integer => |flexible_integer| if (flexible_integer.sign) |sign| switch (sign) { @@ -3060,11 +3265,12 @@ const Analyzer = struct { else => unreachable, }, .type_index => |type_index| type_index, + else => unreachable, }, }); break :blk Value{ - .cast = cast.index, + .cast = cast_index, }; } else { std.debug.panic("Can't cast", .{}); @@ -3074,16 +3280,21 @@ const Analyzer = struct { return result; } + + fn getValueType(analyzer: *Analyzer, value_index: Value.Index) Type.Index { + const value_type_index = analyzer.module.values.array.get(value_index).getType(analyzer.module); + return value_type_index; + } }; -pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_value: *Value) !void { - _ = try analyzeExistingPackage(main_value, compilation, module, package); +pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_value_index: Value.Index) !void { + _ = try analyzeExistingPackage(main_value_index, compilation, module, package); - var decl_iterator = module.declarations.iterator(); + var decl_iterator = module.values.declarations.iterator(); while (decl_iterator.nextPointer()) |decl| { const declaration_name = module.getName(decl.name).?; if (equal(u8, declaration_name, "_start")) { - const value = module.values.get(decl.init_value); + const value = module.values.array.get(decl.init_value); module.entry_point = switch (value.*) { .function_definition => |function_index| function_index, .unresolved => panic("Unresolved declaration: {s}", .{declaration_name}), @@ -3096,17 +3307,17 @@ pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, } } -pub fn analyzeExistingPackage(value: *Value, compilation: *Compilation, module: *Module, package: *Package) !Type.Index { +pub fn analyzeExistingPackage(value_index: Value.Index, compilation: *Compilation, module: *Module, package: *Package) !Type.Index { const package_import = try module.importPackage(compilation.base_allocator, package); assert(!package_import.file.is_new); const file_index = package_import.file.index; - return try analyzeFile(value, compilation.base_allocator, module, file_index); + return try analyzeFile(value_index, compilation.base_allocator, module, file_index); } -pub fn analyzeFile(value: *Value, allocator: Allocator, module: *Module, file_index: File.Index) !Type.Index { - const file = module.files.get(file_index); - assert(value.* == .unresolved); +pub fn analyzeFile(value_index: Value.Index, allocator: Allocator, module: *Module, file_index: File.Index) !Type.Index { + const file = module.values.files.get(file_index); + assert(module.values.array.get(value_index).* == .unresolved); assert(file.status == .parsed); var analyzer = Analyzer{ @@ -3118,6 +3329,6 @@ pub fn analyzeFile(value: *Value, allocator: Allocator, module: *Module, file_in const node = file.syntactic_analyzer_result.nodes.items[0]; const node_list_node = analyzer.getFileNode(file_index, node.left); const nodes = analyzer.getFileNodeList(file_index, node_list_node); - const result = try analyzer.structType(value, Scope.Index.invalid, nodes.items, file_index, .{ .value = 0 }); + const result = try analyzer.structType(value_index, Scope.Index.invalid, nodes.items, file_index, .{ .value = 0 }); return result; } diff --git a/bootstrap/frontend/syntactic_analyzer.zig b/bootstrap/frontend/syntactic_analyzer.zig index e919af3..ab34821 100644 --- a/bootstrap/frontend/syntactic_analyzer.zig +++ b/bootstrap/frontend/syntactic_analyzer.zig @@ -115,6 +115,7 @@ pub const Node = struct { unsigned_integer_type, signed_integer_type, slice_type, + array_type, argument_declaration, compiler_intrinsic, ssize_type, @@ -123,6 +124,8 @@ pub const Node = struct { call, const_many_pointer_type, many_pointer_type, + zero_terminated_const_many_pointer_type, + zero_terminated_many_pointer_type, enum_literal, address_of, pointer_dereference, @@ -178,6 +181,7 @@ pub const Node = struct { for_condition, for_loop, add_assign, + undefined, }; }; @@ -446,13 +450,18 @@ const Analyzer = struct { }, else => try analyzer.assignExpressionStatement(), }, - .fixed_keyword_unreachable, .fixed_keyword_return => try analyzer.assignExpressionStatement(), + .fixed_keyword_unreachable, + .fixed_keyword_return, + .discard, + => try analyzer.assignExpressionStatement(), .fixed_keyword_while => try analyzer.whileExpression(options), .fixed_keyword_switch => try analyzer.switchExpression(), .fixed_keyword_if => try analyzer.ifExpression(), .fixed_keyword_for => try analyzer.forExpression(), - .fixed_keyword_const, .fixed_keyword_var => try analyzer.symbolDeclaration(), + .fixed_keyword_const, + .fixed_keyword_var, + => try analyzer.symbolDeclaration(), .hash => blk: { const intrinsic = try analyzer.compilerIntrinsic(); _ = try analyzer.expectToken(.semicolon); @@ -581,9 +590,13 @@ const Analyzer = struct { const payload = if (analyzer.tokens[analyzer.token_i].id == .vertical_bar) blk: { analyzer.token_i += 1; - const payload_identifier_node = try analyzer.identifierNode(); + const payload_node = switch (analyzer.tokens[analyzer.token_i].id) { + .identifier => try analyzer.identifierNode(), + .discard => try analyzer.discardNode(), + else => unreachable, + }; _ = try analyzer.expectToken(.vertical_bar); - break :blk payload_identifier_node; + break :blk payload_node; } else Node.Index.invalid; const if_block = try analyzer.block(.{ .is_comptime = false }); @@ -660,7 +673,14 @@ const Analyzer = struct { var payload_nodes = ArrayList(Node.Index){}; while (analyzer.tokens[analyzer.token_i].id != .vertical_bar) { - const payload_identifier = try analyzer.expectToken(.identifier); + const payload_token = analyzer.token_i; + const id: Node.Id = switch (analyzer.tokens[payload_token].id) { + .identifier => .identifier, + .discard => .discard, + else => |t| @panic(@tagName(t)), + }; + + analyzer.token_i += 1; switch (analyzer.tokens[analyzer.token_i].id) { .vertical_bar => {}, @@ -669,8 +689,8 @@ const Analyzer = struct { } try payload_nodes.append(analyzer.allocator, try analyzer.addNode(.{ - .id = .identifier, - .token = payload_identifier, + .id = id, + .token = payload_token, .left = Node.Index.invalid, .right = Node.Index.invalid, })); @@ -1105,6 +1125,8 @@ const Analyzer = struct { .keyword_unsigned_integer, .fixed_keyword_enum, .fixed_keyword_struct, + .discard, + .fixed_keyword_undefined, => try analyzer.curlySuffixExpression(), .fixed_keyword_fn => try analyzer.function(), .fixed_keyword_return => try analyzer.addNode(.{ @@ -1168,6 +1190,7 @@ const Analyzer = struct { fn pointerTypeExpression(analyzer: *Analyzer, arguments: struct { many: bool, + zero_terminated: bool = false, start_token: Token.Index, }) !Node.Index { const is_const = analyzer.tokens[analyzer.token_i].id == .fixed_keyword_const; @@ -1181,12 +1204,21 @@ const Analyzer = struct { } const pointer_element_type = try analyzer.typeExpression(); + if (!arguments.many) { + assert(!arguments.zero_terminated); + } return try analyzer.addNode(.{ .id = switch (arguments.many) { - true => switch (is_const) { - true => .const_many_pointer_type, - false => .many_pointer_type, + true => switch (arguments.zero_terminated) { + true => switch (is_const) { + true => .const_many_pointer_type, + false => .many_pointer_type, + }, + false => switch (is_const) { + true => .zero_terminated_const_many_pointer_type, + false => .zero_terminated_many_pointer_type, + }, }, false => switch (is_const) { true => .const_single_pointer_type, @@ -1224,11 +1256,19 @@ const Analyzer = struct { .ampersand => blk: { // many item pointer analyzer.token_i += 2; + var zero_terminated: bool = false; + if (analyzer.tokens[analyzer.token_i].id == .colon) { + // TODO: parse properly + analyzer.token_i += 1; + zero_terminated = true; + analyzer.token_i += 1; + } _ = try analyzer.expectToken(.right_bracket); break :blk try analyzer.pointerTypeExpression(.{ .many = true, .start_token = first, + .zero_terminated = zero_terminated, }); }, .asterisk => @panic("Meant to use ampersand?"), @@ -1236,25 +1276,38 @@ const Analyzer = struct { const left_bracket = analyzer.token_i; analyzer.token_i += 1; // TODO: compute length - const length_expression = false; + const length_expression = switch (analyzer.tokens[analyzer.token_i].id) { + .identifier => try analyzer.expression(), + .right_bracket => Node.Index.invalid, + else => |t| @panic(@tagName(t)), + }; + _ = try analyzer.expectToken(.right_bracket); - // Slice - if (!length_expression) { - // TODO: modifiers - const is_const = analyzer.tokens[analyzer.token_i].id == .fixed_keyword_const; - analyzer.token_i += @intFromBool(is_const); + switch (length_expression.invalid) { + true => analyzer.token_i += @intFromBool(analyzer.tokens[analyzer.token_i].id == .fixed_keyword_const), + false => {}, + } - const slice_type = try analyzer.typeExpression(); - return try analyzer.addNode(.{ + const type_expression = try analyzer.typeExpression(); + const node = switch (length_expression.invalid) { + false => Node{ + .id = .array_type, + .token = left_bracket, + .left = length_expression, + .right = type_expression, + }, + true => Node{ // TODO: modifiers .id = .slice_type, .token = left_bracket, .left = Node.Index.invalid, - .right = slice_type, - }); - } else { - unreachable; - } + .right = type_expression, + }, + }; + + const node_index = try analyzer.addNode(node); + + return node_index; }, }, }; @@ -1359,6 +1412,18 @@ const Analyzer = struct { return try analyzer.nodeList(list); } + fn discardNode(analyzer: *Analyzer) !Node.Index { + const token = analyzer.token_i; + assert(analyzer.tokens[token].id == .discard); + analyzer.token_i += 1; + return try analyzer.addNode(.{ + .id = .discard, + .token = token, + .left = Node.Index.invalid, + .right = Node.Index.invalid, + }); + } + fn primaryTypeExpression(analyzer: *Analyzer) anyerror!Node.Index { const token_i = analyzer.token_i; const token = analyzer.tokens[token_i]; @@ -1387,8 +1452,18 @@ const Analyzer = struct { }); }, .identifier => analyzer.identifierNode(), + .discard => try analyzer.discardNode(), .fixed_keyword_noreturn => analyzer.noReturn(), .fixed_keyword_true, .fixed_keyword_false => analyzer.boolLiteral(), + .fixed_keyword_undefined => analyzer.addNode(.{ + .id = .undefined, + .token = blk: { + analyzer.token_i += 1; + break :blk token_i; + }, + .left = Node.Index.invalid, + .right = Node.Index.invalid, + }), .fixed_keyword_null => analyzer.addNode(.{ .id = .null_literal, .token = blk: { @@ -1653,10 +1728,8 @@ const Analyzer = struct { const identifier_token = analyzer.token_i; assert(analyzer.tokens[identifier_token].id == .identifier); analyzer.token_i += 1; - const identifier = analyzer.bytes(identifier_token); - // logln("identifier: {s}", .{identifier}); return try analyzer.addNode(.{ - .id = if (equal(u8, identifier, "_")) .discard else .identifier, + .id = .identifier, .token = identifier_token, .left = Node.Index.invalid, .right = Node.Index.invalid, diff --git a/build.zig b/build.zig index edad48c..9968dd0 100644 --- a/build.zig +++ b/build.zig @@ -7,7 +7,7 @@ pub fn build(b: *std.Build) !void { const optimization = b.standardOptimizeOption(.{}); const use_llvm = b.option(bool, "use_llvm", "Use LLVM as the backend for generate the compiler binary") orelse true; const exe = b.addExecutable(.{ - .name = "nativity", + .name = "nat", .root_source_file = .{ .path = "bootstrap/main.zig" }, .target = target, .optimize = optimization, @@ -25,6 +25,7 @@ pub fn build(b: *std.Build) !void { }); const run_command = b.addRunArtifact(exe); + run_command.step.dependOn(b.getInstallStep()); const debug_command = switch (@import("builtin").os.tag) { .linux => blk: { diff --git a/ci.sh b/ci.sh index 9c9c55e..e4a5c75 100755 --- a/ci.sh +++ b/ci.sh @@ -19,51 +19,65 @@ failed_tests=() for dir in $test_directory do MY_TESTNAME=${dir##*/} - zig build run -Duse_llvm=$nativity_use_llvm -- $dir/main.nat -log ir + zig build run -Duse_llvm=$nativity_use_llvm -- $dir/main.nat + if [[ "$?" == "0" ]]; then passed_compilation_count=$(($passed_compilation_count + 1)) if [[ "$OSTYPE" == "linux-gnu"* ]]; then nat/$MY_TESTNAME + if [[ "$?" == "0" ]]; then passed_test_count=$(($passed_test_count + 1)) - result="PASSED" + result="\e[32mPASSED\e[0m" else failed_test_count=$(($failed_test_count + 1)) - result="FAILED" + result="\e[31mFAILED\e[0m" failed_tests+=("$test_i. $MY_TESTNAME") fi - echo "[$test_i/$total_test_count] [$result] $MY_TESTNAME" + ran_test_count=$(($ran_test_count + 1)) + else + result="\e[31mOS NOT SUPPORTED\e[0m" fi else failed_compilation_count=$(($failed_compilation_count + 1)) - echo "$MY_TESTNAME failed to compile" + result="\e[31mCOMPILATION FAILURE\e[0m" failed_compilations+=("$test_i. $MY_TESTNAME") fi + + echo -e "[$test_i/$total_test_count] [$result] $MY_TESTNAME" + test_i=$(($test_i + 1)) done -echo "Ran $total_test_count compilations ($passed_compilation_count succeeded, $failed_compilation_count failed)." -echo "Ran $ran_test_count tests ($passed_test_count passed, $failed_test_count failed)." +printf "\n" +echo "[SUMMARY]" +echo "=========" +echo -e "Ran $total_test_count compilations (\e[32m$passed_compilation_count\e[0m succeeded, \e[31m$failed_compilation_count\e[0m failed)." +echo -e "Ran $ran_test_count tests (\e[32m $passed_test_count\e[0m passed, \e[31m$failed_test_count\e[0m failed)." if [[ "$failed_compilation_count" != "0" ]]; then - echo "Failed compilations:" + printf $"\nFailed compilations:\n" for failed_compilation in "${failed_compilations[@]}" do - echo "$failed_compilation" + echo -e "\e[31m$failed_compilation\e[0m" done fi + if [[ "$failed_test_count" != "0" ]]; then + echo $'\n' echo "Failed tests:" for failed_test in "${failed_tests[@]}" do - echo "$failed_test" + echo -e "\e[31m$failed_test\e[0m" done fi if [[ "$failed_test_count" == "0" && "$failed_compilation_count" == "0" ]]; then + echo -e "\e[32mSUCCESS!\e[0m" true else + echo -e "\e[31mFAILURE!\e[0m" false fi diff --git a/lib/std/os.nat b/lib/std/os.nat index a15a118..98ad789 100644 --- a/lib/std/os.nat +++ b/lib/std/os.nat @@ -1,3 +1,5 @@ +const std = #import("std"); +const Allocator = std.Allocator; const current = #import("builtin").os; const system = switch (current) { .linux => linux, @@ -56,21 +58,70 @@ const allocate_virtual_memory = fn(address: ?[&]u8, length: usize, general_prote const protection_flags = system.get_protection_flags(flags = general_protection_flags); const map_flags = system.get_map_flags(flags = general_map_flags); - const result = switch (#import("builtin").os) { - .linux => linux.mmap(address, length, protection_flags, map_flags, fd = -1, offset = 0), + switch (current) { + .linux => { + if (linux.unwrapSyscall(syscall_result = linux.mmap(address, length, protection_flags, map_flags, fd = -1, offset = 0))) |result_address| { + const pointer: [&]u8 = #cast(result_address); + return pointer; + } else { + return null; + } + }, else => #error("OS not supported"), - }; + } - return result; } const free_virtual_memory = fn(bytes_ptr: [&]const u8, bytes_len: usize) bool { - const result = switch (#import("builtin").os) { - .linux => linux.munmap(bytes_ptr, bytes_len), + switch (current) { + .linux => { + if (linux.unwrapSyscall(syscall_result = linux.munmap(bytes_ptr, bytes_len))) |result| { + return result == 0; + } else { + return false; + } + }, else => #error("OS not supported"), - }; + } +} - return result; +const readlink = fn(file_path: [&:0]const u8, buffer: []u8) ?[]u8 { + switch (current) { + .linux => { + const raw_result = linux.readlink(file_path, bytes_ptr = buffer.ptr, bytes_len = buffer.len); + + if (linux.unwrapSyscall(syscall_result = raw_result)) |byte_count| { + const bytes = buffer[0..byte_count]; + return bytes; + } else { + return null; + } + }, + else => #error("OS not supported"), + } +} + +const max_path_byte_count = switch (current) { + .linux => 0x1000, + else => #error("OS not supported"), +}; + +const currentExecutablePath = fn(allocator: &Allocator) ?[]u8 { + switch (current) { + .linux => { + var buffer: [max_path_byte_count]u8 = undefined; + if (readlink(file_path = "/proc/self/exe", buffer = buffer.&)) |bytes| { + if (allocator.duplicateBytes(bytes)) |result| { + return result; + } else { + return null; + } + } else { + return null; + } + }, + else => #error("OS not supported"), + } } const linux = #import("os/linux.nat"); diff --git a/lib/std/os/linux.nat b/lib/std/os/linux.nat index 22e8aa6..2bcf15f 100644 --- a/lib/std/os/linux.nat +++ b/lib/std/os/linux.nat @@ -33,12 +33,26 @@ const get_map_flags = fn(flags: std.os.MapFlags) MapFlags{ }; } -const mmap = fn(address: ?[&]u8, length: usize, protection_flags: ProtectionFlags, map_flags: MapFlags, fd: s32, offset: u64) ?[&]u8 { +const mmap = fn(address: ?[&]u8, length: usize, protection_flags: ProtectionFlags, map_flags: MapFlags, fd: s32, offset: u64) usize { const result = #syscall(9, #cast(address), length, #cast(protection_flags), #cast(map_flags), fd, offset); - return #cast(result); + return result; } -const munmap = fn(bytes_ptr: [&]const u8, bytes_len: usize) bool { - const result: ssize = #syscall(11, #cast(bytes_ptr), bytes_len); - return result == 0; +const munmap = fn(bytes_ptr: [&]const u8, bytes_len: usize) usize { + const result = #syscall(11, #cast(bytes_ptr), bytes_len); + return result; +} + +const readlink = fn(file_path: [&:0]const u8, bytes_ptr: [&]u8, bytes_len: usize) usize { + const result = #syscall(89, #cast(file_path), #cast(bytes_ptr), bytes_len); + return result; +} + +const unwrapSyscall = fn(syscall_result: usize) ?usize { + const signed_syscall_result: ssize = #cast(syscall_result); + if (signed_syscall_result >= 0) { + return syscall_result; + } else { + return null; + } } diff --git a/lib/std/std.nat b/lib/std/std.nat index fd4e741..270db68 100644 --- a/lib/std/std.nat +++ b/lib/std/std.nat @@ -31,6 +31,15 @@ const Allocator = struct { return false; } } + + const duplicateBytes = fn (allocator: &Allocator, bytes: []const u8) ?[]u8 { + if (allocator.allocate(size = bytes.len, alignment = 0)) |result| { + copyBytes(destination = result, source = bytes); + return result; + } else { + return null; + } + } }; const PageAllocator = struct{ @@ -85,4 +94,11 @@ const PageAllocator = struct{ } }; +const copyBytes = fn(destination: []u8, source: []const u8) void { + assert(ok = destination.len == source.len); + for (0..destination.len) |i| { + destination[i] = source[i]; + } +} + var page_allocator = PageAllocator{}; diff --git a/test/self_exe_path/main.nat b/test/self_exe_path/main.nat new file mode 100644 index 0000000..4ed822d --- /dev/null +++ b/test/self_exe_path/main.nat @@ -0,0 +1,13 @@ +const std = #import("std"); +const print = std.print; + +const main = fn () s32 { + if (std.os.currentExecutablePath(allocator = std.page_allocator.allocator.&)) |result| { + print(bytes_ptr = result.ptr, bytes_len = result.len); + print(bytes_ptr = "\n", bytes_len = 1); + return 0; + } else { + print(bytes_ptr = "Failed\n", bytes_len = 7); + return 1; + } +}