Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ code-bin/
# Build artifacts
*.bin
*.yul
*.hir.json
*.hir.json
vendor/mlir/lib/

vendor/mlir
255 changes: 254 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ pub fn build(b: *std.Build) void {
// Link Solidity libraries to the executable
linkSolidityLibraries(b, exe, cmake_step, target);

// Build and link MLIR (required)
const mlir_step = buildMlirLibraries(b, target, optimize);
linkMlirLibraries(b, exe, mlir_step, target);

// This declares intent for the executable to be installed into the
// standard location when the user invokes the "install" step (the default
// step when running `zig build`).
Expand Down Expand Up @@ -176,6 +180,26 @@ pub fn build(b: *std.Build) void {
// Add comprehensive compiler testing framework
addCompilerTestFramework(b, lib_mod, target, optimize);

// Create MLIR demo executable (Ora -> AST -> MLIR IR file)
const mlir_demo_mod = b.createModule(.{
.root_source_file = b.path("examples/demos/mlir_demo.zig"),
.target = target,
.optimize = optimize,
});
mlir_demo_mod.addImport("ora_lib", lib_mod);

const mlir_demo = b.addExecutable(.{
.name = "mlir_demo",
.root_module = mlir_demo_mod,
});
// Reuse MLIR build step
linkMlirLibraries(b, mlir_demo, mlir_step, target);
b.installArtifact(mlir_demo);
const run_mlir_demo = b.addRunArtifact(mlir_demo);
run_mlir_demo.step.dependOn(b.getInstallStep());
const mlir_demo_step = b.step("mlir-demo", "Run the MLIR hello-world demo");
mlir_demo_step.dependOn(&run_mlir_demo.step);

// Add new lexer testing framework
addLexerTestFramework(b, lib_mod, target, optimize);

Expand Down Expand Up @@ -435,8 +459,37 @@ fn buildSolidityLibrariesImpl(step: *std.Build.Step, options: std.Build.Step.Mak
var cmake_args = std.ArrayList([]const u8).init(allocator);
defer cmake_args.deinit();

// Prefer Ninja generator when available for faster, more parallel builds
var use_ninja: bool = false;
{
const probe = std.process.Child.run(.{ .allocator = allocator, .argv = &[_][]const u8{ "ninja", "--version" }, .cwd = "." }) catch null;
if (probe) |res| {
switch (res.term) {
.Exited => |code| {
if (code == 0) use_ninja = true;
},
else => {},
}
}
if (!use_ninja) {
const probe_alt = std.process.Child.run(.{ .allocator = allocator, .argv = &[_][]const u8{ "ninja-build", "--version" }, .cwd = "." }) catch null;
if (probe_alt) |res2| {
switch (res2.term) {
.Exited => |code| {
if (code == 0) use_ninja = true;
},
else => {},
}
}
}
}

try cmake_args.append("cmake");
if (use_ninja) {
try cmake_args.append("-G");
try cmake_args.append("Ninja");
}
try cmake_args.appendSlice(&[_][]const u8{
"cmake",
"-S",
"vendor/solidity",
"-B",
Expand Down Expand Up @@ -652,6 +705,206 @@ fn linkSolidityLibraries(b: *std.Build, exe: *std.Build.Step.Compile, cmake_step
exe.addIncludePath(b.path("vendor/solidity/libyul"));
}

/// Build MLIR from vendored llvm-project and install into vendor/mlir
fn buildMlirLibraries(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step {
_ = target;
_ = optimize;

const step = b.allocator.create(std.Build.Step) catch @panic("OOM");
step.* = std.Build.Step.init(.{
.id = .custom,
.name = "cmake-build-mlir",
.owner = b,
.makeFn = buildMlirLibrariesImpl,
});
return step;
}

/// Implementation of CMake build for MLIR libraries
fn buildMlirLibrariesImpl(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!void {
_ = options;

const b = step.owner;
const allocator = b.allocator;

// Ensure submodule exists
const cwd = std.fs.cwd();
_ = cwd.openDir("vendor/llvm-project", .{ .iterate = false }) catch {
std.log.err("Missing submodule: vendor/llvm-project. Add it and pin a commit.", .{});
std.log.err("Example: git submodule add https://github.com/llvm/llvm-project.git vendor/llvm-project", .{});
return error.SubmoduleMissing;
};

// Create build and install directories
const build_dir = "vendor/llvm-project/build-mlir";
cwd.makeDir(build_dir) catch |err| switch (err) {
error.PathAlreadyExists => {},
else => return err,
};
const install_prefix = "vendor/mlir";
cwd.makeDir(install_prefix) catch |err| switch (err) {
error.PathAlreadyExists => {},
else => return err,
};

// Platform-specific flags
const builtin = @import("builtin");
var cmake_args = std.ArrayList([]const u8).init(allocator);
defer cmake_args.deinit();

// Prefer Ninja generator when available for faster, more parallel builds
var use_ninja: bool = false;
{
const probe = std.process.Child.run(.{ .allocator = allocator, .argv = &[_][]const u8{ "ninja", "--version" }, .cwd = "." }) catch null;
if (probe) |res| {
switch (res.term) {
.Exited => |code| {
if (code == 0) use_ninja = true;
},
else => {},
}
}
if (!use_ninja) {
const probe_alt = std.process.Child.run(.{ .allocator = allocator, .argv = &[_][]const u8{ "ninja-build", "--version" }, .cwd = "." }) catch null;
if (probe_alt) |res2| {
switch (res2.term) {
.Exited => |code| {
if (code == 0) use_ninja = true;
},
else => {},
}
}
}
}

try cmake_args.append("cmake");
if (use_ninja) {
try cmake_args.append("-G");
try cmake_args.append("Ninja");
}
try cmake_args.appendSlice(&[_][]const u8{
"-S",
"vendor/llvm-project/llvm",
"-B",
build_dir,
"-DCMAKE_BUILD_TYPE=Release",
"-DLLVM_ENABLE_PROJECTS=mlir",
"-DLLVM_TARGETS_TO_BUILD=Native",
"-DLLVM_INCLUDE_TESTS=OFF",
"-DMLIR_INCLUDE_TESTS=OFF",
"-DLLVM_INCLUDE_BENCHMARKS=OFF",
"-DLLVM_INCLUDE_EXAMPLES=OFF",
"-DLLVM_INCLUDE_DOCS=OFF",
"-DMLIR_INCLUDE_DOCS=OFF",
"-DMLIR_ENABLE_BINDINGS_PYTHON=OFF",
"-DMLIR_ENABLE_EXECUTION_ENGINE=OFF",
"-DMLIR_ENABLE_CUDA=OFF",
"-DMLIR_ENABLE_ROCM=OFF",
"-DMLIR_ENABLE_SPIRV_CPU_RUNNER=OFF",
"-DLLVM_ENABLE_ZLIB=OFF",
"-DLLVM_ENABLE_TERMINFO=OFF",
"-DLLVM_ENABLE_RTTI=ON",
"-DLLVM_ENABLE_EH=ON",
"-DLLVM_BUILD_LLVM_DYLIB=OFF",
"-DLLVM_LINK_LLVM_DYLIB=OFF",
"-DLLVM_BUILD_TOOLS=ON", // needed for tblgen
"-DMLIR_BUILD_MLIR_C_DYLIB=ON",
b.fmt("-DCMAKE_INSTALL_PREFIX={s}", .{install_prefix}),
});

if (builtin.os.tag == .linux) {
try cmake_args.append("-DCMAKE_CXX_FLAGS=-stdlib=libc++ -lc++abi");
try cmake_args.append("-DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++ -lc++abi");
try cmake_args.append("-DCMAKE_SHARED_LINKER_FLAGS=-stdlib=libc++ -lc++abi");
try cmake_args.append("-DCMAKE_MODULE_LINKER_FLAGS=-stdlib=libc++ -lc++abi");
try cmake_args.append("-DCMAKE_CXX_COMPILER=clang++");
try cmake_args.append("-DCMAKE_C_COMPILER=clang");
} else if (builtin.os.tag == .macos) {
try cmake_args.append("-DCMAKE_CXX_FLAGS=-stdlib=libc++");
if (std.process.getEnvVarOwned(allocator, "ORA_CMAKE_OSX_ARCH") catch null) |arch| {
defer allocator.free(arch);
const flag = b.fmt("-DCMAKE_OSX_ARCHITECTURES={s}", .{arch});
try cmake_args.append(flag);
std.log.info("Using CMAKE_OSX_ARCHITECTURES={s}", .{arch});
}
} else if (builtin.os.tag == .windows) {
try cmake_args.append("-DCMAKE_CXX_FLAGS=/std:c++20");
}

var cfg_child = std.process.Child.init(cmake_args.items, allocator);
cfg_child.cwd = ".";
cfg_child.stdin_behavior = .Inherit;
cfg_child.stdout_behavior = .Inherit;
cfg_child.stderr_behavior = .Inherit;
const cfg_term = cfg_child.spawnAndWait() catch |err| {
std.log.err("Failed to configure MLIR CMake: {}", .{err});
return err;
};
switch (cfg_term) {
.Exited => |code| if (code != 0) {
std.log.err("MLIR CMake configure failed with exit code: {}", .{code});
return error.CMakeConfigureFailed;
},
else => {
std.log.err("MLIR CMake configure did not exit cleanly", .{});
return error.CMakeConfigureFailed;
},
}

// Build and install MLIR (with sparse checkout and minimal flags above this is lightweight)
var build_args = [_][]const u8{ "cmake", "--build", build_dir, "--parallel", "--target", "install" };
var build_child = std.process.Child.init(&build_args, allocator);
build_child.cwd = ".";
build_child.stdin_behavior = .Inherit;
build_child.stdout_behavior = .Inherit;
build_child.stderr_behavior = .Inherit;
const build_term = build_child.spawnAndWait() catch |err| {
std.log.err("Failed to build MLIR with CMake: {}", .{err});
return err;
};
switch (build_term) {
.Exited => |code| if (code != 0) {
std.log.err("MLIR CMake build failed with exit code: {}", .{code});
return error.CMakeBuildFailed;
},
else => {
std.log.err("MLIR CMake build did not exit cleanly", .{});
return error.CMakeBuildFailed;
},
}

std.log.info("Successfully built MLIR libraries", .{});
}

/// Link MLIR to the given executable using the installed prefix
fn linkMlirLibraries(b: *std.Build, exe: *std.Build.Step.Compile, mlir_step: *std.Build.Step, target: std.Build.ResolvedTarget) void {
// Depend on MLIR build
exe.step.dependOn(mlir_step);

const include_path = b.path("vendor/mlir/include");
const lib_path = b.path("vendor/mlir/lib");

exe.addIncludePath(include_path);
exe.addLibraryPath(lib_path);

exe.linkSystemLibrary("MLIR-C");

switch (target.result.os.tag) {
.linux => {
exe.linkLibCpp();
exe.linkSystemLibrary("c++abi");
exe.addRPath(lib_path);
},
.macos => {
exe.linkLibCpp();
exe.addRPath(lib_path);
},
else => {
exe.linkLibCpp();
},
}
}

/// Create example testing step that runs the compiler on all .ora files
fn createExampleTestStep(b: *std.Build, exe: *std.Build.Step.Compile) *std.Build.Step {
const test_step = b.allocator.create(std.Build.Step) catch @panic("OOM");
Expand Down
Empty file added examples/counter.ora
Empty file.
65 changes: 65 additions & 0 deletions examples/demos/mlir_demo.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const std = @import("std");
const lib = @import("ora_lib");

const c = @cImport({
@cInclude("mlir-c/IR.h");
@cInclude("mlir-c/Support.h");
@cInclude("mlir-c/RegisterEverything.h");
});

fn writeToFile(str: c.MlirStringRef, user_data: ?*anyopaque) callconv(.C) void {
const file: *std.fs.File = @ptrCast(@alignCast(user_data.?));
_ = file.writeAll(str.data[0..str.length]) catch {};
}

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();

const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
if (args.len < 2) {
std.debug.print("Usage: mlir_demo <input.ora> [output.mlir]\n", .{});
return;
}
const input = args[1];
const output = if (args.len >= 3) args[2] else "output.mlir";

// Frontend: lex + parse to AST
const source = try std.fs.cwd().readFileAlloc(allocator, input, 10 * 1024 * 1024);
defer allocator.free(source);

var lexer = lib.Lexer.init(allocator, source);
defer lexer.deinit();
const tokens = try lexer.scanTokens();
defer allocator.free(tokens);

var arena = lib.ast_arena.AstArena.init(allocator);
defer arena.deinit();
var parser = lib.Parser.init(tokens, &arena);
parser.setFileId(1);
const ast_nodes = try parser.parse();
_ = ast_nodes; // Placeholder: real lowering would traverse AST

// MLIR: create empty module and print to file
const ctx = c.mlirContextCreate();
defer c.mlirContextDestroy(ctx);
const registry = c.mlirDialectRegistryCreate();
defer c.mlirDialectRegistryDestroy(registry);
c.mlirRegisterAllDialects(registry);
c.mlirContextAppendDialectRegistry(ctx, registry);
c.mlirContextLoadAllAvailableDialects(ctx);

const loc = c.mlirLocationUnknownGet(ctx);
const module = c.mlirModuleCreateEmpty(loc);
defer c.mlirModuleDestroy(module);

var file = try std.fs.cwd().createFile(output, .{});
defer file.close();

const op = c.mlirModuleGetOperation(module);
c.mlirOperationPrint(op, writeToFile, @ptrCast(&file));

std.debug.print("Wrote MLIR to {s}\n", .{output});
}
Loading
Loading