diff --git a/src/compiler.bbb b/src/compiler.bbb index 6a69b54..068e85f 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -533,8 +533,7 @@ CompilerCommand = enum BuildMode = enum { debug_none, - debug_fast, - debug_size, + debug, soft_optimize, optimize_for_speed, optimize_for_size, @@ -590,7 +589,21 @@ TypeId = enum { void, noreturn, + forward_declaration, integer, + function, + pointer, + array, + enum, + struct, + bits, + alias, + union, + unresolved, + vector, + floating_point, + enum_array, + opaque, } TypeInteger = struct @@ -609,6 +622,7 @@ Type = struct content: TypeContent, id: TypeId, name: []u8, + next: &Type, } ValueId = enum @@ -625,16 +639,148 @@ Value = struct i128_offset: u64 = 64 * 2; void_offset: u64 = i128_offset + 2; +ScopeKind = enum +{ + global, + function, + local, + for_each, + macro_declaration, + macro_instantiation, +} + +Scope = struct +{ + 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, +} + +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, + + first_type: &Type, + last_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 @@ -642,12 +788,12 @@ module_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; + return module.first_type + index; } module_void_type = fn (module: &Module) &Type { - return module.base_type_allocation + void_offset; + return module.first_type + void_offset; } module_noreturn_type = fn (module: &Module) &Type @@ -697,7 +843,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 +870,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 +986,447 @@ parse_identifier = fn (module: &Module) []u8 return result; } +CallingConvention = enum +{ + c, +} + +InlineBehavior = enum +{ + default = 0, + always_inline = 1, + no_inline = 2, + inline_hint = 3, +} + +FunctionAttributes = struct +{ + inline_behavior: InlineBehavior, + naked: u1, +} + +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) +{ + assert(is_decimal(ch)); + return (accumulator * 10) + (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_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; + }, + } + } + } + #trap(); + } + else if (start_character == '&') + { + #trap(); + } + else if (start_character == left_bracket) + { + #trap(); + } + else if (start_character == '#') + { + #trap(); + } + else + { + report_error(); + } +} + 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.first_type; + >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); + + #trap(); + }, + .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 @@ -902,12 +1510,15 @@ compile = fn (arena: &Arena, options: CompileOptions) void >module: Module = { .arena = arena, - .base_type_allocation = base_type_allocation, + .first_type = base_type_allocation, .void_value = void_value, .content = options.content, .offset = 0, .line_offset = 0, .line_character_offset = 0, + .scope = { + zero, + }, }; parse(&module); diff --git a/src/compiler.cpp b/src/compiler.cpp index f85712e..115d80e 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -306,8 +306,11 @@ 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"), }; void entry_point(Slice arguments, Slice environment) @@ -488,7 +491,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..9b0ffb9 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -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,6 +470,7 @@ struct Type TypeBits bits; TypeAlias alias; TypeUnion union_type; + TypeEnumArray enum_array; }; TypeId id; String name; @@ -528,6 +538,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 +596,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(); } } @@ -1126,6 +1151,7 @@ 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; @@ -1714,5 +1740,52 @@ 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(")"), + }; + + 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)), + }); + return enum_array_type; +} + + void parse(Module* module); void emit(Module* module); diff --git a/src/emitter.cpp b/src/emitter.cpp index 6ffb0ee..024dd9d 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); +fn void analyze_value(Module* module, Value* value, Type* expected_type, TypeKind type_kind); 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(); } @@ -460,9 +518,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 +559,7 @@ fn bool contains_no_user_data(Type* type, u64 start, u64 end) return false; } } + trap(); } break; default: return false; @@ -826,6 +907,17 @@ 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; + } break; default: unreachable(); } @@ -849,6 +941,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 +1011,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 +1116,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(); } @@ -3014,7 +3121,6 @@ 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); @@ -3026,6 +3132,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)); + Type* element_type = 0; switch (pointer_element_type->id) { @@ -3048,6 +3156,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(); } @@ -3118,6 +3230,7 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) if (!result_field) { + // Field not found report_error(); } @@ -3349,7 +3462,6 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) { auto fields = resolved_type->bits.fields; - assert(values.length == names.length); for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) @@ -3410,8 +3522,43 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type) auto field = &fields[i]; analyze_type(module, initialization_value, field->type); + } 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; + for (u32 initialization_index = 0; initialization_index < values.length; initialization_index += 1) + { + auto value = values[initialization_index]; + auto name = names[initialization_index]; + + 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(); + } + + is_ordered = is_ordered && declaration_index == initialization_index; + + analyze_type(module, value, element_type); + is_constant = is_constant && value->is_constant(); + } + + value->aggregate_initialization.is_constant = is_constant && is_ordered; } break; default: report_error(); } @@ -6072,6 +6219,23 @@ fn void analyze_block(Module* module, Block* block) } } +fn LLVMValueRef emit_constant_array(Module* module, Slice values, Type* element_type) +{ + LLVMValueRef value_buffer[64]; + + resolve_type_in_place(module, element_type); + + for (u64 i = 0; i < values.length; 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, values.length); + return constant_array; +} + fn void emit_value(Module* module, Value* value, TypeKind type_kind) { assert(value->type); @@ -6286,41 +6450,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 +6472,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); + 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); @@ -6509,25 +6646,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 { @@ -6592,20 +6716,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) { @@ -6877,6 +7029,12 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind) } } } break; + case TypeId::enum_array: + { + assert(is_constant); + auto element_type = resolved_value_type->enum_array.element_type; + llvm_value = emit_constant_array(module, values, element_type); + } break; default: unreachable(); } } break; @@ -7104,19 +7262,7 @@ fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u3 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); + 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); @@ -7187,22 +7333,7 @@ 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(); - } - } + auto llvm_condition = emit_condition(module, condition); LLVMBuildCondBr(module->llvm.builder, llvm_condition, body_block, exit_block); } diff --git a/src/parser.cpp b/src/parser.cpp index 5d1efe6..e54f1a4 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'); @@ -1191,6 +1204,83 @@ 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; + String name_buffer[64]; + Value* value_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); + 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); + + auto result = new_value(module); + *result = { + .aggregate_initialization = { + .names = names, + .values = values, + .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 +1512,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 +1576,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: { @@ -2779,6 +2809,7 @@ void parse(Module* module) enumerator, function, macro, + opaque, structure, typealias, union_type, @@ -2798,6 +2829,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"), @@ -3605,6 +3637,16 @@ 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, + }); + unused(opaque_type); + } break; case GlobalKeyword::count: { set_checkpoint(module, checkpoint); 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; +}