Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
29188a6
Add dev backends for x86_64 and aarch64
rtfeldman Dec 31, 2025
1888f33
Merge remote-tracking branch 'origin/main' into dev-backends
rtfeldman Dec 31, 2025
14223ec
Fix lint issues and remove dead code in dev backends
rtfeldman Dec 31, 2025
2b3e8c2
Exclude src/backend/dev/object/ from spell check
rtfeldman Jan 1, 2026
c32347b
Merge origin/main into dev-backends
rtfeldman Jan 1, 2026
ba3cda6
Merge branch 'dev-backends' of github.com:roc-lang/roc into dev-backends
rtfeldman Jan 1, 2026
fbee65d
Merge origin/main into dev-backends
rtfeldman Jan 10, 2026
28b158e
Add dev backend JIT infrastructure and numeric evaluation
rtfeldman Jan 15, 2026
0ba237f
Add binary operation and unary minus code generation
rtfeldman Jan 15, 2026
54e2468
Add integration tests for binary operations
rtfeldman Jan 15, 2026
73f5db1
Add if/else expression code generation
rtfeldman Jan 15, 2026
3059fb5
Add function/lambda expression support to dev backend
rtfeldman Jan 15, 2026
6aa65fb
Add lambda expression tests for dev backend
rtfeldman Jan 15, 2026
4ba2804
Add data structure support to dev backend (Phase 6 partial)
rtfeldman Jan 15, 2026
c0a3c28
Add block, string, and tag expression support to dev backend (Phase 7)
rtfeldman Jan 15, 2026
3d55370
Add record, tuple, and list support to dev backend
rtfeldman Jan 15, 2026
2a7a38a
Add comprehensive expression type support to dev backend
rtfeldman Jan 15, 2026
bac4578
Disable e_empty_record handler due to canonicalizer bug
rtfeldman Jan 15, 2026
b34ff45
Fix if expression tests to use correct Roc syntax
rtfeldman Jan 15, 2026
6ff5b49
Add comparison operator tests to dev evaluator
rtfeldman Jan 15, 2026
e4d5c4c
Add more dev evaluator tests and boolean tag support
rtfeldman Jan 15, 2026
f48a090
Add e_tag support for boolean and/or operations
rtfeldman Jan 15, 2026
bb9c1f9
Add more boolean operation tests
rtfeldman Jan 15, 2026
47f3a7c
Add e_dot_access support for record field access
rtfeldman Jan 15, 2026
8b8122a
Expand generateCode entry point for more expression types
rtfeldman Jan 15, 2026
7ff6236
Add tests for tuple, block, and unary not expressions
rtfeldman Jan 15, 2026
5eff6d2
Dev evaluator: implement e_for and e_low_level_lambda support
rtfeldman Jan 15, 2026
8b76c38
Dev evaluator: add e_nominal support
rtfeldman Jan 15, 2026
63eb617
Dev evaluator: add e_nominal to constant folding helpers
rtfeldman Jan 15, 2026
876ac9b
Dev evaluator: add list_get_unsafe for constant lists
rtfeldman Jan 15, 2026
3016471
Dev evaluator: add if and call to constant folding
rtfeldman Jan 15, 2026
a3537b5
Dev evaluator: add block and unary_not to constant folding
rtfeldman Jan 15, 2026
ea1fb5f
Dev evaluator: implement crash and error expressions
rtfeldman Jan 15, 2026
5c7fcd6
Dev evaluator: crash on unsupported complex expressions
rtfeldman Jan 15, 2026
537a7ce
Add comptime_value.zig for dev backend compile-time evaluation
rtfeldman Jan 15, 2026
481d1d4
Dev evaluator: add comptime imports (WIP)
rtfeldman Jan 15, 2026
2ec0cb2
Dev evaluator: add dual-evaluator comparison in tests
rtfeldman Jan 16, 2026
b171823
Fix lint issues: remove separator comments, dead code, banned patterns
rtfeldman Jan 16, 2026
f14528f
Merge origin/main into dev-backends
rtfeldman Jan 16, 2026
eb1a246
Fix Windows VirtualAlloc error handling in JIT
rtfeldman Jan 16, 2026
a6e2677
Fix cross-compilation issues for JIT and ELF
rtfeldman Jan 16, 2026
a96f7e5
Fix VirtualFree return type handling for native vs cross-compilation
rtfeldman Jan 16, 2026
29a32b9
Merge remote-tracking branch 'origin/main' into dev-backends
rtfeldman Jan 17, 2026
7a4d2b3
Remove dead TypeWriter import in lsp/syntax.zig
rtfeldman Jan 17, 2026
fe94531
Remove NumKind.numeral from backend code
rtfeldman Jan 17, 2026
58bd76d
Replace ResultType with layout.Idx and integrate dev backend into REPL
rtfeldman Jan 17, 2026
3752fa7
Add top-level constant evaluation infrastructure
rtfeldman Jan 17, 2026
394dab0
Fix lint issues: remove separator comments and dead code
rtfeldman Jan 17, 2026
b395677
Simplify comptime_value.zig and make DevEvaluator panic on unsupported
rtfeldman Jan 17, 2026
045a964
Implement full record/dot-access handling in DevEvaluator
rtfeldman Jan 17, 2026
218a606
Refactor DevEvaluator: simplify Scope, remove duplicate functions
rtfeldman Jan 17, 2026
0e4d581
Fix JIT calls to use result pointer as generated code expects
rtfeldman Jan 18, 2026
b319173
Fix x86_64 Windows calling convention in JIT code generation
rtfeldman Jan 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
360 changes: 360 additions & 0 deletions src/backend/dev/Backend.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
//! Unified dev backend for generating native object files.
//!
//! This module integrates code generation with object file writing
//! to produce complete relocatable object files from CIR.

