407 lines
8.5 KiB
Plaintext
407 lines
8.5 KiB
Plaintext
[extern] memcmp = fn [cc(c)] (a: &u8, b: &u8, byte_count: u64) s32;
|
|
[extern] exit = fn [cc(c)] (exit_code: s32) noreturn;
|
|
|
|
assert = fn (ok: u1) void
|
|
{
|
|
if (!ok)
|
|
{
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
align_forward = fn (value: u64, alignment: u64) u64
|
|
{
|
|
assert(alignment != 0);
|
|
>mask = alignment - 1;
|
|
>result = (value + mask) & ~mask;
|
|
return result;
|
|
}
|
|
|
|
string_no_match = #integer_max(u64);
|
|
|
|
c_string_length = fn (c_string: &u8) u64
|
|
{
|
|
>it = c_string;
|
|
|
|
while (it.&)
|
|
{
|
|
it = it + 1;
|
|
}
|
|
|
|
return #int_from_pointer(it) - #int_from_pointer(c_string);
|
|
}
|
|
|
|
c_string_to_slice = fn (c_string: &u8) []u8
|
|
{
|
|
>length = c_string_length(c_string);
|
|
return c_string[0..length];
|
|
}
|
|
|
|
string_equal = fn(a: []u8, b: []u8) u1
|
|
{
|
|
>result: #ReturnType = 0;
|
|
|
|
if (a.length == b.length)
|
|
{
|
|
result = memcmp(a.pointer, b.pointer, a.length) == 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
string_last_character = fn(string: []u8, character: u8) u64
|
|
{
|
|
>i = string.length;
|
|
|
|
while (i > 0)
|
|
{
|
|
i -= 1;
|
|
|
|
if (string[i] == character)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return string_no_match;
|
|
}
|
|
|
|
OS_Linux_PROT = bits u32
|
|
{
|
|
read: u1,
|
|
write: u1,
|
|
execute: u1,
|
|
sem: u1,
|
|
_: u28,
|
|
}
|
|
|
|
OS_Linux_MAP_Type = enum u4
|
|
{
|
|
shared = 0x1,
|
|
private = 0x2,
|
|
shared_validate = 0x3,
|
|
}
|
|
|
|
OS_Linux_MAP = bits u32
|
|
{
|
|
type: OS_Linux_MAP_Type,
|
|
fixed: u1,
|
|
anonymous: u1,
|
|
bit_32: u1,
|
|
_: u1,
|
|
grows_down: u1,
|
|
_: u2,
|
|
deny_write: u1,
|
|
executable: u1,
|
|
locked: u1,
|
|
no_reserve: u1,
|
|
populate: u1,
|
|
non_block: u1,
|
|
stack: u1,
|
|
huge_tlb: u1,
|
|
sync: u1,
|
|
fixed_noreplace: u1,
|
|
_: u5,
|
|
uninitialized: u1,
|
|
_: u5,
|
|
}
|
|
|
|
[extern] mmap = fn [cc(c)] (address: u64, size: u64, protection: OS_Linux_PROT, map: OS_Linux_MAP, file_descriptor: s32, offset: s64) &u8;
|
|
[extern] mprotect = fn [cc(c)] (address: u64, size: u64, protection: OS_Linux_PROT) s32;
|
|
|
|
OS_ProtectionFlags = bits
|
|
{
|
|
read: u1,
|
|
write: u1,
|
|
execute: u1,
|
|
}
|
|
|
|
OS_MapFlags = bits
|
|
{
|
|
private: u1,
|
|
anonymous: u1,
|
|
no_reserve: u1,
|
|
populate: u1,
|
|
}
|
|
|
|
os_linux_protection_flags = fn(map_flags: OS_ProtectionFlags) OS_Linux_PROT
|
|
{
|
|
return {
|
|
.read = map_flags.read,
|
|
.write = map_flags.write,
|
|
.execute = map_flags.execute,
|
|
zero,
|
|
};
|
|
}
|
|
|
|
os_linux_map_flags = fn(map_flags: OS_MapFlags) OS_Linux_MAP
|
|
{
|
|
return {
|
|
.type = #select(map_flags.private, .private, .shared),
|
|
.anonymous = map_flags.anonymous,
|
|
.no_reserve = map_flags.no_reserve,
|
|
.populate = map_flags.populate,
|
|
zero,
|
|
};
|
|
}
|
|
|
|
os_reserve = fn (base: u64, size: u64, protection: OS_ProtectionFlags, map: OS_MapFlags) &u8
|
|
{
|
|
>protection_flags = os_linux_protection_flags(protection);
|
|
>map_flags = os_linux_map_flags(map);
|
|
>address = mmap(base, size, protection_flags, map_flags, -1, 0);
|
|
if (#int_from_pointer(address) == #integer_max(u64))
|
|
{
|
|
unreachable;
|
|
}
|
|
|
|
return address;
|
|
}
|
|
|
|
os_commit = fn (address: u64, size: u64, protection: OS_ProtectionFlags) void
|
|
{
|
|
>protection_flags = os_linux_protection_flags(protection);
|
|
>result = mprotect(address, size, protection_flags);
|
|
if (result != 0)
|
|
{
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
Arena = struct
|
|
{
|
|
reserved_size: u64,
|
|
position: u64,
|
|
os_position: u64,
|
|
granularity: u64,
|
|
reserved: [32]u8,
|
|
}
|
|
|
|
minimum_position: u64 = #byte_size(Arena);
|
|
|
|
ArenaInitialization = struct
|
|
{
|
|
reserved_size: u64,
|
|
granularity: u64,
|
|
initial_size: u64,
|
|
}
|
|
|
|
arena_initialize = fn (initialization: ArenaInitialization) &Arena
|
|
{
|
|
>protection_flags: OS_ProtectionFlags = {
|
|
.read = 1,
|
|
.write = 1,
|
|
zero,
|
|
};
|
|
|
|
>map_flags: OS_MapFlags = {
|
|
.private = 1,
|
|
.anonymous = 1,
|
|
.no_reserve = 1,
|
|
.populate = 0,
|
|
};
|
|
|
|
>arena: &Arena = #pointer_cast(os_reserve(0, initialization.reserved_size, protection_flags, map_flags));
|
|
os_commit(#int_from_pointer(arena), initialization.initial_size, {
|
|
.read = 1,
|
|
.write = 1,
|
|
zero,
|
|
});
|
|
|
|
arena.& = {
|
|
.reserved_size = initialization.reserved_size,
|
|
.position = minimum_position,
|
|
.os_position = initialization.initial_size,
|
|
.granularity = initialization.granularity,
|
|
zero,
|
|
};
|
|
|
|
return arena;
|
|
}
|
|
|
|
arena_initialize_default = fn (initial_size: u64) &Arena
|
|
{
|
|
return arena_initialize({
|
|
.reserved_size = 4 * 1024 * 1024 * 1024,
|
|
.granularity = 4 * 1024,
|
|
.initial_size = initial_size,
|
|
});
|
|
}
|
|
|
|
arena_allocate_bytes = fn (arena: &Arena, size: u64, alignment: u64) &u8
|
|
{
|
|
>aligned_offset = align_forward(arena.position, alignment);
|
|
>aligned_size_after = aligned_offset + size;
|
|
|
|
if (aligned_size_after > arena.os_position)
|
|
{
|
|
#trap();
|
|
}
|
|
|
|
>arena_byte_pointer: &u8 = #pointer_cast(arena);
|
|
>result = arena_byte_pointer + aligned_offset;
|
|
arena.position = aligned_size_after;
|
|
assert(arena.position <= arena.os_position);
|
|
|
|
return result;
|
|
}
|
|
|
|
GlobalState = struct
|
|
{
|
|
arena: &Arena,
|
|
}
|
|
|
|
global_state: GlobalState = undefined;
|
|
|
|
global_state_initialize = fn () void
|
|
{
|
|
global_state = {
|
|
.arena = arena_initialize_default(2 * 1024 * 1024),
|
|
};
|
|
}
|
|
|
|
fail = fn () noreturn
|
|
{
|
|
exit(1);
|
|
}
|
|
|
|
CompilerCommand = enum
|
|
{
|
|
compile,
|
|
test,
|
|
}
|
|
|
|
BuildMode = enum
|
|
{
|
|
debug_none,
|
|
debug_fast,
|
|
debug_size,
|
|
soft_optimize,
|
|
optimize_for_speed,
|
|
optimize_for_size,
|
|
aggressively_optimize_for_speed,
|
|
aggressively_optimize_for_size,
|
|
}
|
|
|
|
CompileFile = struct
|
|
{
|
|
relative_file_path: []u8,
|
|
build_mode: BuildMode,
|
|
has_debug_info: u1,
|
|
silent: u1,
|
|
}
|
|
|
|
compile_file = fn (arena: &Arena, compile: CompileFile) void
|
|
{
|
|
>relative_file_path = compile.relative_file_path;
|
|
if (relative_file_path.length < 5)
|
|
{
|
|
fail();
|
|
}
|
|
|
|
>extension_start = string_last_character(relative_file_path, '.');
|
|
if (extension_start == string_no_match)
|
|
{
|
|
fail();
|
|
}
|
|
|
|
if (!string_equal(relative_file_path[extension_start..], ".bbb"))
|
|
{
|
|
fail();
|
|
}
|
|
|
|
>separator_index = string_last_character(relative_file_path, '/');
|
|
if (separator_index == string_no_match)
|
|
{
|
|
separator_index = 0;
|
|
}
|
|
|
|
>base_start = separator_index + #extend(separator_index != 0 or relative_file_path[separator_index] == '/');
|
|
>base_name = relative_file_path[base_start..extension_start];
|
|
|
|
>is_compiler = string_equal(relative_file_path, "src/compiler.bbb");
|
|
}
|
|
|
|
[export] main = fn [cc(c)] (argument_count: u32, argv: &&u8) s32
|
|
{
|
|
global_state_initialize();
|
|
>arena = global_state.arena;
|
|
|
|
if (argument_count < 2)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
>command_string = c_string_to_slice(argv[1]);
|
|
|
|
>command_string_to_enum = #string_to_enum(CompilerCommand, command_string);
|
|
if (!command_string_to_enum.is_valid)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
>command = command_string_to_enum.enum_value;
|
|
switch (command)
|
|
{
|
|
.compile =>
|
|
{
|
|
if (argument_count < 3)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
>build_mode: BuildMode = .debug_none;
|
|
>has_debug_info: u1 = 1;
|
|
|
|
if (argument_count >= 4)
|
|
{
|
|
>build_mode_string_to_enum = #string_to_enum(BuildMode, c_string_to_slice(argv[3]));
|
|
if (!build_mode_string_to_enum.is_valid)
|
|
{
|
|
return 1;
|
|
}
|
|
build_mode = build_mode_string_to_enum.enum_value;
|
|
}
|
|
|
|
if (argument_count >= 5)
|
|
{
|
|
>has_debug_info_string = c_string_to_slice(argv[4]);
|
|
if (string_equal(has_debug_info_string, "true"))
|
|
{
|
|
has_debug_info = 1;
|
|
}
|
|
else if (string_equal(has_debug_info_string, "false"))
|
|
{
|
|
has_debug_info = 0;
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
>relative_file_path_pointer = argv[2];
|
|
if (!relative_file_path_pointer)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
>relative_file_path = c_string_to_slice(relative_file_path_pointer);
|
|
|
|
compile_file(arena, {
|
|
.relative_file_path = relative_file_path,
|
|
.build_mode = build_mode,
|
|
.has_debug_info = has_debug_info,
|
|
.silent = 0,
|
|
});
|
|
},
|
|
.test =>
|
|
{
|
|
// TODO
|
|
#trap();
|
|
},
|
|
}
|
|
|
|
return 0;
|
|
}
|