This commit is contained in:
David Gonzalez Martin 2025-05-31 17:56:44 -06:00
parent 9b1c5ce7b0
commit 1395f1926e
9 changed files with 1254 additions and 308 deletions

View File

@ -97,6 +97,19 @@ align_forward = fn (value: u64, alignment: u64) u64
return result; return result;
} }
next_power_of_two = fn (n: u64) u64
{
n -= 1;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
n += 1;
return n;
}
string_no_match = #integer_max(u64); string_no_match = #integer_max(u64);
c_string_length = fn (c_string: &u8) u64 c_string_length = fn (c_string: &u8) u64
@ -551,10 +564,14 @@ LLVMTargetMachineOptions = opaque;
LLVMIntrinsicIndex = enum u32 LLVMIntrinsicIndex = enum u32
{ {
trap, "llvm.smax",
va_start, "llvm.smin",
va_end, "llvm.trap",
va_copy, "llvm.umax",
"llvm.umin",
"llvm.va_start",
"llvm.va_end",
"llvm.va_copy",
} }
CompilerCommand = enum CompilerCommand = enum
@ -658,6 +675,7 @@ Type = struct;
Value = struct; Value = struct;
Local = struct; Local = struct;
Block = struct; Block = struct;
Module = struct;
ScopeKind = enum ScopeKind = enum
{ {
@ -746,7 +764,7 @@ TypeId = enum
TypeInteger = struct TypeInteger = struct
{ {
bit_count: u32, bit_count: u64,
signed: u1, signed: u1,
} }
@ -767,9 +785,67 @@ AbiRegisterCount = union
system_v: AbiRegisterCountSystemV, system_v: AbiRegisterCountSystemV,
}; };
AbiInformationPadding = union
{
type: &Type,
unpadded_coerce_and_expand_type: &Type,
}
AbiInformationDirectAttributes = struct
{
offset: u32,
alignment: u32,
}
AbiInformationIndirectAttributes = struct
{
alignment: u32,
address_space: u32,
}
AbiInformationAttributes = union
{
direct: AbiInformationDirectAttributes,
indirect: AbiInformationIndirectAttributes,
alloca_field_index: u32,
}
AbiKind = enum
{
ignore,
direct,
extend,
indirect,
indirect_aliased,
expand,
coerce_and_expand,
in_alloca,
}
AbiFlags = struct
{
kind: AbiKind,
padding_in_reg: u1,
in_alloca_sret: u1,
in_alloca_indirect: u1,
in_alloca_by_value: u1,
in_alloca_realign: u1,
sret_after_this: u1,
in_reg: u1,
can_be_flattened: u1,
sign_extension: u1,
}
AbiInformation = struct AbiInformation = struct
{ {
foo: u32, semantic_type: &Type,
coerce_to_type: &Type,
padding: AbiInformationPadding,
padding_argument_index: u16,
attributes: AbiInformationAttributes,
flags: AbiFlags,
abi_start: u16,
abi_count: u16,
} }
TypeFunction = struct TypeFunction = struct
@ -802,12 +878,88 @@ Type = struct
scope: &Scope, scope: &Scope,
} }
align_bit_count = fn (bit_count: u64) u64
{
>aligned_bit_count = #max(next_power_of_two(bit_count), 8);
assert(aligned_bit_count % 8 == 0);
return aligned_bit_count;
}
aligned_byte_count_from_bit_count = fn (bit_count: u64) u64
{
>aligned_bit_count = align_bit_count(bit_count);
return aligned_bit_count / 8;
}
get_byte_size = fn (type: &Type) u64
{
switch (type.id)
{
.integer =>
{
>byte_count = aligned_byte_count_from_bit_count(type.content.integer.bit_count);
assert(byte_count == 1 or byte_count == 2 or byte_count == 4 or byte_count == 8 or byte_count == 16);
return byte_count;
},
else =>
{
#trap();
},
}
}
is_integral_or_enumeration_type = fn (type: &Type) u1
{
switch (type.id)
{
.integer, .bits, .enum =>
{
return 1;
},
.array, .struct =>
{
return 0;
},
else =>
{
unreachable;
},
}
}
is_promotable_integer_type_for_abi = fn (type: &Type) u1
{
switch (type.id)
{
.integer =>
{
return type.content.integer.bit_count < 32;
},
else =>
{
#trap();
}
}
}
resolve_type_in_place_abi = fn (module: &Module, type: &Type) void;
resolve_type_in_place_memory = fn (module: &Module, type: &Type) void;
resolve_type_in_place_debug = fn (module: &Module, type: &Type) void;
resolve_type_in_place = fn (module: &Module, type: &Type) void
{
resolve_type_in_place_abi(module, type);
resolve_type_in_place_memory(module, type);
resolve_type_in_place_debug(module, type);
}
ValueId = enum ValueId = enum
{ {
infer_or_ignore, infer_or_ignore,
external_function, external_function,
function, function,
constant_integer, constant_integer,
global,
} }
ValueConstantInteger = struct ValueConstantInteger = struct
@ -1223,7 +1375,7 @@ integer_type = fn (module: &Module, integer: TypeInteger) &Type
{ {
assert(integer.bit_count != 0); assert(integer.bit_count != 0);
assert(integer.bit_count <= 64); 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)); >index = #select(integer.bit_count == 128, i128_offset + #extend(integer.signed), integer.bit_count - 1 + 64 * #extend(integer.signed));
>result = module.scope.types.first + index; >result = module.scope.types.first + index;
assert(result.id == .integer); assert(result.id == .integer);
assert(result.content.integer.bit_count == integer.bit_count); assert(result.content.integer.bit_count == integer.bit_count);
@ -1685,7 +1837,7 @@ parse_type = fn (module: &Module, scope: &Scope) &Type
} }
} }
>result = integer_type(module, { .bit_count = #truncate(bit_count), .signed = is_signed }); >result = integer_type(module, { .bit_count = bit_count, .signed = is_signed });
return result; return result;
} }
else else
@ -2784,6 +2936,278 @@ parse = fn (module: &Module) void
} }
} }
resolve_alias = fn (module: &Module, type: &Type) &Type
{
>result: &Type = zero;
switch (type.id)
{
.void,
.integer,
=>
{
result = type;
},
else =>
{
#trap();
},
}
assert(result != zero);
return result;
}
resolve_type_in_place_abi = fn (module: &Module, type: &Type) void
{
#trap();
}
resolve_type_in_place_memory = fn (module: &Module, type: &Type) void
{
#trap();
}
resolve_type_in_place_debug = fn (module: &Module, type: &Type) void
{
#trap();
}
AbiSystemVClass = enum
{
none,
integer,
sse,
sse_up,
x87,
x87_up,
complex_x87,
memory,
}
AbiSystemVClassifyArgument = struct
{
base_offset: u64,
is_variable_argument: u1,
is_register_call: u1,
}
abi_system_v_classify_type = fn (type: &Type, options: AbiSystemVClassifyArgument) [2]AbiSystemVClass
{
>result: [2]AbiSystemVClass = zero;
>is_memory = options.base_offset >= 8;
>current_index: u64 = #extend(is_memory);
>not_current_index: u64 = #extend(!is_memory);
assert(current_index != not_current_index);
result[current_index] = .memory;
switch (type.id)
{
.void, .noreturn =>
{
result[current_index] = .none;
},
.integer =>
{
>bit_count = type.content.integer.bit_count;
if (bit_count <= 64)
{
result[current_index] = .integer;
}
else if (bit_count == 128)
{
#trap();
}
else
{
report_error();
}
},
else =>
{
#trap();
},
}
return result;
}
contains_no_user_data = fn (type: &Type, start: u64, end: u64) u1
{
>byte_size = get_byte_size(type);
>result = byte_size <= start;
if (!result)
{
switch (type.id)
{
.struct =>
{
result = 1;
#trap();
},
.array, .enum_array =>
{
result = 1;
#trap();
},
else => {},
}
}
return result;
}
abi_system_v_get_integer_type_at_offset = fn (module: &Module, type: &Type, offset: u64, source_type: &Type, source_offset: u64) &Type
{
switch (type.id)
{
.integer =>
{
if (offset == 0)
{
>bit_count = type.content.integer.bit_count;
>start = source_offset + get_byte_size(type);
>end = source_offset + 8;
if (bit_count == 64)
{
return type;
}
if (bit_count == 32 or bit_count == 16 or bit_count == 8)
{
if (contains_no_user_data(source_type, start, end))
{
return type;
}
}
}
},
}
>source_size = get_byte_size(source_type);
assert(source_size != source_offset);
>byte_count = source_size - source_offset;
>bit_count = #select(byte_count > 8, 64, byte_count * 8);
>result = integer_type(module, { .bit_count = bit_count, .signed = 0 });
return result;
}
AbiSystemVDirect = struct
{
semantic_type: &Type,
type: &Type,
padding: &Type,
offset: u32,
alignment: u32,
cannot_be_flattened: u1,
}
abi_system_v_get_direct = fn (module: &Module, direct: AbiSystemVDirect) AbiInformation
{
>result: AbiInformation = {
.semantic_type = direct.semantic_type,
.flags = {
.kind = .direct,
zero,
},
zero,
};
resolve_type_in_place(module, direct.semantic_type);
resolve_type_in_place(module, direct.type);
if (direct.padding)
{
resolve_type_in_place(module, direct.padding);
}
#trap();
}
abi_system_v_classify_return_type = fn (module: &Module, semantic_return_type: &Type) AbiInformation
{
>classes = abi_system_v_classify_type(semantic_return_type, zero);
assert(classes[1] != .memory or classes[0] == .memory);
assert(classes[1] != .sse_up or classes[0] == .sse);
>low_type: &Type = zero;
switch (classes[0])
{
.none =>
{
if (classes[1] == .none)
{
#trap();
}
else
{
report_error();
}
},
.integer =>
{
low_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, 0, semantic_return_type, 0);
if (classes[1] == .none and low_type.id == .integer)
{
if (is_integral_or_enumeration_type(semantic_return_type) and? is_promotable_integer_type_for_abi(semantic_return_type))
{
#trap();
}
}
},
else =>
{
#trap();
},
}
>high_type: &Type = zero;
switch (classes[1])
{
.none => {},
.integer =>
{
>high_offset: u64 = 8;
high_type = abi_system_v_get_integer_type_at_offset(module, semantic_return_type, high_offset, semantic_return_type, high_offset);
if (classes[0] == .none)
{
#trap();
}
},
else =>
{
#trap();
},
}
if (high_type)
{
#trap();
}
>result = abi_system_v_get_direct(module, {
.semantic_type = semantic_return_type,
.type = low_type,
zero,
});
return result;
}
ResolvedCallingConvention = enum
{
system_v,
win64,
}
emit = fn (module: &Module) void emit = fn (module: &Module) void
{ {
assert(!module.current_function); assert(!module.current_function);
@ -2889,6 +3313,79 @@ emit = fn (module: &Module) void
>name = #enum_name(e); >name = #enum_name(e);
module.llvm.intrinsic_table[i] = LLVMLookupIntrinsicID(name.pointer, name.length); module.llvm.intrinsic_table[i] = LLVMLookupIntrinsicID(name.pointer, name.length);
} }
>global = module.first_global;
while (global)
{
assert(!module.current_function);
assert(!module.current_macro_instantiation);
assert(!module.current_macro_declaration);
if (global.emitted)
{
continue;
}
>global_pointer_type = global.variable.storage.type;
assert(global_pointer_type.id == .pointer);
>global_value_type = global_pointer_type.content.pointer.element_type;
switch (global.variable.storage.id)
{
.function, .external_function =>
{
>function_type = &global_value_type.content.function;
>semantic_argument_types = function_type.semantic_argument_types;
>semantic_return_type = function_type.semantic_return_type;
function_type.argument_abis = arena_allocate_slice[AbiInformation](module.arena, semantic_argument_types.length);
>llvm_abi_argument_type_buffer: [64]&LLVMType = undefined;
>resolved_calling_convention: ResolvedCallingConvention = undefined;
switch (function_type.calling_convention)
{
.c =>
{
// TODO: switch on platform
resolved_calling_convention = .system_v;
},
}
>is_reg_call = resolved_calling_convention == .system_v and 0; // TODO: regcall calling convention
switch (resolved_calling_convention)
{
.system_v =>
{
function_type.available_registers = {
.system_v = {
.gpr = #select(is_reg_call, 11, 6),
.sse = #select(is_reg_call, 16, 8),
},
};
function_type.return_abi = abi_system_v_classify_return_type(module, resolve_alias(module, semantic_return_type));
#trap();
},
.win64 =>
{
#trap();
},
}
},
.global =>
{
#trap();
},
else =>
{
report_error();
},
}
global = global.next;
}
#trap();
} }
compile = fn (arena: &Arena, options: CompileOptions) void compile = fn (arena: &Arena, options: CompileOptions) void
@ -2919,7 +3416,7 @@ compile = fn (arena: &Arena, options: CompileOptions) void
type_it.& = { type_it.& = {
.content = { .content = {
.integer = { .integer = {
.bit_count = #truncate(bit_count), .bit_count = bit_count,
.signed = sign, .signed = sign,
}, },
}, },

View File

@ -435,6 +435,10 @@ global_variable String names[] =
string_literal("opaque"), string_literal("opaque"),
string_literal("basic_struct_passing"), string_literal("basic_struct_passing"),
string_literal("enum_arbitrary_abi"), string_literal("enum_arbitrary_abi"),
string_literal("enum_debug_info"),
string_literal("return_array"),
string_literal("bool_pair"),
string_literal("min_max"),
}; };
void entry_point(Slice<char* const> arguments, Slice<char* const> envp) void entry_point(Slice<char* const> arguments, Slice<char* const> envp)

