From 826bfc86b27633de3d7d2cd669e182c03ed0d964 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 19 Dec 2023 19:17:44 +0100 Subject: [PATCH] implement extern functionality --- bootstrap/Compilation.zig | 18 ++-- bootstrap/backend/c_transpiler.zig | 105 ++++++++++++++++------ bootstrap/frontend/semantic_analyzer.zig | 48 ++++++---- bootstrap/frontend/syntactic_analyzer.zig | 18 +++- 4 files changed, 137 insertions(+), 52 deletions(-) diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index 4d038be..b5e4e9c 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -563,7 +563,6 @@ pub const Declaration = struct { }; pub const Function = struct { - scope: Scope.Index, body: Block.Index, prototype: Type.Index, @@ -571,11 +570,12 @@ pub const Function = struct { arguments: ArrayList(Declaration.Index), return_type: Type.Index, attributes: Attributes = .{}, + scope: Scope.Index, pub const List = BlockList(@This()); pub const Index = Prototype.List.Index; - pub const Attributes = packed struct { + pub const Attributes = struct { @"extern": bool = false, @"export": bool = false, @"inline": Inline = .none, @@ -885,7 +885,6 @@ 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, @@ -908,9 +907,16 @@ pub const Value = union(enum) { _ = module; return switch (value.*) { + .bool, + .void, + .function_definition, + .function_declaration, + .type, + .enum_field, + .string_literal, + => true, .integer => |integer| integer.type.eq(Type.comptime_int), .declaration_reference => false, - .bool, .void, .function_definition, .type, .enum_field => true, // TODO: .call, // .syscall, @@ -1048,7 +1054,8 @@ pub const Module = struct { function_prototypes: BlockList(Function.Prototype) = .{}, } = .{}, map: struct { - functions: data_structures.AutoArrayHashMap(Function.Index, Declaration.Index) = .{}, + function_definitions: data_structures.AutoArrayHashMap(Function.Index, Declaration.Index) = .{}, + function_declarations: data_structures.AutoArrayHashMap(Function.Index, Declaration.Index) = .{}, strings: StringKeyMap([]const u8) = .{}, imports: StringArrayHashMap(File.Index) = .{}, types: data_structures.AutoArrayHashMap(Type.Index, Declaration.Index) = .{}, @@ -1057,6 +1064,7 @@ pub const Module = struct { pointers: data_structures.AutoArrayHashMap(Type.Pointer, Type.Index) = .{}, optionals: data_structures.AutoArrayHashMap(Type.Index, Type.Index) = .{}, arrays: data_structures.AutoArrayHashMap(Type.Array, Type.Index) = .{}, + libraries: data_structures.StringArrayHashMap(void) = .{}, } = .{}, main_package: *Package, entry_point: Function.Index = Function.Index.invalid, diff --git a/bootstrap/backend/c_transpiler.zig b/bootstrap/backend/c_transpiler.zig index 1474100..b1a6b57 100644 --- a/bootstrap/backend/c_transpiler.zig +++ b/bootstrap/backend/c_transpiler.zig @@ -64,7 +64,8 @@ pub const TranslationUnit = struct { global_variable_declarations: ArrayList(u8) = .{}, function_definitions: ArrayList(u8) = .{}, syscall_bitset: SyscallBitset = SyscallBitset.initEmpty(), - function_set: AutoArrayHashMap(Compilation.Function.Index, []const u8) = .{}, + function_definition_set: AutoArrayHashMap(Compilation.Function.Index, []const u8) = .{}, + function_declaration_set: AutoArrayHashMap(Compilation.Function.Index, []const u8) = .{}, macro_set: std.EnumSet(Macro) = std.EnumSet(Macro).initEmpty(), struct_type_set: TypeSet = .{}, optional_type_set: TypeSet = .{}, @@ -102,6 +103,13 @@ pub const TranslationUnit = struct { \\ ); + { + var function_declarations = module.types.function_declarations.iterator(); + while (function_declarations.nextIndex()) |function_declaration_index| { + _ = try unit.writeFunctionDeclaration(module, allocator, function_declaration_index); + } + } + { var function_definitions = module.types.function_definitions.iterator(); while (function_definitions.nextIndex()) |function_definition_index| { @@ -112,18 +120,33 @@ pub const TranslationUnit = struct { return unit; } + fn writeFunctionDeclaration(unit: *TranslationUnit, module: *Module, allocator: Allocator, function_declaration_index: Compilation.Function.Index) ![]const u8 { + if (unit.function_declaration_set.getIndex(function_declaration_index)) |index| { + return unit.function_declaration_set.values()[index]; + } else { + const function_name = try unit.renderFunctionDeclarationName(module, allocator, function_declaration_index); + try unit.writeFunctionHeader(module, &unit.function_declarations, allocator, module.types.function_declarations.get(function_declaration_index), function_name); + try unit.function_declaration_set.putNoClobber(allocator, function_declaration_index, function_name); + + try unit.function_declarations.appendSlice(allocator, ";\n\n"); + + return function_name; + } + } + fn writeFunctionDefinition(unit: *TranslationUnit, module: *Module, allocator: Allocator, function_definition_index: Compilation.Function.Index) ![]const u8 { - if (unit.function_set.getIndex(function_definition_index)) |index| { - return unit.function_set.values()[index]; + if (unit.function_definition_set.getIndex(function_definition_index)) |index| { + return unit.function_definition_set.values()[index]; } else { const function_definition = module.types.function_definitions.get(function_definition_index); const function_prototype_type = function_definition.prototype; 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); - try unit.function_set.putNoClobber(allocator, function_definition_index, function_name); + const function_name = try unit.renderFunctionDefinitionName(module, allocator, function_definition_index); + try unit.writeFunctionHeader(module, &unit.function_declarations, allocator, function_definition, function_name); + try unit.function_definition_set.putNoClobber(allocator, function_definition_index, function_name); - _ = try unit.writeFunctionHeader(module, &unit.function_definitions, allocator, function_definition_index); + try unit.writeFunctionHeader(module, &unit.function_definitions, allocator, function_definition, function_name); try unit.function_declarations.appendSlice(allocator, ";\n\n"); try unit.function_definitions.append(allocator, ' '); @@ -387,17 +410,28 @@ pub const TranslationUnit = struct { return result; } - fn renderFunctionName(unit: *TranslationUnit, module: *Module, allocator: Allocator, function_index: Compilation.Function.Index) ![]const u8 { - const function_definition = module.types.function_definitions.get(function_index); + fn renderFunctionDefinitionName(unit: *TranslationUnit, module: *Module, allocator: Allocator, function_definition_index: Compilation.Function.Index) ![]const u8 { + const function_definition = module.types.function_definitions.get(function_definition_index); const function_prototype_type = module.types.array.get(function_definition.prototype); const function_prototype_index = function_prototype_type.function; 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.map.functions.get(function_index).?; + const function_declaration_index = module.map.function_definitions.get(function_definition_index).?; const name = try unit.renderDeclarationName(module, allocator, function_declaration_index, mangle); return name; } + fn renderFunctionDeclarationName(unit: *TranslationUnit, module: *Module, allocator: Allocator, function_declaration_index: Compilation.Function.Index) ![]const u8 { + const function_declaration = module.types.function_declarations.get(function_declaration_index); + const function_prototype_type = module.types.array.get(function_declaration.prototype); + const function_prototype_index = function_prototype_type.function; + const function_prototype = module.types.function_prototypes.get(function_prototype_index); + const mangle = !(function_prototype.attributes.@"export" or function_prototype.attributes.@"extern"); + const declaration_index = module.map.function_declarations.get(function_declaration_index).?; + const name = try unit.renderDeclarationName(module, allocator, declaration_index, mangle); + return name; + } + fn renderDeclarationName(unit: *TranslationUnit, module: *Module, allocator: Allocator, declaration_index: Compilation.Declaration.Index, mangle: bool) anyerror![]const u8 { if (unit.declaration_set.getIndex(declaration_index)) |index| { return unit.declaration_set.values()[index]; @@ -481,6 +515,11 @@ pub const TranslationUnit = struct { try list.appendSlice(allocator, "int main(int argc, char** argv, char** envp)"); } else { const function_prototype = module.types.function_prototypes.get(function_prototype_index); + + if (function_prototype.attributes.@"extern") { + try list.appendSlice(allocator, "extern "); + } + switch (function_prototype.attributes.calling_convention) { .system_v => {}, .naked => try list.appendSlice(allocator, "[[gnu::naked]] "), @@ -494,14 +533,12 @@ pub const TranslationUnit = struct { try list.append(allocator, '('); - if (function_prototype.arguments.items.len > 0) { for (function_prototype.arguments.items, 0..) |argument_index, index| { _ = index; const arg_declaration = module.values.declarations.get(argument_index); - if (is_main) { - } else { + if (is_main) {} else { try unit.writeType(module, list, allocator, arg_declaration.getType(), ' '); } try list.append(allocator, ' '); @@ -517,15 +554,11 @@ 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.types.function_definitions.get(function_index); - const function_prototype_type = module.types.array.get(function_definition.prototype); + fn writeFunctionHeader(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, function: *const Compilation.Function, name: []const u8) !void { + const function_prototype_type = module.types.array.get(function.prototype); const function_prototype_index = function_prototype_type.function; try unit.writeFunctionPrototype(module, list, allocator, function_prototype_index, name); - - return name; } fn writeType(unit: *TranslationUnit, module: *Module, list: *ArrayList(u8), allocator: Allocator, type_index: Type.Index, separation_character: u8) anyerror!void { @@ -1241,12 +1274,10 @@ pub const TranslationUnit = struct { const call = module.values.calls.get(call_index); const call_value = module.values.array.get(call.value); var argument_declarations = ArrayList(Compilation.Declaration.Index){}; - switch (call_value.*) { - .function_definition => |function_definition_index| { - const name = try unit.renderFunctionName(module, allocator, function_definition_index); - // if (equal(u8, name, "os_execute")) { - // @breakpoint(); - // } + + const callable_name = switch (call_value.*) { + .function_definition => |function_definition_index| blk: { + const name = try unit.renderFunctionDefinitionName(module, allocator, function_definition_index); const function_definition = module.types.function_definitions.get(function_definition_index); const function_prototype_type = module.types.array.get(function_definition.prototype); const function_prototype = module.types.function_prototypes.get(function_prototype_type.function); @@ -1254,8 +1285,22 @@ pub const TranslationUnit = struct { try list.appendSlice(allocator, name); try list.append(allocator, '('); + + break :blk name; }, - .field_access => |field_access_index| { + .function_declaration => |function_declaration_index| blk: { + const name = try unit.renderFunctionDeclarationName(module, allocator, function_declaration_index); + const function_declaration = module.types.function_declarations.get(function_declaration_index); + const function_prototype_type = module.types.array.get(function_declaration.prototype); + const function_prototype = module.types.function_prototypes.get(function_prototype_type.function); + argument_declarations = function_prototype.arguments; + + try list.appendSlice(allocator, name); + try list.append(allocator, '('); + + break :blk name; + }, + .field_access => |field_access_index| blk: { 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, @@ -1291,9 +1336,11 @@ pub const TranslationUnit = struct { const field_name = module.getName(field.name).?; try list.appendSlice(allocator, field_name); try list.append(allocator, '('); + break :blk "field_access"; }, else => |t| @panic(@tagName(t)), - } + }; + _ = callable_name; if (!call.arguments.invalid) { const argument_list = module.values.argument_lists.get(call.arguments); @@ -1961,6 +2008,11 @@ pub fn initialize(compilation: *Compilation, module: *Module) !void { try zig_command_line.append(allocator, "-lc"); } + for (module.map.libraries.keys()) |library_name| { + const library_argument = try std.mem.concat(allocator, u8, &.{ "-l", library_name }); + try zig_command_line.append(allocator, library_argument); + } + const local_cache_dir = std.fs.cwd().realpathAlloc(allocator, "zig-cache") catch b: { std.fs.cwd().makeDir("nat/zig-cache") catch {}; break :b try std.fs.cwd().realpathAlloc(allocator, "nat/zig-cache"); @@ -1972,7 +2024,6 @@ pub fn initialize(compilation: *Compilation, module: *Module) !void { try zig_command_line.append(allocator, "--global-cache-dir"); try zig_command_line.append(allocator, global_cache_dir); - try zig_command_line.append(allocator, try std.mem.concat(allocator, u8, &.{ "-femit-bin=", module.descriptor.executable_path })); try zig_command_line.append(allocator, "-cflags"); diff --git a/bootstrap/frontend/semantic_analyzer.zig b/bootstrap/frontend/semantic_analyzer.zig index 764814c..617781d 100644 --- a/bootstrap/frontend/semantic_analyzer.zig +++ b/bootstrap/frontend/semantic_analyzer.zig @@ -667,6 +667,7 @@ const Analyzer = struct { const left_type = switch (left_value_index.invalid) { 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, + .function_declaration => |function_index| analyzer.module.types.function_prototypes.get(analyzer.module.types.array.get(analyzer.module.types.function_declarations.get(function_index).prototype).function).return_type, .field_access => |field_access_index| blk: { 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); @@ -723,6 +724,15 @@ const Analyzer = struct { }; break :b try analyzer.processCallToFunctionPrototype(scope_index, function_prototype_index, call_argument_node_list.items, method_object); }, + .function_declaration => |function_index| { + const function_declaration = analyzer.module.types.function_declarations.get(function_index); + const function_prototype_index = analyzer.module.types.array.get(function_declaration.prototype).function; + // TODO: + assert(!is_field_access); + const method_object = Value.Index.invalid; + + 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.values.field_accesses.get(field_access_index); const container_field = analyzer.module.types.container_fields.get(field_access.field); @@ -1693,7 +1703,10 @@ const Analyzer = struct { 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.map.functions.put(analyzer.allocator, function_index, declaration_index); + try analyzer.module.map.function_definitions.put(analyzer.allocator, function_index, declaration_index); + }, + .function_declaration => |function_index| { + try analyzer.module.map.function_declarations.put(analyzer.allocator, function_index, declaration_index); }, .type => |type_index| { try analyzer.module.map.types.put(analyzer.allocator, type_index, declaration_index); @@ -1880,17 +1893,10 @@ const Analyzer = struct { }, .compiler_intrinsic => try analyzer.compilerIntrinsic(scope_index, expect_type, node_index), .function_definition => blk: { - const function_scope_index = try analyzer.module.values.scopes.append(analyzer.allocator, .{ - .parent = scope_index, - .file = analyzer.module.values.scopes.get(scope_index).file, - .token = node.token, - }); - - logln(.sema, .type, "Creating function scope #{}. Parent #{}", .{ function_scope_index.uniqueInteger(), scope_index.uniqueInteger() }); - - const function_prototype_index = try analyzer.functionPrototype(function_scope_index, node.left); + const function_prototype_index = try analyzer.functionPrototype(scope_index, node.left); const function_prototype = analyzer.module.types.function_prototypes.get(function_prototype_index); assert(!function_prototype.attributes.@"extern"); + const function_scope_index = function_prototype.scope; const expected_type = ExpectType{ .type_index = analyzer.functionPrototypeReturnType(function_prototype_index), @@ -1904,7 +1910,6 @@ const Analyzer = struct { const function_index = try analyzer.module.types.function_definitions.append(analyzer.allocator, .{ .prototype = prototype_type_index, .body = function_body, - .scope = function_scope_index, }); const result = Value{ @@ -1924,7 +1929,6 @@ const Analyzer = struct { 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 Value{ .function_declaration = function_declaration_index, @@ -1933,7 +1937,6 @@ const Analyzer = struct { false => unreachable, }; }, - .simple_while => unreachable, .block => blk: { const block_index = try analyzer.block(scope_index, expect_type, node_index); break :blk Value{ @@ -3077,12 +3080,18 @@ const Analyzer = struct { return type_index; } - fn processSimpleFunctionPrototype(analyzer: *Analyzer, scope_index: Scope.Index, simple_function_prototype_node_index: Node.Index) !Function.Prototype { - const simple_function_prototype_node = analyzer.getScopeNode(scope_index, simple_function_prototype_node_index); + fn processSimpleFunctionPrototype(analyzer: *Analyzer, old_scope_index: Scope.Index, simple_function_prototype_node_index: Node.Index) !Function.Prototype { + const simple_function_prototype_node = analyzer.getScopeNode(old_scope_index, simple_function_prototype_node_index); assert(simple_function_prototype_node.id == .simple_function_prototype); const arguments_node_index = simple_function_prototype_node.left; const return_type_node_index = simple_function_prototype_node.right; + const scope_index = try analyzer.module.values.scopes.append(analyzer.allocator, .{ + .parent = old_scope_index, + .file = analyzer.module.values.scopes.get(old_scope_index).file, + .token = simple_function_prototype_node.token, + }); + var argument_declarations = ArrayList(Declaration.Index){}; switch (arguments_node_index.invalid) { true => {}, @@ -3128,6 +3137,7 @@ const Analyzer = struct { return .{ .arguments = argument_declarations, .return_type = return_type, + .scope = scope_index, }; } @@ -3149,8 +3159,14 @@ const Analyzer = struct { const attribute_node = analyzer.getScopeNode(scope_index, attribute_node_index); switch (attribute_node.id) { - .extern_qualifier => function_prototype.attributes.@"extern" = true, .export_qualifier => function_prototype.attributes.@"export" = true, + .extern_qualifier => { + const string_literal_node = analyzer.getScopeNode(scope_index, attribute_node.left); + const original_string_literal = analyzer.tokenStringLiteral(scope_index, string_literal_node.token); + const fixed_string_literal = try fixupStringLiteral(analyzer.allocator, original_string_literal); + _ = try analyzer.module.map.libraries.getOrPut(analyzer.allocator, fixed_string_literal); + function_prototype.attributes.@"extern" = true; + }, .calling_convention => { const calling_convention_type_declaration = try analyzer.forceDeclarationAnalysis(scope_index, "std.builtin.CallingConvention"); const calling_convention_type = switch (analyzer.module.values.array.get(calling_convention_type_declaration).*) { diff --git a/bootstrap/frontend/syntactic_analyzer.zig b/bootstrap/frontend/syntactic_analyzer.zig index a5ae29c..9df6f72 100644 --- a/bootstrap/frontend/syntactic_analyzer.zig +++ b/bootstrap/frontend/syntactic_analyzer.zig @@ -384,13 +384,22 @@ const Analyzer = struct { .left_brace, => break, .fixed_keyword_extern => b: { - const result = try analyzer.addNode(.{ - .id = .extern_qualifier, - .token = attribute_token, + analyzer.consumeToken(); + _ = try analyzer.expectToken(.left_parenthesis); + const string_literal = try analyzer.addNode(.{ + .id = .string_literal, + .token = try analyzer.expectToken(.string_literal), .left = Node.Index.invalid, .right = Node.Index.invalid, }); - analyzer.consumeToken(); + _ = try analyzer.expectToken(.right_parenthesis); + + const result = try analyzer.addNode(.{ + .id = .extern_qualifier, + .token = attribute_token, + .left = string_literal, + .right = Node.Index.invalid, + }); break :b result; }, .fixed_keyword_export => b: { @@ -1391,6 +1400,7 @@ const Analyzer = struct { .right_parenthesis, .left_brace, .equal, + .fixed_keyword_extern, => return node_index, else => |t| @panic(@tagName(t)), }