http server
This commit is contained in:
101
src/codegen.zig
101
src/codegen.zig
@@ -186,6 +186,10 @@ pub const CodeGen = struct {
|
||||
foreign_libraries: std.ArrayList([]const u8),
|
||||
// Set of foreign function names (for ABI lowering at call sites)
|
||||
foreign_fns: std.StringHashMap(void),
|
||||
// Named library constants: sx name → lib filename (e.g. "libc" → "c")
|
||||
library_constants: std.StringHashMap([]const u8),
|
||||
// Foreign function rename map: sx name → C symbol name (e.g. "write_fd" → "write")
|
||||
foreign_name_map: std.StringHashMap([]const u8),
|
||||
// Global mutable variables (from top-level var_decl, e.g. function pointers loaded at runtime)
|
||||
global_mutable_vars: std.StringHashMap(NamedValue),
|
||||
// Declared return types for non-generic functions (preserves signedness lost by LLVM round-trip)
|
||||
@@ -394,6 +398,8 @@ pub const CodeGen = struct {
|
||||
.deferred_fn_bodies = std.ArrayList(DeferredFn).empty,
|
||||
.foreign_libraries = std.ArrayList([]const u8).empty,
|
||||
.foreign_fns = std.StringHashMap(void).init(allocator),
|
||||
.library_constants = std.StringHashMap([]const u8).init(allocator),
|
||||
.foreign_name_map = std.StringHashMap([]const u8).init(allocator),
|
||||
.global_mutable_vars = std.StringHashMap(NamedValue).init(allocator),
|
||||
.function_return_types = std.StringHashMap(Type).init(allocator),
|
||||
.target_config = target_config,
|
||||
@@ -426,6 +432,8 @@ pub const CodeGen = struct {
|
||||
self.deferred_fn_bodies.deinit(self.allocator);
|
||||
self.foreign_libraries.deinit(self.allocator);
|
||||
self.foreign_fns.deinit();
|
||||
self.library_constants.deinit();
|
||||
self.foreign_name_map.deinit();
|
||||
c.LLVMDisposeBuilder(self.builder);
|
||||
if (self.target_machine) |tm| c.LLVMDisposeTargetMachine(tm);
|
||||
if (self.module_owned) {
|
||||
@@ -1038,6 +1046,27 @@ pub const CodeGen = struct {
|
||||
// Initialize built-in function declarations (printf, etc.)
|
||||
self.builtins = Builtins.init(self.module, self.context);
|
||||
|
||||
// Pre-scan: collect named library constants (handles forward references)
|
||||
for (root.data.root.decls) |decl| {
|
||||
switch (decl.data) {
|
||||
.library_decl => |ld| {
|
||||
try self.library_constants.put(ld.name, ld.lib_name);
|
||||
},
|
||||
.namespace_decl => |ns| {
|
||||
for (ns.decls) |nd| {
|
||||
switch (nd.data) {
|
||||
.library_decl => |nld| {
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, nld.name });
|
||||
try self.library_constants.put(qualified, nld.lib_name);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 1: Register all declarations (signatures only, no bodies)
|
||||
for (root.data.root.decls) |decl| {
|
||||
switch (decl.data) {
|
||||
@@ -1904,11 +1933,34 @@ pub const CodeGen = struct {
|
||||
|
||||
fn registerFnDecl(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !void {
|
||||
const is_foreign = fd.body.data == .foreign_expr;
|
||||
// For foreign functions: resolve C symbol name (rename) and validate library ref
|
||||
const actual_llvm_name = if (is_foreign) blk: {
|
||||
const fe = fd.body.data.foreign_expr;
|
||||
// Validate library reference
|
||||
if (fe.library_ref) |lib_ref| {
|
||||
if (!self.library_constants.contains(lib_ref)) {
|
||||
return self.emitErrorFmt("unknown library '{s}' in #foreign", .{lib_ref});
|
||||
}
|
||||
}
|
||||
// Use C symbol name if provided, otherwise use the sx name
|
||||
const c_name = fe.c_name orelse llvm_name;
|
||||
// Track rename mapping for call resolution
|
||||
if (fe.c_name != null and !std.mem.eql(u8, c_name, llvm_name)) {
|
||||
try self.foreign_name_map.put(llvm_name, c_name);
|
||||
}
|
||||
break :blk c_name;
|
||||
} else llvm_name;
|
||||
const fn_type = try self.buildFnType(fd.params, fd.return_type, fd.name, is_foreign);
|
||||
const name_z = try self.allocator.dupeZ(u8, llvm_name);
|
||||
const name_z = try self.allocator.dupeZ(u8, actual_llvm_name);
|
||||
_ = c.LLVMAddFunction(self.module, name_z.ptr, fn_type);
|
||||
// Track foreign functions for ABI lowering at call sites
|
||||
if (is_foreign) try self.foreign_fns.put(llvm_name, {});
|
||||
// Track foreign functions for ABI lowering at call sites (use sx name for call-site lookup)
|
||||
if (is_foreign) {
|
||||
try self.foreign_fns.put(llvm_name, {});
|
||||
// Also track under the C name for direct lookups
|
||||
if (!std.mem.eql(u8, actual_llvm_name, llvm_name)) {
|
||||
try self.foreign_fns.put(actual_llvm_name, {});
|
||||
}
|
||||
}
|
||||
// Track resolved parameter types for accurate call-site conversion
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (fd.params) |param| {
|
||||
@@ -1949,10 +2001,17 @@ pub const CodeGen = struct {
|
||||
}
|
||||
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, fd.name });
|
||||
if (fd.body.data == .foreign_expr) {
|
||||
// External C function in namespace — register LLVM declaration with C name only
|
||||
// External C function in namespace — register LLVM declaration
|
||||
try self.registerFnDecl(fd, fd.name);
|
||||
// Also track qualified name as foreign for ABI lowering at call sites
|
||||
try self.foreign_fns.put(qualified, {});
|
||||
// Track qualified rename mapping if C name differs
|
||||
const fe = fd.body.data.foreign_expr;
|
||||
if (fe.c_name) |c_name| {
|
||||
if (!std.mem.eql(u8, c_name, fd.name)) {
|
||||
try self.foreign_name_map.put(qualified, c_name);
|
||||
}
|
||||
}
|
||||
// Store param types under qualified name so call-site type resolution works
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (fd.params) |param| {
|
||||
@@ -2054,8 +2113,16 @@ pub const CodeGen = struct {
|
||||
if (expr.data == .call) {
|
||||
if (self.resolveCalleeName(expr.data.call)) |callee_name| {
|
||||
var cnbuf: [256]u8 = undefined;
|
||||
const callee_fn = c.LLVMGetNamedFunction(self.module, self.nameToCStr(callee_name, &cnbuf)) orelse return Type.s(64);
|
||||
const fn_type = c.LLVMGlobalGetValueType(callee_fn);
|
||||
var callee_fn = c.LLVMGetNamedFunction(self.module, self.nameToCStr(callee_name, &cnbuf));
|
||||
// Foreign rename fallback
|
||||
if (callee_fn == null) {
|
||||
if (self.foreign_name_map.get(callee_name)) |c_name| {
|
||||
var rbuf: [256]u8 = undefined;
|
||||
callee_fn = c.LLVMGetNamedFunction(self.module, self.nameToCStr(c_name, &rbuf));
|
||||
}
|
||||
}
|
||||
const resolved_fn = callee_fn orelse return Type.s(64);
|
||||
const fn_type = c.LLVMGlobalGetValueType(resolved_fn);
|
||||
const ret_llvm = c.LLVMGetReturnType(fn_type);
|
||||
return self.llvmTypeToSxType(ret_llvm);
|
||||
}
|
||||
@@ -3160,6 +3227,13 @@ pub const CodeGen = struct {
|
||||
fn_val = c.LLVMGetNamedFunction(self.module, self.nameToCStr(qualified, &qbuf));
|
||||
}
|
||||
}
|
||||
// Foreign rename fallback: sx name → C symbol name
|
||||
if (fn_val == null) {
|
||||
if (self.foreign_name_map.get(ident.name)) |c_name| {
|
||||
var rbuf: [256]u8 = undefined;
|
||||
fn_val = c.LLVMGetNamedFunction(self.module, self.nameToCStr(c_name, &rbuf));
|
||||
}
|
||||
}
|
||||
if (fn_val != null) return fn_val.?;
|
||||
}
|
||||
return self.emitErrorFmt("undefined identifier '{s}'", .{ident.name});
|
||||
@@ -5386,6 +5460,13 @@ pub const CodeGen = struct {
|
||||
callee_fn = c.LLVMGetNamedFunction(self.module, self.nameToCStr(qualified2, &qbuf));
|
||||
}
|
||||
}
|
||||
// Foreign rename fallback: sx name → C symbol name (e.g. "write_fd" → "write")
|
||||
if (callee_fn == null) {
|
||||
if (self.foreign_name_map.get(callee_name)) |c_name| {
|
||||
var rbuf: [256]u8 = undefined;
|
||||
callee_fn = c.LLVMGetNamedFunction(self.module, self.nameToCStr(c_name, &rbuf));
|
||||
}
|
||||
}
|
||||
// Function pointer indirect call: callee is a variable with function_type
|
||||
if (callee_fn == null) {
|
||||
if (self.lookupValue(callee_name)) |v| {
|
||||
@@ -6795,7 +6876,7 @@ pub const CodeGen = struct {
|
||||
fn dispatchBuiltin(self: *CodeGen, name: []const u8, call_node: ast.Call) !c.LLVMValueRef {
|
||||
// Extract base name (strip namespace prefix)
|
||||
const base = baseName(name);
|
||||
if (std.mem.eql(u8, base, "write")) return self.genWriteCall(call_node.args);
|
||||
if (std.mem.eql(u8, base, "out")) return self.genOutCall(call_node.args);
|
||||
if (std.mem.eql(u8, base, "sqrt")) return self.genMathIntrinsic(call_node, "sqrt");
|
||||
if (std.mem.eql(u8, base, "sin")) return self.genMathIntrinsic(call_node, "sin");
|
||||
if (std.mem.eql(u8, base, "cos")) return self.genMathIntrinsic(call_node, "cos");
|
||||
@@ -6816,8 +6897,8 @@ pub const CodeGen = struct {
|
||||
return self.emitErrorFmt("unknown builtin function '{s}'", .{name});
|
||||
}
|
||||
|
||||
fn genWriteCall(self: *CodeGen, args: []const *Node) !c.LLVMValueRef {
|
||||
if (args.len != 1) return self.emitError("write expects exactly 1 argument");
|
||||
fn genOutCall(self: *CodeGen, args: []const *Node) !c.LLVMValueRef {
|
||||
if (args.len != 1) return self.emitError("out expects exactly 1 argument");
|
||||
const builtins = try self.requireBuiltins();
|
||||
const val = try self.genExpr(args[0]);
|
||||
// Extract ptr and len from string slice
|
||||
@@ -7311,7 +7392,7 @@ pub const CodeGen = struct {
|
||||
}
|
||||
defer _ = c.LLVMOrcDisposeLLJIT(jit);
|
||||
|
||||
// Add process symbols so JIT can find libc (printf, write, etc.)
|
||||
// Add process symbols so JIT can find libc (printf, etc.)
|
||||
const jd = c.LLVMOrcLLJITGetMainJITDylib(jit);
|
||||
const prefix = c.LLVMOrcLLJITGetGlobalPrefix(jit);
|
||||
var gen: c.LLVMOrcDefinitionGeneratorRef = null;
|
||||
|
||||
Reference in New Issue
Block a user