authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-12 15:08:40-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-12 15:12:44-04:00
log687bd92f9c3d9f521c8fe5884627ef1b00320364
tree298cff9330e4c4d4914d0cb4d047805c5b15153d
parentce11d6d16cf388ec7abff9680ee3a263185a9986

self-hosted: generate zig IR for simple function

no tests for this yet. I think the quickest path to testing will be creating the .o files and linking with libc, executing, and then comparing output.

11 files changed, 1555 insertions(+), 315 deletions(-)

src-self-hosted/decl.zig created+96
......@@ -0,0 +1,96 @@
1const std = @import("std");
2const Allocator = mem.Allocator;
3const mem = std.mem;
4const ast = std.zig.ast;
5const Visib = @import("visib.zig").Visib;
6const ParsedFile = @import("parsed_file.zig").ParsedFile;
7const event = std.event;
8const Value = @import("value.zig").Value;
9const Token = std.zig.Token;
10const errmsg = @import("errmsg.zig");
11const Scope = @import("scope.zig").Scope;
12const Module = @import("module.zig").Module;
13
14pub const Decl = struct {
15 id: Id,
16 name: []const u8,
17 visib: Visib,
18 resolution: event.Future(Module.BuildError!void),
19 resolution_in_progress: u8,
20 parsed_file: *ParsedFile,
21 parent_scope: *Scope,
22
23 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
24
25 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
26 switch (base.id) {
27 Id.Fn => {
28 const fn_decl = @fieldParentPtr(Fn, "base", base);
29 return fn_decl.isExported(tree);
30 },
31 else => return false,
32 }
33 }
34
35 pub fn getSpan(base: *const Decl) errmsg.Span {
36 switch (base.id) {
37 Id.Fn => {
38 const fn_decl = @fieldParentPtr(Fn, "base", base);
39 const fn_proto = fn_decl.fn_proto;
40 const start = fn_proto.fn_token;
41 const end = fn_proto.name_token orelse start;
42 return errmsg.Span{
43 .first = start,
44 .last = end + 1,
45 };
46 },
47 else => @panic("TODO"),
48 }
49 }
50
51 pub const Id = enum {
52 Var,
53 Fn,
54 CompTime,
55 };
56
57 pub const Var = struct {
58 base: Decl,
59 };
60
61 pub const Fn = struct {
62 base: Decl,
63 value: Val,
64 fn_proto: *const ast.Node.FnProto,
65
66 // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
67 pub const Val = union {
68 Unresolved: void,
69 Ok: *Value.Fn,
70 };
71
72 pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
73 return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
74 const token = tree.tokens.at(tok_index);
75 break :x switch (token.id) {
76 Token.Id.Extern => tree.tokenSlicePtr(token),
77 else => null,
78 };
79 } else null;
80 }
81
82 pub fn isExported(self: Fn, tree: *ast.Tree) bool {
83 if (self.fn_proto.extern_export_inline_token) |tok_index| {
84 const token = tree.tokens.at(tok_index);
85 return token.id == Token.Id.Keyword_export;
86 } else {
87 return false;
88 }
89 }
90 };
91
92 pub const CompTime = struct {
93 base: Decl,
94 };
95};
96
src-self-hosted/ir.zig+645-100
......@@ -1,111 +1,656 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Module = @import("module.zig").Module;
14const Scope = @import("scope.zig").Scope;
5const ast = std.zig.ast;
6const Allocator = std.mem.Allocator;
7const Value = @import("value.zig").Value;
8const Type = Value.Type;
9const assert = std.debug.assert;
10const Token = std.zig.Token;
11const ParsedFile = @import("parsed_file.zig").ParsedFile;
12
13pub const LVal = enum {
14 None,
15 Ptr,
16};
17
18pub const Mut = enum {
19 Mut,
20 Const,
21};
22
23pub const Volatility = enum {
24 NonVolatile,
25 Volatile,
26};
27
28pub const IrVal = union(enum) {
29 Unknown,
30 Known: *Value,
31
32 pub fn dump(self: IrVal) void {
33 switch (self) {
34 IrVal.Unknown => std.debug.warn("Unknown"),
35 IrVal.Known => |value| {
36 std.debug.warn("Known(");
37 value.dump();
38 std.debug.warn(")");
39 },
40 }
41 }
42};
243
344pub const Instruction = struct {
445 id: Id,
546 scope: *Scope,
47 debug_id: usize,
48 val: IrVal,
49
50 /// true if this instruction was generated by zig and not from user code
51 is_generated: bool,
52
53 pub fn cast(base: *Instruction, comptime T: type) ?*T {
54 if (base.id == comptime typeToId(T)) {
55 return @fieldParentPtr(T, "base", base);
56 }
57 return null;
58 }
59
60 pub fn typeToId(comptime T: type) Id {
61 comptime var i = 0;
62 inline while (i < @memberCount(Id)) : (i += 1) {
63 if (T == @field(Instruction, @memberName(Id, i))) {
64 return @field(Id, @memberName(Id, i));
65 }
66 }
67 unreachable;
68 }
69
70 pub fn dump(base: *const Instruction) void {
71 comptime var i = 0;
72 inline while (i < @memberCount(Id)) : (i += 1) {
73 if (base.id == @field(Id, @memberName(Id, i))) {
74 const T = @field(Instruction, @memberName(Id, i));
75 std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id));
76 @fieldParentPtr(T, "base", base).dump();
77 std.debug.warn(")");
78 return;
79 }
80 }
81 unreachable;
82 }
83
84 pub fn setGenerated(base: *Instruction) void {
85 base.is_generated = true;
86 }
87
88 pub fn isNoReturn(base: *const Instruction) bool {
89 switch (base.val) {
90 IrVal.Unknown => return false,
91 IrVal.Known => |x| return x.typeof.id == Type.Id.NoReturn,
92 }
93 }
694
795 pub const Id = enum {
8 Br,
9 CondBr,
10 SwitchBr,
11 SwitchVar,
12 SwitchTarget,
13 Phi,
14 UnOp,
15 BinOp,
16 DeclVar,
17 LoadPtr,
18 StorePtr,
19 FieldPtr,
20 StructFieldPtr,
21 UnionFieldPtr,
22 ElemPtr,
23 VarPtr,
24 Call,
25 Const,
2696 Return,
27 Cast,
28 ContainerInitList,
29 ContainerInitFields,
30 StructInit,
31 UnionInit,
32 Unreachable,
33 TypeOf,
34 ToPtrType,
35 PtrTypeChild,
36 SetRuntimeSafety,
37 SetFloatMode,
38 ArrayType,
39 SliceType,
40 Asm,
41 SizeOf,
42 TestNonNull,
43 UnwrapMaybe,
44 MaybeWrap,
45 UnionTag,
46 Clz,
47 Ctz,
48 Import,
49 CImport,
50 CInclude,
51 CDefine,
52 CUndef,
53 ArrayLen,
97 Const,
5498 Ref,
55 MinValue,
56 MaxValue,
57 CompileErr,
58 CompileLog,
59 ErrName,
60 EmbedFile,
61 Cmpxchg,
62 Fence,
63 Truncate,
64 IntType,
65 BoolNot,
66 Memset,
67 Memcpy,
68 Slice,
69 MemberCount,
70 MemberType,
71 MemberName,
72 Breakpoint,
73 ReturnAddress,
74 FrameAddress,
75 AlignOf,
76 OverflowOp,
77 TestErr,
78 UnwrapErrCode,
79 UnwrapErrPayload,
80 ErrWrapCode,
81 ErrWrapPayload,
82 FnProto,
83 TestComptime,
84 PtrCast,
85 BitCast,
86 WidenOrShorten,
87 IntToPtr,
88 PtrToInt,
89 IntToEnum,
90 IntToErr,
91 ErrToInt,
92 CheckSwitchProngs,
93 CheckStatementIsVoid,
94 TypeName,
95 CanImplicitCast,
96 DeclRef,
97 Panic,
98 TagName,
99 TagType,
100 FieldParentPtr,
101 OffsetOf,
102 TypeId,
103 SetEvalBranchQuota,
104 PtrTypeOf,
105 AlignCast,
106 OpaqueType,
107 SetAlignStack,
108 ArgType,
109 Export,
99 DeclVar,
100 CheckVoidStmt,
101 Phi,
102 Br,
103 };
104
105 pub const Const = struct {
106 base: Instruction,
107
108 pub fn buildBool(irb: *Builder, scope: *Scope, val: bool) !*Instruction {
109 const inst = try irb.arena().create(Const{
110 .base = Instruction{
111 .id = Instruction.Id.Const,
112 .is_generated = false,
113 .scope = scope,
114 .debug_id = irb.next_debug_id,
115 .val = IrVal{ .Known = &Value.Bool.get(irb.module, val).base },
116 },
117 });
118 irb.next_debug_id += 1;
119 try irb.current_basic_block.instruction_list.append(&inst.base);
120 return &inst.base;
121 }
122
123 pub fn buildVoid(irb: *Builder, scope: *Scope, is_generated: bool) !*Instruction {
124 const inst = try irb.arena().create(Const{
125 .base = Instruction{
126 .id = Instruction.Id.Const,
127 .is_generated = is_generated,
128 .scope = scope,
129 .debug_id = irb.next_debug_id,
130 .val = IrVal{ .Known = &Value.Void.get(irb.module).base },
131 },
132 });
133 irb.next_debug_id += 1;
134 try irb.current_basic_block.instruction_list.append(&inst.base);
135 return &inst.base;
136 }
137
138 pub fn dump(inst: *const Const) void {
139 inst.base.val.Known.dump();
140 }
141 };
142
143 pub const Return = struct {
144 base: Instruction,
145 return_value: *Instruction,
146
147 pub fn build(irb: *Builder, scope: *Scope, return_value: *Instruction) !*Instruction {
148 const inst = try irb.arena().create(Return{
149 .base = Instruction{
150 .id = Instruction.Id.Return,
151 .is_generated = false,
152 .scope = scope,
153 .debug_id = irb.next_debug_id,
154 .val = IrVal{ .Known = &Value.Void.get(irb.module).base },
155 },
156 .return_value = return_value,
157 });
158 irb.next_debug_id += 1;
159 try irb.current_basic_block.instruction_list.append(&inst.base);
160 return &inst.base;
161 }
162
163 pub fn dump(inst: *const Return) void {
164 std.debug.warn("#{}", inst.return_value.debug_id);
165 }
166 };
167
168 pub const Ref = struct {
169 base: Instruction,
170 target: *Instruction,
171 mut: Mut,
172 volatility: Volatility,
173
174 pub fn build(
175 irb: *Builder,
176 scope: *Scope,
177 target: *Instruction,
178 mut: Mut,
179 volatility: Volatility,
180 ) !*Instruction {
181 const inst = try irb.arena().create(Ref{
182 .base = Instruction{
183 .id = Instruction.Id.Ref,
184 .is_generated = false,
185 .scope = scope,
186 .debug_id = irb.next_debug_id,
187 .val = IrVal.Unknown,
188 },
189 .target = target,
190 .mut = mut,
191 .volatility = volatility,
192 });
193 irb.next_debug_id += 1;
194 try irb.current_basic_block.instruction_list.append(&inst.base);
195 return &inst.base;
196 }
197
198 pub fn dump(inst: *const Ref) void {}
199 };
200
201 pub const DeclVar = struct {
202 base: Instruction,
203 variable: *Variable,
204
205 pub fn dump(inst: *const DeclVar) void {}
206 };
207
208 pub const CheckVoidStmt = struct {
209 base: Instruction,
210 target: *Instruction,
211
212 pub fn build(
213 irb: *Builder,
214 scope: *Scope,
215 target: *Instruction,
216 ) !*Instruction {
217 const inst = try irb.arena().create(CheckVoidStmt{
218 .base = Instruction{
219 .id = Instruction.Id.CheckVoidStmt,
220 .is_generated = true,
221 .scope = scope,
222 .debug_id = irb.next_debug_id,
223 .val = IrVal{ .Known = &Value.Void.get(irb.module).base },
224 },
225 .target = target,
226 });
227 irb.next_debug_id += 1;
228 try irb.current_basic_block.instruction_list.append(&inst.base);
229 return &inst.base;
230 }
231
232 pub fn dump(inst: *const CheckVoidStmt) void {}
233 };
234
235 pub const Phi = struct {
236 base: Instruction,
237 incoming_blocks: []*BasicBlock,
238 incoming_values: []*Instruction,
239
240 pub fn build(
241 irb: *Builder,
242 scope: *Scope,
243 incoming_blocks: []*BasicBlock,
244 incoming_values: []*Instruction,
245 ) !*Instruction {
246 const inst = try irb.arena().create(Phi{
247 .base = Instruction{
248 .id = Instruction.Id.Phi,
249 .is_generated = false,
250 .scope = scope,
251 .debug_id = irb.next_debug_id,
252 .val = IrVal.Unknown,
253 },
254 .incoming_blocks = incoming_blocks,
255 .incoming_values = incoming_values,
256 });
257 irb.next_debug_id += 1;
258 try irb.current_basic_block.instruction_list.append(&inst.base);
259 return &inst.base;
260 }
261
262 pub fn dump(inst: *const Phi) void {}
263 };
264
265 pub const Br = struct {
266 base: Instruction,
267 dest_block: *BasicBlock,
268 is_comptime: *Instruction,
269
270 pub fn build(
271 irb: *Builder,
272 scope: *Scope,
273 dest_block: *BasicBlock,
274 is_comptime: *Instruction,
275 ) !*Instruction {
276 const inst = try irb.arena().create(Br{
277 .base = Instruction{
278 .id = Instruction.Id.Br,
279 .is_generated = false,
280 .scope = scope,
281 .debug_id = irb.next_debug_id,
282 .val = IrVal{ .Known = &Value.NoReturn.get(irb.module).base },
283 },
284 .dest_block = dest_block,
285 .is_comptime = is_comptime,
286 });
287 irb.next_debug_id += 1;
288 try irb.current_basic_block.instruction_list.append(&inst.base);
289 return &inst.base;
290 }
291
292 pub fn dump(inst: *const Br) void {}
110293 };
111294};
295
296pub const Variable = struct {
297 child_scope: *Scope,
298};
299
300pub const BasicBlock = struct {
301 ref_count: usize,
302 name_hint: []const u8,
303 debug_id: usize,
304 scope: *Scope,
305 instruction_list: std.ArrayList(*Instruction),
306
307 pub fn ref(self: *BasicBlock) void {
308 self.ref_count += 1;
309 }
310};
311
312/// Stuff that survives longer than Builder
313pub const Code = struct {
314 basic_block_list: std.ArrayList(*BasicBlock),
315 arena: std.heap.ArenaAllocator,
316
317 /// allocator is module.a()
318 pub fn destroy(self: *Code, allocator: *Allocator) void {
319 self.arena.deinit();
320 allocator.destroy(self);
321 }
322
323 pub fn dump(self: *Code) void {
324 var bb_i: usize = 0;
325 for (self.basic_block_list.toSliceConst()) |bb| {
326 std.debug.warn("{}_{}:\n", bb.name_hint, bb.debug_id);
327 for (bb.instruction_list.toSliceConst()) |instr| {
328 std.debug.warn(" ");
329 instr.dump();
330 std.debug.warn("\n");
331 }
332 }
333 }
334};
335
336pub const Builder = struct {
337 module: *Module,
338 code: *Code,
339 current_basic_block: *BasicBlock,
340 next_debug_id: usize,
341 parsed_file: *ParsedFile,
342 is_comptime: bool,
343
344 pub const Error = error{
345 OutOfMemory,
346 Unimplemented,
347 };
348
349 pub fn init(module: *Module, parsed_file: *ParsedFile) !Builder {
350 const code = try module.a().create(Code{
351 .basic_block_list = undefined,
352 .arena = std.heap.ArenaAllocator.init(module.a()),
353 });
354 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
355 errdefer code.destroy(module.a());
356
357 return Builder{
358 .module = module,
359 .parsed_file = parsed_file,
360 .current_basic_block = undefined,
361 .code = code,
362 .next_debug_id = 0,
363 .is_comptime = false,
364 };
365 }
366
367 pub fn abort(self: *Builder) void {
368 self.code.destroy(self.module.a());
369 }
370
371 /// Call code.destroy() when done
372 pub fn finish(self: *Builder) *Code {
373 return self.code;
374 }
375
376 /// No need to clean up resources thanks to the arena allocator.
377 pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: []const u8) !*BasicBlock {
378 const basic_block = try self.arena().create(BasicBlock{
379 .ref_count = 0,
380 .name_hint = name_hint,
381 .debug_id = self.next_debug_id,
382 .scope = scope,
383 .instruction_list = std.ArrayList(*Instruction).init(self.arena()),
384 });
385 self.next_debug_id += 1;
386 return basic_block;
387 }
388
389 pub fn setCursorAtEndAndAppendBlock(self: *Builder, basic_block: *BasicBlock) !void {
390 try self.code.basic_block_list.append(basic_block);
391 self.setCursorAtEnd(basic_block);
392 }
393
394 pub fn setCursorAtEnd(self: *Builder, basic_block: *BasicBlock) void {
395 self.current_basic_block = basic_block;
396 }
397
398 pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Instruction {
399 switch (node.id) {
400 ast.Node.Id.Root => unreachable,
401 ast.Node.Id.Use => unreachable,
402 ast.Node.Id.TestDecl => unreachable,
403 ast.Node.Id.VarDecl => @panic("TODO"),
404 ast.Node.Id.Defer => @panic("TODO"),
405 ast.Node.Id.InfixOp => @panic("TODO"),
406 ast.Node.Id.PrefixOp => @panic("TODO"),
407 ast.Node.Id.SuffixOp => @panic("TODO"),
408 ast.Node.Id.Switch => @panic("TODO"),
409 ast.Node.Id.While => @panic("TODO"),
410 ast.Node.Id.For => @panic("TODO"),
411 ast.Node.Id.If => @panic("TODO"),
412 ast.Node.Id.ControlFlowExpression => return error.Unimplemented,
413 ast.Node.Id.Suspend => @panic("TODO"),
414 ast.Node.Id.VarType => @panic("TODO"),
415 ast.Node.Id.ErrorType => @panic("TODO"),
416 ast.Node.Id.FnProto => @panic("TODO"),
417 ast.Node.Id.PromiseType => @panic("TODO"),
418 ast.Node.Id.IntegerLiteral => @panic("TODO"),
419 ast.Node.Id.FloatLiteral => @panic("TODO"),
420 ast.Node.Id.StringLiteral => @panic("TODO"),
421 ast.Node.Id.MultilineStringLiteral => @panic("TODO"),
422 ast.Node.Id.CharLiteral => @panic("TODO"),
423 ast.Node.Id.BoolLiteral => @panic("TODO"),
424 ast.Node.Id.NullLiteral => @panic("TODO"),
425 ast.Node.Id.UndefinedLiteral => @panic("TODO"),
426 ast.Node.Id.ThisLiteral => @panic("TODO"),
427 ast.Node.Id.Unreachable => @panic("TODO"),
428 ast.Node.Id.Identifier => @panic("TODO"),
429 ast.Node.Id.GroupedExpression => {
430 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
431 return irb.genNode(grouped_expr.expr, scope, lval);
432 },
433 ast.Node.Id.BuiltinCall => @panic("TODO"),
434 ast.Node.Id.ErrorSetDecl => @panic("TODO"),
435 ast.Node.Id.ContainerDecl => @panic("TODO"),
436 ast.Node.Id.Asm => @panic("TODO"),
437 ast.Node.Id.Comptime => @panic("TODO"),
438 ast.Node.Id.Block => {
439 const block = @fieldParentPtr(ast.Node.Block, "base", node);
440 return irb.lvalWrap(scope, try irb.genBlock(block, scope), lval);
441 },
442 ast.Node.Id.DocComment => @panic("TODO"),
443 ast.Node.Id.SwitchCase => @panic("TODO"),
444 ast.Node.Id.SwitchElse => @panic("TODO"),
445 ast.Node.Id.Else => @panic("TODO"),
446 ast.Node.Id.Payload => @panic("TODO"),
447 ast.Node.Id.PointerPayload => @panic("TODO"),
448 ast.Node.Id.PointerIndexPayload => @panic("TODO"),
449 ast.Node.Id.StructField => @panic("TODO"),
450 ast.Node.Id.UnionTag => @panic("TODO"),
451 ast.Node.Id.EnumTag => @panic("TODO"),
452 ast.Node.Id.ErrorTag => @panic("TODO"),
453 ast.Node.Id.AsmInput => @panic("TODO"),
454 ast.Node.Id.AsmOutput => @panic("TODO"),
455 ast.Node.Id.AsyncAttribute => @panic("TODO"),
456 ast.Node.Id.ParamDecl => @panic("TODO"),
457 ast.Node.Id.FieldInitializer => @panic("TODO"),
458 }
459 }
460
461 fn isCompTime(irb: *Builder, target_scope: *Scope) bool {
462 if (irb.is_comptime)
463 return true;
464
465 var scope = target_scope;
466 while (true) {
467 switch (scope.id) {
468 Scope.Id.CompTime => return true,
469 Scope.Id.FnDef => return false,
470 Scope.Id.Decls => unreachable,
471 Scope.Id.Block,
472 Scope.Id.Defer,
473 Scope.Id.DeferExpr,
474 => scope = scope.parent orelse return false,
475 }
476 }
477 }
478
479 pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Instruction {
480 const block_scope = try Scope.Block.create(irb.module, parent_scope);
481
482 const outer_block_scope = &block_scope.base;
483 var child_scope = outer_block_scope;
484
485 if (parent_scope.findFnDef()) |fndef_scope| {
486 if (fndef_scope.fn_val.child_scope == parent_scope) {
487 fndef_scope.fn_val.block_scope = block_scope;
488 }
489 }
490
491 if (block.statements.len == 0) {
492 // {}
493 return Instruction.Const.buildVoid(irb, child_scope, false);
494 }
495
496 if (block.label) |label| {
497 block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena());
498 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());
499 block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd");
500 block_scope.is_comptime = try Instruction.Const.buildBool(irb, parent_scope, irb.isCompTime(parent_scope));
501 }
502
503 var is_continuation_unreachable = false;
504 var noreturn_return_value: ?*Instruction = null;
505
506 var stmt_it = block.statements.iterator(0);
507 while (stmt_it.next()) |statement_node_ptr| {
508 const statement_node = statement_node_ptr.*;
509
510 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
511 // defer starts a new scope
512 const defer_token = irb.parsed_file.tree.tokens.at(defer_node.defer_token);
513 const kind = switch (defer_token.id) {
514 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
515 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
516 else => unreachable,
517 };
518 const defer_expr_scope = try Scope.DeferExpr.create(irb.module, parent_scope, defer_node.expr);
519 const defer_child_scope = try Scope.Defer.create(irb.module, parent_scope, kind, defer_expr_scope);
520 child_scope = &defer_child_scope.base;
521 continue;
522 }
523 const statement_value = try irb.genNode(statement_node, child_scope, LVal.None);
524
525 is_continuation_unreachable = statement_value.isNoReturn();
526 if (is_continuation_unreachable) {
527 // keep the last noreturn statement value around in case we need to return it
528 noreturn_return_value = statement_value;
529 }
530
531 if (statement_value.cast(Instruction.DeclVar)) |decl_var| {
532 // variable declarations start a new scope
533 child_scope = decl_var.variable.child_scope;
534 } else if (!is_continuation_unreachable) {
535 // this statement's value must be void
536 _ = Instruction.CheckVoidStmt.build(irb, child_scope, statement_value);
537 }
538 }
539
540 if (is_continuation_unreachable) {
541 assert(noreturn_return_value != null);
542 if (block.label == null or block_scope.incoming_blocks.len == 0) {
543 return noreturn_return_value.?;
544 }
545
546 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
547 return Instruction.Phi.build(
548 irb,
549 parent_scope,
550 block_scope.incoming_blocks.toOwnedSlice(),
551 block_scope.incoming_values.toOwnedSlice(),
552 );
553 }
554
555 if (block.label) |label| {
556 try block_scope.incoming_blocks.append(irb.current_basic_block);
557 try block_scope.incoming_values.append(
558 try Instruction.Const.buildVoid(irb, parent_scope, true),
559 );
560 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
561 (try Instruction.Br.build(
562 irb,
563 parent_scope,
564 block_scope.end_block,
565 block_scope.is_comptime,
566 )).setGenerated();
567 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
568 return Instruction.Phi.build(
569 irb,
570 parent_scope,
571 block_scope.incoming_blocks.toOwnedSlice(),
572 block_scope.incoming_values.toOwnedSlice(),
573 );
574 }
575
576 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
577 const result = try Instruction.Const.buildVoid(irb, child_scope, false);
578 result.setGenerated();
579 return result;
580 }
581
582 fn genDefersForBlock(
583 irb: *Builder,
584 inner_scope: *Scope,
585 outer_scope: *Scope,
586 gen_kind: Scope.Defer.Kind,
587 ) !bool {
588 var scope = inner_scope;
589 var is_noreturn = false;
590 while (true) {
591 switch (scope.id) {
592 Scope.Id.Defer => {
593 const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope);
594 const generate = switch (defer_scope.kind) {
595 Scope.Defer.Kind.ScopeExit => true,
596 Scope.Defer.Kind.ErrorExit => gen_kind == Scope.Defer.Kind.ErrorExit,
597 };
598 if (generate) {
599 const defer_expr_scope = defer_scope.defer_expr_scope;
600 const instruction = try irb.genNode(
601 defer_expr_scope.expr_node,
602 &defer_expr_scope.base,
603 LVal.None,
604 );
605 if (instruction.isNoReturn()) {
606 is_noreturn = true;
607 } else {
608 _ = Instruction.CheckVoidStmt.build(irb, &defer_expr_scope.base, instruction);
609 }
610 }
611 },
612 Scope.Id.FnDef,
613 Scope.Id.Decls,
614 => return is_noreturn,
615
616 Scope.Id.CompTime,
617 Scope.Id.Block,
618 => scope = scope.parent orelse return is_noreturn,
619
620 Scope.Id.DeferExpr => unreachable,
621 }
622 }
623 }
624
625 pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Instruction, lval: LVal) !*Instruction {
626 switch (lval) {
627 LVal.None => return instruction,
628 LVal.Ptr => {
629 // We needed a pointer to a value, but we got a value. So we create
630 // an instruction which just makes a const pointer of it.
631 return Instruction.Ref.build(irb, scope, instruction, Mut.Const, Volatility.NonVolatile);
632 },
633 }
634 }
635
636 fn arena(self: *Builder) *Allocator {
637 return &self.code.arena.allocator;
638 }
639};
640
641pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_file: *ParsedFile) !*Code {
642 var irb = try Builder.init(module, parsed_file);
643 errdefer irb.abort();
644
645 const entry_block = try irb.createBasicBlock(scope, "Entry");
646 entry_block.ref(); // Entry block gets a reference because we enter it to begin.
647 try irb.setCursorAtEndAndAppendBlock(entry_block);
648
649 const result = try irb.genNode(body_node, scope, LVal.None);
650 if (!result.isNoReturn()) {
651 const void_inst = try Instruction.Const.buildVoid(&irb, scope, false);
652 (try Instruction.Return.build(&irb, scope, void_inst)).setGenerated();
653 }
654
655 return irb.finish();
656}
src-self-hosted/module.zig+186-197
......@@ -15,12 +15,21 @@ const errmsg = @import("errmsg.zig");
1515const ast = std.zig.ast;
1616const event = std.event;
1717const assert = std.debug.assert;
18const AtomicRmwOp = builtin.AtomicRmwOp;
19const AtomicOrder = builtin.AtomicOrder;
20const Scope = @import("scope.zig").Scope;
21const Decl = @import("decl.zig").Decl;
22const ir = @import("ir.zig");
23const Visib = @import("visib.zig").Visib;
24const ParsedFile = @import("parsed_file.zig").ParsedFile;
25const Value = @import("value.zig").Value;
26const Type = Value.Type;
1827
1928pub const Module = struct {
2029 loop: *event.Loop,
2130 name: Buffer,
2231 root_src_path: ?[]const u8,
23 module: llvm.ModuleRef,
32 llvm_module: llvm.ModuleRef,
2433 context: llvm.ContextRef,
2534 builder: llvm.BuilderRef,
2635 target: Target,
......@@ -91,6 +100,16 @@ pub const Module = struct {
91100
92101 compile_errors: event.Locked(CompileErrList),
93102
103 meta_type: *Type.MetaType,
104 void_type: *Type.Void,
105 bool_type: *Type.Bool,
106 noreturn_type: *Type.NoReturn,
107
108 void_value: *Value.Void,
109 true_value: *Value.Bool,
110 false_value: *Value.Bool,
111 noreturn_value: *Value.NoReturn,
112
94113 const CompileErrList = std.ArrayList(*errmsg.Msg);
95114
96115 // TODO handle some of these earlier and report them in a way other than error codes
......@@ -129,6 +148,7 @@ pub const Module = struct {
129148 Overflow,
130149 NotSupported,
131150 BufferTooSmall,
151 Unimplemented,
132152 };
133153
134154 pub const Event = union(enum) {
......@@ -180,8 +200,8 @@ pub const Module = struct {
180200 const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
181201 errdefer c.LLVMContextDispose(context);
182202
183 const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory;
184 errdefer c.LLVMDisposeModule(module);
203 const llvm_module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory;
204 errdefer c.LLVMDisposeModule(llvm_module);
185205
186206 const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
187207 errdefer c.LLVMDisposeBuilder(builder);
......@@ -189,12 +209,12 @@ pub const Module = struct {
189209 const events = try event.Channel(Event).create(loop, 0);
190210 errdefer events.destroy();
191211
192 return loop.allocator.create(Module{
212 const module = try loop.allocator.create(Module{
193213 .loop = loop,
194214 .events = events,
195215 .name = name_buffer,
196216 .root_src_path = root_src_path,
197 .module = module,
217 .llvm_module = llvm_module,
198218 .context = context,
199219 .builder = builder,
200220 .target = target.*,
......@@ -248,7 +268,109 @@ pub const Module = struct {
248268 .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
249269 .build_group = event.Group(BuildError!void).init(loop),
250270 .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
271
272 .meta_type = undefined,
273 .void_type = undefined,
274 .void_value = undefined,
275 .bool_type = undefined,
276 .true_value = undefined,
277 .false_value = undefined,
278 .noreturn_type = undefined,
279 .noreturn_value = undefined,
280 });
281 try module.initTypes();
282 return module;
283 }
284
285 fn initTypes(module: *Module) !void {
286 module.meta_type = try module.a().create(Type.MetaType{
287 .base = Type{
288 .base = Value{
289 .id = Value.Id.Type,
290 .typeof = undefined,
291 .ref_count = 3, // 3 because it references itself twice
292 },
293 .id = builtin.TypeId.Type,
294 },
295 .value = undefined,
296 });
297 module.meta_type.value = &module.meta_type.base;
298 module.meta_type.base.base.typeof = &module.meta_type.base;
299 errdefer module.a().destroy(module.meta_type);
300
301 module.void_type = try module.a().create(Type.Void{
302 .base = Type{
303 .base = Value{
304 .id = Value.Id.Type,
305 .typeof = &Type.MetaType.get(module).base,
306 .ref_count = 1,
307 },
308 .id = builtin.TypeId.Void,
309 },
310 });
311 errdefer module.a().destroy(module.void_type);
312
313 module.noreturn_type = try module.a().create(Type.NoReturn{
314 .base = Type{
315 .base = Value{
316 .id = Value.Id.Type,
317 .typeof = &Type.MetaType.get(module).base,
318 .ref_count = 1,
319 },
320 .id = builtin.TypeId.NoReturn,
321 },
322 });
323 errdefer module.a().destroy(module.noreturn_type);
324
325 module.bool_type = try module.a().create(Type.Bool{
326 .base = Type{
327 .base = Value{
328 .id = Value.Id.Type,
329 .typeof = &Type.MetaType.get(module).base,
330 .ref_count = 1,
331 },
332 .id = builtin.TypeId.Bool,
333 },
334 });
335 errdefer module.a().destroy(module.bool_type);
336
337 module.void_value = try module.a().create(Value.Void{
338 .base = Value{
339 .id = Value.Id.Void,
340 .typeof = &Type.Void.get(module).base,
341 .ref_count = 1,
342 },
343 });
344 errdefer module.a().destroy(module.void_value);
345
346 module.true_value = try module.a().create(Value.Bool{
347 .base = Value{
348 .id = Value.Id.Bool,
349 .typeof = &Type.Bool.get(module).base,
350 .ref_count = 1,
351 },
352 .x = true,
353 });
354 errdefer module.a().destroy(module.true_value);
355
356 module.false_value = try module.a().create(Value.Bool{
357 .base = Value{
358 .id = Value.Id.Bool,
359 .typeof = &Type.Bool.get(module).base,
360 .ref_count = 1,
361 },
362 .x = false,
251363 });
364 errdefer module.a().destroy(module.false_value);
365
366 module.noreturn_value = try module.a().create(Value.NoReturn{
367 .base = Value{
368 .id = Value.Id.NoReturn,
369 .typeof = &Type.NoReturn.get(module).base,
370 .ref_count = 1,
371 },
372 });
373 errdefer module.a().destroy(module.noreturn_value);
252374 }
253375
254376 fn dump(self: *Module) void {
......@@ -256,9 +378,17 @@ pub const Module = struct {
256378 }
257379
258380 pub fn destroy(self: *Module) void {
381 self.noreturn_value.base.deref(self);
382 self.void_value.base.deref(self);
383 self.false_value.base.deref(self);
384 self.true_value.base.deref(self);
385 self.noreturn_type.base.base.deref(self);
386 self.void_type.base.base.deref(self);
387 self.meta_type.base.base.deref(self);
388
259389 self.events.destroy();
260390 c.LLVMDisposeBuilder(self.builder);
261 c.LLVMDisposeModule(self.module);
391 c.LLVMDisposeModule(self.llvm_module);
262392 c.LLVMContextDispose(self.context);
263393 self.name.deinit();
264394
......@@ -331,8 +461,8 @@ pub const Module = struct {
331461 const tree = &parsed_file.tree;
332462
333463 // create empty struct for it
334 const decls = try Scope.Decls.create(self.a(), null);
335 errdefer decls.destroy();
464 const decls = try Scope.Decls.create(self, null);
465 defer decls.base.deref(self);
336466
337467 var decl_group = event.Group(BuildError!void).init(self.loop);
338468 errdefer decl_group.cancelAll();
......@@ -359,14 +489,17 @@ pub const Module = struct {
359489 .id = Decl.Id.Fn,
360490 .name = name,
361491 .visib = parseVisibToken(tree, fn_proto.visib_token),
362 .resolution = Decl.Resolution.Unresolved,
492 .resolution = event.Future(BuildError!void).init(self.loop),
493 .resolution_in_progress = 0,
494 .parsed_file = parsed_file,
495 .parent_scope = &decls.base,
363496 },
364497 .value = Decl.Fn.Val{ .Unresolved = {} },
365498 .fn_proto = fn_proto,
366499 });
367500 errdefer self.a().destroy(fn_decl);
368501
369 try decl_group.call(addTopLevelDecl, self, parsed_file, &fn_decl.base);
502 try decl_group.call(addTopLevelDecl, self, &fn_decl.base);
370503 },
371504 ast.Node.Id.TestDecl => @panic("TODO"),
372505 else => unreachable,
......@@ -376,12 +509,12 @@ pub const Module = struct {
376509 try await (async self.build_group.wait() catch unreachable);
377510 }
378511
379 async fn addTopLevelDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void {
380 const is_export = decl.isExported(&parsed_file.tree);
512 async fn addTopLevelDecl(self: *Module, decl: *Decl) !void {
513 const is_export = decl.isExported(&decl.parsed_file.tree);
381514
382515 if (is_export) {
383 try self.build_group.call(verifyUniqueSymbol, self, parsed_file, decl);
384 try self.build_group.call(generateDecl, self, parsed_file, decl);
516 try self.build_group.call(verifyUniqueSymbol, self, decl);
517 try self.build_group.call(resolveDecl, self, decl);
385518 }
386519 }
387520
......@@ -416,36 +549,21 @@ pub const Module = struct {
416549 try compile_errors.value.append(msg);
417550 }
418551
419 async fn verifyUniqueSymbol(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void {
552 async fn verifyUniqueSymbol(self: *Module, decl: *Decl) !void {
420553 const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable);
421554 defer exported_symbol_names.release();
422555
423556 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
424557 try self.addCompileError(
425 parsed_file,
558 decl.parsed_file,
426559 decl.getSpan(),
427560 "exported symbol collision: '{}'",
428561 decl.name,
429562 );
563 // TODO add error note showing location of other symbol
430564 }
431565 }
432566
433 /// This declaration has been blessed as going into the final code generation.
434 async fn generateDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) void {
435 switch (decl.id) {
436 Decl.Id.Var => @panic("TODO"),
437 Decl.Id.Fn => {
438 const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl);
439 return await (async self.generateDeclFn(parsed_file, fn_decl) catch unreachable);
440 },
441 Decl.Id.CompTime => @panic("TODO"),
442 }
443 }
444
445 async fn generateDeclFn(self: *Module, parsed_file: *ParsedFile, fn_decl: *Decl.Fn) void {
446 fn_decl.value = Decl.Fn.Val{ .Ok = Value.Fn{} };
447 }
448
449567 pub fn link(self: *Module, out_file: ?[]const u8) !void {
450568 warn("TODO link");
451569 return error.Todo;
......@@ -501,177 +619,48 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
501619 }
502620}
503621
504pub const Scope = struct {
505 id: Id,
506 parent: ?*Scope,
507
508 pub const Id = enum {
509 Decls,
510 Block,
511 };
512
513 pub const Decls = struct {
514 base: Scope,
515 table: Decl.Table,
516
517 pub fn create(a: *Allocator, parent: ?*Scope) !*Decls {
518 const self = try a.create(Decls{
519 .base = Scope{
520 .id = Id.Decls,
521 .parent = parent,
522 },
523 .table = undefined,
524 });
525 errdefer a.destroy(self);
526
527 self.table = Decl.Table.init(a);
528 errdefer self.table.deinit();
529
530 return self;
531 }
532
533 pub fn destroy(self: *Decls) void {
534 self.table.deinit();
535 self.table.allocator.destroy(self);
536 self.* = undefined;
537 }
538 };
539
540 pub const Block = struct {
541 base: Scope,
542 };
543};
544
545pub const Visib = enum {
546 Private,
547 Pub,
548};
549
550pub const Decl = struct {
551 id: Id,
552 name: []const u8,
553 visib: Visib,
554 resolution: Resolution,
555
556 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
557
558 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
559 switch (base.id) {
560 Id.Fn => {
561 const fn_decl = @fieldParentPtr(Fn, "base", base);
562 return fn_decl.isExported(tree);
563 },
564 else => return false,
565 }
622/// This declaration has been blessed as going into the final code generation.
623pub async fn resolveDecl(module: *Module, decl: *Decl) !void {
624 if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
625 decl.resolution.data = await (async generateDecl(module, decl) catch unreachable);
626 decl.resolution.resolve();
627 } else {
628 return (await (async decl.resolution.get() catch unreachable)).*;
566629 }
630}
567631
568 pub fn getSpan(base: *const Decl) errmsg.Span {
569 switch (base.id) {
570 Id.Fn => {
571 const fn_decl = @fieldParentPtr(Fn, "base", base);
572 const fn_proto = fn_decl.fn_proto;
573 const start = fn_proto.fn_token;
574 const end = fn_proto.name_token orelse start;
575 return errmsg.Span{
576 .first = start,
577 .last = end + 1,
578 };
579 },
580 else => @panic("TODO"),
581 }
632/// The function that actually does the generation.
633async fn generateDecl(module: *Module, decl: *Decl) !void {
634 switch (decl.id) {
635 Decl.Id.Var => @panic("TODO"),
636 Decl.Id.Fn => {
637 const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl);
638 return await (async generateDeclFn(module, fn_decl) catch unreachable);
639 },
640 Decl.Id.CompTime => @panic("TODO"),
582641 }
642}
583643
584 pub const Resolution = enum {
585 Unresolved,
586 InProgress,
587 Invalid,
588 Ok,
589 };
590
591 pub const Id = enum {
592 Var,
593 Fn,
594 CompTime,
595 };
596
597 pub const Var = struct {
598 base: Decl,
599 };
644async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void {
645 const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl");
600646
601 pub const Fn = struct {
602 base: Decl,
603 value: Val,
604 fn_proto: *const ast.Node.FnProto,
647 const fndef_scope = try Scope.FnDef.create(module, fn_decl.base.parent_scope);
648 defer fndef_scope.base.deref(module);
605649
606 // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
607 pub const Val = union {
608 Unresolved: void,
609 Ok: Value.Fn,
610 };
650 const fn_type = try Type.Fn.create(module);
651 defer fn_type.base.base.deref(module);
611652
612 pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
613 return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
614 const token = tree.tokens.at(tok_index);
615 break :x switch (token.id) {
616 Token.Id.Extern => tree.tokenSlicePtr(token),
617 else => null,
618 };
619 } else null;
620 }
653 const fn_val = try Value.Fn.create(module, fn_type, fndef_scope);
654 defer fn_val.base.deref(module);
621655
622 pub fn isExported(self: Fn, tree: *ast.Tree) bool {
623 if (self.fn_proto.extern_export_inline_token) |tok_index| {
624 const token = tree.tokens.at(tok_index);
625 return token.id == Token.Id.Keyword_export;
626 } else {
627 return false;
628 }
629 }
630 };
656 fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
631657
632 pub const CompTime = struct {
633 base: Decl,
634 };
635};
636
637pub const Value = struct {
638 pub const Fn = struct {};
639};
640
641pub const Type = struct {
642 id: Id,
643
644 pub const Id = enum {
645 Type,
646 Void,
647 Bool,
648 NoReturn,
649 Int,
650 Float,
651 Pointer,
652 Array,
653 Struct,
654 ComptimeFloat,
655 ComptimeInt,
656 Undefined,
657 Null,
658 Optional,
659 ErrorUnion,
660 ErrorSet,
661 Enum,
662 Union,
663 Fn,
664 Opaque,
665 Promise,
666 };
667
668 pub const Struct = struct {
669 base: Type,
670 decls: *Scope.Decls,
671 };
672};
673
674pub const ParsedFile = struct {
675 tree: ast.Tree,
676 realpath: []const u8,
677};
658 const code = try await (async ir.gen(
659 module,
660 body_node,
661 &fndef_scope.base,
662 fn_decl.base.parsed_file,
663 ) catch unreachable);
664 //code.dump();
665 //try await (async irAnalyze(module, func) catch unreachable);
666}
src-self-hosted/parsed_file.zig created+6
......@@ -0,0 +1,6 @@
1const ast = @import("std").zig.ast;
2
3pub const ParsedFile = struct {
4 tree: ast.Tree,
5 realpath: []const u8,
6};
src-self-hosted/scope.zig+224-6
......@@ -1,16 +1,234 @@
1const std = @import("std");
2const Allocator = mem.Allocator;
3const Decl = @import("decl.zig").Decl;
4const Module = @import("module.zig").Module;
5const mem = std.mem;
6const ast = std.zig.ast;
7const Value = @import("value.zig").Value;
8const ir = @import("ir.zig");
9
110pub const Scope = struct {
211 id: Id,
3 parent: *Scope,
12 parent: ?*Scope,
13 ref_count: usize,
14
15 pub fn ref(base: *Scope) void {
16 base.ref_count += 1;
17 }
18
19 pub fn deref(base: *Scope, module: *Module) void {
20 base.ref_count -= 1;
21 if (base.ref_count == 0) {
22 if (base.parent) |parent| parent.deref(module);
23 switch (base.id) {
24 Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(),
25 Id.Block => @fieldParentPtr(Block, "base", base).destroy(module),
26 Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(module),
27 Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(module),
28 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(module),
29 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(module),
30 }
31 }
32 }
33
34 pub fn findFnDef(base: *Scope) ?*FnDef {
35 var scope = base;
36 while (true) {
37 switch (scope.id) {
38 Id.FnDef => return @fieldParentPtr(FnDef, "base", base),
39 Id.Decls => return null,
40
41 Id.Block,
42 Id.Defer,
43 Id.DeferExpr,
44 Id.CompTime,
45 => scope = scope.parent orelse return null,
46 }
47 }
48 }
449
550 pub const Id = enum {
651 Decls,
752 Block,
8 Defer,
9 DeferExpr,
10 VarDecl,
11 CImport,
12 Loop,
1353 FnDef,
1454 CompTime,
55 Defer,
56 DeferExpr,
57 };
58
59 pub const Decls = struct {
60 base: Scope,
61 table: Decl.Table,
62
63 /// Creates a Decls scope with 1 reference
64 pub fn create(module: *Module, parent: ?*Scope) !*Decls {
65 const self = try module.a().create(Decls{
66 .base = Scope{
67 .id = Id.Decls,
68 .parent = parent,
69 .ref_count = 1,
70 },
71 .table = undefined,
72 });
73 errdefer module.a().destroy(self);
74
75 self.table = Decl.Table.init(module.a());
76 errdefer self.table.deinit();
77
78 if (parent) |p| p.ref();
79
80 return self;
81 }
82
83 pub fn destroy(self: *Decls) void {
84 self.table.deinit();
85 self.table.allocator.destroy(self);
86 }
87 };
88
89 pub const Block = struct {
90 base: Scope,
91 incoming_values: std.ArrayList(*ir.Instruction),
92 incoming_blocks: std.ArrayList(*ir.BasicBlock),
93 end_block: *ir.BasicBlock,
94 is_comptime: *ir.Instruction,
95
96 /// Creates a Block scope with 1 reference
97 pub fn create(module: *Module, parent: ?*Scope) !*Block {
98 const self = try module.a().create(Block{
99 .base = Scope{
100 .id = Id.Block,
101 .parent = parent,
102 .ref_count = 1,
103 },
104 .incoming_values = undefined,
105 .incoming_blocks = undefined,
106 .end_block = undefined,
107 .is_comptime = undefined,
108 });
109 errdefer module.a().destroy(self);
110
111 if (parent) |p| p.ref();
112 return self;
113 }
114
115 pub fn destroy(self: *Block, module: *Module) void {
116 module.a().destroy(self);
117 }
118 };
119
120 pub const FnDef = struct {
121 base: Scope,
122
123 /// This reference is not counted so that the scope can get destroyed with the function
124 fn_val: *Value.Fn,
125
126 /// Creates a FnDef scope with 1 reference
127 /// Must set the fn_val later
128 pub fn create(module: *Module, parent: ?*Scope) !*FnDef {
129 const self = try module.a().create(FnDef{
130 .base = Scope{
131 .id = Id.FnDef,
132 .parent = parent,
133 .ref_count = 1,
134 },
135 .fn_val = undefined,
136 });
137
138 if (parent) |p| p.ref();
139
140 return self;
141 }
142
143 pub fn destroy(self: *FnDef, module: *Module) void {
144 module.a().destroy(self);
145 }
146 };
147
148 pub const CompTime = struct {
149 base: Scope,
150
151 /// Creates a CompTime scope with 1 reference
152 pub fn create(module: *Module, parent: ?*Scope) !*CompTime {
153 const self = try module.a().create(CompTime{
154 .base = Scope{
155 .id = Id.CompTime,
156 .parent = parent,
157 .ref_count = 1,
158 },
159 });
160
161 if (parent) |p| p.ref();
162 return self;
163 }
164
165 pub fn destroy(self: *CompTime, module: *Module) void {
166 module.a().destroy(self);
167 }
168 };
169
170 pub const Defer = struct {
171 base: Scope,
172 defer_expr_scope: *DeferExpr,
173 kind: Kind,
174
175 pub const Kind = enum {
176 ScopeExit,
177 ErrorExit,
178 };
179
180 /// Creates a Defer scope with 1 reference
181 pub fn create(
182 module: *Module,
183 parent: ?*Scope,
184 kind: Kind,
185 defer_expr_scope: *DeferExpr,
186 ) !*Defer {
187 const self = try module.a().create(Defer{
188 .base = Scope{
189 .id = Id.Defer,
190 .parent = parent,
191 .ref_count = 1,
192 },
193 .defer_expr_scope = defer_expr_scope,
194 .kind = kind,
195 });
196 errdefer module.a().destroy(self);
197
198 defer_expr_scope.base.ref();
199
200 if (parent) |p| p.ref();
201 return self;
202 }
203
204 pub fn destroy(self: *Defer, module: *Module) void {
205 self.defer_expr_scope.base.deref(module);
206 module.a().destroy(self);
207 }
208 };
209
210 pub const DeferExpr = struct {
211 base: Scope,
212 expr_node: *ast.Node,
213
214 /// Creates a DeferExpr scope with 1 reference
215 pub fn create(module: *Module, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr {
216 const self = try module.a().create(DeferExpr{
217 .base = Scope{
218 .id = Id.DeferExpr,
219 .parent = parent,
220 .ref_count = 1,
221 },
222 .expr_node = expr_node,
223 });
224 errdefer module.a().destroy(self);
225
226 if (parent) |p| p.ref();
227 return self;
228 }
229
230 pub fn destroy(self: *DeferExpr, module: *Module) void {
231 module.a().destroy(self);
232 }
15233 };
16234};
src-self-hosted/type.zig created+268
......@@ -0,0 +1,268 @@
1const builtin = @import("builtin");
2const Scope = @import("scope.zig").Scope;
3const Module = @import("module.zig").Module;
4const Value = @import("value.zig").Value;
5
6pub const Type = struct {
7 base: Value,
8 id: Id,
9
10 pub const Id = builtin.TypeId;
11
12 pub fn destroy(base: *Type, module: *Module) void {
13 switch (base.id) {
14 Id.Struct => @fieldParentPtr(Struct, "base", base).destroy(module),
15 Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(module),
16 Id.Type => @fieldParentPtr(MetaType, "base", base).destroy(module),
17 Id.Void => @fieldParentPtr(Void, "base", base).destroy(module),
18 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module),
19 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module),
20 Id.Int => @fieldParentPtr(Int, "base", base).destroy(module),
21 Id.Float => @fieldParentPtr(Float, "base", base).destroy(module),
22 Id.Pointer => @fieldParentPtr(Pointer, "base", base).destroy(module),
23 Id.Array => @fieldParentPtr(Array, "base", base).destroy(module),
24 Id.ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(module),
25 Id.ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(module),
26 Id.Undefined => @fieldParentPtr(Undefined, "base", base).destroy(module),
27 Id.Null => @fieldParentPtr(Null, "base", base).destroy(module),
28 Id.Optional => @fieldParentPtr(Optional, "base", base).destroy(module),
29 Id.ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(module),
30 Id.ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(module),
31 Id.Enum => @fieldParentPtr(Enum, "base", base).destroy(module),
32 Id.Union => @fieldParentPtr(Union, "base", base).destroy(module),
33 Id.Namespace => @fieldParentPtr(Namespace, "base", base).destroy(module),
34 Id.Block => @fieldParentPtr(Block, "base", base).destroy(module),
35 Id.BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(module),
36 Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(module),
37 Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(module),
38 Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(module),
39 }
40 }
41
42 pub const Struct = struct {
43 base: Type,
44 decls: *Scope.Decls,
45
46 pub fn destroy(self: *Struct, module: *Module) void {
47 module.a().destroy(self);
48 }
49 };
50
51 pub const Fn = struct {
52 base: Type,
53
54 pub fn create(module: *Module) !*Fn {
55 return module.a().create(Fn{
56 .base = Type{
57 .base = Value{
58 .id = Value.Id.Type,
59 .typeof = &MetaType.get(module).base,
60 .ref_count = 1,
61 },
62 .id = builtin.TypeId.Fn,
63 },
64 });
65 }
66
67 pub fn destroy(self: *Fn, module: *Module) void {
68 module.a().destroy(self);
69 }
70 };
71
72 pub const MetaType = struct {
73 base: Type,
74 value: *Type,
75
76 /// Adds 1 reference to the resulting type
77 pub fn get(module: *Module) *MetaType {
78 module.meta_type.base.base.ref();
79 return module.meta_type;
80 }
81
82 pub fn destroy(self: *MetaType, module: *Module) void {
83 module.a().destroy(self);
84 }
85 };
86
87 pub const Void = struct {
88 base: Type,
89
90 /// Adds 1 reference to the resulting type
91 pub fn get(module: *Module) *Void {
92 module.void_type.base.base.ref();
93 return module.void_type;
94 }
95
96 pub fn destroy(self: *Void, module: *Module) void {
97 module.a().destroy(self);
98 }
99 };
100
101 pub const Bool = struct {
102 base: Type,
103
104 /// Adds 1 reference to the resulting type
105 pub fn get(module: *Module) *Bool {
106 module.bool_type.base.base.ref();
107 return module.bool_type;
108 }
109
110 pub fn destroy(self: *Bool, module: *Module) void {
111 module.a().destroy(self);
112 }
113 };
114
115 pub const NoReturn = struct {
116 base: Type,
117
118 /// Adds 1 reference to the resulting type
119 pub fn get(module: *Module) *NoReturn {
120 module.noreturn_type.base.base.ref();
121 return module.noreturn_type;
122 }
123
124 pub fn destroy(self: *NoReturn, module: *Module) void {
125 module.a().destroy(self);
126 }
127 };
128
129 pub const Int = struct {
130 base: Type,
131
132 pub fn destroy(self: *Int, module: *Module) void {
133 module.a().destroy(self);
134 }
135 };
136
137 pub const Float = struct {
138 base: Type,
139
140 pub fn destroy(self: *Float, module: *Module) void {
141 module.a().destroy(self);
142 }
143 };
144 pub const Pointer = struct {
145 base: Type,
146
147 pub fn destroy(self: *Pointer, module: *Module) void {
148 module.a().destroy(self);
149 }
150 };
151 pub const Array = struct {
152 base: Type,
153
154 pub fn destroy(self: *Array, module: *Module) void {
155 module.a().destroy(self);
156 }
157 };
158 pub const ComptimeFloat = struct {
159 base: Type,
160
161 pub fn destroy(self: *ComptimeFloat, module: *Module) void {
162 module.a().destroy(self);
163 }
164 };
165 pub const ComptimeInt = struct {
166 base: Type,
167
168 pub fn destroy(self: *ComptimeInt, module: *Module) void {
169 module.a().destroy(self);
170 }
171 };
172 pub const Undefined = struct {
173 base: Type,
174
175 pub fn destroy(self: *Undefined, module: *Module) void {
176 module.a().destroy(self);
177 }
178 };
179 pub const Null = struct {
180 base: Type,
181
182 pub fn destroy(self: *Null, module: *Module) void {
183 module.a().destroy(self);
184 }
185 };
186 pub const Optional = struct {
187 base: Type,
188
189 pub fn destroy(self: *Optional, module: *Module) void {
190 module.a().destroy(self);
191 }
192 };
193 pub const ErrorUnion = struct {
194 base: Type,
195
196 pub fn destroy(self: *ErrorUnion, module: *Module) void {
197 module.a().destroy(self);
198 }
199 };
200 pub const ErrorSet = struct {
201 base: Type,
202
203 pub fn destroy(self: *ErrorSet, module: *Module) void {
204 module.a().destroy(self);
205 }
206 };
207 pub const Enum = struct {
208 base: Type,
209
210 pub fn destroy(self: *Enum, module: *Module) void {
211 module.a().destroy(self);
212 }
213 };
214 pub const Union = struct {
215 base: Type,
216
217 pub fn destroy(self: *Union, module: *Module) void {
218 module.a().destroy(self);
219 }
220 };
221 pub const Namespace = struct {
222 base: Type,
223
224 pub fn destroy(self: *Namespace, module: *Module) void {
225 module.a().destroy(self);
226 }
227 };
228
229 pub const Block = struct {
230 base: Type,
231
232 pub fn destroy(self: *Block, module: *Module) void {
233 module.a().destroy(self);
234 }
235 };
236
237 pub const BoundFn = struct {
238 base: Type,
239
240 pub fn destroy(self: *BoundFn, module: *Module) void {
241 module.a().destroy(self);
242 }
243 };
244
245 pub const ArgTuple = struct {
246 base: Type,
247
248 pub fn destroy(self: *ArgTuple, module: *Module) void {
249 module.a().destroy(self);
250 }
251 };
252
253 pub const Opaque = struct {
254 base: Type,
255
256 pub fn destroy(self: *Opaque, module: *Module) void {
257 module.a().destroy(self);
258 }
259 };
260
261 pub const Promise = struct {
262 base: Type,
263
264 pub fn destroy(self: *Promise, module: *Module) void {
265 module.a().destroy(self);
266 }
267 };
268};
src-self-hosted/value.zig created+125
......@@ -0,0 +1,125 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Scope = @import("scope.zig").Scope;
4const Module = @import("module.zig").Module;
5
6/// Values are ref-counted, heap-allocated, and copy-on-write
7/// If there is only 1 ref then write need not copy
8pub const Value = struct {
9 id: Id,
10 typeof: *Type,
11 ref_count: usize,
12
13 pub fn ref(base: *Value) void {
14 base.ref_count += 1;
15 }
16
17 pub fn deref(base: *Value, module: *Module) void {
18 base.ref_count -= 1;
19 if (base.ref_count == 0) {
20 base.typeof.base.deref(module);
21 switch (base.id) {
22 Id.Type => @fieldParentPtr(Type, "base", base).destroy(module),
23 Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(module),
24 Id.Void => @fieldParentPtr(Void, "base", base).destroy(module),
25 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module),
26 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module),
27 }
28 }
29 }
30
31 pub fn dump(base: *const Value) void {
32 std.debug.warn("{}", @tagName(base.id));
33 }
34
35 pub const Id = enum {
36 Type,
37 Fn,
38 Void,
39 Bool,
40 NoReturn,
41 };
42
43 pub const Type = @import("type.zig").Type;
44
45 pub const Fn = struct {
46 base: Value,
47
48 /// parent should be the top level decls or container decls
49 fndef_scope: *Scope.FnDef,
50
51 /// parent is scope for last parameter
52 child_scope: *Scope,
53
54 /// parent is child_scope
55 block_scope: *Scope.Block,
56
57 /// Creates a Fn value with 1 ref
58 pub fn create(module: *Module, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef) !*Fn {
59 const self = try module.a().create(Fn{
60 .base = Value{
61 .id = Value.Id.Fn,
62 .typeof = &fn_type.base,
63 .ref_count = 1,
64 },
65 .fndef_scope = fndef_scope,
66 .child_scope = &fndef_scope.base,
67 .block_scope = undefined,
68 });
69 fn_type.base.base.ref();
70 fndef_scope.fn_val = self;
71 fndef_scope.base.ref();
72 return self;
73 }
74
75 pub fn destroy(self: *Fn, module: *Module) void {
76 self.fndef_scope.base.deref(module);
77 module.a().destroy(self);
78 }
79 };
80
81 pub const Void = struct {
82 base: Value,
83
84 pub fn get(module: *Module) *Void {
85 module.void_value.base.ref();
86 return module.void_value;
87 }
88
89 pub fn destroy(self: *Void, module: *Module) void {
90 module.a().destroy(self);
91 }
92 };
93
94 pub const Bool = struct {
95 base: Value,
96 x: bool,
97
98 pub fn get(module: *Module, x: bool) *Bool {
99 if (x) {
100 module.true_value.base.ref();
101 return module.true_value;
102 } else {
103 module.false_value.base.ref();
104 return module.false_value;
105 }
106 }
107
108 pub fn destroy(self: *Bool, module: *Module) void {
109 module.a().destroy(self);
110 }
111 };
112
113 pub const NoReturn = struct {
114 base: Value,
115
116 pub fn get(module: *Module) *NoReturn {
117 module.noreturn_value.base.ref();
118 return module.noreturn_value;
119 }
120
121 pub fn destroy(self: *NoReturn, module: *Module) void {
122 module.a().destroy(self);
123 }
124 };
125};
src-self-hosted/visib.zig created+4
......@@ -0,0 +1,4 @@
1pub const Visib = enum {
2 Private,
3 Pub,
4};
std/event/future.zig+1-1
......@@ -57,7 +57,7 @@ test "std.event.Future" {
5757 const allocator = &da.allocator;
5858
5959 var loop: Loop = undefined;
60 try loop.initSingleThreaded(allocator);
60 try loop.initMultiThreaded(allocator);
6161 defer loop.deinit();
6262
6363 const handle = try async<allocator> testFuture(&loop);
std/zig/ast.zig-6
......@@ -970,14 +970,8 @@ pub const Node = struct {
970970 pub const Defer = struct {
971971 base: Node,
972972 defer_token: TokenIndex,
973 kind: Kind,
974973 expr: *Node,
975974
976 const Kind = enum {
977 Error,
978 Unconditional,
979 };
980
981975 pub fn iterate(self: *Defer, index: usize) ?*Node {
982976 var i = index;
983977
std/zig/parse.zig-5
......@@ -1041,11 +1041,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
10411041 const node = try arena.create(ast.Node.Defer{
10421042 .base = ast.Node{ .id = ast.Node.Id.Defer },
10431043 .defer_token = token_index,
1044 .kind = switch (token_ptr.id) {
1045 Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
1046 Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
1047 else => unreachable,
1048 },
10491044 .expr = undefined,
10501045 });
10511046 const node_ptr = try block.statements.addOne();