View File

@ -351,13 +351,16 @@ union AbiRegisterCount
AbiRegisterCountSystemV system_v; AbiRegisterCountSystemV system_v;
}; };
struct TypeFunction struct TypeFunctionBase
{ {
Type* semantic_return_type; Type* semantic_return_type;
Slice<Type*> semantic_argument_types; Slice<Type*> semantic_argument_types;
CallingConvention calling_convention; CallingConvention calling_convention;
bool is_variable_arguments; bool is_variable_arguments;
// ABI };
struct TypeFunctionAbi
{
Slice<Type*> abi_argument_types; Slice<Type*> abi_argument_types;
Type* abi_return_type; Type* abi_return_type;
AbiRegisterCount available_registers; AbiRegisterCount available_registers;
@ -365,6 +368,13 @@ struct TypeFunction
AbiInformation return_abi; AbiInformation return_abi;
}; };
struct TypeFunction
{
TypeFunctionBase base;
TypeFunctionAbi abi;
Type* next;
};
struct TypePointer struct TypePointer
{ {
Type* element_type; Type* element_type;
@ -629,6 +639,11 @@ fn u64 get_bit_size(Type* type)
case TypeId::integer: return type->integer.bit_count; case TypeId::integer: return type->integer.bit_count;
case TypeId::enumerator: return get_bit_size(type->enumerator.backing_type); case TypeId::enumerator: return get_bit_size(type->enumerator.backing_type);
case TypeId::alias: return get_bit_size(type->alias.type); case TypeId::alias: return get_bit_size(type->alias.type);
case TypeId::array: return get_byte_size(type->array.element_type) * type->array.element_count * 8;
case TypeId::pointer: return 64;
case TypeId::structure: return type->structure.byte_size * 8;
case TypeId::union_type: return type->union_type.byte_size * 8;
case TypeId::enum_array: return get_byte_size(type->enum_array.element_type) * type->enum_array.enum_type->enumerator.fields.length * 8;
default: trap(); default: trap();
} }
} }
@ -793,7 +808,7 @@ struct Block
enum class ValueId enum class ValueId
{ {
infer_or_ignore, infer_or_ignore,
external_function, forward_declared_function,
function, function,
constant_integer, constant_integer,
unary, unary,
@ -905,6 +920,8 @@ enum class BinaryId
logical_or, logical_or,
logical_and_shortcircuit, logical_and_shortcircuit,
logical_or_shortcircuit, logical_or_shortcircuit,
max,
min,
}; };
struct ValueBinary struct ValueBinary
@ -1078,6 +1095,7 @@ struct Value
case ValueId::array_expression: case ValueId::array_expression:
case ValueId::call: case ValueId::call:
case ValueId::select: case ValueId::select:
case ValueId::slice_expression:
return false; return false;
case ValueId::variable_reference: case ValueId::variable_reference:
{ {
@ -1164,7 +1182,11 @@ struct LLVMIntrinsicId
enum class IntrinsicIndex enum class IntrinsicIndex
{ {
smax,
smin,
trap, trap,
umax,
umin,
va_start, va_start,
va_end, va_end,
va_copy, va_copy,
@ -1172,7 +1194,11 @@ enum class IntrinsicIndex
}; };
global_variable String intrinsic_names[] = { global_variable String intrinsic_names[] = {
string_literal("llvm.smax"),
string_literal("llvm.smin"),
string_literal("llvm.trap"), string_literal("llvm.trap"),
string_literal("llvm.umax"),
string_literal("llvm.umin"),
string_literal("llvm.va_start"), string_literal("llvm.va_start"),
string_literal("llvm.va_end"), string_literal("llvm.va_end"),
string_literal("llvm.va_copy"), string_literal("llvm.va_copy"),
@ -1213,6 +1239,7 @@ struct Module
Type* first_pair_struct_type; Type* first_pair_struct_type;
Type* first_array_type; Type* first_array_type;
Type* first_enum_array_type; Type* first_enum_array_type;
Type* first_function_type;
Type* va_list_type; Type* va_list_type;

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,8 @@ enum class ValueIntrinsic
integer_max, integer_max,
int_from_enum, int_from_enum,
int_from_pointer, int_from_pointer,
max,
min,
pointer_cast, pointer_cast,
pointer_from_int, pointer_from_int,
select, select,
@ -668,7 +670,7 @@ fn Type* parse_type(Module* module, Scope* scope)
{ {
case TypeIntrinsic::return_type: case TypeIntrinsic::return_type:
{ {
auto return_type = module->current_function->variable.type->function.semantic_return_type; auto return_type = module->current_function->variable.type->function.base.semantic_return_type;
return return_type; return return_type;
} break; } break;
case TypeIntrinsic::count: report_error(); case TypeIntrinsic::count: report_error();
@ -862,6 +864,8 @@ fn Token tokenize(Module* module)
string_literal("integer_max"), string_literal("integer_max"),
string_literal("int_from_enum"), string_literal("int_from_enum"),
string_literal("int_from_pointer"), string_literal("int_from_pointer"),
string_literal("max"),
string_literal("min"),
string_literal("pointer_cast"), string_literal("pointer_cast"),
string_literal("pointer_from_int"), string_literal("pointer_from_int"),
string_literal("select"), string_literal("select"),
@ -1241,6 +1245,7 @@ fn Value* parse_aggregate_initialization(Module* module, Scope* scope, ValueBuil
} }
auto field_index = field_count; auto field_index = field_count;
auto checkpoint = get_checkpoint(module);
if (consume_character_if_match(module, '.')) if (consume_character_if_match(module, '.'))
{ {
auto name = parse_identifier(module); auto name = parse_identifier(module);
@ -1530,6 +1535,37 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
{ {
trap(); trap();
} break; } break;
case ValueIntrinsic::min:
case ValueIntrinsic::max:
{
skip_space(module);
expect_character(module, left_parenthesis);
skip_space(module);
auto left = parse_value(module, scope, {});
skip_space(module);
expect_character(module, ',');
skip_space(module);
auto right = parse_value(module, scope, {});
skip_space(module);
expect_character(module, right_parenthesis);
BinaryId binary_id;
switch (intrinsic)
{
case ValueIntrinsic::max: binary_id = BinaryId::max; break;
case ValueIntrinsic::min: binary_id = BinaryId::min; break;
default: unreachable();
}
*result = {
.binary = {
.left = left,
.right = right,
.id = binary_id,
},
.id = ValueId::binary,
};
} break;
case ValueIntrinsic::count: unreachable(); case ValueIntrinsic::count: unreachable();
} }
} break; } break;
@ -1540,7 +1576,26 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
skip_space(module); skip_space(module);
if (module->content[module->offset] == '.') auto checkpoint = get_checkpoint(module);
bool is_aggregate_initialization = false;
if (consume_character_if_match(module, '.'))
{
auto identifier = parse_identifier(module);
skip_space(module);
is_aggregate_initialization = consume_character_if_match(module, '=');
if (!is_aggregate_initialization)
{
if (!consume_character_if_match(module, ','))
{
report_error();
}
}
}
set_checkpoint(module, checkpoint);
if (is_aggregate_initialization)
{ {
result = parse_aggregate_initialization(module, scope, builder, right_bracket); result = parse_aggregate_initialization(module, scope, builder, right_bracket);
} }
@ -2746,6 +2801,75 @@ fn String parse_name(Module* module)
return result; return result;
} }
fn bool type_function_base_compare(TypeFunctionBase& a, TypeFunctionBase& b)
{
auto same_return_type = a.semantic_return_type == b.semantic_return_type;
auto same_calling_convention = a.calling_convention == b.calling_convention;
auto same_is_variable_arguments = a.is_variable_arguments == b.is_variable_arguments;
auto same_argument_length = a.semantic_argument_types.length == b.semantic_argument_types.length;
auto same_argument_types = same_argument_length;
if (same_argument_length)
{
for (u64 i = 0; i < a.semantic_argument_types.length; i += 1)
{
auto a_type = a.semantic_argument_types[i];
auto b_type = b.semantic_argument_types[i];
auto is_same_argument_type = a_type == b_type;
same_argument_types = same_argument_types && is_same_argument_type;
}
}
return same_return_type && same_calling_convention && same_is_variable_arguments && same_argument_types;
}
fn Type* get_function_type(Module* module, TypeFunctionBase base)
{
Type* last_function_type = module->first_function_type;
while (last_function_type)
{
assert(last_function_type->id == TypeId::function);
if (type_function_base_compare(base, last_function_type->function.base))
{
return last_function_type;
}
auto next = last_function_type->function.next;
if (!next)
{
break;
}
last_function_type = next;
}
auto result = type_allocate_init(module, Type{
.function = {
.base = base,
},
.id = TypeId::function,
.name = string_literal(""),
.scope = &module->scope,
});
if (last_function_type)
{
assert(module->first_function_type);
last_function_type->function.next = result;
}
else
{
assert(!module->first_function_type);
module->first_function_type = result;
}
return result;
}
void parse(Module* module) void parse(Module* module)
{ {
auto scope = &module->scope; auto scope = &module->scope;
@ -2823,16 +2947,23 @@ void parse(Module* module)
auto global_name = parse_identifier(module); auto global_name = parse_identifier(module);
Global* global_forward_declaration = 0;
Global* last_global = module->first_global; Global* last_global = module->first_global;
while (last_global) while (last_global)
{ {
if (global_name.equal(last_global->variable.name)) if (global_name.equal(last_global->variable.name))
{ {
report_error(); global_forward_declaration = last_global;
} if (last_global->variable.storage->id != ValueId::forward_declared_function)
{
report_error();
}
if (last_global->linkage == Linkage::external)
{
report_error();
}
if (!last_global->next)
{
break; break;
} }
@ -2840,14 +2971,14 @@ void parse(Module* module)
} }
Type* type_it = module->scope.types.first; Type* type_it = module->scope.types.first;
Type* forward_declaration = 0; Type* type_forward_declaration = 0;
while (type_it) while (type_it)
{ {
if (global_name.equal(type_it->name)) if (global_name.equal(type_it->name))
{ {
if (type_it->id == TypeId::forward_declaration) if (type_it->id == TypeId::forward_declaration)
{ {
forward_declaration = type_it; type_forward_declaration = type_it;
break; break;
} }
else else
@ -2924,6 +3055,12 @@ void parse(Module* module)
} }
auto global_keyword = (GlobalKeyword)i; auto global_keyword = (GlobalKeyword)i;
if (global_forward_declaration && global_keyword != GlobalKeyword::function)
{
report_error();
}
switch (global_keyword) switch (global_keyword)
{ {
case GlobalKeyword::bits: case GlobalKeyword::bits:
@ -3292,37 +3429,43 @@ void parse(Module* module)
auto is_declaration = consume_character_if_match(module, ';'); auto is_declaration = consume_character_if_match(module, ';');
auto function_type = type_allocate_init(module, { auto function_type = get_function_type(module, {
.function = { .semantic_return_type = return_type,
.semantic_return_type = return_type, .semantic_argument_types = argument_types,
.semantic_argument_types = argument_types, .calling_convention = calling_convention,
.calling_convention = calling_convention, .is_variable_arguments = is_variable_arguments,
.is_variable_arguments = is_variable_arguments,
},
.id = TypeId::function,
.name = string_literal(""),
.scope = &module->scope,
}); });
auto storage = new_value(module); auto pointer_to_function_type = get_pointer_type(module, function_type);
*storage = {
.type = get_pointer_type(module, function_type), Global* global = 0;
.id = ValueId::external_function, if (global_forward_declaration)
// TODO? .kind = ValueKind::left, {
}; trap();
auto global = new_global(module); }
*global = { else
.variable = { {
.storage = storage, auto storage = new_value(module);
.initial_value = 0, *storage = {
.type = function_type, .type = pointer_to_function_type,
.scope = scope, .id = ValueId::forward_declared_function,
.name = global_name, // TODO? .kind = ValueKind::left,
.line = global_line, };
.column = global_column,
}, global = new_global(module);
.linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal, *global = {
}; .variable = {
.storage = storage,
.initial_value = 0,
.type = function_type,
.scope = scope,
.name = global_name,
.line = global_line,
.column = global_column,
},
.linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal,
};
}
if (!is_declaration) if (!is_declaration)
{ {
@ -3340,7 +3483,7 @@ void parse(Module* module)
.storage = 0, .storage = 0,
.initial_value = 0, .initial_value = 0,
.type = type, .type = type,
.scope = &storage->function.scope, .scope = &global->variable.storage->function.scope,
.name = name, .name = name,
.line = line, .line = line,
.column = 0, .column = 0,
@ -3349,7 +3492,7 @@ void parse(Module* module)
}; };
} }
storage->function = { global->variable.storage->function = {
.arguments = arguments, .arguments = arguments,
.scope = { .scope = {
.parent = scope, .parent = scope,
@ -3360,9 +3503,9 @@ void parse(Module* module)
.block = 0, .block = 0,
.attributes = function_attributes, .attributes = function_attributes,
}; };
storage->id = ValueId::function; global->variable.storage->id = ValueId::function;
storage->function.block = parse_block(module, &storage->function.scope); global->variable.storage->function.block = parse_block(module, &global->variable.storage->function.scope);
module->current_function = 0; module->current_function = 0;
} }
} break; } break;
@ -3532,9 +3675,9 @@ void parse(Module* module)
skip_space(module); skip_space(module);
Type* struct_type; Type* struct_type;
if (forward_declaration) if (type_forward_declaration)
{ {
struct_type = forward_declaration; struct_type = type_forward_declaration;
} }
else else
{ {
@ -3646,9 +3789,9 @@ void parse(Module* module)
expect_character(module, left_brace); expect_character(module, left_brace);
Type* union_type; Type* union_type;
if (forward_declaration) if (type_forward_declaration)
{ {
union_type = forward_declaration; union_type = type_forward_declaration;
} }
else else
{ {

18
tests/bool_pair.bbb Normal file
View File

@ -0,0 +1,18 @@
BoolPair = struct
{
a: u1,
b: u1,
}
bool_pair = fn () BoolPair
{
return { .a = 0, .b = 1 };
}
[export] main = fn [cc(c)] () s32
{
>result = bool_pair();
if (result.a) #trap();
if (!result.b) #trap();
return 0;
}

38
tests/enum_debug_info.bbb Normal file
View File

@ -0,0 +1,38 @@
TypeId = enum
{
void,
noreturn,
forward_declaration,
integer,
function,
pointer,
array,
enum,
struct,
bits,
alias,
union,
unresolved,
vector,
floating_point,
enum_array,
opaque,
}
Type = struct
{
arr: [5]u32,
id: TypeId,
a: [2]u64,
b: u64,
}
[export] main = fn [cc(c)] () s32
{
>t: Type = {
.id = .integer,
zero,
};
t.arr[0] = 1;
return 0;
}

10
tests/min_max.bbb Normal file
View File

@ -0,0 +1,10 @@
[export] main = fn [cc(c)] () s32
{
>a: u32 = 1;
>b: u32 = 2;
>min = #min(a, b);
>max = #max(a, b);
if (min != a) #trap();
if (max != b) #trap();
return 0;
}

22
tests/return_array.bbb Normal file
View File

@ -0,0 +1,22 @@
SomeEnum = enum
{
a,
b,
c,
d,
e,
f,
}
foo = fn () [2]SomeEnum
{
return [ .f, .e ];
}
[export] main = fn [cc(c)] () s32
{
>result = foo();
if (result[0] != .f) #trap();
if (result[1] != .e) #trap();
return 0;
}