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

View File

@ -97,6 +97,19 @@ align_forward = fn (value: u64, alignment: u64) u64
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);
c_string_length = fn (c_string: &u8) u64
@ -551,10 +564,14 @@ LLVMTargetMachineOptions = opaque;
LLVMIntrinsicIndex = enum u32
{
trap,
va_start,
va_end,
va_copy,
"llvm.smax",
"llvm.smin",
"llvm.trap",
"llvm.umax",
"llvm.umin",
"llvm.va_start",
"llvm.va_end",
"llvm.va_copy",
}
CompilerCommand = enum
@ -658,6 +675,7 @@ Type = struct;
Value = struct;
Local = struct;
Block = struct;
Module = struct;
ScopeKind = enum
{
@ -746,7 +764,7 @@ TypeId = enum
TypeInteger = struct
{
bit_count: u32,
bit_count: u64,
signed: u1,
}
@ -767,9 +785,67 @@ AbiRegisterCount = union
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
{
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
@ -802,12 +878,88 @@ Type = struct
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
{
infer_or_ignore,
external_function,
function,
constant_integer,
global,
}
ValueConstantInteger = struct
@ -1223,7 +1375,7 @@ integer_type = fn (module: &Module, integer: TypeInteger) &Type
{
assert(integer.bit_count != 0);
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;
assert(result.id == .integer);
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;
}
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
{
assert(!module.current_function);
@ -2889,6 +3313,79 @@ emit = fn (module: &Module) void
>name = #enum_name(e);
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
@ -2919,7 +3416,7 @@ compile = fn (arena: &Arena, options: CompileOptions) void
type_it.& = {
.content = {
.integer = {
.bit_count = #truncate(bit_count),
.bit_count = bit_count,
.signed = sign,
},
},

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,12 @@ struct TypeFunction
AbiInformation return_abi;
};
struct TypeFunction
{
TypeFunctionBase base;
TypeFunctionAbi abi;
};
struct TypePointer
{
Type* element_type;
@ -629,6 +638,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 +807,7 @@ struct Block
enum class ValueId
{
infer_or_ignore,
external_function,
forward_declared_function,
function,
constant_integer,
unary,
@ -905,6 +919,8 @@ enum class BinaryId
logical_or,
logical_and_shortcircuit,
logical_or_shortcircuit,
max,
min,
};
struct ValueBinary
@ -1078,6 +1094,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 +1181,11 @@ struct LLVMIntrinsicId
enum class IntrinsicIndex
{
smax,
smin,
trap,
umax,
umin,
va_start,
va_end,
va_copy,
@ -1172,7 +1193,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"),
@ -1213,6 +1238,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,33 @@ fn String parse_name(Module* module)
return result;
}
struct FunctionTypeCreate
{
Type* semantic_return_type;
Slice<Type*> semantic_argument_types;
CallingConvention calling_convention;
bool is_variable_arguments;
};
fn Type* get_function_type(Module* module, FunctionTypeCreate create)
{
Type* last_function_type = module->first_function_type;
while (last_function_type)
{
trap();
if (!last_function_type->next)
{
break;
}
last_function_type = last_function_type->next;
}
trap();
}
void parse(Module* module)
{
auto scope = &module->scope;
@ -2823,16 +2905,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 +2929,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 +3013,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 +3387,48 @@ 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,
});
Global* global = 0;
if (global_forward_declaration)
{
trap();
}
else
{
auto function_type = type_allocate_init(module, {
.function = {
.base = {
.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 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 storage = new_value(module);
*storage = {
.type = get_pointer_type(module, 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 +3446,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 +3455,7 @@ void parse(Module* module)
};
}
storage->function = {
global->variable.storage->function = {
.arguments = arguments,
.scope = {
.parent = scope,
@ -3360,9 +3466,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 +3638,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 +3752,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;
}