Compare commits

...

5 Commits

Author SHA1 Message Date
Luna 81fd718403 codegen: emit vardecl initializer 2019-09-27 22:49:33 -03:00
Luna d235ce0d13 add codegen for allocating vardecls onto stack (not init yet)
codegen pass needs type information about the variable. thankfully,
analyze pass already did the hard work for it, and scope info has types
of all its declared names. we use that info to generate the alloca

to have information about which current scope we are, we add a "child
index" to scopes so we can repoint current context at them instead of
bumpScope(), which always creates new, empty ones.

initializer exprs are next
2019-09-27 22:36:35 -03:00
Luna 77c88fad34 update readme with usage instructions 2019-09-27 21:23:11 -03:00
Luna dfb7989123 add necessary hello.c code as we dont have linking step 2019-09-27 21:22:03 -03:00
Luna 849bbc0e94 add support for resolving parameters in function body 2019-09-27 20:39:16 -03:00
7 changed files with 116 additions and 52 deletions

View File

@ -1,6 +1,6 @@
# rayoko
a programming language
a toy programming language and compiler
this time it'll work i promise it wont be like vig plrease
@ -11,3 +11,11 @@ git clone https://gitdab.com/luna/rayoko
cd rayoko
zig build install --prefix ~/.local/
```
# use
```
rayoko examples/hello.ry # outputs to outpath.o
gcc outpath.o examples/hello.c -o hello
./hello
```

5
examples/hello.c Normal file
View File

@ -0,0 +1,5 @@
#include <stdio.h>
int main(void) {
printf("add(2, 2) = %d\n", add(2, 2));
}

View File

@ -32,22 +32,21 @@ fn test_function() B {
}
fn multwo(num: i32, double_flag: bool) i32 {
// TODO resolve expr variables
if (true) {
if (double_flag) {
var truthy = true;
return 1 * 2;
return num * 2;
} else {
var falsey = false;
return 1;
return num;
}
}
fn multwo_with_one(b: i32) i32 {
return multwo(b, false) + b;
fn func_refer_param(b: i32) i32 {
return b * 231 + b;
}
fn add(a: i32, b: i32) i32 {
return 69 + 69;
return a + b;
}
fn and_fn() bool {

View File

@ -458,7 +458,7 @@ pub const TypeSolver = struct {
// for a function, we always create a new root scope for it
// and force-set it into the current context
var scope = try comp.Scope.create(self.allocator, null, "function");
var scope = try comp.Scope.create(self.allocator, null, name);
errdefer scope.deinit();
// we intentionally insert the function so that:

View File

@ -401,7 +401,7 @@ pub fn printContext(ctx: CompilationContext) void {
std.debug.warn(
"\tparameter {} typ {}\n",
param_kv.key,
prettyType(param_kv.value),
prettyType(param_kv.value.typ),
);
}

View File

@ -31,6 +31,8 @@ pub const Codegen = struct {
ctx: *comp.CompilationContext,
llvm_table: LLVMTable,
current_function_name: ?[]const u8 = null,
pub fn init(allocator: *std.mem.Allocator, ctx: *comp.CompilationContext) Codegen {
return Codegen{
.allocator = allocator,
@ -218,7 +220,32 @@ pub const Codegen = struct {
return llvm.LLVMBuildStore(builder, null, assign_expr);
},
.Variable => @panic("TODO emit variables"),
.Variable => |vari| {
var kv_opt = self.ctx.metadata_map.get(expr);
if (kv_opt == null) {
std.debug.warn("variable {} not fully analyzed\n", vari);
return CompileError.EmitError;
}
// we have metadata, which means we can check if the variable
// is coming from the scope or from the function
var metadata = kv_opt.?.value;
return switch (metadata.using) {
.Function => blk: {
var param = metadata.from_function.?.parameters.get(vari.lexeme).?.value;
var llvm_func = self.llvm_table.get(self.current_function_name.?).?.value;
// may i thank all the demons in hell for giving me
// good energies to make this work
break :blk llvm.LLVMGetParam(llvm_func, @intCast(c_uint, param.idx));
},
.Scope => @panic("TODO local variables"),
};
},
else => {
std.debug.warn("Got unexpected expr {}\n", ast.ExprType(expr.*));
@ -257,6 +284,8 @@ pub const Codegen = struct {
var then_rets = false;
var else_rets = false;
self.ctx.setScope(self.ctx.current_scope.?.nextChild());
for (ifstmt.then_branch.toSlice()) |then_stmt| {
// keep emitting until branch has ret
@ -269,6 +298,8 @@ pub const Codegen = struct {
}
}
self.ctx.dumpScope();
// 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
@ -282,6 +313,7 @@ pub const Codegen = struct {
// roughly translating to kaleidoscope's
// 'Else *ElseV = Else->codegen();'
if (ifstmt.else_branch) |else_block| {
self.ctx.setScope(self.ctx.current_scope.?.nextChild());
for (else_block.toSlice()) |else_stmt| {
// keep emitting until branch has ret
if (!else_rets)
@ -292,6 +324,8 @@ pub const Codegen = struct {
else => {},
}
}
self.ctx.dumpScope();
}
if (!else_rets)
@ -303,18 +337,30 @@ pub const Codegen = struct {
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);
},
// TODO
.VarDecl => {},
.VarDecl => |vardecl| {
// we alaready inferred the type of the variable in the
// analyze pass and the current scope contains the variable's
// type(hopefully), so we resolve it
const name = vardecl.name.lexeme;
var var_metadata = try self.ctx.resolveVarType(name);
var name_cstr = try std.cstr.addNullByte(self.allocator, name);
errdefer self.allocator.free(name_cstr);
var fn_symbol = self.getFnSymbol(self.current_function_name.?);
// TODO add llvm value ref to var metadata as well
var variable = llvm.LLVMBuildAlloca(
builder,
try self.typeToLLVM(var_metadata.typ),
name_cstr.ptr,
);
var llvm_expr = try self.emitExpr(builder, vardecl.value);
_ = llvm.LLVMBuildStore(builder, llvm_expr, variable);
},
else => {
std.debug.warn("Got unexpected stmt {}\n", stmt.*);
@ -323,6 +369,12 @@ pub const Codegen = struct {
}
}
fn getFnSymbol(self: *@This(), name: []const u8) comp.FunctionSymbol {
var fn_sym_search = self.ctx.symbol_table.get(name).?.value;
std.debug.assert(comp.SymbolType(fn_sym_search) == .Function);
return fn_sym_search.Function;
}
/// Emit LLVM ir for the given node.
fn genNode(
self: *Codegen,
@ -333,10 +385,10 @@ pub const Codegen = struct {
.Root => @panic("Should not have gotten Root"),
.FnDecl => |decl| {
const name = decl.func_name.lexeme;
self.current_function_name = name;
std.debug.warn("cgen: genning function '{}'\n", name);
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;
var fn_sym = self.getFnSymbol(name);
const name_cstr = try std.cstr.addNullByte(self.allocator, name);
errdefer self.allocator.free(name_cstr);
@ -347,7 +399,7 @@ pub const Codegen = struct {
for (decl.params.toSlice()) |param| {
try param_types.append(try self.typeToLLVM(fn_sym.parameters.get(
param.name.lexeme,
).?.value));
).?.value.typ));
}
var llvm_ret_type = llvm.LLVMFunctionType(
@ -368,17 +420,13 @@ pub const Codegen = struct {
var builder = llvm.LLVMCreateBuilder();
llvm.LLVMPositionBuilderAtEnd(builder, entry);
self.ctx.setScope(fn_sym.scope);
defer self.ctx.dumpScope();
for (decl.body.toSlice()) |stmt| {
try self.emitStmt(builder, &stmt);
}
//var tmp = llvm.LLVMBuildAdd(
// builder,
// llvm.LLVMGetParam(func, 0),
// llvm.LLVMGetParam(func, 1),
// c"tmp",
//);
std.debug.warn("cgen: generated function '{}'\n", name);
},

View File

@ -39,8 +39,9 @@ pub const Scope = struct {
parent: ?*Scope,
env: UnderlyingTypeMap,
/// Used for debug information.
/// List of children in the scope.
children: ScopeList,
cur_child_idx: usize = 0,
allocator: *std.mem.Allocator,
id: ?[]const u8 = null,
@ -64,11 +65,24 @@ pub const Scope = struct {
return child;
}
pub fn nextChild(self: *@This()) *Scope {
var child = self.children.at(self.cur_child_idx);
self.cur_child_idx += 1;
return child;
}
pub fn deinit(self: *const @This()) void {
self.env.deinit();
}
};
pub const Parameter = struct {
idx: usize,
typ: SymbolUnderlyingType,
};
pub const ParameterMap = std.StringHashMap(Parameter);
// functions, for our purposes, other than symbols, have:
// - a return type
// - parameters
@ -78,24 +92,10 @@ pub const FunctionSymbol = struct {
/// Parameters for a function are also a table instead of an ArrayList
/// because we want to resolve identifiers to them.
parameters: UnderlyingTypeMap,
parameters: ParameterMap,
parameter_list: TypeList,
scope: *Scope,
/// Find a given identifier in the function. Can resolve to either a parameter
pub fn findSymbol(self: *const @This(), identifier: []const u8) ?SymbolData {
// try to find it on overall variable symbols
// TODO
//var var_sym = self.symbols.get(identifier);
//if (var_sym != null) return var_sym.?.value;
//var param_sym = self.parameters.get(identifier);
//if (param_sym != null) return param_sym.?.value;
return null;
}
};
// structs are hashmaps pointing to SymbolUnderlyingType
@ -191,6 +191,7 @@ pub const CompilationContext = struct {
/// Set a given scope as the current scope.
pub fn setScope(self: *@This(), scope: *Scope) void {
std.debug.warn("==set== set scope to {}\n", scope.id);
self.current_scope = scope;
}
@ -264,10 +265,13 @@ pub const CompilationContext = struct {
param_types: TypeList,
scope: *Scope,
) !void {
var type_map = UnderlyingTypeMap.init(self.allocator);
var param_map = ParameterMap.init(self.allocator);
for (decl.params.toSlice()) |param, idx| {
_ = try type_map.put(param.name.lexeme, param_types.at(idx));
_ = try param_map.put(param.name.lexeme, Parameter{
.idx = idx,
.typ = param_types.at(idx),
});
}
const lex = decl.func_name.lexeme;
@ -276,7 +280,7 @@ pub const CompilationContext = struct {
.Function = FunctionSymbol{
.decl = decl,
.return_type = ret_type,
.parameters = type_map,
.parameters = param_map,
.parameter_list = param_types,
.scope = scope,
},
@ -353,7 +357,7 @@ pub const CompilationContext = struct {
if (self.cur_function) |cur_function| {
var kv_opt = cur_function.parameters.get(name);
if (kv_opt) |kv| return VariableMetadata.withParam(cur_function, kv.value);
if (kv_opt) |kv| return VariableMetadata.withParam(cur_function, kv.value.typ);
}
std.debug.warn("Unknown name {}\n", name);