From 849bbc0e94207d9cecd08c69a910f71b141529c1 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 27 Sep 2019 20:39:16 -0300 Subject: [PATCH 1/5] add support for resolving parameters in function body --- examples/hello.ry | 4 ++-- src/ast_printer.zig | 2 +- src/codegen.zig | 39 ++++++++++++++++++++++++++++++--------- src/comp_ctx.zig | 34 +++++++++++++++------------------- 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/examples/hello.ry b/examples/hello.ry index e935ddc..7be6a9a 100644 --- a/examples/hello.ry +++ b/examples/hello.ry @@ -42,8 +42,8 @@ fn multwo(num: i32, double_flag: bool) i32 { } } -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 { diff --git a/src/ast_printer.zig b/src/ast_printer.zig index a5d23e3..48f9ae2 100644 --- a/src/ast_printer.zig +++ b/src/ast_printer.zig @@ -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), ); } diff --git a/src/codegen.zig b/src/codegen.zig index 64c7e72..0e1f0be 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -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.*)); @@ -333,6 +360,7 @@ pub const Codegen = struct { .Root => @panic("Should not have gotten Root"), .FnDecl => |decl| { const name = decl.func_name.lexeme; + self.current_function_name = name; var fn_sym_search = self.ctx.symbol_table.get(name).?.value; std.debug.assert(comp.SymbolType(fn_sym_search) == .Function); @@ -347,7 +375,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( @@ -372,13 +400,6 @@ pub const Codegen = struct { 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); }, diff --git a/src/comp_ctx.zig b/src/comp_ctx.zig index e50a5e9..c6d7a37 100644 --- a/src/comp_ctx.zig +++ b/src/comp_ctx.zig @@ -69,6 +69,13 @@ pub const Scope = struct { } }; +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 +85,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 @@ -264,10 +257,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 +272,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 +349,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); From dfb79891237be767515bcf763af232a8e659c2cc Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 27 Sep 2019 21:22:03 -0300 Subject: [PATCH 2/5] add necessary hello.c code as we dont have linking step --- examples/hello.c | 5 +++++ examples/hello.ry | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 examples/hello.c diff --git a/examples/hello.c b/examples/hello.c new file mode 100644 index 0000000..fff2531 --- /dev/null +++ b/examples/hello.c @@ -0,0 +1,5 @@ +#include + +int main(void) { + printf("add(2, 2) = %d\n", add(2, 2)); +} diff --git a/examples/hello.ry b/examples/hello.ry index 7be6a9a..d3a4e54 100644 --- a/examples/hello.ry +++ b/examples/hello.ry @@ -47,7 +47,7 @@ fn func_refer_param(b: i32) i32 { } fn add(a: i32, b: i32) i32 { - return 69 + 69; + return a + b; } fn and_fn() bool { From 77c88fad34fc9a7a0ae6e62918cb59a6363044e5 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 27 Sep 2019 21:23:11 -0300 Subject: [PATCH 3/5] update readme with usage instructions --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6eb90c3..434dec4 100644 --- a/README.md +++ b/README.md @@ -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 +``` From d235ce0d139b73b8d371dc9bc56ec6338563b4ff Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 27 Sep 2019 22:36:35 -0300 Subject: [PATCH 4/5] 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 --- examples/hello.ry | 7 +++---- src/analysis.zig | 2 +- src/codegen.zig | 51 ++++++++++++++++++++++++++++++++++++----------- src/comp_ctx.zig | 10 +++++++++- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/examples/hello.ry b/examples/hello.ry index d3a4e54..0f97d4f 100644 --- a/examples/hello.ry +++ b/examples/hello.ry @@ -32,13 +32,12 @@ 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; } } diff --git a/src/analysis.zig b/src/analysis.zig index f5c4897..4081d43 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -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: diff --git a/src/codegen.zig b/src/codegen.zig index 0e1f0be..b7212eb 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -284,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 @@ -296,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 @@ -309,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) @@ -319,6 +324,8 @@ pub const Codegen = struct { else => {}, } } + + self.ctx.dumpScope(); } if (!else_rets) @@ -330,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.?); + + _ = llvm.LLVMBuildAlloca( + builder, + try self.typeToLLVM(var_metadata.typ), + name_cstr.ptr, + ); + + // TODO generate the store for the initializer + //var llvm_expr = try self.emitExpr(vardecl.value); + }, else => { std.debug.warn("Got unexpected stmt {}\n", stmt.*); @@ -350,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, @@ -361,10 +386,9 @@ pub const Codegen = struct { .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); @@ -396,6 +420,9 @@ 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); } diff --git a/src/comp_ctx.zig b/src/comp_ctx.zig index c6d7a37..b22768c 100644 --- a/src/comp_ctx.zig +++ b/src/comp_ctx.zig @@ -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,6 +65,12 @@ 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(); } @@ -184,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; } From 81fd7184037889b2ee175d56229b7d2189d591ce Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 27 Sep 2019 22:49:33 -0300 Subject: [PATCH 5/5] codegen: emit vardecl initializer --- src/codegen.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index b7212eb..45d369d 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -339,7 +339,6 @@ pub const Codegen = struct { _ = llvm.LLVMBuildUnreachable(builder); }, - // TODO .VarDecl => |vardecl| { // we alaready inferred the type of the variable in the // analyze pass and the current scope contains the variable's @@ -352,14 +351,15 @@ pub const Codegen = struct { var fn_symbol = self.getFnSymbol(self.current_function_name.?); - _ = llvm.LLVMBuildAlloca( + // TODO add llvm value ref to var metadata as well + var variable = llvm.LLVMBuildAlloca( builder, try self.typeToLLVM(var_metadata.typ), name_cstr.ptr, ); - // TODO generate the store for the initializer - //var llvm_expr = try self.emitExpr(vardecl.value); + var llvm_expr = try self.emitExpr(builder, vardecl.value); + _ = llvm.LLVMBuildStore(builder, llvm_expr, variable); }, else => {