2019-09-21 02:40:21 +00:00
|
|
|
const std = @import("std");
|
|
|
|
const ast = @import("ast.zig");
|
|
|
|
const llvm = @import("llvm.zig");
|
|
|
|
|
2019-09-22 02:56:22 +00:00
|
|
|
fn sliceify(non_slice: ?[*]const u8) []const u8 {
|
|
|
|
if (non_slice == null) return "";
|
|
|
|
return non_slice.?[0..std.mem.len(u8, non_slice.?)];
|
|
|
|
}
|
|
|
|
|
2019-09-24 01:07:19 +00:00
|
|
|
pub const CompileError = error{
|
|
|
|
LLVMError,
|
|
|
|
EmitError,
|
2019-09-24 16:23:23 +00:00
|
|
|
TypeError,
|
2019-09-24 01:07:19 +00:00
|
|
|
};
|
2019-09-22 21:26:00 +00:00
|
|
|
|
2019-09-24 16:32:00 +00:00
|
|
|
/// Does not account for custom types e.g structs, better type resolution
|
|
|
|
/// should be found
|
|
|
|
fn basicTypeToLLVM(ret_type: []const u8) !llvm.LLVMTypeRef {
|
2019-09-24 16:23:23 +00:00
|
|
|
if (std.mem.eql(u8, ret_type, "i32")) {
|
|
|
|
return llvm.LLVMInt32Type();
|
|
|
|
} else if (std.mem.eql(u8, ret_type, "bool")) {
|
|
|
|
return llvm.LLVMInt1Type();
|
|
|
|
} else {
|
|
|
|
std.debug.warn("Invalid return type: {}\n", ret_type);
|
|
|
|
return CompileError.TypeError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-24 17:12:37 +00:00
|
|
|
fn mkLLVMBool(val: bool) llvm.LLVMValueRef {
|
|
|
|
if (val) {
|
|
|
|
return llvm.LLVMConstInt(llvm.LLVMInt1Type(), 1, 1);
|
|
|
|
} else {
|
|
|
|
return llvm.LLVMConstInt(llvm.LLVMInt1Type(), 0, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-21 02:40:21 +00:00
|
|
|
pub const Codegen = struct {
|
|
|
|
allocator: *std.mem.Allocator,
|
|
|
|
|
|
|
|
pub fn init(allocator: *std.mem.Allocator) Codegen {
|
|
|
|
return Codegen{ .allocator = allocator };
|
|
|
|
}
|
|
|
|
|
2019-09-24 01:07:19 +00:00
|
|
|
fn emitExpr(self: *Codegen, builder: var, expr: *const ast.Expr) anyerror!llvm.LLVMValueRef {
|
2019-09-23 21:33:30 +00:00
|
|
|
// TODO if expr is Variable, we should do a variable lookup
|
|
|
|
// in a symbol table, going up in scope, etc.
|
|
|
|
|
|
|
|
// TODO VarDecl add things to the symbol table
|
|
|
|
// TODO Assign modify symbol table
|
|
|
|
|
|
|
|
// TODO Calls fetch symbol table, check arity of it at codegen level
|
2019-09-24 01:07:19 +00:00
|
|
|
return switch (expr.*) {
|
2019-09-23 21:33:30 +00:00
|
|
|
|
|
|
|
// TODO handle all literals, construct llvm values for them
|
2019-09-24 01:07:19 +00:00
|
|
|
.Literal => |literal| blk: {
|
|
|
|
break :blk switch (literal) {
|
|
|
|
// TODO other literals
|
|
|
|
.Integer => |val| blk2: {
|
|
|
|
var val_cstr = try std.cstr.addNullByte(self.allocator, val);
|
|
|
|
break :blk2 llvm.LLVMConstIntOfString(llvm.LLVMInt32Type(), val_cstr.ptr, 10);
|
|
|
|
},
|
|
|
|
.Float => |val| blk2: {
|
|
|
|
var val_cstr = try std.cstr.addNullByte(self.allocator, val);
|
|
|
|
break :blk2 llvm.LLVMConstRealOfString(llvm.LLVMDoubleType(), val_cstr.ptr);
|
|
|
|
},
|
2019-09-24 16:23:23 +00:00
|
|
|
.Bool => |val| blk2: {
|
2019-09-24 17:12:37 +00:00
|
|
|
break :blk2 mkLLVMBool(val);
|
2019-09-24 16:23:23 +00:00
|
|
|
},
|
2019-09-24 01:07:19 +00:00
|
|
|
else => unreachable,
|
|
|
|
};
|
|
|
|
},
|
2019-09-24 16:23:23 +00:00
|
|
|
|
2019-09-23 21:33:30 +00:00
|
|
|
.Binary => |binary| {
|
2019-09-24 01:07:19 +00:00
|
|
|
var left = try self.emitExpr(builder, binary.left);
|
|
|
|
var right = try self.emitExpr(builder, binary.right);
|
2019-09-23 21:33:30 +00:00
|
|
|
|
2019-09-24 02:18:31 +00:00
|
|
|
return switch (binary.op) {
|
|
|
|
.Add => llvm.LLVMBuildAdd(builder, left, right, c"addtmp"),
|
|
|
|
.Sub => llvm.LLVMBuildSub(builder, left, right, c"subtmp"),
|
|
|
|
.Mul => llvm.LLVMBuildMul(builder, left, right, c"multmp"),
|
|
|
|
|
|
|
|
//.Div => llvm.LLVMBuildDiv(builder, left, right, c"divtmp"),
|
2019-09-24 16:23:23 +00:00
|
|
|
.And => llvm.LLVMBuildAnd(builder, left, right, c"andtmp"),
|
|
|
|
.Or => llvm.LLVMBuildOr(builder, left, right, c"ortmp"),
|
2019-09-23 21:33:30 +00:00
|
|
|
|
2019-09-24 01:07:19 +00:00
|
|
|
else => {
|
2019-09-24 02:18:31 +00:00
|
|
|
std.debug.warn("Unexpected binary operator: '{}'\n", binary.op);
|
2019-09-24 01:07:19 +00:00
|
|
|
return CompileError.EmitError;
|
|
|
|
},
|
2019-09-23 21:33:30 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
// TODO codegen errors
|
2019-09-24 01:07:19 +00:00
|
|
|
else => {
|
|
|
|
std.debug.warn("Got unexpected expr {}\n", expr.*);
|
|
|
|
return CompileError.EmitError;
|
|
|
|
},
|
|
|
|
};
|
2019-09-23 21:33:30 +00:00
|
|
|
}
|
|
|
|
|
2019-09-24 20:47:17 +00:00
|
|
|
fn emitStmt(self: *Codegen, builder: var, stmt: *const ast.Stmt) anyerror!void {
|
2019-09-23 21:33:30 +00:00
|
|
|
switch (stmt.*) {
|
2019-09-24 01:07:19 +00:00
|
|
|
.Expr => |expr| _ = try self.emitExpr(builder, expr),
|
2019-09-24 17:12:37 +00:00
|
|
|
|
2019-09-24 01:07:19 +00:00
|
|
|
.Return => |ret| {
|
|
|
|
var ret_expr = try self.emitExpr(builder, ret.value);
|
|
|
|
_ = llvm.LLVMBuildRet(builder, ret_expr);
|
|
|
|
},
|
2019-09-24 17:12:37 +00:00
|
|
|
|
|
|
|
.If => |ifstmt| {
|
|
|
|
var cond = try self.emitExpr(builder, ifstmt.condition);
|
|
|
|
var zero = mkLLVMBool(false);
|
|
|
|
var icmp = llvm.LLVMBuildICmp(builder, llvm.LLVMIntPredicate.LLVMIntNE, cond, zero, c"ifcond");
|
2019-09-24 20:47:17 +00:00
|
|
|
|
|
|
|
var insert = llvm.LLVMGetInsertBlock(builder);
|
|
|
|
var function = llvm.LLVMGetBasicBlockParent(insert);
|
|
|
|
var global_ctx = llvm.LLVMGetGlobalContext();
|
|
|
|
|
|
|
|
var then_bb = llvm.LLVMAppendBasicBlock(function, c"then");
|
|
|
|
var else_bb = llvm.LLVMCreateBasicBlockInContext(global_ctx, c"else");
|
|
|
|
var merge_bb = llvm.LLVMCreateBasicBlockInContext(global_ctx, c"ifcont");
|
|
|
|
|
|
|
|
var condbr = llvm.LLVMBuildCondBr(builder, icmp, then_bb, else_bb);
|
|
|
|
|
|
|
|
llvm.LLVMPositionBuilderAtEnd(builder, then_bb);
|
|
|
|
|
|
|
|
// roughly translating to kaleidoscope's
|
|
|
|
// 'Value *ThenV = Then->codegen();'
|
|
|
|
for (ifstmt.then_branch.toSlice()) |then_stmt| {
|
|
|
|
try self.emitStmt(builder, &then_stmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = llvm.LLVMBuildBr(builder, merge_bb);
|
|
|
|
then_bb = llvm.LLVMGetInsertBlock(builder);
|
|
|
|
|
|
|
|
llvm.LLVMPositionBuilderAtEnd(builder, else_bb);
|
|
|
|
|
|
|
|
// roughly translating to kaleidoscope's
|
|
|
|
// 'Else *ElseV = Else->codegen();'
|
|
|
|
if (ifstmt.else_branch) |else_block| {
|
|
|
|
for (else_block.toSlice()) |else_stmt| {
|
|
|
|
try self.emitStmt(builder, &else_stmt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_ = llvm.LLVMBuildBr(builder, merge_bb);
|
|
|
|
else_bb = llvm.LLVMGetInsertBlock(builder);
|
|
|
|
|
|
|
|
llvm.LLVMPositionBuilderAtEnd(builder, merge_bb);
|
|
|
|
|
|
|
|
var phi = llvm.LLVMBuildPhi(builder, llvm.LLVMVoidType(), c"iftmp");
|
|
|
|
|
|
|
|
var then_bb_val = llvm.LLVMBasicBlockAsValue(then_bb);
|
|
|
|
var else_bb_val = llvm.LLVMBasicBlockAsValue(else_bb);
|
|
|
|
|
|
|
|
llvm.LLVMAddIncoming(phi, &then_bb_val, &then_bb, 1);
|
|
|
|
llvm.LLVMAddIncoming(phi, &else_bb_val, &else_bb, 1);
|
2019-09-24 17:12:37 +00:00
|
|
|
},
|
|
|
|
|
2019-09-24 01:07:19 +00:00
|
|
|
else => {
|
|
|
|
std.debug.warn("Got unexpected statement {}\n", stmt.*);
|
|
|
|
return CompileError.EmitError;
|
|
|
|
},
|
2019-09-23 21:33:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-24 20:47:17 +00:00
|
|
|
fn emitBlock(self: *Codegen, builder: var, block: ast.Block) !llvm.LLVMValueRef {
|
|
|
|
var entry = llvm.LLVMAppendBasicBlock(func, entry_lbl_cstr.ptr);
|
|
|
|
|
|
|
|
var builder = llvm.LLVMCreateBuilder();
|
|
|
|
llvm.LLVMPositionBuilderAtEnd(builder, entry);
|
|
|
|
for (block.toSlice()) |stmt| {
|
|
|
|
try self.emitStmt(builder, &stmt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-21 02:40:21 +00:00
|
|
|
fn genNode(
|
|
|
|
self: *Codegen,
|
|
|
|
mod: llvm.LLVMModuleRef,
|
|
|
|
node: *const ast.Node,
|
|
|
|
) !void {
|
|
|
|
switch (node.*) {
|
|
|
|
.Root => @panic("Should not have gotten Root"),
|
|
|
|
.FnDecl => |decl| {
|
|
|
|
const name = decl.func_name.lexeme;
|
|
|
|
const name_cstr = try std.cstr.addNullByte(self.allocator, name);
|
|
|
|
errdefer self.allocator.free(name_cstr);
|
|
|
|
|
2019-09-24 16:23:23 +00:00
|
|
|
const fn_ret_type = decl.return_type.lexeme;
|
2019-09-21 02:40:21 +00:00
|
|
|
|
|
|
|
var param_types = llvm.LLVMTypeList.init(self.allocator);
|
|
|
|
errdefer param_types.deinit();
|
|
|
|
|
|
|
|
for (decl.params.toSlice()) |param| {
|
2019-09-24 16:32:00 +00:00
|
|
|
// TODO better type resolution
|
|
|
|
try param_types.append(try basicTypeToLLVM(param.typ.lexeme));
|
2019-09-21 02:40:21 +00:00
|
|
|
}
|
|
|
|
|
2019-09-24 16:23:23 +00:00
|
|
|
var llvm_ret_type = llvm.LLVMFunctionType(
|
2019-09-24 16:32:00 +00:00
|
|
|
try basicTypeToLLVM(fn_ret_type),
|
2019-09-21 02:40:21 +00:00
|
|
|
param_types.toSlice().ptr,
|
|
|
|
@intCast(c_uint, param_types.len),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
|
2019-09-24 16:23:23 +00:00
|
|
|
var func = llvm.LLVMAddFunction(mod, name_cstr.ptr, llvm_ret_type);
|
2019-09-24 17:12:37 +00:00
|
|
|
|
|
|
|
var buf = try self.allocator.alloc(u8, 512);
|
|
|
|
var entry_lbl = try std.fmt.bufPrint(buf, "fn_{}_entry", name);
|
|
|
|
var entry_lbl_cstr = try std.cstr.addNullByte(self.allocator, entry_lbl);
|
|
|
|
var entry = llvm.LLVMAppendBasicBlock(func, entry_lbl_cstr.ptr);
|
2019-09-21 02:40:21 +00:00
|
|
|
|
|
|
|
var builder = llvm.LLVMCreateBuilder();
|
|
|
|
llvm.LLVMPositionBuilderAtEnd(builder, entry);
|
|
|
|
|
2019-09-23 21:33:30 +00:00
|
|
|
for (decl.body.toSlice()) |stmt| {
|
|
|
|
// TODO custom function context for us
|
2019-09-24 01:07:19 +00:00
|
|
|
try self.emitStmt(builder, &stmt);
|
2019-09-23 21:33:30 +00:00
|
|
|
}
|
|
|
|
|
2019-09-24 01:07:19 +00:00
|
|
|
//var tmp = llvm.LLVMBuildAdd(
|
|
|
|
// builder,
|
|
|
|
// llvm.LLVMGetParam(func, 0),
|
|
|
|
// llvm.LLVMGetParam(func, 1),
|
|
|
|
// c"tmp",
|
|
|
|
//);
|
2019-09-21 02:40:21 +00:00
|
|
|
|
2019-09-24 17:12:37 +00:00
|
|
|
std.debug.warn("cgen: generated function '{}'\n", name);
|
2019-09-21 02:40:21 +00:00
|
|
|
},
|
2019-09-24 20:47:17 +00:00
|
|
|
|
2019-09-21 02:40:21 +00:00
|
|
|
else => {
|
|
|
|
std.debug.warn("got unhandled Node {}\n", node.*);
|
|
|
|
unreachable;
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gen(self: *Codegen, root: *ast.Node) !void {
|
|
|
|
std.debug.warn("cgen: start gen\n");
|
2019-09-22 02:56:22 +00:00
|
|
|
_ = llvm.LLVMInitializeNativeTarget();
|
2019-09-21 15:31:47 +00:00
|
|
|
|
|
|
|
var mod = llvm.LLVMModuleCreateWithName(c"awoo").?;
|
2019-09-21 02:40:21 +00:00
|
|
|
defer llvm.LLVMDisposeModule(mod);
|
|
|
|
|
|
|
|
for (root.Root.toSlice()) |child| {
|
|
|
|
std.debug.warn("cgen: gen child {}\n", child);
|
|
|
|
try self.genNode(mod, &child);
|
|
|
|
}
|
|
|
|
|
|
|
|
var err: ?[*]u8 = null;
|
2019-09-22 02:56:22 +00:00
|
|
|
defer llvm.LLVMDisposeMessage(err);
|
|
|
|
|
2019-09-21 02:40:21 +00:00
|
|
|
_ = llvm.LLVMVerifyModule(
|
|
|
|
mod,
|
|
|
|
llvm.LLVMVerifierFailureAction.LLVMAbortProcessAction,
|
|
|
|
&err,
|
|
|
|
);
|
2019-09-21 15:37:41 +00:00
|
|
|
|
|
|
|
if (llvm.LLVMWriteBitcodeToFile(mod, c"awoo.bc") != 0) {
|
2019-09-22 02:56:22 +00:00
|
|
|
std.debug.warn("error writing bitcode to file: {}\n", sliceify(err));
|
2019-09-22 21:26:00 +00:00
|
|
|
return CompileError.LLVMError;
|
2019-09-22 02:56:22 +00:00
|
|
|
}
|
|
|
|
|
2019-09-22 21:40:15 +00:00
|
|
|
llvm.LLVMInitializeAllTargetInfos();
|
|
|
|
llvm.LLVMInitializeAllTargets();
|
|
|
|
llvm.LLVMInitializeAllTargetMCs();
|
|
|
|
llvm.LLVMInitializeAllAsmParsers();
|
|
|
|
llvm.LLVMInitializeAllAsmPrinters();
|
2019-09-22 02:56:22 +00:00
|
|
|
|
|
|
|
var engine: llvm.LLVMExecutionEngineRef = undefined;
|
|
|
|
if (llvm.LLVMCreateExecutionEngineForModule(&engine, mod, &err) != 0) {
|
|
|
|
std.debug.warn("failed to create execution engine: {}\n", sliceify(err));
|
2019-09-22 21:26:00 +00:00
|
|
|
return CompileError.LLVMError;
|
2019-09-22 02:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var machine = llvm.LLVMGetExecutionEngineTargetMachine(engine);
|
|
|
|
defer llvm.LLVMDisposeTargetMachine(machine);
|
|
|
|
|
|
|
|
var target = llvm.LLVMGetTargetMachineTarget(machine);
|
|
|
|
var target_data = llvm.LLVMCreateTargetDataLayout(machine);
|
|
|
|
var data_layout = llvm.LLVMCopyStringRepOfTargetData(target_data);
|
|
|
|
llvm.LLVMSetDataLayout(mod, data_layout);
|
|
|
|
|
2019-09-24 01:07:19 +00:00
|
|
|
var outpath_cstr = try std.cstr.addNullByte(self.allocator, "outpath.o");
|
2019-09-22 02:56:22 +00:00
|
|
|
|
2019-09-24 01:07:19 +00:00
|
|
|
//var asmpath_cstr = try std.cstr.addNullByte(self.allocator, "output.S");
|
2019-09-22 21:40:15 +00:00
|
|
|
|
2019-09-22 02:56:22 +00:00
|
|
|
var desc = llvm.LLVMGetTargetDescription(target);
|
|
|
|
var features = llvm.LLVMGetTargetMachineFeatureString(machine);
|
|
|
|
var triple = llvm.LLVMGetTargetMachineTriple(machine);
|
|
|
|
|
|
|
|
std.debug.warn("target: {}\n", sliceify(desc));
|
|
|
|
std.debug.warn("triple: {}\n", sliceify(triple));
|
|
|
|
std.debug.warn("features: {}\n", sliceify(features));
|
|
|
|
|
2019-09-22 21:40:15 +00:00
|
|
|
//if (llvm.LLVMTargetMachineEmitToFile(
|
|
|
|
// machine,
|
|
|
|
// mod,
|
|
|
|
// asmpath_cstr.ptr,
|
|
|
|
// llvm.LLVMCodeGenFileType.LLVMAssemblyFile,
|
|
|
|
// &err,
|
|
|
|
//) != 0) {
|
|
|
|
// std.debug.warn("failed to emit to assembly file: {}\n", sliceify(err));
|
|
|
|
// return CompileError.LLVMError;
|
|
|
|
//}
|
|
|
|
|
2019-09-22 02:56:22 +00:00
|
|
|
if (llvm.LLVMTargetMachineEmitToFile(
|
|
|
|
machine,
|
|
|
|
mod,
|
|
|
|
outpath_cstr.ptr,
|
|
|
|
llvm.LLVMCodeGenFileType.LLVMObjectFile,
|
|
|
|
&err,
|
|
|
|
) != 0) {
|
|
|
|
std.debug.warn("failed to emit to file: {}\n", sliceify(err));
|
2019-09-22 21:26:00 +00:00
|
|
|
return CompileError.LLVMError;
|
2019-09-21 15:37:41 +00:00
|
|
|
}
|
2019-09-21 02:40:21 +00:00
|
|
|
}
|
|
|
|
};
|