Compile first executable with self-hosted compiler
All checks were successful
CI / ci (MinSizeRel, ubuntu-latest) (push) Successful in 34s
CI / ci (Release, ubuntu-latest) (push) Successful in 31s
CI / ci (RelWithDebInfo, ubuntu-latest) (push) Successful in 36s
CI / ci (Debug, ubuntu-latest) (push) Successful in 2m46s

This commit is contained in:
David Gonzalez Martin 2025-06-04 21:25:30 -06:00
parent 7ad2bca45c
commit eb88c61f88
12 changed files with 4691 additions and 2672 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ imgui.ini
/.zig-cache/
/zig-out/
/bb-cache/
/self-hosted-bb-cache/

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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];

File diff suppressed because it is too large Load Diff

View File

@ -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?

View File

@ -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);

View File

@ -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
View 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
View 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
View 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
View 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;
}