diff --git a/src/converter.zig b/src/converter.zig index eec175e..d4fa20d 100644 --- a/src/converter.zig +++ b/src/converter.zig @@ -2751,6 +2751,9 @@ fn llvm_emit_function_site_attributes(module: *Module, value: *Value) void { } pub noinline fn convert(arena: *Arena, options: ConvertOptions) void { + const build_dir = "bb-cache"; + os.make_directory(build_dir); + var converter = Converter{ .content = options.content, .offset = 0, diff --git a/src/converter_test.zig b/src/converter_test.zig index 413e4bb..59f9f7c 100644 --- a/src/converter_test.zig +++ b/src/converter_test.zig @@ -23,21 +23,88 @@ fn invoke(name: []const u8) !void { inline for (@typeInfo(BuildMode).@"enum".fields) |f| { const build_mode = @field(BuildMode, f.name); inline for ([2]bool{ false, true }) |has_debug_info| { - var tmp_dir = std.testing.tmpDir(.{}); - defer tmp_dir.cleanup(); - const base_path = arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path, "/", name }); - const executable_path = base_path; - const directory_path = arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path }); - const object_path = arena.join_string(&.{ base_path, ".o" }); - try unit_test(arena, allocator, .{ - .object_paths = if (lib.string.equal(name, "c_abi")) &.{ object_path, c_abi_object_path } else &.{object_path}, - .executable_path = executable_path, - .file_path = arena.join_string(&.{ "tests/", name, ".bbb" }), - .name = name, - .directory_path = directory_path, - .build_mode = build_mode, - .has_debug_info = has_debug_info, - }); + // Bootstrap + { + var tmp_dir = std.testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + const base_path = arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path, "/", name }); + const executable_path = base_path; + const directory_path = arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path }); + const object_path = arena.join_string(&.{ base_path, ".o" }); + try unit_test(arena, allocator, .{ + .object_paths = if (lib.string.equal(name, "c_abi")) &.{ object_path, c_abi_object_path } else &.{object_path}, + .executable_path = executable_path, + .file_path = arena.join_string(&.{ "tests/", name, ".bbb" }), + .name = name, + .directory_path = directory_path, + .build_mode = build_mode, + .has_debug_info = has_debug_info, + .self_hosted_path = null, + }); + } + + // Self-hosted + { + var tmp_dir = std.testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + const base_path = arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path, "/", name }); + const executable_path = base_path; + const directory_path = arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path }); + const object_path = arena.join_string(&.{ base_path, ".o" }); + try unit_test(arena, allocator, .{ + .object_paths = if (lib.string.equal(name, "c_abi")) &.{ object_path, c_abi_object_path } else &.{object_path}, + .executable_path = executable_path, + .file_path = arena.join_string(&.{ "tests/", name, ".bbb" }), + .name = name, + .directory_path = directory_path, + .build_mode = build_mode, + .has_debug_info = has_debug_info, + .self_hosted_path = arena.join_string(&.{ "bb-cache/", compiler_basename(arena, build_mode, has_debug_info) }), + }); + } + } + } +} + +fn compiler_basename(arena: *Arena, build_mode: BuildMode, has_debug_info: bool) [:0]const u8 { + return arena.join_string(&.{ "compiler_", @tagName(build_mode), if (has_debug_info) "_di" else "_nodi" }); +} + +var compiler_compiled = false; +fn compile_the_compiler() !void { + if (!compiler_compiled) { + defer compiler_compiled = true; + + if (!lib.GlobalState.initialized) { + lib.GlobalState.initialize(); + } + + comptime assert(lib.is_test); + const allocator = std.testing.allocator; + const arena = lib.global.arena; + const arena_position = arena.position; + defer arena.restore(arena_position); + + inline for (@typeInfo(BuildMode).@"enum".fields) |f| { + const build_mode = @field(BuildMode, f.name); + inline for ([2]bool{ false, true }) |has_debug_info| { + var tmp_dir = std.testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + const base_path = arena.join_string(&.{ "bb-cache/", compiler_basename(arena, build_mode, has_debug_info) }); + const executable_path = base_path; + const directory_path = "bb-cache"; + const object_path = arena.join_string(&.{ base_path, ".o" }); + try unit_test(arena, allocator, .{ + .object_paths = &.{object_path}, + .executable_path = executable_path, + .file_path = arena.join_string(&.{"src/compiler.bbb"}), + .name = "compiler", + .directory_path = directory_path, + .build_mode = build_mode, + .has_debug_info = has_debug_info, + .self_hosted_path = null, + }); + } } } } @@ -50,44 +117,67 @@ const InvokeWrapper = struct { build_mode: BuildMode, has_debug_info: bool, directory_path: [:0]const u8, + self_hosted_path: ?[]const u8, }; -fn unit_test(arena: *Arena, allocator: std.mem.Allocator, options: InvokeWrapper) !void { +fn unit_test(arena: *Arena, allocator: std.mem.Allocator, options: InvokeWrapper) anyerror!void { + const position = arena.position; + defer arena.restore(position); + const file_content = lib.file.read(arena, options.file_path); - converter.convert(arena, .{ - .path = options.file_path, - .content = file_content, - .objects = options.object_paths, - .executable = options.executable_path, - .build_mode = options.build_mode, - .name = options.name, - .has_debug_info = options.has_debug_info, - .target = converter.Target.get_native(), - }); - const run_result = std.process.Child.run(.{ - .allocator = allocator, - .argv = &.{options.executable_path}, - }) catch |err| { - std.debug.print("error: {}\n", .{err}); - const r = try std.process.Child.run(.{ + if (options.self_hosted_path) |self_hosted_path| { + try compile_the_compiler(); + const run_result = try std.process.Child.run(.{ .allocator = allocator, - .argv = &.{ "/usr/bin/ls", "-lasR", options.directory_path }, - .max_output_bytes = std.math.maxInt(usize), + .argv = &.{ + self_hosted_path, + options.file_path, + }, }); - defer allocator.free(r.stdout); - defer allocator.free(r.stderr); - std.debug.print("ls {s} {s}\n", .{ options.directory_path, r.stdout }); - return err; - }; + const success = switch (run_result.term) { + .Exited => |exit_code| exit_code == 0, + else => false, + }; + if (!success) { + std.debug.print("{}\n{}\n", .{ run_result, options }); + return error.compiler_failed_to_run_successfully; + } + } else { + converter.convert(arena, .{ + .path = options.file_path, + .content = file_content, + .objects = options.object_paths, + .executable = options.executable_path, + .build_mode = options.build_mode, + .name = options.name, + .has_debug_info = options.has_debug_info, + .target = converter.Target.get_native(), + }); + const run_result = std.process.Child.run(.{ + .allocator = allocator, + .argv = &.{options.executable_path}, + }) catch |err| { + std.debug.print("error: {}\n", .{err}); + const r = try std.process.Child.run(.{ + .allocator = allocator, + .argv = &.{ "/usr/bin/ls", "-lasR", options.directory_path }, + .max_output_bytes = std.math.maxInt(usize), + }); + defer allocator.free(r.stdout); + defer allocator.free(r.stderr); + std.debug.print("ls {s} {s}\n", .{ options.directory_path, r.stdout }); + return err; + }; - const success = switch (run_result.term) { - .Exited => |exit_code| exit_code == 0, - else => false, - }; - if (!success) { - std.debug.print("{}\n{}\n", .{ run_result, options }); - return error.executable_failed_to_run_successfully; + const success = switch (run_result.term) { + .Exited => |exit_code| exit_code == 0, + else => false, + }; + if (!success) { + std.debug.print("{}\n{}\n", .{ run_result, options }); + return error.executable_failed_to_run_successfully; + } } } diff --git a/src/lib.zig b/src/lib.zig index b963443..13a8ba5 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -515,7 +515,10 @@ pub const os = struct { if (os.is_being_debugged()) { @trap(); } else { - libc.exit(1); + switch (is_test) { + true => @panic("aborting"), + false => libc.exit(1), + } } } @@ -2668,3 +2671,144 @@ pub fn print_string_stdout(str: []const u8) void { pub fn print_string_stderr(str: []const u8) void { os.get_stderr().write(str); } + +pub const panic_struct = switch (is_test) { + true => @import("std").debug.FullPanic(@import("std").debug.defaultPanic), + false => struct { + const abort = os.abort; + pub fn call(_: []const u8, _: ?usize) noreturn { + @branchHint(.cold); + abort(); + } + + pub fn sentinelMismatch(_: anytype, _: anytype) noreturn { + @branchHint(.cold); + abort(); + } + + pub fn unwrapError(_: anyerror) noreturn { + @branchHint(.cold); + abort(); + } + + pub fn outOfBounds(_: usize, _: usize) noreturn { + @branchHint(.cold); + abort(); + } + + pub fn startGreaterThanEnd(_: usize, _: usize) noreturn { + @branchHint(.cold); + abort(); + } + + pub fn inactiveUnionField(_: anytype, _: anytype) noreturn { + @branchHint(.cold); + abort(); + } + + pub fn reachedUnreachable() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn unwrapNull() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn castToNull() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn incorrectAlignment() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn invalidErrorCode() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn castTruncatedData() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn negativeToUnsigned() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn integerOverflow() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn shlOverflow() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn shrOverflow() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn divideByZero() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn exactDivisionRemainder() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn integerPartOutOfBounds() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn corruptSwitch() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn shiftRhsTooBig() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn invalidEnumValue() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn forLenMismatch() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn memcpyLenMismatch() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn memcpyAlias() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn noreturnReturned() noreturn { + @branchHint(.cold); + abort(); + } + + pub fn sliceCastLenRemainder(_: usize) noreturn { + @branchHint(.cold); + abort(); + } + }, +}; diff --git a/src/main.zig b/src/main.zig index 414972b..b2364cd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,143 +5,7 @@ const llvm = @import("LLVM.zig"); const converter = @import("converter.zig"); const Arena = lib.Arena; -pub const panic = struct { - const abort = lib.os.abort; - pub fn call(_: []const u8, _: ?usize) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn sentinelMismatch(_: anytype, _: anytype) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn unwrapError(_: anyerror) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn outOfBounds(_: usize, _: usize) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn startGreaterThanEnd(_: usize, _: usize) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn inactiveUnionField(_: anytype, _: anytype) noreturn { - @branchHint(.cold); - abort(); - } - - pub fn reachedUnreachable() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn unwrapNull() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn castToNull() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn incorrectAlignment() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn invalidErrorCode() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn castTruncatedData() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn negativeToUnsigned() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn integerOverflow() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn shlOverflow() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn shrOverflow() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn divideByZero() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn exactDivisionRemainder() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn integerPartOutOfBounds() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn corruptSwitch() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn shiftRhsTooBig() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn invalidEnumValue() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn forLenMismatch() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn memcpyLenMismatch() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn memcpyAlias() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn noreturnReturned() noreturn { - @branchHint(.cold); - abort(); - } - - pub fn sliceCastLenRemainder(_: usize) noreturn { - @branchHint(.cold); - abort(); - } -}; +pub const panic = lib.panic_struct; pub fn main(argc: c_int, argv: [*:null]const ?[*:0]const u8) callconv(.C) c_int { if (argc != 2) { @@ -167,7 +31,6 @@ pub fn main(argc: c_int, argv: [*:null]const ?[*:0]const u8) callconv(.C) c_int const arena = lib.global.arena; const build_dir = "bb-cache"; - os.make_directory(build_dir); const output_path_base = arena.join_string(&.{ build_dir, "/", base_name, "_", @tagName(lib.optimization_mode) }); const output_object_path = arena.join_string(&.{ output_path_base, ".o" }); const output_executable_path = output_path_base;