bloat-buster/src/compiler.h
2025-05-15 20:07:46 -06:00

1460 lines
29 KiB
C++

#pragma once
#include <lib.h>
#include <llvm-c/Types.h>
#define report_error() trap()
enum class Command
{
compile,
test,
count,
};
enum class BuildMode
{
debug_none,
debug,
soft_optimize,
optimize_for_speed,
optimize_for_size,
aggressively_optimize_for_speed,
aggressively_optimize_for_size,
count,
};
fn String build_mode_to_string(BuildMode build_mode)
{
switch (build_mode)
{
case_to_name(BuildMode, debug_none);
case_to_name(BuildMode, debug);
case_to_name(BuildMode, soft_optimize);
case_to_name(BuildMode, optimize_for_speed);
case_to_name(BuildMode, optimize_for_size);
case_to_name(BuildMode, aggressively_optimize_for_speed);
case_to_name(BuildMode, aggressively_optimize_for_size);
case BuildMode::count: unreachable();
}
}
fn bool build_mode_is_optimized(BuildMode build_mode)
{
switch (build_mode)
{
case BuildMode::debug_none:
case BuildMode::debug:
return false;
case BuildMode::soft_optimize:
case BuildMode::optimize_for_speed:
case BuildMode::optimize_for_size:
case BuildMode::aggressively_optimize_for_speed:
case BuildMode::aggressively_optimize_for_size:
return true;
case BuildMode::count: unreachable();
}
}
enum class ValueKind
{
right,
left,
};
enum class CPUArchitecture
{
x86_64,
};
enum class OperatingSystem
{
linux_,
};
struct Type;
struct Value;
struct Local;
struct Global;
struct Block;
struct Statement;
struct Variable;
struct Argument;
struct Scope;
struct MacroDeclaration;
struct DirectAttributes
{
u32 offset;
u32 alignment;
};
struct IndirectAttributes
{
u32 alignment;
u32 address_space;
};
enum class AbiKind : u8
{
ignore,
direct,
extend,
indirect,
indirect_aliased,
expand,
coerce_and_expand,
in_alloca,
};
struct AbiFlags
{
AbiKind kind;
bool padding_in_reg;
bool in_alloca_sret;
bool in_alloca_indirect;
bool indirect_by_value;
bool indirect_realign;
bool sret_after_this;
bool in_reg;
bool can_be_flattened;
bool sign_extension;
};
struct AbiInformation
{
Type* semantic_type;
Type* coerce_to_type;
union
{
Type* type;
Type* unpadded_coerce_and_expand_type;
} padding;
u16 padding_argument_index;
union
{
DirectAttributes direct;
IndirectAttributes indirect;
u32 alloca_field_index;
} attributes;
AbiFlags flags;
u16 abi_start;
u16 abi_count;
inline void set_sret_after_this(bool sret_after_this)
{
assert(flags.kind == AbiKind::indirect);
flags.sret_after_this = sret_after_this;
}
inline void set_indirect_realign(bool realign)
{
assert(flags.kind == AbiKind::indirect);
flags.indirect_realign = realign;
}
inline void set_indirect_by_value(bool by_value)
{
assert(flags.kind == AbiKind::indirect);
flags.indirect_by_value = by_value;
}
inline void set_indirect_align(u32 alignment)
{
assert(flags.kind == AbiKind::indirect || flags.kind == AbiKind::indirect_aliased);
attributes.indirect.alignment = alignment;
}
inline bool can_have_coerce_to_type()
{
switch (flags.kind)
{
case AbiKind::direct:
case AbiKind::extend:
case AbiKind::coerce_and_expand:
return true;
default:
return false;
}
}
inline void set_coerce_to_type(Type* coerce_to_type)
{
assert(can_have_coerce_to_type());
this->coerce_to_type = coerce_to_type;
}
inline Type* get_coerce_to_type()
{
assert(can_have_coerce_to_type());
return coerce_to_type;
}
inline void set_padding_type(Type* padding_type)
{
assert(can_have_padding_type());
padding = {
.type = padding_type,
};
}
inline bool can_have_padding_type()
{
switch (flags.kind)
{
case AbiKind::direct:
case AbiKind::extend:
case AbiKind::indirect:
case AbiKind::indirect_aliased:
case AbiKind::expand:
return true;
default:
return false;
}
}
inline Type* get_padding_type()
{
return can_have_padding_type() ? padding.type : 0;
}
inline void set_direct_offset(u32 offset)
{
assert(flags.kind == AbiKind::direct || flags.kind == AbiKind::extend);
attributes.direct.offset = offset;
}
inline void set_direct_alignment(u32 alignment)
{
assert(flags.kind == AbiKind::direct || flags.kind == AbiKind::extend);
attributes.direct.alignment = alignment;
}
inline void set_can_be_flattened(bool can_be_flattened)
{
assert(flags.kind == AbiKind::direct);
flags.can_be_flattened = can_be_flattened;
}
inline bool get_can_be_flattened()
{
return flags.can_be_flattened;
}
};
struct Target
{
CPUArchitecture cpu;
OperatingSystem os;
};
struct Compile
{
String relative_file_path;
BuildMode build_mode;
bool has_debug_info;
bool silent;
};
#define base_cache_dir "bb-cache"
enum class CallingConvention
{
c,
count,
};
enum class ResolvedCallingConvention
{
system_v,
win64,
count,
};
fn ResolvedCallingConvention resolve_calling_convention(CallingConvention cc)
{
switch (cc)
{
case CallingConvention::c:
// TODO:
return ResolvedCallingConvention::system_v;
case CallingConvention::count: unreachable();
}
}
enum class InlineBehavior
{
normal,
always_inline,
no_inline,
inline_hint,
};
struct FunctionAttributes
{
InlineBehavior inline_behavior;
bool naked;
};
enum class TypeId
{
void_type,
noreturn,
forward_declaration,
integer,
function,
pointer,
array,
enumerator,
structure,
bits,
alias,
union_type,
unresolved,
vector,
floating_point,
};
struct TypeInteger
{
u32 bit_count;
bool is_signed;
};
struct AbiRegisterCountSystemV
{
u32 gpr;
u32 sse;
};
union AbiRegisterCount
{
AbiRegisterCountSystemV system_v;
};
struct TypeFunction
{
Type* semantic_return_type;
Slice<Type*> semantic_argument_types;
CallingConvention calling_convention;
bool is_variable_arguments;
// ABI
Slice<Type*> abi_argument_types;
Type* abi_return_type;
AbiRegisterCount available_registers;
Slice<AbiInformation> argument_abis;
AbiInformation return_abi;
};
struct TypePointer
{
Type* element_type;
Type* next;
};
struct TypeArray
{
Type* element_type;
u64 element_count;
Type* next;
};
struct UnresolvedEnumField
{
String name;
Value* value;
};
struct EnumField
{
String name;
u64 value;
};
struct UnresolvedTypeEnum
{
Slice<UnresolvedEnumField> fields;
Type* backing_type;
u32 line;
bool implicit_backing_type;
Type* resolved_type;
};
struct TypeEnum
{
Slice<EnumField> fields;
Type* backing_type;
LLVMValueRef enum_to_string_function;
LLVMValueRef string_to_enum_function;
Type* string_to_enum_struct_type;
Global* name_array;
u32 line;
};
struct Field
{
String name;
Type* type;
u64 offset;
u32 line;
};
struct TypeStruct
{
Slice<Field> fields;
u64 byte_size;
u32 byte_alignment;
u32 line;
bool is_slice;
Type* next;
};
struct TypeBits
{
Slice<Field> fields;
Type* backing_type;
u32 line;
bool is_implicit_backing_type;
};
struct TypeAlias
{
Type* type;
Scope* scope;
u32 line;
};
struct UnionField
{
Type* type;
String name;
u32 line;
};
struct TypeUnion
{
Slice<UnionField> fields;
u64 byte_size;
u32 byte_alignment;
u32 line;
u32 biggest_field;
};
struct LLVMType
{
LLVMTypeRef abi;
LLVMTypeRef memory;
LLVMMetadataRef debug;
};
struct Type
{
union
{
TypeInteger integer;
TypeFunction function;
TypePointer pointer;
TypeArray array;
TypeEnum enumerator;
TypeStruct structure;
TypeBits bits;
TypeAlias alias;
TypeUnion union_type;
};
TypeId id;
String name;
Type* next;
LLVMType llvm;
};
fn u32 align_bit_count(u32 bit_count)
{
auto aligned_bit_count = MAX(8, next_power_of_two(bit_count));
assert(aligned_bit_count % 8 == 0);
return aligned_bit_count;
}
fn u32 aligned_byte_count_from_bit_count(u32 bit_count)
{
auto aligned_bit_count = align_bit_count(bit_count);
return aligned_bit_count / 8;
}
fn u64 get_byte_size(Type* type)
{
switch (type->id)
{
case TypeId::integer:
{
auto byte_count = aligned_byte_count_from_bit_count(type->integer.bit_count);
assert(byte_count == 1 || byte_count == 2 || byte_count == 4 || byte_count == 8 || byte_count == 16);
return byte_count;
} break;
case TypeId::pointer:
{
return 8;
} break;
case TypeId::array:
{
auto element_type = type->array.element_type;
auto element_size = get_byte_size(element_type);
auto element_count = type->array.element_count;
auto result = element_size * element_count;
return result;
} break;
case TypeId::structure:
{
auto result = type->structure.byte_size;
return result;
} break;
case TypeId::enumerator:
{
auto result = get_byte_size(type->enumerator.backing_type);
return result;
} break;
case TypeId::bits:
{
auto result = get_byte_size(type->bits.backing_type);
return result;
} break;
default: trap();
}
}
fn u32 get_byte_alignment(Type* type)
{
switch (type->id)
{
case TypeId::integer:
{
auto aligned_byte_count = aligned_byte_count_from_bit_count(type->integer.bit_count);
assert(aligned_byte_count == 1 || aligned_byte_count == 2 || aligned_byte_count == 4 || aligned_byte_count == 8 || aligned_byte_count == 16);
return aligned_byte_count;
} break;
case TypeId::array:
{
auto element_type = type->array.element_type;
auto result = get_byte_alignment(element_type);
return result;
} break;
case TypeId::structure:
{
auto result = type->structure.byte_alignment;
return result;
} break;
case TypeId::enumerator:
{
auto result = get_byte_alignment(type->enumerator.backing_type);
return result;
} break;
case TypeId::pointer:
{
return 8;
} break;
case TypeId::bits:
{
auto result = get_byte_alignment(type->bits.backing_type);
return result;
} break;
case TypeId::union_type:
{
return type->union_type.byte_alignment;
} break;
default: trap();
}
}
fn u64 get_bit_size(Type* type)
{
switch (type->id)
{
case TypeId::integer: return type->integer.bit_count;
case TypeId::enumerator: return get_bit_size(type->enumerator.backing_type);
default: trap();
}
}
enum class ScopeKind
{
global,
function,
local,
for_each,
macro_declaration,
macro_instantiation,
};
struct Scope
{
Scope* parent;
u32 line;
u32 column;
ScopeKind kind;
LLVMMetadataRef llvm;
};
enum class StatementId
{
local,
expression,
return_st,
assignment,
if_st,
block,
while_st,
switch_st,
for_each,
break_st,
continue_st,
};
enum class StatementAssignmentId
{
assign,
assign_add,
assign_sub,
assign_mul,
assign_div,
assign_rem,
assign_shift_left,
assign_shift_right,
assign_and,
assign_or,
assign_xor,
};
struct StatementAssignment
{
Value* left;
Value* right;
StatementAssignmentId id;
};
struct StatementIf
{
Value* condition;
Statement* if_statement;
Statement* else_statement;
};
struct StatementWhile
{
Value* condition;
Block* block;
};
struct StatementSwitchClause
{
Slice<Value*> values;
Block* block;
LLVMBasicBlockRef basic_block;
};
struct StatementSwitch
{
Value* discriminant;
Slice<StatementSwitchClause> clauses;
};
enum class ForEachKind
{
slice,
range,
};
struct StatementForEach
{
Local* first_local;
Local* last_local;
Slice<ValueKind> left_values;
Slice<Value*> right_values;
Statement* predicate;
Scope scope;
ForEachKind kind;
};
struct Statement
{
union
{
Local* local;
Value* expression;
Value* return_st;
StatementAssignment assignment;
StatementIf if_st;
Block* block;
StatementWhile while_st;
StatementSwitch switch_st;
StatementForEach for_each;
};
Statement* next;
StatementId id;
u32 line;
u32 column;
};
struct Block
{
Local* first_local;
Local* last_local;
Statement* first_statement;
Scope scope;
};
enum class ValueId
{
infer_or_ignore,
external_function,
function,
constant_integer,
unary,
binary,
unary_type,
variable_reference,
macro_reference,
macro_instantiation,
call,
global,
array_initialization,
array_expression,
slice_expression,
enum_literal,
trap,
field_access,
string_literal,
va_start,
va_arg,
aggregate_initialization,
undefined,
unreachable,
zero,
select,
string_to_enum,
local,
argument,
};
struct ValueConstantInteger
{
u64 value;
bool is_signed;
};
struct FunctionLLVM
{
LLVMBasicBlockRef return_block;
LLVMValueRef return_alloca;
};
struct ValueFunction
{
Slice<Argument> arguments;
Scope scope;
Block* block;
FunctionAttributes attributes;
FunctionLLVM llvm;
};
enum class UnaryId
{
minus,
plus,
ampersand,
exclamation,
tilde,
enum_name,
extend,
truncate,
pointer_cast,
int_from_enum,
int_from_pointer,
va_end,
bitwise_not,
dereference,
};
struct ValueUnary
{
Value* value;
UnaryId id;
};
enum class UnaryTypeId
{
byte_size,
integer_max,
};
struct ValueUnaryType
{
Type* type;
UnaryTypeId id;
};
enum class BinaryId
{
add,
sub,
mul,
div,
rem,
bitwise_and,
bitwise_or,
bitwise_xor,
shift_left,
shift_right,
compare_equal,
compare_not_equal,
compare_greater,
compare_less,
compare_greater_equal,
compare_less_equal,
logical_and,
logical_or,
logical_and_shortcircuit,
logical_or_shortcircuit,
};
struct ValueBinary
{
Value* left;
Value* right;
BinaryId id;
};
struct ValueCall
{
Value* callable;
Slice<Value*> arguments;
Type* function_type;
};
struct ValueArrayInitialization
{
Slice<Value*> values;
bool is_constant;
};
struct ValueArrayExpression
{
Value* array_like;
Value* index;
};
struct ValueFieldAccess
{
Value* aggregate;
String field_name;
};
struct ValueSliceExpression
{
Value* array_like;
Value* start;
Value* end;
};
struct ValueVaArg
{
Value* va_list;
Type* type;
};
struct ValueAggregateInitialization
{
Slice<String> names;
Slice<Value*> values;
bool is_constant;
bool zero;
};
struct ValueSelect
{
Value* condition;
Value* true_value;
Value* false_value;
};
struct ValueStringToEnum
{
Type* type;
Value* string;
};
enum class ConstantArgumentId
{
value,
type,
};
struct ConstantArgument
{
String name;
union
{
Type* type;
Value* value;
};
ConstantArgumentId id;
};
struct MacroDeclaration
{
Slice<Argument> arguments;
Slice<ConstantArgument> constant_arguments;
Type* return_type;
Block* block;
String name;
Scope scope;
MacroDeclaration* next;
bool is_generic()
{
return constant_arguments.length != 0;
}
};
struct MacroInstantiation
{
MacroDeclaration* declaration;
Global* instantiation_function;
Slice<Argument> declaration_arguments;
Slice<Value*> instantiation_arguments;
Slice<ConstantArgument> constant_arguments;
Type* return_type;
Block* block;
Scope scope;
u32 line;
u32 column;
};
fn bool variable_is_constant(Value* value);
struct Value
{
union
{
ValueConstantInteger constant_integer;
ValueFunction function;
ValueUnary unary;
ValueBinary binary;
Variable* variable_reference;
ValueUnaryType unary_type;
ValueCall call;
ValueArrayInitialization array_initialization;
ValueArrayExpression array_expression;
String enum_literal;
ValueFieldAccess field_access;
ValueSliceExpression slice_expression;
String string_literal;
ValueVaArg va_arg;
ValueAggregateInitialization aggregate_initialization;
ValueSelect select;
ValueStringToEnum string_to_enum;
MacroDeclaration* macro_reference;
MacroInstantiation macro_instantiation;
};
Type* type;
ValueId id;
ValueKind kind;
LLVMValueRef llvm;
bool is_constant()
{
switch (id)
{
case ValueId::constant_integer:
case ValueId::enum_literal:
return true;
case ValueId::unary:
case ValueId::binary:
case ValueId::field_access:
case ValueId::array_expression:
case ValueId::call:
return false;
case ValueId::variable_reference:
{
return variable_is_constant(this);
} break;
case ValueId::array_initialization:
{
assert(type); // This asserts that the value type has been analyzed and `is_constant` was properly set
return array_initialization.is_constant;
} break;
case ValueId::aggregate_initialization:
{
assert(type); // This asserts that the value type has been analyzed and `is_constant` was properly set
return aggregate_initialization.is_constant;
} break;
default: trap();
}
}
};
struct Variable
{
Value* storage;
Value* initial_value;
Type* type;
Scope* scope;
String name;
u32 line;
u32 column;
};
fn bool variable_is_constant(Value* value)
{
assert(value->id == ValueId::variable_reference);
auto* variable = value->variable_reference;
switch (value->kind)
{
case ValueKind::left:
{
switch (variable->scope->kind)
{
case ScopeKind::global:
return true;
default:
return false;
}
} break;
case ValueKind::right:
return false;
}
}
enum class Linkage
{
internal,
external,
};
struct Global
{
Variable variable;
Linkage linkage;
bool emitted;
Global* next;
};
struct Local
{
Variable variable;
Local* next;
};
struct Argument
{
Variable variable;
u32 index;
};
struct LLVMIntrinsicId
{
u32 n;
};
enum class IntrinsicIndex
{
trap,
va_start,
va_end,
va_copy,
count,
};
global_variable String intrinsic_names[] = {
string_literal("llvm.trap"),
string_literal("llvm.va_start"),
string_literal("llvm.va_end"),
string_literal("llvm.va_copy"),
};
static_assert(array_length(intrinsic_names) == (u64)IntrinsicIndex::count);
struct ModuleLLVM
{
LLVMContextRef context;
LLVMModuleRef module;
LLVMBuilderRef builder;
LLVMDIBuilderRef di_builder;
LLVMMetadataRef file;
LLVMMetadataRef compile_unit;
LLVMTypeRef pointer_type;
LLVMTypeRef void_type;
LLVMIntrinsicId intrinsic_table[(u64)IntrinsicIndex::count];
LLVMValueRef memcmp;
LLVMMetadataRef inlined_at;
LLVMBasicBlockRef continue_block;
LLVMBasicBlockRef exit_block;
u32 debug_tag;
};
struct Module
{
Arena* arena;
String content;
u64 offset;
u64 line_offset;
u64 line_character_offset;
Type* first_pointer_type;
Type* first_slice_type;
Type* first_pair_struct_type;
Type* first_array_type;
Type* first_type;
Type* last_type;
Type* va_list_type;
Value* void_value;
Global* first_global;
Global* last_global;
Global* current_function;
MacroDeclaration* first_macro_declaration;
MacroDeclaration* last_macro_declaration;
MacroDeclaration* current_macro_declaration;
MacroInstantiation* current_macro_instantiation;
ModuleLLVM llvm;
Scope scope;
String name;
String path;
String executable;
Slice<String>objects;
Target target;
BuildMode build_mode;
bool has_debug_info;
bool silent;
};
constexpr u64 i128_offset = 64 * 2;
constexpr u64 void_offset = i128_offset + 2;
fn Type* integer_type(Module* module, TypeInteger integer)
{
assert(integer.bit_count);
assert(integer.bit_count <= 64 || integer.bit_count == 128);
if (integer.is_signed)
{
assert(integer.bit_count > 1);
}
auto index = integer.bit_count == 128 ? (i128_offset + integer.is_signed) : (integer.bit_count - 1 + (64 * integer.is_signed));
auto* result_type = module->first_type + index;
assert(result_type->id == TypeId::integer);
assert(result_type->integer.bit_count == integer.bit_count);
assert(result_type->integer.is_signed == integer.is_signed);
return result_type;
}
fn Type* void_type(Module* module)
{
return module->first_type + void_offset;
}
fn Type* noreturn_type(Module* module)
{
return void_type(module) + 1;
}
fn Type* uint1(Module* module)
{
return integer_type(module, { .bit_count = 1, .is_signed = false });
}
fn Type* uint8(Module* module)
{
return integer_type(module, { .bit_count = 8, .is_signed = false });
}
fn Type* uint32(Module* module)
{
return integer_type(module, { .bit_count = 32, .is_signed = false });
}
fn Type* uint64(Module* module)
{
return integer_type(module, { .bit_count = 64, .is_signed = false });
}
fn Type* sint32(Module* module)
{
return integer_type(module, { .bit_count = 32, .is_signed = true });
}
struct Options
{
String content;
String path;
String executable;
String name;
Slice<String> objects;
Target target;
BuildMode build_mode;
bool has_debug_info;
bool silent;
};
fn Type* type_allocate_init(Module* module, Type type)
{
auto* result = &arena_allocate<Type>(module->arena, 1)[0];
*result = type;
if (module->last_type)
{
module->last_type->next = result;
module->last_type = result;
}
else
{
assert(!module->first_type);
module->first_type = result;
module->last_type = result;
}
return result;
}
fn Value* new_value(Module* module)
{
auto* result = &arena_allocate<Value>(module->arena, 1)[0];
return result;
}
fn Slice<Value*> new_value_array(Module* module, u64 count)
{
auto result = arena_allocate<Value*>(module->arena, count);
return result;
}
fn Slice<Type*> new_type_array(Module* module, u64 count)
{
auto result = arena_allocate<Type*>(module->arena, count);
return result;
}
fn Global* new_global(Module* module)
{
auto* result = &arena_allocate<Global>(module->arena, 1)[0];
if (module->last_global)
{
module->last_global->next = result;
module->last_global = result;
}
else
{
assert(!module->first_global);
module->first_global = result;
module->last_global = result;
}
return result;
}
fn Type* get_pointer_type(Module* module, Type* element_type)
{
auto last_pointer_type = module->first_pointer_type;
while (last_pointer_type)
{
assert(last_pointer_type->id == TypeId::pointer);
if (last_pointer_type->pointer.element_type == element_type)
{
return last_pointer_type;
}
if (!last_pointer_type->pointer.next)
{
break;
}
last_pointer_type = last_pointer_type->pointer.next;
}
String name_parts[] = {
string_literal("&"),
element_type->name,
};
auto result = type_allocate_init(module, {
.pointer = {
.element_type = element_type,
},
.id = TypeId::pointer,
.name = arena_join_string(module->arena, array_to_slice(name_parts)),
});
if (last_pointer_type)
{
assert(module->first_pointer_type);
last_pointer_type->pointer.next = result;
}
else
{
assert(!module->first_pointer_type);
module->first_pointer_type = result;
}
return result;
}
fn bool is_slice(Type* type)
{
switch (type->id)
{
case TypeId::structure:
{
return type->structure.is_slice;
}
default: return false;
}
}
fn Type* get_slice_type(Module* module, Type* element_type)
{
Type* slice_type = module->first_slice_type;
if (slice_type)
{
while (1)
{
assert(is_slice(slice_type));
assert(slice_type->structure.fields.length == 2);
auto* pointer_type = slice_type->structure.fields[0].type;
assert(pointer_type->id == TypeId::pointer);
auto* candidate_element_type = pointer_type->pointer.element_type;
if (candidate_element_type == element_type)
{
return slice_type;
}
if (!slice_type->structure.next)
{
break;
}
slice_type = slice_type->structure.next;
}
}
Type* last_slice_type = slice_type;
auto fields = arena_allocate<Field>(module->arena, 2);
fields[0] = {
.name = string_literal("pointer"),
.type = get_pointer_type(module, element_type),
.offset = 0,
.line = 0,
};
fields[1] = {
.name = string_literal("length"),
.type = uint64(module),
.offset = 8,
.line = 0,
};
String name_parts[] = {
string_literal("[]"),
element_type->name,
};
auto result = type_allocate_init(module, {
.structure = {
.fields = fields,
.byte_size = 16,
.byte_alignment = 8,
.line = 0,
.is_slice = true,
},
.id = TypeId::structure,
.name = arena_join_string(module->arena, array_to_slice(name_parts)),
});
if (last_slice_type)
{
last_slice_type->structure.next = result;
}
else
{
module->first_slice_type = result;
}
return result;
}
fn String array_name(Module* module, Type* element_type, u64 element_count)
{
u8 buffer[512];
auto buffer_slice = String{ .pointer = buffer, .length = array_length(buffer) };
u64 i = 0;
buffer[i] = '[';
i += 1;
i += format_integer_decimal(buffer_slice(i), element_count);
buffer[i] = ']';
i += 1;
auto element_name = element_type->name;
memcpy(buffer + i, element_name.pointer, element_name.length);
i += element_name.length;
auto name = arena_duplicate_string(module->arena, buffer_slice(0, i));
return name;
}
fn Type* get_array_type(Module* module, Type* element_type, u64 element_count)
{
assert(element_type);
assert(element_count);
Type* array_type = module->first_array_type;
if (array_type)
{
while (1)
{
assert(array_type->id == TypeId::array);
auto* candidate_element_type = array_type->array.element_type;
if (candidate_element_type == element_type)
{
return array_type;
}
if (!array_type->array.next)
{
break;
}
array_type = array_type->array.next;
}
}
Type* last_array_type = array_type;
auto result = type_allocate_init(module, {
.array = {
.element_type = element_type,
.element_count = element_count,
},
.id = TypeId::array,
.name = array_name(module, element_type, element_count),
});
if (last_array_type)
{
last_array_type->array.next = result;
}
else
{
module->first_array_type = result;
}
return result;
}
void parse(Module* module);
void emit(Module* module);