diff --git a/src/compiler.bbb b/src/compiler.bbb index 5614547..ee3b871 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1,3 +1,8 @@ +report_error = fn () noreturn +{ + #trap(); +} + mode_t = typealias u64; int = typealias s32; usize = typealias u64; @@ -802,6 +807,7 @@ LLVMAttribute = opaque; LLVMMetadata = opaque; LLVMDIBuilder = opaque; +LLVMDebugRecord = opaque; LLVMTarget = opaque; LLVMTargetDataLayout = opaque; @@ -1490,6 +1496,8 @@ ValueId = enum unary, binary, string_literal, + variable, + local, } ValueConstantInteger = struct @@ -1641,6 +1649,7 @@ ValueContent = union function: ValueFunction, unary: ValueUnary, binary: ValueBinary, + variable: &Variable, } ValueKind = enum @@ -1993,6 +2002,10 @@ LLVMICmpPredicate = enum u32 [extern] LLVMDIBuilderCreateLexicalBlock = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, file: &LLVMMetadata, line: u32, column: u32) &LLVMMetadata; [extern] LLVMDIBuilderCreateDebugLocation = fn [cc(c)] (context: &LLVMContext, line: u32, column: u32, scope: &LLVMMetadata, inlined_at: &LLVMMetadata) &LLVMMetadata; +[extern] LLVMDIBuilderCreateAutoVariable = fn [cc(c)] (di_builder: &LLVMDIBuilder, scope: &LLVMMetadata, name_pointer: &u8, name_length: u64, file: &LLVMMetadata, line: u32, type: &LLVMMetadata, always_preserve: s32, flags: LLVMDIFlags, bit_alignment: u32) &LLVMMetadata; +[extern] LLVMDIBuilderInsertDeclareRecordAtEnd = fn [cc(c)] (di_builder: &LLVMDIBuilder, storage: &LLVMValue, local_variable: &LLVMMetadata, expression: &LLVMMetadata, debug_location: &LLVMMetadata, basic_block: &LLVMBasicBlock) &LLVMDebugRecord; + +[extern] LLVMDIBuilderCreateExpression = fn [cc(c)] (di_builder: &LLVMDIBuilder, address: &u64, length: u64) &LLVMMetadata; [extern] LLVMDIBuilderFinalize = fn [cc(c)] (di_builder: &LLVMDIBuilder) void; [extern] LLVMSetSubprogram = fn [cc(c)] (function: &LLVMValue, subprogram: &LLVMMetadata) void; @@ -2264,6 +2277,47 @@ Statement = struct id: StatementId, } +scope_to_block = fn (scope: &Scope) &Block +{ + assert(scope.kind == .local); + >result: &Block = #field_parent_pointer(scope, "scope"); + return result; +} + +new_local = fn (module: &Module, scope: &Scope) &Local +{ + >result = arena_allocate[Local](module.arena, 1); + result.& = zero; + + switch (scope.kind) + { + .local => + { + >block = scope_to_block(scope); + if (block.last_local) + { + block.last_local.next = result; + block.last_local = result; + } + else + { + block.first_local = result; + block.last_local = result; + } + }, + .for_each => + { + #trap(); + } + else => + { + report_error(); + } + } + + return result; +} + new_global = fn (module: &Module) &Global { >result = arena_allocate[Global](module.arena, 1); @@ -2437,11 +2491,6 @@ is_identifier = fn (ch: u8) u1 return is_identifier_start(ch) or is_decimal(ch); } -report_error = fn () noreturn -{ - #trap(); -} - get_line = fn (module: &Module) u32 { >line = module.line_offset + 1; @@ -2987,6 +3036,7 @@ TokenContent = union identifier: []u8, string_literal: []u8, } + Token = struct { content: TokenContent, @@ -3216,7 +3266,50 @@ tokenize = fn (module: &Module) Token { if (is_identifier_start(start_character)) { - #trap(); + >identifier = parse_identifier(module); + >value_keyword_s2e = #string_to_enum(ValueKeyword, identifier); + if (value_keyword_s2e.is_valid) + { + >value_keyword = value_keyword_s2e.enum_value; + token = { + .content = { + .value_keyword = value_keyword, + }, + .id = .value_keyword, + }; + } + else + { + >advance = identifier.pointer[identifier.length] == '?'; + identifier.length += #extend(advance); + module.offset += #extend(advance); + + >operator_keyword_s2e = #string_to_enum(OperatorKeyword, identifier); + + if (operator_keyword_s2e.is_valid) + { + >operator_keyword = operator_keyword_s2e.enum_value; + + token = { + .content = { + .operator_keyword = operator_keyword, + }, + .id = .operator_keyword, + }; + } + else + { + identifier.length -= #extend(advance); + module.offset -= #extend(advance); + + token = { + .content = { + .identifier = identifier, + }, + .id = .identifier, + }; + } + } } else { @@ -3256,6 +3349,83 @@ ValueBuilder = struct parse_precedence = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value; +reference_identifier = fn (module: &Module, current_scope: &Scope, identifier: []u8, kind: ValueKind) &Value +{ + assert(!string_equal(identifier, "")); + assert(!string_equal(identifier, "_")); + + >variable: &Variable = zero; + >scope = current_scope; + + while (scope != zero and variable == zero) + { + switch (scope.kind) + { + .global => + { + #trap(); + }, + .function => + { + #trap(); + }, + .local => + { + assert(scope.parent != zero); + assert(scope.parent.kind != .global); + + >block = scope_to_block(scope); + >local = block.first_local; + + while (local != zero) + { + assert(local.next == zero or block.last_local != local); + + if (string_equal(identifier, local.variable.name)) + { + variable = &local.variable; + break; + } + + local = local.next; + } + }, + .for_each => + { + #trap(); + }, + .macro_declaration => + { + #trap(); + }, + .macro_instantiation => + { + #trap(); + }, + } + + scope = scope.parent; + } + + if (variable != zero) + { + >result = new_value(module); + result.& = { + .content = { + .variable = variable, + }, + .id = .variable, + .kind = kind, + zero, + }; + return result; + } + else + { + report_error(); + } +} + parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value { >token = builder.token; @@ -3315,7 +3485,7 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value }, .identifier => { - #trap(); + result = reference_identifier(module, scope, token.content.identifier, builder.kind); }, .value_intrinsic => { @@ -3632,7 +3802,43 @@ parse_statement = fn (module: &Module, scope: &Scope) &Statement { '>' => { - #trap(); + module.offset += 1; + skip_space(module); + + >local_name = parse_identifier(module); + skip_space(module); + + >local_type: &Type = zero; + + if (consume_character_if_match(module, ':')) + { + skip_space(module); + local_type = parse_type(module, scope); + skip_space(module); + } + + expect_character(module, '='); + + >initial_value = parse_value(module, scope, zero); + + >local = new_local(module, scope); + local.& = { + .variable = { + .type = local_type, + .scope = scope, + .name = local_name, + .line = statement_line, + .column = statement_column, + zero, + }, + .initial_value = initial_value, + zero, + }; + + statement.content = { + .local = local, + }; + statement.id = .local; }, '#' => { @@ -5356,6 +5562,68 @@ emit_assignment = fn (module: &Module, left_llvm: &LLVMValue, left_type: &Type, } } +emit_local_storage = fn (module: &Module, variable: &Variable) void +{ + assert(!variable.storage); + assert(variable.name.pointer != zero); + assert(variable.name.length != zero); + + >value_type = variable.type; + resolve_type_in_place(module, value_type); + >pointer_type = get_pointer_type(module, value_type); + + >alloca = create_alloca(module, { + .type = value_type, + .name = variable.name, + zero, + }); + + >storage = new_value(module); + storage.& = { + .type = pointer_type, + .id = .local, + .llvm = alloca, + zero, + }; + + variable.storage = storage; +} + +null_expression = fn (module: &Module) &LLVMMetadata +{ + return LLVMDIBuilderCreateExpression(module.llvm.di_builder, zero, 0); +} + +end_debug_local = fn (module: &Module, variable: &Variable, llvm_local: &LLVMMetadata) void +{ + >debug_location = LLVMDIBuilderCreateDebugLocation(module.llvm.context, variable.line, variable.column, variable.scope.llvm, module.llvm.inlined_at); + LLVMSetCurrentDebugLocation2(module.llvm.builder, debug_location); + >basic_block = LLVMGetInsertBlock(module.llvm.builder); + assert(basic_block != zero); + LLVMDIBuilderInsertDeclareRecordAtEnd(module.llvm.di_builder, variable.storage.llvm, llvm_local, null_expression(module), debug_location, basic_block); +} + +emit_local = fn (module: &Module, local: &Local) void +{ + emit_local_storage(module, &local.variable); + assert(local.variable.storage != zero); + + if (module.has_debug_info) + { + >debug_type = local.variable.type.llvm.debug; + assert(debug_type != zero); + + >always_preserve: s32 = 1; + >flags: LLVMDIFlags = zero; + >scope = local.variable.scope.llvm; + >bit_alignment = get_byte_alignment(local.variable.type) * 8; + >name = local.variable.name; + >local_variable = LLVMDIBuilderCreateAutoVariable(module.llvm.di_builder, scope, name.pointer, name.length, module.llvm.file, local.variable.line, debug_type, always_preserve, flags, bit_alignment); + + end_debug_local(module, &local.variable, local_variable); + } +} + analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) void { >parent_function_global: &Global = undefined; @@ -5445,6 +5713,18 @@ analyze_statement = fn (module: &Module, scope: &Scope, statement: &Statement) v report_error(); } }, + .local => + { + >local = statement.content.local; + >expected_type = local.variable.type; + assert(!local.variable.storage); + + analyze_type(module, local.initial_value, expected_type, zero); + local.variable.type = #select(expected_type != zero, expected_type, local.initial_value.type); + assert(local.variable.type != zero); + + #trap(); + }, else => { #trap(); @@ -6480,7 +6760,16 @@ names: [_][]u8 = [ "constant_xor", "constant_shift_left", "constant_shift_right", - + "minimal_stack", + "minimal_stack_arithmetic", + "minimal_stack_arithmetic2", + "minimal_stack_arithmetic3", + "stack_negation", + "stack_add", + "stack_sub", + "extend", + "integer_max", + "integer_hex", ]; [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8, envp: &&u8) s32 diff --git a/src/compiler.cpp b/src/compiler.cpp index 3615b92..543d771 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -439,6 +439,7 @@ global_variable String names[] = string_literal("return_array"), string_literal("bool_pair"), string_literal("min_max"), + string_literal("field_parent_pointer"), }; void entry_point(Slice arguments, Slice envp) diff --git a/src/compiler.hpp b/src/compiler.hpp index 3cefaa2..83c45d2 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -842,6 +842,7 @@ enum class ValueId argument, build_mode, has_debug_info, + field_parent_pointer, }; struct ValueConstantInteger @@ -1055,6 +1056,12 @@ struct MacroInstantiation LLVMBasicBlockRef return_block; }; +struct FieldParentPointer +{ + Value* pointer; + String name; +}; + fn bool variable_is_constant(Value* value); struct Value @@ -1080,6 +1087,7 @@ struct Value ValueStringToEnum string_to_enum; MacroDeclaration* macro_reference; MacroInstantiation macro_instantiation; + FieldParentPointer field_parent_pointer; }; Type* type; ValueId id; diff --git a/src/emitter.cpp b/src/emitter.cpp index 9e9119d..2aba2c9 100644 --- a/src/emitter.cpp +++ b/src/emitter.cpp @@ -4445,6 +4445,51 @@ fn void analyze_type(Module* module, Value* value, Type* expected_type, TypeAnal value_type = uint1(module); typecheck(module, expected_type, value_type); } break; + case ValueId::field_parent_pointer: + { + auto field_pointer = value->field_parent_pointer.pointer; + auto field_name = value->field_parent_pointer.name; + + if (!expected_type) + { + report_error(); + } + + value_type = expected_type; + + if (value_type->id != TypeId::pointer) + { + report_error(); + } + + auto aggregate_type = value_type->pointer.element_type; + + Type* field_type = 0; + switch (aggregate_type->id) + { + case TypeId::structure: + { + auto fields = aggregate_type->structure.fields; + for (auto& field : fields) + { + if (field_name.equal(field.name)) + { + field_type = field.type; + break; + } + } + } break; + default: report_error(); + } + + if (!field_type) + { + report_error(); + } + + auto pointer_to_field = get_pointer_type(module, field_type); + analyze_type(module, field_pointer, pointer_to_field, {}); + } break; default: unreachable(); } @@ -7813,6 +7858,49 @@ fn void emit_value(Module* module, Value* value, TypeKind type_kind, bool expect { llvm_value = LLVMConstInt(get_llvm_type(resolved_value_type, type_kind), module->has_debug_info, false); } break; + case ValueId::field_parent_pointer: + { + auto field_pointer = value->field_parent_pointer.pointer; + auto field_name = value->field_parent_pointer.name; + + emit_value(module, field_pointer, TypeKind::memory, false); + auto llvm_field_pointer = field_pointer->llvm; + assert(llvm_field_pointer); + + assert(resolved_value_type->id == TypeId::pointer); + auto aggregate_type = resolved_value_type->pointer.element_type; + + switch (aggregate_type->id) + { + case TypeId::structure: + { + auto fields = aggregate_type->structure.fields; + Field* result_field = 0; + for (auto& field: fields) + { + if (field_name.equal(field.name)) + { + result_field = &field; + break; + } + } + + assert(result_field); + auto offset = result_field->offset; + auto u64_type = uint64(module); + resolve_type_in_place(module, u64_type); + auto llvm_u64 = u64_type->llvm.abi; + auto address_int = LLVMBuildPtrToInt(module->llvm.builder, llvm_field_pointer, llvm_u64, ""); + + address_int = LLVMBuildSub(module->llvm.builder, address_int, LLVMConstInt(llvm_u64, offset, false), ""); + + auto address_pointer = LLVMBuildIntToPtr(module->llvm.builder, address_int, resolved_value_type->llvm.abi, ""); + llvm_value = address_pointer; + } break; + default: + report_error(); + } + } break; default: unreachable(); } diff --git a/src/parser.cpp b/src/parser.cpp index 36181cb..3a4a7d7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -9,6 +9,7 @@ enum class ValueIntrinsic enum_name, enum_values, extend, + field_parent_pointer, has_debug_info, integer_max, int_from_enum, @@ -1162,6 +1163,7 @@ fn Token tokenize(Module* module) string_literal("enum_name"), string_literal("enum_values"), string_literal("extend"), + string_literal("field_parent_pointer"), string_literal("has_debug_info"), string_literal("integer_max"), string_literal("int_from_enum"), @@ -1494,6 +1496,9 @@ fn Token tokenize(Module* module) auto operator_keyword = (OperatorKeyword)i; if (operator_keyword == OperatorKeyword::count) { + identifier.length -= advance; + module->offset -= advance; + token = { .identifier = identifier, .id = TokenId::identifier, @@ -1878,6 +1883,30 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder) .id = ValueId::build_mode, }; } break; + case ValueIntrinsic::field_parent_pointer: + { + skip_space(module); + expect_character(module, left_parenthesis); + + auto field_pointer = parse_value(module, scope, {}); + + skip_space(module); + expect_character(module, ','); + skip_space(module); + + auto field_name = parse_string_literal(module); + + skip_space(module); + expect_character(module, right_parenthesis); + + *result = { + .field_parent_pointer = { + .pointer = field_pointer, + .name = field_name, + }, + .id = ValueId::field_parent_pointer, + }; + } break; case ValueIntrinsic::count: unreachable(); } } break; diff --git a/tests/field_parent_pointer.bbb b/tests/field_parent_pointer.bbb new file mode 100644 index 0000000..4e10524 --- /dev/null +++ b/tests/field_parent_pointer.bbb @@ -0,0 +1,43 @@ +S = struct +{ + a: u8, + b: u32, + c: u8, +} + +assert = fn (ok: u1) void +{ + if (!ok) #trap(); +} + +[export] main = fn [cc(c)] () s32 +{ + >s: S = { + .a = 241, + .b = 12356, + .c = 128, + }; + + >p_a = &s.a; + >p_a_struct: &S = #field_parent_pointer(p_a, "a"); + assert(p_a_struct == &s); + assert(p_a_struct.a == s.a); + assert(p_a_struct.b == s.b); + assert(p_a_struct.c == s.c); + + >p_b = &s.b; + >p_b_struct: &S = #field_parent_pointer(p_b, "b"); + assert(p_b_struct == &s); + assert(p_b_struct.a == s.a); + assert(p_b_struct.b == s.b); + assert(p_b_struct.c == s.c); + + >p_c = &s.c; + >p_c_struct: &S = #field_parent_pointer(p_c, "c"); + assert(p_c_struct == &s); + assert(p_c_struct.a == s.a); + assert(p_c_struct.b == s.b); + assert(p_c_struct.c == s.c); + + return 0; +}