This commit is contained in:
David Gonzalez Martin 2025-05-31 17:56:44 -06:00
parent 9b1c5ce7b0
commit a1113f2555
9 changed files with 2408 additions and 846 deletions

File diff suppressed because it is too large Load Diff

View File

@ -435,6 +435,10 @@ global_variable String names[] =
string_literal("opaque"),
string_literal("basic_struct_passing"),
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)

View File

@ -351,13 +351,16 @@ union AbiRegisterCount
AbiRegisterCountSystemV system_v;
};
struct TypeFunction
struct TypeFunctionBase
{
Type* semantic_return_type;
Slice<Type*> semantic_argument_types;
CallingConvention calling_convention;
bool is_variable_arguments;
// ABI
};
struct TypeFunctionAbi
{
Slice<Type*> abi_argument_types;
Type* abi_return_type;
AbiRegisterCount available_registers;
@ -365,6 +368,13 @@ struct TypeFunction
AbiInformation return_abi;
};
struct TypeFunction
{
TypeFunctionBase base;
TypeFunctionAbi abi;
Type* next;
};
struct TypePointer
{
Type* element_type;
@ -629,6 +639,11 @@ fn u64 get_bit_size(Type* type)
case TypeId::integer: return type->integer.bit_count;
case TypeId::enumerator: return get_bit_size(type->enumerator.backing_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();
}
}
@ -793,7 +808,7 @@ struct Block
enum class ValueId
{
infer_or_ignore,
external_function,
forward_declared_function,
function,
constant_integer,
unary,
@ -905,6 +920,8 @@ enum class BinaryId
logical_or,
logical_and_shortcircuit,
logical_or_shortcircuit,
max,
min,
};
struct ValueBinary
@ -1078,6 +1095,7 @@ struct Value
case ValueId::array_expression:
case ValueId::call:
case ValueId::select:
case ValueId::slice_expression:
return false;
case ValueId::variable_reference:
{
@ -1164,7 +1182,11 @@ struct LLVMIntrinsicId
enum class IntrinsicIndex
{
smax,
smin,
trap,
umax,
umin,
va_start,
va_end,
va_copy,
@ -1172,7 +1194,11 @@ enum class IntrinsicIndex
};
global_variable String intrinsic_names[] = {
string_literal("llvm.smax"),
string_literal("llvm.smin"),
string_literal("llvm.trap"),
string_literal("llvm.umax"),
string_literal("llvm.umin"),
string_literal("llvm.va_start"),
string_literal("llvm.va_end"),
string_literal("llvm.va_copy"),
@ -1180,6 +1206,52 @@ global_variable String intrinsic_names[] = {
static_assert(array_length(intrinsic_names) == (u64)IntrinsicIndex::count);
struct LLVMAttributeId
{
u32 n;
};
enum class AttributeIndex
{
align,
alwaysinline,
byval,
dead_on_unwind,
inlinehint,
inreg,
naked,
noalias,
noinline,
noreturn,
nounwind,
signext,
sret,
writable,
zeroext,
count,
};
global_variable String attribute_names[] = {
string_literal("align"),
string_literal("alwaysinline"),
string_literal("byval"),
string_literal("dead_on_unwind"),
string_literal("inlinehint"),
string_literal("inreg"),
string_literal("naked"),
string_literal("noalias"),
string_literal("noinline"),
string_literal("noreturn"),
string_literal("nounwind"),
string_literal("signext"),
string_literal("sret"),
string_literal("writable"),
string_literal("zeroext"),
};
static_assert(array_length(attribute_names) == (u64)AttributeIndex::count);
struct ModuleLLVM
{
LLVMContextRef context;
@ -1193,6 +1265,7 @@ struct ModuleLLVM
LLVMTypeRef pointer_type;
LLVMTypeRef void_type;
LLVMIntrinsicId intrinsic_table[(u64)IntrinsicIndex::count];
LLVMAttributeId attribute_table[(u64)AttributeIndex::count];
LLVMValueRef memcmp;
LLVMMetadataRef inlined_at;
LLVMBasicBlockRef continue_block;
@ -1213,6 +1286,7 @@ struct Module
Type* first_pair_struct_type;
Type* first_array_type;
Type* first_enum_array_type;
Type* first_function_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,
int_from_enum,
int_from_pointer,
max,
min,
pointer_cast,
pointer_from_int,
select,
@ -668,7 +670,7 @@ fn Type* parse_type(Module* module, Scope* scope)
{
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;
} break;
case TypeIntrinsic::count: report_error();
@ -862,6 +864,8 @@ fn Token tokenize(Module* module)
string_literal("integer_max"),
string_literal("int_from_enum"),
string_literal("int_from_pointer"),
string_literal("max"),
string_literal("min"),
string_literal("pointer_cast"),
string_literal("pointer_from_int"),
string_literal("select"),
@ -1241,6 +1245,7 @@ fn Value* parse_aggregate_initialization(Module* module, Scope* scope, ValueBuil
}
auto field_index = field_count;
auto checkpoint = get_checkpoint(module);
if (consume_character_if_match(module, '.'))
{
auto name = parse_identifier(module);
@ -1530,6 +1535,37 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
{
trap();
} 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();
}
} break;
@ -1540,7 +1576,26 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
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);
}
@ -2746,6 +2801,75 @@ fn String parse_name(Module* module)
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)
{
auto scope = &module->scope;
@ -2823,16 +2947,23 @@ void parse(Module* module)
auto global_name = parse_identifier(module);
Global* global_forward_declaration = 0;
Global* last_global = module->first_global;
while (last_global)
{
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;
}
@ -2840,14 +2971,14 @@ void parse(Module* module)
}
Type* type_it = module->scope.types.first;
Type* forward_declaration = 0;
Type* type_forward_declaration = 0;
while (type_it)
{
if (global_name.equal(type_it->name))
{
if (type_it->id == TypeId::forward_declaration)
{
forward_declaration = type_it;
type_forward_declaration = type_it;
break;
}
else
@ -2924,6 +3055,12 @@ void parse(Module* module)
}
auto global_keyword = (GlobalKeyword)i;
if (global_forward_declaration && global_keyword != GlobalKeyword::function)
{
report_error();
}
switch (global_keyword)
{
case GlobalKeyword::bits:
@ -3292,37 +3429,53 @@ void parse(Module* module)
auto is_declaration = consume_character_if_match(module, ';');
auto function_type = type_allocate_init(module, {
.function = {
.semantic_return_type = return_type,
.semantic_argument_types = argument_types,
.calling_convention = calling_convention,
.is_variable_arguments = is_variable_arguments,
},
.id = TypeId::function,
.name = string_literal(""),
.scope = &module->scope,
auto function_type = get_function_type(module, {
.semantic_return_type = return_type,
.semantic_argument_types = argument_types,
.calling_convention = calling_convention,
.is_variable_arguments = is_variable_arguments,
});
auto storage = new_value(module);
*storage = {
.type = get_pointer_type(module, function_type),
.id = ValueId::external_function,
// TODO? .kind = ValueKind::left,
};
auto global = new_global(module);
*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,
};
auto pointer_to_function_type = get_pointer_type(module, function_type);
Global* global = 0;
if (global_forward_declaration)
{
global = global_forward_declaration;
if (global_forward_declaration->variable.type != function_type)
{
report_error();
}
assert(global_forward_declaration->variable.storage->type == pointer_to_function_type);
global->variable.name = global_name;
global->variable.line = global_line;
global->variable.column = global_column;
}
else
{
auto storage = new_value(module);
*storage = {
.type = pointer_to_function_type,
.id = ValueId::forward_declared_function,
// TODO? .kind = ValueKind::left,
};
global = new_global(module);
*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)
{
@ -3340,7 +3493,7 @@ void parse(Module* module)
.storage = 0,
.initial_value = 0,
.type = type,
.scope = &storage->function.scope,
.scope = &global->variable.storage->function.scope,
.name = name,
.line = line,
.column = 0,
@ -3349,7 +3502,7 @@ void parse(Module* module)
};
}
storage->function = {
global->variable.storage->function = {
.arguments = arguments,
.scope = {
.parent = scope,
@ -3360,9 +3513,9 @@ void parse(Module* module)
.block = 0,
.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;
}
} break;
@ -3532,9 +3685,9 @@ void parse(Module* module)
skip_space(module);
Type* struct_type;
if (forward_declaration)
if (type_forward_declaration)
{
struct_type = forward_declaration;
struct_type = type_forward_declaration;
}
else
{
@ -3646,9 +3799,9 @@ void parse(Module* module)
expect_character(module, left_brace);
Type* union_type;
if (forward_declaration)
if (type_forward_declaration)
{
union_type = forward_declaration;
union_type = type_forward_declaration;
}
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;
}