1460 lines
29 KiB
C++
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);
|