This commit is contained in:
David Gonzalez Martin 2025-05-06 08:34:42 -06:00
parent c07849aa79
commit 7eff4f9f00
14 changed files with 10011 additions and 911 deletions

View File

@ -21,6 +21,7 @@ add_executable(bb
src/llvm.cpp
)
target_include_directories(bb PUBLIC src)
target_compile_definitions(bb PUBLIC
$<$<CONFIG:Debug>:BB_DEBUG=1>
@ -45,5 +46,9 @@ target_link_libraries(bb PUBLIC
# ${LLD_WASM}
)
#target_compile_options(bb PRIVATE -fsanitize=address)
#target_link_options(bb PRIVATE -fsanitize=address)
target_compile_options(bb PRIVATE -Wall -Wextra -pedantic -Wpedantic -Werror -Wno-c99-extensions -Wno-unused-function -funsigned-char -fwrapv -fno-strict-aliasing)
if (NOT APPLE)
target_compile_options(bb PRIVATE -Wno-missing-designated-field-initializers)
endif()
# target_compile_options(bb PRIVATE -fsanitize=address)
# target_link_options(bb PRIVATE -fsanitize=address)

View File

@ -1,7 +1,34 @@
#!/usr/bin/env bash
set -eu
rm -rf build
mkdir build
cd build
cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_LINKER_TYPE=MOLD -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_PREFIX_PATH=/home/david/dev/llvm/install/llvm_20.1.3_x86_64-linux-Release -DCMAKE_COLOR_DIAGNOSTICS=ON
CMAKE_BUILD_TYPE=Debug
LLVM_CMAKE_BUILD_TYPE=Release
BUILD_DIR=build
BIRTH_NATIVE_OS_STRING=$OSTYPE
case "$BIRTH_NATIVE_OS_STRING" in
darwin*) BIRTH_OS="macos";;
linux*) BIRTH_OS="linux";;
msys*) BIRTH_OS="windows";;
*) exit 1
esac
BIRTH_NATIVE_ARCH_STRING="$(uname -m)"
case "$BIRTH_NATIVE_ARCH_STRING" in
x86_64) BIRTH_ARCH="x86_64";;
arm64) BIRTH_ARCH="aarch64";;
*) exit 1
esac
case "$BIRTH_OS" in
linux) LINKER_TYPE=MOLD;;
*) LINKER_TYPE=DEFAULT;;
esac
rm -rf $BUILD_DIR
mkdir $BUILD_DIR
cd $BUILD_DIR
cmake .. -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_LINKER_TYPE=$LINKER_TYPE -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_PREFIX_PATH=$HOME/dev/llvm/install/llvm_20.1.3_$BIRTH_ARCH-$BIRTH_OS-$LLVM_CMAKE_BUILD_TYPE -DCMAKE_COLOR_DIAGNOSTICS=ON
cd ..

View File

