Compile first executable with self-hosted compiler
All checks were successful
All checks were successful
This commit is contained in:
parent
7ad2bca45c
commit
eb88c61f88
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,3 +13,4 @@ imgui.ini
|
||||
/.zig-cache/
|
||||
/zig-out/
|
||||
/bb-cache/
|
||||
/self-hosted-bb-cache/
|
||||
|
2697
src/compiler.bbb
2697
src/compiler.bbb
File diff suppressed because it is too large
Load Diff
@ -181,12 +181,12 @@ fn String compile_file(Arena* arena, Compile options)
|
||||
|
||||
String c_abi_library = string_literal("build/libc_abi.a");
|
||||
String llvm_bindings_library = string_literal("build/libllvm_bindings.a");
|
||||
Slice<String> library_names = {};
|
||||
Slice<String> library_paths = {};
|
||||
String library_buffer[256];
|
||||
String library_directory = {};
|
||||
|
||||
Slice<String> library_directories = {};
|
||||
String library_directory = {};
|
||||
Slice<String> library_names = {};
|
||||
Slice<String> library_paths = {};
|
||||
|
||||
if (is_compiler)
|
||||
{
|
||||
@ -435,6 +435,10 @@ global_variable String names[] =
|
||||
string_literal("opaque"),
|
||||
string_literal("basic_struct_passing"),
|
||||
string_literal("enum_arbitrary_abi"),
|
||||
string_literal("enum_debug_info"),
|
||||
string_literal("return_array"),
|
||||
string_literal("bool_pair"),
|
||||
string_literal("min_max"),
|
||||
};
|
||||
|
||||
void entry_point(Slice<char* const> arguments, Slice<char* const> envp)
|
||||
|
179
src/compiler.hpp
179
src/compiler.hpp
@ -351,13 +351,16 @@ union AbiRegisterCount
|
||||
AbiRegisterCountSystemV system_v;
|
||||
};
|
||||
|
||||
struct TypeFunction
|
||||
struct TypeFunctionBase
|
||||
{
|
||||
Type* semantic_return_type;
|
||||
Slice<Type*> semantic_argument_types;
|
||||
CallingConvention calling_convention;
|
||||
bool is_variable_arguments;
|
||||
// ABI
|
||||
};
|
||||
|
||||
struct TypeFunctionAbi
|
||||
{
|
||||
Slice<Type*> abi_argument_types;
|
||||
Type* abi_return_type;
|
||||
AbiRegisterCount available_registers;
|
||||
@ -365,6 +368,13 @@ struct TypeFunction
|
||||
AbiInformation return_abi;
|
||||
};
|
||||
|
||||
struct TypeFunction
|
||||
{
|
||||
TypeFunctionBase base;
|
||||
TypeFunctionAbi abi;
|
||||
Type* next;
|
||||
};
|
||||
|
||||
struct TypePointer
|
||||
{
|
||||
Type* element_type;
|
||||
@ -597,6 +607,7 @@ fn u32 get_byte_alignment(Type* type)
|
||||
return result;
|
||||
} break;
|
||||
case TypeId::pointer:
|
||||
case TypeId::opaque:
|
||||
{
|
||||
return 8;
|
||||
} break;
|
||||
@ -617,6 +628,10 @@ fn u32 get_byte_alignment(Type* type)
|
||||
{
|
||||
return get_byte_alignment(type->enum_array.element_type);
|
||||
} break;
|
||||
case TypeId::function:
|
||||
{
|
||||
return 1;
|
||||
} break;
|
||||
default: trap();
|
||||
}
|
||||
}
|
||||
@ -628,6 +643,11 @@ fn u64 get_bit_size(Type* type)
|
||||
case TypeId::integer: return type->integer.bit_count;
|
||||
case TypeId::enumerator: return get_bit_size(type->enumerator.backing_type);
|
||||
case TypeId::alias: return get_bit_size(type->alias.type);
|
||||
case TypeId::array: return get_byte_size(type->array.element_type) * type->array.element_count * 8;
|
||||
case TypeId::pointer: return 64;
|
||||
case TypeId::structure: return type->structure.byte_size * 8;
|
||||
case TypeId::union_type: return type->union_type.byte_size * 8;
|
||||
case TypeId::enum_array: return get_byte_size(type->enum_array.element_type) * type->enum_array.enum_type->enumerator.fields.length * 8;
|
||||
default: trap();
|
||||
}
|
||||
}
|
||||
@ -792,7 +812,7 @@ struct Block
|
||||
enum class ValueId
|
||||
{
|
||||
infer_or_ignore,
|
||||
external_function,
|
||||
forward_declared_function,
|
||||
function,
|
||||
constant_integer,
|
||||
unary,
|
||||
@ -860,6 +880,7 @@ enum class UnaryId
|
||||
bitwise_not,
|
||||
dereference,
|
||||
pointer_from_int,
|
||||
enum_from_int,
|
||||
};
|
||||
|
||||
struct ValueUnary
|
||||
@ -903,6 +924,8 @@ enum class BinaryId
|
||||
logical_or,
|
||||
logical_and_shortcircuit,
|
||||
logical_or_shortcircuit,
|
||||
max,
|
||||
min,
|
||||
};
|
||||
|
||||
struct ValueBinary
|
||||
@ -1076,6 +1099,7 @@ struct Value
|
||||
case ValueId::array_expression:
|
||||
case ValueId::call:
|
||||
case ValueId::select:
|
||||
case ValueId::slice_expression:
|
||||
return false;
|
||||
case ValueId::variable_reference:
|
||||
{
|
||||
@ -1162,7 +1186,11 @@ struct LLVMIntrinsicId
|
||||
|
||||
enum class IntrinsicIndex
|
||||
{
|
||||
smax,
|
||||
smin,
|
||||
trap,
|
||||
umax,
|
||||
umin,
|
||||
va_start,
|
||||
va_end,
|
||||
va_copy,
|
||||
@ -1170,7 +1198,11 @@ enum class IntrinsicIndex
|
||||
};
|
||||
|
||||
global_variable String intrinsic_names[] = {
|
||||
string_literal("llvm.smax"),
|
||||
string_literal("llvm.smin"),
|
||||
string_literal("llvm.trap"),
|
||||
string_literal("llvm.umax"),
|
||||
string_literal("llvm.umin"),
|
||||
string_literal("llvm.va_start"),
|
||||
string_literal("llvm.va_end"),
|
||||
string_literal("llvm.va_copy"),
|
||||
@ -1178,6 +1210,52 @@ global_variable String intrinsic_names[] = {
|
||||
|
||||
static_assert(array_length(intrinsic_names) == (u64)IntrinsicIndex::count);
|
||||
|
||||
struct LLVMAttributeId
|
||||
{
|
||||
u32 n;
|
||||
};
|
||||
|
||||
enum class AttributeIndex
|
||||
{
|
||||
align,
|
||||
alwaysinline,
|
||||
byval,
|
||||
dead_on_unwind,
|
||||
inlinehint,
|
||||
inreg,
|
||||
naked,
|
||||
noalias,
|
||||
noinline,
|
||||
noreturn,
|
||||
nounwind,
|
||||
signext,
|
||||
sret,
|
||||
writable,
|
||||
zeroext,
|
||||
|
||||
count,
|
||||
};
|
||||
|
||||
global_variable String attribute_names[] = {
|
||||
string_literal("align"),
|
||||
string_literal("alwaysinline"),
|
||||
string_literal("byval"),
|
||||
string_literal("dead_on_unwind"),
|
||||
string_literal("inlinehint"),
|
||||
string_literal("inreg"),
|
||||
string_literal("naked"),
|
||||
string_literal("noalias"),
|
||||
string_literal("noinline"),
|
||||
string_literal("noreturn"),
|
||||
string_literal("nounwind"),
|
||||
string_literal("signext"),
|
||||
string_literal("sret"),
|
||||
string_literal("writable"),
|
||||
string_literal("zeroext"),
|
||||
};
|
||||
|
||||
static_assert(array_length(attribute_names) == (u64)AttributeIndex::count);
|
||||
|
||||
struct ModuleLLVM
|
||||
{
|
||||
LLVMContextRef context;
|
||||
@ -1191,6 +1269,7 @@ struct ModuleLLVM
|
||||
LLVMTypeRef pointer_type;
|
||||
LLVMTypeRef void_type;
|
||||
LLVMIntrinsicId intrinsic_table[(u64)IntrinsicIndex::count];
|
||||
LLVMAttributeId attribute_table[(u64)AttributeIndex::count];
|
||||
LLVMValueRef memcmp;
|
||||
LLVMMetadataRef inlined_at;
|
||||
LLVMBasicBlockRef continue_block;
|
||||
@ -1211,6 +1290,7 @@ struct Module
|
||||
Type* first_pair_struct_type;
|
||||
Type* first_array_type;
|
||||
Type* first_enum_array_type;
|
||||
Type* first_function_type;
|
||||
|
||||
Type* va_list_type;
|
||||
|
||||
@ -1864,6 +1944,99 @@ fn Type* get_enum_array_type(Module* module, Type* enum_type, Type* element_type
|
||||
return enum_array_type;
|
||||
}
|
||||
|
||||
fn Type* resolve_alias(Module* module, Type* type)
|
||||
{
|
||||
Type* result_type = 0;
|
||||
switch (type->id)
|
||||
{
|
||||
case TypeId::void_type:
|
||||
case TypeId::noreturn:
|
||||
case TypeId::integer:
|
||||
case TypeId::enumerator:
|
||||
case TypeId::function:
|
||||
case TypeId::bits:
|
||||
case TypeId::union_type:
|
||||
case TypeId::opaque:
|
||||
case TypeId::forward_declaration:
|
||||
{
|
||||
result_type = type;
|
||||
} break;
|
||||
case TypeId::pointer:
|
||||
{
|
||||
auto* element_type = type->pointer.element_type;
|
||||
auto* resolved_element_type = resolve_alias(module, element_type);
|
||||
if (element_type == resolved_element_type)
|
||||
{
|
||||
result_type = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
result_type = get_pointer_type(module, resolved_element_type);
|
||||
}
|
||||
} break;
|
||||
case TypeId::array:
|
||||
{
|
||||
auto* element_type = type->array.element_type;
|
||||
auto element_count = type->array.element_count;
|
||||
assert(element_count);
|
||||
auto* resolved_element_type = resolve_alias(module, element_type);
|
||||
if (element_type == resolved_element_type)
|
||||
{
|
||||
result_type = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
result_type = get_array_type(module, resolved_element_type, element_count);
|
||||
}
|
||||
} break;
|
||||
case TypeId::structure:
|
||||
{
|
||||
if (type->structure.is_slice)
|
||||
{
|
||||
auto old_element_type = type->structure.fields[0].type->pointer.element_type;
|
||||
auto element_type = resolve_alias(module, old_element_type);
|
||||
if (old_element_type == element_type)
|
||||
{
|
||||
result_type = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
result_type = get_slice_type(module, element_type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result_type = type;
|
||||
}
|
||||
} break;
|
||||
case TypeId::alias:
|
||||
{
|
||||
result_type = resolve_alias(module, type->alias.type);
|
||||
} break;
|
||||
case TypeId::enum_array:
|
||||
{
|
||||
auto old_enum_type = type->enum_array.enum_type;
|
||||
auto old_element_type = type->enum_array.element_type;
|
||||
auto enum_type = resolve_alias(module, old_enum_type);
|
||||
auto element_type = resolve_alias(module, old_element_type);
|
||||
|
||||
if (old_enum_type == enum_type && old_element_type == element_type)
|
||||
{
|
||||
result_type = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
result_type = get_enum_array_type(module, enum_type, element_type);
|
||||
}
|
||||
} break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
assert(result_type);
|
||||
return result_type;
|
||||
}
|
||||
|
||||
|
||||
struct ArgBuilder
|
||||
{
|
||||
char* args[128];
|
||||
|
2320
src/emitter.cpp
2320
src/emitter.cpp
File diff suppressed because it is too large
Load Diff
922
src/llvm.cpp
922
src/llvm.cpp
@ -37,12 +37,6 @@ EXPORT LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String n
|
||||
return wrap(module);
|
||||
}
|
||||
|
||||
EXPORT unsigned llvm_integer_type_get_bit_count(const llvm::IntegerType& integer_type)
|
||||
{
|
||||
auto result = integer_type.getBitWidth();
|
||||
return result;
|
||||
}
|
||||
|
||||
EXPORT LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMValueRef before, LLVMThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized)
|
||||
{
|
||||
llvm::GlobalValue::LinkageTypes linkage;
|
||||
@ -63,16 +57,6 @@ EXPORT LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLV
|
||||
return wrap(global);
|
||||
}
|
||||
|
||||
EXPORT void llvm_global_variable_add_debug_info(llvm::GlobalVariable& global, llvm::DIGlobalVariableExpression* debug_global_variable)
|
||||
{
|
||||
global.addDebugInfo(debug_global_variable);
|
||||
}
|
||||
|
||||
EXPORT void llvm_global_variable_delete(llvm::GlobalVariable* global)
|
||||
{
|
||||
delete global;
|
||||
}
|
||||
|
||||
EXPORT void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type)
|
||||
{
|
||||
auto sp = llvm::unwrap<llvm::DISubprogram>(subprogram);
|
||||
@ -119,30 +103,6 @@ EXPORT LLVMBasicBlockRef llvm_context_create_basic_block(LLVMContextRef context,
|
||||
return wrap(basic_block);
|
||||
}
|
||||
|
||||
EXPORT bool llvm_value_has_one_use(LLVMValueRef value)
|
||||
{
|
||||
auto v = llvm::unwrap(value);
|
||||
auto result = v->hasOneUse();
|
||||
return result;
|
||||
}
|
||||
|
||||
EXPORT LLVMValueRef llvm_basic_block_user_begin(LLVMBasicBlockRef basic_block)
|
||||
{
|
||||
llvm::Value* value = *llvm::unwrap(basic_block)->user_begin();
|
||||
return wrap(value);
|
||||
}
|
||||
|
||||
EXPORT void llvm_basic_block_delete(LLVMBasicBlockRef basic_block)
|
||||
{
|
||||
delete llvm::unwrap(basic_block);
|
||||
}
|
||||
|
||||
EXPORT llvm::BranchInst* llvm_value_to_branch(llvm::Value* value)
|
||||
{
|
||||
auto* result = dyn_cast<llvm::BranchInst>(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
// If there are multiple uses of the return-value slot, just check
|
||||
// for something immediately preceding the IP. Sometimes this can
|
||||
// happen with how we generate implicit-returns; it can also happen
|
||||
@ -226,588 +186,6 @@ EXPORT LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef b, LLVMTypeRef typ
|
||||
return wrap(builder.Insert(new llvm::AllocaInst(llvm::unwrap(type), address_space, 0, llvm_alignment), string_ref(name)));
|
||||
}
|
||||
|
||||
enum class BBLLVMAttributeFramePointerKind : u8
|
||||
{
|
||||
None = 0,
|
||||
Reserved = 1,
|
||||
NonLeaf = 2,
|
||||
All = 3,
|
||||
};
|
||||
|
||||
const unsigned BB_LLVM_ONLY_USED = 1U << 1;
|
||||
const unsigned BB_LLVM_ONLY_GPR = 1U << 2;
|
||||
const unsigned BB_LLVM_ONLY_ARG = 1U << 3;
|
||||
|
||||
enum class BBLLVMZeroCallUsedRegsKind : unsigned int
|
||||
{
|
||||
// Don't zero any call-used regs.
|
||||
Skip = 1U << 0,
|
||||
// Only zeros call-used GPRs used in the fn and pass args.
|
||||
UsedGPRArg = BB_LLVM_ONLY_USED | BB_LLVM_ONLY_GPR | BB_LLVM_ONLY_ARG,
|
||||
// Only zeros call-used GPRs used in the fn.
|
||||
UsedGPR = BB_LLVM_ONLY_USED | BB_LLVM_ONLY_GPR,
|
||||
// Only zeros call-used regs used in the fn and pass args.
|
||||
UsedArg = BB_LLVM_ONLY_USED | BB_LLVM_ONLY_ARG,
|
||||
// Only zeros call-used regs used in the fn.
|
||||
Used = BB_LLVM_ONLY_USED,
|
||||
// Zeros all call-used GPRs that pass args.
|
||||
AllGPRArg = BB_LLVM_ONLY_GPR | BB_LLVM_ONLY_ARG,
|
||||
// Zeros all call-used GPRs.
|
||||
AllGPR = BB_LLVM_ONLY_GPR,
|
||||
// Zeros all call-used regs that pass args.
|
||||
AllArg = BB_LLVM_ONLY_ARG,
|
||||
// Zeros all call-used regs.
|
||||
All = 0,
|
||||
};
|
||||
|
||||
enum class BBLLVMFPClassTest : unsigned
|
||||
{
|
||||
None = 0,
|
||||
|
||||
SNan = 0x0001,
|
||||
QNan = 0x0002,
|
||||
NegInf = 0x0004,
|
||||
NegNormal = 0x0008,
|
||||
NegSubnormal = 0x0010,
|
||||
NegZero = 0x0020,
|
||||
PosZero = 0x0040,
|
||||
PosSubnormal = 0x0080,
|
||||
PosNormal = 0x0100,
|
||||
PosInf = 0x0200,
|
||||
|
||||
Nan = SNan | QNan,
|
||||
Inf = PosInf | NegInf,
|
||||
Normal = PosNormal | NegNormal,
|
||||
Subnormal = PosSubnormal | NegSubnormal,
|
||||
Zero = PosZero | NegZero,
|
||||
PosFinite = PosNormal | PosSubnormal | PosZero,
|
||||
NegFinite = NegNormal | NegSubnormal | NegZero,
|
||||
Finite = PosFinite | NegFinite,
|
||||
Positive = PosFinite | PosInf,
|
||||
Negative = NegFinite | NegInf,
|
||||
|
||||
AllFlags = Nan | Inf | Finite,
|
||||
};
|
||||
|
||||
fn llvm::AttributeSet build_argument_attributes(LLVMContextRef context, BBLLVMArgumentAttributes* attributes)
|
||||
{
|
||||
llvm::AttrBuilder builder(*llvm::unwrap(context));
|
||||
|
||||
if (attributes->alignment)
|
||||
{
|
||||
builder.addAlignmentAttr(attributes->alignment);
|
||||
}
|
||||
|
||||
if (attributes->no_alias)
|
||||
{
|
||||
builder.addAttribute(llvm::Attribute::NoAlias);
|
||||
}
|
||||
|
||||
if (attributes->non_null)
|
||||
{
|
||||
builder.addAttribute(llvm::Attribute::NonNull);
|
||||
}
|
||||
|
||||
if (attributes->no_undef)
|
||||
{
|
||||
builder.addAttribute(llvm::Attribute::NoUndef);
|
||||
}
|
||||
|
||||
if (attributes->sign_extend)
|
||||
{
|
||||
builder.addAttribute(llvm::Attribute::SExt);
|
||||
}
|
||||
|
||||
if (attributes->zero_extend)
|
||||
{
|
||||
builder.addAttribute(llvm::Attribute::ZExt);
|
||||
}
|
||||
|
||||
if (attributes->in_reg)
|
||||
{
|
||||
builder.addAttribute(llvm::Attribute::InReg);
|
||||
}
|
||||
|
||||
if (attributes->no_fp_class)
|
||||
{
|
||||
__builtin_trap(); // TODO
|
||||
}
|
||||
|
||||
if (attributes->struct_return)
|
||||
{
|
||||
builder.addStructRetAttr(llvm::unwrap(attributes->semantic_type));
|
||||
}
|
||||
|
||||
if (attributes->writable)
|
||||
{
|
||||
builder.addAttribute(llvm::Attribute::Writable);
|
||||
}
|
||||
|
||||
if (attributes->dead_on_unwind)
|
||||
{
|
||||
builder.addAttribute(llvm::Attribute::DeadOnUnwind);
|
||||
}
|
||||
|
||||
if (attributes->in_alloca)
|
||||
{
|
||||
__builtin_trap(); // TODO
|
||||
}
|
||||
|
||||
if (attributes->dereferenceable)
|
||||
{
|
||||
builder.addDereferenceableAttr(attributes->dereferenceable_bytes);
|
||||
}
|
||||
|
||||
if (attributes->dereferenceable_or_null)
|
||||
{
|
||||
builder.addDereferenceableOrNullAttr(attributes->dereferenceable_bytes);
|
||||
}
|
||||
|
||||
if (attributes->nest)
|
||||
{
|
||||
builder.addAttribute(llvm::Attribute::Nest);
|
||||
}
|
||||
|
||||
if (attributes->by_value)
|
||||
{
|
||||
builder.addByValAttr(llvm::unwrap(attributes->semantic_type));
|
||||
}
|
||||
|
||||
if (attributes->by_reference)
|
||||
{
|
||||
builder.addByRefAttr(llvm::unwrap(attributes->semantic_type));
|
||||
}
|
||||
|
||||
if (attributes->no_capture)
|
||||
{
|
||||
builder.addAttribute(llvm::Attribute::NoCapture);
|
||||
}
|
||||
|
||||
auto attribute_set = llvm::AttributeSet::get(*llvm::unwrap(context), builder);
|
||||
return attribute_set;
|
||||
}
|
||||
|
||||
inline fn BBLLVMAttributeList llvm_attribute_list_to_abi(llvm::AttributeList list)
|
||||
{
|
||||
static_assert(sizeof(llvm::AttributeList) == sizeof(uintptr_t));
|
||||
static_assert(sizeof(BBLLVMAttributeList) == sizeof(uintptr_t));
|
||||
|
||||
return list.getRawPointer();
|
||||
}
|
||||
|
||||
inline fn llvm::AttributeList abi_attribute_list_to_llvm(BBLLVMAttributeList list)
|
||||
{
|
||||
static_assert(sizeof(llvm::AttributeList) == sizeof(uintptr_t));
|
||||
static_assert(sizeof(BBLLVMAttributeList) == sizeof(uintptr_t));
|
||||
auto attribute_list = *(llvm::AttributeList*)&list;
|
||||
return attribute_list;
|
||||
}
|
||||
|
||||
EXPORT BBLLVMAttributeList llvm_attribute_list_build(LLVMContextRef context, BBLLVMAttributeListOptions* attributes, bool call_site)
|
||||
{
|
||||
llvm::AttrBuilder function_attribute_builder(*llvm::unwrap(context));
|
||||
|
||||
if (attributes->function.prefer_vector_width.length)
|
||||
{
|
||||
function_attribute_builder.addAttribute("prefer-vector-width", string_ref(attributes->function.prefer_vector_width));
|
||||
}
|
||||
|
||||
if (attributes->function.stack_protector_buffer_size.length)
|
||||
{
|
||||
function_attribute_builder.addAttribute("stack-protector-buffer-size", string_ref(attributes->function.stack_protector_buffer_size));
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.noreturn)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NoReturn);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.cmse_ns_call)
|
||||
{
|
||||
function_attribute_builder.addAttribute("cmse_nonsecure_call");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.nounwind)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NoUnwind);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.returns_twice)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::ReturnsTwice);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.cold)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::Cold);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.hot)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::Hot);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.no_duplicate)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NoDuplicate);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.convergent)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::Convergent);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.no_merge)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NoMerge);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.will_return)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::WillReturn);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.no_caller_saved_registers)
|
||||
{
|
||||
function_attribute_builder.addAttribute("no-caller-saved-registers");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.no_cf_check)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NoCfCheck);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.no_callback)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NoCallback);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.alloc_size)
|
||||
{
|
||||
__builtin_trap(); // TODO
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.uniform_work_group_size)
|
||||
{
|
||||
__builtin_trap(); // TODO
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_pstate_sm_body)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_pstate_sm_body");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_pstate_sm_enabled)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_pstate_sm_enabled");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_pstate_sm_compatible)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_pstate_sm_compatible");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_preserves_za)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_preserves_za");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_in_za)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_in_za");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_out_za)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_out_za");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_inout_za)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_inout_za");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_preserves_zt0)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_preserves_zt0");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_in_zt0)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_in_zt0");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_out_zt0)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_out_zt0");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.aarch64_inout_zt0)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_inout_zt0");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.optimize_for_size)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::OptimizeForSize);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.min_size)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::MinSize);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.no_red_zone)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NoRedZone);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.indirect_tls_seg_refs)
|
||||
{
|
||||
function_attribute_builder.addAttribute("indirect-tls-seg-refs");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.no_implicit_floats)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NoImplicitFloat);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.sample_profile_suffix_elision_policy)
|
||||
{
|
||||
function_attribute_builder.addAttribute("sample-profile-suffix-elision-policy", "selected");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.memory_none)
|
||||
{
|
||||
function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::none());
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.memory_readonly)
|
||||
{
|
||||
function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::readOnly());
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.memory_inaccessible_or_arg_memory_only)
|
||||
{
|
||||
function_attribute_builder.addMemoryAttr(llvm::MemoryEffects::inaccessibleOrArgMemOnly());
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.memory_arg_memory_only)
|
||||
{
|
||||
llvm::Attribute attribute = function_attribute_builder.getAttribute(llvm::Attribute::Memory);
|
||||
function_attribute_builder.addMemoryAttr(attribute.getMemoryEffects() | llvm::MemoryEffects::argMemOnly());
|
||||
}
|
||||
|
||||
// TODO: branch protection function attributes
|
||||
|
||||
// TODO: cpu features
|
||||
|
||||
if (call_site)
|
||||
{
|
||||
if (attributes->function.flags0.call_no_builtins)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NoBuiltin);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (attributes->function.definition_probe_stack.length)
|
||||
{
|
||||
function_attribute_builder.addAttribute("probe-stack", string_ref(attributes->function.definition_probe_stack));
|
||||
}
|
||||
|
||||
if (attributes->function.definition_stack_probe_size.length)
|
||||
{
|
||||
function_attribute_builder.addAttribute("stack-probe-size", string_ref(attributes->function.definition_stack_probe_size));
|
||||
}
|
||||
|
||||
llvm::StringRef frame_pointer_kind_name;
|
||||
switch ((BBLLVMAttributeFramePointerKind) attributes->function.flags0.definition_frame_pointer_kind)
|
||||
{
|
||||
case BBLLVMAttributeFramePointerKind::None: frame_pointer_kind_name = "none"; break;
|
||||
case BBLLVMAttributeFramePointerKind::Reserved: frame_pointer_kind_name = "reserved"; break;
|
||||
case BBLLVMAttributeFramePointerKind::NonLeaf: frame_pointer_kind_name = "non-leaf"; break;
|
||||
case BBLLVMAttributeFramePointerKind::All: frame_pointer_kind_name = "all"; break;
|
||||
}
|
||||
function_attribute_builder.addAttribute("frame-pointer", frame_pointer_kind_name);
|
||||
|
||||
if (attributes->function.flags0.definition_less_precise_fpmad)
|
||||
{
|
||||
function_attribute_builder.addAttribute("less-precise-fp-mad", "true");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_null_pointer_is_valid)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NullPointerIsValid);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_no_trapping_fp_math)
|
||||
{
|
||||
function_attribute_builder.addAttribute("no-trapping-math", "true");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_no_infs_fp_math)
|
||||
{
|
||||
function_attribute_builder.addAttribute("no-infs-fp-math", "true");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_no_nans_fp_math)
|
||||
{
|
||||
function_attribute_builder.addAttribute("no-nans-fp-math", "true");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_approx_func_fp_math)
|
||||
{
|
||||
function_attribute_builder.addAttribute("approx-func-fp-math", "true");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_unsafe_fp_math)
|
||||
{
|
||||
function_attribute_builder.addAttribute("unsafe-fp-math", "true");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_use_soft_float)
|
||||
{
|
||||
function_attribute_builder.addAttribute("use-soft-float", "true");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_no_signed_zeroes_fp_math)
|
||||
{
|
||||
function_attribute_builder.addAttribute("no-signed-zeros-fp-math", "true");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_stack_realignment)
|
||||
{
|
||||
function_attribute_builder.addAttribute("stackrealign");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_backchain)
|
||||
{
|
||||
function_attribute_builder.addAttribute("backchain");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_split_stack)
|
||||
{
|
||||
function_attribute_builder.addAttribute("split-stack");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_speculative_load_hardening)
|
||||
{
|
||||
function_attribute_builder.addAttribute("split-stack");
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_zero_call_used_registers != ZeroCallUsedRegsKind::all)
|
||||
{
|
||||
__builtin_trap(); // TODO
|
||||
}
|
||||
|
||||
// TODO: denormal builtins
|
||||
|
||||
if (attributes->function.flags0.definition_non_lazy_bind)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::NonLazyBind);
|
||||
}
|
||||
|
||||
if (attributes->function.flags0.definition_cmse_nonsecure_entry)
|
||||
{
|
||||
function_attribute_builder.addAttribute("cmse_nonsecure_entry");
|
||||
}
|
||||
|
||||
llvm::UWTableKind unwind_table_kind;
|
||||
switch ((BBLLVMUWTableKind)attributes->function.flags0.definition_unwind_table_kind)
|
||||
{
|
||||
case BBLLVMUWTableKind::None: unwind_table_kind = llvm::UWTableKind::None; break;
|
||||
case BBLLVMUWTableKind::Sync: unwind_table_kind = llvm::UWTableKind::Sync; break;
|
||||
case BBLLVMUWTableKind::Async: unwind_table_kind = llvm::UWTableKind::Async; break;
|
||||
}
|
||||
|
||||
function_attribute_builder.addUWTableAttr(unwind_table_kind);
|
||||
|
||||
if (attributes->function.flags1.definition_disable_tail_calls)
|
||||
{
|
||||
function_attribute_builder.addAttribute("disable-tail-calls", "true");
|
||||
}
|
||||
|
||||
if (attributes->function.flags1.definition_stack_protect_strong)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::StackProtectStrong);
|
||||
}
|
||||
|
||||
if (attributes->function.flags1.definition_stack_protect)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::StackProtect);
|
||||
}
|
||||
|
||||
if (attributes->function.flags1.definition_stack_protect_req)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::StackProtectReq);
|
||||
}
|
||||
|
||||
if (attributes->function.flags1.definition_aarch64_new_za)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_new_za");
|
||||
}
|
||||
|
||||
if (attributes->function.flags1.definition_aarch64_new_zt0)
|
||||
{
|
||||
function_attribute_builder.addAttribute("aarch64_new_zt0");
|
||||
}
|
||||
|
||||
if (attributes->function.flags1.definition_optimize_none)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::OptimizeNone);
|
||||
}
|
||||
|
||||
if (attributes->function.flags1.definition_naked)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::Naked);
|
||||
}
|
||||
|
||||
if (attributes->function.flags1.definition_inline_hint)
|
||||
{
|
||||
function_attribute_builder.addAttribute(llvm::Attribute::InlineHint);
|
||||
}
|
||||
}
|
||||
|
||||
auto function_attributes = llvm::AttributeSet::get(*llvm::unwrap(context), function_attribute_builder);
|
||||
|
||||
auto return_attributes = build_argument_attributes(context, &attributes->return_);
|
||||
|
||||
llvm::AttributeSet argument_attribute_buffer[128];
|
||||
assert(attributes->argument_count < array_length(argument_attribute_buffer));
|
||||
|
||||
for (u64 i = 0; i < attributes->argument_count; i += 1)
|
||||
{
|
||||
auto attribute_set = build_argument_attributes(context, &attributes->argument_pointer[i]);
|
||||
argument_attribute_buffer[i] = attribute_set;
|
||||
}
|
||||
|
||||
llvm::ArrayRef<llvm::AttributeSet> argument_attributes = llvm::ArrayRef(argument_attribute_buffer, attributes->argument_count);
|
||||
|
||||
auto attribute_list = llvm::AttributeList::get(*llvm::unwrap(context), function_attributes, return_attributes, argument_attributes);
|
||||
|
||||
return llvm_attribute_list_to_abi(attribute_list);
|
||||
}
|
||||
|
||||
EXPORT bool llvm_instruction_is_call_base(llvm::Instruction* instruction)
|
||||
{
|
||||
return isa<llvm::CallBase>(instruction);
|
||||
}
|
||||
|
||||
EXPORT void llvm_function_set_attributes(LLVMValueRef f, BBLLVMAttributeList attribute_list_handle)
|
||||
{
|
||||
auto* function = llvm::unwrap<llvm::Function>(f);
|
||||
auto attribute_list = abi_attribute_list_to_llvm(attribute_list_handle);
|
||||
function->setAttributes(attribute_list);
|
||||
}
|
||||
|
||||
EXPORT void llvm_call_base_set_attributes(LLVMValueRef call_value, BBLLVMAttributeList attribute_list_handle)
|
||||
{
|
||||
auto call = llvm::unwrap<llvm::CallBase>(call_value);
|
||||
auto attribute_list = abi_attribute_list_to_llvm(attribute_list_handle);
|
||||
call->setAttributes(attribute_list);
|
||||
}
|
||||
|
||||
fn String stream_to_string(llvm::raw_string_ostream& stream)
|
||||
{
|
||||
// No need to call stream.flush(); because it's string-based
|
||||
@ -819,8 +197,9 @@ fn String stream_to_string(llvm::raw_string_ostream& stream)
|
||||
u8* result = 0;
|
||||
if (length)
|
||||
{
|
||||
result = new u8[length];
|
||||
result = new u8[length + 1];
|
||||
memcpy(result, string.c_str(), length);
|
||||
result[length] = 0;
|
||||
}
|
||||
|
||||
return String{ result, length };
|
||||
@ -918,7 +297,7 @@ EXPORT String llvm_host_cpu_features()
|
||||
u8* result = 0;
|
||||
if (length)
|
||||
{
|
||||
result = new u8[length];
|
||||
result = new u8[length + 1];
|
||||
memcpy(result, feature_string.c_str(), length + 1);
|
||||
result[length] = 0;
|
||||
}
|
||||
@ -926,293 +305,6 @@ EXPORT String llvm_host_cpu_features()
|
||||
return { result, length };
|
||||
}
|
||||
|
||||
EXPORT LLVMTargetMachineRef llvm_create_target_machine(const BBLLVMTargetMachineCreate* create, String* error_message)
|
||||
{
|
||||
std::string error_message_string;
|
||||
const llvm::Target* target = llvm::TargetRegistry::lookupTarget(string_ref(create->target_triple), error_message_string);
|
||||
|
||||
if (target)
|
||||
{
|
||||
std::optional<llvm::CodeModel::Model> code_model;
|
||||
switch (create->code_model)
|
||||
{
|
||||
case BBLLVMCodeModel::none: code_model = std::nullopt; break;
|
||||
case BBLLVMCodeModel::tiny: code_model = llvm::CodeModel::Tiny; break;
|
||||
case BBLLVMCodeModel::small: code_model = llvm::CodeModel::Small; break;
|
||||
case BBLLVMCodeModel::kernel: code_model = llvm::CodeModel::Kernel; break;
|
||||
case BBLLVMCodeModel::medium: code_model = llvm::CodeModel::Medium; break;
|
||||
case BBLLVMCodeModel::large: code_model = llvm::CodeModel::Large; break;
|
||||
}
|
||||
|
||||
std::optional<llvm::Reloc::Model> relocation_model;
|
||||
|
||||
switch (create->relocation_model)
|
||||
{
|
||||
case BBLLVMRelocationModel::default_relocation: relocation_model = std::nullopt; break;
|
||||
case BBLLVMRelocationModel::static_relocation: relocation_model = llvm::Reloc::Static; break;
|
||||
case BBLLVMRelocationModel::pic: relocation_model = llvm::Reloc::PIC_; break;
|
||||
case BBLLVMRelocationModel::dynamic_no_pic: relocation_model = llvm::Reloc::DynamicNoPIC; break;
|
||||
case BBLLVMRelocationModel::ropi: relocation_model = llvm::Reloc::ROPI; break;
|
||||
case BBLLVMRelocationModel::rwpi: relocation_model = llvm::Reloc::RWPI; break;
|
||||
case BBLLVMRelocationModel::ropi_rwpi: relocation_model = llvm::Reloc::ROPI_RWPI; break;
|
||||
}
|
||||
|
||||
llvm::CodeGenOptLevel optimization_level;
|
||||
switch (create->optimization_level)
|
||||
{
|
||||
case BBLLVMCodeGenerationOptimizationLevel::none: optimization_level = llvm::CodeGenOptLevel::None; break;
|
||||
case BBLLVMCodeGenerationOptimizationLevel::less: optimization_level = llvm::CodeGenOptLevel::Less; break;
|
||||
case BBLLVMCodeGenerationOptimizationLevel::normal: optimization_level = llvm::CodeGenOptLevel::Default; break;
|
||||
case BBLLVMCodeGenerationOptimizationLevel::aggressive: optimization_level = llvm::CodeGenOptLevel::Aggressive; break;
|
||||
}
|
||||
|
||||
// INFO: This calls the default constructor, so all LLVM defaults are set and we only override what we control
|
||||
llvm::TargetOptions target_options;
|
||||
|
||||
target_options.UnsafeFPMath = create->target_options.unsafe_fp_math;
|
||||
target_options.NoInfsFPMath = create->target_options.no_infs_fp_math;
|
||||
target_options.NoNaNsFPMath = create->target_options.no_nans_fp_math;
|
||||
target_options.NoTrappingFPMath = create->target_options.no_trapping_fp_math;
|
||||
target_options.NoSignedZerosFPMath = create->target_options.no_signed_zeroes_fp_math;
|
||||
target_options.ApproxFuncFPMath = create->target_options.approx_func_fp_math;
|
||||
target_options.EnableAIXExtendedAltivecABI = create->target_options.enable_aix_extended_altivec_abi;
|
||||
target_options.HonorSignDependentRoundingFPMathOption = create->target_options.honor_sign_dependent_rounding_fp_math;
|
||||
target_options.NoZerosInBSS = create->target_options.no_zeroes_in_bss;
|
||||
target_options.GuaranteedTailCallOpt = create->target_options.guaranteed_tail_call_optimization;
|
||||
target_options.StackSymbolOrdering = create->target_options.stack_symbol_ordering;
|
||||
target_options.EnableFastISel = create->target_options.enable_fast_isel;
|
||||
target_options.EnableGlobalISel = create->target_options.enable_global_isel;
|
||||
|
||||
auto global_isel_abort_mode = (BBLLVMGlobalISelAbortMode)create->target_options.global_isel_abort_mode;
|
||||
switch (global_isel_abort_mode)
|
||||
{
|
||||
case BBLLVMGlobalISelAbortMode::disable: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::Disable; break;
|
||||
case BBLLVMGlobalISelAbortMode::enable: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::Enable; break;
|
||||
case BBLLVMGlobalISelAbortMode::disable_with_diag: target_options.GlobalISelAbort = llvm::GlobalISelAbortMode::DisableWithDiag; break;
|
||||
}
|
||||
auto swift_async_frame_pointer = (BBLLVMSwiftAsyncFramePointerMode)create->target_options.swift_async_frame_pointer;
|
||||
switch (swift_async_frame_pointer)
|
||||
{
|
||||
case BBLLVMSwiftAsyncFramePointerMode::deployment_based: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::DeploymentBased; break;
|
||||
case BBLLVMSwiftAsyncFramePointerMode::always: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::Always; break;
|
||||
case BBLLVMSwiftAsyncFramePointerMode::never: target_options.SwiftAsyncFramePointer = llvm::SwiftAsyncFramePointerMode::Never; break;
|
||||
}
|
||||
|
||||
target_options.UseInitArray = create->target_options.use_init_array;
|
||||
target_options.DisableIntegratedAS = create->target_options.disable_integrated_assembler;
|
||||
target_options.FunctionSections = create->target_options.function_sections;
|
||||
target_options.DataSections = create->target_options.data_sections;
|
||||
target_options.IgnoreXCOFFVisibility = create->target_options.ignore_xcoff_visibility;
|
||||
target_options.XCOFFTracebackTable = create->target_options.xcoff_traceback_table;
|
||||
target_options.UniqueSectionNames = create->target_options.unique_section_names;
|
||||
target_options.UniqueBasicBlockSectionNames = create->target_options.unique_basic_block_section_names;
|
||||
#if LLVM_VERSION_MAJOR >= 19
|
||||
target_options.SeparateNamedSections = create->target_options.separate_named_sections;
|
||||
#endif
|
||||
target_options.TrapUnreachable = create->target_options.trap_unreachable;
|
||||
target_options.NoTrapAfterNoreturn = create->target_options.no_trap_after_noreturn;
|
||||
target_options.TLSSize = create->target_options.tls_size;
|
||||
target_options.EmulatedTLS = create->target_options.emulated_tls;
|
||||
target_options.EnableTLSDESC = create->target_options.enable_tls_descriptors;
|
||||
target_options.EnableIPRA = create->target_options.enable_ipra;
|
||||
target_options.EmitStackSizeSection = create->target_options.emit_stack_size_section;
|
||||
target_options.EnableMachineOutliner = create->target_options.enable_machine_outliner;
|
||||
target_options.EnableMachineFunctionSplitter = create->target_options.enable_machine_function_splitter;
|
||||
target_options.SupportsDefaultOutlining = create->target_options.supports_default_outlining;
|
||||
target_options.EmitAddrsig = create->target_options.emit_address_significance_table;
|
||||
#if LLVM_VERSION_MAJOR >= 19
|
||||
target_options.BBAddrMap = create->target_options.bb_address_map;
|
||||
#endif
|
||||
|
||||
auto bb_sections = (BBLLVMBasicBlockSection) create->target_options.bb_sections;
|
||||
switch (bb_sections)
|
||||
{
|
||||
case BBLLVMBasicBlockSection::all: target_options.BBSections = llvm::BasicBlockSection::All; break;
|
||||
case BBLLVMBasicBlockSection::list: target_options.BBSections = llvm::BasicBlockSection::List; break;
|
||||
case BBLLVMBasicBlockSection::preset: target_options.BBSections = llvm::BasicBlockSection::Preset; break;
|
||||
case BBLLVMBasicBlockSection::none: target_options.BBSections = llvm::BasicBlockSection::None; break;
|
||||
}
|
||||
|
||||
target_options.EmitCallSiteInfo = create->target_options.emit_call_site_information;
|
||||
target_options.SupportsDebugEntryValues = create->target_options.supports_debug_entry_values;
|
||||
target_options.EnableDebugEntryValues = create->target_options.enable_debug_entry_values;
|
||||
target_options.ValueTrackingVariableLocations = create->target_options.value_tracking_variable_locations;
|
||||
target_options.ForceDwarfFrameSection = create->target_options.force_dwarf_frame_section;
|
||||
target_options.XRayFunctionIndex = create->target_options.xray_function_index;
|
||||
target_options.DebugStrictDwarf = create->target_options.debug_strict_dwarf;
|
||||
target_options.Hotpatch = create->target_options.hotpatch;
|
||||
target_options.PPCGenScalarMASSEntries = create->target_options.ppc_gen_scalar_mass_entries;
|
||||
target_options.JMCInstrument = create->target_options.jmc_instrument;
|
||||
target_options.EnableCFIFixup = create->target_options.enable_cfi_fixup;
|
||||
target_options.MisExpect = create->target_options.mis_expect;
|
||||
target_options.XCOFFReadOnlyPointers = create->target_options.xcoff_read_only_pointers;
|
||||
|
||||
auto float_abi = (BBLLVMFloatAbi) create->target_options.float_abi;
|
||||
switch (float_abi)
|
||||
{
|
||||
case BBLLVMFloatAbi::normal: target_options.FloatABIType = llvm::FloatABI::Default; break;
|
||||
case BBLLVMFloatAbi::soft: target_options.FloatABIType = llvm::FloatABI::Soft; break;
|
||||
case BBLLVMFloatAbi::hard: target_options.FloatABIType = llvm::FloatABI::Hard; break;
|
||||
}
|
||||
|
||||
auto thread_model = (BBLLVMThreadModel) create->target_options.thread_model;
|
||||
switch (thread_model)
|
||||
{
|
||||
case BBLLVMThreadModel::posix: target_options.ThreadModel = llvm::ThreadModel::POSIX; break;
|
||||
case BBLLVMThreadModel::single: target_options.ThreadModel = llvm::ThreadModel::Single; break;
|
||||
}
|
||||
|
||||
auto fp_op_fusion_mode = (BBLLVMFPOpFusion) create->target_options.fp_op_fusion_mode;
|
||||
switch (fp_op_fusion_mode)
|
||||
{
|
||||
case BBLLVMFPOpFusion::fast: target_options.AllowFPOpFusion = llvm::FPOpFusion::Fast; break;
|
||||
case BBLLVMFPOpFusion::standard: target_options.AllowFPOpFusion = llvm::FPOpFusion::Standard; break;
|
||||
case BBLLVMFPOpFusion::strict: target_options.AllowFPOpFusion = llvm::FPOpFusion::Strict; break;
|
||||
}
|
||||
|
||||
auto eabi_version = (BBLLVMEAbi) create->target_options.eabi_version;
|
||||
switch (eabi_version)
|
||||
{
|
||||
case BBLLVMEAbi::unknown: target_options.EABIVersion = llvm::EABI::Unknown; break;
|
||||
case BBLLVMEAbi::normal: target_options.EABIVersion = llvm::EABI::Default; break;
|
||||
case BBLLVMEAbi::eabi4: target_options.EABIVersion = llvm::EABI::EABI4; break;
|
||||
case BBLLVMEAbi::eabi5: target_options.EABIVersion = llvm::EABI::EABI5; break;
|
||||
case BBLLVMEAbi::gnu: target_options.EABIVersion = llvm::EABI::GNU; break;
|
||||
}
|
||||
|
||||
auto debugger_kind = (BBLLVMDebuggerKind) create->target_options.debugger_kind;
|
||||
switch (debugger_kind)
|
||||
{
|
||||
case BBLLVMDebuggerKind::normal: target_options.DebuggerTuning = llvm::DebuggerKind::Default; break;
|
||||
case BBLLVMDebuggerKind::gdb: target_options.DebuggerTuning = llvm::DebuggerKind::GDB; break;
|
||||
case BBLLVMDebuggerKind::lldb: target_options.DebuggerTuning = llvm::DebuggerKind::LLDB; break;
|
||||
case BBLLVMDebuggerKind::sce: target_options.DebuggerTuning = llvm::DebuggerKind::SCE; break;
|
||||
case BBLLVMDebuggerKind::dbx: target_options.DebuggerTuning = llvm::DebuggerKind::DBX; break;
|
||||
}
|
||||
|
||||
auto exception_handling = (BBLLVMExceptionHandling) create->target_options.exception_handling;
|
||||
switch (exception_handling)
|
||||
{
|
||||
case BBLLVMExceptionHandling::none: target_options.ExceptionModel = llvm::ExceptionHandling::None; break;
|
||||
case BBLLVMExceptionHandling::dwarf_cfi: target_options.ExceptionModel = llvm::ExceptionHandling::DwarfCFI; break;
|
||||
case BBLLVMExceptionHandling::setjmp_longjmp: target_options.ExceptionModel = llvm::ExceptionHandling::SjLj; break;
|
||||
case BBLLVMExceptionHandling::arm: target_options.ExceptionModel = llvm::ExceptionHandling::ARM; break;
|
||||
case BBLLVMExceptionHandling::win_eh: target_options.ExceptionModel = llvm::ExceptionHandling::WinEH; break;
|
||||
case BBLLVMExceptionHandling::wasm: target_options.ExceptionModel = llvm::ExceptionHandling::Wasm; break;
|
||||
case BBLLVMExceptionHandling::aix: target_options.ExceptionModel = llvm::ExceptionHandling::AIX; break;
|
||||
case BBLLVMExceptionHandling::zos: target_options.ExceptionModel = llvm::ExceptionHandling::ZOS; break;
|
||||
}
|
||||
|
||||
target_options.LoopAlignment = create->target_options.loop_alignment;
|
||||
target_options.BinutilsVersion = { create->target_options.binutils_version[0], create->target_options.binutils_version[1] };
|
||||
|
||||
if (create->target_options.mc.abi_name.length)
|
||||
{
|
||||
target_options.MCOptions.ABIName = { (char*)create->target_options.mc.abi_name.pointer, create->target_options.mc.abi_name.length };
|
||||
}
|
||||
|
||||
if (create->target_options.mc.assembly_language.length)
|
||||
{
|
||||
target_options.MCOptions.AssemblyLanguage = { (char*)create->target_options.mc.assembly_language.pointer, create->target_options.mc.assembly_language.length };
|
||||
}
|
||||
|
||||
if (create->target_options.mc.split_dwarf_file.length)
|
||||
{
|
||||
target_options.MCOptions.SplitDwarfFile = { (char*)create->target_options.mc.split_dwarf_file.pointer, create->target_options.mc.split_dwarf_file.length };
|
||||
}
|
||||
|
||||
if (create->target_options.mc.as_secure_log_file.length)
|
||||
{
|
||||
target_options.MCOptions.AsSecureLogFile = { (char*)create->target_options.mc.as_secure_log_file.pointer, create->target_options.mc.as_secure_log_file.length };
|
||||
}
|
||||
|
||||
if (create->target_options.mc.argv_count)
|
||||
{
|
||||
target_options.MCOptions.Argv0 = create->target_options.mc.argv0;
|
||||
|
||||
// TODO:
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
if (create->target_options.mc.integrated_assembler_search_path_count)
|
||||
{
|
||||
// TODO:
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
target_options.MCOptions.MCRelaxAll = create->target_options.mc.relax_all;
|
||||
target_options.MCOptions.MCNoExecStack = create->target_options.mc.no_exec_stack;
|
||||
target_options.MCOptions.MCFatalWarnings = create->target_options.mc.fatal_warnings;
|
||||
target_options.MCOptions.MCNoWarn = create->target_options.mc.no_warn;
|
||||
target_options.MCOptions.MCNoDeprecatedWarn = create->target_options.mc.no_deprecated_warn;
|
||||
target_options.MCOptions.MCNoTypeCheck = create->target_options.mc.no_type_check;
|
||||
target_options.MCOptions.MCSaveTempLabels = create->target_options.mc.save_temp_labels;
|
||||
target_options.MCOptions.MCIncrementalLinkerCompatible = create->target_options.mc.incremental_linker_compatible;
|
||||
#if LLVM_VERSION_MAJOR >= 19
|
||||
target_options.MCOptions.FDPIC = create->target_options.mc.fdpic;
|
||||
#endif
|
||||
target_options.MCOptions.ShowMCEncoding = create->target_options.mc.show_mc_encoding;
|
||||
target_options.MCOptions.ShowMCInst = create->target_options.mc.show_mc_inst;
|
||||
target_options.MCOptions.AsmVerbose = create->target_options.mc.asm_verbose;
|
||||
target_options.MCOptions.PreserveAsmComments = create->target_options.mc.preserve_asm_comments;
|
||||
target_options.MCOptions.Dwarf64 = create->target_options.mc.dwarf64;
|
||||
#if LLVM_VERSION_MAJOR >= 19
|
||||
target_options.MCOptions.Crel = create->target_options.mc.crel;
|
||||
target_options.MCOptions.X86RelaxRelocations = create->target_options.mc.x86_relax_relocations;
|
||||
target_options.MCOptions.X86Sse2Avx = create->target_options.mc.x86_sse2_avx;
|
||||
#endif
|
||||
|
||||
auto emit_dwarf_unwind = (BBLLVMEmitDwarfUnwindType) create->target_options.mc.emit_dwarf_unwind;
|
||||
switch (emit_dwarf_unwind)
|
||||
{
|
||||
case BBLLVMEmitDwarfUnwindType::always: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::Always; break;
|
||||
case BBLLVMEmitDwarfUnwindType::no_compact_unwind: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::NoCompactUnwind; break;
|
||||
case BBLLVMEmitDwarfUnwindType::normal: target_options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::Default; break;
|
||||
}
|
||||
|
||||
auto use_dwarf_directory = (BBLLVMDwarfDirectory) create->target_options.mc.use_dwarf_directory;
|
||||
switch (use_dwarf_directory)
|
||||
{
|
||||
case BBLLVMDwarfDirectory::disable: target_options.MCOptions.MCUseDwarfDirectory = llvm::MCTargetOptions::DwarfDirectory::DisableDwarfDirectory; break;
|
||||
case BBLLVMDwarfDirectory::enable: target_options.MCOptions.MCUseDwarfDirectory = llvm::MCTargetOptions::DwarfDirectory::EnableDwarfDirectory; break;
|
||||
case BBLLVMDwarfDirectory::normal: target_options.MCOptions.MCUseDwarfDirectory = llvm::MCTargetOptions::DwarfDirectory::DefaultDwarfDirectory; break;
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 19
|
||||
auto debug_compression_type = (BBLLVMDebugCompressionType) create->target_options.mc.debug_compression_type;
|
||||
switch (debug_compression_type)
|
||||
{
|
||||
case BBLLVMDebugCompressionType::none: target_options.MCOptions.CompressDebugSections = llvm::DebugCompressionType::None; break;
|
||||
case BBLLVMDebugCompressionType::zlib: target_options.MCOptions.CompressDebugSections = llvm::DebugCompressionType::Zlib; break;
|
||||
case BBLLVMDebugCompressionType::zstd: target_options.MCOptions.CompressDebugSections = llvm::DebugCompressionType::Zstd; break;
|
||||
}
|
||||
#endif
|
||||
|
||||
target_options.MCOptions.EmitCompactUnwindNonCanonical = create->target_options.mc.emit_compact_unwind_non_canonical;
|
||||
target_options.MCOptions.PPCUseFullRegisterNames = create->target_options.mc.ppc_use_full_register_names;
|
||||
|
||||
return reinterpret_cast<LLVMTargetMachineRef>(const_cast<llvm::TargetMachine*>(target->createTargetMachine(string_ref(create->target_triple), string_ref(create->cpu_model), string_ref(create->cpu_features), target_options, relocation_model, code_model, optimization_level, create->jit)));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto length = error_message_string.length();
|
||||
auto* result = new u8[length];
|
||||
memcpy(result, error_message_string.c_str(), length);
|
||||
|
||||
*error_message = { result, length };
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void llvm_module_set_target(LLVMModuleRef m, LLVMTargetMachineRef tm)
|
||||
{
|
||||
auto module = llvm::unwrap(m);
|
||||
auto target_machine = (llvm::TargetMachine*)tm;
|
||||
module->setDataLayout(target_machine->createDataLayout());
|
||||
auto& triple_string = target_machine->getTargetTriple().getTriple();
|
||||
module->setTargetTriple(llvm::StringRef(triple_string));
|
||||
}
|
||||
|
||||
EXPORT void llvm_module_run_optimization_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, BBLLVMOptimizationPipelineOptions options)
|
||||
{
|
||||
auto module = llvm::unwrap(m);
|
||||
@ -1344,7 +436,7 @@ EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeli
|
||||
}
|
||||
|
||||
llvm::CodeGenFileType file_type;
|
||||
switch ((BBLLVMCodeGenerationFileType)options->code_generation_file_type)
|
||||
switch (options->file_type)
|
||||
{
|
||||
case BBLLVMCodeGenerationFileType::assembly_file: file_type = llvm::CodeGenFileType::AssemblyFile; break;
|
||||
case BBLLVMCodeGenerationFileType::object_file: file_type = llvm::CodeGenFileType::ObjectFile; break;
|
||||
@ -1397,17 +489,19 @@ fn LLDResult lld_api_generic(lld_api_args(), LinkerFunction linker_function)
|
||||
auto stdout_length = stdout_string.length();
|
||||
if (stdout_length)
|
||||
{
|
||||
auto* stdout_pointer = new u8[stdout_length];
|
||||
auto* stdout_pointer = new u8[stdout_length + 1];
|
||||
memcpy(stdout_pointer, stdout_string.data(), stdout_length);
|
||||
result.stdout_string = { stdout_pointer, stdout_length };
|
||||
stdout_pointer[stdout_length] = 0;
|
||||
}
|
||||
|
||||
auto stderr_length = stderr_string.length();
|
||||
if (stderr_length)
|
||||
{
|
||||
auto* stderr_pointer = new u8[stderr_length];
|
||||
auto* stderr_pointer = new u8[stderr_length + 1];
|
||||
memcpy(stderr_pointer, stderr_string.data(), stderr_length);
|
||||
result.stderr_string = { stderr_pointer, stderr_length };
|
||||
stderr_pointer[stderr_length] = 0;
|
||||
}
|
||||
|
||||
// TODO: should we only call it on success?
|
||||
|
499
src/llvm.hpp
499
src/llvm.hpp
@ -29,20 +29,16 @@ enum class BBLLVMCodeGenerationFileType : u8
|
||||
null = 2,
|
||||
};
|
||||
|
||||
#define BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT (60)
|
||||
|
||||
struct BBLLVMCodeGenerationPipelineOptions
|
||||
{
|
||||
String output_dwarf_file_path;
|
||||
String output_file_path;
|
||||
u64 code_generation_file_type:2;
|
||||
u64 optimize_when_possible:1;
|
||||
u64 verify_module:1;
|
||||
u64 reserved: BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT;
|
||||
BBLLVMCodeGenerationFileType file_type;
|
||||
bool optimize_when_possible;
|
||||
bool verify_module;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BBLLVMCodeGenerationPipelineOptions) == 5 * sizeof(u64));
|
||||
static_assert(BB_LLVM_CODE_GENERATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 60);
|
||||
|
||||
enum class BBLLVMOptimizationLevel : u8
|
||||
{
|
||||
@ -74,186 +70,6 @@ struct BBLLVMOptimizationPipelineOptions
|
||||
static_assert(sizeof(BBLLVMOptimizationPipelineOptions) == sizeof(u64));
|
||||
static_assert(BB_LLVM_OPTIMIZATION_PIPELINE_OPTIONS_PADDING_BIT_COUNT == 51);
|
||||
|
||||
enum class BBLLVMUWTableKind : u64
|
||||
{
|
||||
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(LLVMTypeRef) + 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,
|
||||
@ -301,306 +117,6 @@ enum class DwarfType
|
||||
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 BBLLVMEmitDwarfUnwindType : u8
|
||||
{
|
||||
always = 0,
|
||||
no_compact_unwind = 1,
|
||||
normal = 2,
|
||||
};
|
||||
|
||||
enum class BBLLVMDwarfDirectory : u8
|
||||
{
|
||||
disable = 0,
|
||||
enable = 1,
|
||||
normal = 2,
|
||||
};
|
||||
|
||||
enum class BBLLVMDebugCompressionType : u8
|
||||
{
|
||||
none = 0,
|
||||
zlib = 1,
|
||||
zstd = 2,
|
||||
};
|
||||
|
||||
#define BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT (7)
|
||||
|
||||
struct BBLLVMMCTargetOptions
|
||||
{
|
||||
String abi_name;
|
||||
String assembly_language;
|
||||
String split_dwarf_file;
|
||||
String as_secure_log_file;
|
||||
const char* argv0;
|
||||
String* argv_pointer;
|
||||
u64 argv_count;
|
||||
String* integrated_assembler_search_path_pointer;
|
||||
u64 integrated_assembler_search_path_count;
|
||||
u32 relax_all:1;
|
||||
u32 no_exec_stack:1;
|
||||
u32 fatal_warnings:1;
|
||||
u32 no_warn:1;
|
||||
u32 no_deprecated_warn:1;
|
||||
u32 no_type_check:1;
|
||||
u32 save_temp_labels:1;
|
||||
u32 incremental_linker_compatible:1;
|
||||
u32 fdpic:1;
|
||||
u32 show_mc_encoding:1;
|
||||
u32 show_mc_inst:1;
|
||||
u32 asm_verbose:1;
|
||||
u32 preserve_asm_comments:1 = true;
|
||||
u32 dwarf64:1;
|
||||
u32 crel:1;
|
||||
u32 x86_relax_relocations:1;
|
||||
u32 x86_sse2_avx:1;
|
||||
u32 emit_dwarf_unwind:2 = 2;
|
||||
u32 use_dwarf_directory:2 = 2;
|
||||
u32 debug_compression_type:2 = 0;
|
||||
u32 emit_compact_unwind_non_canonical:1;
|
||||
u32 ppc_use_full_register_names:1;
|
||||
u32 reserved:BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BBLLVMMCTargetOptions) == 112);
|
||||
static_assert(BB_LLVM_MC_TARGET_OPTIONS_PADDING_BIT_COUNT == 7);
|
||||
|
||||
enum class BBLLVMCodeModel : u8
|
||||
{
|
||||
none = 0,
|
||||
tiny = 1,
|
||||
small = 2,
|
||||
kernel = 3,
|
||||
medium = 4,
|
||||
large = 5,
|
||||
};
|
||||
|
||||
enum class BBLLVMRelocationModel : u8
|
||||
{
|
||||
default_relocation = 0,
|
||||
static_relocation = 1,
|
||||
pic = 2,
|
||||
dynamic_no_pic = 3,
|
||||
ropi = 4,
|
||||
rwpi = 5,
|
||||
ropi_rwpi = 6,
|
||||
};
|
||||
|
||||
enum class BBLLVMCodeGenerationOptimizationLevel : u8
|
||||
{
|
||||
none = 0, // -O0
|
||||
less = 1, // -O1
|
||||
normal = 2, // -O2, -Os
|
||||
aggressive = 3 // -O3
|
||||
};
|
||||
|
||||
enum class BBLLVMGlobalISelAbortMode : u8
|
||||
{
|
||||
disable = 0,
|
||||
enable = 1,
|
||||
disable_with_diag = 2,
|
||||
};
|
||||
|
||||
enum class BBLLVMSwiftAsyncFramePointerMode : u8
|
||||
{
|
||||
deployment_based = 0,
|
||||
always = 1,
|
||||
never = 2,
|
||||
};
|
||||
|
||||
enum class BBLLVMBasicBlockSection : u8
|
||||
{
|
||||
all = 0,
|
||||
list = 1,
|
||||
preset = 2,
|
||||
none = 3,
|
||||
};
|
||||
|
||||
enum class BBLLVMFloatAbi : u8
|
||||
{
|
||||
normal = 0,
|
||||
soft = 1,
|
||||
hard = 2,
|
||||
};
|
||||
|
||||
enum class BBLLVMFPOpFusion : u8
|
||||
{
|
||||
fast = 0,
|
||||
standard = 1,
|
||||
strict = 2,
|
||||
};
|
||||
|
||||
enum class BBLLVMThreadModel : u8
|
||||
{
|
||||
posix = 0,
|
||||
single = 1,
|
||||
};
|
||||
|
||||
enum class BBLLVMEAbi : u8
|
||||
{
|
||||
unknown = 0,
|
||||
normal = 1,
|
||||
eabi4 = 2,
|
||||
eabi5 = 3,
|
||||
gnu = 4,
|
||||
};
|
||||
|
||||
enum class BBLLVMDebuggerKind : u8
|
||||
{
|
||||
normal = 0,
|
||||
gdb = 1,
|
||||
lldb = 2,
|
||||
sce = 3,
|
||||
dbx = 4,
|
||||
};
|
||||
|
||||
enum class BBLLVMExceptionHandling : u8
|
||||
{
|
||||
none = 0,
|
||||
dwarf_cfi = 1,
|
||||
setjmp_longjmp = 2,
|
||||
arm = 3,
|
||||
win_eh = 4,
|
||||
wasm = 5,
|
||||
aix = 6,
|
||||
zos = 7,
|
||||
};
|
||||
|
||||
#define BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT (21)
|
||||
|
||||
struct BBLLVMTargetOptions
|
||||
{
|
||||
u64 unsafe_fp_math:1;
|
||||
u64 no_infs_fp_math:1;
|
||||
u64 no_nans_fp_math:1;
|
||||
u64 no_trapping_fp_math:1 = true;
|
||||
u64 no_signed_zeroes_fp_math:1;
|
||||
u64 approx_func_fp_math:1;
|
||||
u64 enable_aix_extended_altivec_abi:1;
|
||||
u64 honor_sign_dependent_rounding_fp_math:1;
|
||||
u64 no_zeroes_in_bss:1;
|
||||
u64 guaranteed_tail_call_optimization:1;
|
||||
u64 stack_symbol_ordering:1 = true;
|
||||
u64 enable_fast_isel:1;
|
||||
u64 enable_global_isel:1 = 1;
|
||||
u64 global_isel_abort_mode:2;
|
||||
u64 swift_async_frame_pointer:2 = 1;
|
||||
u64 use_init_array:1;
|
||||
u64 disable_integrated_assembler:1;
|
||||
u64 function_sections:1;
|
||||
u64 data_sections:1;
|
||||
u64 ignore_xcoff_visibility:1;
|
||||
u64 xcoff_traceback_table:1 = true;
|
||||
u64 unique_section_names:1 = true;
|
||||
u64 unique_basic_block_section_names:1;
|
||||
u64 separate_named_sections:1;
|
||||
u64 trap_unreachable:1;
|
||||
u64 no_trap_after_noreturn:1;
|
||||
u64 tls_size:8;
|
||||
u64 emulated_tls:1;
|
||||
u64 enable_tls_descriptors:1;
|
||||
u64 enable_ipra:1;
|
||||
u64 emit_stack_size_section:1;
|
||||
u64 enable_machine_outliner:1;
|
||||
u64 enable_machine_function_splitter:1;
|
||||
u64 supports_default_outlining:1;
|
||||
u64 emit_address_significance_table:1;
|
||||
u64 bb_address_map:1;
|
||||
u64 bb_sections:3 = 3;
|
||||
u64 emit_call_site_information:1;
|
||||
u64 supports_debug_entry_values:1;
|
||||
u64 enable_debug_entry_values:1;
|
||||
u64 value_tracking_variable_locations:1;
|
||||
u64 force_dwarf_frame_section:1;
|
||||
u64 xray_function_index:1 = true;
|
||||
u64 debug_strict_dwarf:1;
|
||||
u64 hotpatch:1;
|
||||
u64 ppc_gen_scalar_mass_entries:1;
|
||||
u64 jmc_instrument:1;
|
||||
u64 enable_cfi_fixup:1;
|
||||
u64 mis_expect:1;
|
||||
u64 xcoff_read_only_pointers:1;
|
||||
u64 float_abi:2 = 0;
|
||||
u64 thread_model:1 = 0;
|
||||
u32 fp_op_fusion_mode:2 = 1;
|
||||
u32 eabi_version:3 = 1;
|
||||
u32 debugger_kind:3 = 0;
|
||||
u32 exception_handling:3 = 0;
|
||||
u32 reserved:BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT;
|
||||
unsigned loop_alignment = 0;
|
||||
int binutils_version[2];
|
||||
|
||||
BBLLVMMCTargetOptions mc;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BBLLVMTargetOptions) == 136);
|
||||
static_assert(BB_LLVM_TARGET_OPTIONS_PADDING_BIT_COUNT == 21);
|
||||
|
||||
#define BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT (4)
|
||||
|
||||
struct BBLLVMTargetMachineCreate
|
||||
{
|
||||
BBLLVMTargetOptions target_options;
|
||||
String target_triple;
|
||||
String cpu_model;
|
||||
String cpu_features;
|
||||
BBLLVMRelocationModel relocation_model;
|
||||
BBLLVMCodeModel code_model;
|
||||
BBLLVMCodeGenerationOptimizationLevel optimization_level;
|
||||
bool jit;
|
||||
u8 reserved[BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT];
|
||||
};
|
||||
|
||||
static_assert(sizeof(BBLLVMTargetMachineCreate) == 192);
|
||||
static_assert(BB_LLVM_TARGET_MACHINE_CREATE_PADDING_BYTE_COUNT == 4);
|
||||
|
||||
fn bool llvm_initialized = false;
|
||||
|
||||
@ -611,21 +127,14 @@ extern "C" String llvm_host_cpu_features();
|
||||
extern "C" LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String name);
|
||||
|
||||
extern "C" LLVMValueRef llvm_module_create_function(LLVMModuleRef module, LLVMTypeRef function_type, LLVMLinkage linkage_type, unsigned address_space, String name);
|
||||
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" LLVMValueRef llvm_module_create_global_variable(LLVMModuleRef module, LLVMTypeRef type, bool is_constant, LLVMLinkage linkage_type, LLVMValueRef initial_value, String name, LLVMValueRef before, LLVMThreadLocalMode thread_local_mode, unsigned address_space, bool externally_initialized);
|
||||
|
||||
extern "C" LLVMValueRef llvm_builder_create_alloca(LLVMBuilderRef builder, LLVMTypeRef type, unsigned address_space, u32 alignment, String name);
|
||||
extern "C" bool llvm_value_has_one_use(LLVMValueRef value);
|
||||
extern "C" LLVMValueRef llvm_basic_block_user_begin(LLVMBasicBlockRef basic_block);
|
||||
extern "C" void llvm_basic_block_delete(LLVMBasicBlockRef basic_block);
|
||||
extern "C" bool llvm_basic_block_is_empty(LLVMBasicBlockRef basic_block);
|
||||
extern "C" void llvm_function_set_attributes(LLVMValueRef f, BBLLVMAttributeList attribute_list_handle);
|
||||
extern "C" void llvm_call_base_set_attributes(LLVMValueRef call_value, BBLLVMAttributeList attribute_list_handle);
|
||||
|
||||
extern "C" BBLLVMAttributeList llvm_attribute_list_build(LLVMContextRef context, BBLLVMAttributeListOptions* attributes, bool call_site);
|
||||
extern "C" LLVMValueRef llvm_find_return_value_dominating_store(LLVMBuilderRef b, LLVMValueRef ra, LLVMTypeRef et);
|
||||
extern "C" bool llvm_value_use_empty(LLVMValueRef value);
|
||||
extern "C" bool llvm_function_verify(LLVMValueRef function_value, String* error_message);
|
||||
@ -635,8 +144,6 @@ extern "C" void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMet
|
||||
|
||||
extern "C" String llvm_module_to_string(LLVMModuleRef module);
|
||||
|
||||
extern "C" LLVMTargetMachineRef llvm_create_target_machine(const BBLLVMTargetMachineCreate* create, String* error_message);
|
||||
extern "C" void llvm_module_set_target(LLVMModuleRef m, LLVMTargetMachineRef tm);
|
||||
extern "C" void llvm_module_run_optimization_pipeline(LLVMModuleRef module, LLVMTargetMachineRef target_machine, BBLLVMOptimizationPipelineOptions options);
|
||||
extern "C" BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeline(LLVMModuleRef m, LLVMTargetMachineRef tm, const BBLLVMCodeGenerationPipelineOptions* options);
|
||||
|
||||
|
647
src/parser.cpp
647
src/parser.cpp
@ -4,11 +4,14 @@ enum class ValueIntrinsic
|
||||
{
|
||||
align_of,
|
||||
byte_size,
|
||||
enum_from_int,
|
||||
enum_name,
|
||||
extend,
|
||||
integer_max,
|
||||
int_from_enum,
|
||||
int_from_pointer,
|
||||
max,
|
||||
min,
|
||||
pointer_cast,
|
||||
pointer_from_int,
|
||||
select,
|
||||
@ -450,6 +453,280 @@ fn u64 parse_integer_decimal_assume_valid(String string)
|
||||
|
||||
fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder);
|
||||
|
||||
struct FunctionHeaderArgument
|
||||
{
|
||||
String name;
|
||||
u32 line;
|
||||
};
|
||||
|
||||
struct FunctionHeaderParsing
|
||||
{
|
||||
Type* type;
|
||||
Slice<FunctionHeaderArgument> arguments;
|
||||
FunctionAttributes attributes;
|
||||
};
|
||||
|
||||
fn bool type_function_base_compare(Module* module, TypeFunctionBase& a, TypeFunctionBase& b)
|
||||
{
|
||||
auto same_return_type = resolve_alias(module, a.semantic_return_type) == b.semantic_return_type;
|
||||
auto same_calling_convention = a.calling_convention == b.calling_convention;
|
||||
auto same_is_variable_arguments = a.is_variable_arguments == b.is_variable_arguments;
|
||||
|
||||
auto same_argument_length = a.semantic_argument_types.length == b.semantic_argument_types.length;
|
||||
auto same_argument_types = same_argument_length;
|
||||
|
||||
if (same_argument_length)
|
||||
{
|
||||
for (u64 i = 0; i < a.semantic_argument_types.length; i += 1)
|
||||
{
|
||||
auto a_type = resolve_alias(module, a.semantic_argument_types[i]);
|
||||
auto b_type = resolve_alias(module, b.semantic_argument_types[i]);
|
||||
|
||||
auto is_same_argument_type = a_type == b_type;
|
||||
|
||||
same_argument_types = same_argument_types && is_same_argument_type;
|
||||
}
|
||||
}
|
||||
|
||||
return same_return_type && same_calling_convention && same_is_variable_arguments && same_argument_types;
|
||||
}
|
||||
|
||||
fn Type* get_function_type(Module* module, TypeFunctionBase base)
|
||||
{
|
||||
base.semantic_return_type = resolve_alias(module, base.semantic_return_type);
|
||||
for (u64 i = 0; i < base.semantic_argument_types.length; i += 1)
|
||||
{
|
||||
base.semantic_argument_types[i] = resolve_alias(module, base.semantic_argument_types[i]);
|
||||
}
|
||||
|
||||
Type* last_function_type = module->first_function_type;
|
||||
|
||||
while (last_function_type)
|
||||
{
|
||||
assert(last_function_type->id == TypeId::function);
|
||||
if (type_function_base_compare(module, base, last_function_type->function.base))
|
||||
{
|
||||
return last_function_type;
|
||||
}
|
||||
|
||||
auto next = last_function_type->function.next;
|
||||
if (!next)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
last_function_type = next;
|
||||
}
|
||||
|
||||
auto result = type_allocate_init(module, Type{
|
||||
.function = {
|
||||
.base = base,
|
||||
},
|
||||
.id = TypeId::function,
|
||||
.name = string_literal(""),
|
||||
.scope = &module->scope,
|
||||
});
|
||||
|
||||
if (last_function_type)
|
||||
{
|
||||
assert(module->first_function_type);
|
||||
last_function_type->function.next = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(!module->first_function_type);
|
||||
module->first_function_type = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
fn Type* parse_type(Module* module, Scope* scope);
|
||||
fn FunctionHeaderParsing parse_function_header(Module* module, Scope* scope, bool mandate_argument_names)
|
||||
{
|
||||
auto calling_convention = CallingConvention::c;
|
||||
auto function_attributes = FunctionAttributes{};
|
||||
bool is_variable_arguments = false;
|
||||
|
||||
if (consume_character_if_match(module, left_bracket))
|
||||
{
|
||||
while (module->offset < module->content.length)
|
||||
{
|
||||
auto function_identifier = parse_identifier(module);
|
||||
|
||||
enum class FunctionKeyword
|
||||
{
|
||||
cc,
|
||||
count,
|
||||
};
|
||||
|
||||
String function_keywords[] = {
|
||||
string_literal("cc"),
|
||||
};
|
||||
static_assert(array_length(function_keywords) == (u64)FunctionKeyword::count);
|
||||
|
||||
backing_type(FunctionKeyword) i;
|
||||
for (i = 0; i < (backing_type(FunctionKeyword))(FunctionKeyword::count); i += 1)
|
||||
{
|
||||
auto function_keyword = function_keywords[i];
|
||||
if (function_keyword.equal(function_identifier))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto function_keyword = (FunctionKeyword)i;
|
||||
skip_space(module);
|
||||
|
||||
switch (function_keyword)
|
||||
{
|
||||
case FunctionKeyword::cc:
|
||||
{
|
||||
expect_character(module, left_parenthesis);
|
||||
skip_space(module);
|
||||
auto calling_convention_string = parse_identifier(module);
|
||||
String calling_conventions[] = {
|
||||
string_literal("c"),
|
||||
};
|
||||
static_assert(array_length(calling_conventions) == (u64)CallingConvention::count);
|
||||
|
||||
backing_type(CallingConvention) i;
|
||||
for (i = 0; i < (backing_type(CallingConvention))CallingConvention::count; i += 1)
|
||||
{
|
||||
auto calling_convention = calling_conventions[i];
|
||||
if (calling_convention.equal(calling_convention_string))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto candidate_calling_convention = (CallingConvention)i;
|
||||
if (candidate_calling_convention == CallingConvention::count)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
calling_convention = candidate_calling_convention;
|
||||
|
||||
skip_space(module);
|
||||
expect_character(module, right_parenthesis);
|
||||
} break;
|
||||
case FunctionKeyword::count:
|
||||
{
|
||||
report_error();
|
||||
} break;
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, right_bracket))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
expect_character(module, left_parenthesis);
|
||||
|
||||
Type* semantic_argument_type_buffer[64];
|
||||
String semantic_argument_name_buffer[64];
|
||||
u32 argument_line_buffer[64];
|
||||
u32 semantic_argument_count = 0;
|
||||
|
||||
while (module->offset < module->content.length)
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, '.'))
|
||||
{
|
||||
expect_character(module, '.');
|
||||
expect_character(module, '.');
|
||||
skip_space(module);
|
||||
expect_character(module, right_parenthesis);
|
||||
is_variable_arguments = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (consume_character_if_match(module, right_parenthesis))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto line = get_line(module);
|
||||
argument_line_buffer[semantic_argument_count] = line;
|
||||
|
||||
String argument_name = {};
|
||||
|
||||
if (mandate_argument_names)
|
||||
{
|
||||
argument_name = parse_identifier(module);
|
||||
|
||||
skip_space(module);
|
||||
|
||||
expect_character(module, ':');
|
||||
|
||||
skip_space(module);
|
||||
}
|
||||
|
||||
semantic_argument_name_buffer[semantic_argument_count] = argument_name;
|
||||
|
||||
auto argument_type = parse_type(module, scope);
|
||||
semantic_argument_type_buffer[semantic_argument_count] = argument_type;
|
||||
|
||||
skip_space(module);
|
||||
|
||||
unused(consume_character_if_match(module, ','));
|
||||
|
||||
semantic_argument_count += 1;
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
auto return_type = parse_type(module, scope);
|
||||
|
||||
skip_space(module);
|
||||
|
||||
Slice<Type*> argument_types = {};
|
||||
if (semantic_argument_count != 0)
|
||||
{
|
||||
argument_types = new_type_array(module, semantic_argument_count);
|
||||
memcpy(argument_types.pointer, semantic_argument_type_buffer, semantic_argument_count * sizeof(Type*));
|
||||
}
|
||||
|
||||
auto function_type = get_function_type(module, {
|
||||
.semantic_return_type = return_type,
|
||||
.semantic_argument_types = argument_types,
|
||||
.calling_convention = calling_convention,
|
||||
.is_variable_arguments = is_variable_arguments,
|
||||
});
|
||||
|
||||
Slice<FunctionHeaderArgument> arguments = {};
|
||||
if (mandate_argument_names)
|
||||
{
|
||||
arguments = arena_allocate<FunctionHeaderArgument>(module->arena, semantic_argument_count);
|
||||
for (u64 i = 0; i < semantic_argument_count; i += 1)
|
||||
{
|
||||
arguments[i] = {
|
||||
.name = semantic_argument_name_buffer[i],
|
||||
.line = argument_line_buffer[i],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
.type = function_type,
|
||||
.arguments = arguments,
|
||||
.attributes = function_attributes,
|
||||
};
|
||||
}
|
||||
|
||||
fn Type* parse_type(Module* module, Scope* scope)
|
||||
{
|
||||
auto start_character = module->content[module->offset];
|
||||
@ -478,6 +755,14 @@ fn Type* parse_type(Module* module, Scope* scope)
|
||||
auto enum_array_type = get_enum_array_type(module, enum_type, element_type);
|
||||
return enum_array_type;
|
||||
}
|
||||
else if (identifier.equal(string_literal("fn")))
|
||||
{
|
||||
skip_space(module);
|
||||
auto mandate_argument_names = false;
|
||||
auto function_header = parse_function_header(module, scope, mandate_argument_names);
|
||||
auto result = function_header.type;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto is_int_type = identifier.length > 1 && (identifier[0] == 's' || identifier[0] == 'u');
|
||||
@ -667,7 +952,7 @@ fn Type* parse_type(Module* module, Scope* scope)
|
||||
{
|
||||
case TypeIntrinsic::return_type:
|
||||
{
|
||||
auto return_type = module->current_function->variable.type->function.semantic_return_type;
|
||||
auto return_type = module->current_function->variable.type->function.base.semantic_return_type;
|
||||
return return_type;
|
||||
} break;
|
||||
case TypeIntrinsic::count: report_error();
|
||||
@ -767,7 +1052,7 @@ fn u8 escape_character(u8 ch)
|
||||
case 't': return '\t';
|
||||
case 'r': return '\r';
|
||||
case '\'': return '\'';
|
||||
default: trap();
|
||||
default: report_error();
|
||||
}
|
||||
}
|
||||
|
||||
@ -776,6 +1061,7 @@ fn String parse_string_literal(Module* module)
|
||||
expect_character(module, '"');
|
||||
|
||||
auto start = module->offset;
|
||||
u64 escape_character_count = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
@ -784,19 +1070,32 @@ fn String parse_string_literal(Module* module)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (ch == '\\')
|
||||
{
|
||||
trap();
|
||||
}
|
||||
else
|
||||
{
|
||||
module->offset += 1;
|
||||
}
|
||||
escape_character_count += ch == '\\';
|
||||
module->offset += 1;
|
||||
}
|
||||
|
||||
auto end = module->offset;
|
||||
module->offset += 1;
|
||||
auto string_literal = module->content(start, end);
|
||||
auto length = end - start - escape_character_count;
|
||||
auto pointer = (u8*)arena_allocate_bytes(module->arena, length + 1, 1);
|
||||
auto string_literal = String{ .pointer = pointer, .length = length };
|
||||
|
||||
for (u64 source_i = start, i = 0; source_i < end; source_i += 1, i += 1)
|
||||
{
|
||||
auto ch = module->content[source_i];
|
||||
if (ch == '\\')
|
||||
{
|
||||
source_i += 1;
|
||||
ch = module->content[source_i];
|
||||
string_literal[i] = escape_character(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
string_literal[i] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
expect_character(module, '"');
|
||||
|
||||
return string_literal;
|
||||
}
|
||||
|
||||
@ -855,11 +1154,14 @@ fn Token tokenize(Module* module)
|
||||
String value_intrinsics[] = {
|
||||
string_literal("align_of"),
|
||||
string_literal("byte_size"),
|
||||
string_literal("enum_from_int"),
|
||||
string_literal("enum_name"),
|
||||
string_literal("extend"),
|
||||
string_literal("integer_max"),
|
||||
string_literal("int_from_enum"),
|
||||
string_literal("int_from_pointer"),
|
||||
string_literal("max"),
|
||||
string_literal("min"),
|
||||
string_literal("pointer_cast"),
|
||||
string_literal("pointer_from_int"),
|
||||
string_literal("select"),
|
||||
@ -1239,6 +1541,7 @@ fn Value* parse_aggregate_initialization(Module* module, Scope* scope, ValueBuil
|
||||
}
|
||||
|
||||
auto field_index = field_count;
|
||||
auto checkpoint = get_checkpoint(module);
|
||||
if (consume_character_if_match(module, '.'))
|
||||
{
|
||||
auto name = parse_identifier(module);
|
||||
@ -1363,6 +1666,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
||||
|
||||
switch (intrinsic)
|
||||
{
|
||||
case ValueIntrinsic::enum_from_int:
|
||||
case ValueIntrinsic::enum_name:
|
||||
case ValueIntrinsic::extend:
|
||||
case ValueIntrinsic::int_from_enum:
|
||||
@ -1375,6 +1679,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
||||
UnaryId id;
|
||||
switch (intrinsic)
|
||||
{
|
||||
case ValueIntrinsic::enum_from_int: id = UnaryId::enum_from_int; break;
|
||||
case ValueIntrinsic::enum_name: id = UnaryId::enum_name; break;
|
||||
case ValueIntrinsic::extend: id = UnaryId::extend; break;
|
||||
case ValueIntrinsic::int_from_enum: id = UnaryId::int_from_enum; break;
|
||||
@ -1526,6 +1831,37 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
||||
{
|
||||
trap();
|
||||
} break;
|
||||
case ValueIntrinsic::min:
|
||||
case ValueIntrinsic::max:
|
||||
{
|
||||
skip_space(module);
|
||||
expect_character(module, left_parenthesis);
|
||||
skip_space(module);
|
||||
auto left = parse_value(module, scope, {});
|
||||
skip_space(module);
|
||||
expect_character(module, ',');
|
||||
skip_space(module);
|
||||
auto right = parse_value(module, scope, {});
|
||||
skip_space(module);
|
||||
expect_character(module, right_parenthesis);
|
||||
|
||||
BinaryId binary_id;
|
||||
switch (intrinsic)
|
||||
{
|
||||
case ValueIntrinsic::max: binary_id = BinaryId::max; break;
|
||||
case ValueIntrinsic::min: binary_id = BinaryId::min; break;
|
||||
default: unreachable();
|
||||
}
|
||||
|
||||
*result = {
|
||||
.binary = {
|
||||
.left = left,
|
||||
.right = right,
|
||||
.id = binary_id,
|
||||
},
|
||||
.id = ValueId::binary,
|
||||
};
|
||||
} break;
|
||||
case ValueIntrinsic::count: unreachable();
|
||||
}
|
||||
} break;
|
||||
@ -1536,7 +1872,26 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
|
||||
|
||||
skip_space(module);
|
||||
|
||||
if (module->content[module->offset] == '.')
|
||||
auto checkpoint = get_checkpoint(module);
|
||||
bool is_aggregate_initialization = false;
|
||||
if (consume_character_if_match(module, '.'))
|
||||
{
|
||||
auto identifier = parse_identifier(module);
|
||||
|
||||
skip_space(module);
|
||||
is_aggregate_initialization = consume_character_if_match(module, '=');
|
||||
if (!is_aggregate_initialization)
|
||||
{
|
||||
if (!consume_character_if_match(module, ','))
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_checkpoint(module, checkpoint);
|
||||
|
||||
if (is_aggregate_initialization)
|
||||
{
|
||||
result = parse_aggregate_initialization(module, scope, builder, right_bracket);
|
||||
}
|
||||
@ -2819,16 +3174,23 @@ void parse(Module* module)
|
||||
|
||||
auto global_name = parse_identifier(module);
|
||||
|
||||
Global* global_forward_declaration = 0;
|
||||
Global* last_global = module->first_global;
|
||||
while (last_global)
|
||||
{
|
||||
if (global_name.equal(last_global->variable.name))
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
global_forward_declaration = last_global;
|
||||
if (last_global->variable.storage->id != ValueId::forward_declared_function)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
if (last_global->linkage == Linkage::external)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
if (!last_global->next)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2836,14 +3198,14 @@ void parse(Module* module)
|
||||
}
|
||||
|
||||
Type* type_it = module->scope.types.first;
|
||||
Type* forward_declaration = 0;
|
||||
Type* type_forward_declaration = 0;
|
||||
while (type_it)
|
||||
{
|
||||
if (global_name.equal(type_it->name))
|
||||
{
|
||||
if (type_it->id == TypeId::forward_declaration)
|
||||
{
|
||||
forward_declaration = type_it;
|
||||
type_forward_declaration = type_it;
|
||||
break;
|
||||
}
|
||||
else
|
||||
@ -2920,6 +3282,12 @@ void parse(Module* module)
|
||||
}
|
||||
|
||||
auto global_keyword = (GlobalKeyword)i;
|
||||
|
||||
if (global_forward_declaration && global_keyword != GlobalKeyword::function)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
switch (global_keyword)
|
||||
{
|
||||
case GlobalKeyword::bits:
|
||||
@ -3137,206 +3505,73 @@ void parse(Module* module)
|
||||
} break;
|
||||
case GlobalKeyword::function:
|
||||
{
|
||||
auto calling_convention = CallingConvention::c;
|
||||
auto function_attributes = FunctionAttributes{};
|
||||
bool is_variable_arguments = false;
|
||||
auto mandate_argument_names = true;
|
||||
auto function_header = parse_function_header(module, scope, mandate_argument_names);
|
||||
|
||||
if (consume_character_if_match(module, left_bracket))
|
||||
auto function_type = function_header.type;
|
||||
auto function_attributes = function_header.attributes;
|
||||
|
||||
auto semantic_argument_types = function_type->function.base.semantic_argument_types;
|
||||
|
||||
auto pointer_to_function_type = get_pointer_type(module, function_type);
|
||||
|
||||
Global* global = 0;
|
||||
if (global_forward_declaration)
|
||||
{
|
||||
while (module->offset < module->content.length)
|
||||
global = global_forward_declaration;
|
||||
if (global_forward_declaration->variable.type != function_type)
|
||||
{
|
||||
auto function_identifier = parse_identifier(module);
|
||||
|
||||
enum class FunctionKeyword
|
||||
{
|
||||
cc,
|
||||
count,
|
||||
};
|
||||
|
||||
String function_keywords[] = {
|
||||
string_literal("cc"),
|
||||
};
|
||||
static_assert(array_length(function_keywords) == (u64)FunctionKeyword::count);
|
||||
|
||||
backing_type(FunctionKeyword) i;
|
||||
for (i = 0; i < (backing_type(FunctionKeyword))(FunctionKeyword::count); i += 1)
|
||||
{
|
||||
auto function_keyword = function_keywords[i];
|
||||
if (function_keyword.equal(function_identifier))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto function_keyword = (FunctionKeyword)i;
|
||||
skip_space(module);
|
||||
|
||||
switch (function_keyword)
|
||||
{
|
||||
case FunctionKeyword::cc:
|
||||
{
|
||||
expect_character(module, left_parenthesis);
|
||||
skip_space(module);
|
||||
auto calling_convention_string = parse_identifier(module);
|
||||
String calling_conventions[] = {
|
||||
string_literal("c"),
|
||||
};
|
||||
static_assert(array_length(calling_conventions) == (u64)CallingConvention::count);
|
||||
|
||||
backing_type(CallingConvention) i;
|
||||
for (i = 0; i < (backing_type(CallingConvention))CallingConvention::count; i += 1)
|
||||
{
|
||||
auto calling_convention = calling_conventions[i];
|
||||
if (calling_convention.equal(calling_convention_string))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto candidate_calling_convention = (CallingConvention)i;
|
||||
if (candidate_calling_convention == CallingConvention::count)
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
|
||||
calling_convention = candidate_calling_convention;
|
||||
|
||||
skip_space(module);
|
||||
expect_character(module, right_parenthesis);
|
||||
} break;
|
||||
case FunctionKeyword::count:
|
||||
{
|
||||
report_error();
|
||||
} break;
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, right_bracket))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
report_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
expect_character(module, left_parenthesis);
|
||||
|
||||
Type* semantic_argument_type_buffer[64];
|
||||
String semantic_argument_name_buffer[64];
|
||||
u32 argument_line_buffer[64];
|
||||
u32 semantic_argument_count = 0;
|
||||
|
||||
while (module->offset < module->content.length)
|
||||
{
|
||||
skip_space(module);
|
||||
|
||||
if (consume_character_if_match(module, '.'))
|
||||
{
|
||||
expect_character(module, '.');
|
||||
expect_character(module, '.');
|
||||
skip_space(module);
|
||||
expect_character(module, right_parenthesis);
|
||||
is_variable_arguments = true;
|
||||
break;
|
||||
report_error();
|
||||
}
|
||||
|
||||
if (consume_character_if_match(module, right_parenthesis))
|
||||
{
|
||||
break;
|
||||
}
|
||||
assert(global_forward_declaration->variable.storage->type == pointer_to_function_type);
|
||||
|
||||
auto line = get_line(module);
|
||||
argument_line_buffer[semantic_argument_count] = line;
|
||||
|
||||
auto argument_name = parse_identifier(module);
|
||||
semantic_argument_name_buffer[semantic_argument_count] = argument_name;
|
||||
|
||||
skip_space(module);
|
||||
|
||||
expect_character(module, ':');
|
||||
|
||||
skip_space(module);
|
||||
|
||||
auto argument_type = parse_type(module, scope);
|
||||
semantic_argument_type_buffer[semantic_argument_count] = argument_type;
|
||||
|
||||
skip_space(module);
|
||||
|
||||
unused(consume_character_if_match(module, ','));
|
||||
|
||||
semantic_argument_count += 1;
|
||||
global->variable.name = global_name;
|
||||
global->variable.line = global_line;
|
||||
global->variable.column = global_column;
|
||||
}
|
||||
|
||||
skip_space(module);
|
||||
|
||||
auto return_type = parse_type(module, scope);
|
||||
|
||||
skip_space(module);
|
||||
|
||||
Slice<Type*> argument_types = {};
|
||||
if (semantic_argument_count != 0)
|
||||
else
|
||||
{
|
||||
argument_types = new_type_array(module, semantic_argument_count);
|
||||
memcpy(argument_types.pointer, semantic_argument_type_buffer, semantic_argument_count * sizeof(Type*));
|
||||
auto storage = new_value(module);
|
||||
*storage = {
|
||||
.type = pointer_to_function_type,
|
||||
.id = ValueId::forward_declared_function,
|
||||
// TODO? .kind = ValueKind::left,
|
||||
};
|
||||
|
||||
global = new_global(module);
|
||||
*global = {
|
||||
.variable = {
|
||||
.storage = storage,
|
||||
.initial_value = 0,
|
||||
.type = function_type,
|
||||
.scope = scope,
|
||||
.name = global_name,
|
||||
.line = global_line,
|
||||
.column = global_column,
|
||||
},
|
||||
.linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal,
|
||||
};
|
||||
}
|
||||
|
||||
auto is_declaration = consume_character_if_match(module, ';');
|
||||
|
||||
auto function_type = type_allocate_init(module, {
|
||||
.function = {
|
||||
.semantic_return_type = return_type,
|
||||
.semantic_argument_types = argument_types,
|
||||
.calling_convention = calling_convention,
|
||||
.is_variable_arguments = is_variable_arguments,
|
||||
},
|
||||
.id = TypeId::function,
|
||||
.name = string_literal(""),
|
||||
.scope = &module->scope,
|
||||
});
|
||||
|
||||
auto storage = new_value(module);
|
||||
*storage = {
|
||||
.type = get_pointer_type(module, function_type),
|
||||
.id = ValueId::external_function,
|
||||
// TODO? .kind = ValueKind::left,
|
||||
};
|
||||
auto global = new_global(module);
|
||||
*global = {
|
||||
.variable = {
|
||||
.storage = storage,
|
||||
.initial_value = 0,
|
||||
.type = function_type,
|
||||
.scope = scope,
|
||||
.name = global_name,
|
||||
.line = global_line,
|
||||
.column = global_column,
|
||||
},
|
||||
.linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal,
|
||||
};
|
||||
|
||||
if (!is_declaration)
|
||||
if (!consume_character_if_match(module, ';'))
|
||||
{
|
||||
module->current_function = global;
|
||||
Slice<Argument> arguments = arena_allocate<Argument>(module->arena, semantic_argument_count);
|
||||
for (u32 i = 0; i < semantic_argument_count; i += 1)
|
||||
Slice<Argument> arguments = arena_allocate<Argument>(module->arena, semantic_argument_types.length);
|
||||
for (u32 i = 0; i < semantic_argument_types.length; i += 1)
|
||||
{
|
||||
Argument* argument = &arguments[i];
|
||||
auto name = semantic_argument_name_buffer[i];
|
||||
auto* type = semantic_argument_type_buffer[i];
|
||||
auto line = argument_line_buffer[i];
|
||||
auto header_argument = function_header.arguments[i];
|
||||
auto name = header_argument.name;
|
||||
auto* type = semantic_argument_types[i];
|
||||
auto line = header_argument.line;
|
||||
|
||||
*argument = {
|
||||
.variable = {
|
||||
.storage = 0,
|
||||
.initial_value = 0,
|
||||
.type = type,
|
||||
.scope = &storage->function.scope,
|
||||
.scope = &global->variable.storage->function.scope,
|
||||
.name = name,
|
||||
.line = line,
|
||||
.column = 0,
|
||||
@ -3345,7 +3580,7 @@ void parse(Module* module)
|
||||
};
|
||||
}
|
||||
|
||||
storage->function = {
|
||||
global->variable.storage->function = {
|
||||
.arguments = arguments,
|
||||
.scope = {
|
||||
.parent = scope,
|
||||
@ -3356,9 +3591,9 @@ void parse(Module* module)
|
||||
.block = 0,
|
||||
.attributes = function_attributes,
|
||||
};
|
||||
storage->id = ValueId::function;
|
||||
global->variable.storage->id = ValueId::function;
|
||||
|
||||
storage->function.block = parse_block(module, &storage->function.scope);
|
||||
global->variable.storage->function.block = parse_block(module, &global->variable.storage->function.scope);
|
||||
module->current_function = 0;
|
||||
}
|
||||
} break;
|
||||
@ -3528,9 +3763,9 @@ void parse(Module* module)
|
||||
skip_space(module);
|
||||
|
||||
Type* struct_type;
|
||||
if (forward_declaration)
|
||||
if (type_forward_declaration)
|
||||
{
|
||||
struct_type = forward_declaration;
|
||||
struct_type = type_forward_declaration;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3642,9 +3877,9 @@ void parse(Module* module)
|
||||
expect_character(module, left_brace);
|
||||
|
||||
Type* union_type;
|
||||
if (forward_declaration)
|
||||
if (type_forward_declaration)
|
||||
{
|
||||
union_type = forward_declaration;
|
||||
union_type = type_forward_declaration;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
18
tests/bool_pair.bbb
Normal file
18
tests/bool_pair.bbb
Normal file
@ -0,0 +1,18 @@
|
||||
BoolPair = struct
|
||||
{
|
||||
a: u1,
|
||||
b: u1,
|
||||
}
|
||||
|
||||
bool_pair = fn () BoolPair
|
||||
{
|
||||
return { .a = 0, .b = 1 };
|
||||
}
|
||||
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
>result = bool_pair();
|
||||
if (result.a) #trap();
|
||||
if (!result.b) #trap();
|
||||
return 0;
|
||||
}
|
38
tests/enum_debug_info.bbb
Normal file
38
tests/enum_debug_info.bbb
Normal file
@ -0,0 +1,38 @@
|
||||
TypeId = enum
|
||||
{
|
||||
void,
|
||||
noreturn,
|
||||
forward_declaration,
|
||||
integer,
|
||||
function,
|
||||
pointer,
|
||||
array,
|
||||
enum,
|
||||
struct,
|
||||
bits,
|
||||
alias,
|
||||
union,
|
||||
unresolved,
|
||||
vector,
|
||||
floating_point,
|
||||
enum_array,
|
||||
opaque,
|
||||
}
|
||||
|
||||
Type = struct
|
||||
{
|
||||
arr: [5]u32,
|
||||
id: TypeId,
|
||||
a: [2]u64,
|
||||
b: u64,
|
||||
}
|
||||
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
>t: Type = {
|
||||
.id = .integer,
|
||||
zero,
|
||||
};
|
||||
t.arr[0] = 1;
|
||||
return 0;
|
||||
}
|
10
tests/min_max.bbb
Normal file
10
tests/min_max.bbb
Normal file
@ -0,0 +1,10 @@
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
>a: u32 = 1;
|
||||
>b: u32 = 2;
|
||||
>min = #min(a, b);
|
||||
>max = #max(a, b);
|
||||
if (min != a) #trap();
|
||||
if (max != b) #trap();
|
||||
return 0;
|
||||
}
|
22
tests/return_array.bbb
Normal file
22
tests/return_array.bbb
Normal file
@ -0,0 +1,22 @@
|
||||
SomeEnum = enum
|
||||
{
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
f,
|
||||
}
|
||||
|
||||
foo = fn () [2]SomeEnum
|
||||
{
|
||||
return [ .f, .e ];
|
||||
}
|
||||
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
>result = foo();
|
||||
if (result[0] != .f) #trap();
|
||||
if (result[1] != .e) #trap();
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user