| author | |
| committer | |
| log | d29dd5834b9d7386bb88e44bd2852428863cae81 |
| tree | 6a1f15d341c00be937ef9308ec750b35c308a5c0 |
| parent | af12596e8d728423e361e4755a6078c5ef8faf69 |
These are now supported enough that this example code hits the
limitations of the register allocator:
fn add(a: u32, b: u32) void {
const c = a + b; // 7
const d = a + c; // 10
const e = d + b; // 14
assert(e == 14);
}
// error: TODO implement copyToNewRegister
So now the next step is to implement register allocation as planned.5 files changed, 72 insertions(+), 36 deletions(-)
src-self-hosted/Module.zig+33-10| ... | ... | @@ -700,19 +700,22 @@ pub const Scope = struct { |
| 700 | 700 | pub const GenZIR = struct { |
| 701 | 701 | pub const base_tag: Tag = .gen_zir; |
| 702 | 702 | base: Scope = Scope{ .tag = base_tag }, |
| 703 | /// Parents can be: `GenZIR`, `ZIRModule`, `File` | |
| 704 | parent: *Scope, | |
| 703 | 705 | decl: *Decl, |
| 704 | 706 | arena: *Allocator, |
| 707 | /// The first N instructions in a function body ZIR are arg instructions. | |
| 705 | 708 | instructions: std.ArrayListUnmanaged(*zir.Inst) = .{}, |
| 706 | 709 | }; |
| 707 | 710 | |
| 708 | 711 | /// This structure lives as long as the AST generation of the Block |
| 709 | /// node that contains the variable. This struct's parents can be | |
| 710 | /// other `LocalVar` and finally a `GenZIR` at the top. | |
| 712 | /// node that contains the variable. | |
| 711 | 713 | pub const LocalVar = struct { |
| 712 | 714 | pub const base_tag: Tag = .local_var; |
| 713 | 715 | base: Scope = Scope{ .tag = base_tag }, |
| 714 | gen_zir: *GenZIR, | |
| 716 | /// Parents can be: `LocalVar`, `GenZIR`. | |
| 715 | 717 | parent: *Scope, |
| 718 | gen_zir: *GenZIR, | |
| 716 | 719 | name: []const u8, |
| 717 | 720 | inst: *zir.Inst, |
| 718 | 721 | }; |
| ... | ... | @@ -1164,6 +1167,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1164 | 1167 | var fn_type_scope: Scope.GenZIR = .{ |
| 1165 | 1168 | .decl = decl, |
| 1166 | 1169 | .arena = &fn_type_scope_arena.allocator, |
| 1170 | .parent = decl.scope, | |
| 1167 | 1171 | }; |
| 1168 | 1172 | defer fn_type_scope.instructions.deinit(self.gpa); |
| 1169 | 1173 | |
| ... | ... | @@ -1241,12 +1245,32 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1241 | 1245 | var gen_scope: Scope.GenZIR = .{ |
| 1242 | 1246 | .decl = decl, |
| 1243 | 1247 | .arena = &gen_scope_arena.allocator, |
| 1248 | .parent = decl.scope, | |
| 1244 | 1249 | }; |
| 1245 | 1250 | defer gen_scope.instructions.deinit(self.gpa); |
| 1246 | 1251 | |
| 1252 | // We need an instruction for each parameter, and they must be first in the body. | |
| 1253 | try gen_scope.instructions.resize(self.gpa, fn_proto.params_len); | |
| 1254 | var params_scope = &gen_scope.base; | |
| 1255 | for (fn_proto.params()) |param, i| { | |
| 1256 | const name_token = param.name_token.?; | |
| 1257 | const src = tree.token_locs[name_token].start; | |
| 1258 | const param_name = tree.tokenSlice(name_token); | |
| 1259 | const arg = try newZIRInst(&gen_scope_arena.allocator, src, zir.Inst.Arg, .{}, .{}); | |
| 1260 | gen_scope.instructions.items[i] = &arg.base; | |
| 1261 | const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVar); | |
| 1262 | sub_scope.* = .{ | |
| 1263 | .parent = params_scope, | |
| 1264 | .gen_zir = &gen_scope, | |
| 1265 | .name = param_name, | |
| 1266 | .inst = &arg.base, | |
| 1267 | }; | |
| 1268 | params_scope = &sub_scope.base; | |
| 1269 | } | |
| 1270 | ||
| 1247 | 1271 | const body_block = body_node.cast(ast.Node.Block).?; |
| 1248 | 1272 | |
| 1249 | try astgen.blockExpr(self, &gen_scope.base, body_block); | |
| 1273 | try astgen.blockExpr(self, params_scope, body_block); | |
| 1250 | 1274 | |
| 1251 | 1275 | if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or |
| 1252 | 1276 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())) |
| ... | ... | @@ -2236,17 +2260,16 @@ fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.Compile |
| 2236 | 2260 | fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { |
| 2237 | 2261 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 2238 | 2262 | const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; |
| 2263 | const param_index = b.instructions.items.len; | |
| 2239 | 2264 | const param_count = fn_ty.fnParamLen(); |
| 2240 | if (inst.positionals.index >= param_count) { | |
| 2265 | if (param_index >= param_count) { | |
| 2241 | 2266 | return self.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{ |
| 2242 | inst.positionals.index, | |
| 2267 | param_index, | |
| 2243 | 2268 | param_count, |
| 2244 | 2269 | }); |
| 2245 | 2270 | } |
| 2246 | const param_type = fn_ty.fnParamType(inst.positionals.index); | |
| 2247 | return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, .{ | |
| 2248 | .index = inst.positionals.index, | |
| 2249 | }); | |
| 2271 | const param_type = fn_ty.fnParamType(param_index); | |
| 2272 | return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, {}); | |
| 2250 | 2273 | } |
| 2251 | 2274 | |
| 2252 | 2275 | fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst { |
src-self-hosted/astgen.zig+25-14| ... | ... | @@ -160,6 +160,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In |
| 160 | 160 | } |
| 161 | 161 | } |
| 162 | 162 | var block_scope: Scope.GenZIR = .{ |
| 163 | .parent = scope, | |
| 163 | 164 | .decl = scope.decl().?, |
| 164 | 165 | .arena = scope.arena(), |
| 165 | 166 | .instructions = .{}, |
| ... | ... | @@ -180,6 +181,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In |
| 180 | 181 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), |
| 181 | 182 | }); |
| 182 | 183 | var then_scope: Scope.GenZIR = .{ |
| 184 | .parent = scope, | |
| 183 | 185 | .decl = block_scope.decl, |
| 184 | 186 | .arena = block_scope.arena, |
| 185 | 187 | .instructions = .{}, |
| ... | ... | @@ -199,6 +201,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In |
| 199 | 201 | }; |
| 200 | 202 | |
| 201 | 203 | var else_scope: Scope.GenZIR = .{ |
| 204 | .parent = scope, | |
| 202 | 205 | .decl = block_scope.decl, |
| 203 | 206 | .arena = block_scope.arena, |
| 204 | 207 | .instructions = .{}, |
| ... | ... | @@ -250,7 +253,11 @@ fn controlFlowExpr( |
| 250 | 253 | } |
| 251 | 254 | |
| 252 | 255 | fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst { |
| 256 | const tracy = trace(@src()); | |
| 257 | defer tracy.end(); | |
| 258 | ||
| 253 | 259 | const tree = scope.tree(); |
| 260 | // TODO implement @"aoeu" identifiers | |
| 254 | 261 | const ident_name = tree.tokenSlice(ident.token); |
| 255 | 262 | const src = tree.token_locs[ident.token].start; |
| 256 | 263 | if (mem.eql(u8, ident_name, "_")) { |
| ... | ... | @@ -288,23 +295,27 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr |
| 288 | 295 | } |
| 289 | 296 | } |
| 290 | 297 | |
| 291 | if (mod.lookupDeclName(scope, ident_name)) |decl| { | |
| 292 | return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}); | |
| 298 | // Local variables, including function parameters. | |
| 299 | { | |
| 300 | var s = scope; | |
| 301 | while (true) switch (s.tag) { | |
| 302 | .local_var => { | |
| 303 | const local_var = s.cast(Scope.LocalVar).?; | |
| 304 | if (mem.eql(u8, local_var.name, ident_name)) { | |
| 305 | return local_var.inst; | |
| 306 | } | |
| 307 | s = local_var.parent; | |
| 308 | }, | |
| 309 | .gen_zir => s = s.cast(Scope.GenZIR).?.parent, | |
| 310 | else => break, | |
| 311 | }; | |
| 293 | 312 | } |
| 294 | 313 | |
| 295 | // Function parameter | |
| 296 | if (scope.decl()) |decl| { | |
| 297 | if (tree.root_node.decls()[decl.src_index].cast(ast.Node.FnProto)) |fn_proto| { | |
| 298 | for (fn_proto.params()) |param, i| { | |
| 299 | const param_name = tree.tokenSlice(param.name_token.?); | |
| 300 | if (mem.eql(u8, param_name, ident_name)) { | |
| 301 | return try mod.addZIRInst(scope, src, zir.Inst.Arg, .{ .index = i }, .{}); | |
| 302 | } | |
| 303 | } | |
| 304 | } | |
| 314 | if (mod.lookupDeclName(scope, ident_name)) |decl| { | |
| 315 | return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}); | |
| 305 | 316 | } |
| 306 | 317 | |
| 307 | return mod.failNode(scope, &ident.base, "TODO implement local variable identifier lookup", .{}); | |
| 318 | return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name}); | |
| 308 | 319 | } |
| 309 | 320 | |
| 310 | 321 | fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst { |
| ... | ... | @@ -434,7 +445,7 @@ fn callExpr(mod: *Module, scope: *Scope, node: *ast.Node.Call) InnerError!*zir.I |
| 434 | 445 | const lhs = try expr(mod, scope, node.lhs); |
| 435 | 446 | |
| 436 | 447 | const param_nodes = node.params(); |
| 437 | const args = try scope.cast(Scope.GenZIR).?.arena.alloc(*zir.Inst, param_nodes.len); | |
| 448 | const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len); | |
| 438 | 449 | for (param_nodes) |param_node, i| { |
| 439 | 450 | args[i] = try expr(mod, scope, param_node); |
| 440 | 451 | } |
src-self-hosted/codegen.zig+5-1| ... | ... | @@ -73,6 +73,7 @@ pub fn generateSymbol( |
| 73 | 73 | .code = code, |
| 74 | 74 | .err_msg = null, |
| 75 | 75 | .args = mc_args, |
| 76 | .arg_index = 0, | |
| 76 | 77 | .branch_stack = &branch_stack, |
| 77 | 78 | .src = src, |
| 78 | 79 | }; |
| ... | ... | @@ -255,6 +256,7 @@ const Function = struct { |
| 255 | 256 | code: *std.ArrayList(u8), |
| 256 | 257 | err_msg: ?*ErrorMsg, |
| 257 | 258 | args: []MCValue, |
| 259 | arg_index: usize, | |
| 258 | 260 | src: usize, |
| 259 | 261 | |
| 260 | 262 | /// Whenever there is a runtime branch, we push a Branch onto this stack, |
| ... | ... | @@ -603,7 +605,9 @@ const Function = struct { |
| 603 | 605 | } |
| 604 | 606 | |
| 605 | 607 | fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue { |
| 606 | return self.args[inst.args.index]; | |
| 608 | const i = self.arg_index; | |
| 609 | self.arg_index += 1; | |
| 610 | return self.args[i]; | |
| 607 | 611 | } |
| 608 | 612 | |
| 609 | 613 | fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue { |
src-self-hosted/ir.zig+1-4| ... | ... | @@ -101,10 +101,7 @@ pub const Inst = struct { |
| 101 | 101 | pub const Arg = struct { |
| 102 | 102 | pub const base_tag = Tag.arg; |
| 103 | 103 | base: Inst, |
| 104 | ||
| 105 | args: struct { | |
| 106 | index: usize, | |
| 107 | }, | |
| 104 | args: void, | |
| 108 | 105 | }; |
| 109 | 106 | |
| 110 | 107 | pub const Assembly = struct { |
src-self-hosted/zir.zig+8-7| ... | ... | @@ -34,7 +34,8 @@ pub const Inst = struct { |
| 34 | 34 | |
| 35 | 35 | /// These names are used directly as the instruction names in the text format. |
| 36 | 36 | pub const Tag = enum { |
| 37 | /// Function parameter value. | |
| 37 | /// Function parameter value. These must be first in a function's main block, | |
| 38 | /// in respective order with the parameters. | |
| 38 | 39 | arg, |
| 39 | 40 | /// A labeled block of code, which can return a value. |
| 40 | 41 | block, |
| ... | ... | @@ -184,9 +185,7 @@ pub const Inst = struct { |
| 184 | 185 | pub const base_tag = Tag.arg; |
| 185 | 186 | base: Inst, |
| 186 | 187 | |
| 187 | positionals: struct { | |
| 188 | index: usize, | |
| 189 | }, | |
| 188 | positionals: struct {}, | |
| 190 | 189 | kw_args: struct {}, |
| 191 | 190 | }; |
| 192 | 191 | |
| ... | ... | @@ -1384,15 +1383,17 @@ const EmitZIR = struct { |
| 1384 | 1383 | for (src_decls.items) |ir_decl| { |
| 1385 | 1384 | switch (ir_decl.analysis) { |
| 1386 | 1385 | .unreferenced => continue, |
| 1386 | ||
| 1387 | 1387 | .complete => {}, |
| 1388 | .codegen_failure => {}, // We still can emit the ZIR. | |
| 1389 | .codegen_failure_retryable => {}, // We still can emit the ZIR. | |
| 1390 | ||
| 1388 | 1391 | .in_progress => unreachable, |
| 1389 | 1392 | .outdated => unreachable, |
| 1390 | 1393 | |
| 1391 | 1394 | .sema_failure, |
| 1392 | 1395 | .sema_failure_retryable, |
| 1393 | .codegen_failure, | |
| 1394 | 1396 | .dependency_failure, |
| 1395 | .codegen_failure_retryable, | |
| 1396 | 1397 | => if (self.old_module.failed_decls.get(ir_decl)) |err_msg| { |
| 1397 | 1398 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); |
| 1398 | 1399 | fail_inst.* = .{ |
| ... | ... | @@ -1728,7 +1729,7 @@ const EmitZIR = struct { |
| 1728 | 1729 | .src = inst.src, |
| 1729 | 1730 | .tag = Inst.Arg.base_tag, |
| 1730 | 1731 | }, |
| 1731 | .positionals = .{ .index = old_inst.args.index }, | |
| 1732 | .positionals = .{}, | |
| 1732 | 1733 | .kw_args = .{}, |
| 1733 | 1734 | }; |
| 1734 | 1735 | break :blk &new_inst.base; |