const std = @import("std");
const Allocator = std.mem.Allocator;

const object = @import("object/mod.zig");
const x86_64 = @import("x86_64/mod.zig");
const aarch64 = @import("aarch64/mod.zig");
const Relocation = @import("Relocation.zig").Relocation;

/// Target architecture for code generation
pub const Architecture = enum {
x86_64,
aarch64,

pub fn fromTarget(target: anytype) Architecture {
const arch = if (@hasField(@TypeOf(target), "cpu"))
target.cpu.arch
else if (@hasField(@TypeOf(target), "arch"))
target.arch
else
@compileError("Unknown target type");

return switch (arch) {
.x86_64 => .x86_64,
.aarch64 => .aarch64,
else => @panic("Unsupported architecture for dev backend"),
};
}
};

/// Target operating system
pub const OperatingSystem = enum {
linux,
macos,
windows,

pub fn fromTarget(target: anytype) OperatingSystem {
const os_tag = if (@hasField(@TypeOf(target), "os"))
target.os.tag
else if (@hasField(@TypeOf(target), "os_tag"))
target.os_tag
else
@compileError("Unknown target type");

return switch (os_tag) {
.linux => .linux,
.macos => .macos,
.windows => .windows,
else => .linux, // Default to Linux for other Unix-like systems
};
}
};

/// Generate an object file from code and relocations.
///
/// This is the main entry point for the dev backend. It takes generated
/// machine code and produces a relocatable object file.
pub fn generateObjectFile(
allocator: Allocator,
arch: Architecture,
os: OperatingSystem,
code: []const u8,
symbols: []const Symbol,
relocations: []const Relocation,
output: *std.ArrayList(u8),
) !void {
switch (os) {
.linux => {
var elf = try object.ElfWriter.init(allocator, switch (arch) {
.x86_64 => .x86_64,
.aarch64 => .aarch64,
});
defer elf.deinit();

try elf.setCode(code);

// Add symbols
for (symbols) |sym| {
const sym_idx = try elf.addSymbol(.{
.name = sym.name,
.section = if (sym.is_external) .undef else .text,
.offset = sym.offset,
.size = sym.size,
.is_global = sym.is_global,
.is_function = sym.is_function,
});

// Add relocations for this symbol
for (relocations) |rel| {
const rel_name = switch (rel) {
.linked_function => |f| f.name,
.linked_data => |d| d.name,
else => continue,
};
if (std.mem.eql(u8, rel_name, sym.name)) {
try elf.addTextRelocation(rel.getOffset(), sym_idx, 0);
}
}
}

try elf.write(output);
},
.macos => {
var macho = try object.MachOWriter.init(allocator, switch (arch) {
.x86_64 => .x86_64,
.aarch64 => .aarch64,
});
defer macho.deinit();

try macho.setCode(code);

// Add symbols
for (symbols) |sym| {
const sym_idx = try macho.addSymbol(.{
.name = sym.name,
.section = if (sym.is_external) 0 else 1, // 0 = NO_SECT, 1 = __text
.offset = sym.offset,
.is_external = sym.is_global or sym.is_external,
});

// Add relocations for this symbol
for (relocations) |rel| {
const rel_name = switch (rel) {
.linked_function => |f| f.name,
.linked_data => |d| d.name,
else => continue,
};
if (std.mem.eql(u8, rel_name, sym.name)) {
try macho.addTextRelocation(@intCast(rel.getOffset()), sym_idx, sym.is_external);
}
}
}

try macho.write(output);
},
.windows => {
var coff_writer = try object.CoffWriter.init(allocator, switch (arch) {
.x86_64 => .x86_64,
.aarch64 => .aarch64,
});
defer coff_writer.deinit();

try coff_writer.setCode(code);

// Add symbols
for (symbols) |sym| {
const sym_idx = try coff_writer.addSymbol(.{
.name = sym.name,
.section = if (sym.is_external) .undef else .text,
.offset = @intCast(sym.offset),
.is_global = sym.is_global,
.is_function = sym.is_function,
});

// Add relocations for this symbol
for (relocations) |rel| {
const rel_name = switch (rel) {
.linked_function => |f| f.name,
.linked_data => |d| d.name,
else => continue,
};
if (std.mem.eql(u8, rel_name, sym.name)) {
try coff_writer.addTextRelocation(@intCast(rel.getOffset()), sym_idx);
}
}
}

try coff_writer.write(output);
},
}
}

