This commit is contained in:
David Gonzalez Martin 2025-06-07 09:14:54 -06:00
parent 0b1a070250
commit 8b6cf1e7fb
6 changed files with 467 additions and 9 deletions

View File

@ -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

View File

@ -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<char* const> arguments, Slice<char* const> envp)

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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;
}