| author | |
| committer | |
| log | 8e425c0c8d78acc64a4223a35010df478d5b7e16 |
| tree | b08ffa0b517c132392e78e6dfb97c5424cac5697 |
| parent | be0546d877e0df18201e73e803d8a966b57625c5 |
5 files changed, 317 insertions(+), 128 deletions(-)
lib/std/zig/ast.zig+2| ... | ... | @@ -959,6 +959,8 @@ pub const Node = struct { |
| 959 | 959 | }; |
| 960 | 960 | |
| 961 | 961 | /// The params are directly after the FnProto in memory. |
| 962 | /// TODO have a flags field for the optional nodes, and have them appended | |
| 963 | /// before or after the parameters in memory. | |
| 962 | 964 | pub const FnProto = struct { |
| 963 | 965 | base: Node = Node{ .id = .FnProto }, |
| 964 | 966 | doc_comments: ?*DocComment, |
src-self-hosted/Module.zig+145-41| ... | ... | @@ -303,14 +303,14 @@ pub const Scope = struct { |
| 303 | 303 | switch (self.tag) { |
| 304 | 304 | .block => return self.cast(Block).?.arena, |
| 305 | 305 | .decl => return &self.cast(DeclAnalysis).?.arena.allocator, |
| 306 | .gen_zir => return &self.cast(GenZIR).?.arena.allocator, | |
| 306 | .gen_zir => return self.cast(GenZIR).?.arena, | |
| 307 | 307 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, |
| 308 | 308 | .file => unreachable, |
| 309 | 309 | } |
| 310 | 310 | } |
| 311 | 311 | |
| 312 | /// Asserts the scope has a parent which is a DeclAnalysis and | |
| 313 | /// returns the Decl. | |
| 312 | /// If the scope has a parent which is a `DeclAnalysis`, | |
| 313 | /// returns the `Decl`, otherwise returns `null`. | |
| 314 | 314 | pub fn decl(self: *Scope) ?*Decl { |
| 315 | 315 | return switch (self.tag) { |
| 316 | 316 | .block => self.cast(Block).?.decl, |
| ... | ... | @@ -653,7 +653,7 @@ pub const Scope = struct { |
| 653 | 653 | label: ?Label = null, |
| 654 | 654 | |
| 655 | 655 | pub const Label = struct { |
| 656 | name: []const u8, | |
| 656 | zir_block: *zir.Inst.Block, | |
| 657 | 657 | results: ArrayListUnmanaged(*Inst), |
| 658 | 658 | block_inst: *Inst.Block, |
| 659 | 659 | }; |
| ... | ... | @@ -674,8 +674,8 @@ pub const Scope = struct { |
| 674 | 674 | pub const base_tag: Tag = .gen_zir; |
| 675 | 675 | base: Scope = Scope{ .tag = base_tag }, |
| 676 | 676 | decl: *Decl, |
| 677 | arena: std.heap.ArenaAllocator, | |
| 678 | instructions: std.ArrayList(*zir.Inst), | |
| 677 | arena: *Allocator, | |
| 678 | instructions: std.ArrayListUnmanaged(*zir.Inst) = .{}, | |
| 679 | 679 | }; |
| 680 | 680 | }; |
| 681 | 681 | |
| ... | ... | @@ -1115,19 +1115,19 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1115 | 1115 | // This arena allocator's memory is discarded at the end of this function. It is used |
| 1116 | 1116 | // to determine the type of the function, and hence the type of the decl, which is needed |
| 1117 | 1117 | // to complete the Decl analysis. |
| 1118 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | |
| 1119 | defer fn_type_scope_arena.deinit(); | |
| 1118 | 1120 | var fn_type_scope: Scope.GenZIR = .{ |
| 1119 | 1121 | .decl = decl, |
| 1120 | .arena = std.heap.ArenaAllocator.init(self.gpa), | |
| 1121 | .instructions = std.ArrayList(*zir.Inst).init(self.gpa), | |
| 1122 | .arena = &fn_type_scope_arena.allocator, | |
| 1122 | 1123 | }; |
| 1123 | defer fn_type_scope.arena.deinit(); | |
| 1124 | defer fn_type_scope.instructions.deinit(); | |
| 1124 | defer fn_type_scope.instructions.deinit(self.gpa); | |
| 1125 | 1125 | |
| 1126 | 1126 | const body_node = fn_proto.body_node orelse |
| 1127 | 1127 | return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{}); |
| 1128 | 1128 | |
| 1129 | 1129 | const param_decls = fn_proto.params(); |
| 1130 | const param_types = try fn_type_scope.arena.allocator.alloc(*zir.Inst, param_decls.len); | |
| 1130 | const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len); | |
| 1131 | 1131 | for (param_decls) |param_decl, i| { |
| 1132 | 1132 | const param_type_node = switch (param_decl.param_type) { |
| 1133 | 1133 | .var_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}), |
| ... | ... | @@ -1190,24 +1190,24 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1190 | 1190 | const fn_zir = blk: { |
| 1191 | 1191 | // This scope's arena memory is discarded after the ZIR generation |
| 1192 | 1192 | // pass completes, and semantic analysis of it completes. |
| 1193 | var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | |
| 1194 | errdefer gen_scope_arena.deinit(); | |
| 1193 | 1195 | var gen_scope: Scope.GenZIR = .{ |
| 1194 | 1196 | .decl = decl, |
| 1195 | .arena = std.heap.ArenaAllocator.init(self.gpa), | |
| 1196 | .instructions = std.ArrayList(*zir.Inst).init(self.gpa), | |
| 1197 | .arena = &gen_scope_arena.allocator, | |
| 1197 | 1198 | }; |
| 1198 | errdefer gen_scope.arena.deinit(); | |
| 1199 | defer gen_scope.instructions.deinit(); | |
| 1199 | defer gen_scope.instructions.deinit(self.gpa); | |
| 1200 | 1200 | |
| 1201 | 1201 | const body_block = body_node.cast(ast.Node.Block).?; |
| 1202 | 1202 | |
| 1203 | 1203 | try self.astGenBlock(&gen_scope.base, body_block); |
| 1204 | 1204 | |
| 1205 | const fn_zir = try gen_scope.arena.allocator.create(Fn.ZIR); | |
| 1205 | const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR); | |
| 1206 | 1206 | fn_zir.* = .{ |
| 1207 | 1207 | .body = .{ |
| 1208 | .instructions = try gen_scope.arena.allocator.dupe(*zir.Inst, gen_scope.instructions.items), | |
| 1208 | .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), | |
| 1209 | 1209 | }, |
| 1210 | .arena = gen_scope.arena.state, | |
| 1210 | .arena = gen_scope_arena.state, | |
| 1211 | 1211 | }; |
| 1212 | 1212 | break :blk fn_zir; |
| 1213 | 1213 | }; |
| ... | ... | @@ -1351,9 +1351,70 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir |
| 1351 | 1351 | return self.failNode(scope, payload, "TODO implement astGenIf for error unions", .{}); |
| 1352 | 1352 | } |
| 1353 | 1353 | } |
| 1354 | const cond = try self.astGenExpr(scope, if_node.condition); | |
| 1355 | const body = try self.astGenExpr(scope, if_node.condition); | |
| 1356 | return self.failNode(scope, if_node.condition, "TODO implement astGenIf", .{}); | |
| 1354 | var block_scope: Scope.GenZIR = .{ | |
| 1355 | .decl = scope.decl().?, | |
| 1356 | .arena = scope.arena(), | |
| 1357 | .instructions = .{}, | |
| 1358 | }; | |
| 1359 | defer block_scope.instructions.deinit(self.gpa); | |
| 1360 | ||
| 1361 | const cond = try self.astGenExpr(&block_scope.base, if_node.condition); | |
| 1362 | ||
| 1363 | const tree = scope.tree(); | |
| 1364 | const if_src = tree.token_locs[if_node.if_token].start; | |
| 1365 | const condbr = try self.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{ | |
| 1366 | .condition = cond, | |
| 1367 | .true_body = undefined, // populated below | |
| 1368 | .false_body = undefined, // populated below | |
| 1369 | }, .{}); | |
| 1370 | ||
| 1371 | const block = try self.addZIRInstBlock(scope, if_src, .{ | |
| 1372 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), | |
| 1373 | }); | |
| 1374 | var then_scope: Scope.GenZIR = .{ | |
| 1375 | .decl = block_scope.decl, | |
| 1376 | .arena = block_scope.arena, | |
| 1377 | .instructions = .{}, | |
| 1378 | }; | |
| 1379 | defer then_scope.instructions.deinit(self.gpa); | |
| 1380 | ||
| 1381 | const then_result = try self.astGenExpr(&then_scope.base, if_node.body); | |
| 1382 | const then_src = tree.token_locs[if_node.body.lastToken()].start; | |
| 1383 | _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{ | |
| 1384 | .block = block, | |
| 1385 | .operand = then_result, | |
| 1386 | }, .{}); | |
| 1387 | condbr.positionals.true_body = .{ | |
| 1388 | .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items), | |
| 1389 | }; | |
| 1390 | ||
| 1391 | var else_scope: Scope.GenZIR = .{ | |
| 1392 | .decl = block_scope.decl, | |
| 1393 | .arena = block_scope.arena, | |
| 1394 | .instructions = .{}, | |
| 1395 | }; | |
| 1396 | defer else_scope.instructions.deinit(self.gpa); | |
| 1397 | ||
| 1398 | if (if_node.@"else") |else_node| { | |
| 1399 | const else_result = try self.astGenExpr(&else_scope.base, else_node.body); | |
| 1400 | const else_src = tree.token_locs[else_node.body.lastToken()].start; | |
| 1401 | _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{ | |
| 1402 | .block = block, | |
| 1403 | .operand = else_result, | |
| 1404 | }, .{}); | |
| 1405 | } else { | |
| 1406 | // TODO Optimization opportunity: we can avoid an allocation and a memcpy here | |
| 1407 | // by directly allocating the body for this one instruction. | |
| 1408 | const else_src = tree.token_locs[if_node.lastToken()].start; | |
| 1409 | _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.BreakVoid, .{ | |
| 1410 | .block = block, | |
| 1411 | }, .{}); | |
| 1412 | } | |
| 1413 | condbr.positionals.false_body = .{ | |
| 1414 | .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), | |
| 1415 | }; | |
| 1416 | ||
| 1417 | return &block.base; | |
| 1357 | 1418 | } |
| 1358 | 1419 | |
| 1359 | 1420 | fn astGenControlFlowExpression( |
| ... | ... | @@ -1379,12 +1440,12 @@ fn astGenControlFlowExpression( |
| 1379 | 1440 | fn astGenIdent(self: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst { |
| 1380 | 1441 | const tree = scope.tree(); |
| 1381 | 1442 | const ident_name = tree.tokenSlice(ident.token); |
| 1443 | const src = tree.token_locs[ident.token].start; | |
| 1382 | 1444 | if (mem.eql(u8, ident_name, "_")) { |
| 1383 | 1445 | return self.failNode(scope, &ident.base, "TODO implement '_' identifier", .{}); |
| 1384 | 1446 | } |
| 1385 | 1447 | |
| 1386 | 1448 | if (getSimplePrimitiveValue(ident_name)) |typed_value| { |
| 1387 | const src = tree.token_locs[ident.token].start; | |
| 1388 | 1449 | return self.addZIRInstConst(scope, src, typed_value); |
| 1389 | 1450 | } |
| 1390 | 1451 | |
| ... | ... | @@ -1408,7 +1469,6 @@ fn astGenIdent(self: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerE |
| 1408 | 1469 | 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type), |
| 1409 | 1470 | else => return self.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}), |
| 1410 | 1471 | }; |
| 1411 | const src = tree.token_locs[ident.token].start; | |
| 1412 | 1472 | return self.addZIRInstConst(scope, src, .{ |
| 1413 | 1473 | .ty = Type.initTag(.type), |
| 1414 | 1474 | .val = val, |
| ... | ... | @@ -1417,10 +1477,21 @@ fn astGenIdent(self: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerE |
| 1417 | 1477 | } |
| 1418 | 1478 | |
| 1419 | 1479 | if (self.lookupDeclName(scope, ident_name)) |decl| { |
| 1420 | const src = tree.token_locs[ident.token].start; | |
| 1421 | 1480 | return try self.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}); |
| 1422 | 1481 | } |
| 1423 | 1482 | |
| 1483 | // Function parameter | |
| 1484 | if (scope.decl()) |decl| { | |
| 1485 | if (tree.root_node.decls()[decl.src_index].cast(ast.Node.FnProto)) |fn_proto| { | |
| 1486 | for (fn_proto.params()) |param, i| { | |
| 1487 | const param_name = tree.tokenSlice(param.name_token.?); | |
| 1488 | if (mem.eql(u8, param_name, ident_name)) { | |
| 1489 | return try self.addZIRInst(scope, src, zir.Inst.Arg, .{ .index = i }, .{}); | |
| 1490 | } | |
| 1491 | } | |
| 1492 | } | |
| 1493 | } | |
| 1494 | ||
| 1424 | 1495 | return self.failNode(scope, &ident.base, "TODO implement local variable identifier lookup", .{}); |
| 1425 | 1496 | } |
| 1426 | 1497 | |
| ... | ... | @@ -1563,7 +1634,7 @@ fn astGenCall(self: *Module, scope: *Scope, call: *ast.Node.Call) InnerError!*zi |
| 1563 | 1634 | const lhs = try self.astGenExpr(scope, call.lhs); |
| 1564 | 1635 | |
| 1565 | 1636 | const param_nodes = call.params(); |
| 1566 | const args = try scope.cast(Scope.GenZIR).?.arena.allocator.alloc(*zir.Inst, param_nodes.len); | |
| 1637 | const args = try scope.cast(Scope.GenZIR).?.arena.alloc(*zir.Inst, param_nodes.len); | |
| 1567 | 1638 | for (param_nodes) |param_node, i| { |
| 1568 | 1639 | args[i] = try self.astGenExpr(scope, param_node); |
| 1569 | 1640 | } |
| ... | ... | @@ -2239,7 +2310,7 @@ fn newZIRInst( |
| 2239 | 2310 | comptime T: type, |
| 2240 | 2311 | positionals: std.meta.fieldInfo(T, "positionals").field_type, |
| 2241 | 2312 | kw_args: std.meta.fieldInfo(T, "kw_args").field_type, |
| 2242 | ) !*zir.Inst { | |
| 2313 | ) !*T { | |
| 2243 | 2314 | const inst = try gpa.create(T); |
| 2244 | 2315 | inst.* = .{ |
| 2245 | 2316 | .base = .{ |
| ... | ... | @@ -2249,30 +2320,48 @@ fn newZIRInst( |
| 2249 | 2320 | .positionals = positionals, |
| 2250 | 2321 | .kw_args = kw_args, |
| 2251 | 2322 | }; |
| 2252 | return &inst.base; | |
| 2323 | return inst; | |
| 2253 | 2324 | } |
| 2254 | 2325 | |
| 2255 | fn addZIRInst( | |
| 2326 | fn addZIRInstSpecial( | |
| 2256 | 2327 | self: *Module, |
| 2257 | 2328 | scope: *Scope, |
| 2258 | 2329 | src: usize, |
| 2259 | 2330 | comptime T: type, |
| 2260 | 2331 | positionals: std.meta.fieldInfo(T, "positionals").field_type, |
| 2261 | 2332 | kw_args: std.meta.fieldInfo(T, "kw_args").field_type, |
| 2262 | ) !*zir.Inst { | |
| 2333 | ) !*T { | |
| 2263 | 2334 | const gen_zir = scope.cast(Scope.GenZIR).?; |
| 2264 | try gen_zir.instructions.ensureCapacity(gen_zir.instructions.items.len + 1); | |
| 2265 | const inst = try newZIRInst(&gen_zir.arena.allocator, src, T, positionals, kw_args); | |
| 2266 | gen_zir.instructions.appendAssumeCapacity(inst); | |
| 2335 | try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1); | |
| 2336 | const inst = try newZIRInst(gen_zir.arena, src, T, positionals, kw_args); | |
| 2337 | gen_zir.instructions.appendAssumeCapacity(&inst.base); | |
| 2267 | 2338 | return inst; |
| 2268 | 2339 | } |
| 2269 | 2340 | |
| 2341 | fn addZIRInst( | |
| 2342 | self: *Module, | |
| 2343 | scope: *Scope, | |
| 2344 | src: usize, | |
| 2345 | comptime T: type, | |
| 2346 | positionals: std.meta.fieldInfo(T, "positionals").field_type, | |
| 2347 | kw_args: std.meta.fieldInfo(T, "kw_args").field_type, | |
| 2348 | ) !*zir.Inst { | |
| 2349 | const inst_special = try self.addZIRInstSpecial(scope, src, T, positionals, kw_args); | |
| 2350 | return &inst_special.base; | |
| 2351 | } | |
| 2352 | ||
| 2270 | 2353 | /// TODO The existence of this function is a workaround for a bug in stage1. |
| 2271 | 2354 | fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst { |
| 2272 | 2355 | const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type; |
| 2273 | 2356 | return self.addZIRInst(scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{}); |
| 2274 | 2357 | } |
| 2275 | 2358 | |
| 2359 | /// TODO The existence of this function is a workaround for a bug in stage1. | |
| 2360 | fn addZIRInstBlock(self: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block { | |
| 2361 | const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type; | |
| 2362 | return self.addZIRInstSpecial(scope, src, zir.Inst.Block, P{ .body = body }, .{}); | |
| 2363 | } | |
| 2364 | ||
| 2276 | 2365 | fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T { |
| 2277 | 2366 | const inst = try block.arena.create(T); |
| 2278 | 2367 | inst.* = .{ |
| ... | ... | @@ -2403,6 +2492,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In |
| 2403 | 2492 | switch (old_inst.tag) { |
| 2404 | 2493 | .arg => return self.analyzeInstArg(scope, old_inst.cast(zir.Inst.Arg).?), |
| 2405 | 2494 | .block => return self.analyzeInstBlock(scope, old_inst.cast(zir.Inst.Block).?), |
| 2495 | .@"break" => return self.analyzeInstBreak(scope, old_inst.cast(zir.Inst.Break).?), | |
| 2406 | 2496 | .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?), |
| 2407 | 2497 | .breakvoid => return self.analyzeInstBreakVoid(scope, old_inst.cast(zir.Inst.BreakVoid).?), |
| 2408 | 2498 | .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?), |
| ... | ... | @@ -2559,7 +2649,7 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr |
| 2559 | 2649 | .arena = parent_block.arena, |
| 2560 | 2650 | // TODO @as here is working around a miscompilation compiler bug :( |
| 2561 | 2651 | .label = @as(?Scope.Block.Label, Scope.Block.Label{ |
| 2562 | .name = inst.positionals.label, | |
| 2652 | .zir_block = inst, | |
| 2563 | 2653 | .results = .{}, |
| 2564 | 2654 | .block_inst = block_inst, |
| 2565 | 2655 | }), |
| ... | ... | @@ -2588,25 +2678,39 @@ fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoin |
| 2588 | 2678 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, {}); |
| 2589 | 2679 | } |
| 2590 | 2680 | |
| 2681 | fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst { | |
| 2682 | const operand = try self.resolveInst(scope, inst.positionals.operand); | |
| 2683 | const block = inst.positionals.block; | |
| 2684 | return self.analyzeBreak(scope, inst.base.src, block, operand); | |
| 2685 | } | |
| 2686 | ||
| 2591 | 2687 | fn analyzeInstBreakVoid(self: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst { |
| 2592 | const label_name = inst.positionals.label; | |
| 2688 | const block = inst.positionals.block; | |
| 2593 | 2689 | const void_inst = try self.constVoid(scope, inst.base.src); |
| 2690 | return self.analyzeBreak(scope, inst.base.src, block, void_inst); | |
| 2691 | } | |
| 2594 | 2692 | |
| 2693 | fn analyzeBreak( | |
| 2694 | self: *Module, | |
| 2695 | scope: *Scope, | |
| 2696 | src: usize, | |
| 2697 | zir_block: *zir.Inst.Block, | |
| 2698 | operand: *Inst, | |
| 2699 | ) InnerError!*Inst { | |
| 2595 | 2700 | var opt_block = scope.cast(Scope.Block); |
| 2596 | 2701 | while (opt_block) |block| { |
| 2597 | 2702 | if (block.label) |*label| { |
| 2598 | if (mem.eql(u8, label.name, label_name)) { | |
| 2599 | try label.results.append(self.gpa, void_inst); | |
| 2600 | const b = try self.requireRuntimeBlock(scope, inst.base.src); | |
| 2601 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.BreakVoid, .{ | |
| 2703 | if (label.zir_block == zir_block) { | |
| 2704 | try label.results.append(self.gpa, operand); | |
| 2705 | const b = try self.requireRuntimeBlock(scope, src); | |
| 2706 | return self.addNewInstArgs(b, src, Type.initTag(.noreturn), Inst.Br, .{ | |
| 2602 | 2707 | .block = label.block_inst, |
| 2708 | .operand = operand, | |
| 2603 | 2709 | }); |
| 2604 | 2710 | } |
| 2605 | 2711 | } |
| 2606 | 2712 | opt_block = block.parent; |
| 2607 | } else { | |
| 2608 | return self.fail(scope, inst.base.src, "use of undeclared label '{}'", .{label_name}); | |
| 2609 | } | |
| 2713 | } else unreachable; | |
| 2610 | 2714 | } |
| 2611 | 2715 | |
| 2612 | 2716 | fn analyzeInstDeclRefStr(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst { |
src-self-hosted/codegen.zig+10-3| ... | ... | @@ -418,8 +418,9 @@ const Function = struct { |
| 418 | 418 | .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch), |
| 419 | 419 | .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?), |
| 420 | 420 | .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch), |
| 421 | .br => return self.genBr(inst.cast(ir.Inst.Br).?, arch), | |
| 421 | 422 | .breakpoint => return self.genBreakpoint(inst.src, arch), |
| 422 | .breakvoid => return self.genBreakVoid(inst.cast(ir.Inst.BreakVoid).?, arch), | |
| 423 | .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?, arch), | |
| 423 | 424 | .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch), |
| 424 | 425 | .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch), |
| 425 | 426 | .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch), |
| ... | ... | @@ -767,7 +768,13 @@ const Function = struct { |
| 767 | 768 | } |
| 768 | 769 | } |
| 769 | 770 | |
| 770 | fn genBreakVoid(self: *Function, inst: *ir.Inst.BreakVoid, comptime arch: std.Target.Cpu.Arch) !MCValue { | |
| 771 | fn genBr(self: *Function, inst: *ir.Inst.Br, comptime arch: std.Target.Cpu.Arch) !MCValue { | |
| 772 | switch (arch) { | |
| 773 | else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}), | |
| 774 | } | |
| 775 | } | |
| 776 | ||
| 777 | fn genBrVoid(self: *Function, inst: *ir.Inst.BrVoid, comptime arch: std.Target.Cpu.Arch) !MCValue { | |
| 771 | 778 | // Emit a jump with a relocation. It will be patched up after the block ends. |
| 772 | 779 | try inst.args.block.codegen.relocs.ensureCapacity(self.gpa, inst.args.block.codegen.relocs.items.len + 1); |
| 773 | 780 | |
| ... | ... | @@ -780,7 +787,7 @@ const Function = struct { |
| 780 | 787 | // Leave the jump offset undefined |
| 781 | 788 | inst.args.block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 }); |
| 782 | 789 | }, |
| 783 | else => return self.fail(inst.base.src, "TODO implement breakvoid for {}", .{self.target.cpu.arch}), | |
| 790 | else => return self.fail(inst.base.src, "TODO implement brvoid for {}", .{self.target.cpu.arch}), | |
| 784 | 791 | } |
| 785 | 792 | return .none; |
| 786 | 793 | } |
src-self-hosted/ir.zig+15-4| ... | ... | @@ -46,8 +46,9 @@ pub const Inst = struct { |
| 46 | 46 | assembly, |
| 47 | 47 | bitcast, |
| 48 | 48 | block, |
| 49 | br, | |
| 49 | 50 | breakpoint, |
| 50 | breakvoid, | |
| 51 | brvoid, | |
| 51 | 52 | call, |
| 52 | 53 | cmp, |
| 53 | 54 | condbr, |
| ... | ... | @@ -80,7 +81,8 @@ pub const Inst = struct { |
| 80 | 81 | .sub, |
| 81 | 82 | => false, |
| 82 | 83 | |
| 83 | .breakvoid, | |
| 84 | .br, | |
| 85 | .brvoid, | |
| 84 | 86 | .condbr, |
| 85 | 87 | .ret, |
| 86 | 88 | .retvoid, |
| ... | ... | @@ -162,14 +164,23 @@ pub const Inst = struct { |
| 162 | 164 | codegen: codegen.BlockData = .{}, |
| 163 | 165 | }; |
| 164 | 166 | |
| 167 | pub const Br = struct { | |
| 168 | pub const base_tag = Tag.br; | |
| 169 | base: Inst, | |
| 170 | args: struct { | |
| 171 | block: *Block, | |
| 172 | operand: *Inst, | |
| 173 | }, | |
| 174 | }; | |
| 175 | ||
| 165 | 176 | pub const Breakpoint = struct { |
| 166 | 177 | pub const base_tag = Tag.breakpoint; |
| 167 | 178 | base: Inst, |
| 168 | 179 | args: void, |
| 169 | 180 | }; |
| 170 | 181 | |
| 171 | pub const BreakVoid = struct { | |
| 172 | pub const base_tag = Tag.breakvoid; | |
| 182 | pub const BrVoid = struct { | |
| 183 | pub const base_tag = Tag.brvoid; | |
| 173 | 184 | base: Inst, |
| 174 | 185 | args: struct { |
| 175 | 186 | block: *Block, |
src-self-hosted/zir.zig+145-80| ... | ... | @@ -38,6 +38,8 @@ pub const Inst = struct { |
| 38 | 38 | arg, |
| 39 | 39 | /// A labeled block of code, which can return a value. |
| 40 | 40 | block, |
| 41 | /// Return a value from a `Block`. | |
| 42 | @"break", | |
| 41 | 43 | breakpoint, |
| 42 | 44 | /// Same as `break` but without an operand; the operand is assumed to be the void value. |
| 43 | 45 | breakvoid, |
| ... | ... | @@ -85,6 +87,7 @@ pub const Inst = struct { |
| 85 | 87 | return switch (tag) { |
| 86 | 88 | .arg => Arg, |
| 87 | 89 | .block => Block, |
| 90 | .@"break" => Break, | |
| 88 | 91 | .breakpoint => Breakpoint, |
| 89 | 92 | .breakvoid => BreakVoid, |
| 90 | 93 | .call => Call, |
| ... | ... | @@ -143,12 +146,22 @@ pub const Inst = struct { |
| 143 | 146 | base: Inst, |
| 144 | 147 | |
| 145 | 148 | positionals: struct { |
| 146 | label: []const u8, | |
| 147 | 149 | body: Module.Body, |
| 148 | 150 | }, |
| 149 | 151 | kw_args: struct {}, |
| 150 | 152 | }; |
| 151 | 153 | |
| 154 | pub const Break = struct { | |
| 155 | pub const base_tag = Tag.@"break"; | |
| 156 | base: Inst, | |
| 157 | ||
| 158 | positionals: struct { | |
| 159 | block: *Block, | |
| 160 | operand: *Inst, | |
| 161 | }, | |
| 162 | kw_args: struct {}, | |
| 163 | }; | |
| 164 | ||
| 152 | 165 | pub const Breakpoint = struct { |
| 153 | 166 | pub const base_tag = Tag.breakpoint; |
| 154 | 167 | base: Inst, |
| ... | ... | @@ -162,7 +175,7 @@ pub const Inst = struct { |
| 162 | 175 | base: Inst, |
| 163 | 176 | |
| 164 | 177 | positionals: struct { |
| 165 | label: []const u8, | |
| 178 | block: *Block, | |
| 166 | 179 | }, |
| 167 | 180 | kw_args: struct {}, |
| 168 | 181 | }; |
| ... | ... | @@ -610,8 +623,6 @@ pub const Module = struct { |
| 610 | 623 | self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {}; |
| 611 | 624 | } |
| 612 | 625 | |
| 613 | const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 }); | |
| 614 | ||
| 615 | 626 | const DeclAndIndex = struct { |
| 616 | 627 | decl: *Decl, |
| 617 | 628 | index: usize, |
| ... | ... | @@ -645,84 +656,100 @@ pub const Module = struct { |
| 645 | 656 | /// The allocator is used for temporary storage, but this function always returns |
| 646 | 657 | /// with no resources allocated. |
| 647 | 658 | pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void { |
| 648 | // First, build a map of *Inst to @ or % indexes | |
| 649 | var inst_table = InstPtrTable.init(allocator); | |
| 650 | defer inst_table.deinit(); | |
| 659 | var write = Writer{ | |
| 660 | .module = &self, | |
| 661 | .inst_table = InstPtrTable.init(allocator), | |
| 662 | .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator), | |
| 663 | .arena = std.heap.ArenaAllocator.init(allocator), | |
| 664 | .indent = 2, | |
| 665 | }; | |
| 666 | defer write.arena.deinit(); | |
| 667 | defer write.inst_table.deinit(); | |
| 668 | defer write.block_table.deinit(); | |
| 651 | 669 | |
| 652 | try inst_table.ensureCapacity(self.decls.len); | |
| 670 | // First, build a map of *Inst to @ or % indexes | |
| 671 | try write.inst_table.ensureCapacity(self.decls.len); | |
| 653 | 672 | |
| 654 | 673 | for (self.decls) |decl, decl_i| { |
| 655 | try inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name }); | |
| 674 | try write.inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name }); | |
| 656 | 675 | |
| 657 | 676 | if (decl.inst.cast(Inst.Fn)) |fn_inst| { |
| 658 | 677 | for (fn_inst.positionals.body.instructions) |inst, inst_i| { |
| 659 | try inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i, .name = undefined }); | |
| 678 | try write.inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i, .name = undefined }); | |
| 660 | 679 | } |
| 661 | 680 | } |
| 662 | 681 | } |
| 663 | 682 | |
| 664 | 683 | for (self.decls) |decl, i| { |
| 665 | 684 | try stream.print("@{} ", .{decl.name}); |
| 666 | try self.writeInstToStream(stream, decl.inst, &inst_table, 2); | |
| 685 | try write.writeInstToStream(stream, decl.inst); | |
| 667 | 686 | try stream.writeByte('\n'); |
| 668 | 687 | } |
| 669 | 688 | } |
| 670 | 689 | |
| 690 | }; | |
| 691 | ||
| 692 | const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 }); | |
| 693 | ||
| 694 | const Writer = struct { | |
| 695 | module: *const Module, | |
| 696 | inst_table: InstPtrTable, | |
| 697 | block_table: std.AutoHashMap(*Inst.Block, []const u8), | |
| 698 | arena: std.heap.ArenaAllocator, | |
| 699 | indent: usize, | |
| 700 | ||
| 671 | 701 | fn writeInstToStream( |
| 672 | self: Module, | |
| 702 | self: *Writer, | |
| 673 | 703 | stream: var, |
| 674 | 704 | inst: *Inst, |
| 675 | inst_table: *const InstPtrTable, | |
| 676 | indent: usize, | |
| 677 | ) @TypeOf(stream).Error!void { | |
| 705 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 678 | 706 | // TODO I tried implementing this with an inline for loop and hit a compiler bug |
| 679 | 707 | switch (inst.tag) { |
| 680 | .arg => return self.writeInstToStreamGeneric(stream, .arg, inst, inst_table, indent), | |
| 681 | .block => return self.writeInstToStreamGeneric(stream, .block, inst, inst_table, indent), | |
| 682 | .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, inst, inst_table, indent), | |
| 683 | .breakvoid => return self.writeInstToStreamGeneric(stream, .breakvoid, inst, inst_table, indent), | |
| 684 | .call => return self.writeInstToStreamGeneric(stream, .call, inst, inst_table, indent), | |
| 685 | .declref => return self.writeInstToStreamGeneric(stream, .declref, inst, inst_table, indent), | |
| 686 | .declref_str => return self.writeInstToStreamGeneric(stream, .declref_str, inst, inst_table, indent), | |
| 687 | .declval => return self.writeInstToStreamGeneric(stream, .declval, inst, inst_table, indent), | |
| 688 | .declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, inst, inst_table, indent), | |
| 689 | .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, inst, inst_table, indent), | |
| 690 | .@"const" => return self.writeInstToStreamGeneric(stream, .@"const", inst, inst_table, indent), | |
| 691 | .str => return self.writeInstToStreamGeneric(stream, .str, inst, inst_table, indent), | |
| 692 | .int => return self.writeInstToStreamGeneric(stream, .int, inst, inst_table, indent), | |
| 693 | .inttype => return self.writeInstToStreamGeneric(stream, .inttype, inst, inst_table, indent), | |
| 694 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, inst, inst_table, indent), | |
| 695 | .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, inst, inst_table, indent), | |
| 696 | .deref => return self.writeInstToStreamGeneric(stream, .deref, inst, inst_table, indent), | |
| 697 | .as => return self.writeInstToStreamGeneric(stream, .as, inst, inst_table, indent), | |
| 698 | .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", inst, inst_table, indent), | |
| 699 | .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", inst, inst_table, indent), | |
| 700 | .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", inst, inst_table, indent), | |
| 701 | .returnvoid => return self.writeInstToStreamGeneric(stream, .returnvoid, inst, inst_table, indent), | |
| 702 | .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", inst, inst_table, indent), | |
| 703 | .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", inst, inst_table, indent), | |
| 704 | .primitive => return self.writeInstToStreamGeneric(stream, .primitive, inst, inst_table, indent), | |
| 705 | .fntype => return self.writeInstToStreamGeneric(stream, .fntype, inst, inst_table, indent), | |
| 706 | .intcast => return self.writeInstToStreamGeneric(stream, .intcast, inst, inst_table, indent), | |
| 707 | .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, inst, inst_table, indent), | |
| 708 | .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, inst, inst_table, indent), | |
| 709 | .add => return self.writeInstToStreamGeneric(stream, .add, inst, inst_table, indent), | |
| 710 | .sub => return self.writeInstToStreamGeneric(stream, .sub, inst, inst_table, indent), | |
| 711 | .cmp => return self.writeInstToStreamGeneric(stream, .cmp, inst, inst_table, indent), | |
| 712 | .condbr => return self.writeInstToStreamGeneric(stream, .condbr, inst, inst_table, indent), | |
| 713 | .isnull => return self.writeInstToStreamGeneric(stream, .isnull, inst, inst_table, indent), | |
| 714 | .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, inst, inst_table, indent), | |
| 708 | .arg => return self.writeInstToStreamGeneric(stream, .arg, inst), | |
| 709 | .block => return self.writeInstToStreamGeneric(stream, .block, inst), | |
| 710 | .@"break" => return self.writeInstToStreamGeneric(stream, .@"break", inst), | |
| 711 | .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, inst), | |
| 712 | .breakvoid => return self.writeInstToStreamGeneric(stream, .breakvoid, inst), | |
| 713 | .call => return self.writeInstToStreamGeneric(stream, .call, inst), | |
| 714 | .declref => return self.writeInstToStreamGeneric(stream, .declref, inst), | |
| 715 | .declref_str => return self.writeInstToStreamGeneric(stream, .declref_str, inst), | |
| 716 | .declval => return self.writeInstToStreamGeneric(stream, .declval, inst), | |
| 717 | .declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, inst), | |
| 718 | .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, inst), | |
| 719 | .@"const" => return self.writeInstToStreamGeneric(stream, .@"const", inst), | |
| 720 | .str => return self.writeInstToStreamGeneric(stream, .str, inst), | |
| 721 | .int => return self.writeInstToStreamGeneric(stream, .int, inst), | |
| 722 | .inttype => return self.writeInstToStreamGeneric(stream, .inttype, inst), | |
| 723 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, inst), | |
| 724 | .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, inst), | |
| 725 | .deref => return self.writeInstToStreamGeneric(stream, .deref, inst), | |
| 726 | .as => return self.writeInstToStreamGeneric(stream, .as, inst), | |
| 727 | .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", inst), | |
| 728 | .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", inst), | |
| 729 | .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", inst), | |
| 730 | .returnvoid => return self.writeInstToStreamGeneric(stream, .returnvoid, inst), | |
| 731 | .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", inst), | |
| 732 | .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", inst), | |
| 733 | .primitive => return self.writeInstToStreamGeneric(stream, .primitive, inst), | |
| 734 | .fntype => return self.writeInstToStreamGeneric(stream, .fntype, inst), | |
| 735 | .intcast => return self.writeInstToStreamGeneric(stream, .intcast, inst), | |
| 736 | .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, inst), | |
| 737 | .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, inst), | |
| 738 | .add => return self.writeInstToStreamGeneric(stream, .add, inst), | |
| 739 | .sub => return self.writeInstToStreamGeneric(stream, .sub, inst), | |
| 740 | .cmp => return self.writeInstToStreamGeneric(stream, .cmp, inst), | |
| 741 | .condbr => return self.writeInstToStreamGeneric(stream, .condbr, inst), | |
| 742 | .isnull => return self.writeInstToStreamGeneric(stream, .isnull, inst), | |
| 743 | .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, inst), | |
| 715 | 744 | } |
| 716 | 745 | } |
| 717 | 746 | |
| 718 | 747 | fn writeInstToStreamGeneric( |
| 719 | self: Module, | |
| 748 | self: *Writer, | |
| 720 | 749 | stream: var, |
| 721 | 750 | comptime inst_tag: Inst.Tag, |
| 722 | 751 | base: *Inst, |
| 723 | inst_table: *const InstPtrTable, | |
| 724 | indent: usize, | |
| 725 | ) @TypeOf(stream).Error!void { | |
| 752 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 726 | 753 | const SpecificInst = Inst.TagToType(inst_tag); |
| 727 | 754 | const inst = @fieldParentPtr(SpecificInst, "base", base); |
| 728 | 755 | const Positionals = @TypeOf(inst.positionals); |
| ... | ... | @@ -732,7 +759,7 @@ pub const Module = struct { |
| 732 | 759 | if (i != 0) { |
| 733 | 760 | try stream.writeAll(", "); |
| 734 | 761 | } |
| 735 | try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table, indent); | |
| 762 | try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name)); | |
| 736 | 763 | } |
| 737 | 764 | |
| 738 | 765 | comptime var need_comma = pos_fields.len != 0; |
| ... | ... | @@ -742,13 +769,13 @@ pub const Module = struct { |
| 742 | 769 | if (@field(inst.kw_args, arg_field.name)) |non_optional| { |
| 743 | 770 | if (need_comma) try stream.writeAll(", "); |
| 744 | 771 | try stream.print("{}=", .{arg_field.name}); |
| 745 | try self.writeParamToStream(stream, non_optional, inst_table, indent); | |
| 772 | try self.writeParamToStream(stream, non_optional); | |
| 746 | 773 | need_comma = true; |
| 747 | 774 | } |
| 748 | 775 | } else { |
| 749 | 776 | if (need_comma) try stream.writeAll(", "); |
| 750 | 777 | try stream.print("{}=", .{arg_field.name}); |
| 751 | try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table, indent); | |
| 778 | try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name)); | |
| 752 | 779 | need_comma = true; |
| 753 | 780 | } |
| 754 | 781 | } |
| ... | ... | @@ -756,31 +783,37 @@ pub const Module = struct { |
| 756 | 783 | try stream.writeByte(')'); |
| 757 | 784 | } |
| 758 | 785 | |
| 759 | fn writeParamToStream(self: Module, stream: var, param: var, inst_table: *const InstPtrTable, indent: usize) !void { | |
| 786 | fn writeParamToStream(self: *Writer, stream: var, param: var) !void { | |
| 760 | 787 | if (@typeInfo(@TypeOf(param)) == .Enum) { |
| 761 | 788 | return stream.writeAll(@tagName(param)); |
| 762 | 789 | } |
| 763 | 790 | switch (@TypeOf(param)) { |
| 764 | *Inst => return self.writeInstParamToStream(stream, param, inst_table), | |
| 791 | *Inst => return self.writeInstParamToStream(stream, param), | |
| 765 | 792 | []*Inst => { |
| 766 | 793 | try stream.writeByte('['); |
| 767 | 794 | for (param) |inst, i| { |
| 768 | 795 | if (i != 0) { |
| 769 | 796 | try stream.writeAll(", "); |
| 770 | 797 | } |
| 771 | try self.writeInstParamToStream(stream, inst, inst_table); | |
| 798 | try self.writeInstParamToStream(stream, inst); | |
| 772 | 799 | } |
| 773 | 800 | try stream.writeByte(']'); |
| 774 | 801 | }, |
| 775 | 802 | Module.Body => { |
| 776 | 803 | try stream.writeAll("{\n"); |
| 777 | 804 | for (param.instructions) |inst, i| { |
| 778 | try stream.writeByteNTimes(' ', indent); | |
| 805 | try stream.writeByteNTimes(' ', self.indent); | |
| 779 | 806 | try stream.print("%{} ", .{i}); |
| 780 | try self.writeInstToStream(stream, inst, inst_table, indent + 2); | |
| 807 | if (inst.cast(Inst.Block)) |block| { | |
| 808 | const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{}", .{i}); | |
| 809 | try self.block_table.put(block, name); | |
| 810 | } | |
| 811 | self.indent += 2; | |
| 812 | try self.writeInstToStream(stream, inst); | |
| 813 | self.indent -= 2; | |
| 781 | 814 | try stream.writeByte('\n'); |
| 782 | 815 | } |
| 783 | try stream.writeByteNTimes(' ', indent - 2); | |
| 816 | try stream.writeByteNTimes(' ', self.indent - 2); | |
| 784 | 817 | try stream.writeByte('}'); |
| 785 | 818 | }, |
| 786 | 819 | bool => return stream.writeByte("01"[@boolToInt(param)]), |
| ... | ... | @@ -788,12 +821,16 @@ pub const Module = struct { |
| 788 | 821 | BigIntConst, usize => return stream.print("{}", .{param}), |
| 789 | 822 | TypedValue => unreachable, // this is a special case |
| 790 | 823 | *IrModule.Decl => unreachable, // this is a special case |
| 824 | *Inst.Block => { | |
| 825 | const name = self.block_table.get(param).?; | |
| 826 | return std.zig.renderStringLiteral(name, stream); | |
| 827 | }, | |
| 791 | 828 | else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), |
| 792 | 829 | } |
| 793 | 830 | } |
| 794 | 831 | |
| 795 | fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void { | |
| 796 | if (inst_table.get(inst)) |info| { | |
| 832 | fn writeInstParamToStream(self: *Writer, stream: var, inst: *Inst) !void { | |
| 833 | if (self.inst_table.get(inst)) |info| { | |
| 797 | 834 | if (info.index) |i| { |
| 798 | 835 | try stream.print("%{}", .{info.index}); |
| 799 | 836 | } else { |
| ... | ... | @@ -823,7 +860,9 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module |
| 823 | 860 | .global_name_map = &global_name_map, |
| 824 | 861 | .decls = .{}, |
| 825 | 862 | .unnamed_index = 0, |
| 863 | .block_table = std.StringHashMap(*Inst.Block).init(allocator), | |
| 826 | 864 | }; |
| 865 | defer parser.block_table.deinit(); | |
| 827 | 866 | errdefer parser.arena.deinit(); |
| 828 | 867 | |
| 829 | 868 | parser.parseRoot() catch |err| switch (err) { |
| ... | ... | @@ -849,6 +888,7 @@ const Parser = struct { |
| 849 | 888 | global_name_map: *std.StringHashMap(*Inst), |
| 850 | 889 | error_msg: ?ErrorMsg = null, |
| 851 | 890 | unnamed_index: usize, |
| 891 | block_table: std.StringHashMap(*Inst.Block), | |
| 852 | 892 | |
| 853 | 893 | const Body = struct { |
| 854 | 894 | instructions: std.ArrayList(*Inst), |
| ... | ... | @@ -1057,6 +1097,10 @@ const Parser = struct { |
| 1057 | 1097 | .tag = InstType.base_tag, |
| 1058 | 1098 | }; |
| 1059 | 1099 | |
| 1100 | if (InstType == Inst.Block) { | |
| 1101 | try self.block_table.put(inst_name, inst_specific); | |
| 1102 | } | |
| 1103 | ||
| 1060 | 1104 | if (@hasField(InstType, "ty")) { |
| 1061 | 1105 | inst_specific.ty = opt_type orelse { |
| 1062 | 1106 | return self.fail("instruction '" ++ fn_name ++ "' requires type", .{}); |
| ... | ... | @@ -1162,6 +1206,10 @@ const Parser = struct { |
| 1162 | 1206 | }, |
| 1163 | 1207 | TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}), |
| 1164 | 1208 | *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}), |
| 1209 | *Inst.Block => { | |
| 1210 | const name = try self.parseStringLiteral(); | |
| 1211 | return self.block_table.get(name).?; | |
| 1212 | }, | |
| 1165 | 1213 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), |
| 1166 | 1214 | } |
| 1167 | 1215 | return self.fail("TODO parse parameter {}", .{@typeName(T)}); |
| ... | ... | @@ -1226,7 +1274,9 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module { |
| 1226 | 1274 | .names = std.StringHashMap(void).init(allocator), |
| 1227 | 1275 | .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator), |
| 1228 | 1276 | .indent = 0, |
| 1277 | .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator), | |
| 1229 | 1278 | }; |
| 1279 | defer ctx.block_table.deinit(); | |
| 1230 | 1280 | defer ctx.decls.deinit(allocator); |
| 1231 | 1281 | defer ctx.names.deinit(); |
| 1232 | 1282 | defer ctx.primitive_table.deinit(); |
| ... | ... | @@ -1249,6 +1299,7 @@ const EmitZIR = struct { |
| 1249 | 1299 | next_auto_name: usize, |
| 1250 | 1300 | primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl), |
| 1251 | 1301 | indent: usize, |
| 1302 | block_table: std.AutoHashMap(*ir.Inst.Block, *Inst.Block), | |
| 1252 | 1303 | |
| 1253 | 1304 | fn emit(self: *EmitZIR) !void { |
| 1254 | 1305 | // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced |
| ... | ... | @@ -1611,33 +1662,47 @@ const EmitZIR = struct { |
| 1611 | 1662 | const old_inst = inst.cast(ir.Inst.Block).?; |
| 1612 | 1663 | const new_inst = try self.arena.allocator.create(Inst.Block); |
| 1613 | 1664 | |
| 1614 | // We do this now so that the break instructions within the block | |
| 1615 | // can find it. | |
| 1616 | try inst_table.put(&old_inst.base, &new_inst.base); | |
| 1665 | try self.block_table.put(old_inst, new_inst); | |
| 1666 | ||
| 1667 | var block_body = std.ArrayList(*Inst).init(self.allocator); | |
| 1668 | defer block_body.deinit(); | |
| 1669 | ||
| 1670 | try self.emitBody(old_inst.args.body, inst_table, &block_body); | |
| 1671 | ||
| 1617 | 1672 | new_inst.* = .{ |
| 1618 | 1673 | .base = .{ |
| 1619 | 1674 | .src = inst.src, |
| 1620 | 1675 | .tag = Inst.Block.base_tag, |
| 1621 | 1676 | }, |
| 1622 | 1677 | .positionals = .{ |
| 1623 | .label = try self.autoName(), | |
| 1624 | .body = undefined, | |
| 1678 | .body = .{ .instructions = block_body.toOwnedSlice() }, | |
| 1625 | 1679 | }, |
| 1626 | 1680 | .kw_args = .{}, |
| 1627 | 1681 | }; |
| 1628 | 1682 | |
| 1629 | var block_body = std.ArrayList(*Inst).init(self.allocator); | |
| 1630 | defer block_body.deinit(); | |
| 1631 | ||
| 1632 | try self.emitBody(old_inst.args.body, inst_table, &block_body); | |
| 1633 | new_inst.positionals.body = .{ .instructions = block_body.toOwnedSlice() }; | |
| 1634 | ||
| 1683 | break :blk &new_inst.base; | |
| 1684 | }, | |
| 1685 | .br => blk: { | |
| 1686 | const old_inst = inst.cast(ir.Inst.Br).?; | |
| 1687 | const new_block = self.block_table.get(old_inst.args.block).?; | |
| 1688 | const new_inst = try self.arena.allocator.create(Inst.Break); | |
| 1689 | new_inst.* = .{ | |
| 1690 | .base = .{ | |
| 1691 | .src = inst.src, | |
| 1692 | .tag = Inst.Break.base_tag, | |
| 1693 | }, | |
| 1694 | .positionals = .{ | |
| 1695 | .block = new_block, | |
| 1696 | .operand = try self.resolveInst(new_body, old_inst.args.operand), | |
| 1697 | }, | |
| 1698 | .kw_args = .{}, | |
| 1699 | }; | |
| 1635 | 1700 | break :blk &new_inst.base; |
| 1636 | 1701 | }, |
| 1637 | 1702 | .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint), |
| 1638 | .breakvoid => blk: { | |
| 1639 | const old_inst = inst.cast(ir.Inst.BreakVoid).?; | |
| 1640 | const new_block = inst_table.get(&old_inst.args.block.base).?; | |
| 1703 | .brvoid => blk: { | |
| 1704 | const old_inst = inst.cast(ir.Inst.BrVoid).?; | |
| 1705 | const new_block = self.block_table.get(old_inst.args.block).?; | |
| 1641 | 1706 | const new_inst = try self.arena.allocator.create(Inst.BreakVoid); |
| 1642 | 1707 | new_inst.* = .{ |
| 1643 | 1708 | .base = .{ |
| ... | ... | @@ -1645,7 +1710,7 @@ const EmitZIR = struct { |
| 1645 | 1710 | .tag = Inst.BreakVoid.base_tag, |
| 1646 | 1711 | }, |
| 1647 | 1712 | .positionals = .{ |
| 1648 | .label = new_block.cast(Inst.Block).?.positionals.label, | |
| 1713 | .block = new_block, | |
| 1649 | 1714 | }, |
| 1650 | 1715 | .kw_args = .{}, |
| 1651 | 1716 | }; |