@ -60,7 +60,7 @@ fn void compile(Arena* arena, Options options)
type_it += 1;
auto noreturn_type = type_it;
type_it += 1;
assert(type_it - base_type_allocation.pointer == base_allocation_type_count);
assert((u64)(type_it - base_type_allocation.pointer) == base_allocation_type_count);
previous->next = void_type;
*void_type = {
@ -100,7 +100,7 @@ fn void compile(Arena* arena, Options options)
emit(&module);
}
fn void compile_file(Arena* arena, Compile options)
fn String compile_file(Arena* arena, Compile options)
{
auto relative_file_path = options.relative_file_path;
if (relative_file_path.length < 5)
@ -183,9 +183,12 @@ fn void compile_file(Arena* arena, Compile options)
.has_debug_info = options.has_debug_info,
.silent = options.silent,
});
return output_executable_path;
}
global_variable String names[] = {
global_variable String names[] =
{
string_literal("minimal"),
string_literal("comments"),
string_literal("constant_add"),
@ -260,12 +263,14 @@ global_variable String names[] = {
string_literal("c_string_to_slice"),
string_literal("c_struct_with_array"),
string_literal("c_function_pointer"),
string_literal("basic_bool_call"),
string_literal("c_abi"),
string_literal("string_to_enum"),
string_literal("abi_enum_bool"),
string_literal("empty_if"),
string_literal("else_if"),
string_literal("else_if_complicated"),
string_literal("basic_shortcircuiting_if"),
string_literal("shortcircuiting_if"),
string_literal("field_access_left_assign"),
string_literal("for_each"),
@ -280,10 +285,13 @@ global_variable String names[] = {
string_literal("basic_union"),
string_literal("break_continue"),
string_literal("constant_global_reference"),
string_literal("self_referential_struct"),
string_literal("forward_declared_type"),
// string_literal("self_referential_struct"), // TODO
// string_literal("forward_declared_type"),
string_literal("basic_macro"),
string_literal("generic_macro"),
string_literal("generic_pointer_macro"),
string_literal("noreturn_macro"),
string_literal("generic_pointer_array"),
@ -291,6 +299,7 @@ global_variable String names[] = {
void entry_point(Slice<const char*> arguments, Slice<const char*> environment)
{
unused(environment);
Arena* arena = arena_initialize_default(8 * mb);
if (arguments.length < 2)
@ -393,26 +402,53 @@ void entry_point(Slice<const char*> arguments, Slice<const char*> environment)
fail_with_message(string_literal("error: 'test' command takes no arguments"));
}
// TODO: introduce build mode, debug info switch, etc
bool has_debug_info_array[] = {true, false};
String test_matrix[array_length(names)][build_mode_count][2];
auto name_i = 0;
for (auto name: names)
{
auto position = arena->position;
auto build_mode_i = 0;
for (BuildMode build_mode = BuildMode::debug_none; build_mode < BuildMode::count; build_mode = (BuildMode)((backing_type(BuildMode))build_mode + 1))
{
for (bool has_debug_info : has_debug_info_array)
{
auto position = arena->position;
String relative_file_path_parts[] = { string_literal("tests/"), name, string_literal(".bbb") };
auto relative_file_path = arena_join_string(arena, array_to_slice(relative_file_path_parts));
auto build_mode = BuildMode::debug_none;
bool has_debug_info = true;
compile_file(arena, {
.relative_file_path = relative_file_path,
.build_mode = build_mode,
.has_debug_info = has_debug_info,
.silent = false,
});
String relative_file_path_parts[] = { string_literal("tests/"), name, string_literal(".bbb") };
auto relative_file_path = arena_join_string(arena, array_to_slice(relative_file_path_parts));
// TODO: introduce test
auto executable_path = compile_file(arena, {
.relative_file_path = relative_file_path,
.build_mode = build_mode,
.has_debug_info = has_debug_info,
.silent = true,
});
arena_restore(arena, position);
test_matrix[name_i][build_mode_i][!has_debug_info] = executable_path;
arena_restore(arena, position);
}
build_mode_i += 1;
}
name_i += 1;
}
print(string_literal("Compiled tests successfully!\nRunning tests...\n"));
u64 test_count = 0;
for (auto& a : test_matrix)
{
for (auto& b: a)
{
for (String executable_path : b)
{
test_count += 1;
}
}
}
} break;
case Command::count:

View File

@ -1,8 +1,8 @@
#pragma once
#include <lib.h>
#define report_error() trap_raw()
#include <llvm-c/Types.h>
#define report_error() trap()
enum class Command
{
@ -23,6 +23,8 @@ enum class BuildMode
count,
};
global_variable constexpr u64 build_mode_count = (u64)BuildMode::count;
fn String build_mode_to_string(BuildMode build_mode)
{
switch (build_mode)
@ -38,6 +40,23 @@ fn String build_mode_to_string(BuildMode build_mode)
}
}
fn bool build_mode_is_optimized(BuildMode build_mode)
{
switch (build_mode)
{
case BuildMode::debug_none:
case BuildMode::debug:
return false;
case BuildMode::soft_optimize:
case BuildMode::optimize_for_speed:
case BuildMode::optimize_for_size:
case BuildMode::aggressively_optimize_for_speed:
case BuildMode::aggressively_optimize_for_size:
return true;
case BuildMode::count: unreachable();
}
}
enum class ValueKind
{
right,
@ -65,6 +84,165 @@ struct Argument;
struct Scope;
struct MacroDeclaration;
struct DirectAttributes
{
u32 offset;
u32 alignment;
};
struct IndirectAttributes
{
u32 alignment;
u32 address_space;
};
enum class AbiKind : u8
{
ignore,
direct,
extend,
indirect,
indirect_aliased,
expand,
coerce_and_expand,
in_alloca,
};
struct AbiFlags
{
AbiKind kind;
bool padding_in_reg;
bool in_alloca_sret;
bool in_alloca_indirect;
bool indirect_by_value;
bool indirect_realign;
bool sret_after_this;
bool in_reg;
bool can_be_flattened;
bool sign_extension;
};
struct AbiInformation
{
Type* semantic_type;
Type* coerce_to_type;
union
{
Type* type;
Type* unpadded_coerce_and_expand_type;
} padding;
u16 padding_argument_index;
union
{
DirectAttributes direct;
IndirectAttributes indirect;
u32 alloca_field_index;
} attributes;
AbiFlags flags;
u16 abi_start;
u16 abi_count;
inline void set_sret_after_this(bool sret_after_this)
{
assert(flags.kind == AbiKind::indirect);
flags.sret_after_this = sret_after_this;
}
inline void set_indirect_realign(bool realign)
{
assert(flags.kind == AbiKind::indirect);
flags.indirect_realign = realign;
}
inline void set_indirect_by_value(bool by_value)
{
assert(flags.kind == AbiKind::indirect);
flags.indirect_by_value = by_value;
}
inline void set_indirect_align(u32 alignment)
{
assert(flags.kind == AbiKind::indirect || flags.kind == AbiKind::indirect_aliased);
attributes.indirect.alignment = alignment;
}
inline bool can_have_coerce_to_type()
{
switch (flags.kind)
{
case AbiKind::direct:
case AbiKind::extend:
case AbiKind::coerce_and_expand:
return true;
default:
return false;
}
}
inline void set_coerce_to_type(Type* coerce_to_type)
{
assert(can_have_coerce_to_type());
this->coerce_to_type = coerce_to_type;
}
inline Type* get_coerce_to_type()
{
assert(can_have_coerce_to_type());
return coerce_to_type;
}
inline void set_padding_type(Type* padding_type)
{
assert(can_have_padding_type());
padding = {
.type = padding_type,
};
}
inline bool can_have_padding_type()
{
switch (flags.kind)
{
case AbiKind::direct:
case AbiKind::extend:
case AbiKind::indirect:
case AbiKind::indirect_aliased:
case AbiKind::expand:
return true;
default:
return false;
}
}
inline Type* get_padding_type()
{
return can_have_padding_type() ? padding.type : 0;
}
inline void set_direct_offset(u32 offset)
{
assert(flags.kind == AbiKind::direct || flags.kind == AbiKind::extend);
attributes.direct.offset = offset;
}
inline void set_direct_alignment(u32 alignment)
{
assert(flags.kind == AbiKind::direct || flags.kind == AbiKind::extend);
attributes.direct.alignment = alignment;
}
inline void set_can_be_flattened(bool can_be_flattened)
{
assert(flags.kind == AbiKind::direct);
flags.can_be_flattened = can_be_flattened;
}
inline bool get_can_be_flattened()
{
return flags.can_be_flattened;
}
};
struct Target
{
CPUArchitecture cpu;
@ -87,6 +265,24 @@ enum class CallingConvention
count,
};
enum class ResolvedCallingConvention
{
system_v,
win64,
count,
};
fn ResolvedCallingConvention resolve_calling_convention(CallingConvention cc)
{
switch (cc)
{
case CallingConvention::c:
// TODO:
return ResolvedCallingConvention::system_v;
case CallingConvention::count: unreachable();
}
}
enum class InlineBehavior
{
normal,
@ -116,6 +312,8 @@ enum class TypeId
alias,
union_type,
unresolved,
vector,
floating_point,
};
struct TypeInteger
@ -124,12 +322,29 @@ struct TypeInteger
bool is_signed;
};
struct AbiRegisterCountSystemV
{
u32 gpr;
u32 sse;
};
union AbiRegisterCount
{
AbiRegisterCountSystemV system_v;
};
struct TypeFunction
{
Type* semantic_return_type;
Slice<Type*> semantic_argument_types;
CallingConvention calling_convention;
bool is_variable_arguments;
// ABI
Slice<Type*> abi_argument_types;
Type* abi_return_type;
AbiRegisterCount available_registers;
Slice<AbiInformation> argument_abis;
AbiInformation return_abi;
};
struct TypePointer
@ -140,8 +355,9 @@ struct TypePointer
struct TypeArray
{
u64 element_count;
Type* element_type;
u64 element_count;
Type* next;
};
struct UnresolvedEnumField
@ -169,6 +385,10 @@ struct TypeEnum
{
Slice<EnumField> fields;
Type* backing_type;
LLVMValueRef enum_to_string_function;
LLVMValueRef string_to_enum_function;
Type* string_to_enum_struct_type;
Global* name_array;
u32 line;
};
@ -221,6 +441,13 @@ struct TypeUnion
u32 biggest_field;
};
struct LLVMType
{
LLVMTypeRef abi;
LLVMTypeRef memory;
LLVMMetadataRef debug;
};
struct Type
{
union
@ -238,6 +465,7 @@ struct Type
TypeId id;
String name;
Type* next;
LLVMType llvm;
};
fn u32 align_bit_count(u32 bit_count)
@ -263,6 +491,10 @@ fn u64 get_byte_size(Type* type)
assert(byte_count == 1 || byte_count == 2 || byte_count == 4 || byte_count == 8 || byte_count == 16);
return byte_count;
} break;
case TypeId::pointer:
{
return 8;
} break;
case TypeId::array:
{
auto element_type = type->array.element_type;
@ -281,7 +513,12 @@ fn u64 get_byte_size(Type* type)
auto result = get_byte_size(type->enumerator.backing_type);
return result;
} break;
default: trap_raw();
case TypeId::bits:
{
auto result = get_byte_size(type->bits.backing_type);
return result;
} break;
default: trap();
}
}
@ -311,7 +548,24 @@ fn u32 get_byte_alignment(Type* type)
auto result = get_byte_alignment(type->enumerator.backing_type);
return result;
} break;
default: trap_raw();
case TypeId::pointer:
{
return 8;
} break;
case TypeId::bits:
{
auto result = get_byte_alignment(type->bits.backing_type);
return result;
} break;
case TypeId::union_type:
{
return type->union_type.byte_alignment;
} break;
case TypeId::alias:
{
return get_byte_alignment(type->alias.type);
} break;
default: trap();
}
}
@ -320,7 +574,9 @@ fn u64 get_bit_size(Type* type)
switch (type->id)
{
case TypeId::integer: return type->integer.bit_count;
default: trap_raw();
case TypeId::enumerator: return get_bit_size(type->enumerator.backing_type);
case TypeId::alias: return get_bit_size(type->alias.type);
default: trap();
}
}
@ -340,6 +596,7 @@ struct Scope
u32 line;
u32 column;
ScopeKind kind;
LLVMMetadataRef llvm;
};
enum class StatementId
@ -396,6 +653,7 @@ struct StatementSwitchClause
{
Slice<Value*> values;
Block* block;
LLVMBasicBlockRef basic_block;
};
struct StatementSwitch
@ -461,7 +719,6 @@ enum class ValueId
variable_reference,
macro_reference,
macro_instantiation,
dereference,
call,
global,
array_initialization,
@ -479,6 +736,8 @@ enum class ValueId
zero,
select,
string_to_enum,
local,
argument,
};
struct ValueConstantInteger
@ -487,12 +746,19 @@ struct ValueConstantInteger
bool is_signed;
};
struct FunctionLLVM
{
LLVMBasicBlockRef return_block;
LLVMValueRef return_alloca;
};
struct ValueFunction
{
Slice<Argument> arguments;
Scope scope;
Block* block;
FunctionAttributes attributes;
FunctionLLVM llvm;
};
enum class UnaryId
@ -510,6 +776,7 @@ enum class UnaryId
int_from_pointer,
va_end,
bitwise_not,
dereference,
};
struct ValueUnary
@ -565,6 +832,7 @@ struct ValueCall
{
Value* callable;
Slice<Value*> arguments;
Type* function_type;
};
struct ValueArrayInitialization
@ -664,8 +932,12 @@ struct MacroInstantiation
Scope scope;
u32 line;
u32 column;
LLVMValueRef return_alloca;
LLVMBasicBlockRef return_block;
};
fn bool variable_is_constant(Value* value);
struct Value
{
union
@ -676,7 +948,6 @@ struct Value
ValueBinary binary;
Variable* variable_reference;
ValueUnaryType unary_type;
Value* dereference;
ValueCall call;
ValueArrayInitialization array_initialization;
ValueArrayExpression array_expression;
@ -694,14 +965,36 @@ struct Value
Type* type;
ValueId id;
ValueKind kind;
LLVMValueRef llvm;
bool is_constant()
{
switch (id)
{
case ValueId::constant_integer:
case ValueId::enum_literal:
return true;
default: trap_raw();
case ValueId::unary:
case ValueId::binary:
case ValueId::field_access:
case ValueId::array_expression:
case ValueId::call:
return false;
case ValueId::variable_reference:
{
return variable_is_constant(this);
} break;
case ValueId::array_initialization:
{
assert(type); // This asserts that the value type has been analyzed and `is_constant` was properly set
return array_initialization.is_constant;
} break;
case ValueId::aggregate_initialization:
{
assert(type); // This asserts that the value type has been analyzed and `is_constant` was properly set
return aggregate_initialization.is_constant;
} break;
default: trap();
}
}
};
@ -717,6 +1010,28 @@ struct Variable
u32 column;
};
fn bool variable_is_constant(Value* value)
{
assert(value->id == ValueId::variable_reference);
auto* variable = value->variable_reference;
switch (value->kind)
{
case ValueKind::left:
{
switch (variable->scope->kind)
{
case ScopeKind::global:
return true;
default:
return false;
}
} break;
case ValueKind::right:
return false;
}
}
enum class Linkage
{
internal,
@ -727,6 +1042,7 @@ struct Global
{
Variable variable;
Linkage linkage;
bool emitted;
Global* next;
};
@ -742,6 +1058,47 @@ struct Argument
u32 index;
};
struct LLVMIntrinsicId
{
u32 n;
};
enum class IntrinsicIndex
{
trap,
va_start,
va_end,
va_copy,
count,
};
global_variable String intrinsic_names[] = {
string_literal("llvm.trap"),
string_literal("llvm.va_start"),
string_literal("llvm.va_end"),
string_literal("llvm.va_copy"),
};
static_assert(array_length(intrinsic_names) == (u64)IntrinsicIndex::count);
struct ModuleLLVM
{
LLVMContextRef context;
LLVMModuleRef module;
LLVMBuilderRef builder;
LLVMDIBuilderRef di_builder;
LLVMMetadataRef file;
LLVMMetadataRef compile_unit;
LLVMTypeRef pointer_type;
LLVMTypeRef void_type;
LLVMIntrinsicId intrinsic_table[(u64)IntrinsicIndex::count];
LLVMValueRef memcmp;
LLVMMetadataRef inlined_at;
LLVMBasicBlockRef continue_block;
LLVMBasicBlockRef exit_block;
u32 debug_tag;
};
struct Module
{
Arena* arena;
@ -768,6 +1125,7 @@ struct Module
MacroDeclaration* current_macro_declaration;
MacroInstantiation* current_macro_instantiation;
ModuleLLVM llvm;
Scope scope;
String name;
@ -788,6 +1146,10 @@ fn Type* integer_type(Module* module, TypeInteger integer)
{
assert(integer.bit_count);
assert(integer.bit_count <= 64 || integer.bit_count == 128);
if (integer.is_signed)
{
assert(integer.bit_count > 1);
}
auto index = integer.bit_count == 128 ? (i128_offset + integer.is_signed) : (integer.bit_count - 1 + (64 * integer.is_signed));
auto* result_type = module->first_type + index;
assert(result_type->id == TypeId::integer);
@ -806,6 +1168,31 @@ fn Type* noreturn_type(Module* module)
return void_type(module) + 1;
}
fn Type* uint1(Module* module)
{
return integer_type(module, { .bit_count = 1, .is_signed = false });
}
fn Type* uint8(Module* module)
{
return integer_type(module, { .bit_count = 8, .is_signed = false });
}
fn Type* uint32(Module* module)
{
return integer_type(module, { .bit_count = 32, .is_signed = false });
}
fn Type* uint64(Module* module)
{
return integer_type(module, { .bit_count = 64, .is_signed = false });
}
fn Type* sint32(Module* module)
{
return integer_type(module, { .bit_count = 32, .is_signed = true });
}
struct Options
{
String content;
@ -899,6 +1286,7 @@ fn Type* get_pointer_type(Module* module, Type* element_type)
string_literal("&"),
element_type->name,
};
auto result = type_allocate_init(module, {
.pointer = {
.element_type = element_type,
@ -907,6 +1295,17 @@ fn Type* get_pointer_type(Module* module, Type* element_type)
.name = arena_join_string(module->arena, array_to_slice(name_parts)),
});
if (last_pointer_type)
{
assert(module->first_pointer_type);
last_pointer_type->pointer.next = result;
}
else
{
assert(!module->first_pointer_type);
module->first_pointer_type = result;
}
return result;
}
@ -959,7 +1358,7 @@ fn Type* get_slice_type(Module* module, Type* element_type)
};
fields[1] = {
.name = string_literal("length"),
.type = integer_type(module, { .bit_count = 64, .is_signed = false }),
.type = uint64(module),
.offset = 8,
.line = 0,
};
@ -982,7 +1381,7 @@ fn Type* get_slice_type(Module* module, Type* element_type)
if (last_slice_type)
{
last_slice_type->next = result;
last_slice_type->structure.next = result;
}
else
{
@ -992,5 +1391,309 @@ fn Type* get_slice_type(Module* module, Type* element_type)
return result;
}
fn String array_name(Module* module, Type* element_type, u64 element_count)
{
u8 buffer[512];
auto buffer_slice = String{ .pointer = buffer, .length = array_length(buffer) };
u64 i = 0;
buffer[i] = '[';
i += 1;
i += format_integer_decimal(buffer_slice(i), element_count);
buffer[i] = ']';
i += 1;
auto element_name = element_type->name;
memcpy(buffer + i, element_name.pointer, element_name.length);
i += element_name.length;
auto name = arena_duplicate_string(module->arena, buffer_slice(0, i));
return name;
}
fn Type* get_array_type(Module* module, Type* element_type, u64 element_count)
{
assert(element_type);
assert(element_count);
Type* array_type = module->first_array_type;
if (array_type)
{
while (1)
{
assert(array_type->id == TypeId::array);
auto* candidate_element_type = array_type->array.element_type;
if (candidate_element_type == element_type)
{
return array_type;
}
if (!array_type->array.next)
{
break;
}
array_type = array_type->array.next;
}
}
Type* last_array_type = array_type;
auto result = type_allocate_init(module, {
.array = {
.element_type = element_type,
.element_count = element_count,
},
.id = TypeId::array,
.name = array_name(module, element_type, element_count),
});
if (last_array_type)
{
last_array_type->array.next = result;
}
else
{
module->first_array_type = result;
}
return result;
}
fn Block* scope_to_block(Scope* scope)
{
assert(scope->kind == ScopeKind::local);
auto byte_offset = offsetof(Block, scope);
auto result = (Block*)((u8*)scope - byte_offset);
assert(result->scope.kind == ScopeKind::local);
return result;
}
fn StatementForEach* scope_to_for_each(Scope* scope)
{
assert(scope->kind == ScopeKind::for_each);
auto byte_offset = offsetof(StatementForEach, scope);
auto result = (StatementForEach*)((u8*)scope - byte_offset);
assert(result->scope.kind == ScopeKind::for_each);
return result;
}
fn MacroDeclaration* scope_to_macro_declaration(Scope* scope)
{
assert(scope->kind == ScopeKind::macro_declaration);
auto byte_offset = offsetof(MacroDeclaration, scope);
auto result = (MacroDeclaration*)((u8*)scope - byte_offset);
assert(result->scope.kind == ScopeKind::macro_declaration);
return result;
}
fn MacroInstantiation* scope_to_macro_instantiation(Scope* scope)
{
assert(scope->kind == ScopeKind::macro_instantiation);
auto byte_offset = offsetof(MacroInstantiation, scope);
auto result = (MacroInstantiation*)((u8*)scope - byte_offset);
assert(result->scope.kind == ScopeKind::macro_instantiation);
return result;
}
fn ValueFunction* scope_to_function(Scope* scope)
{
assert(scope->kind == ScopeKind::function);
auto byte_offset = offsetof(ValueFunction, scope);
auto result = (ValueFunction*)((u8*)scope - byte_offset);
assert(result->scope.kind == ScopeKind::function);
return result;
}
fn Module* scope_to_module(Scope* scope)
{
assert(scope->kind == ScopeKind::global);
auto byte_offset = offsetof(Module, scope);
auto result = (Module*)((u8*)scope - byte_offset);
assert(result->scope.kind == ScopeKind::global);
return result;
}
fn Value* reference_identifier(Module* module, Scope* current_scope, String identifier, ValueKind kind)
{
assert(!identifier.equal(string_literal("")));
assert(!identifier.equal(string_literal("_")));
Variable* variable = 0;
for (Scope* scope = current_scope; scope && !variable; scope = scope->parent)
{
switch (scope->kind)
{
case ScopeKind::global:
{
assert(module == scope_to_module(scope));
for (Global* global = module->first_global; global; global = global->next)
{
if (identifier.equal(global->variable.name))
{
variable = &global->variable;
break;
}
}
for (MacroDeclaration* macro_declaration = module->first_macro_declaration; macro_declaration; macro_declaration = macro_declaration->next)
{
if (identifier.equal(macro_declaration->name))
{
auto result = new_value(module);
*result = {
.macro_reference = macro_declaration,
.id = ValueId::macro_reference,
};
return result;
}
}
} break;
case ScopeKind::function:
{
assert(scope->parent);
auto function = scope_to_function(scope);
for (auto& argument: function->arguments)
{
if (identifier.equal(argument.variable.name))
{
variable = &argument.variable;
break;
}
}
} break;
case ScopeKind::local:
{
assert(scope->parent);
assert(scope->parent->kind != ScopeKind::global);
auto block = scope_to_block(scope);
for (Local* local = block->first_local; local; local = local->next)
{
assert(!local->next || block->last_local != local);
assert((u64)local->next != 0x6e66203d206e6961);
if (identifier.equal(local->variable.name))
{
variable = &local->variable;
break;
}
}
} break;
case ScopeKind::for_each:
{
assert(scope->parent);
auto for_each = scope_to_for_each(scope);
for (Local* local = for_each->first_local; local; local = local->next)
{
if (identifier.equal(local->variable.name))
{
variable = &local->variable;
break;
}
}
} break;
case ScopeKind::macro_declaration:
{
assert(scope->parent);
auto macro_declaration = scope_to_macro_declaration(scope);
for (auto& constant_argument: macro_declaration->constant_arguments)
{
if (identifier.equal(constant_argument.name))
{
trap();
}
}
for (auto& argument: macro_declaration->arguments)
{
if (identifier.equal(argument.variable.name))
{
variable = &argument.variable;
break;
}
}
} break;
case ScopeKind::macro_instantiation:
{
assert(scope->parent);
auto macro_instantiation = scope_to_macro_instantiation(scope);
for (auto& argument : macro_instantiation->declaration_arguments)
{
if (identifier.equal(argument.variable.name))
{
variable = &argument.variable;
break;
}
}
} break;
}
}
if (variable)
{
auto result = new_value(module);
*result = {
.variable_reference = variable,
.id = ValueId::variable_reference,
.kind = kind,
};
return result;
}
else
{
report_error();
}
}
fn Local* new_local(Module* module, Scope* scope)
{
auto* result = &arena_allocate<Local>(module->arena, 1)[0];
*result = {};
switch (scope->kind)
{
case ScopeKind::local:
{
auto block = scope_to_block(scope);
if (block->last_local)
{
block->last_local->next = result;
block->last_local = result;
}
else
{
block->first_local = result;
block->last_local = result;
}
} break;
case ScopeKind::for_each:
{
auto for_each = scope_to_for_each(scope);
if (for_each->last_local)
{
for_each->last_local->next = result;
for_each->last_local = result;
}
else
{
for_each->first_local = result;
for_each->last_local = result;
}
} break;
default: report_error();
}
return result;
}
void parse(Module* module);
void emit(Module* module);

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@
#define breakpoint() __builtin_debugtrap()
#define string_literal_length(s) (sizeof(s) - 1)
#define string_literal(s) ((String){ .pointer = (u8*)(s), .length = string_literal_length(s), })
#define split_string_literal(s) (char*)(s), string_literal_length(s)
#define offsetof(S, f) __builtin_offsetof(S, f)
#define array_length(arr) sizeof(arr) / sizeof((arr)[0])
@ -16,9 +17,9 @@
#define backing_type(E) __underlying_type(E)
#define unreachable_raw() __builtin_unreachable()
#define trap_raw() __builtin_trap()
#define trap() __builtin_trap()
#if BB_DEBUG
#define unreachable() trap_raw()
#define unreachable() trap()
#else
#define unreachable() unreachable_raw()
#endif
@ -300,14 +301,32 @@ fn void* os_reserve(void* base, u64 size, ProtectionFlags protection, MapFlags m
.read = protection.read,
.write = protection.write,
.execute = protection.execute,
.sem = 0,
._ = 0,
};
auto map_flags = MAP
{
.type = map.priv ? MAP::Type::priv : MAP::Type::shared,
.fixed = 0,
.anonymous = map.anonymous,
.bit32 = 0,
._0 = 0,
.grows_down = 0,
._1 = 0,
.deny_write = 0,
.executable = 0,
.locked = 0,
.no_reserve = map.no_reserve,
.populate = map.populate,
.non_block = 0,
.stack = 0,
.huge_tlb = 0,
.sync = 0,
.fixed_no_replace = 0,
._2 = 0,
.uninitialized = 0,
._3 = 0,
};
auto* address = mmap(base, size, protection_flags, map_flags, -1, 0);
@ -323,6 +342,8 @@ fn void os_commit(void* address, u64 size, ProtectionFlags protection)
.read = protection.read,
.write = protection.write,
.execute = protection.execute,
.sem = 0,
._ = 0,
};
auto result = mprotect(address, size, protection_flags);
assert(!result);
@ -616,7 +637,7 @@ fn String file_read(Arena* arena, String file_path)
{
if (os_is_debugger_present())
{
trap_raw();
trap();
}
exit(1);
}
@ -639,3 +660,39 @@ fn u64 next_power_of_two(u64 n)
n += 1;
return n;
}
fn u8 format_integer_decimal(String buffer, u64 v)
{
u8 byte_count = 0;
auto value = v;
if (value != 0)
{
u8 reverse_buffer[64];
u8 reverse_index = 0;
while (value != 0)
{
auto digit_value = (u8)(value % 10);
auto ascii_character = digit_value + '0';
value /= 10;
reverse_buffer[reverse_index] = ascii_character;
reverse_index += 1;
}
while (reverse_index != 0)
{
reverse_index -= 1;
buffer[byte_count] = reverse_buffer[reverse_index];
byte_count += 1;
}
}
else
{
buffer[0] = '0';
byte_count = 1;
}
return byte_count;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +1,320 @@
#include <lib.h>
#pragma once
namespace llvm
#include <lib.h>
#include <llvm-c/Core.h>
#include <llvm-c/Analysis.h>
#include <llvm-c/Target.h>
#include <llvm-c/Analysis.h>
#include <llvm-c/DebugInfo.h>
enum class BBLLVMUWTableKind : u64
{
class Type;
class Value;
}
None = 0, ///< No unwind table requested
Sync = 1, ///< "Synchronous" unwind tables
Async = 2, ///< "Asynchronous" unwind tables (instr precise)
Default = 2,
};
enum class BBLLVMFramePointerKind : u64
{
none = 0,
reserved = 1,
non_leaf = 2,
all = 3,
};
enum class ZeroCallUsedRegsKind : u64
{
all = 0,
skip = 1 << 0,
only_used = 1 << 1,
only_gpr = 1 << 2,
only_arg = 1 << 3,
used_gpr_arg = only_used | only_gpr | only_arg,
used_gpr = only_used | only_gpr,
used_arg = only_used | only_arg,
used = only_used,
all_gpr_arg = only_gpr | only_arg,
all_gpr = only_gpr,
all_arg = only_arg,
};
struct BBLLVMFunctionAttributesFlags0
{
u64 noreturn:1;
u64 cmse_ns_call:1;
u64 nounwind:1;
u64 returns_twice:1;
u64 cold:1;
u64 hot:1;
u64 no_duplicate:1;
u64 convergent:1;
u64 no_merge:1;
u64 will_return:1;
u64 no_caller_saved_registers:1;
u64 no_cf_check:1;
u64 no_callback:1;
u64 alloc_size:1;
u64 uniform_work_group_size:1;
u64 aarch64_pstate_sm_body:1;
u64 aarch64_pstate_sm_enabled:1;
u64 aarch64_pstate_sm_compatible:1;
u64 aarch64_preserves_za:1;
u64 aarch64_in_za:1;
u64 aarch64_out_za:1;
u64 aarch64_inout_za:1;
u64 aarch64_preserves_zt0:1;
u64 aarch64_in_zt0:1;
u64 aarch64_out_zt0:1;
u64 aarch64_inout_zt0:1;
u64 optimize_for_size:1;
u64 min_size:1;
u64 no_red_zone:1;
u64 indirect_tls_seg_refs:1;
u64 no_implicit_floats:1;
u64 sample_profile_suffix_elision_policy:1;
u64 memory_none:1;
u64 memory_readonly:1;
u64 memory_inaccessible_or_arg_memory_only:1;
u64 memory_arg_memory_only:1;
u64 strict_fp:1;
u64 no_inline:1;
u64 always_inline:1;
u64 guard_no_cf:1;
// TODO: branch protection function attributes
// TODO: cpu features
// Call-site begin
u64 call_no_builtins:1;
BBLLVMFramePointerKind definition_frame_pointer_kind:2;
u64 definition_less_precise_fpmad:1;
u64 definition_null_pointer_is_valid:1;
u64 definition_no_trapping_fp_math:1;
u64 definition_no_infs_fp_math:1;
u64 definition_no_nans_fp_math:1;
u64 definition_approx_func_fp_math:1;
u64 definition_unsafe_fp_math:1;
u64 definition_use_soft_float:1;
u64 definition_no_signed_zeroes_fp_math:1;
u64 definition_stack_realignment:1;
u64 definition_backchain:1;
u64 definition_split_stack:1;
u64 definition_speculative_load_hardening:1;
ZeroCallUsedRegsKind definition_zero_call_used_registers:4;
// TODO: denormal builtins
u64 definition_non_lazy_bind:1;
u64 definition_cmse_nonsecure_entry:1;
BBLLVMUWTableKind definition_unwind_table_kind:2;
};
static_assert(sizeof(BBLLVMFunctionAttributesFlags0) == sizeof(u64));
struct BBLLVMFunctionAttributesFlags1
{
u64 definition_disable_tail_calls:1;
u64 definition_stack_protect_strong:1;
u64 definition_stack_protect:1;
u64 definition_stack_protect_req:1;
u64 definition_aarch64_new_za:1;
u64 definition_aarch64_new_zt0:1;
u64 definition_optimize_none:1;
u64 definition_naked:1;
u64 definition_inline_hint:1;
u64 _:55;
};
static_assert(sizeof(BBLLVMFunctionAttributesFlags1) == sizeof(u64));
struct BBLLVMFunctionAttributes
{
String prefer_vector_width;
String stack_protector_buffer_size;
String definition_probe_stack;
String definition_stack_probe_size;
BBLLVMFunctionAttributesFlags0 flags0;
BBLLVMFunctionAttributesFlags1 flags1;
};
static_assert(sizeof(BBLLVMFunctionAttributes) == 10 * sizeof(u64));
struct BBLLVMArgumentAttributes
{
LLVMTypeRef semantic_type;
LLVMTypeRef abi_type;
u64 dereferenceable_bytes;
u32 alignment;
u32 no_alias:1;
u32 non_null:1;
u32 no_undef:1;
u32 sign_extend:1;
u32 zero_extend:1;
u32 in_reg:1;
u32 no_fp_class:10;
u32 struct_return:1;
u32 writable:1;
u32 dead_on_unwind:1;
u32 in_alloca:1;
u32 dereferenceable:1;
u32 dereferenceable_or_null:1;
u32 nest:1;
u32 by_value:1;
u32 by_reference:1;
u32 no_capture:1;
u32 _:6;
};
static_assert(sizeof(BBLLVMArgumentAttributes) == 2 * sizeof(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,
address = 0x1,
boolean = 0x2,
complex_float = 0x3,
float_type = 0x4,
signed_type = 0x5,
signed_char = 0x6,
unsigned_type = 0x7,
unsigned_char = 0x8,
// DWARF 3.
imaginary_float = 0x9,
packed_decimal = 0xa,
numeric_string = 0xb,
edited = 0xc,
signed_fixed = 0xd,
unsigned_fixed = 0xe,
decimal_float = 0xf,
// DWARF 4.
UTF = 0x10,
// DWARF 5.
UCS = 0x11,
ASCII = 0x12,
// HP extensions.
HP_float80 = 0x80, // Floating-point (80 bit).
HP_complex_float80 = 0x81, // Complex floating-point (80 bit).
HP_float128 = 0x82, // Floating-point (128 bit).
HP_complex_float128 = 0x83, // Complex fp (128 bit).
HP_floathpintel = 0x84, // Floating-point (82 bit IA64).
HP_imaginary_float80 = 0x85,
HP_imaginary_float128 = 0x86,
HP_VAX_float = 0x88, // F or G floating.
HP_VAX_float_d = 0x89, // D floating.
HP_packed_decimal = 0x8a, // Cobol.
HP_zoned_decimal = 0x8b, // Cobol.
HP_edited = 0x8c, // Cobol.
HP_signed_fixed = 0x8d, // Cobol.
HP_unsigned_fixed = 0x8e, // Cobol.
HP_VAX_complex_float = 0x8f, // F or G floating complex.
HP_VAX_complex_float_d = 0x90, // D floating complex.
};
enum class DIFlagsVisibility : u32
{
none = 0,
private_ = 1,
protected_ = 2,
public_ = 3,
};
enum class DIFlagsInheritance : u32
{
none = 0,
single_ = 1,
multiple_ = 2,
virtual_ = 3,
};
struct DIFlags
{
DIFlagsVisibility visibility:2;
u32 forward_declaration:1;
u32 apple_block:1;
u32 block_by_ref_struct:1;
u32 virtual_:1;
u32 artificial:1;
u32 explicit_:1;
u32 prototyped:1;
u32 objective_c_class_complete:1;
u32 object_pointer:1;
u32 vector:1;
u32 static_member:1;
u32 lvalue_reference:1;
u32 rvalue_reference:1;
u32 reserved:1;
DIFlagsInheritance inheritance:2;
u32 introduced_virtual:1;
u32 bit_field:1;
u32 no_return:1;
u32 type_pass_by_value:1;
u32 type_pass_by_reference:1;
u32 enum_class:1;
u32 thunk:1;
u32 non_trivial:1;
u32 big_endian:1;
u32 little_endian:1;
u32 all_calls_described:1;
u32 _:3;
};
static_assert(sizeof(DIFlags) == sizeof(u32));
fn bool llvm_initialized = false;
extern "C" void LLVMInitializeX86TargetInfo();
extern "C" void LLVMInitializeX86Target();
extern "C" void LLVMInitializeX86TargetMC();
extern "C" void LLVMInitializeX86AsmPrinter();
extern "C" void LLVMInitializeX86AsmParser();
extern "C" void LLVMInitializeX86Disassembler();
extern "C" String llvm_default_target_triple();
extern "C" String llvm_host_cpu_name();
extern "C" String llvm_host_cpu_features();
struct LLVMGlobal
{
String host_triple;
String host_cpu_model;
String host_cpu_features;
};
extern "C" LLVMModuleRef llvm_context_create_module(LLVMContextRef context, String name);
global_variable LLVMGlobal llvm_global;
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);
fn void initialize_all()
{
assert(!llvm_initialized);
LLVMInitializeX86TargetInfo();
LLVMInitializeX86Target();
LLVMInitializeX86TargetMC();
LLVMInitializeX86AsmPrinter();
LLVMInitializeX86AsmParser();
LLVMInitializeX86Disassembler();
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);
extern "C" bool llvm_module_verify(LLVMModuleRef m, String* error_message);
extern "C" void llvm_subprogram_replace_type(LLVMMetadataRef subprogram, LLVMMetadataRef subroutine_type);
extern "C" String llvm_module_to_string(LLVMModuleRef module);
llvm_global = {
.host_triple = llvm_default_target_triple(),
.host_cpu_model = llvm_host_cpu_name(),
.host_cpu_features = llvm_host_cpu_features(),
};
}

