This commit is contained in:
agra
2026-02-22 17:24:04 +02:00
parent 775dcb44cc
commit d3e574eae5
38 changed files with 16135 additions and 33 deletions

View File

@@ -25,7 +25,7 @@ pub fn main(init: std.process.Init) !void {
var lib_paths = std.ArrayList([]const u8).empty;
var show_timing: bool = false;
var explicit_opt: bool = false;
var no_cache: bool = false;
var enable_cache: bool = false;
var i: usize = 2;
while (i < args.len) : (i += 1) {
@@ -60,8 +60,8 @@ pub fn main(init: std.process.Init) !void {
target_config.sysroot = args[i];
} else if (std.mem.eql(u8, arg, "--time")) {
show_timing = true;
} else if (std.mem.eql(u8, arg, "--no-cache")) {
no_cache = true;
} else if (std.mem.eql(u8, arg, "--cache")) {
enable_cache = true;
} else if (std.mem.startsWith(u8, arg, "-L")) {
if (arg.len > 2) {
try lib_paths.append(allocator, arg[2..]);
@@ -87,7 +87,7 @@ pub fn main(init: std.process.Init) !void {
if (std.mem.eql(u8, command, "build")) {
const output_name = target_config.output_path orelse deriveOutputName(path);
compile(allocator, io, path, output_name, target_config, show_timing, no_cache) catch return;
compile(allocator, io, path, output_name, target_config, show_timing, enable_cache) catch return;
std.debug.print("compiled: {s}\n", .{output_name});
} else if (std.mem.eql(u8, command, "ir")) {
emitIR(allocator, io, path, target_config) catch return;
@@ -117,7 +117,7 @@ pub fn main(init: std.process.Init) !void {
// Cache check — use .o files (precompiled object, skip IR compilation in JIT)
// Disable caching for files with top-level #run (side effects lost on cache hit)
const root = comp.resolved_root orelse comp.root orelse return;
const use_cache = !no_cache and !hasTopLevelRun(root);
const use_cache = enable_cache and !hasTopLevelRun(root);
const key = computeCacheKey(source, &comp.import_sources, target_config);
const cache_obj = cachePath(allocator, key, "o") catch return;
@@ -151,13 +151,19 @@ pub fn main(init: std.process.Init) !void {
break :blk buf;
};
// Compile C sources natively and dlopen before JIT
timer.mark();
var c_handle = compileCForJIT(allocator, io, &comp) catch { comp.renderErrors(); return; };
defer c_handle.unload(io);
timer.record("c-import");
// JIT from precompiled object (relocation only, no IR compilation)
sx.llvm_api.initNativeTarget();
timer.mark();
const exit_code = sx.codegen.CodeGen.runJITFromObject(obj_buf) catch {
// JIT failed — fall back to AOT
timer.record("jit-fail");
runAOT(allocator, io, path, target_config, &timer, no_cache) catch return;
runAOT(allocator, io, path, target_config, &timer, enable_cache) catch return;
timer.printAll();
return;
};
@@ -170,6 +176,24 @@ pub fn main(init: std.process.Init) !void {
}
}
/// Compile C sources from #import c blocks and dlopen them for JIT.
fn compileCForJIT(allocator: std.mem.Allocator, io: std.Io, comp: *sx.core.Compilation) !sx.c_import.CImportHandle {
const c_infos = try comp.collectCImportSources();
if (c_infos.len == 0) return .{ .allocator = allocator };
const obj_bufs = try sx.c_import.compileCToObjects(allocator, c_infos);
return try sx.c_import.loadCObjectsForJIT(allocator, io, obj_bufs);
}
/// Compile C sources from #import c blocks to .o files for linking.
fn compileCForBuild(allocator: std.mem.Allocator, io: std.Io, comp: *sx.core.Compilation) ![]const []const u8 {
const c_infos = try comp.collectCImportSources();
if (c_infos.len == 0) return &.{};
const obj_bufs = try sx.c_import.compileCToObjects(allocator, c_infos);
return try sx.c_import.writeCObjectFiles(allocator, io, obj_bufs);
}
fn parseOptLevel(s: []const u8) ?sx.codegen.TargetConfig.OptLevel {
if (std.mem.eql(u8, s, "none") or std.mem.eql(u8, s, "0")) return .none;
if (std.mem.eql(u8, s, "less") or std.mem.eql(u8, s, "1")) return .less;
@@ -197,7 +221,7 @@ fn printUsage() void {
\\ -L <path> Library search path (repeatable)
\\ --linker <cmd> Linker command (default: cc)
\\ --sysroot <path> Sysroot for cross-compilation
\\ --no-cache Disable build caching
\\ --cache Enable build caching
\\ --time Show compilation timing breakdown
\\
, .{});
@@ -232,8 +256,8 @@ fn runLsp(allocator: std.mem.Allocator, io: std.Io) void {
fn deriveOutputName(input_path: []const u8) []const u8 {
// Get basename (strip directory)
var start: usize = 0;
for (input_path, 0..) |ch, i| {
if (ch == '/' or ch == '\\') start = i + 1;
for (input_path, 0..) |ch, idx| {
if (ch == '/' or ch == '\\') start = idx + 1;
}
const basename = input_path[start..];
// Strip .sx extension
@@ -300,13 +324,13 @@ fn emitAsm(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8, tar
std.debug.print("emitted: {s}\n", .{asm_path});
}
fn compile(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8, output_path: []const u8, target_config: sx.codegen.TargetConfig, show_timing: bool, no_cache: bool) !void {
fn compile(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8, output_path: []const u8, target_config: sx.codegen.TargetConfig, show_timing: bool, enable_cache: bool) !void {
var timer = Timing.init(show_timing);
try compileWithTimer(allocator, io, input_path, output_path, target_config, &timer, no_cache);
try compileWithTimer(allocator, io, input_path, output_path, target_config, &timer, enable_cache);
timer.printAll();
}
fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8, output_path: []const u8, target_config: sx.codegen.TargetConfig, timer: *Timing, no_cache: bool) !void {
fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8, output_path: []const u8, target_config: sx.codegen.TargetConfig, timer: *Timing, enable_cache: bool) !void {
// Phase A: read + parse + resolveImports (fast: ~0.5ms)
timer.mark();
const source = try readSource(allocator, io, input_path);
@@ -336,7 +360,7 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons
const cache_bin = try cachePath(allocator, key, "bin");
// Level 1: Try cached binary (skip everything — no codegen, no link)
if (!no_cache) bin_cache: {
if (enable_cache) bin_cache: {
std.Io.Dir.copyFile(.cwd(), cache_bin, .cwd(), output_path, io, .{}) catch break :bin_cache;
timer.record("cache");
return;
@@ -344,7 +368,7 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons
// Level 2: Try cached .o (skip codegen+emit, still need link)
const used_obj_cache = blk: {
if (no_cache) break :blk false;
if (!enable_cache) break :blk false;
std.Io.Dir.copyFile(.cwd(), cache_obj, .cwd(), obj_path, io, .{}) catch break :blk false;
break :blk true;
};
@@ -367,31 +391,42 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons
timer.record("emit");
// Save .o to cache
if (!no_cache) {
if (enable_cache) {
std.Io.Dir.copyFile(.cwd(), obj_path, .cwd(), cache_obj, io, .{ .make_path = true }) catch {};
}
}
// Link
// Compile C sources from #import c blocks to .o files
timer.mark();
sx.codegen.CodeGen.link(allocator, io, obj_path, output_path, libs, target_config) catch {
const c_obj_paths = compileCForBuild(allocator, io, &comp) catch {
std.debug.print("error: C import compilation failed\n", .{});
return error.CompileError;
};
timer.record("c-import");
// Link (sx .o + C .o files)
timer.mark();
sx.codegen.CodeGen.link(allocator, io, obj_path, c_obj_paths, output_path, libs, target_config) catch {
std.debug.print("error: linking failed\n", .{});
return error.CompileError;
};
timer.record("link");
// Save linked binary to cache
if (!no_cache) {
if (enable_cache) {
std.Io.Dir.copyFile(.cwd(), output_path, .cwd(), cache_bin, io, .{ .make_path = true }) catch {};
}
// Clean up object file
// Clean up object files
std.Io.Dir.deleteFile(.cwd(), io, obj_path) catch {};
for (c_obj_paths) |cop| {
std.Io.Dir.deleteFile(.cwd(), io, cop) catch {};
}
}
fn runAOT(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8, target_config: sx.codegen.TargetConfig, timer: *Timing, no_cache: bool) !void {
fn runAOT(allocator: std.mem.Allocator, io: std.Io, input_path: []const u8, target_config: sx.codegen.TargetConfig, timer: *Timing, enable_cache: bool) !void {
const tmp_bin = if (comptime @import("builtin").os.tag == .windows) "sx_run_tmp.exe" else "/tmp/sx_run_tmp";
try compileWithTimer(allocator, io, input_path, tmp_bin, target_config, timer, no_cache);
try compileWithTimer(allocator, io, input_path, tmp_bin, target_config, timer, enable_cache);
defer {
std.Io.Dir.deleteFile(.cwd(), io, tmp_bin) catch {};
}