From a7166d1b000fd3a4e842ba89d87acb188ca18b66 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Fri, 23 May 2025 20:02:10 -0600 Subject: [PATCH] Forward declared type --- CMakeLists.txt | 2 - src/compiler.bbb | 1339 +++++++++++++++++++++++++++++++- src/compiler.cpp | 25 +- src/compiler.hpp | 137 +++- src/emitter.cpp | 1257 +++++++++++++++++++----------- src/parser.cpp | 372 +++++---- tests/basic_struct_passing.bbb | 140 ++++ tests/enum_array.bbb | 22 + tests/opaque.bbb | 16 + 9 files changed, 2675 insertions(+), 635 deletions(-) create mode 100644 tests/basic_struct_passing.bbb create mode 100644 tests/enum_array.bbb create mode 100644 tests/opaque.bbb diff --git a/CMakeLists.txt b/CMakeLists.txt index 34c88aa..dfe795d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,5 +48,3 @@ target_link_libraries(bb PUBLIC ) target_compile_options(bb PRIVATE -Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -Wno-missing-designated-field-initializers -funsigned-char -fwrapv -fno-strict-aliasing) -# target_compile_options(bb PRIVATE -fsanitize=address) -# target_link_options(bb PRIVATE -fsanitize=address) diff --git a/src/compiler.bbb b/src/compiler.bbb index 6a69b54..c14f1c5 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -295,6 +295,7 @@ os_file_open = fn (path: &u8, flags: OpenFlags, permissions: OpenPermissions) Fi .trunc = flags.truncate, .creat = flags.create, .directory = flags.directory, + zero, }; >mode: mode_t = #select(permissions.execute, 0o755, 0o644); @@ -448,6 +449,12 @@ arena_allocate = macro [T] (arena: &Arena, count: u64) &T return #pointer_cast(arena_allocate_bytes(arena, #byte_size(T) * count, #align_of(T))); } +arena_allocate_slice = macro [T] (arena: &Arena, count: u64) []T +{ + >pointer: &T = #pointer_cast(arena_allocate_bytes(arena, #byte_size(T) * count, #align_of(T))); + return pointer[..count]; +} + arena_duplicate_string = fn (arena: &Arena, string: []u8) []u8 { >result = arena_allocate_bytes(arena, string.length + 1, 1); @@ -481,7 +488,7 @@ arena_join_string = fn (arena: &Arena, pieces: [][]u8) []u8 file_read = fn (arena: &Arena, path: []u8) []u8 { - >fd = os_file_open(path.pointer, { .read = 1 }, { .read = 1 }); + >fd = os_file_open(path.pointer, { .read = 1, zero }, { .read = 1, zero }); >result: []u8 = zero; @@ -533,8 +540,7 @@ CompilerCommand = enum BuildMode = enum { debug_none, - debug_fast, - debug_size, + debug, soft_optimize, optimize_for_speed, optimize_for_size, @@ -580,17 +586,119 @@ CompileOptions = struct executable: []u8, name: []u8, objects: [][]u8, + libraries: [][]u8, target: Target, build_mode: BuildMode, has_debug_info: u1, silent: u1, } +CallingConvention = enum +{ + c, +} + +InlineBehavior = enum +{ + default = 0, + always_inline = 1, + no_inline = 2, + inline_hint = 3, +} + +FunctionAttributes = struct +{ + inline_behavior: InlineBehavior, + naked: u1, +} + +Type = struct; +Value = struct; +Local = struct; +Block = struct; + +ScopeKind = enum +{ + global, + function, + local, + for_each, + macro_declaration, + macro_instantiation, +} + +TypeList = struct +{ + first: &Type, + last: &Type, +} + +Scope = struct +{ + types: TypeList, + parent: &Scope, + line: u32, + column: u32, + kind: ScopeKind, +} + +Variable = struct +{ + storage: &Value, + type: &Type, + scope: &Scope, + name: []u8, + line: u32, + column: u32, +} + +Linkage = enum +{ + internal, + external, +} + +Global = struct +{ + variable: Variable, + initial_value: &Value, + next: &Global, + linkage: Linkage, + emitted: u1, +} + +Local = struct +{ + variable: Variable, + initial_value: &Value, + next: &Local, +} + +Argument = struct +{ + variable: Variable, + index: u32, +} + TypeId = enum { void, noreturn, + forward_declaration, integer, + function, + pointer, + array, + enum, + struct, + bits, + alias, + union, + unresolved, + vector, + floating_point, + enum_array, + opaque, } TypeInteger = struct @@ -599,9 +707,47 @@ TypeInteger = struct signed: u1, } +TypePointer = struct +{ + element_type: &Type, + next: &Type, +} + +AbiRegisterCountSystemV = struct +{ + gpr: u32, + sse: u32, +}; + +AbiRegisterCount = union +{ + system_v: AbiRegisterCountSystemV, +}; + +AbiInformation = struct +{ + foo: u32, +} + +TypeFunction = struct +{ + semantic_return_type: &Type, + semantic_argument_types: []&Type, + calling_convention: CallingConvention, + is_variable_arguments: u1, + + abi_argument_types: []&Type, + abi_return_type: &Type, + available_registers: AbiRegisterCount, + argument_abis: []AbiInformation, + return_abi: AbiInformation, +} + TypeContent = union { integer: TypeInteger, + function: TypeFunction, + pointer: TypePointer, } Type = struct @@ -609,50 +755,362 @@ Type = struct content: TypeContent, id: TypeId, name: []u8, + next: &Type, + scope: &Scope, } ValueId = enum { infer_or_ignore, + external_function, + function, +} + +ValueConstantInteger = struct +{ + value: u64, + is_signed: u1, +} + +ValueFunction = struct +{ + arguments: []Argument, + scope: Scope, + block: &Block, + attributes: FunctionAttributes, +} + +ValueContent = union +{ + constant_integer: ValueConstantInteger, + function: ValueFunction, +} + +ValueKind = enum +{ + right, + left, } Value = struct { + content: ValueContent, type: &Type, id: ValueId, + kind: ValueKind, } i128_offset: u64 = 64 * 2; void_offset: u64 = i128_offset + 2; +MacroDeclaration = struct +{ + foo: u32, +} + +MacroInstantiation = struct +{ + foo: u32, +} + +LLVMContext = opaque; +LLVMModule = opaque; +LLVMBuilder = opaque; +LLVMDIBuilder = opaque; +LLVMValue = opaque; +LLVMType = opaque; +LLVMMetadata = opaque; +LLVMBasicBlock = opaque; +LLVMIntrinsicId = typealias u32; + +LLVMIntrinsicIndex = enum +{ + trap, + va_start, + va_end, + va_copy, +} + +ModuleLLVM = struct +{ + context: &LLVMContext, + module: &LLVMModule, + builder: &LLVMBuilder, + di_builder: &LLVMDIBuilder, + file: &LLVMMetadata, + compile_unit: &LLVMMetadata, + pointer_type: &LLVMType, + void_type: &LLVMType, + intrinsic_table: enum_array[LLVMIntrinsicIndex](LLVMIntrinsicId), + memcmp: &LLVMValue, + inlined_at: &LLVMMetadata, + continue_block: &LLVMBasicBlock, + exit_block: &LLVMBasicBlock, + debug_tag: u32, +} + Module = struct { arena: &Arena, - base_type_allocation: &Type, - void_value: &Value, - // Parser data content: []u8, offset: u64, line_offset: u64, line_character_offset: u64, + + first_pointer_type: &Type, + first_slice_type: &Type, + first_pair_struct_type: &Type, + first_array_type: &Type, + + va_list_type: &Type, + + void_value: &Value, + first_global: &Global, + last_global: &Global, + first_macro_declaration: &MacroDeclaration, + last_macro_declaration: &MacroDeclaration, + + current_function: &Global, + current_macro_declaration: &MacroDeclaration, + current_macro_instantiation: &MacroInstantiation, + + llvm: ModuleLLVM, + scope: Scope, + + name: []u8, + path: []u8, + executable: []u8, + objects: [][]u8, + libraries: [][]u8, + + target: Target, + build_mode: BuildMode, + has_debug_info: u1, + silent: u1, } -module_integer_type = fn (module: &Module, integer: TypeInteger) &Type +Statement = struct; + +StatementId = enum +{ + local, + expression, + return, + assignment, + if, + block, + while, + switch, + for, + break, + continue, +} + +StatementAssignmentId = enum +{ + assign, + assign_add, + assign_sub, + assign_mul, + assign_div, + assign_rem, + assign_shift_left, + assign_shift_right, + assign_and, + assign_or, + assign_xor, +} + +StatementAssignment = struct +{ + left: &Value, + right: &Value, + id: StatementAssignmentId, +} + +StatementIf = struct +{ + condition: &Value, + if: &Statement, + else: &Statement, +} + +Block = struct +{ + first_local: &Local, + last_local: &Local, + first_statement: &Statement, + scope: Scope, +} + +StatementWhile = struct +{ + condition: &Value, + block: &Block, +} + +StatementSwitchClause = struct +{ + values: []&Value, + block: &Block, +} + +StatementSwitch = struct +{ + discriminant: &Value, + clauses: []StatementSwitchClause, +} + +StatementForKind = enum +{ + slice, + range, +} + +StatementFor = struct +{ + first_local: &Local, + last_local: &Local, + kinds: ValueKind, + iteratables: &Value, + predicate: &Statement, + scope: Scope, + kind: StatementForKind, +} + +StatementContent = union +{ + local: &Local, + expression: &Value, + return: &Value, + assignment: StatementAssignment, + if: StatementIf, + block: &Block, + while: StatementWhile, + switch: StatementSwitch, + for: StatementFor, +} + +Statement = struct +{ + content: StatementContent, + next: &Statement, + line: u32, + column: u32, + id: StatementId, +} + +new_global = fn (module: &Module) &Global +{ + >result = arena_allocate[Global](module.arena, 1); + + if (module.last_global) + { + assert(module.first_global != zero); + module.last_global.next = result; + module.last_global = result; + } + else + { + assert(module.first_global == zero); + module.first_global = result; + module.last_global = result; + } + + return result; +} + +new_type = fn (module: &Module, type: Type) &Type +{ + >result = arena_allocate[Type](module.arena, 1); + result.& = type; + + >scope = type.scope; + assert(scope != zero); + + if (scope.types.last) + { + assert(scope.types.first != zero); + scope.types.last.next = result; + scope.types.last = result; + } + else + { + assert(scope.types.first == zero); + scope.types.first = result; + scope.types.last = result; + } + + return result; +} + +integer_type = fn (module: &Module, integer: TypeInteger) &Type { assert(integer.bit_count != 0); assert(integer.bit_count <= 64); >index = #select(integer.bit_count == 128, i128_offset + #extend(integer.signed), #extend(integer.bit_count) - 1 + 64 * #extend(integer.signed)); - return module.base_type_allocation + index; + >result = module.scope.types.first + index; + assert(result.id == .integer); + assert(result.content.integer.bit_count == integer.bit_count); + assert(result.content.integer.signed == integer.signed); + return result; } -module_void_type = fn (module: &Module) &Type +void_type = fn (module: &Module) &Type { - return module.base_type_allocation + void_offset; + return module.scope.types.first + void_offset; } -module_noreturn_type = fn (module: &Module) &Type +noreturn_type = fn (module: &Module) &Type { - return module_void_type(module) + 1; + return void_type(module) + 1; +} + +get_pointer_type = fn (module: &Module, element_type: &Type) &Type +{ + >last_pointer_type = module.first_pointer_type; + + while (last_pointer_type) + { + assert(last_pointer_type.id == .pointer); + if (last_pointer_type.content.pointer.element_type == element_type) + { + return last_pointer_type; + } + + if (!last_pointer_type.content.pointer.next) + { + break; + } + + last_pointer_type = last_pointer_type.content.pointer.next; + } + + >result = new_type(module, { + .content = { + .pointer = { + .element_type = element_type, + zero, + }, + }, + .id = .pointer, + .name = arena_join_string(module.arena, [ "&", element_type.name ][..]), + .scope = element_type.scope, + zero, + }); + + if (last_pointer_type) + { + assert(module.first_pointer_type != zero); + last_pointer_type.content.pointer.next = result; + } + else + { + assert(!module.first_pointer_type); + module.first_pointer_type = result; + } + + return result; } is_space = fn (ch: u8) u1 @@ -697,7 +1155,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 @@ -724,6 +1182,29 @@ get_column = fn (module: &Module) u32 return #truncate(column); } +Checkpoint = struct +{ + offset: u64, + line_offset: u64, + line_character_offset: u64, +} + +get_checkpoint = fn (module: &Module) Checkpoint +{ + return { + .offset = module.offset, + .line_offset = module.line_offset, + .line_character_offset = module.line_character_offset, + }; +} + +set_checkpoint = fn (module: &Module, checkpoint: Checkpoint) void +{ + module.offset = checkpoint.offset; + module.line_offset = checkpoint.line_offset; + module.line_character_offset = checkpoint.line_character_offset; +} + skip_space = fn (module: &Module) void { while (1) @@ -817,8 +1298,789 @@ parse_identifier = fn (module: &Module) []u8 return result; } +new_value = fn (module: &Module) &Value +{ + >value = arena_allocate[Value](module.arena, 1); + return value; +} + +GlobalAttributeKeyword = enum +{ + export, + extern, +} + +GlobalKeyword = enum +{ + bits, + enum, + fn, + macro, + opaque, + struct, + typealias, + union, +} + +FunctionTypeAttribute = enum +{ + cc, +} + +left_bracket: u8 = '['; +right_bracket: u8 = ']'; +left_parenthesis: u8 = '('; +right_parenthesis: u8 = ')'; +left_brace: u8 = '{'; +right_brace: u8 = '}'; + +accumulate_decimal = fn(accumulator: u64, ch: u8) u64 +{ + assert(is_decimal(ch)); + return (accumulator * 10) + #extend(ch - '0'); +} + +parse_integer_decimal_assume_valid = fn (string: []u8) u64 +{ + >value: u64 = 0; + + for (ch: string) + { + value = accumulate_decimal(value, ch); + } + + return value; +} + +TypeKeyword = enum +{ + void, + noreturn, + enum_array, +} + +parse_type = fn (module: &Module, scope: &Scope) &Type +{ + >start_character = module.content[module.offset]; + + if (is_identifier_start(start_character)) + { + >identifier = parse_identifier(module); + + >type_keyword_s2e = #string_to_enum(TypeKeyword, identifier); + + if (type_keyword_s2e.is_valid) + { + >type_keyword = type_keyword_s2e.enum_value; + + switch (type_keyword) + { + .void => + { + #trap(); + }, + .noreturn => + { + #trap(); + }, + .enum_array => + { + #trap(); + }, + } + } + else + { + >is_integer_type = identifier.length > 1 and (identifier[0] == 's' or identifier[0] == 'u'); + + if (is_integer_type) + { + for (ch: identifier[1..]) + { + is_integer_type = is_integer_type and is_decimal(ch); + } + } + + if (is_integer_type) + { + >is_signed: u1 = undefined; + switch (identifier[0]) + { + 's' => + { + is_signed = 1; + }, + 'u' => + { + is_signed = 0; + }, + else => + { + unreachable; + }, + } + + >bit_count = parse_integer_decimal_assume_valid(identifier[1..]); + + if (bit_count == 0) + { + report_error(); + } + + if (bit_count > 64) + { + if (bit_count != 128) + { + report_error(); + } + } + + >result = integer_type(module, { .bit_count = #truncate(bit_count), .signed = is_signed }); + return result; + } + else + { + //>type = module.first_type; + + //while (type) + //{ + // if (string_equal(identifier, type.name)) + // { + // return type; + // } + + // type = type.next; + //} + + report_error(); + } + } + } + else if (start_character == '&') + { + #trap(); + } + else if (start_character == left_bracket) + { + #trap(); + } + else if (start_character == '#') + { + #trap(); + } + else + { + report_error(); + } +} + +TokenId = enum +{ + none, + comma, + end_of_statement, + integer, + left_brace, + left_bracket, + left_parenthesis, + right_brace, + right_bracket, + right_parenthesis, + + plus, + dash, + asterisk, + forward_slash, + percentage, + caret, + bar, + ampersand, + exclamation, + + assign_plus, + assign_dash, + assign_asterisk, + assign_forward_slash, + assign_percentage, + assign_caret, + assign_bar, + assign_ampersand, + + value_keyword, + operator_keyword, + identifier, + string_literal, + value_intrinsic, + + shift_left, + shift_right, + assign_shift_left, + assign_shift_right, + + compare_less, + compare_less_equal, + compare_greater, + compare_greater_equal, + compare_equal, + compare_not_equal, + + dot, + double_dot, + triple_dot, + + pointer_dereference, + + assign, + tilde, +} + +TokenIntegerKind = enum +{ + hexadecimal, + decimal, + octal, + binary, + character_literal, +} + +TokenInteger = struct +{ + value: u64, + kind: TokenIntegerKind, +}; + +ValueKeyword = enum +{ + undefined, + unreachable, + zero, +} + +ValueIntrinsic = enum +{ + align_of, + byte_size, + enum_name, + extend, + integer_max, + int_from_enum, + int_from_pointer, + pointer_cast, + pointer_from_int, + select, + string_to_enum, + trap, + truncate, + va_start, + va_end, + va_arg, + va_copy, +} + +OperatorKeyword = enum +{ + and, + or, + "and?", + "or?", +} + +TokenContent = union +{ + integer: TokenInteger, + value_keyword: ValueKeyword, + value_intrinsic: ValueIntrinsic, + operator_keyword: OperatorKeyword, + identifier: []u8, + string_literal: []u8, +}; + +Token = struct +{ + content: TokenContent, + id: TokenId, +} + +ValueBuilder = struct +{ + token: Token, +} + +parse_value = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value +{ + #trap(); +} + + +parse_statement = fn (module: &Module, scope: &Scope) &Statement +{ + >require_semicolon: u1 = zero; + + >statement_line = get_line(module); + >statement_column = get_column(module); + + >statement = arena_allocate[Statement](module.arena, 1); + statement.& = { + .line = statement_line, + .column = statement_column, + zero, + }; + + >statement_start_character = module.content[module.offset]; + + switch (statement_start_character) + { + '>' => + { + #trap(); + }, + '#' => + { + statement.content.expression = parse_value(module, scope, zero); + statement.id = .expression; + }, + left_brace => + { + #trap(); + }, + else => + { + if (is_identifier_start(statement_start_character)) + { + #trap(); + } + else + { + #trap(); + } + }, + } + + if (require_semicolon) + { + expect_character(module, ';'); + } + + return statement; +} + +parse_block = fn (module: &Module, parent_scope: &Scope) &Block +{ + >block = arena_allocate[Block](module.arena, 1); + block.& = { + .scope = { + .parent = parent_scope, + .line = get_line(module), + .column = get_column(module), + .kind = .local, + zero, + }, + zero, + }; + + >scope = &block.scope; + + expect_character(module, left_brace); + + >current_statement: &Statement = zero; + + while (1) + { + skip_space(module); + + if (module.offset == module.content.length) + { + break; + } + + if (consume_character_if_match(module, right_brace)) + { + break; + } + + >statement = parse_statement(module, scope); + + if (!block.first_statement) + { + block.first_statement = statement; + } + + if (current_statement) + { + current_statement.next = statement; + } + + assert(!statement.next); + current_statement = statement; + } + + return block; +} + parse = fn (module: &Module) void { + >scope = &module.scope; + + 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_line(module); + + if (consume_character_if_match(module, left_bracket)) + { + while (module.offset < module.content.length) + { + >global_attribute_keyword_string = parse_identifier(module); + >global_attribute_keyword_s2e = #string_to_enum(GlobalAttributeKeyword, global_attribute_keyword_string); + if (!global_attribute_keyword_s2e.is_valid) + { + report_error(); + } + + >global_attribute_keyword = global_attribute_keyword_s2e.enum_value; + switch (global_attribute_keyword) + { + .export => + { + is_export = 1; + }, + .extern => + { + is_extern = 1; + }, + } + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + else + { + report_error(); + } + } + + skip_space(module); + } + + >global_name = parse_identifier(module); + + >last_global = module.first_global; + + while (last_global) + { + if (string_equal(global_name, last_global.variable.name)) + { + report_error(); + } + + if (!last_global.next) + { + break; + } + + last_global = last_global.next; + } + + >type_it = module.scope.types.first; + >forward_declaration: &Type = zero; + + while (type_it) + { + if (string_equal(global_name, type_it.name)) + { + if (type_it.id == .forward_declaration) + { + forward_declaration = type_it; + break; + } + else + { + report_error(); + } + } + + if (!type_it.next) + { + break; + } + + type_it = type_it.next; + } + + skip_space(module); + + >global_type: &Type = zero; + + if (consume_character_if_match(module, ':')) + { + skip_space(module); + + global_type = parse_type(module, scope); + + skip_space(module); + } + + expect_character(module, '='); + + skip_space(module); + + >is_global_keyword: u1 = 0; + + if (is_identifier_start(module.content[module.offset])) + { + >checkpoint = get_checkpoint(module); + >global_keyword_string = parse_identifier(module); + skip_space(module); + + >global_keyword_s2e = #string_to_enum(GlobalKeyword, global_keyword_string); + + is_global_keyword = global_keyword_s2e.is_valid; + + if (is_global_keyword) + { + >global_keyword = global_keyword_s2e.enum_value; + + switch (global_keyword) + { + .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_type_attribute_s2e = #string_to_enum(FunctionTypeAttribute, function_identifier); + + if (!function_type_attribute_s2e.is_valid) + { + report_error(); + } + + >function_type_attribute = function_type_attribute_s2e.enum_value; + + switch (function_type_attribute) + { + .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) + { + report_error(); + } + + >candidate_calling_convention = calling_convention_s2e.enum_value; + calling_convention = candidate_calling_convention; + + skip_space(module); + expect_character(module, right_parenthesis); + }, + } + + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + 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; + >argument_line_buffer: [64]u32 = undefined; + >semantic_argument_count: u64 = 0; + + while (module.offset < module.content.length) + { + skip_space(module); + + if (consume_character_if_match(module, '.')) + { + #trap(); + } + + if (consume_character_if_match(module, right_parenthesis)) + { + break; + } + + >line = get_line(module); + argument_line_buffer[semantic_argument_count] = line; + + >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, scope); + semantic_argument_type_buffer[semantic_argument_count] = argument_type; + + skip_space(module); + + consume_character_if_match(module, '.'); + + semantic_argument_count += 1; + } + + skip_space(module); + + >return_type = parse_type(module, scope); + + skip_space(module); + + >argument_types: []&Type = zero; + + if (semantic_argument_count != 0) + { + #trap(); + } + + >is_declaration = consume_character_if_match(module, ';'); + + >function_type = new_type(module, { + .content = { + .function = { + .semantic_return_type = return_type, + .semantic_argument_types = argument_types, + .calling_convention = calling_convention, + .is_variable_arguments = is_variable_arguments, + zero, + }, + }, + .id = .function, + .name = "", + .scope = &module.scope, + zero, + }); + + >storage = new_value(module); + storage.& = { + .id = .external_function, + .type = get_pointer_type(module, function_type), + zero, + // TODO? kind = .left, + }; + + >global = new_global(module); + global.& = { + .variable = { + .storage = storage, + .type = function_type, + .scope = scope, + .name = global_name, + .line = global_line, + .column = global_column, + }, + .linkage = #select(is_export or is_extern, .external, .internal), + zero, + }; + + if (!is_declaration) + { + module.current_function = global; + >arguments = arena_allocate_slice[Argument](module.arena, semantic_argument_count); + + for (i: 0..semantic_argument_count) + { + >argument = &arguments[i]; + >name = semantic_argument_name_buffer[i]; + >type = semantic_argument_type_buffer[i]; + >line = argument_line_buffer[i]; + + #trap(); + } + + storage.content.function = { + .arguments = arguments, + .scope = { + .parent = scope, + .line = global_line, + .column = global_column, + .kind = .function, + zero, + }, + .attributes = function_attributes, + zero, + }; + storage.id = .function; + storage.content.function.block = parse_block(module, &storage.content.function.scope); + module.current_function = zero; + } + }, + .macro => + { + #trap(); + }, + .opaque => + { + #trap(); + }, + .struct => + { + #trap(); + }, + .typealias => + { + #trap(); + }, + .union => + { + #trap(); + }, + } + } + else + { + set_checkpoint(module, checkpoint); + } + } + + if (!is_global_keyword) + { + #trap(); + } + } } emit = fn (module: &Module) void @@ -836,6 +2098,7 @@ compile = fn (arena: &Arena, options: CompileOptions) void ); >type_it = base_type_allocation; + >previous: &Type = zero; for (sign: signs) { @@ -858,7 +2121,16 @@ compile = fn (arena: &Arena, options: CompileOptions) void }, .id = .integer, .name = name, + zero, }; + + if (previous) + { + previous.next = type_it; + } + + previous = type_it; + type_it += 1; } } @@ -875,7 +2147,12 @@ compile = fn (arena: &Arena, options: CompileOptions) void }, .id = .integer, .name = name, + zero, }; + + previous.next = type_it; + previous = type_it; + type_it += 1; } @@ -884,31 +2161,45 @@ compile = fn (arena: &Arena, options: CompileOptions) void >noreturn_type = type_it; type_it += 1; + previous.next = void_type; + void_type.& = { .id = .void, .name = "void", + .next = noreturn_type, + zero, }; noreturn_type.& = { .id = .noreturn, .name = "noreturn", + zero, }; >void_value = arena_allocate[Value](arena, 1); void_value.& = { - .type = void_type, .id = .infer_or_ignore, + .type = void_type, + zero, }; >module: Module = { .arena = arena, - .base_type_allocation = base_type_allocation, - .void_value = void_value, .content = options.content, - .offset = 0, - .line_offset = 0, - .line_character_offset = 0, + .void_value = void_value, + .name = options.name, + .path = options.path, + .executable = options.executable, + .objects = options.objects, + .libraries = options.libraries, + .target = options.target, + .build_mode = options.build_mode, + .has_debug_info = options.has_debug_info, + .silent = options.silent, + zero, }; + module.scope.types.first = base_type_allocation; + module.scope.types.last = noreturn_type; parse(&module); emit(&module); @@ -969,18 +2260,12 @@ compile_file = fn (arena: &Arena, compile_options: CompileFile) void >c_abi_object_path = ""; // TODO >objects: [][]u8 = undefined; - if (string_equal(base_name, "c_abi")) - { - objects = [ output_object_path, c_abi_object_path ][..]; - } - else - { - objects = [ output_object_path ][..]; - } + objects = [ output_object_path ][..]; >options: CompileOptions = { .executable = output_executable_path, .objects = objects, + .libraries = zero, .name = base_name, .build_mode = compile_options.build_mode, .content = file_content, diff --git a/src/compiler.cpp b/src/compiler.cpp index f85712e..4c2d981 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -2,6 +2,7 @@ fn void compile(Arena* arena, Options options) { + Module module; auto base_allocation_type_count = i128_offset + // 64 * 2 for basic integer types 2 + // u128, s128 2; // void, noreturn @@ -32,6 +33,7 @@ fn void compile(Arena* arena, Options options) }, .id = TypeId::integer, .name = name, + .scope = &module.scope, }; if (previous) previous->next = type_it; previous = type_it; @@ -50,6 +52,7 @@ fn void compile(Arena* arena, Options options) .id = TypeId::integer, .name = name, .next = previous, + .scope = &module.scope, }; if (previous) previous->next = type_it; previous = type_it; @@ -67,18 +70,22 @@ fn void compile(Arena* arena, Options options) .id = TypeId::void_type, .name = string_literal("void"), .next = noreturn_type, + .scope = &module.scope, }; *noreturn_type = { .id = TypeId::noreturn, .name = string_literal("noreturn"), + .scope = &module.scope, }; - auto module = Module{ + module = Module{ .arena = arena, .content = options.content, - .first_type = base_type_allocation.pointer, - .last_type = noreturn_type, .scope = { + .types = { + .first = base_type_allocation.pointer, + .last = noreturn_type, + }, .kind = ScopeKind::global, }, .name = options.name, @@ -306,8 +313,12 @@ global_variable String names[] = string_literal("noreturn_macro"), string_literal("generic_pointer_array"), - string_literal("self_referential_struct"), // TODO - // string_literal("forward_declared_type"), + string_literal("self_referential_struct"), + string_literal("forward_declared_type"), + + string_literal("enum_array"), + string_literal("opaque"), + string_literal("basic_struct_passing"), }; void entry_point(Slice arguments, Slice environment) @@ -382,7 +393,7 @@ void entry_point(Slice arguments, Slice environment) if (arguments.length >= 5) { - auto has_debug_info_string = c_string_to_slice(arguments[3]); + auto has_debug_info_string = c_string_to_slice(arguments[4]); if (has_debug_info_string.equal(string_literal("true"))) { has_debug_info = true; @@ -488,7 +499,7 @@ void entry_point(Slice arguments, Slice environment) auto success = execution.termination_kind == TerminationKind::exit && execution.termination_code == 0; if (!success) { - print(string_literal("Standalone test failed: ")); + print(string_literal("Self-hosted test failed: ")); print(name); print(string_literal("\n")); bb_fail(); diff --git a/src/compiler.hpp b/src/compiler.hpp index 53adbbc..8ca9f87 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #define report_error() trap() enum class Command @@ -314,6 +314,8 @@ enum class TypeId unresolved, vector, floating_point, + enum_array, + opaque, }; struct TypeInteger @@ -448,6 +450,13 @@ struct LLVMType LLVMMetadataRef debug; }; +struct TypeEnumArray +{ + Type* enum_type; + Type* element_type; + Type* next; +}; + struct Type { union @@ -461,10 +470,12 @@ struct Type TypeBits bits; TypeAlias alias; TypeUnion union_type; + TypeEnumArray enum_array; }; TypeId id; String name; Type* next; + Scope* scope; LLVMType llvm; }; @@ -528,6 +539,17 @@ fn u64 get_byte_size(Type* type) auto result = type->union_type.byte_size; return result; } break; + case TypeId::enum_array: + { + auto enum_type = type->enum_array.enum_type; + assert(enum_type->id == TypeId::enumerator); + auto element_count = enum_type->enumerator.fields.length; + auto element_type = type->enum_array.element_type; + auto element_size = get_byte_size(element_type); + + auto result = element_size * element_count; + return result; + } break; default: trap(); } } @@ -575,6 +597,10 @@ fn u32 get_byte_alignment(Type* type) { return get_byte_alignment(type->alias.type); } break; + case TypeId::enum_array: + { + return get_byte_alignment(type->enum_array.element_type); + } break; default: trap(); } } @@ -590,6 +616,12 @@ fn u64 get_bit_size(Type* type) } } +struct TypeList +{ + Type* first; + Type* last; +}; + enum class ScopeKind { global, @@ -602,6 +634,7 @@ enum class ScopeKind struct Scope { + TypeList types; Scope* parent; u32 line; u32 column; @@ -609,6 +642,13 @@ struct Scope LLVMMetadataRef llvm; }; +struct SourceLocation +{ + Scope* scope; + u32 line; + u32 column; +}; + enum class StatementId { local, @@ -878,10 +918,18 @@ struct ValueVaArg Type* type; }; +struct AggregateInitializationElement +{ + String name; + Value* value; + u32 line; + u32 column; +}; + struct ValueAggregateInitialization { - Slice names; - Slice values; + Slice elements; + Scope* scope; bool is_constant; bool zero; }; @@ -920,6 +968,7 @@ struct MacroDeclaration { Slice arguments; Slice constant_arguments; + TypeList types; Type* return_type; Block* block; String name; @@ -987,6 +1036,7 @@ struct Value case ValueId::enum_literal: case ValueId::unary_type: case ValueId::string_literal: + case ValueId::zero: return true; case ValueId::unary: case ValueId::binary: @@ -1103,6 +1153,8 @@ struct ModuleLLVM LLVMBuilderRef builder; LLVMDIBuilderRef di_builder; LLVMMetadataRef file; + LLVMTargetMachineRef target_machine; + LLVMTargetDataRef target_data; LLVMMetadataRef compile_unit; LLVMTypeRef pointer_type; LLVMTypeRef void_type; @@ -1126,9 +1178,8 @@ struct Module Type* first_slice_type; Type* first_pair_struct_type; Type* first_array_type; + Type* first_enum_array_type; - Type* first_type; - Type* last_type; Type* va_list_type; Value* void_value; @@ -1167,7 +1218,7 @@ fn Type* integer_type(Module* module, TypeInteger integer) assert(integer.bit_count > 1); } auto index = integer.bit_count == 128 ? (i128_offset + integer.is_signed) : (integer.bit_count - 1 + (64 * integer.is_signed)); - auto* result_type = module->first_type + index; + auto* result_type = module->scope.types.first + index; assert(result_type->id == TypeId::integer); assert(result_type->integer.bit_count == integer.bit_count); assert(result_type->integer.is_signed == integer.is_signed); @@ -1176,7 +1227,7 @@ fn Type* integer_type(Module* module, TypeInteger integer) fn Type* void_type(Module* module) { - return module->first_type + void_offset; + return module->scope.types.first + void_offset; } fn Type* noreturn_type(Module* module) @@ -1228,16 +1279,20 @@ fn Type* type_allocate_init(Module* module, Type type) auto* result = &arena_allocate(module->arena, 1)[0]; *result = type; - if (module->last_type) + auto scope = type.scope; + assert(scope); + + if (scope->types.last) { - module->last_type->next = result; - module->last_type = result; + assert(scope->types.first); + scope->types.last->next = result; + scope->types.last = result; } else { - assert(!module->first_type); - module->first_type = result; - module->last_type = result; + assert(!scope->types.first); + scope->types.first = result; + scope->types.last = result; } return result; @@ -1310,6 +1365,7 @@ fn Type* get_pointer_type(Module* module, Type* element_type) }, .id = TypeId::pointer, .name = arena_join_string(module->arena, array_to_slice(name_parts)), + .scope = element_type->scope, }); if (last_pointer_type) @@ -1394,6 +1450,7 @@ fn Type* get_slice_type(Module* module, Type* element_type) }, .id = TypeId::structure, .name = arena_join_string(module->arena, array_to_slice(name_parts)), + .scope = element_type->scope, }); if (last_slice_type) @@ -1469,6 +1526,7 @@ fn Type* get_array_type(Module* module, Type* element_type, u64 element_count) }, .id = TypeId::array, .name = array_name(module, element_type, element_count), + .scope = element_type->scope, }); if (last_array_type) @@ -1714,5 +1772,58 @@ fn Local* new_local(Module* module, Scope* scope) return result; } +fn Type* get_enum_array_type(Module* module, Type* enum_type, Type* element_type) +{ + assert(enum_type); + assert(element_type); + + Type* last_enum_type = module->first_enum_array_type; + + if (last_enum_type) + { + while (1) + { + assert(last_enum_type->id == TypeId::enum_array); + + if (last_enum_type->enum_array.enum_type == enum_type && last_enum_type->enum_array.element_type == element_type) + { + return last_enum_type; + } + + if (!last_enum_type->enum_array.next) + { + break; + } + + last_enum_type = last_enum_type->enum_array.next; + } + } + + String name_parts[] = { + string_literal("enum_array["), + enum_type->name, + string_literal("]("), + element_type->name, + string_literal(")"), + }; + + assert(enum_type->scope); + assert(element_type->scope); + + auto scope = element_type->scope->kind == ScopeKind::global ? enum_type->scope : element_type->scope; + + auto enum_array_type = type_allocate_init(module, { + .enum_array = { + .enum_type = enum_type, + .element_type = element_type, + }, + .id = TypeId::enum_array, + .name = arena_join_string(module->arena, array_to_slice(name_parts)), + .scope = scope, + }); + return enum_array_type; +} + + void parse(Module* module); void emit(Module* module); diff --git a/src/emitter.cpp b/src/emitter.cpp index 6ffb0ee..7c58999 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -17,6 +17,8 @@ fn void analyze_block(Module* module, Block* block); fn void emit_local_storage(Module* module, Variable* variable); fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, Value* right); fn void emit_macro_instantiation(Module* module, Value* value); +fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool must_be_constant); +fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind, bool must_be_constant); fn void emit_block(Module* module, LLVMBasicBlockRef basic_block) { @@ -42,6 +44,24 @@ fn void emit_block(Module* module, LLVMBasicBlockRef basic_block) LLVMPositionBuilderAtEnd(module->llvm.builder, basic_block); } +fn LLVMValueRef emit_condition(Module* module, Value* condition_value) +{ + auto condition_llvm_value = condition_value->llvm; + auto condition_type = condition_value->type; + assert(condition_type); + assert(condition_llvm_value); + + assert(condition_type->id == TypeId::integer || condition_type->id == TypeId::pointer); + if (!(condition_type->id == TypeId::integer && condition_type->integer.bit_count == 1)) + { + condition_llvm_value = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, condition_llvm_value, LLVMConstNull(condition_type->llvm.abi), ""); + } + + assert(condition_llvm_value); + + return condition_llvm_value; +} + fn LLVMValueRef emit_intrinsic_call(Module* module, IntrinsicIndex index, Slice argument_types, Slice argument_values) { auto intrinsic_id = module->llvm.intrinsic_table[(backing_type(IntrinsicIndex))index]; @@ -70,6 +90,7 @@ fn EvaluationKind get_evaluation_kind(Type* type) case TypeId::array: case TypeId::structure: case TypeId::union_type: + case TypeId::enum_array: return EvaluationKind::aggregate; default: unreachable(); @@ -216,8 +237,6 @@ fn void dump_module(Module* module) print(llvm_module_to_string(module->llvm.module)); } -fn void emit_value(Module* module, Value* value, TypeKind type_kind); - fn LLVMCallConv llvm_calling_convention(CallingConvention calling_convention) { LLVMCallConv cc; @@ -235,20 +254,6 @@ fn Type* resolve_alias(Module* module, Type* type) Type* result_type = 0; switch (type->id) { - case TypeId::pointer: - { - auto* element_type = type->pointer.element_type; - auto* resolved_element_type = resolve_alias(module, element_type); - result_type = get_pointer_type(module, resolved_element_type); - } break; - case TypeId::array: - { - auto* element_type = type->array.element_type; - auto element_count = type->array.element_count; - assert(element_count); - auto* resolved_element_type = resolve_alias(module, element_type); - result_type = get_array_type(module, resolved_element_type, element_count); - } break; case TypeId::void_type: case TypeId::noreturn: case TypeId::integer: @@ -256,15 +261,52 @@ fn Type* resolve_alias(Module* module, Type* type) case TypeId::function: case TypeId::bits: case TypeId::union_type: + case TypeId::opaque: { result_type = type; } break; + case TypeId::pointer: + { + auto* element_type = type->pointer.element_type; + auto* resolved_element_type = resolve_alias(module, element_type); + if (element_type == resolved_element_type) + { + result_type = type; + } + else + { + result_type = get_pointer_type(module, resolved_element_type); + } + } break; + case TypeId::array: + { + auto* element_type = type->array.element_type; + auto element_count = type->array.element_count; + assert(element_count); + auto* resolved_element_type = resolve_alias(module, element_type); + if (element_type == resolved_element_type) + { + result_type = type; + } + else + { + result_type = get_array_type(module, resolved_element_type, element_count); + } + } break; case TypeId::structure: { if (type->structure.is_slice) { - auto element_type = resolve_alias(module, type->structure.fields[0].type->pointer.element_type); - result_type = get_slice_type(module, element_type); + auto old_element_type = type->structure.fields[0].type->pointer.element_type; + auto element_type = resolve_alias(module, old_element_type); + if (old_element_type == element_type) + { + result_type = type; + } + else + { + result_type = get_slice_type(module, element_type); + } } else { @@ -275,6 +317,22 @@ fn Type* resolve_alias(Module* module, Type* type) { result_type = resolve_alias(module, type->alias.type); } break; + case TypeId::enum_array: + { + auto old_enum_type = type->enum_array.enum_type; + auto old_element_type = type->enum_array.element_type; + auto enum_type = resolve_alias(module, old_enum_type); + auto element_type = resolve_alias(module, old_element_type); + + if (old_enum_type == enum_type && old_element_type == element_type) + { + result_type = type; + } + else + { + result_type = get_enum_array_type(module, enum_type, element_type); + } + } break; default: unreachable(); } @@ -289,6 +347,41 @@ fn void llvm_initialize(Module* module) auto context = LLVMContextCreate(); auto m = llvm_context_create_module(context, module->name); auto builder = LLVMCreateBuilderInContext(context); + BBLLVMCodeGenerationOptimizationLevel code_generation_optimization_level; + switch (module->build_mode) + { + case BuildMode::debug_none: + case BuildMode::debug: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::none; + break; + case BuildMode::soft_optimize: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::less; + break; + case BuildMode::optimize_for_speed: + case BuildMode::optimize_for_size: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::normal; + break; + case BuildMode::aggressively_optimize_for_speed: + case BuildMode::aggressively_optimize_for_size: + code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::aggressive; + break; + case BuildMode::count: + unreachable(); + } + auto triple = llvm_default_target_triple(); + BBLLVMTargetMachineCreate target_machine_options = { + .target_triple = triple, + .cpu_model = llvm_host_cpu_name(), + .cpu_features = llvm_host_cpu_features(), + .relocation_model = BBLLVMRelocationModel::default_relocation, + .code_model = BBLLVMCodeModel::none, + .optimization_level = code_generation_optimization_level, + }; + String error_message = {}; + auto target_machine = llvm_create_target_machine(&target_machine_options, &error_message); + auto target_data = LLVMCreateTargetDataLayout(target_machine); + LLVMSetModuleDataLayout(m, target_data); + LLVMSetTarget(m, (char*)triple.pointer); LLVMDIBuilderRef di_builder = 0; LLVMMetadataRef di_compile_unit = 0; LLVMMetadataRef di_file = 0; @@ -321,6 +414,8 @@ fn void llvm_initialize(Module* module) .builder = builder, .di_builder = di_builder, .file = di_file, + .target_machine = target_machine, + .target_data = target_data, .compile_unit = di_compile_unit, .pointer_type = LLVMPointerTypeInContext(context, 0), .void_type = LLVMVoidTypeInContext(context), @@ -460,9 +555,31 @@ fn bool contains_no_user_data(Type* type, u64 start, u64 end) return true; } break; case TypeId::array: + case TypeId::enum_array: { - auto element_type = type->array.element_type; - auto element_count = type->array.element_count; + Type* element_type = 0; + u64 element_count = 0; + + switch (type->id) + { + case TypeId::array: + { + element_type = type->array.element_type; + element_count = type->array.element_count; + } break; + case TypeId::enum_array: + { + auto enum_type = type->enum_array.enum_type; + assert(enum_type->id == TypeId::enumerator); + element_count = enum_type->enumerator.fields.length; + element_type = type->enum_array.element_type; + } break; + default: unreachable(); + } + + assert(element_type); + assert(element_count); + auto element_size = get_byte_size(element_type); for (u64 i = 0; i < element_count; i += 1) @@ -479,6 +596,7 @@ fn bool contains_no_user_data(Type* type, u64 start, u64 end) return false; } } + trap(); } break; default: return false; @@ -807,18 +925,24 @@ fn void resolve_type_in_place_abi(Module* module, Type* type) } result = LLVMStructTypeInContext(module->llvm.context, llvm_type_buffer, fields.length, 0); + auto llvm_size = LLVMStoreSizeOfType(module->llvm.target_data, result); + assert(llvm_size == type->structure.byte_size); } break; case TypeId::bits: { auto backing_type = type->bits.backing_type; resolve_type_in_place_abi(module, backing_type); result = backing_type->llvm.abi; + auto llvm_size = LLVMStoreSizeOfType(module->llvm.target_data, result); + assert(llvm_size == get_byte_size(type)); } break; case TypeId::union_type: { auto biggest_type = type->union_type.fields[type->union_type.biggest_field].type; resolve_type_in_place_memory(module, biggest_type); - result = LLVMStructTypeInContext(module->llvm.context, &biggest_type->llvm.memory, 1, 0); + result = LLVMStructTypeInContext(module->llvm.context, &biggest_type->llvm.memory, 1, false); + auto llvm_size = LLVMStoreSizeOfType(module->llvm.target_data, result); + assert(llvm_size == get_byte_size(type)); } break; case TypeId::alias: { @@ -826,6 +950,19 @@ fn void resolve_type_in_place_abi(Module* module, Type* type) resolve_type_in_place_abi(module, aliased); result = aliased->llvm.abi; } break; + case TypeId::enum_array: + { + auto enum_type = type->enum_array.enum_type; + assert(enum_type->id == TypeId::enumerator); + auto element_type = type->enum_array.element_type; + resolve_type_in_place_memory(module, element_type); + auto element_count = enum_type->enumerator.fields.length; + assert(element_count); + auto array_type = LLVMArrayType2(element_type->llvm.memory, element_count); + result = array_type; + auto llvm_size = LLVMStoreSizeOfType(module->llvm.target_data, result); + assert(llvm_size == get_byte_size(type)); + } break; default: unreachable(); } @@ -849,6 +986,7 @@ fn void resolve_type_in_place_memory(Module* module, Type* type) case TypeId::pointer: case TypeId::array: case TypeId::structure: + case TypeId::enum_array: result = type->llvm.abi; break; case TypeId::integer: @@ -918,11 +1056,8 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) case TypeId::pointer: { resolve_type_in_place_debug(module, type->pointer.element_type); - if (type->llvm.debug) - { - trap(); - } - else + result = type->llvm.debug; + if (!result) { result = LLVMDIBuilderCreatePointerType(module->llvm.di_builder, type->pointer.element_type->llvm.debug, 64, 64, 0, (char*)type->name.pointer, type->name.length); } @@ -1026,6 +1161,23 @@ fn void resolve_type_in_place_debug(Module* module, Type* type) auto alignment = get_byte_alignment(aliased); result = LLVMDIBuilderCreateTypedef(module->llvm.di_builder, aliased->llvm.debug, (char*) type->name.pointer, type->name.length, module->llvm.file, type->alias.line, type->alias.scope->llvm, alignment * 8); } break; + case TypeId::enum_array: + { + auto enum_type = type->enum_array.enum_type; + assert(enum_type->id == TypeId::enumerator); + auto element_type = type->enum_array.element_type; + auto element_count = enum_type->enumerator.fields.length; + resolve_type_in_place_debug(module, element_type); + assert(element_count); + auto bit_alignment = get_byte_alignment(type) * 8; + auto array_type = LLVMDIBuilderCreateArrayType(module->llvm.di_builder, element_count, bit_alignment, element_type->llvm.debug, 0, 0); + result = array_type; + } break; + case TypeId::opaque: + { + // TODO: ? + return; + } break; default: unreachable(); } @@ -1219,7 +1371,13 @@ fn Type* get_anonymous_struct_pair(Module* module, Type* low, Type* high) auto high_alignment = get_byte_alignment(high); auto alignment = MAX(get_byte_alignment(low), high_alignment); - u64 high_offset = align_forward(get_byte_size(low), high_alignment); + u64 high_offset = align_forward(get_byte_size(low), alignment); + auto byte_size = align_forward(high_offset + get_byte_size(high), alignment); + + assert(low->scope); + assert(high->scope); + + auto scope = low->scope->kind == ScopeKind::global ? high->scope : low->scope; auto fields = arena_allocate(module->arena, 2); fields[0] = { @@ -1238,11 +1396,12 @@ fn Type* get_anonymous_struct_pair(Module* module, Type* low, Type* high) auto struct_type = type_allocate_init(module, { .structure = { .fields = fields, - .byte_size = high_offset + get_byte_size(high), + .byte_size = byte_size, .byte_alignment = alignment, }, .id = TypeId::structure, .name = string_literal(""), + .scope = scope, }); if (pair) @@ -2074,9 +2233,9 @@ fn bool binary_is_shortcircuiting(BinaryId id) } } -fn void analyze_type(Module* module, Value* value, Type* expected_type); +fn void analyze_type(Module* module, Value* value, Type* expected_type, bool must_be_constant); -fn void analyze_binary_type(Module* module, Value* left, Value* right, bool is_boolean, Type* expected_type) +fn void analyze_binary_type(Module* module, Value* left, Value* right, bool is_boolean, Type* expected_type, bool must_be_constant) { auto left_constant = left->is_constant(); auto right_constant = right->is_constant(); @@ -2105,19 +2264,19 @@ fn void analyze_binary_type(Module* module, Value* left, Value* right, bool is_b { if (left_constant) { - analyze_type(module, right, 0); - analyze_type(module, left, right->type); + analyze_type(module, right, 0, must_be_constant); + analyze_type(module, left, right->type, must_be_constant); } else { - analyze_type(module, left, 0); - analyze_type(module, right, left->type); + analyze_type(module, left, 0, must_be_constant); + analyze_type(module, right, left->type, must_be_constant); } } else if (!is_boolean && expected_type) { - analyze_type(module, left, expected_type); - analyze_type(module, right, expected_type); + analyze_type(module, left, expected_type, must_be_constant); + analyze_type(module, right, expected_type, must_be_constant); } else { @@ -2148,6 +2307,7 @@ fn Type* get_va_list_type(Module* module) }, .id = TypeId::structure, .name = string_literal("va_list"), + .scope = &module->scope, }); module->va_list_type = get_array_type(module, va_list_struct, 1); @@ -2430,8 +2590,9 @@ fn void copy_block(Module* module, Scope* parent_scope, BlockCopy copy) } } -fn void analyze_type(Module* module, Value* value, Type* expected_type) +fn void analyze_type(Module* module, Value* value, Type* expected_type, bool must_be_constant) { + unused(must_be_constant); assert(!value->type); assert(!value->llvm); @@ -2533,7 +2694,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) } auto extended_value = unary_value; - analyze_type(module, extended_value, 0); + analyze_type(module, extended_value, 0, must_be_constant); auto source = extended_value->type; assert(source); @@ -2557,7 +2718,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) report_error(); } - analyze_type(module, unary_value, 0); + analyze_type(module, unary_value, 0, must_be_constant); auto expected_bit_size = get_bit_size(expected_type); auto source_bit_size = get_bit_size(unary_value->type); @@ -2570,7 +2731,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) } break; case UnaryId::dereference: { - analyze_type(module, unary_value, 0); + analyze_type(module, unary_value, 0, must_be_constant); if (value->kind == ValueKind::left) { report_error(); @@ -2584,7 +2745,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) } break; case UnaryId::int_from_enum: { - analyze_type(module, unary_value, 0); + analyze_type(module, unary_value, 0, must_be_constant); auto value_enum_type = unary_value->type; if (value_enum_type->id != TypeId::enumerator) @@ -2599,7 +2760,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) } break; case UnaryId::int_from_pointer: { - analyze_type(module, unary_value, 0); + analyze_type(module, unary_value, 0, must_be_constant); auto value_enum_type = unary_value->type; if (value_enum_type->id != TypeId::pointer) @@ -2622,7 +2783,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) report_error(); } - analyze_type(module, unary_value, 0); + analyze_type(module, unary_value, 0, must_be_constant); auto value_pointer_type = unary_value->type; if (value_pointer_type == expected_type) { @@ -2640,7 +2801,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) { auto string_type = get_slice_type(module, uint8(module)); typecheck(module, expected_type, string_type); - analyze_type(module, unary_value, 0); + analyze_type(module, unary_value, 0, must_be_constant); auto enum_type = unary_value->type; if (enum_type->id != TypeId::enumerator) { @@ -2755,7 +2916,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) report_error(); } - analyze_type(module, unary_value, 0); + analyze_type(module, unary_value, 0, must_be_constant); auto unary_value_type = unary_value->type; if (unary_value_type->id != TypeId::integer) { @@ -2775,12 +2936,12 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) auto is_boolean = unary_is_boolean(unary_id); if (is_boolean) { - analyze_type(module, unary_value, 0); + analyze_type(module, unary_value, 0, must_be_constant); value_type = uint1(module); } else { - analyze_type(module, unary_value, expected_type); + analyze_type(module, unary_value, expected_type, must_be_constant); value_type = unary_value->type; } @@ -2838,7 +2999,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) case ValueId::binary: { auto is_boolean = binary_is_boolean(value->binary.id); - analyze_binary_type(module, value->binary.left, value->binary.right, is_boolean, expected_type); + analyze_binary_type(module, value->binary.left, value->binary.right, is_boolean, expected_type, must_be_constant); check_types(module, value->binary.left->type, value->binary.right->type); value_type = is_boolean ? uint1(module) : value->binary.left->type; @@ -2857,7 +3018,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) { auto call = &value->call; auto callable = call->callable; - analyze_type(module, callable, 0); + analyze_type(module, callable, 0, must_be_constant); Type* function_type = 0; switch (callable->id) { @@ -2908,14 +3069,14 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) { auto* argument_type = semantic_argument_types[i]; auto* call_argument = call_arguments[i]; - analyze_type(module, call_argument, argument_type); + analyze_type(module, call_argument, argument_type, must_be_constant); check_types(module, argument_type, call_argument->type); } for (u64 i = semantic_argument_types.length; i < call_arguments.length; i += 1) { auto* call_argument = call_arguments[i]; - analyze_type(module, call_argument, 0); + analyze_type(module, call_argument, 0, must_be_constant); } auto semantic_return_type = function_type->function.semantic_return_type; @@ -2951,7 +3112,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) auto* element_type = expected_type->array.element_type; for (auto value : values) { - analyze_type(module, value, element_type); + analyze_type(module, value, element_type, must_be_constant); is_constant = is_constant && value->is_constant(); } @@ -2976,7 +3137,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) for (auto value : values) { - analyze_type(module, value, expected_type); + analyze_type(module, value, expected_type, must_be_constant); is_constant = is_constant && value->is_constant(); @@ -3014,10 +3175,9 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) } break; case ValueId::array_expression: { - analyze_type(module, value->array_expression.index, uint64(module)); auto array_like = value->array_expression.array_like; array_like->kind = ValueKind::left; - analyze_type(module, array_like, 0); + analyze_type(module, array_like, 0, must_be_constant); assert(array_like->kind == ValueKind::left); auto array_like_type = array_like->type; if (array_like_type->id != TypeId::pointer) @@ -3026,6 +3186,8 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) } auto pointer_element_type = array_like_type->pointer.element_type; + analyze_type(module, value->array_expression.index, pointer_element_type->id == TypeId::enum_array ? pointer_element_type->enum_array.enum_type : uint64(module), must_be_constant); + Type* element_type = 0; switch (pointer_element_type->id) { @@ -3048,6 +3210,10 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) { element_type = pointer_element_type->pointer.element_type; } break; + case TypeId::enum_array: + { + element_type = pointer_element_type->enum_array.element_type; + } break; default: report_error(); } @@ -3083,7 +3249,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) { auto aggregate = value->field_access.aggregate; auto field_name = value->field_access.field_name; - analyze_type(module, aggregate, 0); + analyze_type(module, aggregate, 0, must_be_constant); if (aggregate->kind == ValueKind::right) { @@ -3118,6 +3284,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) if (!result_field) { + // Field not found report_error(); } @@ -3214,7 +3381,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) report_error(); } - analyze_type(module, array_like, 0); + analyze_type(module, array_like, 0, must_be_constant); auto pointer_type = array_like->type; if (pointer_type->id != TypeId::pointer) @@ -3222,7 +3389,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) report_error(); } - Type* sliceable_type = pointer_type->pointer.element_type; + Type* sliceable_type = resolve_alias(module, pointer_type->pointer.element_type); Type* element_type = 0; @@ -3263,7 +3430,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) { if (index) { - analyze_type(module, index, index_type); + analyze_type(module, index, index_type, must_be_constant); if (index->type->id != TypeId::integer) { @@ -3288,7 +3455,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) } break; case ValueId::va_arg: { - analyze_type(module, value->va_arg.va_list, get_pointer_type(module, get_va_list_type(module))); + analyze_type(module, value->va_arg.va_list, get_pointer_type(module, get_va_list_type(module)), must_be_constant); value_type = value->va_arg.type; typecheck(module, expected_type, value_type); } break; @@ -3304,8 +3471,14 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) assert(!value->aggregate_initialization.is_constant); bool is_constant = true; - auto values = value->aggregate_initialization.values; - auto names = value->aggregate_initialization.names; + auto elements = value->aggregate_initialization.elements; + auto zero = value->aggregate_initialization.zero; + u64 field_mask = 0; + + // TODO: make consecutive initialization with `zero` constant + // ie: + // Right now 0, 1, 2, 3 => constant values, rest zeroed is constant because `declaration_index == initialization_index` + // With constant initialization values 2, 3, 4 and rest zeroed, the aggregate initialization because `declaration_index != initialization_index`, that is, the first initialization index (0) does not match the declaration index (2). The same case can be applied for cases (1, 3) and (2, 4) switch (resolved_type->id) { @@ -3313,11 +3486,27 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) { bool is_ordered = true; auto fields = resolved_type->structure.fields; + assert(fields.length <= 64); - for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + auto same_values_as_field = fields.length == elements.length; + auto is_properly_initialized = same_values_as_field || zero; + + if (zero && same_values_as_field) { - auto value = values[initialization_index]; - auto name = names[initialization_index]; + report_error(); + } + + if (!is_properly_initialized) + { + report_error(); + } + + assert(elements.length <= fields.length); + + for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) + { + auto value = elements[initialization_index].value; + auto name = elements[initialization_index].name; u32 declaration_index; for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) @@ -3335,11 +3524,20 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) report_error(); } + auto mask = 1 << declaration_index; + auto current_mask = field_mask; + if (current_mask & mask) + { + // Repeated field + report_error(); + } + field_mask = current_mask | mask; + is_ordered = is_ordered && declaration_index == initialization_index; auto field = fields[declaration_index]; auto declaration_type = field.type; - analyze_type(module, value, declaration_type); + analyze_type(module, value, declaration_type, must_be_constant); is_constant = is_constant && value->is_constant(); } @@ -3348,14 +3546,27 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) case TypeId::bits: { auto fields = resolved_type->bits.fields; + assert(fields.length <= 64); + auto same_values_as_field = fields.length == elements.length; + auto is_properly_initialized = same_values_as_field || zero; - assert(values.length == names.length); - - for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + if (zero && same_values_as_field) { - auto value = values[initialization_index]; - auto name = names[initialization_index]; + report_error(); + } + + if (!is_properly_initialized) + { + report_error(); + } + + assert(elements.length <= fields.length); + + for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) + { + auto value = elements[initialization_index].value; + auto name = elements[initialization_index].name; u32 declaration_index; for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) @@ -3373,9 +3584,18 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) report_error(); } + auto mask = 1 << declaration_index; + auto current_mask = field_mask; + if (current_mask & mask) + { + // Repeated field + report_error(); + } + field_mask = current_mask | mask; + auto field = fields[declaration_index]; auto declaration_type = field.type; - analyze_type(module, value, declaration_type); + analyze_type(module, value, declaration_type, must_be_constant); is_constant = is_constant && value->is_constant(); } @@ -3383,14 +3603,13 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) } break; case TypeId::union_type: { - if (values.length != 1) + if (elements.length != 1) { report_error(); } - auto initialization_value = values[0]; - assert(names.length == 1); - auto initialization_name = names[0]; + auto initialization_value = elements[0].value; + auto initialization_name = elements[0].name; u64 i; auto fields = resolved_type->union_type.fields; @@ -3409,9 +3628,70 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) } auto field = &fields[i]; - analyze_type(module, initialization_value, field->type); + analyze_type(module, initialization_value, field->type, must_be_constant); + } break; + case TypeId::enum_array: + { + bool is_ordered = true; + auto enum_type = resolved_type->enum_array.enum_type; + auto element_type = resolved_type->enum_array.element_type; + assert(enum_type->id == TypeId::enumerator); + auto fields = enum_type->enumerator.fields; - value_type = expected_type; + assert(fields.length <= 64); + + auto same_values_as_field = fields.length == elements.length; + auto is_properly_initialized = same_values_as_field || zero; + + if (zero && same_values_as_field) + { + report_error(); + } + + if (!is_properly_initialized) + { + report_error(); + } + + assert(elements.length <= fields.length); + + for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) + { + auto value = elements[initialization_index].value; + auto name = elements[initialization_index].name; + + u32 declaration_index; + for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) + { + auto& field = fields[declaration_index]; + + if (name.equal(field.name)) + { + break; + } + } + + if (declaration_index == fields.length) + { + report_error(); + } + + auto mask = 1 << declaration_index; + auto current_mask = field_mask; + if (current_mask & mask) + { + // Repeated field + report_error(); + } + field_mask = current_mask | mask; + + is_ordered = is_ordered && declaration_index == initialization_index; + + analyze_type(module, value, element_type, must_be_constant); + is_constant = is_constant && value->is_constant(); + } + + value->aggregate_initialization.is_constant = is_constant && is_ordered; } break; default: report_error(); } @@ -3435,9 +3715,9 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) auto condition = value->select.condition; auto true_value = value->select.true_value; auto false_value = value->select.false_value; - analyze_type(module, condition, 0); + analyze_type(module, condition, 0, must_be_constant); auto is_boolean = false; - analyze_binary_type(module, true_value, false_value, is_boolean, expected_type); + analyze_binary_type(module, true_value, false_value, is_boolean, expected_type, must_be_constant); auto left_type = true_value->type; auto right_type = false_value->type; @@ -3505,6 +3785,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) }, .id = TypeId::structure, .name = string_literal("string_to_enum"), + .scope = enum_type->scope, }); resolve_type_in_place(module, struct_type); @@ -3768,7 +4049,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) auto string_type = get_slice_type(module, uint8(module)); - analyze_type(module, enum_string_value, string_type); + analyze_type(module, enum_string_value, string_type, must_be_constant); value_type = struct_type; } break; case ValueId::undefined: @@ -3875,6 +4156,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) }, .id = TypeId::alias, .name = declaration_constant_argument.name, + .scope = ¯o_instantiation->scope, }); instantiation_constant_argument.type = instantiation_type; } break; @@ -3905,7 +4187,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) auto argument_type = declaration_argument.variable.type; assert(argument_type); - analyze_type(module, instantiation_argument, argument_type); + analyze_type(module, instantiation_argument, argument_type, must_be_constant); } LLVMMetadataRef type_buffer[64]; @@ -3949,7 +4231,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) auto argument_type = declaration_argument.variable.type; assert(argument_type); - analyze_type(module, instantiation_argument, argument_type); + analyze_type(module, instantiation_argument, argument_type, must_be_constant); } } @@ -3975,8 +4257,6 @@ fn LLVMTypeRef get_llvm_type(Type* type, TypeKind type_kind) } } -fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind); - fn bool type_is_integer_backing(Type* type) { switch (type->id) @@ -4061,7 +4341,32 @@ fn LLVMValueRef create_coerced_load(Module* module, LLVMValueRef source, Type* s } else { - trap(); + auto scalable_vector_type = false; + if (scalable_vector_type) + { + trap(); + } + else + { + // Coercion through memory + auto original_destination_alignment = get_byte_alignment(destination_type); + auto source_alignment = get_byte_alignment(source_type); + auto destination_alignment = MAX(original_destination_alignment, source_alignment); + auto destination_alloca = create_alloca(module, { + .type = destination_type, + .name = string_literal("coerce"), + .alignment = destination_alignment, + }); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + LLVMBuildMemCpy(module->llvm.builder, destination_alloca, destination_alignment, source, source_alignment, LLVMConstInt(u64_type->llvm.abi, source_size, false)); + auto load = create_load(module, { + .type = destination_type, + .pointer = destination_alloca, + .alignment = destination_alignment, + }); + result = load; + } } } } @@ -4132,7 +4437,25 @@ fn void create_coerced_store(Module* module, LLVMValueRef source_value, Type* so } else { - trap(); + // Coercion through memory + + auto original_destination_alignment = get_byte_alignment(destination_type); + auto source_alloca_alignment = MAX(original_destination_alignment, get_byte_alignment(source_type)); + auto source_alloca = create_alloca(module, { + .type = source_type, + .name = string_literal("coerce"), + .alignment = source_alloca_alignment, + }); + create_store(module, { + .source = source_value, + .destination = source_alloca, + .type = source_type, + .alignment = source_alloca_alignment, + }); + + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + LLVMBuildMemCpy(module->llvm.builder, destination_value, original_destination_alignment, source_alloca, source_alloca_alignment, LLVMConstInt(u64_type->llvm.abi, destination_size, false)); } } @@ -4172,7 +4495,7 @@ fn SliceEmitResult emit_slice_expression(Module* module, Value* value) auto end = value->slice_expression.end; assert(array_like->kind == ValueKind::left); - emit_value(module, array_like, TypeKind::memory); + emit_value(module, array_like, TypeKind::memory, false); auto pointer_type = array_like->type; assert(pointer_type->id == TypeId::pointer); @@ -4185,12 +4508,12 @@ fn SliceEmitResult emit_slice_expression(Module* module, Value* value) if (start) { - emit_value(module, start, TypeKind::memory); + emit_value(module, start, TypeKind::memory, false); } if (end) { - emit_value(module, end, TypeKind::memory); + emit_value(module, end, TypeKind::memory, false); } switch (sliceable_type->id) @@ -4507,7 +4830,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, if (coerce_to_type->id != TypeId::structure && type_is_abi_equal(module, semantic_argument_type, coerce_to_type) && argument_abi.attributes.direct.offset == 0) { - emit_value(module, semantic_call_argument_value, TypeKind::memory); + emit_value(module, semantic_call_argument_value, TypeKind::memory, false); auto evaluation_kind = get_evaluation_kind(argument_abi.semantic_type); Value* v; @@ -4563,11 +4886,11 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, { src->type = 0; src->kind = ValueKind::left; - analyze_type(module, src, 0); + analyze_type(module, src, 0, false); } } - emit_value(module, semantic_call_argument_value, TypeKind::memory); + emit_value(module, semantic_call_argument_value, TypeKind::memory, false); auto destination_size = get_byte_size(coerce_to_type); auto source_size = get_byte_size(argument_abi.semantic_type); auto alignment = get_byte_alignment(argument_abi.semantic_type); @@ -4575,7 +4898,15 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, LLVMValueRef source = src->llvm; if (source_size < destination_size) { - trap(); + auto alloca = create_alloca(module, { + .type = argument_abi.semantic_type, + .name = string_literal("coerce"), + .alignment = alignment, + }); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + LLVMBuildMemCpy(module->llvm.builder, alloca, alignment, source, alignment, LLVMConstInt(u64_type->llvm.abi, source_size, false)); + source = alloca; } auto coerce_fields = coerce_to_type->structure.fields; @@ -4681,25 +5012,51 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, } break; default: { + LLVMValueRef pointer = 0; + Type* pointer_type = 0; if (src->type->id != TypeId::pointer) { - assert(src->kind == ValueKind::right); - assert(src->type->id == TypeId::structure); auto type = src->type; - assert(src->kind == ValueKind::right); - src->type = 0; - src->kind = ValueKind::left; - analyze_type(module, src, get_pointer_type(module, type)); + pointer_type = get_pointer_type(module, type); + if (src->id != ValueId::variable_reference) + { + pointer = create_alloca(module, { + .type = type, + .name = string_literal("my.coerce"), + }); + emit_assignment(module, pointer, pointer_type, src); + } + else + { + assert(src->kind == ValueKind::right); + assert(src->type->id == TypeId::structure); + assert(src->id == ValueId::variable_reference); + assert(src->kind == ValueKind::right); + src->type = 0; + src->kind = ValueKind::left; + analyze_type(module, src, pointer_type, false); + } + } + else + { + trap(); } - assert(src->type->id == TypeId::pointer); - assert(src->type->llvm.abi == module->llvm.pointer_type); - emit_value(module, src, TypeKind::memory); + assert(pointer_type); + assert(pointer_type->id == TypeId::pointer); + auto element_type = pointer_type->pointer.element_type; - assert(src->type->id == TypeId::pointer); - auto source_type = src->type->pointer.element_type; + if (!pointer) + { + assert(src->type->id == TypeId::pointer); + assert(src->type->llvm.abi == module->llvm.pointer_type); + emit_value(module, src, TypeKind::memory, false); + pointer = src->llvm; + } + + auto source_type = element_type; assert(source_type == argument_abi.semantic_type); - auto load = create_coerced_load(module, src->llvm, source_type, destination_type); + auto load = create_coerced_load(module, pointer, source_type, destination_type); auto is_cmse_ns_call = false; if (is_cmse_ns_call) @@ -4747,7 +5104,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, if (is_constant) { - emit_value(module, semantic_call_argument_value, TypeKind::memory); + emit_value(module, semantic_call_argument_value, TypeKind::memory, true); bool is_constant = true; LLVMLinkage linkage_type = LLVMInternalLinkage; @@ -4775,7 +5132,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, { semantic_call_argument_value->type = 0; semantic_call_argument_value->kind = ValueKind::left; - analyze_value(module, semantic_call_argument_value, pointer_type, TypeKind::memory); + analyze_value(module, semantic_call_argument_value, pointer_type, TypeKind::memory, false); llvm_abi_argument_value_buffer[abi_argument_count] = semantic_call_argument_value->llvm; abi_argument_count += 1; } break; @@ -4785,6 +5142,7 @@ fn LLVMValueRef emit_call(Module* module, Value* value, LLVMValueRef left_llvm, assert(abi_argument_type->pointer.element_type == semantic_call_argument_value->type); auto alloca = create_alloca(module, { .type = semantic_call_argument_value->type, + .name = string_literal("indirect.struct.passing"), }); emit_assignment(module, alloca, pointer_type, semantic_call_argument_value); llvm_abi_argument_value_buffer[abi_argument_count] = alloca; @@ -4977,7 +5335,7 @@ fn LLVMValueRef emit_va_arg(Module* module, Value* value, LLVMValueRef left_llvm auto raw_va_list_type = get_va_list_type(module); auto va_list_value = value->va_arg.va_list; - emit_value(module, va_list_value, TypeKind::memory); + emit_value(module, va_list_value, TypeKind::memory, false); auto u64_type = uint64(module); resolve_type_in_place(module, u64_type); auto zero = LLVMConstNull(u64_type->llvm.memory); @@ -5220,7 +5578,7 @@ fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef lef auto aggregate = value->field_access.aggregate; auto field_name = value->field_access.field_name; - emit_value(module, aggregate, TypeKind::memory); + emit_value(module, aggregate, TypeKind::memory, false); assert(aggregate->kind == ValueKind::left); auto aggregate_type = aggregate->type; @@ -5306,6 +5664,7 @@ fn LLVMValueRef emit_field_access(Module* module, Value* value, LLVMValueRef lef } auto field_type = resolved_aggregate_type->union_type.fields[field_index].type; + resolve_type_in_place(module, field_type); auto struct_type = LLVMStructTypeInContext(module->llvm.context, &field_type->llvm.memory, 1, false); assert(struct_type); @@ -5431,7 +5790,7 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, { case EvaluationKind::scalar: { - emit_value(module, right, type_kind); + emit_value(module, right, type_kind, false); create_store(module, { .source = right->llvm, .destination = left_llvm, @@ -5451,7 +5810,7 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, if (right->array_initialization.is_constant) { - emit_value(module, right, TypeKind::memory); + emit_value(module, right, TypeKind::memory, false); bool is_constant = true; LLVMLinkage linkage_type = LLVMInternalLinkage; @@ -5525,15 +5884,19 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, } break; case ValueId::aggregate_initialization: { - auto names = right->aggregate_initialization.names; - auto values = right->aggregate_initialization.values; + auto elements = right->aggregate_initialization.elements; + auto scope = right->aggregate_initialization.scope; auto is_constant = right->aggregate_initialization.is_constant; auto zero = right->aggregate_initialization.zero; - assert(names.length == values.length); + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + u64 byte_size = get_byte_size(value_type); + auto byte_size_value = LLVMConstInt(u64_type->llvm.abi, byte_size, false); + auto alignment = get_byte_alignment(value_type); if (is_constant) { - emit_value(module, right, TypeKind::memory); + emit_value(module, right, TypeKind::memory, true); LLVMLinkage linkage_type = LLVMInternalLinkage; LLVMValueRef before = 0; @@ -5542,12 +5905,8 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, bool externally_initialized = false; auto global = llvm_module_create_global_variable(module->llvm.module, value_type->llvm.memory, is_constant, linkage_type, right->llvm, string_literal("constarray"), before, thread_local_mode, address_space, externally_initialized); LLVMSetUnnamedAddress(global, LLVMGlobalUnnamedAddr); - auto alignment = get_byte_alignment(value_type); LLVMSetAlignment(global, alignment); - auto u64_type = uint64(module); - resolve_type_in_place(module, u64_type); - u64 memcpy_size = get_byte_size(value_type); - LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, LLVMConstInt(u64_type->llvm.abi, memcpy_size, false)); + LLVMBuildMemCpy(module->llvm.builder, left_llvm, alignment, global, alignment, byte_size_value); } else { @@ -5561,10 +5920,17 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, assert(fields.length <= 64); unused(field_mask); - for (u32 initialization_index = 0; initialization_index < (u32)values.length; initialization_index += 1) + if (zero) { - auto name = names[initialization_index]; - auto value = values[initialization_index]; + auto u8_type = uint8(module); + resolve_type_in_place(module, u8_type); + LLVMBuildMemSet(module->llvm.builder, left_llvm, LLVMConstNull(u8_type->llvm.memory), byte_size_value, alignment); + } + + for (const auto& element : elements) + { + auto name = element.name; + auto value = element.value; u32 declaration_index; for (declaration_index = 0; declaration_index < (u32)fields.length; declaration_index += 1) @@ -5579,56 +5945,43 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, assert(declaration_index < fields.length); + if (module->has_debug_info) + { + auto debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, element.line, element.column, scope->llvm, module->llvm.inlined_at); + LLVMSetCurrentDebugLocation2(module->llvm.builder, debug_location); + } + field_mask |= 1 << declaration_index; max_field_index = MAX(max_field_index, declaration_index); auto& field = fields[declaration_index]; auto destination_pointer = LLVMBuildStructGEP2(module->llvm.builder, resolved_value_type->llvm.memory, left_llvm, declaration_index, ""); emit_assignment(module, destination_pointer, get_pointer_type(module, field.type), value); } - - if (zero) - { - u64 buffer_field_count = sizeof(field_mask) * 8; - auto raw_end_uninitialized_field_count = clz(field_mask); - auto unused_buffer_field_count = buffer_field_count - fields.length; - auto end_uninitialized_field_count = raw_end_uninitialized_field_count - unused_buffer_field_count; - auto initialized_field_count = __builtin_popcount(field_mask); - auto uninitialized_field_count = fields.length - initialized_field_count; - - if (uninitialized_field_count != end_uninitialized_field_count) - { - trap(); - } - - if (end_uninitialized_field_count == 0) - { - report_error(); - } - - auto field_index_offset = fields.length - end_uninitialized_field_count; - auto destination_pointer = LLVMBuildStructGEP2(module->llvm.builder, resolved_value_type->llvm.abi, left_llvm, field_index_offset, ""); - auto start_field = &fields[field_index_offset]; - auto memset_size = get_byte_size(resolved_value_type) - start_field->offset; - auto u8_type = uint8(module); - auto u64_type = uint64(module); - resolve_type_in_place(module, u8_type); - resolve_type_in_place(module, u64_type); - LLVMBuildMemSet(module->llvm.builder, destination_pointer, LLVMConstNull(u8_type->llvm.memory), LLVMConstInt(u64_type->llvm.memory, memset_size, false), 1); - } } break; case TypeId::union_type: { - assert(names.length == 1); - assert(values.length == 1); + assert(elements.length == 1); auto fields = resolved_value_type->union_type.fields; auto biggest_field_index = resolved_value_type->union_type.biggest_field; auto& biggest_field = fields[biggest_field_index]; auto biggest_field_type = fields[biggest_field_index].type; - auto value = values[0]; + auto value = elements[0].value; auto field_value_type = value->type; auto field_type_size = get_byte_size(field_value_type); LLVMTypeRef struct_type; + auto union_size = resolved_value_type->union_type.byte_size; + + if (field_type_size < union_size) + { + auto u8_type = uint8(module); + resolve_type_in_place(module, u8_type); + LLVMBuildMemSet(module->llvm.builder, left_llvm, LLVMConstNull(u8_type->llvm.memory), LLVMConstInt(u64_type->llvm.memory, union_size, false), alignment); + } + else if (field_type_size > union_size) + { + unreachable(); + } if (type_is_abi_equal(module, field_value_type, biggest_field_type)) { @@ -5643,16 +5996,6 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, auto field_pointer_type = get_pointer_type(module, field_value_type); unused(biggest_field); emit_assignment(module, destination_pointer, field_pointer_type, value); - - auto union_size = resolved_value_type->union_type.byte_size; - if (field_type_size < union_size) - { - trap(); - } - else if (field_type_size > union_size) - { - unreachable(); - } } break; default: unreachable(); } @@ -5720,7 +6063,7 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, } break; case ValueId::string_to_enum: { - emit_value(module, right, TypeKind::memory); + emit_value(module, right, TypeKind::memory, false); auto enum_type = right->string_to_enum.type; auto s2e_struct_type = enum_type->enumerator.string_to_enum_struct_type; @@ -5745,8 +6088,9 @@ fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, } break; case ValueId::unary: case ValueId::select: + case ValueId::array_expression: { - emit_value(module, right, TypeKind::memory); + emit_value(module, right, TypeKind::memory, false); create_store(module, { .source = right->llvm, .destination = left_llvm, @@ -5960,18 +6304,19 @@ fn void emit_macro_instantiation(Module* module, Value* value) auto macro_instantiation = &value->macro_instantiation; module->current_macro_instantiation = macro_instantiation; - - for (Value* instantiation_argument: macro_instantiation->instantiation_arguments) - { - emit_value(module, instantiation_argument, TypeKind::abi); - } - LLVMMetadataRef caller_debug_location = 0; if (module->has_debug_info) { assert(!module->llvm.inlined_at); caller_debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, macro_instantiation->line, macro_instantiation->column, macro_instantiation->scope.parent->llvm, 0); + LLVMSetCurrentDebugLocation2(module->llvm.builder, caller_debug_location); } + + for (Value* instantiation_argument: macro_instantiation->instantiation_arguments) + { + emit_value(module, instantiation_argument, TypeKind::abi, false); + } + auto older_inlined_at = module->llvm.inlined_at; assert(!older_inlined_at); module->llvm.inlined_at = caller_debug_location; @@ -6072,14 +6417,31 @@ fn void analyze_block(Module* module, Block* block) } } -fn void emit_value(Module* module, Value* value, TypeKind type_kind) +fn LLVMValueRef emit_constant_array(Module* module, Slice elements, Type* element_type) +{ + LLVMValueRef value_buffer[64]; + + resolve_type_in_place(module, element_type); + + for (u64 i = 0; i < elements.length; i += 1) + { + auto* v = elements[i]; + emit_value(module, v, TypeKind::memory, true); + value_buffer[i] = v->llvm; + } + + auto constant_array = LLVMConstArray2(element_type->llvm.memory, value_buffer, elements.length); + return constant_array; +} + +fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect_constant) { assert(value->type); assert(!value->llvm); auto resolved_value_type = resolve_alias(module, value->type); resolve_type_in_place(module, resolved_value_type); - auto must_be_constant = !module->current_function && !module->current_macro_instantiation; + auto must_be_constant = expect_constant || (!module->current_function && !module->current_macro_instantiation); LLVMValueRef llvm_value = 0; switch (value->id) @@ -6095,7 +6457,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) assert(!unary_value->llvm); auto unary_id = value->unary.id; auto resolved_unary_type = resolve_alias(module, unary_value->type); - emit_value(module, unary_value, type_kind); + emit_value(module, unary_value, type_kind, must_be_constant); if (unary_id == UnaryId::truncate || unary_id == UnaryId::enum_name) { type_kind = TypeKind::abi; @@ -6286,41 +6648,10 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) } auto* left = value->binary.left; - if (left->llvm) - { - assert(false); // TODO: check if this if is really necessary - } - else - { - emit_value(module, left, TypeKind::abi); - } - - auto left_llvm = left->llvm; - - LLVMValueRef left_condition = 0; - - switch (left->type->id) - { - case TypeId::integer: - { - switch (left->type->integer.bit_count) - { - case 1: - left_condition = left_llvm; - break; - default: trap(); - } - } break; - default: trap(); - } - - assert(left_condition); auto llvm_function = module->current_function->variable.storage->llvm; assert(llvm_function); - auto current_basic_block = LLVMGetInsertBlock(module->llvm.builder); - auto* right_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.right"), llvm_function); auto* end_block = llvm_context_create_basic_block(module->llvm.context, string_literal("shortcircuit.end"), llvm_function); @@ -6339,7 +6670,11 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) break; } - LLVMBuildCondBr(module->llvm.builder, left_condition, true_block, false_block); + emit_value(module, left, TypeKind::abi, must_be_constant); + auto llvm_condition = emit_condition(module, left); + auto current_basic_block = LLVMGetInsertBlock(module->llvm.builder); + + LLVMBuildCondBr(module->llvm.builder, llvm_condition, true_block, false_block); LLVMPositionBuilderAtEnd(module->llvm.builder, right_block); @@ -6350,7 +6685,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) } else { - emit_value(module, right, TypeKind::abi); + emit_value(module, right, TypeKind::abi, must_be_constant); } auto right_llvm = right->llvm; @@ -6433,7 +6768,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) } else { - emit_value(module, binary_value, TypeKind::abi); + emit_value(module, binary_value, TypeKind::abi, must_be_constant); } llvm_values[i] = binary_value->llvm; @@ -6509,25 +6844,12 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) case ValueId::array_initialization: { auto values = value->array_initialization.values; - auto element_count = values.length; if (value->array_initialization.is_constant) { assert(value->kind == ValueKind::right); auto element_type = resolved_value_type->array.element_type; - LLVMValueRef value_buffer[64]; - - resolve_type_in_place(module, element_type); - - for (u64 i = 0; i < element_count; i += 1) - { - auto* v = values[i]; - emit_value(module, v, TypeKind::memory); - value_buffer[i] = v->llvm; - } - - auto constant_array = LLVMConstArray2(element_type->llvm.memory, value_buffer, element_count); - llvm_value = constant_array; + llvm_value = emit_constant_array(module, values, element_type); } else { @@ -6583,8 +6905,8 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) { case ValueKind::left: { - emit_value(module, array_like, TypeKind::memory); - emit_value(module, index, TypeKind::memory); + emit_value(module, array_like, TypeKind::memory, must_be_constant); + emit_value(module, index, TypeKind::memory, must_be_constant); auto array_like_type = array_like->type; assert(array_like_type->id == TypeId::pointer); @@ -6592,20 +6914,48 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) switch (pointer_element_type->id) { + case TypeId::enum_array: case TypeId::array: { auto array_type = pointer_element_type; auto uint64_type = uint64(module); resolve_type_in_place(module, uint64_type); - auto zero_index = LLVMConstNull(uint64_type->llvm.abi); + auto u64_llvm = uint64_type->llvm.abi; + auto zero_index = LLVMConstNull(u64_llvm); + + Type* element_type = 0; + LLVMValueRef llvm_index = index->llvm; + + switch (pointer_element_type->id) + { + case TypeId::array: + { + element_type = array_type->array.element_type; + } break; + case TypeId::enum_array: + { + auto enum_type = array_type->enum_array.enum_type; + assert(enum_type->id == TypeId::enumerator); + auto enumerator_size = get_bit_size(enum_type->enumerator.backing_type); + if (enumerator_size != 64) + { + llvm_index = LLVMBuildIntCast2(module->llvm.builder, llvm_index, u64_llvm, false, ""); + } + element_type = array_type->enum_array.element_type; + } break; + default: unreachable(); + } + + assert(element_type); + assert(llvm_index); + LLVMValueRef indices[] = { zero_index, index->llvm }; auto gep = create_gep(module, { .type = array_type->llvm.memory, .pointer = array_like->llvm, .indices = array_to_slice(indices), }); - auto element_type = array_type->array.element_type; switch (value->kind) { @@ -6735,9 +7085,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) } break; case ValueId::aggregate_initialization: { - auto names = value->aggregate_initialization.names; - auto values = value->aggregate_initialization.values; - assert(names.length == values.length); + auto elements = value->aggregate_initialization.elements; auto is_constant = value->aggregate_initialization.is_constant; auto zero = value->aggregate_initialization.zero; @@ -6750,27 +7098,26 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) if (is_constant) { LLVMValueRef constant_buffer[64]; - u32 constant_count = (u32)values.length; + u32 constant_count = (u32)elements.length; - for (u64 i = 0; i < values.length; i += 1) + for (u64 i = 0; i < elements.length; i += 1) { - auto* value = values[i]; - emit_value(module, value, TypeKind::memory); + auto* value = elements[i].value; + emit_value(module, value, TypeKind::memory, must_be_constant); auto llvm_value = value->llvm; assert(llvm_value); assert(LLVMIsAConstant(llvm_value)); constant_buffer[i] = llvm_value; } - if (zero) { - if (values.length == fields.length) + if (elements.length == fields.length) { unreachable(); } - for (u64 i = values.length; i < fields.length; i += 1) + for (u64 i = elements.length; i < fields.length; i += 1) { auto& field = fields[i]; auto field_type = field.type; @@ -6804,10 +7151,10 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) { u64 bits_value = 0; - for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) { - auto value = values[initialization_index]; - auto name = names[initialization_index]; + auto value = elements[initialization_index].value; + auto name = elements[initialization_index].name; u32 declaration_index; for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) @@ -6845,10 +7192,10 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) { llvm_value = LLVMConstNull(abi_type); - for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + for (u32 initialization_index = 0; initialization_index < elements.length; initialization_index += 1) { - auto value = values[initialization_index]; - auto name = names[initialization_index]; + auto value = elements[initialization_index].value; + auto name = elements[initialization_index].name; u32 declaration_index; for (declaration_index = 0; declaration_index < fields.length; declaration_index += 1) @@ -6868,7 +7215,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) const auto& field = fields[declaration_index]; - emit_value(module, value, TypeKind::memory); + emit_value(module, value, TypeKind::memory, must_be_constant); auto extended = LLVMBuildZExt(module->llvm.builder, value->llvm, abi_type, ""); auto shl = LLVMBuildShl(module->llvm.builder, extended, LLVMConstInt(abi_type, field.offset, false), ""); @@ -6877,6 +7224,19 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) } } } break; + case TypeId::enum_array: + { + assert(is_constant); + assert(elements.length <= 64); + Value* value_buffer[64]; + for (u64 i = 0; i < elements.length; i += 1) + { + value_buffer[i] = elements[i].value; + } + Slice values = { value_buffer, elements.length }; + auto element_type = resolved_value_type->enum_array.element_type; + llvm_value = emit_constant_array(module, values, element_type); + } break; default: unreachable(); } } break; @@ -6889,7 +7249,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) auto condition = value->select.condition; auto true_value = value->select.true_value; auto false_value = value->select.false_value; - emit_value(module, condition, TypeKind::abi); + emit_value(module, condition, TypeKind::abi, must_be_constant); LLVMValueRef llvm_condition = condition->llvm; auto condition_type = condition->type; @@ -6905,13 +7265,17 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) default: trap(); } - emit_value(module, true_value, type_kind); - emit_value(module, false_value, type_kind); + emit_value(module, true_value, type_kind, must_be_constant); + emit_value(module, false_value, type_kind, must_be_constant); llvm_value = LLVMBuildSelect(module->llvm.builder, llvm_condition, true_value->llvm, false_value->llvm, ""); } break; case ValueId::unreachable: { + if (module->has_debug_info && !build_mode_is_optimized(module->build_mode)) + { + emit_intrinsic_call(module, IntrinsicIndex::trap, {}, {}); + } llvm_value = LLVMBuildUnreachable(module->llvm.builder); LLVMClearInsertionPosition(module->llvm.builder); } break; @@ -6919,7 +7283,7 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) { auto enum_type = value->string_to_enum.type; auto string_value = value->string_to_enum.string; - emit_value(module, string_value, TypeKind::memory); + emit_value(module, string_value, TypeKind::memory, must_be_constant); auto llvm_string_value = string_value->llvm; auto s2e = enum_type->enumerator.string_to_enum_function; @@ -6976,10 +7340,10 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) value->llvm = llvm_value; } -fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind) +fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind, bool must_be_constant) { - analyze_type(module, value, expected_type); - emit_value(module, value, type_kind); + analyze_type(module, value, expected_type, must_be_constant); + emit_value(module, value, type_kind, must_be_constant); } fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u32* last_line, u32* last_column, LLVMMetadataRef* last_debug_location) @@ -7056,7 +7420,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 report_error(); } - analyze_type(module, return_value, return_abi.semantic_type); + analyze_type(module, return_value, return_abi.semantic_type, false); auto pointer_type = get_pointer_type(module, return_abi.semantic_type); emit_assignment(module, return_alloca, pointer_type, return_value); } break; @@ -7071,7 +7435,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto macro_instantiation = module->current_macro_instantiation; auto return_type = macro_instantiation->return_type; assert(return_type); - analyze_type(module, return_value, return_type); + analyze_type(module, return_value, return_type, false); emit_assignment(module, macro_instantiation->return_alloca, get_pointer_type(module, return_type), return_value); LLVMBuildBr(module->llvm.builder, macro_instantiation->return_block); LLVMClearInsertionPosition(module->llvm.builder); @@ -7086,7 +7450,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto local = statement->local; auto expected_type = local->variable.type; assert(!local->variable.storage); - analyze_type(module, local->variable.initial_value, expected_type); + analyze_type(module, local->variable.initial_value, expected_type, false); local->variable.type = expected_type ? expected_type : local->variable.initial_value->type; assert(local->variable.type); if (expected_type) @@ -7103,20 +7467,8 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto* exit_block = llvm_context_create_basic_block(module->llvm.context, string_literal("if.exit"), llvm_function); auto condition = statement->if_st.condition; - analyze_value(module, condition, 0, TypeKind::abi); - auto condition_type = condition->type; - - LLVMValueRef llvm_condition = 0; - assert(condition_type->id == TypeId::integer || condition_type->id == TypeId::pointer); - - llvm_condition = condition->llvm; - - if (!(condition_type->id == TypeId::integer && condition_type->integer.bit_count == 1)) - { - llvm_condition = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, llvm_condition, LLVMConstNull(condition_type->llvm.abi), ""); - } - - assert(llvm_condition); + analyze_value(module, condition, 0, TypeKind::abi, false); + auto llvm_condition = emit_condition(module, statement->if_st.condition); LLVMBuildCondBr(module->llvm.builder, llvm_condition, taken_block, not_taken_block); LLVMPositionBuilderAtEnd(module->llvm.builder, taken_block); @@ -7148,7 +7500,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 } break; case StatementId::expression: { - analyze_value(module, statement->expression, 0, TypeKind::memory); + analyze_value(module, statement->expression, 0, TypeKind::memory, false); } break; case StatementId::while_st: { @@ -7186,23 +7538,8 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 } else { - analyze_value(module, condition, 0, TypeKind::abi); - - auto boolean = uint1(module); - - LLVMValueRef llvm_condition = condition->llvm; - auto condition_type = condition->type; - if (condition_type != boolean) - { - switch (condition_type->id) - { - case TypeId::integer: - { - llvm_condition = LLVMBuildICmp(module->llvm.builder, LLVMIntNE, llvm_condition, LLVMConstNull(condition_type->llvm.abi), ""); - } break; - default: unreachable(); - } - } + analyze_value(module, condition, 0, TypeKind::abi, false); + auto llvm_condition = emit_condition(module, condition); LLVMBuildCondBr(module->llvm.builder, llvm_condition, body_block, exit_block); } @@ -7240,7 +7577,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto left = statement->assignment.left; auto right = statement->assignment.right; auto id = statement->assignment.id; - analyze_value(module, left, 0, TypeKind::memory); + analyze_value(module, left, 0, TypeKind::memory, false); auto left_type = left->type; if (left_type->id != TypeId::pointer) @@ -7254,7 +7591,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 { case StatementAssignmentId::assign: { - analyze_type(module, right, element_type); + analyze_type(module, right, element_type, false); emit_assignment(module, left_llvm, left_type, right); } break; case StatementAssignmentId::assign_add: @@ -7275,7 +7612,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 .pointer = left_llvm, .kind = TypeKind::abi, }); - analyze_value(module, right, element_type, TypeKind::abi); + analyze_value(module, right, element_type, TypeKind::abi, false); auto a = load; auto b = right->llvm; @@ -7311,94 +7648,95 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto discriminant = statement->switch_st.discriminant; auto clauses = statement->switch_st.clauses; - analyze_value(module, discriminant, 0, TypeKind::abi); + analyze_value(module, discriminant, 0, TypeKind::abi, false); auto discriminant_type = discriminant->type; + u32 invalid_clause_index = ~(u32)0; + u32 else_clause_index = invalid_clause_index; + u32 discriminant_case_count = 0; + + // TODO: more analysis switch (discriminant_type->id) { case TypeId::enumerator: { - u32 invalid_clause_index = ~(u32)0; - u32 else_clause_index = invalid_clause_index; - u32 discriminant_case_count = 0; - - for (u64 i = 0; i < clauses.length; i += 1) - { - auto& clause = clauses[i]; - clause.basic_block = llvm_context_create_basic_block(module->llvm.context, clause.values.length == 0 ? string_literal("switch.else_case_block") : string_literal("switch.case_block"), llvm_function); - discriminant_case_count += clause.values.length; - - if (clause.values.length == 0) - { - if (else_clause_index != invalid_clause_index) - { - report_error(); - } - - else_clause_index = i; - } - else - { - for (auto value: clause.values) - { - analyze_value(module, value, discriminant_type, TypeKind::abi); - if (!value->is_constant()) - { - report_error(); - } - } - } - } - - LLVMBasicBlockRef else_block; - if (else_clause_index != invalid_clause_index) - { - else_block = clauses[else_clause_index].basic_block; - } - else - { - else_block = llvm_context_create_basic_block(module->llvm.context, string_literal("switch.else_case_block"), llvm_function); - } - - auto switch_instruction = LLVMBuildSwitch(module->llvm.builder, discriminant->llvm, else_block, discriminant_case_count); - bool all_blocks_terminated = true; - - for (auto& clause : clauses) - { - for (auto value : clause.values) - { - LLVMAddCase(switch_instruction, value->llvm, clause.basic_block); - } - - LLVMPositionBuilderAtEnd(module->llvm.builder, clause.basic_block); - - analyze_block(module, clause.block); - - if (LLVMGetInsertBlock(module->llvm.builder)) - { - all_blocks_terminated = false; - LLVMBuildBr(module->llvm.builder, exit_block); - LLVMClearInsertionPosition(module->llvm.builder); - } - } - - if (else_clause_index == invalid_clause_index) - { - LLVMPositionBuilderAtEnd(module->llvm.builder, else_block); - LLVMBuildUnreachable(module->llvm.builder); - LLVMClearInsertionPosition(module->llvm.builder); - } - - LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); - - if (all_blocks_terminated) - { - LLVMBuildUnreachable(module->llvm.builder); - LLVMClearInsertionPosition(module->llvm.builder); - } } break; - default: trap(); + case TypeId::integer: + { + } break; + default: report_error(); + } + + for (u64 i = 0; i < clauses.length; i += 1) + { + auto& clause = clauses[i]; + clause.basic_block = llvm_context_create_basic_block(module->llvm.context, clause.values.length == 0 ? string_literal("switch.else_case_block") : string_literal("switch.case_block"), llvm_function); + discriminant_case_count += clause.values.length; + + if (clause.values.length == 0) + { + if (else_clause_index != invalid_clause_index) + { + report_error(); + } + + else_clause_index = i; + } + else + { + for (auto value: clause.values) + { + analyze_value(module, value, discriminant_type, TypeKind::abi, true); + } + } + } + + LLVMBasicBlockRef else_block; + if (else_clause_index != invalid_clause_index) + { + else_block = clauses[else_clause_index].basic_block; + } + else + { + else_block = llvm_context_create_basic_block(module->llvm.context, string_literal("switch.else_case_block"), llvm_function); + } + + auto switch_instruction = LLVMBuildSwitch(module->llvm.builder, discriminant->llvm, else_block, discriminant_case_count); + bool all_blocks_terminated = true; + + for (auto& clause : clauses) + { + for (auto value : clause.values) + { + LLVMAddCase(switch_instruction, value->llvm, clause.basic_block); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, clause.basic_block); + + analyze_block(module, clause.block); + + if (LLVMGetInsertBlock(module->llvm.builder)) + { + all_blocks_terminated = false; + LLVMBuildBr(module->llvm.builder, exit_block); + LLVMClearInsertionPosition(module->llvm.builder); + } + } + + if (else_clause_index == invalid_clause_index) + { + LLVMPositionBuilderAtEnd(module->llvm.builder, else_block); + LLVMBuildUnreachable(module->llvm.builder); + LLVMClearInsertionPosition(module->llvm.builder); + } + + LLVMPositionBuilderAtEnd(module->llvm.builder, exit_block); + + if (all_blocks_terminated) + { + LLVMBuildUnreachable(module->llvm.builder); + LLVMClearInsertionPosition(module->llvm.builder); } } break; case StatementId::for_each: @@ -7439,16 +7777,32 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 { auto kind = left_values[i]; auto right = right_values[i]; - assert(right->kind == ValueKind::left); - analyze_type(module, right, 0); - auto pointer_type = right->type; - if (pointer_type->id != TypeId::pointer) + analyze_type(module, right, 0, false); + + Type* aggregate_type = 0; + + switch (right->kind) { - report_error(); - } + case ValueKind::right: + { + aggregate_type = right->type; + if (!is_slice(aggregate_type)) + { + report_error(); + } + } break; + case ValueKind::left: + { + auto pointer_type = right->type; + if (pointer_type->id != TypeId::pointer) + { + report_error(); + } - auto aggregate_type = pointer_type->pointer.element_type; + aggregate_type = pointer_type->pointer.element_type; + } break; + } Type* child_type = 0; @@ -7485,39 +7839,67 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 local->variable.type = local_type; emit_local_variable(module, local); - emit_value(module, right, TypeKind::memory); + emit_value(module, right, TypeKind::memory, false); } assert(!local); LLVMValueRef length_value = 0; - // TODO: make it right for (auto value : right_values) { - auto pointer_type = value->type; - if (pointer_type->id != TypeId::pointer) + Type* aggregate_type = 0; + auto value_type = value->type; + + switch (value->kind) { - report_error(); + case ValueKind::right: + { + aggregate_type = value_type; + } break; + case ValueKind::left: + { + if (value_type->id != TypeId::pointer) + { + report_error(); + } + + aggregate_type = value_type->pointer.element_type; + } break; } - auto aggregate_type = pointer_type->pointer.element_type; + assert(aggregate_type); + + auto llvm_value = value->llvm; + switch (aggregate_type->id) { case TypeId::array: { + assert(value->kind == ValueKind::left); length_value = LLVMConstInt(index_type->llvm.abi, aggregate_type->array.element_count, false); } break; case TypeId::structure: { assert(aggregate_type->structure.is_slice); - auto gep = LLVMBuildStructGEP2(module->llvm.builder, aggregate_type->llvm.abi, value->llvm, 1, "slice.length.pointer"); - auto load = create_load(module, { - .type = index_type, - .pointer = gep, - }); - length_value = load; + switch (value->kind) + { + case ValueKind::right: + { + length_value = LLVMBuildExtractValue(module->llvm.builder, llvm_value, 1, "slice.length"); + } break; + case ValueKind::left: + { + auto gep = LLVMBuildStructGEP2(module->llvm.builder, aggregate_type->llvm.abi, llvm_value, 1, "slice.length.pointer"); + auto load = create_load(module, { + .type = index_type, + .pointer = gep, + }); + length_value = load; + } break; + } + } break; default: unreachable(); } @@ -7545,10 +7927,28 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 for (u64 i = 0; i < right_values.length; i += 1, local = local->next) { - auto kind = left_values[i]; + auto variable_kind = left_values[i]; auto right = right_values[i]; - auto aggregate_type = right->type->pointer.element_type; + auto right_type = right->type; + auto right_kind = right->kind; + auto right_llvm = right->llvm; + + Type* aggregate_type = 0; + switch (right_kind) + { + case ValueKind::right: + { + aggregate_type = right_type; + } break; + case ValueKind::left: + { + assert(right_type->id == TypeId::pointer); + aggregate_type = right_type->pointer.element_type; + } break; + } + + assert(aggregate_type); LLVMValueRef element_pointer_value = 0; @@ -7556,13 +7956,14 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 { case TypeId::array: { + assert(right_kind == ValueKind::left); LLVMValueRef indices[] = { index_zero, body_index_load, }; element_pointer_value = create_gep(module, { - .type = right->type->pointer.element_type->llvm.memory, - .pointer = right->llvm, + .type = aggregate_type->llvm.memory, + .pointer = right_llvm, .indices = array_to_slice(indices), }); } break; @@ -7570,11 +7971,15 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 { assert(aggregate_type->structure.is_slice); - auto load = create_load(module, { - .type = aggregate_type, - .pointer = right->llvm, - }); - auto extract_pointer = LLVMBuildExtractValue(module->llvm.builder, load, 0, ""); + if (right_kind == ValueKind::left) + { + right_llvm = create_load(module, { + .type = aggregate_type, + .pointer = right_llvm, + }); + } + + auto extract_pointer = LLVMBuildExtractValue(module->llvm.builder, right_llvm, 0, ""); LLVMValueRef indices[] = { body_index_load, @@ -7593,7 +7998,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 auto local_type = local->variable.type; - switch (kind) + switch (variable_kind) { case ValueKind::right: { @@ -7676,7 +8081,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 } break; default: { - analyze_type(module, end, 0); + analyze_type(module, end, 0, false); auto end_type = end->type; assert(end_type); start->type = end_type; @@ -7693,13 +8098,13 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 { if (!right->type) { - analyze_type(module, right, local_type); + analyze_type(module, right, local_type, false); } } local->variable.type = local_type; emit_local_variable(module, local); - emit_value(module, start, TypeKind::memory); + emit_value(module, start, TypeKind::memory, false); auto index_alloca = local->variable.storage->llvm; @@ -7716,7 +8121,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 .type = local_type, .pointer = index_alloca, }); - emit_value(module, end, TypeKind::abi); + emit_value(module, end, TypeKind::abi, false); auto length_value = end->llvm; auto index_compare = LLVMBuildICmp(module->llvm.builder, LLVMIntULT, header_index_load, length_value, ""); LLVMBuildCondBr(module->llvm.builder, index_compare, body_block, exit_block); @@ -7808,8 +8213,6 @@ struct ObjectGenerate fn BBLLVMCodeGenerationPipelineResult generate_object(LLVMModuleRef module, LLVMTargetMachineRef target_machine, ObjectGenerate options) { - llvm_module_set_target(module, target_machine); - if (options.run_optimization_passes) { // BBLLVM @@ -8351,8 +8754,8 @@ void emit(Module* module) if (source_size > destination_size) { - unused(pointer); - trap(); + auto u64_type = uint64(module); + LLVMBuildMemCpy(module->llvm.builder, pointer, address_alignment, address, address_alignment, LLVMConstInt(u64_type->llvm.abi, destination_size, false)); } } } @@ -8541,7 +8944,7 @@ void emit(Module* module) case ValueId::global: { assert(!module->current_function); - analyze_value(module, global->variable.initial_value, global->variable.type, TypeKind::memory); + analyze_value(module, global->variable.initial_value, global->variable.type, TypeKind::memory, true); auto initial_value_type = global->variable.initial_value->type; @@ -8610,38 +9013,6 @@ void emit(Module* module) dump_module(module); } - BBLLVMCodeGenerationOptimizationLevel code_generation_optimization_level; - switch (module->build_mode) - { - case BuildMode::debug_none: - case BuildMode::debug: - code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::none; - break; - case BuildMode::soft_optimize: - code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::less; - break; - case BuildMode::optimize_for_speed: - case BuildMode::optimize_for_size: - code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::normal; - break; - case BuildMode::aggressively_optimize_for_speed: - case BuildMode::aggressively_optimize_for_size: - code_generation_optimization_level = BBLLVMCodeGenerationOptimizationLevel::aggressive; - break; - case BuildMode::count: - unreachable(); - } - BBLLVMTargetMachineCreate target_machine_options = { - .target_triple = llvm_default_target_triple(), - .cpu_model = llvm_host_cpu_name(), - .cpu_features = llvm_host_cpu_features(), - .relocation_model = BBLLVMRelocationModel::default_relocation, - .code_model = BBLLVMCodeModel::none, - .optimization_level = code_generation_optimization_level, - }; - String error_message = {}; - auto target_machine = llvm_create_target_machine(&target_machine_options, &error_message); - assert(target_machine); BBLLVMOptimizationLevel optimization_level; switch (module->build_mode) { @@ -8667,7 +9038,7 @@ void emit(Module* module) case BuildMode::count: unreachable(); } - auto object_generation_result = generate_object(module->llvm.module, target_machine, { + auto object_generation_result = generate_object(module->llvm.module, module->llvm.target_machine, { .path = module->objects[0], .optimization_level = optimization_level, .run_optimization_passes = module->build_mode != BuildMode::debug_none, diff --git a/src/parser.cpp b/src/parser.cpp index 5d1efe6..0624718 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -450,7 +450,6 @@ fn u64 parse_integer_decimal_assume_valid(String string) fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder); - fn Type* parse_type(Module* module, Scope* scope) { auto start_character = module->content[module->offset]; @@ -465,6 +464,20 @@ fn Type* parse_type(Module* module, Scope* scope) { return noreturn_type(module); } + else if (identifier.equal(string_literal("enum_array"))) + { + skip_space(module); + expect_character(module, left_bracket); + auto enum_type = parse_type(module, scope); + expect_character(module, right_bracket); + + expect_character(module, left_parenthesis); + auto element_type = parse_type(module, scope); + expect_character(module, right_parenthesis); + + auto enum_array_type = get_enum_array_type(module, enum_type, element_type); + return enum_array_type; + } else { auto is_int_type = identifier.length > 1 && (identifier[0] == 's' || identifier[0] == 'u'); @@ -505,12 +518,19 @@ fn Type* parse_type(Module* module, Scope* scope) } else { - for (Type* type = module->first_type; type; type = type->next) + assert(scope); + auto it_scope = scope; + while (it_scope) { - if (identifier.equal(type->name)) + for (Type* type = it_scope->types.first; type; type = type->next) { - return type; + if (identifier.equal(type->name)) + { + return type; + } } + + it_scope = it_scope->parent; } report_error(); @@ -597,6 +617,7 @@ fn Type* parse_type(Module* module, Scope* scope) }, .id = TypeId::array, .name = string_literal(""), + .scope = element_type->scope, }); return result; @@ -749,6 +770,35 @@ fn u8 escape_character(u8 ch) } } +fn String parse_string_literal(Module* module) +{ + expect_character(module, '"'); + + auto start = module->offset; + + while (1) + { + auto ch = module->content[module->offset]; + if (ch == '"') + { + break; + } + else if (ch == '\\') + { + trap(); + } + else + { + module->offset += 1; + } + } + + auto end = module->offset; + module->offset += 1; + auto string_literal = module->content(start, end); + return string_literal; +} + fn Token tokenize(Module* module) { skip_space(module); @@ -953,29 +1003,7 @@ fn Token tokenize(Module* module) } break; case '"': { - module->offset += 1; - auto start = module->offset; - - while (1) - { - auto ch = module->content[module->offset]; - if (ch == '"') - { - break; - } - else if (ch == '\\') - { - trap(); - } - else - { - module->offset += 1; - } - } - - auto end = module->offset; - module->offset += 1; - auto string_literal = module->content(start, end); + auto string_literal = parse_string_literal(module); token = { .string_literal = string_literal, @@ -1191,6 +1219,89 @@ fn Token tokenize(Module* module) fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder); +fn Value* parse_aggregate_initialization(Module* module, Scope* scope, ValueBuilder builder, u8 end_ch) +{ + skip_space(module); + + u64 field_count = 0; + + AggregateInitializationElement element_buffer[64]; + bool zero = false; + + while (1) + { + skip_space(module); + + if (consume_character_if_match(module, end_ch)) + { + break; + } + + auto field_index = field_count; + if (consume_character_if_match(module, '.')) + { + auto name = parse_identifier(module); + skip_space(module); + expect_character(module, '='); + skip_space(module); + + auto line = get_line(module); + auto column = get_column(module); + + auto value = parse_value(module, scope, {}); + skip_space(module); + consume_character_if_match(module, ','); + + element_buffer[field_index] = { + .name = name, + .value = value, + .line = line, + .column = column, + }; + } + else + { + auto token = tokenize(module); + zero = token.id == TokenId::value_keyword && token.value_keyword == ValueKeyword::zero; + if (zero) + { + skip_space(module); + + if (consume_character_if_match(module, ',')) + { + skip_space(module); + } + + expect_character(module, right_brace); + break; + } + else + { + report_error(); + } + } + + field_count += 1; + } + + auto elements = arena_allocate(module->arena, field_count); + memcpy(elements.pointer, element_buffer, sizeof(element_buffer[0]) * field_count); + + auto result = new_value(module); + *result = { + .aggregate_initialization = { + .elements = elements, + .scope = scope, + .is_constant = false, + .zero = zero, + }, + .id = ValueId::aggregate_initialization, + .kind = builder.kind, + }; + + return result; +} + fn Value* parse_precedence(Module* module, Scope* scope, ValueBuilder builder); fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) { @@ -1422,33 +1533,42 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) u64 element_count = 0; Value* value_buffer[64]; - while (1) - { - skip_space(module); + skip_space(module); - if (consume_character_if_match(module, right_bracket)) + if (module->content[module->offset] == '.') + { + result = parse_aggregate_initialization(module, scope, builder, right_bracket); + } + else + { + while (1) { - break; + skip_space(module); + + if (consume_character_if_match(module, right_bracket)) + { + break; + } + + auto value = parse_value(module, scope, {}); + value_buffer[element_count] = value; + element_count += 1; + + consume_character_if_match(module, ','); } - auto value = parse_value(module, scope, {}); - value_buffer[element_count] = value; - element_count += 1; + auto values = new_value_array(module, element_count); + memcpy(values.pointer, value_buffer, element_count * sizeof(Value*)); - consume_character_if_match(module, ','); + result = new_value(module); + *result = { + .array_initialization = { + .values = values, + .is_constant = false, // This is analyzed later + }, + .id = ValueId::array_initialization, + }; } - - auto values = new_value_array(module, element_count); - memcpy(values.pointer, value_buffer, element_count * sizeof(Value*)); - - result = new_value(module); - *result = { - .array_initialization = { - .values = values, - .is_constant = false, // This is analyzed later - }, - .id = ValueId::array_initialization, - }; } break; case TokenId::dot: { @@ -1477,76 +1597,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) } break; case TokenId::left_brace: { - skip_space(module); - - u64 field_count = 0; - String name_buffer[64]; - Value* value_buffer[64]; - bool zero = false; - - while (1) - { - skip_space(module); - - if (consume_character_if_match(module, right_brace)) - { - break; - } - - auto field_index = field_count; - if (consume_character_if_match(module, '.')) - { - auto name = parse_identifier(module); - name_buffer[field_index] = name; - skip_space(module); - expect_character(module, '='); - skip_space(module); - - auto value = parse_value(module, scope, {}); - value_buffer[field_index] = value; - skip_space(module); - consume_character_if_match(module, ','); - } - else - { - auto token = tokenize(module); - zero = token.id == TokenId::value_keyword && token.value_keyword == ValueKeyword::zero; - if (zero) - { - skip_space(module); - - if (consume_character_if_match(module, ',')) - { - skip_space(module); - } - - expect_character(module, right_brace); - break; - } - else - { - report_error(); - } - } - - field_count += 1; - } - - auto names = arena_allocate(module->arena, field_count); - memcpy(names.pointer, name_buffer, sizeof(String) * field_count); - auto values = new_value_array(module, field_count); - memcpy(values.pointer, value_buffer, sizeof(Value*) * field_count); - - result = new_value(module); - *result = { - .aggregate_initialization = { - .names = names, - .values = values, - .is_constant = false, - .zero = zero, - }, - .id = ValueId::aggregate_initialization, - }; + result = parse_aggregate_initialization(module, scope, builder, right_brace); } break; case TokenId::value_keyword: { @@ -2638,6 +2689,20 @@ fn Block* parse_block(Module* module, Scope* parent_scope) return block; } +fn String parse_name(Module* module) +{ + String result; + if (module->content[module->offset] == '"') + { + result = parse_string_literal(module); + } + else + { + result = parse_identifier(module); + } + return result; +} + void parse(Module* module) { auto scope = &module->scope; @@ -2731,7 +2796,7 @@ void parse(Module* module) last_global = last_global->next; } - Type* type_it = module->first_type; + Type* type_it = module->scope.types.first; Type* forward_declaration = 0; while (type_it) { @@ -2779,6 +2844,7 @@ void parse(Module* module) enumerator, function, macro, + opaque, structure, typealias, union_type, @@ -2798,6 +2864,7 @@ void parse(Module* module) string_literal("enum"), string_literal("fn"), string_literal("macro"), + string_literal("opaque"), string_literal("struct"), string_literal("typealias"), string_literal("union"), @@ -2904,6 +2971,7 @@ void parse(Module* module) }, .id = TypeId::bits, .name = global_name, + .scope = &module->scope, }); unused(bits_type); } break; @@ -2938,7 +3006,7 @@ void parse(Module* module) auto field_index = field_count; field_count += 1; - auto field_name = parse_identifier(module); + auto field_name = parse_name(module); name_buffer[field_index] = field_name; skip_space(module); @@ -3018,6 +3086,7 @@ void parse(Module* module) }, .id = TypeId::enumerator, .name = global_name, + .scope = &module->scope, }); unused(enum_type); @@ -3189,6 +3258,7 @@ void parse(Module* module) }, .id = TypeId::function, .name = string_literal(""), + .scope = &module->scope, }); auto storage = new_value(module); @@ -3255,15 +3325,25 @@ void parse(Module* module) } break; case GlobalKeyword::macro: { - Type* type_argument_buffer[64]; - u64 type_argument_count = 0; - unused(type_argument_buffer); - unused(type_argument_count); - ConstantArgument constant_argument_buffer[64]; u64 constant_argument_count = 0; auto is_generic = consume_character_if_match(module, left_bracket); + auto macro_declaration = &arena_allocate(module->arena, 1)[0]; + + *macro_declaration = { + .arguments = {}, + .constant_arguments = {}, + .return_type = 0, + .block = 0, + .name = global_name, + .scope = { + .parent = scope, + .line = global_line, + .column = global_column, + .kind = ScopeKind::macro_declaration, + }, + }; if (is_generic) { @@ -3293,6 +3373,7 @@ void parse(Module* module) auto ty = type_allocate_init(module, { .id = TypeId::unresolved, .name = argument_name, + .scope = ¯o_declaration->scope, }); constant_argument_buffer[constant_argument_index] = { @@ -3322,23 +3403,8 @@ void parse(Module* module) assert(constant_argument_count == 0); } - auto constant_arguments = arena_allocate(module->arena, constant_argument_count); - memcpy(constant_arguments.pointer, constant_argument_buffer, sizeof(constant_argument_buffer[0]) * constant_argument_count); - - auto macro_declaration = &arena_allocate(module->arena, 1)[0]; - *macro_declaration = { - .arguments = {}, - .constant_arguments = constant_arguments, - .return_type = 0, - .block = 0, - .name = global_name, - .scope = { - .parent = scope, - .line = global_line, - .column = global_column, - .kind = ScopeKind::macro_declaration, - }, - }; + macro_declaration->constant_arguments = arena_allocate(module->arena, constant_argument_count); + memcpy(macro_declaration->constant_arguments.pointer, constant_argument_buffer, sizeof(constant_argument_buffer[0]) * constant_argument_count); if (module->last_macro_declaration) { @@ -3432,6 +3498,7 @@ void parse(Module* module) struct_type = type_allocate_init(module, { .id = TypeId::forward_declaration, .name = global_name, + .scope = &module->scope, }); } @@ -3485,6 +3552,9 @@ void parse(Module* module) field_count += 1; } + byte_size = align_forward(byte_size, byte_alignment); + assert(byte_size % byte_alignment == 0); + skip_space(module); consume_character_if_match(module, ';'); @@ -3523,6 +3593,7 @@ void parse(Module* module) }, .id = TypeId::alias, .name = global_name, + .scope = scope, }); unused(alias_type); } break; @@ -3541,6 +3612,7 @@ void parse(Module* module) union_type = type_allocate_init(module, { .id = TypeId::forward_declaration, .name = global_name, + .scope = &module->scope, }); } @@ -3581,7 +3653,7 @@ void parse(Module* module) .line = field_line, }; - biggest_field = byte_size > field_byte_size ? field_index : biggest_field; + biggest_field = field_byte_size > byte_size ? field_index : biggest_field; byte_alignment = MAX(byte_alignment, field_byte_alignment); byte_size = MAX(byte_size, field_byte_size); @@ -3596,6 +3668,9 @@ void parse(Module* module) auto fields = arena_allocate(module->arena, field_count); memcpy(fields.pointer, field_buffer, sizeof(field_buffer[0]) * field_count); + auto biggest_size = get_byte_size(fields[biggest_field].type); + assert(biggest_size == byte_size); + union_type->union_type = { .fields = fields, .byte_size = byte_size, @@ -3605,6 +3680,17 @@ void parse(Module* module) }; union_type->id = TypeId::union_type; } break; + case GlobalKeyword::opaque: + { + skip_space(module); + expect_character(module, ';'); + auto opaque_type = type_allocate_init(module, { + .id = TypeId::opaque, + .name = global_name, + .scope = &module->scope, + }); + unused(opaque_type); + } break; case GlobalKeyword::count: { set_checkpoint(module, checkpoint); diff --git a/tests/basic_struct_passing.bbb b/tests/basic_struct_passing.bbb new file mode 100644 index 0000000..37bc6d1 --- /dev/null +++ b/tests/basic_struct_passing.bbb @@ -0,0 +1,140 @@ +CallingConvention = enum +{ + c, +} + +InlineBehavior = enum +{ + default = 0, + always_inline = 1, + no_inline = 2, + inline_hint = 3, +} + +FunctionAttributes = struct +{ + inline_behavior: InlineBehavior, + naked: u1, +} + +Type = struct; + +TypeId = enum +{ + void, + noreturn, + forward_declaration, + integer, + function, + pointer, + array, + enum, + struct, + bits, + alias, + union, + unresolved, + vector, + floating_point, + enum_array, + opaque, +} + +TypeInteger = struct +{ + bit_count: u32, + signed: u1, +} + +TypePointer = struct +{ + element_type: &Type, + next: &Type, +} + +AbiRegisterCountSystemV = struct +{ + gpr: u32, + sse: u32, +}; + +AbiRegisterCount = union +{ + system_v: AbiRegisterCountSystemV, +}; + +AbiInformation = struct +{ + foo: u32, +} + +TypeFunction = struct +{ + semantic_return_type: &Type, + semantic_argument_types: []&Type, + calling_convention: CallingConvention, + is_variable_arguments: u1, + + abi_argument_types: []&Type, + abi_return_type: &Type, + available_registers: AbiRegisterCount, + argument_abis: []AbiInformation, + return_abi: AbiInformation, +} + +TypeContent = union +{ + integer: TypeInteger, + function: TypeFunction, + pointer: TypePointer, +} + +Type = struct +{ + content: TypeContent, + id: TypeId, + name: []u8, + next: &Type, +} + +give_me_string = fn () []u8 +{ + return "foooo"; +} + +get_type = fn (src: &Type, type: Type) &Type +{ + src.& = type; + return src; +} + +require = fn (ok: u1) void +{ + if (!ok) #trap(); +} + +[export] main = fn [cc(c)] () s32 +{ + >buffer: Type = undefined; + >pointer = &buffer; + require(pointer == &buffer); + require(pointer != zero); + >result = get_type(pointer, { + .content = { + .pointer = { + .element_type = pointer, + zero, + }, + }, + .id = .pointer, + .name = give_me_string(), + zero, + }); + + require(pointer != zero); + require(pointer == &buffer); + require(buffer.content.pointer.element_type == pointer); + require(buffer.id == .pointer); + + return 0; +} diff --git a/tests/enum_array.bbb b/tests/enum_array.bbb new file mode 100644 index 0000000..42a99c8 --- /dev/null +++ b/tests/enum_array.bbb @@ -0,0 +1,22 @@ +require = fn (ok: u1) void +{ + if (!ok) #trap(); +} + +E = enum +{ + a, + b, + c, + d, +} + +[export] main = fn [cc(c)] () s32 +{ + >some_enum_array: enum_array[E](u32) = [ .a = 4, .b = 3, .c = 2, .d = 1 ]; + require(some_enum_array[.a] == 4); + require(some_enum_array[.b] == 3); + require(some_enum_array[.c] == 2); + require(some_enum_array[.d] == 1); + return 0; +} diff --git a/tests/opaque.bbb b/tests/opaque.bbb new file mode 100644 index 0000000..23f8a8f --- /dev/null +++ b/tests/opaque.bbb @@ -0,0 +1,16 @@ +OpaqueType = opaque; + +[extern] memcpy = fn [cc(c)] (destination: &s32, source: &s32, size: u64) &OpaqueType; + +[export] main = fn [cc(c)] () s32 +{ + >destination: s32 = 1; + >source: s32 = 0; + >opaque_pointer = memcpy(&destination, &source, #byte_size(s32)); + >pointer: &s32 = #pointer_cast(opaque_pointer); + if (pointer != &destination) + { + #trap(); + } + return destination; +}