/// Symbol information for object file generation
pub const Symbol = struct {
name: []const u8,
offset: u64,
size: u64,
is_global: bool,
is_function: bool,
is_external: bool,
};

// Tests

test "generate x86_64 linux object" {
const allocator = std.testing.allocator;

// Simple x86_64 code: ret
const code = &[_]u8{0xC3};

const symbols = &[_]Symbol{
.{
.name = "test_func",
.offset = 0,
.size = 1,
.is_global = true,
.is_function = true,
.is_external = false,
},
};

var output: std.ArrayList(u8) = .{};
defer output.deinit(allocator);

try generateObjectFile(
allocator,
.x86_64,
.linux,
code,
symbols,
&.{},
&output,
);

// Verify ELF magic
try std.testing.expectEqualSlices(u8, "\x7fELF", output.items[0..4]);
}

test "generate x86_64 macos object" {
const allocator = std.testing.allocator;

// Simple x86_64 code: ret
const code = &[_]u8{0xC3};

const symbols = &[_]Symbol{
.{
.name = "_test_func",
.offset = 0,
.size = 1,
.is_global = true,
.is_function = true,
.is_external = false,
},
};

var output: std.ArrayList(u8) = .{};
defer output.deinit(allocator);

try generateObjectFile(
allocator,
.x86_64,
.macos,
code,
symbols,
&.{},
&output,
);

// Verify Mach-O magic
const magic = std.mem.readInt(u32, output.items[0..4], .little);
try std.testing.expectEqual(@as(u32, 0xFEEDFACF), magic);
}

test "generate aarch64 linux object" {
const allocator = std.testing.allocator;

// Simple aarch64 code: ret (RET instruction)
const code = &[_]u8{ 0xC0, 0x03, 0x5F, 0xD6 };

const symbols = &[_]Symbol{
.{
.name = "test_func",
.offset = 0,
.size = 4,
.is_global = true,
.is_function = true,
.is_external = false,
},
};

var output: std.ArrayList(u8) = .{};
defer output.deinit(allocator);

try generateObjectFile(
allocator,
.aarch64,
.linux,
code,
symbols,
&.{},
&output,
);

// Verify ELF magic
try std.testing.expectEqualSlices(u8, "\x7fELF", output.items[0..4]);
}

test "generate x86_64 windows object" {
const allocator = std.testing.allocator;

// Simple x86_64 code: ret
const code = &[_]u8{0xC3};

const symbols = &[_]Symbol{
.{
.name = "test_func",
.offset = 0,
.size = 1,
.is_global = true,
.is_function = true,
.is_external = false,
},
};

var output: std.ArrayList(u8) = .{};
defer output.deinit(allocator);

try generateObjectFile(
allocator,
.x86_64,
.windows,
code,
symbols,
&.{},
&output,
);

// Verify COFF machine type (x86_64 = 0x8664)
const machine = std.mem.readInt(u16, output.items[0..2], .little);
try std.testing.expectEqual(@as(u16, 0x8664), machine);
}

test "generate aarch64 windows object" {
const allocator = std.testing.allocator;

// Simple aarch64 code: ret
const code = &[_]u8{ 0xC0, 0x03, 0x5F, 0xD6 };

const symbols = &[_]Symbol{
.{
.name = "test_func",
.offset = 0,
.size = 4,
.is_global = true,
.is_function = true,
.is_external = false,
},
};

var output: std.ArrayList(u8) = .{};
defer output.deinit(allocator);

try generateObjectFile(
allocator,
.aarch64,
.windows,
code,
symbols,
&.{},
&output,
);

// Verify COFF machine type (ARM64 = 0xAA64)
const machine = std.mem.readInt(u16, output.items[0..2], .little);
try std.testing.expectEqual(@as(u16, 0xAA64), machine);
}
Loading
Loading