rayoko/src/codegen.zig

413 lines
16 KiB
Zig
Raw Normal View History

2019-09-21 02:40:21 +00:00
const std = @import("std");
const ast = @import("ast.zig");
const llvm = @import("llvm.zig");
const comp = @import("comp_ctx.zig");
const types = @import("types.zig");
2019-09-21 02:40:21 +00:00
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.?)];
}
pub const CompileError = error{
LLVMError,
EmitError,
TypeError,
};
2019-09-22 21:26:00 +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,
ctx: *comp.CompilationContext,
pub fn init(allocator: *std.mem.Allocator, ctx: *comp.CompilationContext) Codegen {
return Codegen{ .allocator = allocator, .ctx = ctx };
}
fn typeToLLVM(self: *@This(), typ: comp.SymbolUnderlyingType) !llvm.LLVMTypeRef {
return switch (typ) {
.Integer32 => llvm.LLVMInt32Type(),
.Integer64 => llvm.LLVMInt64Type(),
.Bool => llvm.LLVMInt1Type(),
.OpaqueType => |val| {
std.debug.warn("Invalid return type: {}\n", val);
return CompileError.TypeError;
},
.Struct, .Enum => |lex| blk: {
var sym_data = self.ctx.symbol_table.get(lex).?.value;
break :blk switch (sym_data) {
.Struct => unreachable,
.Enum => llvm.LLVMInt32Type(),
else => {
std.debug.warn("Function {} is not a type\n", lex);
return CompileError.TypeError;
},
};
},
2019-09-21 02:40:21 +00:00
else => {
std.debug.warn("TODO handle {}\n", typ);
return CompileError.TypeError;
},
};
2019-09-21 02:40:21 +00:00
}
fn emitExpr(self: *Codegen, builder: var, expr: *const ast.Expr) anyerror!llvm.LLVMValueRef {
// 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
return switch (expr.*) {
// TODO handle all literals, construct llvm values for them
.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);
},
.Bool => |val| blk2: {
break :blk2 mkLLVMBool(val);
},
else => unreachable,
};
},
.Binary => |binary| {
var left = try self.emitExpr(builder, binary.left);
var right = try self.emitExpr(builder, binary.right);
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"),
.And => llvm.LLVMBuildAnd(builder, left, right, c"andtmp"),
.Or => llvm.LLVMBuildOr(builder, left, right, c"ortmp"),
else => {
std.debug.warn("Unexpected binary operator: '{}'\n", binary.op);
return CompileError.EmitError;
},
};
},
.Get => |get| {
// TODO rename get.struc to get.target
var struc = get.struc.*;
switch (struc) {
.Variable => |vari| {
// first, we must check if the target is a type
// and emit accordingly
var kv_sym_opt = self.ctx.symbol_table.get(vari.lexeme);
if (kv_sym_opt) |kv| {
var sym = kv.value;
switch (sym) {
.Enum => |map| {
var val = map.get(get.name.lexeme);
if (val == null) {
std.debug.warn(
"enum {} does not have field {}\n",
vari.lexeme,
get.name.lexeme,
);
}
return llvm.LLVMConstInt(llvm.LLVMInt32Type(), val.?.value, 1);
},
.Struct => @panic("TODO handle struct"),
else => {
std.debug.warn("Invalid get target: {}\n", comp.SymbolType(sym));
return CompileError.EmitError;
},
}
}
// if not, its likely a variable, we should do it accordingly
// as well
@panic("TODO handle variables");
},
else => {
std.debug.warn("Invalid get target: {}\n", ast.ExprType(struc));
return CompileError.EmitError;
},
}
},
else => {
std.debug.warn("Got unexpected expr {}\n", ast.ExprType(expr.*));
return CompileError.EmitError;
},
};
}
fn emitStmt(self: *Codegen, builder: var, stmt: *const ast.Stmt) anyerror!void {
switch (stmt.*) {
.Expr => |expr| _ = try self.emitExpr(builder, expr),
.Return => |ret| {
var ret_expr = try self.emitExpr(builder, ret.value);
_ = llvm.LLVMBuildRet(builder, ret_expr);
},
.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");
var insert = llvm.LLVMGetInsertBlock(builder);
var function = llvm.LLVMGetBasicBlockParent(insert);
var then_bb = llvm.LLVMAppendBasicBlock(function, c"then");
var else_bb = llvm.LLVMAppendBasicBlock(function, c"else");
var merge_bb = llvm.LLVMAppendBasicBlock(function, 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();'
var then_rets = false;
var else_rets = false;
for (ifstmt.then_branch.toSlice()) |then_stmt| {
// keep emitting until branch has ret
if (!then_rets)
try self.emitStmt(builder, &then_stmt);
switch (then_stmt) {
.Return => then_rets = true,
else => {},
}
}
// only build the br instruction if we didn't ret, because
// there can't be any instruction after a terminator
// same applies for the else branch
if (!then_rets)
_ = 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| {
// keep emitting until branch has ret
if (!else_rets)
try self.emitStmt(builder, &else_stmt);
switch (else_stmt) {
.Return => else_rets = true,
else => {},
}
}
}
if (!else_rets)
_ = llvm.LLVMBuildBr(builder, merge_bb);
else_bb = llvm.LLVMGetInsertBlock(builder);
llvm.LLVMPositionBuilderAtEnd(builder, merge_bb);
if (then_rets and else_rets)
_ = llvm.LLVMBuildUnreachable(builder);
// phis aren't needed since our ifs arent expressions
//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);
},
else => {
std.debug.warn("Got unexpected statement {}\n", stmt.*);
return CompileError.EmitError;
},
}
}
/// Emit LLVM ir for the given node.
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;
var fn_sym_search = self.ctx.symbol_table.get(name).?.value;
std.debug.assert(comp.SymbolType(fn_sym_search) == .Function);
var fn_sym = fn_sym_search.Function;
2019-09-21 02:40:21 +00:00
const name_cstr = try std.cstr.addNullByte(self.allocator, name);
errdefer self.allocator.free(name_cstr);
var param_types = llvm.LLVMTypeList.init(self.allocator);
errdefer param_types.deinit();
for (decl.params.toSlice()) |param| {
try param_types.append(try self.typeToLLVM(fn_sym.parameters.get(
param.name.lexeme,
).?.value));
2019-09-21 02:40:21 +00:00
}
var llvm_ret_type = llvm.LLVMFunctionType(
try self.typeToLLVM(fn_sym.return_type),
2019-09-21 02:40:21 +00:00
param_types.toSlice().ptr,
@intCast(c_uint, param_types.len),
0,
);
var func = llvm.LLVMAddFunction(mod, name_cstr.ptr, llvm_ret_type);
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);
for (decl.body.toSlice()) |stmt| {
// TODO custom function context for us
try self.emitStmt(builder, &stmt);
}
//var tmp = llvm.LLVMBuildAdd(
// builder,
// llvm.LLVMGetParam(func, 0),
// llvm.LLVMGetParam(func, 1),
// c"tmp",
//);
2019-09-21 02:40:21 +00:00
std.debug.warn("cgen: generated function '{}'\n", name);
2019-09-21 02:40:21 +00:00
},
2019-09-25 14:59:36 +00:00
// NOTE: enums don't have specific llvm ir code generated for them
.Enum => {},
2019-09-21 02:40:21 +00:00
else => {
std.debug.warn("TODO handle node type {}\n", @tagName(node.*));
return;
2019-09-21 02:40:21 +00:00
},
}
}
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 {}\n", @tagName(child));
2019-09-21 02:40:21 +00:00
try self.genNode(mod, &child);
}
var err: ?[*]u8 = null;
2019-09-22 02:56:22 +00:00
defer llvm.LLVMDisposeMessage(err);
2019-09-24 21:59:00 +00:00
if (llvm.LLVMPrintModuleToFile(mod, c"output.ll", &err) != 0) {
std.debug.warn("error printing module to file: {}\n", sliceify(err));
return CompileError.LLVMError;
}
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
}
std.debug.warn("cgen: verify llvm module\n");
_ = llvm.LLVMVerifyModule(
mod,
llvm.LLVMVerifierFailureAction.LLVMAbortProcessAction,
&err,
);
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);
var outpath_cstr = try std.cstr.addNullByte(self.allocator, "outpath.o");
2019-09-22 02:56:22 +00:00
//var asmpath_cstr = try std.cstr.addNullByte(self.allocator, "output.S");
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));
//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
}
};