From 9b2d29e8d4acb395699361f79893e9312a40b722 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Tue, 29 Apr 2025 06:29:32 -0600 Subject: [PATCH] wip --- src/bootstrap.zig | 800 ++++++++++++++++-------------- src/compiler.bbb | 427 +++++++++++++++- src/main.zig | 3 + tests/forward_declared_type.bbb | 17 + tests/generic_pointer_array.bbb | 14 + tests/self_referential_struct.bbb | 11 + 6 files changed, 904 insertions(+), 368 deletions(-) create mode 100644 tests/forward_declared_type.bbb create mode 100644 tests/generic_pointer_array.bbb create mode 100644 tests/self_referential_struct.bbb diff --git a/src/bootstrap.zig b/src/bootstrap.zig index 53df3a0..75dab43 100644 --- a/src/bootstrap.zig +++ b/src/bootstrap.zig @@ -396,7 +396,7 @@ pub const Type = struct { }; } - fn resolve(ty: *Type, module: *Module) void { + fn resolve_abi(ty: *Type, module: *Module) void { if (ty.llvm.abi == null) { const abi_type = switch (ty.bb) { .void, .noreturn => module.llvm.void_type, @@ -439,7 +439,11 @@ pub const Type = struct { else => @trap(), }; ty.llvm.abi = abi_type; + } + } + fn resolve_memory(ty: *Type, module: *Module) void { + if (ty.llvm.memory == null) { const memory_type = switch (ty.bb) { .void, .noreturn, @@ -447,7 +451,7 @@ pub const Type = struct { .array, .structure, .@"union", - => abi_type, + => ty.llvm.abi.?, .integer => module.llvm.context.get_integer_type(@intCast(ty.get_byte_size() * 8)).to_type(), .enumerator => |enumerator| enumerator.backing_type.llvm.memory.?, .bits => |bits| bits.backing_type.llvm.memory.?, // TODO: see assert below @@ -456,8 +460,12 @@ pub const Type = struct { }; ty.llvm.memory = memory_type; if (ty.bb == .bits) assert(ty.llvm.memory == ty.llvm.abi); + } + } - if (module.has_debug_info) { + fn resolve_debug(ty: *Type, module: *Module) void { + if (module.has_debug_info) { + if (ty.llvm.debug == null) { const debug_type = switch (ty.bb) { .void, .noreturn => module.llvm.di_builder.create_basic_type(ty.name, 0, .void, .{ .no_return = ty.bb == .noreturn }), .integer => |integer| module.llvm.di_builder.create_basic_type(ty.name, @max(lib.next_power_of_two(integer.bit_count), 8), switch (integer.bit_count) { @@ -467,12 +475,16 @@ pub const Type = struct { false => .unsigned, }, }, .{}), - .pointer => |pointer| b: { - pointer.type.resolve(module); - break :b module.llvm.di_builder.create_pointer_type(pointer.type.llvm.debug.?, 64, 64, 0, ty.name).to_type(); + .pointer => |pointer| p: { + pointer.type.resolve_debug(module); + break :p if (ty.llvm.debug) |d| d else b: { + const pointer_type = module.llvm.di_builder.create_pointer_type(pointer.type.llvm.debug.?, 64, 64, 0, ty.name).to_type(); + break :b pointer_type; + }; }, .array => |array| module.llvm.di_builder.create_array_type(array.element_count, 0, array.element_type.llvm.debug.?, &.{}).to_type(), .enumerator => |enumerator| blk: { + enumerator.backing_type.resolve_debug(module); var enumerator_buffer: [64]*llvm.DI.Enumerator = undefined; const enumerators = enumerator_buffer[0..enumerator.fields.len]; for (enumerators, enumerator.fields) |*enumerator_pointer, *field| { @@ -483,21 +495,21 @@ pub const Type = struct { break :blk enumeration_type.to_type(); }, .structure => |structure| blk: { - const struct_type = module.llvm.di_builder.create_replaceable_composite_type(module.llvm.debug_tag, ty.name, module.scope.llvm.?, module.llvm.file, structure.line); - ty.llvm.debug = struct_type.to_type(); + const fwd = module.llvm.di_builder.create_replaceable_composite_type(module.llvm.debug_tag, ty.name, module.scope.llvm.?, module.llvm.file, structure.line); + ty.llvm.debug = fwd.to_type(); module.llvm.debug_tag += 1; var llvm_debug_member_type_buffer: [64]*llvm.DI.Type.Derived = undefined; const llvm_debug_member_types = llvm_debug_member_type_buffer[0..structure.fields.len]; for (structure.fields, llvm_debug_member_types) |field, *llvm_debug_member_type| { - field.type.resolve(module); + field.type.resolve_debug(module); const member_type = module.llvm.di_builder.create_member_type(module.scope.llvm.?, field.name, module.llvm.file, field.line, field.type.get_byte_size() * 8, @intCast(field.type.get_byte_alignment() * 8), field.bit_offset, .{}, field.type.llvm.debug.?); llvm_debug_member_type.* = member_type; } const debug_struct_type = module.llvm.di_builder.create_struct_type(module.scope.llvm.?, ty.name, module.llvm.file, structure.line, structure.bit_size, @intCast(structure.bit_alignment), .{}, llvm_debug_member_types); - const forward_declared: *llvm.DI.Type.Composite = @ptrCast(ty.llvm.debug); + const forward_declared: *llvm.DI.Type.Composite = @ptrCast(fwd); forward_declared.replace_all_uses_with(debug_struct_type); break :blk debug_struct_type.to_type(); }, @@ -512,6 +524,7 @@ pub const Type = struct { break :blk struct_type.to_type(); }, .alias => |alias| blk: { + alias.type.resolve_debug(module); const typedef = module.llvm.di_builder.create_typedef(alias.type.llvm.debug.?, ty.name, module.llvm.file, alias.line, alias.scope.llvm.?, 0); break :blk typedef.to_type(); }, @@ -524,7 +537,7 @@ pub const Type = struct { const llvm_debug_member_types = llvm_debug_member_type_buffer[0..union_type.fields.len]; for (union_type.fields, llvm_debug_member_types) |field, *llvm_debug_member_type| { - field.type.resolve(module); + field.type.resolve_debug(module); const member_type = module.llvm.di_builder.create_member_type(module.scope.llvm.?, field.name, module.llvm.file, field.line, field.type.get_byte_size() * 8, @intCast(field.type.get_byte_alignment() * 8), 0, .{}, field.type.llvm.debug.?); llvm_debug_member_type.* = member_type; } @@ -536,11 +549,18 @@ pub const Type = struct { }, else => @trap(), }; + ty.llvm.debug = debug_type; } } } + fn resolve(ty: *Type, module: *Module) void { + ty.resolve_abi(module); + ty.resolve_memory(module); + ty.resolve_debug(module); + } + const Bits = struct { fields: []const Struct.Field, backing_type: *Type, @@ -555,14 +575,10 @@ pub const Type = struct { pub const Pointer = struct { type: *Type, - alignment: ?u32, - pub fn get_alignment(p: *Pointer) u32 { - if (p.alignment) |a| return a else { - const type_alignment = p.type.get_byte_alignment(); - p.alignment = type_alignment; - return type_alignment; - } + pub fn get_alignment(p: *const Pointer) u32 { + const type_alignment = p.type.get_byte_alignment(); + return type_alignment; } }; @@ -1182,6 +1198,17 @@ pub const Scope = struct { kind: Scope.Kind, parent: ?*Scope, + pub fn to_module(scope: *Scope) *Module { + assert(scope.kind == .global); + @trap(); + } + + pub fn to_block(scope: *Scope) *LexicalBlock { + assert(scope.kind == .local); + const block: *LexicalBlock = @fieldParentPtr("scope", scope); + return block; + } + pub const Kind = enum { global, function, @@ -1271,6 +1298,26 @@ pub const Module = struct { has_debug_info: bool, silent: bool, + const Checkpoint = struct { + offset: u64, + line_offset: u64, + line_character_offset: u64, + }; + + pub fn get_checkpoint(module: *Module) Checkpoint { + return .{ + .offset = module.offset, + .line_offset = module.line_offset, + .line_character_offset = module.line_character_offset, + }; + } + + pub fn set_checkpoint(module: *Module, checkpoint: Checkpoint) void { + module.offset = checkpoint.offset; + module.line_offset = checkpoint.line_offset; + module.line_character_offset = checkpoint.line_character_offset; + } + const LLVM = struct { context: *llvm.Context, module: *llvm.Module, @@ -1563,19 +1610,17 @@ pub const Module = struct { const Pointer = struct { type: *Type, - alignment: ?u32 = null, }; pub fn get_pointer_type(module: *Module, pointer: Pointer) *Type { const p = Type.Pointer{ .type = pointer.type, - .alignment = if (pointer.alignment) |a| a else if (pointer.type.bb != .unresolved) pointer.type.get_byte_alignment() else null, }; const all_types = module.types.get_slice(); const pointer_type = for (module.pointer_types.get_slice()) |pointer_type_index| { const ty = &all_types[pointer_type_index]; const pointer_type = &all_types[pointer_type_index].bb.pointer; - if (pointer_type.type == p.type and pointer_type.alignment == p.alignment) { + if (pointer_type.type == p.type) { break ty; } } else blk: { @@ -1646,13 +1691,13 @@ pub const Module = struct { }, false => { var length_inferred = false; - const offset = module.offset; + const checkpoint = module.get_checkpoint(); if (module.consume_character_if_match('_')) { module.skip_space(); if (module.consume_character_if_match(']')) { length_inferred = true; } else { - module.offset = offset; + module.set_checkpoint(checkpoint); } } @@ -1736,11 +1781,9 @@ pub const Module = struct { const Slice = struct { type: *Type, - alignment: ?u32 = null, }; pub fn get_slice_type(module: *Module, slice: Slice) *Type { - const alignment = if (slice.alignment) |a| a else slice.type.get_byte_alignment(); const all_types = module.types.get_slice(); for (module.slice_types.get_slice()) |slice_type_index| { @@ -1749,13 +1792,12 @@ pub const Module = struct { assert(struct_type.is_slice); assert(struct_type.fields.len == 2); const pointer_type = struct_type.fields[0].type; - if (pointer_type.bb.pointer.type == slice.type and pointer_type.bb.pointer.alignment == alignment) { + if (pointer_type.bb.pointer.type == slice.type) { return ty; } } else { const pointer_type = module.get_pointer_type(.{ .type = slice.type, - .alignment = slice.alignment, }); const length_type = module.integer_type(64, false); @@ -1853,17 +1895,46 @@ pub const Module = struct { fn skip_space(noalias module: *Module) void { while (true) { const offset = module.offset; - while (module.offset < module.content.len and is_space(module.content[module.offset])) { - module.line_offset += @intFromBool(module.content[module.offset] == '\n'); - module.line_character_offset = if (module.content[module.offset] == '\n') module.offset else module.line_character_offset; + + while (true) { + if (module.offset == module.content.len) { + break; + } + + assert(module.offset < module.content.len); + + const ch = module.content[module.offset]; + + const is_space_ch = is_space(ch); + if (!is_space_ch) { + break; + } + + module.line_offset += @intFromBool(ch == '\n'); + module.line_character_offset = if (ch == '\n') module.offset else module.line_character_offset; module.offset += 1; } if (module.offset + 1 < module.content.len) { const i = module.offset; - const is_comment = module.content[i] == '/' and module.content[i + 1] == '/'; + const first_ch = module.content[i]; + const second_ch = module.content[i + 1]; + const is_comment = first_ch == '/' and second_ch == '/'; + if (is_comment) { - while (module.offset < module.content.len and module.content[module.offset] != '\n') { + while (true) { + if (module.offset == module.content.len) { + break; + } + + assert(module.offset < module.content.len); + + const ch = module.content[module.offset]; + + if (ch == '\n') { + break; + } + module.offset += 1; } @@ -2537,7 +2608,7 @@ pub const Module = struct { var result = value_builder.left; const precedence = value_builder.precedence; while (true) { - const checkpoint = module.offset; + const checkpoint = module.get_checkpoint(); const token = module.tokenize(); const token_rule = &rules[@intFromEnum(token)]; const token_precedence: Precedence = switch (token_rule.precedence) { @@ -2548,7 +2619,7 @@ pub const Module = struct { else => |p| p, }; if (@intFromEnum(precedence) > @intFromEnum(token_precedence)) { - module.offset = checkpoint; + module.set_checkpoint(checkpoint); break; } @@ -2562,12 +2633,15 @@ pub const Module = struct { } fn parse_statement(module: *Module, scope: *Scope) *Statement { + var require_semicolon = true; + const statement_line = module.get_line(); const statement_column = module.get_column(); - const statement_start_character = module.content[module.offset]; const statement = module.statements.add(); - var require_semicolon = true; + + const statement_start_character = module.content[module.offset]; + statement.* = .{ .bb = switch (statement_start_character) { '>' => blk: { @@ -2597,7 +2671,7 @@ pub const Module = struct { }; switch (scope.kind) { .local => { - const block: *LexicalBlock = @fieldParentPtr("scope", scope); + const block = scope.to_block(); _ = block.locals.append(local); }, else => @trap(), @@ -2610,6 +2684,7 @@ pub const Module = struct { .expression = module.parse_value(scope, .{}), }, 'A'...'Z', 'a'...'z' => blk: { + const checkpoint = module.get_checkpoint(); const statement_start_identifier = module.parse_identifier(); if (lib.string.to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| switch (statement_start_keyword) { @@ -2636,10 +2711,11 @@ pub const Module = struct { var is_else = false; if (is_identifier_start_ch(module.content[module.offset])) { + const else_checkpoint = module.get_checkpoint(); const identifier = module.parse_identifier(); is_else = lib.string.equal(identifier, "else"); if (!is_else) { - module.offset -= identifier.len; + module.set_checkpoint(else_checkpoint); } else { module.skip_space(); } @@ -2772,10 +2848,7 @@ pub const Module = struct { const token = module.tokenize(); const kind: Statement.ForEach.Kind = switch (token) { - .@")" => b: { - module.offset += 1; - break :b .slice; - }, + .@")" => .slice, .@".." => b: { if (left_value_count != 1) { module.report_error(); @@ -2794,28 +2867,6 @@ pub const Module = struct { }; statement.bb.for_each.kind = kind; - module.skip_space(); - // if (!module.consume_character_if_match(',')) { - // module.expect_character(right_parenthesis); - // @trap(); - // } - // - // while (true) { - // module.skip_space(); - // - // right_value_buffer[right_value_count] = module.parse_value(scope, .{ - // .kind = .left, - // }); - // right_value_count += 1; - // - // module.skip_space(); - // - // if (!module.consume_character_if_match(',')) { - // module.expect_character(right_parenthesis); - // break; - // } - // } - if (kind == .slice and left_value_count != right_value_count) { module.report_error(); } @@ -2831,6 +2882,8 @@ pub const Module = struct { statement.bb.for_each.left_values = left_values; statement.bb.for_each.right_values = right_values; + module.skip_space(); + const predicate = module.parse_statement(&statement.bb.for_each.scope); statement.bb.for_each.predicate = predicate; @@ -2862,10 +2915,11 @@ pub const Module = struct { const is_else = is_else_blk: { var is_else = false; if (is_identifier_start_ch(module.content[module.offset])) { + const else_checkpoint = module.get_checkpoint(); const i = module.parse_identifier(); is_else = lib.string.equal(i, "else"); if (!is_else) { - module.offset -= i.len; + module.set_checkpoint(else_checkpoint); } } @@ -2936,7 +2990,7 @@ pub const Module = struct { .@"break" => break :blk .break_statement, .@"continue" => break :blk .continue_statement, } else { - module.offset -= statement_start_identifier.len; + module.set_checkpoint(checkpoint); const left = module.parse_value(scope, .{ .kind = .left, @@ -4010,14 +4064,17 @@ pub const Module = struct { const global_name = module.parse_identifier(); - if (module.types.find_by_name(global_name) != null) { - module.report_error(); - } - if (module.globals.find_by_name(global_name) != null) { module.report_error(); } + const forward_declaration = if (module.types.find_by_name(global_name)) |fwd| b: { + if (fwd.bb != .forward_declaration) { + module.report_error(); + } + break :b fwd; + } else null; + module.skip_space(); var global_type: ?*Type = null; @@ -4035,7 +4092,7 @@ pub const Module = struct { var global_keyword = false; if (is_identifier_start_ch(module.content[module.offset])) { - const identifier_offset = module.offset; + const checkpoint = module.get_checkpoint(); const global_string = module.parse_identifier(); module.skip_space(); @@ -4377,90 +4434,85 @@ pub const Module = struct { .@"struct" => { module.skip_space(); - module.expect_character(left_brace); - - if (module.types.find_by_name(global_name) != null) { - @trap(); - } - - const struct_type = module.types.append(.{ + const struct_type = if (forward_declaration) |fwd| fwd else module.types.append(.{ .name = global_name, .bb = .forward_declaration, }); - var field_buffer: [256]Type.Struct.Field = undefined; - var field_count: u64 = 0; - var byte_offset: u64 = 0; - var byte_alignment: u32 = 1; - var bit_alignment: u32 = 1; + if (module.consume_character_if_match(left_brace)) { + var field_buffer: [256]Type.Struct.Field = undefined; + var field_count: u64 = 0; + var byte_offset: u64 = 0; + var byte_alignment: u32 = 1; + var bit_alignment: u32 = 1; - while (true) { - module.skip_space(); + while (true) { + module.skip_space(); - if (module.consume_character_if_match(right_brace)) { - break; + if (module.consume_character_if_match(right_brace)) { + break; + } + + const field_line = module.get_line(); + const field_name = module.parse_identifier(); + + module.skip_space(); + + module.expect_character(':'); + + module.skip_space(); + + const field_type = module.parse_type(); + + const field_byte_alignment = field_type.get_byte_alignment(); + const field_bit_alignment = field_byte_alignment * 8; + const field_byte_size = field_type.get_byte_size(); + + const field_byte_offset = lib.align_forward_u64(byte_offset, field_byte_alignment); + const field_bit_offset = field_byte_offset * 8; + + field_buffer[field_count] = .{ + .byte_offset = field_byte_offset, + .bit_offset = field_bit_offset, + .type = field_type, + .name = field_name, + .line = field_line, + }; + + byte_alignment = @max(byte_alignment, field_byte_alignment); + bit_alignment = @max(bit_alignment, field_bit_alignment); + byte_offset = field_byte_offset + field_byte_size; + + field_count += 1; + + module.skip_space(); + + _ = module.consume_character_if_match(','); } - const field_line = module.get_line(); - const field_name = module.parse_identifier(); - module.skip_space(); - module.expect_character(':'); + _ = module.consume_character_if_match(';'); - module.skip_space(); + const byte_size = byte_offset; - const field_type = module.parse_type(); + const fields = module.arena.allocate(Type.Struct.Field, field_count); + @memcpy(fields, field_buffer[0..field_count]); - const field_byte_alignment = field_type.get_byte_alignment(); - const field_bit_alignment = field_byte_alignment * 8; - const field_byte_size = field_type.get_byte_size(); - - const field_byte_offset = lib.align_forward_u64(byte_offset, field_byte_alignment); - const field_bit_offset = field_byte_offset * 8; - - field_buffer[field_count] = .{ - .byte_offset = field_byte_offset, - .bit_offset = field_bit_offset, - .type = field_type, - .name = field_name, - .line = field_line, + struct_type.bb = .{ + .structure = .{ + .bit_size = byte_size * 8, + .byte_size = byte_size, + .bit_alignment = bit_alignment, + .byte_alignment = byte_alignment, + .fields = fields, + .is_slice = false, + .line = global_line, + }, }; - - byte_alignment = @max(byte_alignment, field_byte_alignment); - bit_alignment = @max(bit_alignment, field_bit_alignment); - byte_offset = field_byte_offset + field_byte_size; - - field_count += 1; - - module.skip_space(); - - switch (module.content[module.offset]) { - ',' => module.offset += 1, - else => {}, - } + } else { + module.expect_character(';'); } - - module.skip_space(); - - _ = module.consume_character_if_match(';'); - - const byte_size = byte_offset; - - const fields = module.arena.allocate(Type.Struct.Field, field_count); - @memcpy(fields, field_buffer[0..field_count]); - - struct_type.bb = .{ - .structure = .{ - .bit_size = byte_size * 8, - .byte_size = byte_size, - .bit_alignment = bit_alignment, - .byte_alignment = byte_alignment, - .fields = fields, - .is_slice = false, - .line = global_line, - }, - }; }, .typealias => { const aliased_type = module.parse_type(); @@ -4688,10 +4740,7 @@ pub const Module = struct { module.skip_space(); - switch (module.content[module.offset]) { - ',' => module.offset += 1, - else => {}, - } + _ = module.consume_character_if_match(','); } module.skip_space(); @@ -4713,7 +4762,7 @@ pub const Module = struct { }, } } else { - module.offset = identifier_offset; + module.set_checkpoint(checkpoint); } } @@ -4951,7 +5000,7 @@ pub const Module = struct { .scalar => module.create_load(.{ .type = va_arg.type, .value = llvm_address }), .aggregate => if (left_llvm) |l| b: { uint64.resolve(module); - _ = module.llvm.builder.create_memcpy(l, left_type.?.bb.pointer.alignment.?, llvm_address, va_arg.type.get_byte_alignment(), uint64.llvm.abi.?.to_integer().get_constant(va_arg.type.get_byte_size(), 0).to_value()); + _ = module.llvm.builder.create_memcpy(l, left_type.?.bb.pointer.get_alignment(), llvm_address, va_arg.type.get_byte_alignment(), uint64.llvm.abi.?.to_integer().get_constant(va_arg.type.get_byte_size(), 0).to_value()); break :b l; } else llvm_address, .complex => @trap(), @@ -5189,32 +5238,44 @@ pub const Module = struct { } } else { assert(argument_abi.abi_count == 1); - // TODO: handmade change - if (src.type.?.bb != .pointer) { - assert(src.kind == .right); - assert(src.type.?.bb == .structure); - src.type = null; - src.kind = .left; - module.analyze_value_type(src, .{}); - } - module.emit_value(src, .memory); - - assert(src.type.?.bb == .pointer); - const source_type = src.type.?.bb.pointer.type; - assert(source_type == argument_abi.semantic_type); const destination_type = argument_abi.get_coerce_to_type(); - const load = module.create_coerced_load(src.llvm.?, source_type, destination_type); - const is_cmse_ns_call = false; - if (is_cmse_ns_call) { - @trap(); - } - const maybe_undef = false; - if (maybe_undef) { - @trap(); - } + const v = switch (src.bb) { + .zero => destination_type.llvm.abi.?.get_zero().to_value(), + else => b: { + // TODO: handmade change + if (src.type.?.bb != .pointer) { + assert(src.kind == .right); + assert(src.type.?.bb == .structure); + const ty = src.type.?; + src.type = null; + src.kind = .left; + module.analyze_value_type(src, .{ .type = module.get_pointer_type(.{ .type = ty }) }); + } - llvm_abi_argument_value_buffer[abi_argument_count] = load; + + assert(src.type.?.bb == .pointer); + assert(src.type.?.llvm.abi == module.llvm.pointer_type); + module.emit_value(src, .memory); + + const source_type = src.type.?.bb.pointer.type; + assert(source_type == argument_abi.semantic_type); + const load = module.create_coerced_load(src.llvm.?, source_type, destination_type); + + const is_cmse_ns_call = false; + if (is_cmse_ns_call) { + @trap(); + } + const maybe_undef = false; + if (maybe_undef) { + @trap(); + } + + break :b load; + }, + }; + + llvm_abi_argument_value_buffer[abi_argument_count] = v; abi_argument_count += 1; } } @@ -5866,18 +5927,18 @@ pub const Module = struct { } } - if (lib.optimization_mode == .Debug) { - const verify_result = llvm_function_value.verify(); - if (!verify_result.success) { - lib.print_string(module.llvm.module.to_string()); - lib.print_string("============================\n"); - lib.print_string(llvm_function_value.to_string()); - lib.print_string("============================\n"); - lib.print_string(verify_result.error_message orelse unreachable); - lib.print_string("\n============================\n"); - lib.os.abort(); - } - } + // if (lib.optimization_mode == .Debug) { + // const verify_result = llvm_function_value.verify(); + // if (!verify_result.success) { + // lib.print_string(module.llvm.module.to_string()); + // lib.print_string("============================\n"); + // lib.print_string(llvm_function_value.to_string()); + // lib.print_string("============================\n"); + // lib.print_string(verify_result.error_message orelse unreachable); + // lib.print_string("\n============================\n"); + // lib.os.abort(); + // } + // } }, .global => { module.analyze(global.variable.initial_value, .{ .type = global.variable.type }, .memory); @@ -5917,6 +5978,26 @@ pub const Module = struct { } } + if (false) { + const fwd = module.llvm.di_builder.create_replaceable_composite_type(0, "S", module.scope.llvm.?, module.llvm.file, 0); + var types: [1]*llvm.DI.Type.Derived = undefined; + const pointer_type = module.llvm.di_builder.create_pointer_type(fwd.to_type(), 64, 64, 0, "&S"); + types[0] = module.llvm.di_builder.create_member_type(module.scope.llvm.?, "self", module.llvm.file, 0, 64, 64, 0, .{}, pointer_type.to_type()); + const debug_struct_type = module.llvm.di_builder.create_struct_type(module.scope.llvm.?, "S", module.llvm.file, 0, 64, 64, .{}, &types); + fwd.replace_all_uses_with(debug_struct_type); + const struct_type = module.llvm.context.get_struct_type(&.{module.llvm.pointer_type}); + const llvm_function_type = llvm.Type.Function.get(struct_type.to_type(), &.{}, false); + const llvm_function_value = module.llvm.module.create_function(.{ + .name = "foo", + .linkage = .ExternalLinkage, + .type = llvm_function_type, + }); + + const subroutine_type = module.llvm.di_builder.create_subroutine_type(module.llvm.file, &.{debug_struct_type.to_type()}, .{}); + const subprogram = module.llvm.di_builder.create_function(module.scope.llvm.?, "foo", "foo", module.llvm.file, 0, subroutine_type, false, false, 0, .{}, false); + llvm_function_value.set_subprogram(subprogram); + } + if (module.has_debug_info) { module.llvm.di_builder.finalize(); } @@ -6094,7 +6175,6 @@ pub const Module = struct { fn fully_resolve_alias(module: *Module, ty: *Type) *Type { const result_type = switch (ty.bb) { .bits, - .structure, .@"union", .integer, .enumerator, @@ -6103,6 +6183,10 @@ pub const Module = struct { .void, .function, => ty, + .structure => |structure| switch (structure.is_slice) { + true => module.get_slice_type(.{ .type = module.fully_resolve_alias(structure.fields[0].type.bb.pointer.type) }), + false => ty, + }, .alias => |alias| alias.type, .pointer => |pointer| module.get_pointer_type(.{ .type = module.fully_resolve_alias(pointer.type) }), else => @trap(), @@ -6159,6 +6243,29 @@ pub const Module = struct { .expression => |v| .{ .expression = module.clone_value(scope, v), }, + .local => |old_local| blk: { + assert(old_local.argument_index == null); + const new_local = module.locals.add(); + new_local.* = .{ + .variable = .{ + .storage = if (old_local.variable.storage) |_| @trap() else null, + .initial_value = module.clone_value(scope, old_local.variable.initial_value), + .type = if (old_local.variable.type) |t| module.resolve_type(t) else null, + .scope = scope, + .name = old_local.variable.name, + .line = old_local.variable.line, + .column = old_local.variable.column, + }, + .argument_index = old_local.argument_index, + }; + + const block = scope.to_block(); + _ = block.locals.append(new_local); + + break :blk .{ + .local = new_local, + }; + }, else => @trap(), }; @@ -6243,9 +6350,17 @@ pub const Module = struct { }, }, .@"unreachable" => .@"unreachable", + .slice_expression => |slice_expression| .{ + .slice_expression = .{ + .array_like = module.clone_value(scope, slice_expression.array_like), + .start = if (slice_expression.start) |start| module.clone_value(scope, start) else null, + .end = if (slice_expression.end) |end| module.clone_value(scope, end) else null, + }, + }, else => @trap(), }, .kind = source.kind, + .type = if (source.type) |_| @trap() else null, }; break :blk result; @@ -7501,6 +7616,76 @@ pub const Module = struct { } } + fn emit_macro_instantiation(module: *Module, value: *Value) void { + switch (value.bb) { + .macro_instantiation => |*macro_instantiation| { + const current_function = module.current_function orelse module.report_error(); + module.current_function = null; + defer module.current_function = current_function; + + const current_macro_instantiation = module.current_macro_instantiation; + assert(current_macro_instantiation == null); + module.current_macro_instantiation = value; + defer module.current_macro_instantiation = current_macro_instantiation; + + for (macro_instantiation.instantiation_arguments) |call_argument| { + module.emit_value(call_argument, .abi); + } + + const caller_debug_location = if (module.has_debug_info) llvm.DI.create_debug_location(module.llvm.context, macro_instantiation.instantiation_line, macro_instantiation.instantiation_column, macro_instantiation.function_scope.parent.?.llvm.?, null) else undefined; + defer if (module.has_debug_info) { + module.llvm.builder.set_current_debug_location(caller_debug_location); + }; + module.inline_at_debug_location = caller_debug_location; + defer module.inline_at_debug_location = null; + + const llvm_function = current_function.variable.storage.?.llvm.?.to_function(); + const macro_entry_block = module.llvm.context.create_basic_block("macro.entry", llvm_function); + _ = module.llvm.builder.create_branch(macro_entry_block); + module.llvm.builder.position_at_end(macro_entry_block); + + const valid_return_value = switch (macro_instantiation.return_type.bb) { + .void, .noreturn => false, + else => true, + }; + + macro_instantiation.return_alloca = if (valid_return_value) module.create_alloca(.{ + .type = macro_instantiation.return_type, + .name = "macro.return", + }) else undefined; + + const macro_return_block = module.llvm.context.create_basic_block("macro.return_block", llvm_function); + macro_instantiation.return_block = macro_return_block; + + for (macro_instantiation.instantiation_arguments, macro_instantiation.declaration_arguments) |call_argument, declaration_argument| { + module.emit_local_storage(declaration_argument); + const storage = declaration_argument.variable.storage.?.llvm.?; + + switch (declaration_argument.variable.type.?.get_evaluation_kind()) { + .scalar => { + _ = module.create_store(.{ + .source_value = call_argument.llvm.?, + .destination_value = storage, + .type = declaration_argument.variable.type.?, + }); + }, + .aggregate => @trap(), + .complex => @trap(), + } + } + + module.analyze_block(macro_instantiation.block); + + if (module.llvm.builder.get_insert_block() != null) { + _ = module.llvm.builder.create_branch(macro_return_block); + } + + module.llvm.builder.position_at_end(macro_return_block); + }, + else => unreachable, + } + } + pub fn emit_value(module: *Module, value: *Value, type_kind: Type.Kind) void { const value_type = value.type orelse unreachable; const resolved_type = module.fully_resolve_alias(value_type); @@ -7716,13 +7901,11 @@ pub const Module = struct { .scalar => module.create_load(.{ .type = value_type, .value = variable.storage.?.llvm.?, - .alignment = variable.storage.?.type.?.bb.pointer.alignment, }), // TODO: this might be wrong .aggregate => module.create_load(.{ .type = value_type, .value = variable.storage.?.llvm.?, - .alignment = variable.storage.?.type.?.bb.pointer.alignment, }), .complex => @trap(), }, @@ -7850,7 +8033,7 @@ pub const Module = struct { .right => module.create_load(.{ .type = dereferenceable_value.type.?.bb.pointer.type, .value = dereferenceable_value.llvm.?, - .alignment = dereferenceable_value.type.?.bb.pointer.alignment, + .alignment = dereferenceable_value.type.?.bb.pointer.get_alignment(), }), }; break :blk result; @@ -8077,7 +8260,7 @@ pub const Module = struct { const field = bits.fields[field_index]; field.type.resolve(module); - const load = module.create_load(.{ .type = ty, .alignment = pointer_type.bb.pointer.alignment, .value = v }); + const load = module.create_load(.{ .type = ty, .alignment = pointer_type.bb.pointer.get_alignment(), .value = v }); const shift = module.llvm.builder.create_lshr(load, bits.backing_type.llvm.abi.?.to_integer().get_constant(field.bit_offset, 0).to_value()); const trunc = module.llvm.builder.create_truncate(shift, field.type.llvm.abi.?); break :blk trunc; @@ -8209,78 +8392,15 @@ pub const Module = struct { break :blk slice_value; }, .macro_instantiation => |*macro_instantiation| blk: { - const current_function = module.current_function orelse module.report_error(); - module.current_function = null; - defer module.current_function = current_function; - - const current_macro_instantiation = module.current_macro_instantiation; - assert(current_macro_instantiation == null); - module.current_macro_instantiation = value; - defer module.current_macro_instantiation = current_macro_instantiation; - - for (macro_instantiation.instantiation_arguments) |call_argument| { - module.emit_value(call_argument, .abi); - } - - const caller_debug_location = if (module.has_debug_info) llvm.DI.create_debug_location(module.llvm.context, macro_instantiation.instantiation_line, macro_instantiation.instantiation_column, macro_instantiation.function_scope.parent.?.llvm.?, null) else undefined; - defer if (module.has_debug_info) { - module.llvm.builder.set_current_debug_location(caller_debug_location); - }; - module.inline_at_debug_location = caller_debug_location; - defer module.inline_at_debug_location = null; - - const llvm_function = current_function.variable.storage.?.llvm.?.to_function(); - const macro_entry_block = module.llvm.context.create_basic_block("macro.entry", llvm_function); - _ = module.llvm.builder.create_branch(macro_entry_block); - module.llvm.builder.position_at_end(macro_entry_block); - - const valid_return_value = switch (macro_instantiation.return_type.bb) { - .void, .noreturn => false, - else => true, - }; - - macro_instantiation.return_alloca = if (valid_return_value) module.create_alloca(.{ - .type = macro_instantiation.return_type, - .name = "macro.return", - }) else undefined; - - const macro_return_block = module.llvm.context.create_basic_block("macro.return_block", llvm_function); - macro_instantiation.return_block = macro_return_block; - - for (macro_instantiation.instantiation_arguments, macro_instantiation.declaration_arguments) |call_argument, declaration_argument| { - module.emit_local_storage(declaration_argument); - const storage = declaration_argument.variable.storage.?.llvm.?; - - switch (declaration_argument.variable.type.?.get_evaluation_kind()) { - .scalar => { - _ = module.create_store(.{ - .source_value = call_argument.llvm.?, - .destination_value = storage, - .type = declaration_argument.variable.type.?, - }); - }, - .aggregate => @trap(), - .complex => @trap(), - } - } - - module.analyze_block(macro_instantiation.block); - - if (module.llvm.builder.get_insert_block() != null) { - _ = module.llvm.builder.create_branch(macro_return_block); - } - - module.llvm.builder.position_at_end(macro_return_block); - - const load = switch (valid_return_value) { - true => module.create_load(.{ + module.emit_macro_instantiation(value); + break :blk switch (macro_instantiation.return_type.bb) { + .void, .noreturn => return, + else => module.create_load(.{ .type = macro_instantiation.return_type, .value = macro_instantiation.return_alloca, .type_kind = type_kind, }), - false => return, }; - break :blk load; }, else => @trap(), }; @@ -8315,93 +8435,16 @@ pub const Module = struct { }, .noreturn => module.report_error(), else => { - const return_value = rv orelse module.report_error(); - module.analyze(return_value, .{ - .type = return_abi.semantic_type, - }, .memory); - if (module.has_debug_info) { module.llvm.builder.set_current_debug_location(last_statement_debug_location.*); } - // Clang equivalent: CodeGenFunction::EmitReturnStmt const return_alloca = module.current_function.?.variable.storage.?.bb.function.return_alloca orelse module.report_error(); + const return_value = rv orelse module.report_error(); + module.analyze_value_type(return_value, .{ .type = return_abi.semantic_type }); - switch (return_abi.semantic_type.get_evaluation_kind()) { - .scalar => { - switch (return_abi.flags.kind) { - .indirect => { - @trap(); - }, - else => { - // assert(!return_value.?.lvalue); - assert(return_value.type.?.is_abi_equal(return_abi.semantic_type, module)); - _ = module.create_store(.{ - .source_value = return_value.llvm.?, - .destination_value = return_alloca, - .type = return_abi.semantic_type, - }); - }, - } - }, - // TODO: handcoded code, might be wrong - .aggregate => switch (return_value.kind) { - .left => @trap(), - .right => { - assert(return_value.type.?.is_abi_equal(return_abi.semantic_type, module)); - _ = module.create_store(.{ - .source_value = return_value.llvm.?, - .destination_value = return_alloca, - .type = return_abi.semantic_type, - }); - }, - // switch (return_value.lvalue) { - // true => { - // const abi_alignment = return_abi.semantic_type.get_byte_alignment(); - // const abi_size = return_abi.semantic_type.get_byte_size(); - // switch (return_abi.flags.kind) { - // .indirect => { - // _ = module.llvm.builder.create_memcpy( - // unreachable, //return_alloca, - // abi_alignment, return_value.llvm, abi_alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(abi_size, @intFromBool(false)).to_value()); - // }, - // else => { - // switch (return_abi.semantic_type.get_evaluation_kind()) { - // .aggregate => { - // // TODO: this is 100% wrong, fix - // assert(abi_alignment == return_abi.semantic_type.get_byte_alignment()); - // assert(abi_size == return_abi.semantic_type.get_byte_size()); - // _ = module.llvm.builder.create_memcpy( - // unreachable, //return_alloca, - // abi_alignment, return_value.llvm, abi_alignment, module.integer_type(64, false).llvm.handle.to_integer().get_constant(abi_size, @intFromBool(false)).to_value()); - // }, - // .scalar => { - // const destination_type = return_abi.semantic_type; - // const source_type = return_abi.semantic_type; - // assert(return_value.type == source_type); - // const ret_val = switch (return_value.type.bb) { - // .pointer => return_value.llvm, - // // TODO: this feels hacky - // else => switch (return_value.lvalue) { - // true => module.create_load(.{ .type = return_value.type, .value = return_value.llvm }), - // false => return_value.llvm, - // }, - // }; - // _ = module.create_store(.{ .source_value = ret_val, .source_type = source_type, - // .destination_value = unreachable, //return_alloca, - // .destination_type = destination_type }); - // }, - // .complex => @trap(), - // } - // }, - // } - // }, - // false => { - // }, - // } - }, - .complex => @trap(), - } + const pointer_type = module.get_pointer_type(.{ .type = return_abi.semantic_type }); + module.emit_assignment(return_alloca, pointer_type, return_value); }, } @@ -8440,7 +8483,7 @@ pub const Module = struct { const pointer_type = assignment.left.type.?.bb.pointer; const element_type = pointer_type.type; assert(element_type.get_evaluation_kind() == .scalar); - const load = module.create_load(.{ .type = element_type, .value = assignment.left.llvm.?, .alignment = pointer_type.alignment }); + const load = module.create_load(.{ .type = element_type, .value = assignment.left.llvm.?, .alignment = pointer_type.get_alignment() }); module.analyze(assignment.right, .{ .type = element_type }, .memory); const a = load; const b = assignment.right.llvm.?; @@ -8511,7 +8554,7 @@ pub const Module = struct { .source_value = right, .destination_value = assignment.left.llvm.?, .type = element_type, - .alignment = pointer_type.alignment, + .alignment = pointer_type.get_alignment(), }); }, } @@ -8667,8 +8710,10 @@ pub const Module = struct { module.llvm.builder.clear_insertion_position(); } - if (!all_blocks_terminated) { - module.llvm.builder.position_at_end(exit_block); + module.llvm.builder.position_at_end(exit_block); + if (all_blocks_terminated) { + _ = module.llvm.builder.create_unreachable(); + module.llvm.builder.clear_insertion_position(); } }, else => @trap(), @@ -8775,7 +8820,7 @@ pub const Module = struct { const load = module.create_load(.{ .type = aggregate_type, .value = right.llvm.?, - .alignment = right.type.?.bb.pointer.alignment, + .alignment = right.type.?.bb.pointer.get_alignment(), }); const extract_pointer = module.llvm.builder.create_extract_value(load, 0); const gep = module.llvm.builder.create_gep(.{ @@ -8938,7 +8983,7 @@ pub const Module = struct { .source_value = right.llvm.?, .destination_value = left_llvm, .type = v_type, - .alignment = pointer_type.bb.pointer.alignment, + .alignment = pointer_type.bb.pointer.get_alignment(), }); }, .aggregate => switch (right.bb) { @@ -8957,7 +9002,7 @@ pub const Module = struct { global_variable.to_value().set_alignment(alignment); const uint64 = module.integer_type(64, false); uint64.resolve(module); - _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, global_variable.to_value(), alignment, uint64.llvm.abi.?.to_integer().get_constant(array_initialization.values.len * pointer_type.bb.pointer.type.bb.array.element_type.get_byte_size(), @intFromBool(false)).to_value()); + _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.get_alignment(), global_variable.to_value(), alignment, uint64.llvm.abi.?.to_integer().get_constant(array_initialization.values.len * pointer_type.bb.pointer.type.bb.array.element_type.get_byte_size(), @intFromBool(false)).to_value()); }, false => { assert(v_type.bb == .array); @@ -9034,7 +9079,7 @@ pub const Module = struct { uint8.resolve(module); const uint64 = module.integer_type(64, false); uint64.resolve(module); - _ = module.llvm.builder.create_memset(destination_pointer, uint8.llvm.abi.?.get_zero().to_value(), uint64.llvm.abi.?.to_integer().get_constant(memset_size, 0).to_value(), pointer_type.bb.pointer.alignment.?); + _ = module.llvm.builder.create_memset(destination_pointer, uint8.llvm.abi.?.get_zero().to_value(), uint64.llvm.abi.?.to_integer().get_constant(memset_size, 0).to_value(), pointer_type.bb.pointer.get_alignment()); } }, .@"union" => |union_type| { @@ -9051,7 +9096,14 @@ pub const Module = struct { const field_pointer_type = module.get_pointer_type(.{ .type = field_value_type }); module.emit_assignment(destination_pointer, field_pointer_type, value); if (field_type_size < union_type.byte_size) { - @trap(); + const uint8 = module.integer_type(8, false).llvm.abi.?; + const uint64 = module.integer_type(64, false).llvm.abi.?.to_integer(); + const padding_gep = module.llvm.builder.create_gep(.{ + .type = uint8, + .aggregate = destination_pointer, + .indices = &.{ uint64.get_constant(field_type_size, 0).to_value() }, + }); + _ = module.llvm.builder.create_memset(padding_gep, uint8.get_zero().to_value(), uint64.get_constant(union_type.byte_size - field_type_size, 0).to_value(), 1); } else if (field_type_size > union_type.byte_size) { unreachable; } @@ -9136,7 +9188,7 @@ pub const Module = struct { .source_value = call, .destination_value = left_llvm, .type = s2e.struct_type, - .alignment = pointer_type.bb.pointer.alignment, + .alignment = pointer_type.bb.pointer.get_alignment(), }); }, .va_start => { @@ -9186,7 +9238,7 @@ pub const Module = struct { .source_value = slice_values[0], .destination_value = left_llvm, .type = slice_pointer_type, - .alignment = pointer_type.bb.pointer.alignment, + .alignment = pointer_type.bb.pointer.get_alignment(), }); const slice_length_destination = module.llvm.builder.create_struct_gep(resolved_value_type.llvm.abi.?.to_struct(), left_llvm, 1); _ = module.create_store(.{ @@ -9200,13 +9252,13 @@ pub const Module = struct { u8_type.resolve(module); const u64_type = module.integer_type(64, false); u64_type.resolve(module); - _ = module.llvm.builder.create_memset(left_llvm, u8_type.llvm.abi.?.get_zero().to_value(), u64_type.llvm.abi.?.to_integer().get_constant(resolved_value_type.get_byte_size(), 0).to_value(), pointer_type.bb.pointer.alignment.?); + _ = module.llvm.builder.create_memset(left_llvm, u8_type.llvm.abi.?.get_zero().to_value(), u64_type.llvm.abi.?.to_integer().get_constant(resolved_value_type.get_byte_size(), 0).to_value(), pointer_type.bb.pointer.get_alignment()); }, .variable_reference => |variable| switch (right.kind) { .left => @trap(), .right => { const uint64 = module.integer_type(64, false); - _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, variable.storage.?.llvm.?, variable.storage.?.type.?.bb.pointer.alignment.?, uint64.llvm.abi.?.to_integer().get_constant(resolved_value_type.get_byte_size(), @intFromBool(false)).to_value()); + _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.get_alignment(), variable.storage.?.llvm.?, variable.storage.?.type.?.bb.pointer.get_alignment(), uint64.llvm.abi.?.to_integer().get_constant(resolved_value_type.get_byte_size(), @intFromBool(false)).to_value()); }, }, .field_access => |field_access| { @@ -9220,9 +9272,14 @@ pub const Module = struct { module.emit_value(field_access.aggregate, .memory); const gep = module.llvm.builder.create_struct_gep(struct_type.llvm.abi.?.to_struct(), field_access.aggregate.llvm.?, field_index); const uint64 = module.integer_type(64, false); - _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.alignment.?, gep, resolved_value_type.get_byte_alignment(), uint64.llvm.abi.?.to_integer().get_constant(resolved_value_type.get_byte_size(), @intFromBool(false)).to_value()); + _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.get_alignment(), gep, resolved_value_type.get_byte_alignment(), uint64.llvm.abi.?.to_integer().get_constant(resolved_value_type.get_byte_size(), @intFromBool(false)).to_value()); }, .undefined => {}, + .macro_instantiation => |*macro_instantiation| { + module.emit_macro_instantiation(right); + const uint64 = module.integer_type(64, false); + _ = module.llvm.builder.create_memcpy(left_llvm, pointer_type.bb.pointer.get_alignment(), macro_instantiation.return_alloca, resolved_value_type.get_byte_alignment(), uint64.llvm.abi.?.to_integer().get_constant(resolved_value_type.get_byte_size(), @intFromBool(false)).to_value()); + }, else => @trap(), }, .complex => @trap(), @@ -9241,7 +9298,7 @@ pub const Module = struct { .bb = .local, .llvm = module.create_alloca(.{ .type = pointer_type.bb.pointer.type, - .alignment = pointer_type.bb.pointer.alignment, + .alignment = pointer_type.bb.pointer.get_alignment(), .name = local.variable.name, }), }; @@ -9366,7 +9423,6 @@ pub const Module = struct { value: *llvm.Value, type: *Type, } { - _ = module; var source_pointer = source_value; var source_type = source_ty; assert(source_type.bb == .structure and source_type.bb.structure.fields.len > 0); @@ -9376,7 +9432,15 @@ pub const Module = struct { source_pointer = switch (first_field_size < destination_size and first_field_size < source_size) { true => source_pointer, - false => @trap(), // TODO: make sure `source_type` is also updated here + false => b: { + source_pointer = module.llvm.builder.create_struct_gep(source_type.llvm.memory.?.to_struct(), source_pointer, 0); + source_type = module.get_pointer_type(.{ .type = first_field_type }); + if (first_field_type.bb == .structure) { + @trap(); + } else { + break :b source_pointer; + } + }, }; return .{ .value = source_pointer, .type = source_type }; @@ -9497,9 +9561,10 @@ pub const Module = struct { return result; } - pub fn coerce_int_or_pointer_to_int_or_pointer(module: *Module, source: *llvm.Value, source_ty: *Type, destination_ty: *Type) *llvm.Value { + pub fn coerce_int_or_pointer_to_int_or_pointer(module: *Module, s: *llvm.Value, source_ty: *Type, destination_ty: *Type) *llvm.Value { const source_type = source_ty; var destination_type = destination_ty; + var source = s; switch (source_type == destination_type) { true => return source, false => { @@ -9507,7 +9572,7 @@ pub const Module = struct { @trap(); } else { if (source_type.bb == .pointer) { - @trap(); + source = module.llvm.builder.create_ptr_to_int(source, module.integer_type(64, false).llvm.abi.?); } if (destination_type.bb == .pointer) { @@ -9550,24 +9615,39 @@ pub const Module = struct { if (old_child_type == new_child_type) { break :blk ty; } else { - const p = module.get_pointer_type(.{ .type = new_child_type, .alignment = pointer.alignment }); + const p = module.get_pointer_type(.{ .type = new_child_type }); break :blk p; } }, .structure => |structure| blk: { var is_the_same = true; + var new_field_type_buffer: [64]*Type = undefined; - for (structure.fields, new_field_type_buffer[0..structure.fields.len]) |*field, *new_field_type| { - const old_field_type = field.type; - const new = module.resolve_type(old_field_type); - new_field_type.* = new; - is_the_same = is_the_same and old_field_type == new; + if (structure.is_slice) { + const old_field_type = structure.fields[0].type; + const new_field_type = module.resolve_type(old_field_type); + new_field_type_buffer[0] = new_field_type; + + is_the_same = old_field_type == new_field_type; + } else { + for (structure.fields, new_field_type_buffer[0..structure.fields.len]) |*field, *new_field_type| { + const old_field_type = field.type; + const new = module.resolve_type(old_field_type); + new_field_type.* = new; + is_the_same = is_the_same and old_field_type == new; + } } if (is_the_same) { break :blk ty; } else { - @trap(); + if (structure.is_slice) { + const element_type = new_field_type_buffer[0].bb.pointer.type; + const slice_type = module.get_slice_type(.{ .type = element_type }); + break :blk slice_type; + } else { + @trap(); + } } }, .integer => ty, diff --git a/src/compiler.bbb b/src/compiler.bbb index c30f9a3..c33f4f6 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -586,11 +586,32 @@ CompileOptions = struct silent: u1, } +ScopeKind = enum +{ + global, + function, + local, + for_each, + macro_declaration, + macro_instantiation_function, +} + +Scope = struct +{ + parent: &Scope, + line: u32, + column: u32, + kind: ScopeKind, +} + +Type = struct; + TypeId = enum { void, noreturn, integer, + function, } TypeInteger = struct @@ -599,9 +620,23 @@ TypeInteger = struct signed: u1, } +CallingConvention = enum +{ + c, +} + +TypeFunction = struct +{ + semantic_return_type: &Type, + semantic_argument_types: []&Type, + calling_convention: CallingConvention, + is_variable_arguments: u1, +} + TypeContent = union { integer: TypeInteger, + function: TypeFunction, } Type = struct @@ -630,11 +665,36 @@ Module = struct arena: &Arena, base_type_allocation: &Type, void_value: &Value, - // Parser data + // Parser data start content: []u8, offset: u64, line_offset: u64, line_character_offset: u64, + // Parser data end + scope: Scope, +} + +ModuleCheckpoint = struct +{ + offset: u64, + line_offset: u64, + line_character_offset: u64, +} + +get_checkpoint = fn (module: &Module) ModuleCheckpoint +{ + return { + .offset = module.offset, + .line_offset = module.line_offset, + .line_character_offset = module.line_character_offset, + }; +} + +set_checkpoint = fn (module: &Module, checkpoint: ModuleCheckpoint) void +{ + module.offset = checkpoint.offset; + module.line_offset = checkpoint.line_offset; + module.line_character_offset = checkpoint.line_character_offset; } module_integer_type = fn (module: &Module, integer: TypeInteger) &Type @@ -655,6 +715,13 @@ module_noreturn_type = fn (module: &Module) &Type return module_void_type(module) + 1; } +left_bracket: u8 = '['; +right_bracket: u8 = ']'; +left_parenthesis: u8 = '('; +right_parenthesis: u8 = ')'; +left_brace: u8 = '{'; +right_brace: u8 = '}'; + is_space = fn (ch: u8) u1 { return ch == ' ' or ch == '\n' or ch == '\t' or ch == '\r'; @@ -697,7 +764,7 @@ is_hex = fn (ch: u8) u1 is_identifier_start = fn (ch: u8) u1 { - return (ch >= 'a' and ch >= 'z') or (ch >= 'A' and ch <= 'Z') or ch == '_'; + return (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z') or ch == '_'; } is_identifier = fn (ch: u8) u1 @@ -817,8 +884,351 @@ parse_identifier = fn (module: &Module) []u8 return result; } +parse_type = fn (module: &Module) &Type +{ +} + +ValueBuilder = struct +{ + foo: u32, +} + +parse_value = fn (module: &Module, scope: &Scope, v: ValueBuilder) &Value +{ +} + +GlobalKeyword = enum +{ + export, + extern, +} + +GlobalKind = enum +{ + bits, + enum, + fn, + macro, + struct, + typealias, + union, +} + +FunctionKeyword = enum +{ + cc, +} + +InlineBehavior = enum +{ + default, + always_inline, + no_inline, + inline_hint, +} + +FunctionAttributes = struct +{ + inline_behavior: InlineBehavior, + naked: u1, +} + +new_type_array = fn (module: &Module, count: u64) []&Type +{ + >result = arena_allocate[&Type](module.arena, count); + return result[..count]; +} + +new_type = fn (module: &Module) &Type +{ + >result = arena_allocate[Type](module.arena, 1); + return result; +} + +new_value_array = fn (module: &Module, count: u64) []&Value +{ + >result = arena_allocate[&Value](module.arena, count); + return result[..count]; +} + +new_value = fn (module: &Module) &Value +{ + >result = arena_allocate[Value](module.arena, 1); + return result; +} + +allocate_value_array = fn (module: &Module, stack_array: []&Value) []&Value +{ + >result = new_value_array(module, stack_array.length); + memcpy(#pointer_cast(result.pointer), #pointer_cast(stack_array.pointer), stack_array.length * #byte_size(&Value)); +} + +allocate_type_array = fn (module: &Module, stack_array: []&Type) []&Type +{ + >result = new_type_array(module, stack_array.length); + memcpy(#pointer_cast(result.pointer), #pointer_cast(stack_array.pointer), stack_array.length * #byte_size(&Type)); +} + parse = fn (module: &Module) void { + while (1) + { + skip_space(module); + + if (module.offset == module.content.length) + { + break; + } + + >is_export: u1 = 0; + >is_extern: u1 = 0; + + >global_line = get_line(module); + >global_column = get_column(module); + + if (consume_character_if_match(module, left_bracket)) + { + while (module.offset < module.content.length) + { + >global_keyword_string = parse_identifier(module); + >global_keyword_s2e = #string_to_enum(GlobalKeyword, global_keyword_string); + if (!global_keyword_s2e.is_valid) + { + report_error(); + } + + >global_keyword = global_keyword_s2e.enum_value; + switch (global_keyword) + { + .export => { is_export = 1; } + .extern => { is_extern = 1; } + } + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + + report_error(); + } + + skip_space(module); + } + + >global_name = parse_identifier(module); + + // TODO: check if the name is repeated in global namespace + + skip_space(module); + + >global_type: &Type = zero; + + if (consume_character_if_match(module, ':')) + { + skip_space(module); + + global_type = parse_type(module); + + skip_space(module); + } + + expect_character(module, '='); + + skip_space(module); + + >has_global_keyword: u1 = 0; + + if (is_identifier_start(module.content[module.offset])) + { + >checkpoint = get_checkpoint(module); + + >global_string = parse_identifier(module); + + skip_space(module); + + >global_kind_s2e = #string_to_enum(GlobalKind, global_string); + + if (global_kind_s2e.is_valid) + { + >global_kind = global_kind_s2e.enum_value; + + switch (global_kind) + { + .bits => { + #trap(); + }, + .enum => { + #trap(); + }, + .fn => { + >calling_convention: CallingConvention = .c; + >function_attributes: FunctionAttributes = zero; + >is_variable_arguments: u1 = 0; + + if (consume_character_if_match(module, left_bracket)) + { + while (module.offset < module.content.length) + { + >function_identifier = parse_identifier(module); + + >function_keyword_s2e = #string_to_enum(FunctionKeyword, function_identifier); + if (function_keyword_s2e.is_valid) + { + >function_keyword = function_keyword_s2e.enum_value; + + skip_space(module); + + switch (function_keyword) + { + .cc => { + expect_character(module, left_parenthesis); + + skip_space(module); + + >calling_convention_string = parse_identifier(module); + + >calling_convention_s2e = #string_to_enum(CallingConvention, calling_convention_string); + + if (calling_convention_s2e.is_valid) + { + calling_convention = calling_convention_s2e.enum_value; + skip_space(module); + expect_character(module, right_parenthesis); + } + else + { + report_error(); + } + }, + } + + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + else + { + report_error(); + } + } + else + { + report_error(); + } + } + } + + skip_space(module); + + expect_character(module, left_parenthesis); + + >semantic_argument_type_buffer: [64]&Type = undefined; + >semantic_argument_name_buffer: [64][]u8 = undefined; + >semantic_argument_count: u64 = 0; + + while (module.offset < module.content.length) + { + skip_space(module); + + if (consume_character_if_match(module, '.')) + { + expect_character(module, '.'); + expect_character(module, '.'); + skip_space(module); + expect_character(module, right_parenthesis); + is_variable_arguments = 1; + break; + } + + if (consume_character_if_match(module, right_parenthesis)) + { + break; + } + + >argument_name = parse_identifier(module); + semantic_argument_name_buffer[semantic_argument_count] = argument_name; + + skip_space(module); + + expect_character(module, ':'); + + skip_space(module); + + >argument_type = parse_type(module); + semantic_argument_type_buffer[semantic_argument_count] = argument_type; + + skip_space(module); + + if (consume_character_if_match(module, ',')) + { + skip_space(module); + } + + semantic_argument_count += 1; + } + + skip_space(module); + + >return_type = parse_type(module); + >argument_types: []&Type = zero; + if (semantic_argument_count != 0) + { + argument_types = allocate_type_array(module, semantic_argument_type_buffer[..semantic_argument_count]); + } + + skip_space(module); + + >is_declaration = consume_character_if_match(module, ';'); + + >function_type = new_type(module); + function_type.& = { + .content = { + .function = { + .semantic_return_type = return_type, + .semantic_argument_types = argument_types, + .calling_convention = calling_convention, + .is_variable_arguments = is_variable_arguments, + }, + }, + .id = .function, + .name = "", + }; + + >storage = new_value(module); + + #trap(); + }, + .macro => { + #trap(); + }, + .struct => { + #trap(); + }, + .typealias => { + #trap(); + }, + .union => { + #trap(); + }, + } + } + else + { + set_checkpoint(module, checkpoint); + } + } + + if (!has_global_keyword) + { + >v = parse_value(module, &module.scope, zero); + skip_space(module); + expect_character(module, ';'); + + #trap(); + } + } } emit = fn (module: &Module) void @@ -894,22 +1304,23 @@ compile = fn (arena: &Arena, options: CompileOptions) void .name = "noreturn", }; - >void_value = arena_allocate[Value](arena, 1); - void_value.& = { - .type = void_type, - .id = .infer_or_ignore, - }; >module: Module = { .arena = arena, .base_type_allocation = base_type_allocation, - .void_value = void_value, + .void_value = undefined, .content = options.content, .offset = 0, .line_offset = 0, .line_character_offset = 0, }; + >void_value = new_value(&module); + void_value.& = { + .type = void_type, + .id = .infer_or_ignore, + }; + parse(&module); emit(&module); } diff --git a/src/main.zig b/src/main.zig index 1f23525..72d16e8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -332,4 +332,7 @@ const names = &[_][]const u8{ "generic_pointer_macro", "break_continue", "noreturn_macro", + "self_referential_struct", + "forward_declared_type", + "generic_pointer_array", }; diff --git a/tests/forward_declared_type.bbb b/tests/forward_declared_type.bbb new file mode 100644 index 0000000..967e39e --- /dev/null +++ b/tests/forward_declared_type.bbb @@ -0,0 +1,17 @@ +T = struct; +Foo = struct +{ + t: &T, +} + +T = struct +{ + f: Foo, +} + +[export] main = fn [cc(c)] () s32 +{ + >t: T = zero; + t.f.t = &t; + return 0; +} diff --git a/tests/generic_pointer_array.bbb b/tests/generic_pointer_array.bbb new file mode 100644 index 0000000..a9d11fc --- /dev/null +++ b/tests/generic_pointer_array.bbb @@ -0,0 +1,14 @@ +foo = macro[T](addr: &u64, count: u64) []T +{ + >pointer: &T = #pointer_cast(addr); + return pointer[..count]; +} + +[export] main = fn [cc(c)] () s32 +{ + >some_var: u64 = 0xaaaaaaaaaaaaaaaa; + >result: []&u8 = foo[&u8](&some_var, 1); + if (#int_from_pointer(result.pointer) != 0xaaaaaaaaaaaaaaaa) #trap(); + if (result.length != 1) #trap(); + return 0; +} diff --git a/tests/self_referential_struct.bbb b/tests/self_referential_struct.bbb new file mode 100644 index 0000000..f2f5463 --- /dev/null +++ b/tests/self_referential_struct.bbb @@ -0,0 +1,11 @@ +S = struct +{ + self: &S, +} + +[export] main = fn [cc(c)] () s32 +{ + >s: S = zero; + s.self = &s; + return 0; +}