Pass some more tests
All checks were successful
CI / ci (Release, ubuntu-latest) (pull_request) Successful in 48s
CI / ci (MinSizeRel, ubuntu-latest) (pull_request) Successful in 52s
CI / ci (RelWithDebInfo, ubuntu-latest) (pull_request) Successful in 53s
CI / ci (Debug, ubuntu-latest) (pull_request) Successful in 4m18s
CI / ci (MinSizeRel, ubuntu-latest) (push) Successful in 53s
CI / ci (Release, ubuntu-latest) (push) Successful in 50s
CI / ci (RelWithDebInfo, ubuntu-latest) (push) Successful in 54s
CI / ci (Debug, ubuntu-latest) (push) Successful in 4m19s
All checks were successful
CI / ci (Release, ubuntu-latest) (pull_request) Successful in 48s
CI / ci (MinSizeRel, ubuntu-latest) (pull_request) Successful in 52s
CI / ci (RelWithDebInfo, ubuntu-latest) (pull_request) Successful in 53s
CI / ci (Debug, ubuntu-latest) (pull_request) Successful in 4m18s
CI / ci (MinSizeRel, ubuntu-latest) (push) Successful in 53s
CI / ci (Release, ubuntu-latest) (push) Successful in 50s
CI / ci (RelWithDebInfo, ubuntu-latest) (push) Successful in 54s
CI / ci (Debug, ubuntu-latest) (push) Successful in 4m19s
This commit is contained in:
parent
0b1a070250
commit
04ca049e6c
731
src/compiler.bbb
731
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;
|
||||
@ -1418,6 +1424,21 @@ get_byte_size = fn (type: &Type) u64
|
||||
}
|
||||
}
|
||||
|
||||
get_bit_size = fn (type: &Type) u64
|
||||
{
|
||||
switch (type.id)
|
||||
{
|
||||
.integer =>
|
||||
{
|
||||
return type.content.integer.bit_count;
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
get_byte_alignment = fn (type: &Type) u32
|
||||
{
|
||||
switch (type.id)
|
||||
@ -1490,6 +1511,9 @@ ValueId = enum
|
||||
unary,
|
||||
binary,
|
||||
string_literal,
|
||||
variable,
|
||||
local,
|
||||
unary_type,
|
||||
}
|
||||
|
||||
ValueConstantInteger = struct
|
||||
@ -1554,6 +1578,20 @@ ValueUnary = struct
|
||||
id: UnaryId,
|
||||
}
|
||||
|
||||
UnaryTypeId = enum
|
||||
{
|
||||
align_of,
|
||||
byte_size,
|
||||
enum_values,
|
||||
integer_max,
|
||||
}
|
||||
|
||||
ValueUnaryType = struct
|
||||
{
|
||||
type: &Type,
|
||||
id: UnaryTypeId,
|
||||
}
|
||||
|
||||
BinaryId = enum
|
||||
{
|
||||
add,
|
||||
@ -1641,6 +1679,8 @@ ValueContent = union
|
||||
function: ValueFunction,
|
||||
unary: ValueUnary,
|
||||
binary: ValueBinary,
|
||||
variable: &Variable,
|
||||
unary_type: ValueUnaryType,
|
||||
}
|
||||
|
||||
ValueKind = enum
|
||||
@ -1670,6 +1710,11 @@ value_is_constant = fn (value: &Value) u1
|
||||
{
|
||||
return 0;
|
||||
},
|
||||
.variable =>
|
||||
{
|
||||
>variable = value.content.variable;
|
||||
return value.kind == .left and variable.scope.kind == .global;
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
@ -1993,6 +2038,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;
|
||||
@ -2015,14 +2064,19 @@ LLVMICmpPredicate = enum u32
|
||||
[extern] LLVMSetCurrentDebugLocation2 = fn [cc(c)] (builder: &LLVMBuilder, debug_location: &LLVMMetadata) void;
|
||||
|
||||
[extern] llvm_builder_create_alloca = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, address_space: u32, alignment: u32, name: []u8) &LLVMValue;
|
||||
|
||||
[extern] LLVMBuildStore = fn [cc(c)] (builder: &LLVMBuilder, source: &LLVMValue, destination: &LLVMValue) &LLVMValue;
|
||||
[extern] LLVMBuildLoad2 = fn [cc(c)] (builder: &LLVMBuilder, type: &LLVMType, pointer: &LLVMValue, name: &u8) &LLVMValue;
|
||||
|
||||
[extern] LLVMBuildRet = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue) &LLVMValue;
|
||||
[extern] LLVMBuildRetVoid = fn [cc(c)] (builder: &LLVMBuilder) &LLVMValue;
|
||||
|
||||
[extern] LLVMBuildBr = fn [cc(c)] (builder: &LLVMBuilder, target_block: &LLVMBasicBlock) &LLVMValue;
|
||||
|
||||
[extern] LLVMBuildIntCast2 = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, signed: s32, name: &u8) &LLVMValue;
|
||||
[extern] LLVMBuildNeg = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, name: &u8) &LLVMValue;
|
||||
[extern] LLVMBuildSExt = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, name: &u8) &LLVMValue;
|
||||
[extern] LLVMBuildZExt = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, name: &u8) &LLVMValue;
|
||||
[extern] LLVMBuildTrunc = fn [cc(c)] (builder: &LLVMBuilder, value: &LLVMValue, type: &LLVMType, name: &u8) &LLVMValue;
|
||||
|
||||
[extern] LLVMBuildICmp = fn [cc(c)] (builder: &LLVMBuilder, predicate: LLVMICmpPredicate, left: &LLVMValue, right: &LLVMValue, name: &u8) &LLVMValue;
|
||||
|
||||
@ -2264,6 +2318,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 +2532,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;
|
||||
@ -2952,12 +3042,19 @@ ValueKeyword = enum
|
||||
ValueIntrinsic = enum
|
||||
{
|
||||
align_of,
|
||||
build_mode,
|
||||
byte_size,
|
||||
enum_from_int,
|
||||
enum_name,
|
||||
enum_values,
|
||||
extend,
|
||||
field_parent_pointer,
|
||||
has_debug_info,
|
||||
integer_max,
|
||||
int_from_enum,
|
||||
int_from_pointer,
|
||||
max,
|
||||
min,
|
||||
pointer_cast,
|
||||
pointer_from_int,
|
||||
select,
|
||||
@ -2987,6 +3084,7 @@ TokenContent = union
|
||||
identifier: []u8,
|
||||
string_literal: []u8,
|
||||
}
|
||||
|
||||
Token = struct
|
||||
{
|
||||
content: TokenContent,
|
||||
@ -3048,7 +3146,32 @@ tokenize = fn (module: &Module) Token
|
||||
},
|
||||
'#' =>
|
||||
{
|
||||
#trap();
|
||||
module.offset += 1;
|
||||
|
||||
if (is_identifier_start(module.content[module.offset]))
|
||||
{
|
||||
>identifier = parse_identifier(module);
|
||||
|
||||
>value_intrinsic_s2e = #string_to_enum(ValueIntrinsic, identifier);
|
||||
if (value_intrinsic_s2e.is_valid)
|
||||
{
|
||||
>value_intrinsic = value_intrinsic_s2e.enum_value;
|
||||
token = {
|
||||
.content = {
|
||||
.value_intrinsic = value_intrinsic,
|
||||
},
|
||||
.id = .value_intrinsic,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
},
|
||||
'<' =>
|
||||
{
|
||||
@ -3216,7 +3339,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
|
||||
{
|
||||
@ -3255,6 +3421,84 @@ ValueBuilder = struct
|
||||
}
|
||||
|
||||
parse_precedence = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value;
|
||||
parse_value = 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
|
||||
{
|
||||
@ -3315,12 +3559,134 @@ parse_left = fn (module: &Module, scope: &Scope, builder: ValueBuilder) &Value
|
||||
},
|
||||
.identifier =>
|
||||
{
|
||||
#trap();
|
||||
result = reference_identifier(module, scope, token.content.identifier, builder.kind);
|
||||
},
|
||||
.value_intrinsic =>
|
||||
{
|
||||
>intrinsic = token.content.value_intrinsic;
|
||||
result = new_value(module);
|
||||
|
||||
switch (intrinsic)
|
||||
{
|
||||
.enum_from_int,
|
||||
.enum_name,
|
||||
.extend,
|
||||
.int_from_enum,
|
||||
.int_from_pointer,
|
||||
.truncate,
|
||||
.pointer_cast,
|
||||
.pointer_from_int,
|
||||
.va_end,
|
||||
=>
|
||||
{
|
||||
>id: UnaryId = undefined;
|
||||
|
||||
switch (intrinsic)
|
||||
{
|
||||
.enum_from_int => { id = .enum_from_int; },
|
||||
.enum_name => { id = .enum_name; },
|
||||
.extend => { id = .extend; },
|
||||
.int_from_enum => { id = .int_from_enum; },
|
||||
.int_from_pointer => { id = .int_from_pointer; },
|
||||
.truncate => { id = .truncate; },
|
||||
.pointer_cast => { id = .pointer_cast; },
|
||||
.pointer_from_int => { id = .pointer_from_int; },
|
||||
.va_end => { id = .va_end; },
|
||||
else => { unreachable; },
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
expect_character(module, left_parenthesis);
|
||||
skip_space(module);
|
||||
>argument = parse_value(module, scope, zero);
|
||||
expect_character(module, right_parenthesis);
|
||||
|
||||
result.& = {
|
||||
.content = {
|
||||
.unary = {
|
||||
.value = argument,
|
||||
.id = id,
|
||||
},
|
||||
},
|
||||
.id = .unary,
|
||||
zero,
|
||||
};
|
||||
},
|
||||
.align_of,
|
||||
.byte_size,
|
||||
.enum_values,
|
||||
.integer_max,
|
||||
=>
|
||||
{
|
||||
skip_space(module);
|
||||
expect_character(module, left_parenthesis);
|
||||
skip_space(module);
|
||||
|
||||
>type = parse_type(module, scope);
|
||||
|
||||
expect_character(module, right_parenthesis);
|
||||
|
||||
>id: UnaryTypeId = undefined;
|
||||
switch (intrinsic)
|
||||
{
|
||||
.align_of => { id = .align_of; },
|
||||
.byte_size => { id = .byte_size; },
|
||||
.enum_values => { id = .enum_values; },
|
||||
.integer_max => { id = .integer_max; },
|
||||
else => { unreachable; },
|
||||
}
|
||||
|
||||
result.& = {
|
||||
.content = {
|
||||
.unary_type = {
|
||||
.type = type,
|
||||
.id = id,
|
||||
},
|
||||
},
|
||||
.id = .unary_type,
|
||||
zero,
|
||||
};
|
||||
},
|
||||
.select =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.string_to_enum =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
// No argument intrinsic call
|
||||
.trap,
|
||||
.va_start,
|
||||
.has_debug_info,
|
||||
=>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
// (value, T)
|
||||
.va_arg =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
// TODO
|
||||
.va_copy =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.max, .min =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.build_mode =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
.field_parent_pointer =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
},
|
||||
.left_bracket =>
|
||||
{
|
||||
#trap();
|
||||
@ -3632,7 +3998,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;
|
||||
},
|
||||
'#' =>
|
||||
{
|
||||
@ -4951,7 +5353,7 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi
|
||||
assert(!value.type);
|
||||
assert(!value.llvm);
|
||||
|
||||
if (expected_type != zero and expected_type.id == .unresolved)
|
||||
if (expected_type != zero and? expected_type.id == .unresolved)
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
@ -5041,11 +5443,48 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi
|
||||
{
|
||||
.extend =>
|
||||
{
|
||||
#trap();
|
||||
if (!expected_type)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
>extended_value = unary_value;
|
||||
analyze_type(module, extended_value, zero, { .must_be_constant = analysis.must_be_constant, zero });
|
||||
>source = extended_value.type;
|
||||
assert(source != zero);
|
||||
|
||||
>source_bit_size = get_bit_size(source);
|
||||
>expected_bit_size = get_bit_size(expected_type);
|
||||
|
||||
if (source_bit_size > expected_bit_size)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
else if (source_bit_size == expected_bit_size and type_is_signed(source) == type_is_signed(expected_type))
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
value_type = expected_type;
|
||||
},
|
||||
.truncate =>
|
||||
{
|
||||
#trap();
|
||||
if (!expected_type)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
analyze_type(module, unary_value, zero, { .must_be_constant = analysis.must_be_constant, zero });
|
||||
|
||||
>expected_bit_size = get_bit_size(expected_type);
|
||||
>source_bit_size = get_bit_size(unary_value.type);
|
||||
|
||||
if (expected_bit_size >= source_bit_size)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
value_type = expected_type;
|
||||
},
|
||||
.dereference =>
|
||||
{
|
||||
@ -5101,6 +5540,73 @@ analyze_type = fn (module: &Module, value: &Value, expected_type: &Type, analysi
|
||||
},
|
||||
}
|
||||
},
|
||||
.variable =>
|
||||
{
|
||||
switch (value.kind)
|
||||
{
|
||||
.left => { value_type = value.content.variable.storage.type; },
|
||||
.right => { value_type = value.content.variable.type; },
|
||||
}
|
||||
|
||||
assert(value_type != zero);
|
||||
|
||||
typecheck(module, expected_type, value_type);
|
||||
},
|
||||
.unary_type =>
|
||||
{
|
||||
>unary_type_id = value.content.unary_type.id;
|
||||
>unary_type = value.content.unary_type.type; // TODO: call resolve_type
|
||||
value.content.unary_type.type = unary_type;
|
||||
|
||||
if (unary_type_id == .enum_values)
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (expected_type != zero)
|
||||
{
|
||||
value_type = expected_type;
|
||||
}
|
||||
else
|
||||
{
|
||||
value_type = unary_type;
|
||||
}
|
||||
|
||||
assert(value_type != zero);
|
||||
|
||||
if (value_type.id != .integer)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
>value: u64 = undefined;
|
||||
>max_value = integer_max_value(value_type.content.integer.bit_count, value_type.content.integer.signed);
|
||||
|
||||
switch (unary_type_id)
|
||||
{
|
||||
.align_of => { value = #extend(get_byte_alignment(unary_type)); },
|
||||
.byte_size => { value = get_byte_size(unary_type); },
|
||||
.integer_max =>
|
||||
{
|
||||
if (unary_type.id != .integer)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
value = integer_max_value(unary_type.content.integer.bit_count, unary_type.content.integer.signed);
|
||||
},
|
||||
.enum_values => { unreachable; },
|
||||
}
|
||||
|
||||
if (value > max_value)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
|
||||
typecheck(module, expected_type, value_type);
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
@ -5299,9 +5805,122 @@ emit_value = fn (module: &Module, value: &Value, type_kind: TypeKind, expect_con
|
||||
llvm_value = LLVMBuildNeg(module.llvm.builder, llvm_unary_value, "");
|
||||
}
|
||||
},
|
||||
.extend =>
|
||||
{
|
||||
assert(resolved_unary_type.id == .integer);
|
||||
if (resolved_unary_type.content.integer.signed)
|
||||
{
|
||||
llvm_value = LLVMBuildSExt(module.llvm.builder, llvm_unary_value, destination_type, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm_value = LLVMBuildZExt(module.llvm.builder, llvm_unary_value, destination_type, "");
|
||||
}
|
||||
},
|
||||
.truncate =>
|
||||
{
|
||||
if (type_kind != .abi)
|
||||
{
|
||||
assert(resolved_value_type.llvm.abi == resolved_value_type.llvm.memory);
|
||||
}
|
||||
|
||||
llvm_value = LLVMBuildTrunc(module.llvm.builder, llvm_unary_value, destination_type, "");
|
||||
},
|
||||
else => { #trap(); },
|
||||
}
|
||||
},
|
||||
.variable =>
|
||||
{
|
||||
>variable = value.content.variable;
|
||||
|
||||
>resolved_variable_value_type = resolve_alias(module, variable.type);
|
||||
>resolved_variable_pointer_type = resolve_alias(module, variable.storage.type);
|
||||
|
||||
>llvm_storage = variable.storage.llvm;
|
||||
|
||||
switch (value.kind)
|
||||
{
|
||||
.left =>
|
||||
{
|
||||
assert(resolved_variable_pointer_type == resolved_value_type);
|
||||
llvm_value = llvm_storage;
|
||||
},
|
||||
.right =>
|
||||
{
|
||||
assert(resolved_variable_value_type == resolved_value_type);
|
||||
|
||||
if (must_be_constant)
|
||||
{
|
||||
if (variable.scope.kind != .global)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
// TODO: field parent pointer
|
||||
#trap();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (get_byte_size(resolved_value_type) <= 16)
|
||||
{
|
||||
>evaluation_kind = get_evaluation_kind(resolved_value_type);
|
||||
switch (evaluation_kind)
|
||||
{
|
||||
.scalar, .aggregate =>
|
||||
{
|
||||
llvm_value = create_load(module, {
|
||||
.type = resolved_value_type,
|
||||
.pointer = llvm_storage,
|
||||
.kind = type_kind,
|
||||
zero,
|
||||
});
|
||||
},
|
||||
.complex => { #trap(); },
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#trap();
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
.unary_type =>
|
||||
{
|
||||
>unary_type_id = value.content.unary_type.id;
|
||||
>unary_type = value.content.unary_type.type;
|
||||
|
||||
resolve_type_in_place(module, unary_type);
|
||||
|
||||
>llvm_result_type = get_llvm_type(resolved_value_type, type_kind);
|
||||
|
||||
switch (unary_type_id)
|
||||
{
|
||||
.align_of =>
|
||||
{
|
||||
assert(resolved_value_type.id == .integer);
|
||||
llvm_value = LLVMConstInt(llvm_result_type, #extend(get_byte_alignment(unary_type)), 0);
|
||||
},
|
||||
.byte_size =>
|
||||
{
|
||||
assert(resolved_value_type.id == .integer);
|
||||
llvm_value = LLVMConstInt(llvm_result_type, get_byte_size(unary_type), 0);
|
||||
},
|
||||
.integer_max =>
|
||||
{
|
||||
assert(resolved_value_type.id == .integer);
|
||||
assert(unary_type.id == .integer);
|
||||
>signed = unary_type.content.integer.signed;
|
||||
>max_value = integer_max_value(unary_type.content.integer.bit_count, signed);
|
||||
llvm_value = LLVMConstInt(llvm_result_type, max_value, #extend(signed));
|
||||
},
|
||||
.enum_values =>
|
||||
{
|
||||
#trap();
|
||||
},
|
||||
}
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
@ -5356,6 +5975,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 +6126,19 @@ 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);
|
||||
|
||||
emit_local(module, local);
|
||||
emit_assignment(module, local.variable.storage.llvm, local.variable.storage.type, local.initial_value);
|
||||
},
|
||||
else =>
|
||||
{
|
||||
#trap();
|
||||
@ -6480,7 +7174,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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
@ -1778,7 +1786,6 @@ fn Value* reference_identifier(Module* module, Scope* current_scope, String iden
|
||||
for (Local* local = block->first_local; local; local = local->next)
|
||||
{
|
||||
assert(!local->next || block->last_local != local);
|
||||
assert((u64)local->next != 0x6e66203d206e6961);
|
||||
if (identifier.equal(local->variable.name))
|
||||
{
|
||||
variable = &local->variable;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
43
tests/field_parent_pointer.bbb
Normal file
43
tests/field_parent_pointer.bbb
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user