View File

@ -21,86 +21,6 @@ enum class ValueIntrinsic
count,
};
fn Block* scope_to_block(Scope* scope)
{
assert(scope->kind == ScopeKind::local);
auto byte_offset = offsetof(Block, scope);
auto result = (Block*)((u8*)scope - byte_offset);
return result;
}
fn StatementForEach* scope_to_for_each(Scope* scope)
{
assert(scope->kind == ScopeKind::for_each);
auto byte_offset = offsetof(StatementForEach, scope);
auto result = (StatementForEach*)((u8*)scope - byte_offset);
return result;
}
fn MacroDeclaration* scope_to_macro_declaration(Scope* scope)
{
assert(scope->kind == ScopeKind::macro_declaration);
auto byte_offset = offsetof(MacroDeclaration, scope);
auto result = (MacroDeclaration*)((u8*)scope - byte_offset);
return result;
}
fn ValueFunction* scope_to_function(Scope* scope)
{
assert(scope->kind == ScopeKind::function);
auto byte_offset = offsetof(ValueFunction, scope);
auto result = (ValueFunction*)((u8*)scope - byte_offset);
return result;
}
fn Module* scope_to_module(Scope* scope)
{
assert(scope->kind == ScopeKind::global);
auto byte_offset = offsetof(Module, scope);
auto result = (Module*)((u8*)scope - byte_offset);
return result;
}
fn Local* new_local(Module* module, Scope* scope)
{
auto* result = &arena_allocate<Local>(module->arena, 1)[0];
switch (scope->kind)
{
case ScopeKind::local:
{
auto block = scope_to_block(scope);
if (block->last_local)
{
block->last_local->next = result;
block->last_local = result;
}
else
{
block->first_local = result;
block->last_local = result;
}
} break;
case ScopeKind::for_each:
{
auto for_each = scope_to_for_each(scope);
if (for_each->last_local)
{
for_each->last_local->next = result;
for_each->last_local = result;
}
else
{
for_each->first_local = result;
for_each->last_local = result;
}
} break;
default: report_error();
}
return result;
}
enum class TokenId
{
none,
@ -309,22 +229,22 @@ fn bool is_hexadecimal_alpha_upper(u8 ch)
fn bool is_hexadecimal_alpha(u8 ch)
{
return is_hexadecimal_alpha_lower(ch) | is_hexadecimal_alpha_upper(ch);
return is_hexadecimal_alpha_lower(ch) || is_hexadecimal_alpha_upper(ch);
}
fn bool is_hexadecimal(u8 ch)
{
return is_decimal(ch) | is_hexadecimal_alpha(ch);
return is_decimal(ch) || is_hexadecimal_alpha(ch);
}
fn bool is_identifier_start(u8 ch)
{
return (is_lower(ch) | is_upper(ch)) | (ch == '_');
return (is_lower(ch) || is_upper(ch)) || (ch == '_');
}
fn bool is_identifier(u8 ch)
{
return is_identifier_start(ch) | is_decimal(ch);
return is_identifier_start(ch) || is_decimal(ch);
}
fn u32 get_line(Module* module)
@ -530,64 +450,6 @@ fn u64 parse_integer_decimal_assume_valid(String string)
fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder);
fn u8 format_integer_decimal(String buffer, u64 v)
{
u8 byte_count = 0;
auto value = v;
if (value != 0)
{
u8 reverse_buffer[64];
u8 reverse_index = 0;
while (value != 0)
{
auto digit_value = (u8)(value % 10);
auto ascii_character = digit_value + '0';
value /= 10;
reverse_buffer[reverse_index] = ascii_character;
reverse_index += 1;
}
while (reverse_index != 0)
{
reverse_index -= 1;
buffer[byte_count] = reverse_buffer[reverse_index];
byte_count += 1;
}
}
else
{
buffer[0] = '0';
byte_count = 1;
}
return byte_count;
}
fn String array_name(Module* module, Type* element_type, u64 element_count)
{
u8 buffer[512];
auto buffer_slice = String{ .pointer = buffer, .length = array_length(buffer) };
u64 i = 0;
buffer[i] = left_bracket;
i += 1;
i += format_integer_decimal(buffer_slice(i), element_count);
buffer[i] = right_bracket;
i += 1;
auto element_name = element_type->name;
memcpy(buffer + i, element_name.pointer, element_name.length);
i += element_name.length;
auto name = arena_duplicate_string(module->arena, buffer_slice(0, i));
return name;
}
fn Type* parse_type(Module* module, Scope* scope)
{
auto start_character = module->content[module->offset];
@ -729,8 +591,8 @@ fn Type* parse_type(Module* module, Scope* scope)
assert(!length_value);
auto result = type_allocate_init(module, {
.array = {
.element_count = 0,
.element_type = element_type,
.element_count = 0,
},
.id = TypeId::array,
.name = string_literal(""),
@ -747,15 +609,8 @@ fn Type* parse_type(Module* module, Scope* scope)
assert(element_count != 0);
auto result = type_allocate_init(module, {
.array = {
.element_count = element_count,
.element_type = element_type,
},
.id = TypeId::array,
.name = array_name(module, element_type, element_count),
});
return result;
auto array_type = get_array_type(module, element_type, element_count);
return array_type;
}
}
}
@ -886,7 +741,7 @@ fn u8 escape_character(u8 ch)
{
switch (ch)
{
default: trap_raw();
default: trap();
}
}
@ -984,7 +839,7 @@ fn Token tokenize(Module* module)
}
else
{
trap_raw();
trap();
}
} break;
case '<':
@ -1100,12 +955,11 @@ fn Token tokenize(Module* module)
auto ch = module->content[module->offset];
if (ch == '"')
{
module->offset += 1;
break;
}
else if (ch == '\\')
{
trap_raw();
trap();
}
else
{
@ -1114,6 +968,7 @@ fn Token tokenize(Module* module)
}
auto end = module->offset;
module->offset += 1;
auto string_literal = module->content(start, end);
token = {
@ -1328,134 +1183,6 @@ fn Token tokenize(Module* module)
return token;
}
fn Value* reference_identifier(Module* module, Scope* current_scope, String identifier, ValueKind kind)
{
assert(!identifier.equal(string_literal("")));
assert(!identifier.equal(string_literal("_")));
Variable* variable = 0;
for (Scope* scope = current_scope; scope; scope = scope->parent)
{
switch (scope->kind)
{
case ScopeKind::global:
{
assert(module == scope_to_module(scope));
for (Global* global = module->first_global; global; global = global->next)
{
if (identifier.equal(global->variable.name))
{
variable = &global->variable;
break;
}
}
for (MacroDeclaration* macro_declaration = module->first_macro_declaration; macro_declaration; macro_declaration = macro_declaration->next)
{
if (identifier.equal(macro_declaration->name))
{
auto result = new_value(module);
*result = {
.macro_reference = macro_declaration,
.id = ValueId::macro_reference,
};
return result;
}
}
} break;
case ScopeKind::function:
{
assert(scope->parent);
auto function = scope_to_function(scope);
for (auto argument: function->arguments)
{
if (identifier.equal(argument.variable.name))
{
variable = &argument.variable;
break;
}
}
} break;
case ScopeKind::local:
{
assert(scope->parent);
assert(scope->parent->kind != ScopeKind::global);
auto block = scope_to_block(scope);
for (Local* local = block->first_local; local; local = local->next)
{
if (identifier.equal(local->variable.name))
{
variable = &local->variable;
break;
}
}
} break;
case ScopeKind::for_each:
{
assert(scope->parent);
auto for_each = scope_to_for_each(scope);
for (Local* local = for_each->first_local; local; local = local->next)
{
if (identifier.equal(local->variable.name))
{
variable = &local->variable;
break;
}
}
} break;
case ScopeKind::macro_declaration:
{
assert(scope->parent);
auto macro_declaration = scope_to_macro_declaration(scope);
for (auto& constant_argument: macro_declaration->constant_arguments)
{
if (identifier.equal(constant_argument.name))
{
trap_raw();
}
}
for (auto& argument: macro_declaration->arguments)
{
if (identifier.equal(argument.variable.name))
{
variable = &argument.variable;
break;
}
}
} break;
case ScopeKind::macro_instantiation:
{
trap_raw();
} break;
}
if (variable)
{
break;
}
}
if (variable)
{
auto result = new_value(module);
*result = {
.variable_reference = variable,
.id = ValueId::variable_reference,
.kind = kind,
};
return result;
}
else
{
report_error();
}
}
fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder);
fn Value* parse_precedence(Module* module, Scope* scope, ValueBuilder builder);
@ -1520,7 +1247,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
{
case ValueIntrinsic::align_of:
{
trap_raw();
trap();
} break;
case ValueIntrinsic::enum_name:
case ValueIntrinsic::extend:
@ -1679,7 +1406,7 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
} break;
case ValueIntrinsic::va_copy:
{
trap_raw();
trap();
} break;
case ValueIntrinsic::count: unreachable();
}
@ -1834,11 +1561,11 @@ fn Value* parse_left(Module* module, Scope* scope, ValueBuilder builder)
return result;
}
fn Precedence get_token_precedence(TokenId id)
fn Precedence get_token_precedence(Token token)
{
Precedence precedence;
switch (id)
switch (token.id)
{
case TokenId::none: unreachable();
case TokenId::comma:
@ -1862,6 +1589,23 @@ fn Precedence get_token_precedence(TokenId id)
case TokenId::assign_ampersand:
precedence = Precedence::assignment;
break;
case TokenId::operator_keyword: // TODO: check if any other operator that is not bitwise is added
{
switch (token.operator_keyword)
{
case OperatorKeyword::and_op:
case OperatorKeyword::or_op:
precedence = Precedence::bitwise;
break;
case OperatorKeyword::and_op_shortcircuit:
precedence = Precedence::boolean_and;
break;
case OperatorKeyword::or_op_shortcircuit:
precedence = Precedence::boolean_or;
break;
case OperatorKeyword::count: unreachable();
}
} break;
case TokenId::compare_equal:
case TokenId::compare_not_equal:
case TokenId::compare_less:
@ -1870,7 +1614,6 @@ fn Precedence get_token_precedence(TokenId id)
case TokenId::compare_greater_equal:
precedence = Precedence::comparison;
break;
case TokenId::operator_keyword: // TODO: check if any other operator that is not bitwise is added
case TokenId::ampersand:
case TokenId::bar:
case TokenId::caret:
@ -1895,7 +1638,7 @@ fn Precedence get_token_precedence(TokenId id)
case TokenId::dot:
precedence = Precedence::postfix;
break;
default: trap_raw();
default: trap();
}
return precedence;
@ -1966,12 +1709,21 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder)
case TokenId::operator_keyword:
// Binary
{
auto precedence = get_token_precedence(token.id);
auto precedence = get_token_precedence(token);
assert(precedence != Precedence::assignment);
BinaryId id;
switch (token.id)
{
case TokenId::operator_keyword:
switch (token.operator_keyword)
{
case OperatorKeyword::and_op: id = BinaryId::logical_and; break;
case OperatorKeyword::or_op: id = BinaryId::logical_or; break;
case OperatorKeyword::and_op_shortcircuit: id = BinaryId::logical_and_shortcircuit; break;
case OperatorKeyword::or_op_shortcircuit: id = BinaryId::logical_or_shortcircuit; break;
case OperatorKeyword::count: unreachable();
} break;
case TokenId::plus: id = BinaryId::add; break;
case TokenId::dash: id = BinaryId::sub; break;
case TokenId::asterisk: id = BinaryId::mul; break;
@ -1988,15 +1740,6 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder)
case TokenId::compare_less_equal: id = BinaryId::compare_less_equal; break;
case TokenId::compare_greater: id = BinaryId::compare_greater; break;
case TokenId::compare_greater_equal: id = BinaryId::compare_greater_equal; break;
case TokenId::operator_keyword: switch (token.operator_keyword)
{
case OperatorKeyword::and_op: id = BinaryId::logical_and; break;
case OperatorKeyword::or_op: id = BinaryId::logical_or; break;
case OperatorKeyword::and_op_shortcircuit: id = BinaryId::logical_and_shortcircuit; break;
case OperatorKeyword::or_op_shortcircuit: id = BinaryId::logical_or_shortcircuit; break;
case OperatorKeyword::count: unreachable();
break;
} break;
default: unreachable();
}
@ -2018,8 +1761,11 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder)
{
result = new_value(module);
*result = {
.dereference = left,
.id = ValueId::dereference,
.unary = {
.value = left,
.id = UnaryId::dereference,
},
.id = ValueId::unary,
.kind = ValueKind::right,
};
} break;
@ -2116,7 +1862,7 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder)
{
case ConstantArgumentId::value:
{
trap_raw(); // TODO
trap(); // TODO
} break;
case ConstantArgumentId::type:
{
@ -2188,6 +1934,7 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder)
.index = index,
},
.id = ValueId::array_expression,
.kind = builder.kind,
};
}
else
@ -2208,6 +1955,7 @@ fn Value* parse_right(Module* module, Scope* scope, ValueBuilder builder)
.start = start_value,
.end = end_value,
},
.id = ValueId::slice_expression,
};
}
}
@ -2244,7 +1992,7 @@ fn Value* parse_precedence_left(Module* module, Scope* scope, ValueBuilder build
{
auto checkpoint = get_checkpoint(module);
auto token = tokenize(module);
auto token_precedence = get_token_precedence(token.id);
auto token_precedence = get_token_precedence(token);
if (token_precedence == Precedence::assignment)
{
token_precedence = builder.allow_assignment_operators ? Precedence::assignment : Precedence::none;
@ -2283,6 +2031,53 @@ fn Value* parse_value(Module* module, Scope* scope, ValueBuilder builder)
fn Block* parse_block(Module* module, Scope* parent_scope);
fn void print_value(Value* value, u32 identation)
{
unused(identation);
for (u32 i = 0; i < identation; i += 1)
{
print(string_literal(" "));
}
switch (value->id)
{
case ValueId::binary:
{
switch (value->binary.id)
{
case BinaryId::compare_not_equal:
{
print(string_literal("!="));
} break;
case BinaryId::logical_and_shortcircuit:
{
print(string_literal("and?"));
} break;
case BinaryId::logical_or_shortcircuit:
{
print(string_literal("or?"));
} break;
default: unreachable();
}
print(string_literal("\n"));
print_value(value->binary.left, identation + 1);
print_value(value->binary.right, identation + 1);
} break;
case ValueId::variable_reference:
{
print(value->variable_reference->name);
} break;
case ValueId::constant_integer:
{
print(string_literal("constant_integer"));
} break;
default: unreachable();
}
print(string_literal("\n"));
}
fn Statement* parse_statement(Module* module, Scope* scope)
{
bool require_semicolon = true;
@ -2394,7 +2189,7 @@ fn Statement* parse_statement(Module* module, Scope* scope)
{
case StatementStartKeyword::underscore_st:
{
trap_raw();
trap();
} break;
case StatementStartKeyword::return_st:
{
@ -2462,7 +2257,7 @@ fn Statement* parse_statement(Module* module, Scope* scope)
.right_values = {},
.predicate = 0,
.scope = {
.parent = scope,
.parent = parent_scope,
.line = statement_line,
.column = statement_column,
.kind = ScopeKind::for_each,
@ -2511,7 +2306,7 @@ fn Statement* parse_statement(Module* module, Scope* scope)
}
else
{
trap_raw();
trap();
}
skip_space(module);
@ -2741,7 +2536,7 @@ fn Statement* parse_statement(Module* module, Scope* scope)
case TokenId::assign_ampersand: id = StatementAssignmentId::assign_and; break;
case TokenId::assign_bar: id = StatementAssignmentId::assign_or; break;
case TokenId::assign_caret: id = StatementAssignmentId::assign_xor; break;
default: trap_raw();
default: trap();
}
skip_space(module);
@ -2759,7 +2554,7 @@ fn Statement* parse_statement(Module* module, Scope* scope)
}
else
{
trap_raw();
trap();
}
} break;
}
@ -2804,12 +2599,17 @@ fn Block* parse_block(Module* module, Scope* parent_scope)
}
auto* statement = parse_statement(module, scope);
if (!block->first_statement)
{
block->first_statement = statement;
}
if (current_statement)
{
current_statement->next = statement;
}
assert(statement->next == 0);
current_statement = statement;
}
@ -2951,8 +2751,6 @@ void parse(Module* module)
skip_space(module);
bool is_global_keyword = false;
enum class GlobalKeyword
{
bits,
@ -3099,16 +2897,13 @@ void parse(Module* module)
skip_space(module);
expect_character(module, left_brace);
u64 highest_value = 0;
u64 lowest_value = ~(u64)0;
u64 field_count = 0;
String name_buffer[64];
Value* value_buffer[64];
u64 int_value_buffer[64];
bool is_resolved = true;
bool implicit_value = false;
unused(implicit_value);
while (1)
{
@ -3122,11 +2917,11 @@ void parse(Module* module)
field_count += 1;
auto field_name = parse_identifier(module);
name_buffer[field_index] = field_name;
skip_space(module);
u64 field_integer_value = field_index;
Value* field_value = 0;
if (consume_character_if_match(module, '='))
{
@ -3142,17 +2937,17 @@ void parse(Module* module)
{
field_integer_value = field_value->constant_integer.value;
} break;
default: trap_raw();
default: trap();
}
}
else
{
trap_raw();
trap();
}
}
else
{
trap_raw();
trap();
}
}
else
@ -3163,6 +2958,8 @@ void parse(Module* module)
}
}
int_value_buffer[field_index] = field_integer_value;
skip_space(module);
consume_character_if_match(module, ',');
}
@ -3171,11 +2968,12 @@ void parse(Module* module)
{
auto fields = arena_allocate<EnumField>(module->arena, field_count);
u64 highest_value = 0;
auto lowest_value = ~(u64)0;
// auto lowest_value = ~(u64)0;
for (u64 i = 0; i < field_count; i += 1)
{
auto value = int_value_buffer[i];
highest_value = MAX(highest_value, value);
fields[i] = {
.name = name_buffer[i],
.value = value,
@ -3204,7 +3002,7 @@ void parse(Module* module)
}
else
{
trap_raw();
trap();
}
} break;
case GlobalKeyword::function:
@ -3390,10 +3188,10 @@ void parse(Module* module)
},
.linkage = (is_export | is_extern) ? Linkage::external : Linkage::internal,
};
module->current_function = global;
if (!is_declaration)
{
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)
{
@ -3412,7 +3210,7 @@ void parse(Module* module)
.line = line,
.column = 0,
},
.index = i,
.index = i + 1,
};
}
@ -3430,12 +3228,15 @@ void parse(Module* module)
storage->id = ValueId::function;
storage->function.block = parse_block(module, &storage->function.scope);
module->current_function = 0;
}
} break;
case GlobalKeyword::macro:
{
Type* type_argument_buffer[64];
u64 type_argument_count = 0;
unused(type_argument_buffer);
unused(type_argument_count);
ConstantArgument constant_argument_buffer[64];
u64 constant_argument_count = 0;
@ -3463,7 +3264,7 @@ void parse(Module* module)
if (has_value)
{
trap_raw(); // TODO
trap(); // TODO
}
else
{
@ -3520,7 +3321,7 @@ void parse(Module* module)
if (module->last_macro_declaration)
{
assert(module->first_macro_declaration);
trap_raw();
trap();
}
else
{
@ -3568,7 +3369,7 @@ void parse(Module* module)
.line = argument_line,
.column = argument_column,
},
.index = argument_index,
.index = argument_index + 1,
};
argument_count += 1;
@ -3601,7 +3402,7 @@ void parse(Module* module)
Type* struct_type;
if (forward_declaration)
{
trap_raw();
struct_type = forward_declaration;
}
else
{

16
tests/basic_bool_call.bbb Normal file
View File

@ -0,0 +1,16 @@
require = fn (ok: u1) void
{
if (!ok) #trap();
}
bb_bool = fn (x: u8) void
{
require(#truncate(x));
}
[export] main = fn [cc(c)] () s32
{
bb_bool(1);
return 0;
}

View File

@ -0,0 +1,12 @@
[export] main = fn [cc(c)] (argument_count: u32) s32
{
>a: s32 = 0;
if (argument_count != 3 and? argument_count != 2)
{
return 0;
}
else
{
return 1;
}
}

View File

@ -0,0 +1,17 @@
T = struct;
Foo = struct
{
t: &T,
}
T = struct
{
f: Foo,
}
[export] main = fn [cc(c)] () s32
{
>t: T = zero;
t.f.t = &t;
return 0;
}

View File

@ -0,0 +1,14 @@
foo = macro[T](addr: &u64, count: u64) []T
{
>pointer: &T = #pointer_cast(addr);
return pointer[..count];
}
[export] main = fn [cc(c)] () s32
{
>some_var: u64 = 0xaaaaaaaaaaaaaaaa;
>result: []&u8 = foo[&u8](&some_var, 1);
if (#int_from_pointer(result.pointer) != 0xaaaaaaaaaaaaaaaa) #trap();
if (result.length != 1) #trap();
return 0;
}

View File

@ -0,0 +1,11 @@
S = struct
{
self: &S,
}
[export] main = fn [cc(c)] () s32
{
>s: S = zero;
s.self = &s;
return 0;
}