From 7ed53949575bae3b14ecc55d0ae1e1f194d496c8 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sun, 20 Apr 2025 21:20:12 -0600 Subject: [PATCH] wip --- src/bootstrap.zig | 34 +++- src/compiler.bbb | 305 +++++++++++++++++++++++++++++++++- src/main.zig | 1 + tests/return_small_struct.bbb | 20 +++ 4 files changed, 346 insertions(+), 14 deletions(-) create mode 100644 tests/return_small_struct.bbb diff --git a/src/bootstrap.zig b/src/bootstrap.zig index 5296927..aad8ab7 100644 --- a/src/bootstrap.zig +++ b/src/bootstrap.zig @@ -516,6 +516,7 @@ pub const Type = struct { .integer => |integer| integer.signed, .bits => |bits| bits.backing_type.is_signed(), .enumerator => |enumerator| enumerator.backing_type.is_signed(), + .alias => |alias| alias.type.is_signed(), else => @trap(), }; } @@ -602,6 +603,7 @@ pub const Type = struct { .array => |array| array.element_type.get_bit_size() * array.element_count, .structure => |structure| structure.bit_size, .enumerator => |enumerator| enumerator.backing_type.get_bit_size(), + .alias => |alias| alias.type.get_bit_size(), else => @trap(), }; return bit_size; @@ -3880,7 +3882,7 @@ pub const Module = struct { const backing_type = maybe_backing_type orelse blk: { const bits_needed = 64 - @clz(highest_value); - const int_type = module.integer_type(bits_needed, false); + const int_type = module.integer_type(if (bits_needed == 0) 1 else bits_needed, false); break :blk int_type; }; @@ -5472,7 +5474,10 @@ pub const Module = struct { pub fn check_types(module: *Module, expected_type: *Type, source_type: *Type) void { if (expected_type != source_type) { const dst_p_src_i = expected_type.bb == .pointer and source_type.bb == .integer; - const result = dst_p_src_i; + const dst_alias_src_not = expected_type.bb == .alias and expected_type.bb.alias.type == source_type; + const src_alias_dst_not = source_type.bb == .alias and source_type.bb.alias.type == expected_type; + const both_alias_to_same_type = expected_type.bb == .alias and source_type.bb == .alias and expected_type.bb.alias.type == source_type.bb.alias.type; + const result = dst_p_src_i or dst_alias_src_not or src_alias_dst_not or both_alias_to_same_type; if (!result) { module.report_error(); } @@ -6782,7 +6787,11 @@ pub const Module = struct { } const llvm_value = extended_value.llvm orelse unreachable; const destination_type = value_type.llvm.abi.?; - const extension_instruction = switch (extended_value.type.?.bb.integer.signed) { + const extension_type = switch (extended_value.type.?.bb) { + .alias => |alias| alias.type, + else => extended_value.type.?, + }; + const extension_instruction = switch (extension_type.bb.integer.signed) { true => module.llvm.builder.create_sign_extend(llvm_value, destination_type), false => module.llvm.builder.create_zero_extend(llvm_value, destination_type), }; @@ -8794,7 +8803,8 @@ pub const Abi = struct { switch (ty.bb) { .void, .noreturn => result[current_index] = .none, - .bits => result[current_index] = .integer, + .bits => |bits| return classify(bits.backing_type, options), + .enumerator => |enumerator| return classify(enumerator.backing_type, options), .pointer => result[current_index] = .integer, .integer => |integer| { if (integer.bit_count <= 64) { @@ -8930,6 +8940,9 @@ pub const Abi = struct { fn get_int_type_at_offset(module: *Module, ty: *Type, offset: u32, source_type: *Type, source_offset: u32) *Type { switch (ty.bb) { + .enumerator => |enumerator| { + return get_int_type_at_offset(module, enumerator.backing_type, offset, if (source_type == ty) enumerator.backing_type else source_type, source_offset); + }, .bits => |bits| { return get_int_type_at_offset(module, bits.backing_type, offset, if (source_type == ty) bits.backing_type else source_type, source_offset); }, @@ -8945,7 +8958,9 @@ pub const Abi = struct { } }, else => { - const byte_count = @min(ty.get_byte_size() - source_offset, 8); + const original_byte_count = ty.get_byte_size(); + assert(original_byte_count != source_offset); + const byte_count = @min(original_byte_count - source_offset, 8); const bit_count = byte_count * 8; return module.integer_type(@intCast(bit_count), integer_type.signed); }, @@ -8954,7 +8969,12 @@ pub const Abi = struct { .pointer => return if (offset == 0) ty else @trap(), .structure => { if (get_member_at_offset(ty, offset)) |field| { - return get_int_type_at_offset(module, field.type, @intCast(offset - field.byte_offset), source_type, source_offset); + // TODO: this is a addition of mine, since we don't allow arbitrary-bit fields inside structs + const field_type = switch (field.type.bb) { + .integer, .enumerator => module.align_integer_type(field.type), + else => field.type, + }; + return get_int_type_at_offset(module, field_type, @intCast(offset - field.byte_offset), source_type, source_offset); } unreachable; }, @@ -8964,7 +8984,7 @@ pub const Abi = struct { const element_offset = (offset / element_size) * element_size; return get_int_type_at_offset(module, element_type, @intCast(offset - element_offset), source_type, source_offset); }, - .alias => |alias| return get_int_type_at_offset(module, alias.type, offset, source_type, source_offset), + .alias => |alias| return get_int_type_at_offset(module, alias.type, offset, if (ty == source_type) alias.type else source_type, source_offset), else => |t| @panic(@tagName(t)), } diff --git a/src/compiler.bbb b/src/compiler.bbb index 52f2bac..63910cb 100644 --- a/src/compiler.bbb +++ b/src/compiler.bbb @@ -1,9 +1,85 @@ mode_t = typealias u64; +int = typealias s32; +usize = typealias u64; +ssize = typealias s64; +File = typealias s32; -[extern] memcmp = fn [cc(c)] (a: &u8, b: &u8, byte_count: u64) s32; +uid_t = typealias u32; +gid_t = typealias u32; +off_t = typealias s64; +ino_t = typealias u64; +dev_t = typealias u64; + +timespec = struct +{ + seconds: ssize, + nanoseconds: ssize, +}; + +Stat = struct +{ + dev: dev_t, + ino: ino_t, + nlink: usize, + + mode: u32, + uid: uid_t, + gid: gid_t, + _: u32, + rdev: dev_t, + size: off_t, + blksize: ssize, + blocks: s64, + + atim: timespec, + mtime: timespec, + ctim: timespec, + _: [3]ssize, +} + +OAccessMode = enum u2 +{ + read_only = 0, + write_only = 1, + read_write = 2, +} + +O = bits u32 +{ + access_mode: OAccessMode, + _: u4, + creat: u1, + excl: u1, + noctty: u1, + trunc: u1, + append: u1, + nonblock: u1, + dsync: u1, + async: u1, + direct: u1, + _: u1, + directory: u1, + nofollow: u1, + noatime: u1, + cloexec: u1, + sync: u1, + path: u1, + tmpfile: u1, + _: u9, +} + +[extern] memcmp = fn [cc(c)] (a: &u8, b: &u8, byte_count: usize) int; [extern] memcpy = fn [cc(c)] (destination: &u8, source: &u8, byte_count: u64) &u8; -[extern] exit = fn [cc(c)] (exit_code: s32) noreturn; -[extern] mkdir = fn [cc(c)] (path: &u8, mode: mode_t) s32; +[extern] exit = fn [cc(c)] (exit_code: int) noreturn; + +[extern] realpath = fn [cc(c)] (source_path: &u8, resolved_path: &u8) &u8; +[extern] mkdir = fn [cc(c)] (path: &u8, mode: mode_t) int; + +[extern] open = fn [cc(c)] (path: &u8, o: O, ...) int; +[extern] close = fn [cc(c)] (fd: File) int; +[extern] fstat = fn [cc(c)] (fd: File, s: &Stat) int; +[extern] write = fn [cc(c)] (fd: File, pointer: &u8, byte_count: u64) ssize; +[extern] read = fn [cc(c)] (fd: File, pointer: &u8, byte_count: u64) ssize; assert = fn (ok: u1) void { @@ -177,6 +253,109 @@ os_make_directory = fn (path: &u8) void >result = mkdir(path, 0o755); } +OpenFlags = bits +{ + truncate: u1, + execute: u1, + write: u1, + read: u1, + create: u1, + directory: u1, +} + +OpenPermissions = bits +{ + read: u1, + write: u1, + execute: u1, +} + +os_file_open = fn (path: &u8, flags: OpenFlags, permissions: OpenPermissions) File +{ + >access_mode: OAccessMode = undefined; + if (flags.read and flags.write) + { + access_mode = .read_write; + } + else if (flags.read) + { + access_mode = .read_only; + } + else if (flags.write) + { + access_mode = .write_only; + } + else + { + unreachable; + } + + >o: O = { + .access_mode = access_mode, + .trunc = flags.truncate, + .creat = flags.create, + .directory = flags.directory, + }; + + >mode: mode_t = #select(permissions.execute, 0o755, 0o644); + >fd = open(path, o, mode); + return fd; +} + +os_file_close = fn (fd: File) void +{ + >result = close(fd); + assert(result == 0); +} + +os_file_is_valid = fn (fd: File) u1 +{ + return fd >= 0; +} + +os_file_get_size = fn (fd: File) u64 +{ + >stat: Stat = undefined; + >result = fstat(fd, &stat); + assert(result == 0); + return #extend(stat.size); +} + +os_file_read_partially = fn (fd: File, pointer: &u8, length: u64) u64 +{ + >result = read(fd, pointer, length); + assert(result > 0); + return #extend(result); +} + +os_file_read = fn (fd: File, buffer: []u8, byte_count: u64) void +{ + assert(byte_count <= buffer.length); + + >total_read_byte_count: u64 = 0; + + while (total_read_byte_count < byte_count) + { + >read_byte_count = os_file_read_partially(fd, buffer.pointer + total_read_byte_count, byte_count - total_read_byte_count); + total_read_byte_count += read_byte_count; + } +} + +os_path_absolute_stack = fn (buffer: []u8, relative_file_path: &u8) []u8 +{ + >syscall_result = realpath(relative_file_path, buffer.pointer); + + >result: []u8 = zero; + + if (syscall_result) + { + result = c_string_to_slice(syscall_result); + assert(result.length < buffer.length); + } + + return result; +} + Arena = struct { reserved_size: u64, @@ -264,6 +443,13 @@ arena_allocate_bytes = fn (arena: &Arena, size: u64, alignment: u64) &u8 return result; } +arena_duplicate_string = fn (arena: &Arena, string: []u8) []u8 +{ + >result = arena_allocate_bytes(arena, string.length + 1, 1); + memcpy(result, string.pointer, string.length); + return result[..string.length]; +} + arena_join_string = fn (arena: &Arena, pieces: [][]u8) []u8 { >size: u64 = 0; @@ -288,6 +474,32 @@ arena_join_string = fn (arena: &Arena, pieces: [][]u8) []u8 return pointer[..size]; } +file_read = fn (arena: &Arena, path: []u8) []u8 +{ + >fd = os_file_open(path.pointer, { .read = 1 }, { .read = 1 }); + + >result: []u8 = zero; + + if (os_file_is_valid(fd)) + { + >file_size = os_file_get_size(fd); + >file_buffer = arena_allocate_bytes(arena, file_size, 1); + result = file_buffer[..file_size]; + os_file_read(fd, result, file_size); + os_file_close(fd); + } + + return result; +} + +path_absolute = fn (arena: &Arena, relative_file_path: &u8) []u8 +{ + >buffer: [4096]u8 = undefined; + >stack_slice = os_path_absolute_stack(buffer[..], relative_file_path); + >result = arena_duplicate_string(arena, stack_slice); + return result; +} + GlobalState = struct { arena: &Arena, @@ -335,9 +547,47 @@ CompileFile = struct base_cache_dir = "bb-cache"; -compile_file = fn (arena: &Arena, compile: CompileFile) void +CPUArchitecture = enum { - >relative_file_path = compile.relative_file_path; + x86_64, +} + +OperatingSystem = enum +{ + linux, +} + +Target = struct +{ + cpu: CPUArchitecture, + os: OperatingSystem, +} + +target_get_native = fn () Target +{ + return { .cpu = .x86_64, .os = .linux }; +} + +CompileOptions = struct +{ + content: []u8, + path: []u8, + executable: []u8, + name: []u8, + objects: [][]u8, + target: Target, + build_mode: BuildMode, + has_debug_info: u1, + silent: u1, +} + +compile = fn (arena: &Arena, options: CompileOptions) void +{ +} + +compile_file = fn (arena: &Arena, compile_options: CompileFile) void +{ + >relative_file_path = compile_options.relative_file_path; if (relative_file_path.length < 5) { fail(); @@ -367,10 +617,51 @@ compile_file = fn (arena: &Arena, compile: CompileFile) void >outputh_path_dir = arena_join_string(arena, [ base_cache_dir, #select(is_compiler, "/compiler/", "/"), - #enum_name(compile.build_mode), + #enum_name(compile_options.build_mode), "_", - #select(compile.has_debug_info, "di", "nodi"), + #select(compile_options.has_debug_info, "di", "nodi"), ][..]); + + os_make_directory(base_cache_dir.pointer); + if (is_compiler) + { + >compiler_dir = arena_join_string(arena, [ base_cache_dir, "/compiler" ][..]); + os_make_directory(compiler_dir.pointer); + } + os_make_directory(outputh_path_dir.pointer); + + >outputh_path_base = arena_join_string(arena, [ outputh_path_dir, "/", base_name ][..]); + + >output_object_path = arena_join_string(arena, [ outputh_path_base, ".o" ][..]); + >output_executable_path = outputh_path_base; + + >file_content = file_read(arena, relative_file_path); + >file_path = path_absolute(arena, relative_file_path.pointer); + >c_abi_object_path = ""; // TODO + + >objects: [][]u8 = undefined; + if (string_equal(base_name, "c_abi")) + { + objects = [ output_object_path, c_abi_object_path ][..]; + } + else + { + objects = [ output_object_path ][..]; + } + + >options: CompileOptions = { + .executable = output_executable_path, + .objects = objects, + .name = base_name, + .build_mode = compile_options.build_mode, + .content = file_content, + .path = file_path, + .has_debug_info = compile_options.has_debug_info, + .target = target_get_native(), + .silent = compile_options.silent, + }; + + compile(arena, options); } [export] main = fn [cc(c)] (argument_count: u32, argv: &&u8) s32 diff --git a/src/main.zig b/src/main.zig index 4a25e2f..c65fbf1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -322,4 +322,5 @@ const names = &[_][]const u8{ "slice_of_slices", "type_alias", "integer_formats", + "return_small_struct", }; diff --git a/tests/return_small_struct.bbb b/tests/return_small_struct.bbb new file mode 100644 index 0000000..26f9e5c --- /dev/null +++ b/tests/return_small_struct.bbb @@ -0,0 +1,20 @@ +S = struct +{ + a: u1, + b: u1, +} + +foo = fn [cc(c)] () S +{ + return { .a = 1, .b = 0 }; +} + +[export] main = fn [cc(c)] () s32 +{ + >s = foo(); + + if (s.a != 1) #trap(); + if (s.b != 0) #trap(); + + return 0; +}