1649 lines
60 KiB
C++
1649 lines
60 KiB
C++
#include <compiler.h>
|
|
#include <llvm.h>
|
|
|
|
fn void dump_module(Module* module)
|
|
{
|
|
print(llvm_module_to_string(module->llvm.module));
|
|
}
|
|
|
|
fn bool type_is_signed(Type* type)
|
|
{
|
|
switch (type->id)
|
|
{
|
|
case TypeId::integer:
|
|
return type->integer.is_signed;
|
|
default: unreachable();
|
|
}
|
|
}
|
|
|
|
enum class EvaluationKind
|
|
{
|
|
scalar,
|
|
aggregate,
|
|
complex,
|
|
};
|
|
|
|
enum class TypeKind
|
|
{
|
|
abi,
|
|
memory,
|
|
};
|
|
|
|
fn Type* resolve_alias(Module* module, Type* type)
|
|
{
|
|
Type* result_type = 0;
|
|
switch (type->id)
|
|
{
|
|
case TypeId::integer:
|
|
{
|
|
result_type = type;
|
|
} break;
|
|
case TypeId::pointer:
|
|
{
|
|
auto* element_type = type->pointer.element_type;
|
|
auto* resolved_element_type = resolve_alias(module, element_type);
|
|
result_type = get_pointer_type(module, resolved_element_type);
|
|
} break;
|
|
default: unreachable();
|
|
}
|
|
|
|
assert(result_type);
|
|
return result_type;
|
|
}
|
|
|
|
fn EvaluationKind get_evaluation_kind(Type* type)
|
|
{
|
|
switch (type->id)
|
|
{
|
|
case TypeId::void_type:
|
|
case TypeId::noreturn:
|
|
case TypeId::forward_declaration:
|
|
case TypeId::unresolved:
|
|
case TypeId::function:
|
|
case TypeId::alias:
|
|
unreachable();
|
|
case TypeId::integer:
|
|
case TypeId::pointer:
|
|
case TypeId::bits:
|
|
case TypeId::enumerator:
|
|
return EvaluationKind::scalar;
|
|
case TypeId::array:
|
|
case TypeId::structure:
|
|
case TypeId::union_type:
|
|
return EvaluationKind::aggregate;
|
|
}
|
|
}
|
|
|
|
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 = 0;
|
|
|
|
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();
|
|
}
|
|
|
|
assert(result);
|
|
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 = 0;
|
|
|
|
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();
|
|
}
|
|
|
|
assert(result);
|
|
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 = 0;
|
|
|
|
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;
|
|
case TypeId::pointer:
|
|
{
|
|
resolve_type_in_place_debug(module, type->pointer.element_type);
|
|
if (type->llvm.debug)
|
|
{
|
|
trap_raw();
|
|
}
|
|
else
|
|
{
|
|
result = LLVMDIBuilderCreatePointerType(module->llvm.di_builder, type->pointer.element_type->llvm.debug, 64, 64, 0, (char*)type->name.pointer, type->name.length);
|
|
}
|
|
} break;
|
|
default: unreachable();
|
|
}
|
|
|
|
assert(result);
|
|
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 bool type_is_abi_equal(Module* module, Type* a, Type* b)
|
|
{
|
|
resolve_type_in_place(module, a);
|
|
resolve_type_in_place(module, b);
|
|
|
|
bool result = a == b;
|
|
if (!result)
|
|
{
|
|
result = a->llvm.abi == b->llvm.abi;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
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);
|
|
|
|
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, alignment, options.name);
|
|
return alloca;
|
|
}
|
|
|
|
struct StoreOptions
|
|
{
|
|
LLVMValueRef source;
|
|
LLVMValueRef destination;
|
|
Type* type;
|
|
u32 alignment;
|
|
bool is_valid_alignment;
|
|
};
|
|
|
|
fn void create_store(Module* module, StoreOptions options)
|
|
{
|
|
assert(options.source);
|
|
assert(options.destination);
|
|
assert(options.type);
|
|
|
|
auto resolved_type = resolve_alias(module, options.type);
|
|
resolve_type_in_place(module, resolved_type);
|
|
|
|
LLVMValueRef source_value;
|
|
|
|
LLVMTypeRef memory_type = resolved_type->llvm.memory;
|
|
if (resolved_type->llvm.abi == memory_type)
|
|
{
|
|
source_value = options.source;
|
|
}
|
|
else
|
|
{
|
|
source_value = LLVMBuildIntCast2(module->llvm.builder, options.source, memory_type, type_is_signed(resolved_type), "");
|
|
}
|
|
|
|
u32 alignment;
|
|
if (options.is_valid_alignment)
|
|
{
|
|
alignment = options.alignment;
|
|
}
|
|
else
|
|
{
|
|
alignment = get_byte_alignment(resolved_type);
|
|
}
|
|
|
|
auto store = LLVMBuildStore(module->llvm.builder, source_value, options.destination);
|
|
LLVMSetAlignment(store, alignment);
|
|
}
|
|
|
|
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 check_types(Module* module, Type* expected, Type* source)
|
|
{
|
|
assert(expected);
|
|
assert(source);
|
|
|
|
if (expected != source)
|
|
{
|
|
auto resolved_expected = resolve_alias(module, expected);
|
|
auto resolved_source = resolve_alias(module, source);
|
|
|
|
if (resolved_expected != resolved_source)
|
|
{
|
|
auto is_dst_p_and_source_int = resolved_expected->id == TypeId::pointer && resolved_source->id == TypeId::integer;
|
|
if (!is_dst_p_and_source_int)
|
|
{
|
|
report_error();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn void typecheck(Module* module, Type* expected, Type* source)
|
|
{
|
|
if (expected)
|
|
{
|
|
check_types(module, expected, source);
|
|
}
|
|
}
|
|
|
|
fn bool unary_is_boolean(UnaryId id)
|
|
{
|
|
switch (id)
|
|
{
|
|
case UnaryId::exclamation:
|
|
return true;
|
|
case UnaryId::minus:
|
|
case UnaryId::plus:
|
|
case UnaryId::ampersand:
|
|
case UnaryId::tilde:
|
|
case UnaryId::enum_name:
|
|
case UnaryId::extend:
|
|
case UnaryId::truncate:
|
|
case UnaryId::pointer_cast:
|
|
case UnaryId::int_from_enum:
|
|
case UnaryId::int_from_pointer:
|
|
case UnaryId::va_end:
|
|
case UnaryId::bitwise_not:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
fn bool binary_is_boolean(BinaryId id)
|
|
{
|
|
switch (id)
|
|
{
|
|
case BinaryId::add:
|
|
case BinaryId::sub:
|
|
case BinaryId::mul:
|
|
case BinaryId::div:
|
|
case BinaryId::rem:
|
|
case BinaryId::bitwise_and:
|
|
case BinaryId::bitwise_or:
|
|
case BinaryId::bitwise_xor:
|
|
case BinaryId::shift_left:
|
|
case BinaryId::shift_right:
|
|
return false;
|
|
case BinaryId::compare_equal:
|
|
case BinaryId::compare_not_equal:
|
|
case BinaryId::compare_greater:
|
|
case BinaryId::compare_less:
|
|
case BinaryId::compare_greater_equal:
|
|
case BinaryId::compare_less_equal:
|
|
case BinaryId::logical_and:
|
|
case BinaryId::logical_or:
|
|
case BinaryId::logical_and_shortcircuit:
|
|
case BinaryId::logical_or_shortcircuit:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
fn bool binary_is_shortcircuiting(BinaryId id)
|
|
{
|
|
switch (id)
|
|
{
|
|
case BinaryId::logical_and_shortcircuit:
|
|
case BinaryId::logical_or_shortcircuit:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
fn void analyze_type(Module* module, Value* value, Type* expected_type);
|
|
|
|
fn void analyze_binary_type(Module* module, Value* left, Value* right, bool is_boolean, Type* expected_type)
|
|
{
|
|
auto left_constant = left->is_constant();
|
|
auto right_constant = right->is_constant();
|
|
|
|
if (!expected_type)
|
|
{
|
|
if (left_constant && right_constant)
|
|
{
|
|
if (!left->type && !right->type)
|
|
{
|
|
auto are_string_literal = left->id == ValueId::string_literal && right->id == ValueId::string_literal;
|
|
|
|
if (are_string_literal)
|
|
{
|
|
expected_type = get_slice_type(module, integer_type(module, { .bit_count = 8, .is_signed = false }));
|
|
}
|
|
else
|
|
{
|
|
report_error();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_boolean || !expected_type)
|
|
{
|
|
trap_raw();
|
|
}
|
|
else if (!is_boolean && expected_type)
|
|
{
|
|
analyze_type(module, left, expected_type);
|
|
analyze_type(module, right, expected_type);
|
|
}
|
|
else
|
|
{
|
|
report_error(); // TODO: this might not be an error necessarily?
|
|
}
|
|
|
|
assert(left->type);
|
|
assert(right->type);
|
|
}
|
|
|
|
fn void analyze_type(Module* module, Value* value, Type* expected_type)
|
|
{
|
|
assert(!value->type);
|
|
assert(!value->llvm);
|
|
|
|
if (expected_type && expected_type->id == TypeId::unresolved)
|
|
{
|
|
trap_raw();
|
|
}
|
|
|
|
Type* value_type = 0;
|
|
|
|
switch (value->id)
|
|
{
|
|
case ValueId::constant_integer:
|
|
{
|
|
if (!expected_type)
|
|
{
|
|
report_error();
|
|
}
|
|
|
|
resolve_type_in_place(module, expected_type);
|
|
auto* resolved_type = resolve_alias(module, expected_type);
|
|
switch (resolved_type->id)
|
|
{
|
|
case TypeId::integer:
|
|
{
|
|
if (value->constant_integer.is_signed)
|
|
{
|
|
if (resolved_type->integer.is_signed)
|
|
{
|
|
report_error();
|
|
}
|
|
|
|
trap_raw();
|
|
}
|
|
else
|
|
{
|
|
auto bit_count = resolved_type->integer.bit_count;
|
|
auto max_value = bit_count == 64 ? ~(u64)0 : ((u64)1 << (bit_count - resolved_type->integer.is_signed)) - 1;
|
|
|
|
if (value->constant_integer.value > max_value)
|
|
{
|
|
report_error();
|
|
}
|
|
|
|
value_type = expected_type;
|
|
}
|
|
} break;
|
|
case TypeId::pointer: value_type = integer_type(module, { .bit_count = 64, .is_signed = false }); break;
|
|
default: trap_raw();
|
|
}
|
|
|
|
typecheck(module, expected_type, value_type);
|
|
} break;
|
|
case ValueId::unary:
|
|
{
|
|
auto is_boolean = unary_is_boolean(value->unary.id);
|
|
if (is_boolean)
|
|
{
|
|
trap_raw();
|
|
}
|
|
else
|
|
{
|
|
analyze_type(module, value->unary.value, expected_type);
|
|
value_type = value->unary.value->type;
|
|
typecheck(module, expected_type, value_type);
|
|
}
|
|
} break;
|
|
case ValueId::binary:
|
|
{
|
|
auto is_boolean = binary_is_boolean(value->binary.id);
|
|
analyze_binary_type(module, value->binary.left, value->binary.right, is_boolean, expected_type);
|
|
check_types(module, value->binary.left->type, value->binary.right->type);
|
|
|
|
value_type = is_boolean ? integer_type(module, { .bit_count = 1, .is_signed = false }) : value->binary.left->type;
|
|
} break;
|
|
case ValueId::variable_reference:
|
|
{
|
|
switch (value->kind)
|
|
{
|
|
case ValueKind::left: value_type = value->variable_reference->storage->type; break;
|
|
case ValueKind::right: value_type = value->variable_reference->type; break;
|
|
}
|
|
assert(value_type);
|
|
typecheck(module, expected_type, value_type);
|
|
} break;
|
|
default: unreachable();
|
|
}
|
|
|
|
assert(value_type);
|
|
value->type = value_type;
|
|
}
|
|
|
|
fn LLVMTypeRef get_llvm_type(Type* type, TypeKind type_kind)
|
|
{
|
|
switch (type_kind)
|
|
{
|
|
case TypeKind::abi:
|
|
return type->llvm.abi;
|
|
case TypeKind::memory:
|
|
return type->llvm.memory;
|
|
}
|
|
}
|
|
|
|
fn void emit_value(Module* module, Value* value, TypeKind type_kind)
|
|
{
|
|
assert(value->type);
|
|
assert(!value->llvm);
|
|
auto resolved_type = resolve_alias(module, value->type);
|
|
resolve_type_in_place(module, resolved_type);
|
|
|
|
auto must_be_constant = !module->current_function && !module->current_macro_instantiation;
|
|
|
|
LLVMValueRef llvm_value = 0;
|
|
switch (value->id)
|
|
{
|
|
case ValueId::constant_integer:
|
|
{
|
|
auto llvm_integer_type = get_llvm_type(resolved_type, type_kind);
|
|
llvm_value = LLVMConstInt(llvm_integer_type, value->constant_integer.value, value->constant_integer.is_signed);
|
|
} break;
|
|
case ValueId::unary:
|
|
{
|
|
switch (value->unary.id)
|
|
{
|
|
case UnaryId::minus:
|
|
{
|
|
LLVMValueRef llvm_unary_value;
|
|
if (value->unary.value->llvm)
|
|
{
|
|
assert(false);
|
|
llvm_unary_value = value->unary.value->llvm;
|
|
}
|
|
else
|
|
{
|
|
emit_value(module, value->unary.value, type_kind);
|
|
llvm_unary_value = value->unary.value->llvm;
|
|
}
|
|
|
|
if (value->unary.value->is_constant())
|
|
{
|
|
llvm_value = LLVMConstNeg(llvm_unary_value);
|
|
}
|
|
else
|
|
{
|
|
trap_raw();
|
|
}
|
|
} break;
|
|
case UnaryId::plus:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::ampersand:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::exclamation:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::tilde:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::enum_name:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::extend:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::truncate:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::pointer_cast:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::int_from_enum:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::int_from_pointer:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::va_end:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
case UnaryId::bitwise_not:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
}
|
|
} break;
|
|
case ValueId::binary:
|
|
{
|
|
bool is_shorcircuiting = binary_is_shortcircuiting(value->binary.id);
|
|
|
|
if (is_shorcircuiting)
|
|
{
|
|
trap_raw();
|
|
}
|
|
else
|
|
{
|
|
LLVMValueRef llvm_values[2];
|
|
Value* values[2] = { value->binary.left, value->binary.right };
|
|
for (u64 i = 0; i < array_length(values); i += 1)
|
|
{
|
|
auto* binary_value = values[i];
|
|
LLVMValueRef llvm_value;
|
|
if (binary_value->llvm)
|
|
{
|
|
assert(false); // TODO: check if this if is really necessary
|
|
}
|
|
else
|
|
{
|
|
emit_value(module, binary_value, TypeKind::abi);
|
|
}
|
|
|
|
llvm_values[i] = binary_value->llvm;
|
|
}
|
|
|
|
switch (resolved_type->id)
|
|
{
|
|
case TypeId::integer:
|
|
{
|
|
switch (value->binary.id)
|
|
{
|
|
case BinaryId::add: llvm_value = LLVMBuildAdd(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break;
|
|
case BinaryId::sub: llvm_value = LLVMBuildSub(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break;
|
|
case BinaryId::mul: llvm_value = LLVMBuildMul(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break;
|
|
case BinaryId::bitwise_and: llvm_value = LLVMBuildAnd(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break;
|
|
case BinaryId::bitwise_or: llvm_value = LLVMBuildOr(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break;
|
|
case BinaryId::bitwise_xor: llvm_value = LLVMBuildXor(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break;
|
|
case BinaryId::shift_left: llvm_value = LLVMBuildShl(module->llvm.builder, llvm_values[0], llvm_values[1], ""); break;
|
|
case BinaryId::shift_right:
|
|
if (resolved_type->integer.is_signed)
|
|
{
|
|
llvm_value = LLVMBuildAShr(module->llvm.builder, llvm_values[0], llvm_values[1], "");
|
|
}
|
|
else
|
|
{
|
|
llvm_value = LLVMBuildLShr(module->llvm.builder, llvm_values[0], llvm_values[1], "");
|
|
}
|
|
break;
|
|
case BinaryId::div:
|
|
if (resolved_type->integer.is_signed)
|
|
{
|
|
llvm_value = LLVMBuildSDiv(module->llvm.builder, llvm_values[0], llvm_values[1], "");
|
|
}
|
|
else
|
|
{
|
|
llvm_value = LLVMBuildUDiv(module->llvm.builder, llvm_values[0], llvm_values[1], "");
|
|
}
|
|
break;
|
|
case BinaryId::rem:
|
|
if (resolved_type->integer.is_signed)
|
|
{
|
|
llvm_value = LLVMBuildSRem(module->llvm.builder, llvm_values[0], llvm_values[1], "");
|
|
}
|
|
else
|
|
{
|
|
llvm_value = LLVMBuildURem(module->llvm.builder, llvm_values[0], llvm_values[1], "");
|
|
}
|
|
break;
|
|
default: unreachable();
|
|
}
|
|
} break;
|
|
default: unreachable();
|
|
}
|
|
}
|
|
} break;
|
|
default: unreachable();
|
|
}
|
|
|
|
assert(llvm_value);
|
|
value->llvm = llvm_value;
|
|
}
|
|
|
|
fn void emit_assignment(Module* module, LLVMValueRef left_llvm, Type* left_type, Value* right)
|
|
{
|
|
assert(!right->llvm);
|
|
auto pointer_type = left_type;
|
|
auto value_type = right->type;
|
|
assert(pointer_type);
|
|
assert(value_type);
|
|
resolve_type_in_place(module, pointer_type);
|
|
resolve_type_in_place(module, value_type);
|
|
|
|
auto resolved_pointer_type = resolve_alias(module, pointer_type);
|
|
auto resolved_value_type = resolve_alias(module, value_type);
|
|
assert(resolved_pointer_type->id == TypeId::pointer);
|
|
assert(resolved_pointer_type->pointer.element_type == resolved_value_type);
|
|
|
|
auto type_kind = TypeKind::memory;
|
|
|
|
auto evaluation_kind = get_evaluation_kind(resolved_value_type);
|
|
switch (evaluation_kind)
|
|
{
|
|
case EvaluationKind::scalar:
|
|
{
|
|
emit_value(module, right, type_kind);
|
|
create_store(module, {
|
|
.source = right->llvm,
|
|
.destination = left_llvm,
|
|
.type = resolved_value_type,
|
|
});
|
|
} break;
|
|
default: unreachable();
|
|
}
|
|
}
|
|
|
|
fn void emit_local_storage(Module* module, Variable* variable)
|
|
{
|
|
assert(!variable->storage);
|
|
auto value_type = variable->type;
|
|
resolve_type_in_place(module, value_type);
|
|
auto pointer_type = get_pointer_type(module, value_type);
|
|
auto storage = new_value(module);
|
|
auto alloca = create_alloca(module, {
|
|
.type = value_type,
|
|
.name = variable->name,
|
|
});
|
|
*storage = Value{
|
|
.type = pointer_type,
|
|
.id = ValueId::local,
|
|
.llvm = alloca,
|
|
};
|
|
variable->storage = storage;
|
|
}
|
|
|
|
fn void emit_local_variable(Module* module, Local* local)
|
|
{
|
|
emit_local_storage(module, &local->variable);
|
|
assert(local->variable.storage);
|
|
|
|
if (module->has_debug_info)
|
|
{
|
|
auto debug_type = local->variable.type->llvm.debug;
|
|
assert(debug_type);
|
|
bool always_preserve = true;
|
|
LLVMDIFlags flags = {};
|
|
|
|
auto scope = local->variable.scope->llvm;
|
|
auto bit_alignment = get_byte_alignment(local->variable.storage->type->pointer.element_type) * 8;
|
|
auto local_variable = LLVMDIBuilderCreateAutoVariable(module->llvm.di_builder, scope, (char*)local->variable.name.pointer, local->variable.name.length, module->llvm.file, local->variable.line, debug_type, always_preserve, flags, bit_alignment);
|
|
auto debug_location = LLVMDIBuilderCreateDebugLocation(module->llvm.context, local->variable.line, local->variable.column, scope, module->llvm.inlined_at);
|
|
LLVMSetCurrentDebugLocation2(module->llvm.builder, debug_location);
|
|
auto basic_block = LLVMGetInsertBlock(module->llvm.builder);
|
|
assert(basic_block);
|
|
LLVMDIBuilderInsertDeclareRecordAtEnd(module->llvm.di_builder, local->variable.storage->llvm, local_variable, LLVMDIBuilderCreateExpression(module->llvm.di_builder, 0, 0), debug_location, basic_block);
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
analyze_type(module, return_value, return_abi.semantic_type);
|
|
auto pointer_type = get_pointer_type(module, return_abi.semantic_type);
|
|
emit_assignment(module, return_alloca, pointer_type, return_value);
|
|
} break;
|
|
}
|
|
|
|
auto return_block = module->current_function->variable.storage->function.llvm.return_block;
|
|
LLVMBuildBr(module->llvm.builder, return_block);
|
|
LLVMClearInsertionPosition(module->llvm.builder);
|
|
}
|
|
else if (module->current_macro_instantiation)
|
|
{
|
|
trap_raw();
|
|
}
|
|
else
|
|
{
|
|
report_error();
|
|
}
|
|
} break;
|
|
case StatementId::local:
|
|
{
|
|
auto local = statement->local;
|
|
auto expected_type = local->variable.type;
|
|
assert(!local->variable.storage);
|
|
analyze_type(module, local->variable.initial_value, expected_type);
|
|
local->variable.type = expected_type ? expected_type : local->variable.initial_value->type;
|
|
assert(local->variable.type);
|
|
if (expected_type)
|
|
{
|
|
assert(expected_type == local->variable.type);
|
|
}
|
|
emit_local_variable(module, local);
|
|
emit_assignment(module, local->variable.storage->llvm, local->variable.storage->type, local->variable.initial_value);
|
|
} 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);
|
|
auto return_block = llvm_context_create_basic_block(module->llvm.context, string_literal("return_block"), 0);
|
|
global->variable.storage->function.llvm.return_block = return_block;
|
|
|
|
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);
|
|
|
|
auto* insert_block = LLVMGetInsertBlock(module->llvm.builder);
|
|
if (insert_block)
|
|
{
|
|
trap_raw();
|
|
}
|
|
else
|
|
{
|
|
bool is_reachable = false;
|
|
|
|
if (llvm_value_has_one_use((LLVMValueRef)return_block))
|
|
{
|
|
auto user = llvm_basic_block_user_begin(return_block);
|
|
is_reachable = LLVMIsABranchInst(user) && !LLVMIsConditional(user) && LLVMGetSuccessor(user, 0) == return_block;
|
|
if (is_reachable)
|
|
{
|
|
LLVMPositionBuilderAtEnd(module->llvm.builder, LLVMGetInstructionParent(user));
|
|
LLVMInstructionEraseFromParent(user);
|
|
llvm_basic_block_delete(return_block);
|
|
}
|
|
}
|
|
|
|
if (!is_reachable)
|
|
{
|
|
trap_raw();
|
|
}
|
|
}
|
|
|
|
if (module->has_debug_info)
|
|
{
|
|
LLVMSetCurrentDebugLocation2(module->llvm.builder, 0);
|
|
auto subprogram = LLVMGetSubprogram(llvm_function);
|
|
LLVMDIBuilderFinalizeSubprogram(module->llvm.di_builder, subprogram);
|
|
}
|
|
|
|
if (function_type->return_abi.semantic_type == noreturn_type(module) || global->variable.storage->function.attributes.naked)
|
|
{
|
|
LLVMBuildUnreachable(module->llvm.builder);
|
|
}
|
|
else if (function_type->return_abi.semantic_type == void_type(module))
|
|
{
|
|
LLVMBuildRetVoid(module->llvm.builder);
|
|
}
|
|
else
|
|
{
|
|
LLVMValueRef return_value = 0;
|
|
|
|
switch (return_abi_kind)
|
|
{
|
|
case AbiKind::direct:
|
|
case AbiKind::extend:
|
|
{
|
|
auto return_alloca = global->variable.storage->function.llvm.return_alloca;
|
|
auto coerce_to_type = function_type->return_abi.get_coerce_to_type();
|
|
if (coerce_to_type->id != TypeId::structure && type_is_abi_equal(module, coerce_to_type, function_type->return_abi.semantic_type) && function_type->return_abi.attributes.direct.offset == 0)
|
|
{
|
|
auto store = llvm_find_return_value_dominating_store(module->llvm.builder, return_alloca, function_type->return_abi.semantic_type->llvm.abi);
|
|
if (store)
|
|
{
|
|
return_value = LLVMGetOperand(store, 0);
|
|
auto alloca = LLVMGetOperand(store, 1);
|
|
assert(alloca == return_alloca);
|
|
LLVMInstructionEraseFromParent(store);
|
|
assert(llvm_value_use_empty(alloca));
|
|
LLVMInstructionEraseFromParent(alloca);
|
|
}
|
|
else
|
|
{
|
|
trap_raw();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
trap_raw();
|
|
}
|
|
} break;
|
|
case AbiKind::indirect:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
default: unreachable();
|
|
}
|
|
|
|
LLVMBuildRet(module->llvm.builder, return_value);
|
|
}
|
|
|
|
// END OF SCOPE
|
|
module->current_function = 0;
|
|
}
|
|
} break;
|
|
case ValueId::global:
|
|
{
|
|
trap_raw();
|
|
} break;
|
|
default: report_error();
|
|
}
|
|
}
|
|
|
|
if (module->has_debug_info)
|
|
{
|
|
LLVMDIBuilderFinalize(module->llvm.di_builder);
|
|
}
|
|
|
|
String verification_error_message = {};
|
|
if (!llvm_module_verify(module->llvm.module, &verification_error_message))
|
|
{
|
|
dump_module(module);
|
|
print(verification_error_message);
|
|
print(string_literal("\n"));
|
|
}
|
|
}
|