bloat-buster/src/compiler.cpp
2025-05-04 16:52:34 -06:00

398 lines
12 KiB
C++

#include <compiler.h>
fn void compile(Arena* arena, Options options)
{
auto base_allocation_type_count = i128_offset + // 64 * 2 for basic integer types
2 + // u128, s128
2; // void, noreturn
auto base_type_allocation = arena_allocate<Type>(arena, base_allocation_type_count);
auto* type_it = base_type_allocation.pointer;
bool signs[] = {false, true};
Type* previous = 0;
for (bool sign: signs)
{
for (u32 bit_index = 0; bit_index < 64; bit_index += 1)
{
auto bit_count = bit_index + 1;
auto first_digit = (u8)(bit_count < 10 ? bit_count % 10 + '0' : bit_count / 10 + '0');
auto second_digit = (u8)(bit_count > 9 ? bit_count % 10 + '0' : 0);
u8 name_buffer[] = { u8(sign ? 's' : 'u'), first_digit, second_digit };
u64 name_length = 2 + (bit_count > 9);
auto name_stack = String{name_buffer, name_length};
auto name = arena_duplicate_string(arena, name_stack);
*type_it = {
.integer = {
.bit_count = bit_count,
.is_signed = sign,
},
.id = TypeId::integer,
.name = name,
};
if (previous) previous->next = type_it;
previous = type_it;
type_it += 1;
}
}
for (bool sign: signs)
{
auto name = sign ? str("s128") : str("u128");
*type_it = {
.integer = {
.bit_count = 128,
.is_signed = sign,
},
.id = TypeId::integer,
.name = name,
.next = previous,
};
if (previous) previous->next = type_it;
previous = type_it;
type_it += 1;
}
auto void_type = type_it;
type_it += 1;
auto noreturn_type = type_it;
type_it += 1;
assert(type_it - base_type_allocation.pointer == base_allocation_type_count);
previous->next = void_type;
*void_type = {
.id = TypeId::void_type,
.name = str("void"),
.next = noreturn_type,
};
*noreturn_type = {
.id = TypeId::noreturn,
.name = str("noreturn"),
};
auto module = Module{
.arena = arena,
.content = options.content,
.first_type = base_type_allocation.pointer,
.last_type = noreturn_type,
.scope = {
.kind = ScopeKind::global,
},
.name = options.name,
.path = options.path,
.executable = options.executable,
.objects = options.objects,
.target = options.target,
.build_mode = options.build_mode,
.has_debug_info = options.has_debug_info,
.silent = options.silent,
};
module.void_value = new_value(&module);
*module.void_value = {
.type = void_type,
.id = ValueId::infer_or_ignore,
};
parse(&module);
emit(&module);
}
fn void compile_file(Arena* arena, Compile options)
{
auto relative_file_path = options.relative_file_path;
if (relative_file_path.length < 5)
{
fail();
}
auto extension_start = string_last_character(relative_file_path, '.');
if (extension_start == string_no_match)
{
fail();
}
if (!relative_file_path(extension_start).equal(str(".bbb")))
{
fail();
}
auto separator_index = string_last_character(relative_file_path, '/');
separator_index = separator_index == string_no_match ? 0 : separator_index;
auto base_start = separator_index + (separator_index != 0 || relative_file_path[separator_index] == '/');
auto base_name = relative_file_path(base_start, extension_start);
auto is_compiler = relative_file_path.equal(str("src/compiler.bbb"));
String output_path_dir_parts[] = {
str(base_cache_dir),
is_compiler ? str("/compiler") : str("/"),
build_mode_to_string(options.build_mode),
str("_"),
options.has_debug_info ? str("di") : str("nodi"),
};
auto output_path_dir = arena_join_string(arena, array_to_slice(output_path_dir_parts));
make_directory(base_cache_dir);
if (is_compiler)
{
make_directory(base_cache_dir "/compiler");
}
make_directory(cstr(output_path_dir));
String output_path_base_parts[] = {
output_path_dir,
str("/"),
base_name,
};
auto output_path_base = arena_join_string(arena, array_to_slice(output_path_base_parts));
String output_object_path_parts[] = {
output_path_base,
str(".o"),
};
auto output_object_path = arena_join_string(arena, array_to_slice(output_object_path_parts));
auto output_executable_path = output_path_base;
auto file_content = file_read(arena, relative_file_path);
auto file_path = path_absolute(arena, relative_file_path);
auto c_abi_object_path = str("build/c_abi.o");
String objects[] = {
c_abi_object_path,
output_object_path,
};
Slice<String> object_slice = array_to_slice(objects);
object_slice = object_slice(!base_name.equal(str("c_abi")));
compile(arena, {
.content = file_content,
.path = file_path,
.executable = output_executable_path,
.name = base_name,
.objects = object_slice,
.target = {
.cpu = CPUArchitecture::x86_64,
.os = OperatingSystem::linux_,
},
.build_mode = options.build_mode,
.has_debug_info = options.has_debug_info,
.silent = options.silent,
});
}
global_variable String names[] = {
str("minimal"),
str("comments"),
str("constant_add"),
str("constant_and"),
str("constant_div"),
str("constant_mul"),
str("constant_rem"),
str("constant_or"),
str("constant_sub"),
str("constant_xor"),
str("constant_shift_left"),
str("constant_shift_right"),
str("minimal_stack"),
str("minimal_stack_arithmetic"),
str("minimal_stack_arithmetic2"),
str("minimal_stack_arithmetic3"),
str("stack_negation"),
str("stack_add"),
str("stack_sub"),
str("extend"),
str("integer_max"),
str("integer_hex"),
str("basic_pointer"),
str("basic_call"),
str("basic_branch"),
str("basic_array"),
str("basic_enum"),
str("basic_slice"),
str("basic_string"),
str("basic_varargs"),
str("basic_while"),
str("pointer"),
str("pointer_cast"),
str("u1_return"),
str("local_type_inference"),
str("global"),
str("function_pointer"),
str("extern"),
str("byte_size"),
str("argv"),
str("assignment_operators"),
str("not_pointer"),
str("bits"),
str("bits_no_backing_type"),
str("bits_return_u1"),
str("bits_zero"),
str("comparison"),
str("global_struct"),
str("if_no_else"),
str("if_no_else_void"),
str("indirect"),
str("indirect_struct"),
str("indirect_varargs"),
str("ret_c_bool"),
str("return_type_builtin"),
str("return_u64_u64"),
str("select"),
str("slice"),
str("small_struct_ints"),
str("struct_assignment"),
str("struct"),
str("struct_u64_u64"),
str("struct_varargs"),
str("struct_zero"),
str("unreachable"),
str("varargs"),
str("c_abi0"),
str("c_abi1"),
str("c_med_struct_ints"),
str("c_ret_struct_array"),
str("c_split_struct_ints"),
str("c_string_to_slice"),
str("c_struct_with_array"),
str("c_function_pointer"),
str("c_abi"),
};
void entry_point(Slice<const char*> arguments, Slice<const char*> environment)
{
Arena* arena = arena_initialize_default(8 * mb);
if (arguments.length < 2)
{
fail_with_message(str("error: Not enough arguments\n"));
}
String command_string = c_string_to_slice(arguments[1]);
String command_strings[] = {
str("compile"),
str("test"),
};
static_assert(array_length(command_strings) == (u64)Command::count);
backing_type(Command) i;
for (i = 0; i < (backing_type(Command))Command::count; i += 1)
{
String candidate = command_strings[i];
if (candidate.equal(command_string))
{
break;
}
}
auto command = (Command)i;
switch (command)
{
case Command::compile:
{
if (arguments.length < 3)
{
fail_with_message(str("Not enough arguments for command 'compile'\n"));
}
auto build_mode = BuildMode::debug_none;
auto has_debug_info = true;
if (arguments.length >= 4)
{
auto build_mode_string = c_string_to_slice(arguments[3]);
String build_mode_strings[] = {
str("debug_none"),
str("debug"),
str("soft_optimize"),
str("optimize_for_speed"),
str("optimize_for_size"),
str("aggressively_optimize_for_speed"),
str("aggressively_optimize_for_size"),
};
backing_type(BuildMode) i;
for (i = 0; i < (backing_type(BuildMode))BuildMode::count; i += 1)
{
String candidate = build_mode_strings[i];
if (build_mode_string.equal(candidate))
{
break;
}
}
build_mode = (BuildMode)i;
if (build_mode == BuildMode::count)
{
fail_with_message(str("Invalid build mode\n"));
}
}
if (arguments.length >= 5)
{
auto has_debug_info_string = c_string_to_slice(arguments[3]);
if (has_debug_info_string.equal(str("true")))
{
has_debug_info = true;
}
else if (has_debug_info_string.equal(str("false")))
{
has_debug_info = false;
}
else
{
fail_with_message(str("Wrong value for has_debug_info\n"));
}
}
auto relative_file_path = c_string_to_slice(arguments[2]);
compile_file(arena, {
.relative_file_path = relative_file_path,
.build_mode = build_mode,
.has_debug_info = has_debug_info,
.silent = false,
});
} break;
case Command::test:
{
// TODO: provide more arguments
if (arguments.length != 2)
{
fail_with_message(str("error: 'test' command takes no arguments"));
}
// TODO: introduce build mode, debug info switch, etc
for (auto name: names)
{
auto position = arena->position;
String relative_file_path_parts[] = { str("tests/"), name, str(".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,
});
// TODO: introduce test
arena_restore(arena, position);
}
} break;
case Command::count:
{
fail_with_message(str("error: Invalid command\n"));
} break;
}
}