wip
This commit is contained in:
parent
c07849aa79
commit
5ef10c4d5a
270
src/compiler.h
270
src/compiler.h
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib.h>
|
||||
|
||||
#include <llvm-c/Types.h>
|
||||
#define report_error() trap_raw()
|
||||
|
||||
enum class Command
|
||||
@ -38,6 +38,23 @@ fn String build_mode_to_string(BuildMode build_mode)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@ -65,6 +82,165 @@ 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_coerce_to_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;
|
||||
@ -87,6 +263,24 @@ enum class CallingConvention
|
||||
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,
|
||||
@ -124,12 +318,29 @@ struct TypeInteger
|
||||
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
|
||||
@ -221,6 +432,13 @@ struct TypeUnion
|
||||
u32 biggest_field;
|
||||
};
|
||||
|
||||
struct LLVMType
|
||||
{
|
||||
LLVMTypeRef abi;
|
||||
LLVMTypeRef memory;
|
||||
LLVMMetadataRef debug;
|
||||
};
|
||||
|
||||
struct Type
|
||||
{
|
||||
union
|
||||
@ -238,6 +456,7 @@ struct Type
|
||||
TypeId id;
|
||||
String name;
|
||||
Type* next;
|
||||
LLVMType llvm;
|
||||
};
|
||||
|
||||
fn u32 align_bit_count(u32 bit_count)
|
||||
@ -340,6 +559,7 @@ struct Scope
|
||||
u32 line;
|
||||
u32 column;
|
||||
ScopeKind kind;
|
||||
LLVMMetadataRef llvm;
|
||||
};
|
||||
|
||||
enum class StatementId
|
||||
@ -487,12 +707,19 @@ struct ValueConstantInteger
|
||||
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
|
||||
@ -694,6 +921,7 @@ struct Value
|
||||
Type* type;
|
||||
ValueId id;
|
||||
ValueKind kind;
|
||||
LLVMValueRef llvm;
|
||||
|
||||
bool is_constant()
|
||||
{
|
||||
@ -742,6 +970,45 @@ struct Argument
|
||||
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;
|
||||
u32 debug_tag;
|
||||
};
|
||||
|
||||
struct Module
|
||||
{
|
||||
Arena* arena;
|
||||
@ -768,6 +1035,7 @@ struct Module
|
||||
MacroDeclaration* current_macro_declaration;
|
||||
MacroInstantiation* current_macro_instantiation;
|
||||
|
||||
ModuleLLVM llvm;
|
||||
Scope scope;
|
||||
|
||||
String name;
|
||||
|
886
src/emitter.cpp
886
src/emitter.cpp
@ -1,5 +1,891 @@
|
||||
#include <compiler.h>
|
||||
#include <llvm.h>
|
||||
|
||||
fn void llvm_initialize(Module* module)
|
||||
{
|
||||
llvm_initialize_all();
|
||||
|
||||
auto context = LLVMContextCreate();
|
||||
auto m = llvm_context_create_module(context, module->name);
|
||||
auto builder = LLVMCreateBuilderInContext(context);
|
||||
LLVMDIBuilderRef di_builder = 0;
|
||||
LLVMMetadataRef di_compile_unit = 0;
|
||||
LLVMMetadataRef di_file = 0;
|
||||
|
||||
if (module->has_debug_info)
|
||||
{
|
||||
di_builder = LLVMCreateDIBuilder(m);
|
||||
auto last_slash = string_last_character(module->path, '/');
|
||||
if (last_slash == string_no_match)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
auto directory = module->path(0, last_slash);
|
||||
auto file_name = module->path(last_slash + 1);
|
||||
auto file = LLVMDIBuilderCreateFile(di_builder, (char*)file_name.pointer, file_name.length, (char*)directory.pointer, directory.length);
|
||||
auto producer_name = string_literal("bloat buster");
|
||||
auto is_optimized = build_mode_is_optimized(module->build_mode);
|
||||
auto flags = string_literal("");
|
||||
u32 runtime_version = 0;
|
||||
auto split_name = string_literal("");
|
||||
auto sysroot = string_literal("");
|
||||
auto sdk = string_literal("");
|
||||
di_compile_unit = LLVMDIBuilderCreateCompileUnit(di_builder, LLVMDWARFSourceLanguageC17, file, (char*)producer_name.pointer, producer_name.length, is_optimized, (char*)flags.pointer, flags.length, runtime_version, (char*)split_name.pointer, split_name.length, LLVMDWARFEmissionFull, 0, 0, is_optimized, (char*)sysroot.pointer, sysroot.length, (char*)sdk.pointer, sdk.length);
|
||||
module->scope.llvm = di_compile_unit;
|
||||
}
|
||||
|
||||
module->llvm = {
|
||||
.context = context,
|
||||
.module = m,
|
||||
.builder = builder,
|
||||
.di_builder = di_builder,
|
||||
.file = di_file,
|
||||
.compile_unit = di_compile_unit,
|
||||
.pointer_type = LLVMPointerTypeInContext(context, 0),
|
||||
.void_type = LLVMVoidTypeInContext(context),
|
||||
};
|
||||
|
||||
for (u64 i = 0; i < (u64)IntrinsicIndex::count; i += 1)
|
||||
{
|
||||
String name = intrinsic_names[i];
|
||||
module->llvm.intrinsic_table[i].n = LLVMLookupIntrinsicID((char*)name.pointer, name.length);
|
||||
}
|
||||
}
|
||||
|
||||
enum class AbiSystemVClass
|
||||
{
|
||||
none,
|
||||
integer,
|
||||
sse,
|
||||
sse_up,
|
||||
x87,
|
||||
x87_up,
|
||||
complex_x87,
|
||||
memory,
|
||||
};
|
||||
|
||||
fn bool contains_no_user_data(Type* type, u64 start, u64 end)
|
||||
{
|
||||
if (get_byte_size(type) <= start)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (type->id)
|
||||
{
|
||||
case TypeId::structure:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
case TypeId::array:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn Type* get_integer_type_at_offset(Module* module, Type* type, u32 offset, Type* source_type, u32 source_offset)
|
||||
{
|
||||
switch (type->id)
|
||||
{
|
||||
case TypeId::integer:
|
||||
{
|
||||
auto bit_count = type->integer.bit_count;
|
||||
switch (bit_count)
|
||||
{
|
||||
case 64: return type;
|
||||
case 32: case 16: case 8:
|
||||
{
|
||||
assert(offset == 0);
|
||||
auto start = source_offset + get_byte_size(type);
|
||||
auto end = source_offset + 8;
|
||||
|
||||
if (contains_no_user_data(source_type, start, end))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
auto source_size = get_byte_size(source_type);
|
||||
auto byte_count = source_size - source_offset;
|
||||
auto bit_count = byte_count > 8 ? 64 : byte_count * 8;
|
||||
auto result = integer_type(module, { .bit_count = 64, .is_signed = false });
|
||||
return result;
|
||||
}
|
||||
|
||||
struct AbiSystemVClassify
|
||||
{
|
||||
u64 base_offset;
|
||||
bool is_variable_argument;
|
||||
bool is_register_call;
|
||||
};
|
||||
|
||||
struct AbiSystemVClassifyResult
|
||||
{
|
||||
AbiSystemVClass r[2];
|
||||
};
|
||||
|
||||
fn AbiSystemVClassifyResult abi_system_v_classify_type(Type* type, AbiSystemVClassify options)
|
||||
{
|
||||
AbiSystemVClassifyResult result = {};
|
||||
auto is_memory = options.base_offset >= 8;
|
||||
auto current_index = is_memory;
|
||||
auto not_current_index = !is_memory;
|
||||
assert(current_index != not_current_index);
|
||||
result.r[current_index] = AbiSystemVClass::memory;
|
||||
|
||||
switch (type->id)
|
||||
{
|
||||
case TypeId::void_type:
|
||||
case TypeId::noreturn:
|
||||
result.r[current_index] = AbiSystemVClass::none;
|
||||
break;
|
||||
case TypeId::bits:
|
||||
return abi_system_v_classify_type(type->bits.backing_type, options);
|
||||
case TypeId::enumerator:
|
||||
return abi_system_v_classify_type(type->enumerator.backing_type, options);
|
||||
case TypeId::pointer:
|
||||
result.r[current_index] = AbiSystemVClass::integer;
|
||||
break;
|
||||
case TypeId::integer:
|
||||
{
|
||||
if (type->integer.bit_count <= 64)
|
||||
{
|
||||
result.r[current_index] = AbiSystemVClass::integer;
|
||||
}
|
||||
else if (type->integer.bit_count == 128)
|
||||
{
|
||||
trap_raw();
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
} break;
|
||||
case TypeId::array:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
case TypeId::structure:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
case TypeId::alias:
|
||||
return abi_system_v_classify_type(type->alias.type, options);
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn bool is_integral_or_enumeration_type(Type* type)
|
||||
{
|
||||
switch (type->id)
|
||||
{
|
||||
case TypeId::alias: return is_integral_or_enumeration_type(type->alias.type);
|
||||
case TypeId::integer:
|
||||
case TypeId::bits:
|
||||
return true;
|
||||
case TypeId::structure:
|
||||
return false;
|
||||
default: unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
fn bool is_promotable_integer_type_for_abi(Type* type)
|
||||
{
|
||||
switch (type->id)
|
||||
{
|
||||
case TypeId::integer: return type->integer.bit_count < 32;
|
||||
case TypeId::bits: return is_promotable_integer_type_for_abi(type->bits.backing_type);
|
||||
case TypeId::alias: return is_promotable_integer_type_for_abi(type->alias.type);
|
||||
default: unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
struct DirectOptions
|
||||
{
|
||||
Type* semantic_type;
|
||||
Type* type;
|
||||
Type* padding;
|
||||
u32 offset;
|
||||
u32 alignment;
|
||||
bool cannot_be_flattened;
|
||||
};
|
||||
|
||||
fn void resolve_type_in_place_abi(Module* module, Type* type)
|
||||
{
|
||||
if (!type->llvm.abi)
|
||||
{
|
||||
LLVMTypeRef result;
|
||||
|
||||
switch (type->id)
|
||||
{
|
||||
case TypeId::void_type:
|
||||
case TypeId::noreturn:
|
||||
result = module->llvm.void_type;
|
||||
break;
|
||||
case TypeId::integer:
|
||||
result = LLVMIntTypeInContext(module->llvm.context, type->integer.bit_count);
|
||||
break;
|
||||
case TypeId::pointer:
|
||||
result = module->llvm.pointer_type;
|
||||
break;
|
||||
case TypeId::array:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
type->llvm.abi = result;
|
||||
}
|
||||
}
|
||||
|
||||
fn void resolve_type_in_place_memory(Module* module, Type* type)
|
||||
{
|
||||
if (!type->llvm.memory)
|
||||
{
|
||||
resolve_type_in_place_abi(module, type);
|
||||
|
||||
LLVMTypeRef result;
|
||||
|
||||
switch (type->id)
|
||||
{
|
||||
case TypeId::void_type:
|
||||
case TypeId::noreturn:
|
||||
case TypeId::pointer:
|
||||
result = type->llvm.abi;
|
||||
break;
|
||||
case TypeId::integer:
|
||||
{
|
||||
auto byte_size = get_byte_size(type);
|
||||
auto bit_count = byte_size * 8;
|
||||
result = LLVMIntTypeInContext(module->llvm.context, bit_count);
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
type->llvm.memory = result;
|
||||
|
||||
if (type->id == TypeId::bits)
|
||||
{
|
||||
assert(type->llvm.memory == type->llvm.abi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void resolve_type_in_place_debug(Module* module, Type* type)
|
||||
{
|
||||
if (module->has_debug_info)
|
||||
{
|
||||
if (!type->llvm.debug)
|
||||
{
|
||||
LLVMMetadataRef result;
|
||||
|
||||
switch (type->id)
|
||||
{
|
||||
case TypeId::integer:
|
||||
{
|
||||
DwarfType dwarf_type = type->integer.bit_count == 1 ? DwarfType::boolean : (type->integer.is_signed ? DwarfType::signed_type : DwarfType::unsigned_type);
|
||||
LLVMDIFlags flags = {};
|
||||
result = LLVMDIBuilderCreateBasicType(module->llvm.di_builder, (char*)type->name.pointer, type->name.length, type->integer.bit_count, (u32)dwarf_type, flags);
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
type->llvm.debug = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void resolve_type_in_place(Module* module, Type* type)
|
||||
{
|
||||
resolve_type_in_place_abi(module, type);
|
||||
resolve_type_in_place_memory(module, type);
|
||||
resolve_type_in_place_debug(module, type);
|
||||
}
|
||||
|
||||
fn AbiInformation abi_system_v_get_direct(Module* module, DirectOptions direct)
|
||||
{
|
||||
AbiInformation result = {
|
||||
.semantic_type = direct.semantic_type,
|
||||
.flags = {
|
||||
.kind = AbiKind::direct,
|
||||
},
|
||||
};
|
||||
resolve_type_in_place(module, direct.semantic_type);
|
||||
resolve_type_in_place(module, direct.type);
|
||||
if (unlikely(direct.padding))
|
||||
{
|
||||
resolve_type_in_place(module, direct.padding);
|
||||
}
|
||||
|
||||
result.set_coerce_to_type(direct.type);
|
||||
result.set_padding_type(direct.type);
|
||||
result.set_direct_offset(direct.offset);
|
||||
result.set_direct_alignment(direct.alignment);
|
||||
result.set_can_be_flattened(!direct.cannot_be_flattened);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn AbiInformation abi_system_classify_return_type(Module* module, Type* semantic_return_type)
|
||||
{
|
||||
auto type_classes = abi_system_v_classify_type(semantic_return_type, {});
|
||||
auto low_class = type_classes.r[0];
|
||||
auto high_class = type_classes.r[1];
|
||||
assert(high_class != AbiSystemVClass::memory || low_class == AbiSystemVClass::memory);
|
||||
assert(high_class != AbiSystemVClass::sse_up || low_class == AbiSystemVClass::sse);
|
||||
|
||||
Type* low_type = 0;
|
||||
|
||||
switch (low_class)
|
||||
{
|
||||
case AbiSystemVClass::none:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
case AbiSystemVClass::integer:
|
||||
{
|
||||
low_type = get_integer_type_at_offset(module, semantic_return_type, 0, semantic_return_type, 0);
|
||||
|
||||
if (high_class == AbiSystemVClass::none && low_type->id == TypeId::integer)
|
||||
{
|
||||
if (semantic_return_type->id == TypeId::enumerator)
|
||||
{
|
||||
trap_raw();
|
||||
}
|
||||
|
||||
if (is_integral_or_enumeration_type(semantic_return_type) && is_promotable_integer_type_for_abi(semantic_return_type))
|
||||
{
|
||||
trap_raw();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
Type* high_type = 0;
|
||||
|
||||
switch (high_class)
|
||||
{
|
||||
case AbiSystemVClass::none:
|
||||
break;
|
||||
case AbiSystemVClass::integer:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
if (high_type)
|
||||
{
|
||||
trap_raw();
|
||||
}
|
||||
|
||||
auto result = abi_system_v_get_direct(module, {
|
||||
.semantic_type = semantic_return_type,
|
||||
.type = low_type,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
struct AttributeBuildOptions
|
||||
{
|
||||
AbiInformation return_abi;
|
||||
Slice<AbiInformation> argument_abis;
|
||||
Slice<Type*> abi_argument_types;
|
||||
Type* abi_return_type;
|
||||
FunctionAttributes attributes;
|
||||
bool call_site;
|
||||
};
|
||||
|
||||
struct AllocaOptions
|
||||
{
|
||||
Type* type;
|
||||
String name = string_literal("");
|
||||
u32 alignment;
|
||||
bool is_valid_alignment;
|
||||
};
|
||||
|
||||
fn LLVMValueRef create_alloca(Module* module, AllocaOptions options)
|
||||
{
|
||||
auto abi_type = options.type;
|
||||
resolve_type_in_place(module, abi_type);
|
||||
assert(options.name.pointer[options.name.length] == 0);
|
||||
|
||||
u32 alignment;
|
||||
if (options.is_valid_alignment)
|
||||
{
|
||||
alignment = options.alignment;
|
||||
}
|
||||
else
|
||||
{
|
||||
alignment = get_byte_alignment(abi_type);
|
||||
}
|
||||
|
||||
auto alloca = llvm_builder_create_alloca(module->llvm.builder, abi_type->llvm.memory, 0, options.name);
|
||||
return alloca;
|
||||
}
|
||||
|
||||
fn BBLLVMAttributeList build_attribute_list(Module* module, AttributeBuildOptions options)
|
||||
{
|
||||
resolve_type_in_place(module, options.return_abi.semantic_type);
|
||||
BBLLVMAttributeListOptions attributes = {};
|
||||
|
||||
attributes.return_ = {
|
||||
.semantic_type = options.return_abi.semantic_type->llvm.memory,
|
||||
.abi_type = options.abi_return_type->llvm.abi,
|
||||
.dereferenceable_bytes = 0,
|
||||
.alignment = 0,
|
||||
.no_alias = false,
|
||||
.non_null = false,
|
||||
.no_undef = false,
|
||||
.sign_extend = options.return_abi.flags.kind == AbiKind::extend and options.return_abi.flags.sign_extension,
|
||||
.zero_extend = options.return_abi.flags.kind == AbiKind::extend and !options.return_abi.flags.sign_extension,
|
||||
.in_reg = false,
|
||||
.no_fp_class = 0, // TODO: this is a struct
|
||||
.struct_return = false,
|
||||
.writable = false,
|
||||
.dead_on_unwind = false,
|
||||
.in_alloca = false,
|
||||
.dereferenceable = false,
|
||||
.dereferenceable_or_null = false,
|
||||
.nest = false,
|
||||
.by_value = false,
|
||||
.by_reference = false,
|
||||
.no_capture = false,
|
||||
};
|
||||
|
||||
BBLLVMArgumentAttributes argument_attribute_buffer[128];
|
||||
auto argument_attributes = Slice<BBLLVMArgumentAttributes>{ .pointer = argument_attribute_buffer, .length = options.abi_argument_types.length };
|
||||
|
||||
if (options.return_abi.flags.kind == AbiKind::indirect)
|
||||
{
|
||||
trap_raw();
|
||||
}
|
||||
|
||||
for (auto& abi: options.argument_abis)
|
||||
{
|
||||
trap_raw();
|
||||
}
|
||||
|
||||
attributes.function = {
|
||||
.prefer_vector_width = {},
|
||||
.stack_protector_buffer_size = {},
|
||||
.definition_probe_stack = {},
|
||||
.definition_stack_probe_size = {},
|
||||
.flags0 = {
|
||||
.noreturn = options.return_abi.semantic_type == noreturn_type(module),
|
||||
.cmse_ns_call = false,
|
||||
.nounwind = true,
|
||||
.returns_twice = false,
|
||||
.cold = false,
|
||||
.hot = false,
|
||||
.no_duplicate = false,
|
||||
.convergent = false,
|
||||
.no_merge = false,
|
||||
.will_return = false,
|
||||
.no_caller_saved_registers = false,
|
||||
.no_cf_check = false,
|
||||
.no_callback = false,
|
||||
.alloc_size = false, // TODO
|
||||
.uniform_work_group_size = false,
|
||||
.aarch64_pstate_sm_body = false,
|
||||
.aarch64_pstate_sm_enabled = false,
|
||||
.aarch64_pstate_sm_compatible = false,
|
||||
.aarch64_preserves_za = false,
|
||||
.aarch64_in_za = false,
|
||||
.aarch64_out_za = false,
|
||||
.aarch64_inout_za = false,
|
||||
.aarch64_preserves_zt0 = false,
|
||||
.aarch64_in_zt0 = false,
|
||||
.aarch64_out_zt0 = false,
|
||||
.aarch64_inout_zt0 = false,
|
||||
.optimize_for_size = false,
|
||||
.min_size = false,
|
||||
.no_red_zone = false,
|
||||
.indirect_tls_seg_refs = false,
|
||||
.no_implicit_floats = false,
|
||||
.sample_profile_suffix_elision_policy = false,
|
||||
.memory_none = false,
|
||||
.memory_readonly = false,
|
||||
.memory_inaccessible_or_arg_memory_only = false,
|
||||
.memory_arg_memory_only = false,
|
||||
.strict_fp = false,
|
||||
.no_inline = options.attributes.inline_behavior == InlineBehavior::no_inline,
|
||||
.always_inline = options.attributes.inline_behavior == InlineBehavior::always_inline,
|
||||
.guard_no_cf = false,
|
||||
// TODO: branch protection function attributes
|
||||
// TODO: cpu features
|
||||
|
||||
// CALL-SITE ATTRIBUTES
|
||||
.call_no_builtins = false,
|
||||
|
||||
// DEFINITION-SITE ATTRIBUTES
|
||||
.definition_frame_pointer_kind = module->has_debug_info ? BBLLVMFramePointerKind::all : BBLLVMFramePointerKind::none,
|
||||
.definition_less_precise_fpmad = false,
|
||||
.definition_null_pointer_is_valid = false,
|
||||
.definition_no_trapping_fp_math = false,
|
||||
.definition_no_infs_fp_math = false,
|
||||
.definition_no_nans_fp_math = false,
|
||||
.definition_approx_func_fp_math = false,
|
||||
.definition_unsafe_fp_math = false,
|
||||
.definition_use_soft_float = false,
|
||||
.definition_no_signed_zeroes_fp_math = false,
|
||||
.definition_stack_realignment = false,
|
||||
.definition_backchain = false,
|
||||
.definition_split_stack = false,
|
||||
.definition_speculative_load_hardening = false,
|
||||
.definition_zero_call_used_registers = ZeroCallUsedRegsKind::all,
|
||||
// TODO: denormal builtins
|
||||
.definition_non_lazy_bind = false,
|
||||
.definition_cmse_nonsecure_entry = false,
|
||||
.definition_unwind_table_kind = BBLLVMUWTableKind::None,
|
||||
},
|
||||
.flags1 = {
|
||||
.definition_disable_tail_calls = false,
|
||||
.definition_stack_protect_strong = false,
|
||||
.definition_stack_protect = false,
|
||||
.definition_stack_protect_req = false,
|
||||
.definition_aarch64_new_za = false,
|
||||
.definition_aarch64_new_zt0 = false,
|
||||
.definition_optimize_none = false,
|
||||
.definition_naked = !options.call_site and options.attributes.naked,
|
||||
.definition_inline_hint = !options.call_site and options.attributes.inline_behavior == InlineBehavior::inline_hint,
|
||||
},
|
||||
};
|
||||
|
||||
auto attribute_list = llvm_attribute_list_build(module->llvm.context, &attributes, options.call_site);
|
||||
return attribute_list;
|
||||
}
|
||||
|
||||
fn void analyze_statement(Module* module, Scope* scope, Statement* statement, u32* last_line, u32* last_column, LLVMMetadataRef* last_debug_location)
|
||||
{
|
||||
Global* parent_function_global;
|
||||
if (module->current_function)
|
||||
{
|
||||
parent_function_global = module->current_function;
|
||||
}
|
||||
else if (module->current_macro_instantiation)
|
||||
{
|
||||
parent_function_global = module->current_macro_instantiation->instantiation_function;
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
auto* llvm_function = parent_function_global->variable.storage->llvm;
|
||||
assert(llvm_function);
|
||||
|
||||
if (module->has_debug_info)
|
||||
{
|
||||
if (statement->line != *last_line || statement->column != *last_column)
|
||||
{
|
||||
auto new_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, statement->line, statement->column, scope->llvm, module->llvm.inlined_at);
|
||||
*last_debug_location = new_location;
|
||||
LLVMSetCurrentDebugLocation2(module->llvm.builder, new_location);
|
||||
*last_line = statement->line;
|
||||
*last_column = statement->column;
|
||||
}
|
||||
}
|
||||
|
||||
switch (statement->id)
|
||||
{
|
||||
case StatementId::return_st:
|
||||
{
|
||||
if (module->current_function)
|
||||
{
|
||||
auto& function_type = parent_function_global->variable.storage->type->pointer.element_type->function;
|
||||
auto& return_abi = function_type.return_abi;
|
||||
auto return_value = statement->return_st;
|
||||
|
||||
switch (return_abi.semantic_type->id)
|
||||
{
|
||||
case TypeId::void_type:
|
||||
{
|
||||
if (return_value)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
} break;
|
||||
case TypeId::noreturn:
|
||||
{
|
||||
report_error();
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
if (module->has_debug_info)
|
||||
{
|
||||
LLVMSetCurrentDebugLocation2(module->llvm.builder, *last_debug_location);
|
||||
}
|
||||
|
||||
auto return_alloca = module->current_function->variable.storage->function.llvm.return_alloca;
|
||||
if (!return_alloca)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
if (!return_value)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
trap_raw();
|
||||
} break;
|
||||
}
|
||||
trap_raw();
|
||||
}
|
||||
else if (module->current_macro_instantiation)
|
||||
{
|
||||
trap_raw();
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
fn void analyze_block(Module* module, Block* block)
|
||||
{
|
||||
if (module->has_debug_info)
|
||||
{
|
||||
auto lexical_block = LLVMDIBuilderCreateLexicalBlock(module->llvm.di_builder, block->scope.parent->llvm, module->llvm.file, block->scope.line, block->scope.column);
|
||||
block->scope.llvm = lexical_block;
|
||||
}
|
||||
|
||||
u32 last_line = 0;
|
||||
u32 last_column = 0;
|
||||
LLVMMetadataRef last_debug_location = 0;
|
||||
|
||||
for (auto* statement = block->first_statement; statement; statement = statement->next)
|
||||
{
|
||||
analyze_statement(module, &block->scope, statement, &last_line, &last_column, &last_debug_location);
|
||||
}
|
||||
}
|
||||
|
||||
void emit(Module* module)
|
||||
{
|
||||
llvm_initialize(module);
|
||||
|
||||
for (auto* global = module->first_global; global; global = global->next)
|
||||
{
|
||||
switch (global->variable.storage->id)
|
||||
{
|
||||
case ValueId::function:
|
||||
case ValueId::external_function:
|
||||
{
|
||||
auto function_type = &global->variable.storage->type->pointer.element_type->function;
|
||||
function_type->argument_abis = arena_allocate<AbiInformation>(module->arena, function_type->semantic_argument_types.length);
|
||||
auto resolved_calling_convention = resolve_calling_convention(function_type->calling_convention);
|
||||
auto is_reg_call = resolved_calling_convention == ResolvedCallingConvention::system_v && false; // TODO: regcall calling convention
|
||||
|
||||
LLVMTypeRef llvm_abi_argument_type_buffer[64];
|
||||
|
||||
switch (resolved_calling_convention)
|
||||
{
|
||||
case ResolvedCallingConvention::system_v:
|
||||
{
|
||||
function_type->available_registers = {
|
||||
.system_v = {
|
||||
.gpr = (u32)(is_reg_call ? 11 : 6),
|
||||
.sse = (u32)(is_reg_call ? 16 : 8),
|
||||
},
|
||||
};
|
||||
function_type->return_abi = abi_system_classify_return_type(module, function_type->semantic_return_type);
|
||||
auto return_abi_kind = function_type->return_abi.flags.kind;
|
||||
|
||||
Type* abi_argument_type_buffer[64];
|
||||
u16 abi_argument_type_count = 0;
|
||||
|
||||
Type* abi_return_type;
|
||||
switch (return_abi_kind)
|
||||
{
|
||||
case AbiKind::direct:
|
||||
case AbiKind::extend:
|
||||
{
|
||||
abi_return_type = function_type->return_abi.coerce_to_type;
|
||||
} break;
|
||||
case AbiKind::ignore:
|
||||
case AbiKind::indirect:
|
||||
{
|
||||
abi_return_type = void_type(module);
|
||||
} break;
|
||||
default: unreachable(); // TODO
|
||||
}
|
||||
assert(abi_return_type);
|
||||
function_type->abi_return_type = abi_return_type;
|
||||
resolve_type_in_place(module, abi_return_type);
|
||||
|
||||
if (function_type->return_abi.flags.kind == AbiKind::indirect)
|
||||
{
|
||||
trap_raw();
|
||||
}
|
||||
|
||||
auto required_argument_count = function_type->semantic_argument_types.length;
|
||||
|
||||
for (auto abi: function_type->argument_abis)
|
||||
{
|
||||
trap_raw();
|
||||
}
|
||||
|
||||
auto abi_argument_types = new_type_array(module, abi_argument_type_count);
|
||||
memcpy(abi_argument_types.pointer, abi_argument_type_buffer, sizeof(abi_argument_type_buffer[0]) * abi_argument_type_count);
|
||||
function_type->abi_argument_types = abi_argument_types;
|
||||
} break;
|
||||
case ResolvedCallingConvention::win64:
|
||||
{
|
||||
report_error();
|
||||
} break;
|
||||
case ResolvedCallingConvention::count: unreachable();
|
||||
}
|
||||
|
||||
auto llvm_function_type = LLVMFunctionType(function_type->abi_return_type->llvm.abi, llvm_abi_argument_type_buffer, (u32)function_type->abi_argument_types.length, function_type->is_variable_arguments);
|
||||
|
||||
LLVMMetadataRef subroutine_type = 0;
|
||||
if (module->has_debug_info)
|
||||
{
|
||||
LLVMMetadataRef debug_argument_type_buffer[64];
|
||||
Slice<LLVMMetadataRef> debug_argument_types = { .pointer = debug_argument_type_buffer, .length = function_type->argument_abis.length + 1 + function_type->is_variable_arguments };
|
||||
debug_argument_types[0] = function_type->return_abi.semantic_type->llvm.debug;
|
||||
assert(debug_argument_types[0]);
|
||||
|
||||
auto debug_argument_type_slice = debug_argument_types(1)(0, function_type->argument_abis.length);
|
||||
|
||||
for (u64 i = 0; i < function_type->argument_abis.length; i += 1)
|
||||
{
|
||||
auto& argument_abi = function_type->argument_abis[i];
|
||||
auto* debug_argument_type = &debug_argument_type_slice[i];
|
||||
*debug_argument_type = argument_abi.semantic_type->llvm.debug;
|
||||
assert(*debug_argument_type);
|
||||
}
|
||||
|
||||
if (function_type->is_variable_arguments)
|
||||
{
|
||||
auto void_ty = void_type(module);
|
||||
assert(void_ty->llvm.debug);
|
||||
debug_argument_types[function_type->argument_abis.length + 1] = void_ty->llvm.debug;
|
||||
}
|
||||
|
||||
LLVMDIFlags flags = {};
|
||||
subroutine_type = LLVMDIBuilderCreateSubroutineType(module->llvm.di_builder, module->llvm.file, debug_argument_types.pointer, (u32)debug_argument_types.length, flags);
|
||||
}
|
||||
|
||||
global->variable.storage->type->pointer.element_type->llvm.abi = llvm_function_type;
|
||||
global->variable.storage->type->pointer.element_type->llvm.debug = subroutine_type;
|
||||
|
||||
LinkageType llvm_linkage_type;
|
||||
switch (global->linkage)
|
||||
{
|
||||
case Linkage::internal: llvm_linkage_type = LinkageType::internal; break;
|
||||
case Linkage::external: llvm_linkage_type = LinkageType::external; break;
|
||||
}
|
||||
unsigned address_space = 0;
|
||||
auto llvm_function = llvm_module_create_function(module->llvm.module, llvm_function_type, llvm_linkage_type, address_space, global->variable.name);
|
||||
global->variable.storage->llvm = llvm_function;
|
||||
|
||||
LLVMCallConv cc;
|
||||
switch (function_type->calling_convention)
|
||||
{
|
||||
case CallingConvention::c: cc = LLVMCCallConv; break;
|
||||
case CallingConvention::count: unreachable();
|
||||
}
|
||||
LLVMSetFunctionCallConv(llvm_function, cc);
|
||||
|
||||
assert(global->variable.storage->id == ValueId::function);
|
||||
auto attribute_list = build_attribute_list(module, {
|
||||
.return_abi = function_type->return_abi,
|
||||
.argument_abis = function_type->argument_abis,
|
||||
.abi_argument_types = function_type->abi_argument_types,
|
||||
.abi_return_type = function_type->abi_return_type,
|
||||
.attributes = global->variable.storage->function.attributes,
|
||||
.call_site = false,
|
||||
});
|
||||
llvm_function_set_attributes(llvm_function, attribute_list);
|
||||
|
||||
LLVMMetadataRef subprogram = 0;
|
||||
auto is_definition = global->variable.storage->id == ValueId::function;
|
||||
|
||||
if (module->has_debug_info)
|
||||
{
|
||||
auto is_local_to_unit = global->linkage == Linkage::internal;
|
||||
auto line = global->variable.line;
|
||||
auto scope_line = line + 1;
|
||||
LLVMDIFlags flags = {};
|
||||
auto is_optimized = build_mode_is_optimized(module->build_mode);
|
||||
subprogram = LLVMDIBuilderCreateFunction(module->llvm.di_builder, module->scope.llvm, (char*)global->variable.name.pointer, global->variable.name.length, (char*)global->variable.name.pointer, global->variable.name.length, module->llvm.file, line, subroutine_type, is_local_to_unit, is_definition, scope_line, flags, is_optimized);
|
||||
}
|
||||
|
||||
if (is_definition)
|
||||
{
|
||||
global->variable.storage->function.scope.llvm = subprogram;
|
||||
|
||||
module->current_function = global;
|
||||
|
||||
auto* entry_block = llvm_context_create_basic_block(module->llvm.context, string_literal("entry"), llvm_function);
|
||||
global->variable.storage->function.llvm.return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("entry"), llvm_function);
|
||||
|
||||
LLVMPositionBuilderAtEnd(module->llvm.builder, entry_block);
|
||||
LLVMSetCurrentDebugLocation2(module->llvm.builder, 0);
|
||||
|
||||
auto return_abi_kind = function_type->return_abi.flags.kind;
|
||||
switch (return_abi_kind)
|
||||
{
|
||||
case AbiKind::indirect:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
case AbiKind::in_alloca:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
auto alloca = create_alloca(module, {
|
||||
.type = function_type->return_abi.semantic_type,
|
||||
.name = string_literal("retval"),
|
||||
});
|
||||
global->variable.storage->function.llvm.return_alloca = alloca;
|
||||
} break;
|
||||
case AbiKind::ignore: break;
|
||||
}
|
||||
|
||||
for (auto& argument : global->variable.storage->function.arguments)
|
||||
{
|
||||
trap_raw();
|
||||
}
|
||||
|
||||
analyze_block(module, global->variable.storage->function.block);
|
||||
trap_raw();
|
||||
|
||||
// END OF SCOPE
|
||||
module->current_function = 0;
|
||||
}
|
||||
} break;
|
||||
case ValueId::global:
|
||||
{
|
||||
trap_raw();
|
||||
} break;
|
||||
default: report_error();
|
||||
}
|
||||
}
|
||||
|
||||
trap_raw();
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define breakpoint() __builtin_debugtrap()
|
||||
#define string_literal_length(s) (sizeof(s) - 1)
|
||||
#define string_literal(s) ((String){ .pointer = (u8*)(s), .length = string_literal_length(s), })
|
||||
#define split_string_literal(s) (char*)(s), string_literal_length(s)
|
||||
#define offsetof(S, f) __builtin_offsetof(S, f)
|
||||
|
||||
#define array_length(arr) sizeof(arr) / sizeof((arr)[0])
|
||||
|
797
src/llvm.cpp
797
src/llvm.cpp
File diff suppressed because it is too large
Load Diff
401
src/llvm.h
401
src/llvm.h
@ -1,24 +1,392 @@
|
||||
#include <lib.h>
|
||||
#pragma once
|
||||
|
||||
namespace llvm
|
||||
#include <compiler.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Analysis.h>
|
||||
#include <llvm-c/Target.h>
|
||||
#include <llvm-c/Analysis.h>
|
||||
#include <llvm-c/DebugInfo.h>
|
||||
|
||||
|
||||
enum class BBLLVMUWTableKind : u64
|
||||
{
|
||||
class Type;
|
||||
class Value;
|
||||
}
|
||||
None = 0, ///< No unwind table requested
|
||||
Sync = 1, ///< "Synchronous" unwind tables
|
||||
Async = 2, ///< "Asynchronous" unwind tables (instr precise)
|
||||
Default = 2,
|
||||
};
|
||||
|
||||
enum class BBLLVMFramePointerKind : u64
|
||||
{
|
||||
none = 0,
|
||||
reserved = 1,
|
||||
non_leaf = 2,
|
||||
all = 3,
|
||||
};
|
||||
|
||||
enum class ZeroCallUsedRegsKind : u64
|
||||
{
|
||||
all = 0,
|
||||
skip = 1 << 0,
|
||||
only_used = 1 << 1,
|
||||
only_gpr = 1 << 2,
|
||||
only_arg = 1 << 3,
|
||||
used_gpr_arg = only_used | only_gpr | only_arg,
|
||||
used_gpr = only_used | only_gpr,
|
||||
used_arg = only_used | only_arg,
|
||||
used = only_used,
|
||||
all_gpr_arg = only_gpr | only_arg,
|
||||
all_gpr = only_gpr,
|
||||
all_arg = only_arg,
|
||||
};
|
||||
|
||||
struct BBLLVMFunctionAttributesFlags0
|
||||
{
|
||||
u64 noreturn:1;
|
||||
u64 cmse_ns_call:1;
|
||||
u64 nounwind:1;
|
||||
u64 returns_twice:1;
|
||||
u64 cold:1;
|
||||
u64 hot:1;
|
||||
u64 no_duplicate:1;
|
||||
u64 convergent:1;
|
||||
u64 no_merge:1;
|
||||
u64 will_return:1;
|
||||
u64 no_caller_saved_registers:1;
|
||||
u64 no_cf_check:1;
|
||||
u64 no_callback:1;
|
||||
u64 alloc_size:1;
|
||||
u64 uniform_work_group_size:1;
|
||||
u64 aarch64_pstate_sm_body:1;
|
||||
u64 aarch64_pstate_sm_enabled:1;
|
||||
u64 aarch64_pstate_sm_compatible:1;
|
||||
u64 aarch64_preserves_za:1;
|
||||
u64 aarch64_in_za:1;
|
||||
u64 aarch64_out_za:1;
|
||||
u64 aarch64_inout_za:1;
|
||||
u64 aarch64_preserves_zt0:1;
|
||||
u64 aarch64_in_zt0:1;
|
||||
u64 aarch64_out_zt0:1;
|
||||
u64 aarch64_inout_zt0:1;
|
||||
u64 optimize_for_size:1;
|
||||
u64 min_size:1;
|
||||
u64 no_red_zone:1;
|
||||
u64 indirect_tls_seg_refs:1;
|
||||
u64 no_implicit_floats:1;
|
||||
u64 sample_profile_suffix_elision_policy:1;
|
||||
u64 memory_none:1;
|
||||
u64 memory_readonly:1;
|
||||
u64 memory_inaccessible_or_arg_memory_only:1;
|
||||
u64 memory_arg_memory_only:1;
|
||||
u64 strict_fp:1;
|
||||
u64 no_inline:1;
|
||||
u64 always_inline:1;
|
||||
u64 guard_no_cf:1;
|
||||
|
||||
// TODO: branch protection function attributes
|
||||
// TODO: cpu features
|
||||
|
||||
// Call-site begin
|
||||
u64 call_no_builtins:1;
|
||||
|
||||
BBLLVMFramePointerKind definition_frame_pointer_kind:2;
|
||||
u64 definition_less_precise_fpmad:1;
|
||||
u64 definition_null_pointer_is_valid:1;
|
||||
u64 definition_no_trapping_fp_math:1;
|
||||
u64 definition_no_infs_fp_math:1;
|
||||
u64 definition_no_nans_fp_math:1;
|
||||
u64 definition_approx_func_fp_math:1;
|
||||
u64 definition_unsafe_fp_math:1;
|
||||
u64 definition_use_soft_float:1;
|
||||
u64 definition_no_signed_zeroes_fp_math:1;
|
||||
u64 definition_stack_realignment:1;
|
||||
u64 definition_backchain:1;
|
||||
u64 definition_split_stack:1;
|
||||
u64 definition_speculative_load_hardening:1;
|
||||
ZeroCallUsedRegsKind definition_zero_call_used_registers:4;
|
||||
// TODO: denormal builtins
|
||||
u64 definition_non_lazy_bind:1;
|
||||
u64 definition_cmse_nonsecure_entry:1;
|
||||
BBLLVMUWTableKind definition_unwind_table_kind:2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BBLLVMFunctionAttributesFlags0) == sizeof(u64));
|
||||
|
||||
struct BBLLVMFunctionAttributesFlags1
|
||||
{
|
||||
u64 definition_disable_tail_calls:1;
|
||||
u64 definition_stack_protect_strong:1;
|
||||
u64 definition_stack_protect:1;
|
||||
u64 definition_stack_protect_req:1;
|
||||
u64 definition_aarch64_new_za:1;
|
||||
u64 definition_aarch64_new_zt0:1;
|
||||
u64 definition_optimize_none:1;
|
||||
u64 definition_naked:1;
|
||||
u64 definition_inline_hint:1;
|
||||
u64 _:55;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BBLLVMFunctionAttributesFlags1) == sizeof(u64));
|
||||
|
||||
struct BBLLVMFunctionAttributes
|
||||
{
|
||||
String prefer_vector_width;
|
||||
String stack_protector_buffer_size;
|
||||
String definition_probe_stack;
|
||||
String definition_stack_probe_size;
|
||||
|
||||
BBLLVMFunctionAttributesFlags0 flags0;
|
||||
BBLLVMFunctionAttributesFlags1 flags1;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BBLLVMFunctionAttributes) == 10 * sizeof(u64));
|
||||
|
||||
struct BBLLVMArgumentAttributes
|
||||
{
|
||||
LLVMTypeRef semantic_type;
|
||||
LLVMTypeRef abi_type;
|
||||
u64 dereferenceable_bytes;
|
||||
u32 alignment;
|
||||
u32 no_alias:1;
|
||||
u32 non_null:1;
|
||||
u32 no_undef:1;
|
||||
u32 sign_extend:1;
|
||||
u32 zero_extend:1;
|
||||
u32 in_reg:1;
|
||||
u32 no_fp_class:10;
|
||||
u32 struct_return:1;
|
||||
u32 writable:1;
|
||||
u32 dead_on_unwind:1;
|
||||
u32 in_alloca:1;
|
||||
u32 dereferenceable:1;
|
||||
u32 dereferenceable_or_null:1;
|
||||
u32 nest:1;
|
||||
u32 by_value:1;
|
||||
u32 by_reference:1;
|
||||
u32 no_capture:1;
|
||||
u32 _:6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BBLLVMArgumentAttributes) == 2 * sizeof(Type*) + 2 * sizeof(u64));
|
||||
|
||||
struct BBLLVMAttributeListOptions
|
||||
{
|
||||
BBLLVMFunctionAttributes function;
|
||||
BBLLVMArgumentAttributes return_;
|
||||
BBLLVMArgumentAttributes* argument_pointer;
|
||||
u64 argument_count;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BBLLVMAttributeListOptions) == sizeof(BBLLVMFunctionAttributes) + sizeof(BBLLVMArgumentAttributes) + sizeof(void*) + sizeof(u64));
|
||||
|
||||
typedef void* BBLLVMAttributeList;
|
||||
|
||||
enum class DwarfEmissionKind
|
||||
{
|
||||
none,
|
||||
full,
|
||||
line_tables_only,
|
||||
};
|
||||
|
||||
enum class DwarfType
|
||||
{
|
||||
void_type = 0x0,
|
||||
address = 0x1,
|
||||
boolean = 0x2,
|
||||
complex_float = 0x3,
|
||||
float_type = 0x4,
|
||||
signed_type = 0x5,
|
||||
signed_char = 0x6,
|
||||
unsigned_type = 0x7,
|
||||
unsigned_char = 0x8,
|
||||
|
||||
// DWARF 3.
|
||||
imaginary_float = 0x9,
|
||||
packed_decimal = 0xa,
|
||||
numeric_string = 0xb,
|
||||
edited = 0xc,
|
||||
signed_fixed = 0xd,
|
||||
unsigned_fixed = 0xe,
|
||||
decimal_float = 0xf,
|
||||
|
||||
// DWARF 4.
|
||||
UTF = 0x10,
|
||||
|
||||
// DWARF 5.
|
||||
UCS = 0x11,
|
||||
ASCII = 0x12,
|
||||
|
||||
// HP extensions.
|
||||
HP_float80 = 0x80, // Floating-point (80 bit).
|
||||
HP_complex_float80 = 0x81, // Complex floating-point (80 bit).
|
||||
HP_float128 = 0x82, // Floating-point (128 bit).
|
||||
HP_complex_float128 = 0x83, // Complex fp (128 bit).
|
||||
HP_floathpintel = 0x84, // Floating-point (82 bit IA64).
|
||||
HP_imaginary_float80 = 0x85,
|
||||
HP_imaginary_float128 = 0x86,
|
||||
HP_VAX_float = 0x88, // F or G floating.
|
||||
HP_VAX_float_d = 0x89, // D floating.
|
||||
HP_packed_decimal = 0x8a, // Cobol.
|
||||
HP_zoned_decimal = 0x8b, // Cobol.
|
||||
HP_edited = 0x8c, // Cobol.
|
||||
HP_signed_fixed = 0x8d, // Cobol.
|
||||
HP_unsigned_fixed = 0x8e, // Cobol.
|
||||
HP_VAX_complex_float = 0x8f, // F or G floating complex.
|
||||
HP_VAX_complex_float_d = 0x90, // D floating complex.
|
||||
};
|
||||
|
||||
enum class DIFlagsVisibility : u32
|
||||
{
|
||||
none = 0,
|
||||
private_ = 1,
|
||||
protected_ = 2,
|
||||
public_ = 3,
|
||||
};
|
||||
|
||||
enum class DIFlagsInheritance : u32
|
||||
{
|
||||
none = 0,
|
||||
single_ = 1,
|
||||
multiple_ = 2,
|
||||
virtual_ = 3,
|
||||
};
|
||||
|
||||
struct DIFlags
|
||||
{
|
||||
DIFlagsVisibility visibility:2;
|
||||
u32 forward_declaration:1;
|
||||
u32 apple_block:1;
|
||||
u32 block_by_ref_struct:1;
|
||||
u32 virtual_:1;
|
||||
u32 artificial:1;
|
||||
u32 explicit_:1;
|
||||
u32 prototyped:1;
|
||||
u32 objective_c_class_complete:1;
|
||||
u32 object_pointer:1;
|
||||
u32 vector:1;
|
||||
u32 static_member:1;
|
||||
u32 lvalue_reference:1;
|
||||
u32 rvalue_reference:1;
|
||||
u32 reserved:1;
|
||||
DIFlagsInheritance inheritance:2;
|
||||
u32 introduced_virtual:1;
|
||||
u32 bit_field:1;
|
||||
u32 no_return:1;
|
||||
u32 type_pass_by_value:1;
|
||||
u32 type_pass_by_reference:1;
|
||||
u32 enum_class:1;
|
||||
u32 thunk:1;
|
||||
u32 non_trivial:1;
|
||||
u32 big_endian:1;
|
||||
u32 little_endian:1;
|
||||
u32 all_calls_described:1;
|
||||
u32 _:3;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DIFlags) == sizeof(u32));
|
||||
|
||||
enum class LinkageType : u32
|
||||
{
|
||||
external,
|
||||
available_externally,
|
||||
link_once_any,
|
||||
link_once_odr,
|
||||
weak_any,
|
||||
weak_odr,
|
||||
appending,
|
||||
internal,
|
||||
private_,
|
||||
external_weak,
|
||||
common,
|
||||
};
|
||||
|
||||
enum class LLVMCallingConvention : u32
|
||||
{
|
||||
c = 0,
|
||||
fast = 8,
|
||||
cold = 9,
|
||||
ghc = 10,
|
||||
hipe = 11,
|
||||
anyreg = 13,
|
||||
preserve_most = 14,
|
||||
preserve_all = 15,
|
||||
swift = 16,
|
||||
cxx_fast_tls = 17,
|
||||
x86_stdcall = 64,
|
||||
x86_fastcall = 65,
|
||||
arm_apcs = 66,
|
||||
arm_aapcs = 67,
|
||||
arm_aapcsvfp = 68,
|
||||
msp430_interrupt = 69,
|
||||
x86_thiscall = 70,
|
||||
ptx_kernel = 71,
|
||||
ptx_device = 72,
|
||||
spir_func = 75,
|
||||
spir_kernel = 76,
|
||||
intel_oclbi = 77,
|
||||
x86_64_system_v = 78,
|
||||
win64 = 79,
|
||||
x86_vector = 80,
|
||||
hhvm = 81,
|
||||
hhvmc = 82,
|
||||
x86_interrupt = 83,
|
||||
avr_interrupt = 84,
|
||||
avr_signal = 85,
|
||||
avr_builtin = 86,
|
||||
amdgpu_vs = 87,
|
||||
amdgpu_gs = 88,
|
||||
amdgpu_ps = 89,
|
||||
amdgpu_cs = 90,
|
||||
amdgpu_kernel = 91,
|
||||
x86_regcall = 92,
|
||||
amdgpu_hs = 93,
|
||||
msp430_builtin = 94,
|
||||
amgpu_ls = 95,
|
||||
amdgpu_es = 96,
|
||||
};
|
||||
|
||||
fn bool llvm_initialized = false;
|
||||
|
||||
extern "C" void LLVMInitializeX86TargetInfo();
|
||||
extern "C" void LLVMInitializeX86Target();
|
||||
extern "C" void LLVMInitializeX86TargetMC();
|
||||
extern "C" void LLVMInitializeX86AsmPrinter();
|
||||
extern "C" void LLVMInitializeX86AsmParser();
|
||||
extern "C" void LLVMInitializeX86Disassembler();
|
||||
// extern "C" void LLVMInitializeX86TargetInfo();
|
||||
// extern "C" void LLVMInitializeX86Target();
|
||||
// extern "C" void LLVMInitializeX86TargetMC();
|
||||
// extern "C" void LLVMInitializeX86AsmPrinter();
|
||||
// extern "C" void LLVMInitializeX86AsmParser();
|
||||
// extern "C" void LLVMInitializeX86Disassembler();
|
||||
|
||||
extern "C" String llvm_default_target_triple();
|
||||
extern "C" String llvm_host_cpu_name();
|
||||
extern "C" String llvm_host_cpu_features();
|
||||
|
||||
// extern "C" llvm::LLVMContext* LLVMContextCreate();
|
||||
extern "C" LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String name);
|
||||
// extern "C" LLVMB LLVMCreateBuilderInContext(llvm::LLVMContext* context);
|
||||
// extern "C" llvm::Type* LLVMVoidTypeInContext(llvm::LLVMContext* context);
|
||||
// extern "C" llvm::Type* LLVMIntTypeInContext(llvm::LLVMContext* context, u32 bit_count);
|
||||
// extern "C" llvm::Type* LLVMPointerTypeInContext(llvm::LLVMContext* context);
|
||||
// extern "C" llvm::FunctionType* LLVMFunctionType(llvm::Type* return_type, llvm::Type** parameter_type_pointer, u32 parameter_type_count, int is_var_args);
|
||||
// extern "C" LLVMIntrinsicId LLVMLookupIntrinsicID(const u8* name_pointer, u64 name_length);
|
||||
|
||||
extern "C" LLVMValueRef llvm_module_create_function(LLVMModuleRef module, LLVMTypeRef function_type, LinkageType linkage_type, unsigned address_space, String name);
|
||||
// extern "C" void LLVMSetFunctionCallConv(llvm::Function* function, LLVMCallingConvention calling_convention);
|
||||
extern "C" void llvm_function_set_attributes(LLVMValueRef function, BBLLVMAttributeList attribute_list);
|
||||
|
||||
extern "C" LLVMBasicBlockRef llvm_context_create_basic_block(LLVMContextRef context, String name, LLVMValueRef parent_function);
|
||||
// extern "C" void LLVMPositionBuilderAtEnd(llvm::Builder* builder, llvm::BasicBlock* basic_block);
|
||||
// extern "C" llvm::DILocation* LLVMGetCurrentDebugLocation2(llvm::Builder* builder);
|
||||
// extern "C" void LLVMSetCurrentDebugLocation2(llvm::Builder* builder, llvm::DILocation* location);
|
||||
|
||||
extern "C" LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef builder, LLVMTypeRef type, unsigned address_space, String name);
|
||||
|
||||
extern "C" BBLLVMAttributeList llvm_attribute_list_build(LLVMContextRef context, BBLLVMAttributeListOptions* attributes, bool call_site);
|
||||
|
||||
// extern "C" llvm::DIBuilder* LLVMCreateDIBuilder(llvm::Module* module);
|
||||
// extern "C" llvm::DIFile* LLVMDIBuilderCreateFile(llvm::DIBuilder* builder, const u8* file_name_pointer, u64 file_name_length, const u8* directory_name_pointer, u64 directory_name_length);
|
||||
// extern "C" llvm::DICompileUnit* LLVMDIBuilderCreateCompileUnit(llvm::DIBuilder* builder, DwarfSourceLanguage dwarf_source_language, llvm::DIFile* file, const u8* producer_name_pointer, u64 producer_name_length, int is_optimized, const u8* flag_pointer, u64 flag_length, unsigned runtime_version, const u8* split_name_pointer, u64 split_name_length, DwarfEmissionKind emission_kind, unsigned debug_with_offset_id, int split_debug_inlining, int debug_info_for_profiling, const u8* sysroot_name_pointer, u64 sysroot_name_length, const u8* sdk_name_pointer, u64 sdk_name_length);
|
||||
// extern "C" llvm::DIType* LLVMDIBuilderCreateBasicType(llvm::DIBuilder* builder, const u8* name_pointer, u64 name_length, u64 bit_count, DwarfType dwarf_type, DIFlags flags);
|
||||
// extern "C" llvm::DISubroutineType* LLVMDIBuilderCreateSubroutineType(llvm::DIBuilder* builder, llvm::DIFile* file, llvm::DIType** parameter_type_pointer, u32 parameter_type_count, DIFlags flags);
|
||||
// extern "C" llvm::DISubprogram* LLVMDIBuilderCreateFunction(llvm::DIBuilder* builder, llvm::DIScope* scope, const u8* name_pointer, u64 name_length, const u8* linkage_name_pointer, u64 linkage_name_length, llvm::DIFile* File, unsigned line_number, llvm::DISubroutineType* subroutine_type, int is_local_to_unit, int is_definition, unsigned scope_line, DIFlags flags, int is_optimized);
|
||||
|
||||
struct LLVMGlobal
|
||||
{
|
||||
String host_triple;
|
||||
@ -28,9 +396,10 @@ struct LLVMGlobal
|
||||
|
||||
global_variable LLVMGlobal llvm_global;
|
||||
|
||||
fn void initialize_all()
|
||||
fn void llvm_initialize_all_raw()
|
||||
{
|
||||
assert(!llvm_initialized);
|
||||
|
||||
LLVMInitializeX86TargetInfo();
|
||||
LLVMInitializeX86Target();
|
||||
LLVMInitializeX86TargetMC();
|
||||
@ -44,3 +413,11 @@ fn void initialize_all()
|
||||
.host_cpu_features = llvm_host_cpu_features(),
|
||||
};
|
||||
}
|
||||
|
||||
fn void llvm_initialize_all()
|
||||
{
|
||||
if (!llvm_initialized)
|
||||
{
|
||||
llvm_initialize_all_raw();
|
||||
}
|
||||
}
|
||||
|
@ -2804,6 +2804,10 @@ fn Block* parse_block(Module* module, Scope* parent_scope)
|
||||
}
|
||||
|
||||
auto* statement = parse_statement(module, scope);
|
||||
if (!block->first_statement)
|
||||
{
|
||||
block->first_statement = statement;
|
||||
}
|
||||
|
||||
if (current_statement)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user