authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-13 21:56:38-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-13 21:56:38-04:00
loge78b1b810fd15dfd135c80d06d621851a59f42c6
tree637fb839f4f24b0137e02fb75c4eacf3359a81fd
parentc87102c3046cfabffe0e680e4793f5ce2a88caa2

self-hosted: basic IR pass2


7 files changed, 708 insertions(+), 205 deletions(-)

src-self-hosted/errmsg.zig+7
......@@ -14,6 +14,13 @@ pub const Color = enum {
1414pub const Span = struct {
1515 first: ast.TokenIndex,
1616 last: ast.TokenIndex,
17
18 pub fn token(i: TokenIndex) Span {
19 return Span {
20 .first = i,
21 .last = i,
22 };
23 }
1724};
1825
1926pub const Msg = struct {
src-self-hosted/ir.zig+569-181
......@@ -9,31 +9,34 @@ const Type = Value.Type;
99const assert = std.debug.assert;
1010const Token = std.zig.Token;
1111const ParsedFile = @import("parsed_file.zig").ParsedFile;
12const Span = @import("errmsg.zig").Span;
1213
1314pub const LVal = enum {
1415 None,
1516 Ptr,
1617};
1718
18pub const Mut = enum {
19 Mut,
20 Const,
21};
22
23pub const Volatility = enum {
24 NonVolatile,
25 Volatile,
26};
27
2819pub const IrVal = union(enum) {
2920 Unknown,
30 Known: *Value,
21 KnownType: *Type,
22 KnownValue: *Value,
23
24 const Init = enum {
25 Unknown,
26 NoReturn,
27 Void,
28 };
3129
3230 pub fn dump(self: IrVal) void {
3331 switch (self) {
34 IrVal.Unknown => std.debug.warn("Unknown"),
35 IrVal.Known => |value| {
36 std.debug.warn("Known(");
32 IrVal.Unknown => typeof.dump(),
33 IrVal.KnownType => |typeof| {
34 std.debug.warn("KnownType(");
35 typeof.dump();
36 std.debug.warn(")");
37 },
38 IrVal.KnownValue => |value| {
39 std.debug.warn("KnownValue(");
3740 value.dump();
3841 std.debug.warn(")");
3942 },
......@@ -46,10 +49,18 @@ pub const Instruction = struct {
4649 scope: *Scope,
4750 debug_id: usize,
4851 val: IrVal,
52 ref_count: usize,
53 span: Span,
4954
5055 /// true if this instruction was generated by zig and not from user code
5156 is_generated: bool,
5257
58 /// the instruction that is derived from this one in analysis
59 child: ?*Instruction,
60
61 /// the instruction that this one derives from in analysis
62 parent: ?*Instruction,
63
5364 pub fn cast(base: *Instruction, comptime T: type) ?*T {
5465 if (base.id == comptime typeToId(T)) {
5566 return @fieldParentPtr(T, "base", base);
......@@ -81,6 +92,47 @@ pub const Instruction = struct {
8192 unreachable;
8293 }
8394
95 pub fn hasSideEffects(base: *const Instruction) bool {
96 comptime var i = 0;
97 inline while (i < @memberCount(Id)) : (i += 1) {
98 if (base.id == @field(Id, @memberName(Id, i))) {
99 const T = @field(Instruction, @memberName(Id, i));
100 return @fieldParentPtr(T, "base", base).hasSideEffects();
101 }
102 }
103 unreachable;
104 }
105
106 pub fn analyze(base: *Instruction, ira: *Analyze) Analyze.Error!*Instruction {
107 comptime var i = 0;
108 inline while (i < @memberCount(Id)) : (i += 1) {
109 if (base.id == @field(Id, @memberName(Id, i))) {
110 const T = @field(Instruction, @memberName(Id, i));
111 const new_inst = try @fieldParentPtr(T, "base", base).analyze(ira);
112 new_inst.linkToParent(base);
113 return new_inst;
114 }
115 }
116 unreachable;
117 }
118
119 fn getAsParam(param: *Instruction) !*Instruction {
120 const child = param.child orelse return error.SemanticAnalysisFailed;
121 switch (child.val) {
122 IrVal.Unknown => return error.SemanticAnalysisFailed,
123 else => return child,
124 }
125 }
126
127 /// asserts that the type is known
128 fn getKnownType(self: *Instruction) *Type {
129 switch (self.val) {
130 IrVal.KnownType => |typeof| return typeof,
131 IrVal.KnownValue => |value| return value.typeof,
132 IrVal.Unknown => unreachable,
133 }
134 }
135
84136 pub fn setGenerated(base: *Instruction) void {
85137 base.is_generated = true;
86138 }
......@@ -88,10 +140,18 @@ pub const Instruction = struct {
88140 pub fn isNoReturn(base: *const Instruction) bool {
89141 switch (base.val) {
90142 IrVal.Unknown => return false,
91 IrVal.Known => |x| return x.typeof.id == Type.Id.NoReturn,
143 IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn,
144 IrVal.KnownType => |typeof| return typeof.id == Type.Id.NoReturn,
92145 }
93146 }
94147
148 pub fn linkToParent(self: *Instruction, parent: *Instruction) void {
149 assert(self.parent == null);
150 assert(parent.child == null);
151 self.parent = parent;
152 parent.child = self;
153 }
154
95155 pub const Id = enum {
96156 Return,
97157 Const,
......@@ -100,196 +160,231 @@ pub const Instruction = struct {
100160 CheckVoidStmt,
101161 Phi,
102162 Br,
163 AddImplicitReturnType,
103164 };
104165
105166 pub const Const = struct {
106167 base: Instruction,
168 params: Params,
107169
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;
170 const Params = struct {};
171
172 // Use Builder.buildConst* methods, or, after building a Const instruction,
173 // manually set the ir_val field.
174 const ir_val_init = IrVal.Init.Unknown;
175
176 pub fn dump(self: *const Const) void {
177 self.base.val.KnownValue.dump();
136178 }
137179
138 pub fn dump(inst: *const Const) void {
139 inst.base.val.Known.dump();
180 pub fn hasSideEffects(self: *const Const) bool {
181 return false;
182 }
183
184 pub fn analyze(self: *const Const, ira: *Analyze) !*Instruction {
185 const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{});
186 new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() };
187 return new_inst;
140188 }
141189 };
142190
143191 pub const Return = struct {
144192 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;
193 params: Params,
194
195 const Params = struct {
196 return_value: *Instruction,
197 };
198
199 const ir_val_init = IrVal.Init.NoReturn;
200
201 pub fn dump(self: *const Return) void {
202 std.debug.warn("#{}", self.params.return_value.debug_id);
161203 }
162204
163 pub fn dump(inst: *const Return) void {
164 std.debug.warn("#{}", inst.return_value.debug_id);
205 pub fn hasSideEffects(self: *const Return) bool {
206 return true;
207 }
208
209 pub fn analyze(self: *const Return, ira: *Analyze) !*Instruction {
210 const value = try self.params.return_value.getAsParam();
211 const casted_value = try ira.implicitCast(value, ira.explicit_return_type);
212
213 // TODO detect returning local variable address
214
215 return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
165216 }
166217 };
167218
168219 pub const Ref = struct {
169220 base: Instruction,
170 target: *Instruction,
171 mut: Mut,
172 volatility: Volatility,
221 params: Params,
173222
174 pub fn build(
175 irb: *Builder,
176 scope: *Scope,
223 const Params = struct {
177224 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 },
225 mut: Type.Pointer.Mut,
226 volatility: Type.Pointer.Vol,
227 };
228
229 const ir_val_init = IrVal.Init.Unknown;
230
231 pub fn dump(inst: *const Ref) void {}
232
233 pub fn hasSideEffects(inst: *const Ref) bool {
234 return false;
235 }
236
237 pub fn analyze(self: *const Ref, ira: *Analyze) !*Instruction {
238 const target = try self.params.target.getAsParam();
239
240 if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
241 return ira.getCompTimeRef(
242 val,
243 Value.Ptr.Mut.CompTimeConst,
244 self.params.mut,
245 self.params.volatility,
246 val.typeof.getAbiAlignment(ira.irb.module),
247 );
248 }
249
250 const new_inst = try ira.irb.build(Ref, self.base.scope, self.base.span, Params{
189251 .target = target,
190 .mut = mut,
191 .volatility = volatility,
252 .mut = self.params.mut,
253 .volatility = self.params.volatility,
192254 });
193 irb.next_debug_id += 1;
194 try irb.current_basic_block.instruction_list.append(&inst.base);
195 return &inst.base;
255 const elem_type = target.getKnownType();
256 const ptr_type = Type.Pointer.get(
257 ira.irb.module,
258 elem_type,
259 self.params.mut,
260 self.params.volatility,
261 Type.Pointer.Size.One,
262 elem_type.getAbiAlignment(ira.irb.module),
263 );
264 // TODO: potentially set the hint that this is a stack pointer. But it might not be - this
265 // could be a ref of a global, for example
266 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
267 // TODO potentially add an alloca entry here
268 return new_inst;
196269 }
197
198 pub fn dump(inst: *const Ref) void {}
199270 };
200271
201272 pub const DeclVar = struct {
202273 base: Instruction,
203 variable: *Variable,
274 params: Params,
275
276 const Params = struct {
277 variable: *Variable,
278 };
279
280 const ir_val_init = IrVal.Init.Unknown;
204281
205282 pub fn dump(inst: *const DeclVar) void {}
283
284 pub fn hasSideEffects(inst: *const DeclVar) bool {
285 return true;
286 }
287
288 pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Instruction {
289 return error.Unimplemented; // TODO
290 }
206291 };
207292
208293 pub const CheckVoidStmt = struct {
209294 base: Instruction,
210 target: *Instruction,
295 params: Params,
211296
212 pub fn build(
213 irb: *Builder,
214 scope: *Scope,
297 const Params = struct {
215298 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 }
299 };
300
301 const ir_val_init = IrVal.Init.Unknown;
231302
232303 pub fn dump(inst: *const CheckVoidStmt) void {}
304
305 pub fn hasSideEffects(inst: *const CheckVoidStmt) bool {
306 return true;
307 }
308
309 pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Instruction {
310 return error.Unimplemented; // TODO
311 }
233312 };
234313
235314 pub const Phi = struct {
236315 base: Instruction,
237 incoming_blocks: []*BasicBlock,
238 incoming_values: []*Instruction,
316 params: Params,
239317
240 pub fn build(
241 irb: *Builder,
242 scope: *Scope,
318 const Params = struct {
243319 incoming_blocks: []*BasicBlock,
244320 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 }
321 };
322
323 const ir_val_init = IrVal.Init.Unknown;
261324
262325 pub fn dump(inst: *const Phi) void {}
326
327 pub fn hasSideEffects(inst: *const Phi) bool {
328 return false;
329 }
330
331 pub fn analyze(self: *const Phi, ira: *Analyze) !*Instruction {
332 return error.Unimplemented; // TODO
333 }
263334 };
264335
265336 pub const Br = struct {
266337 base: Instruction,
267 dest_block: *BasicBlock,
268 is_comptime: *Instruction,
338 params: Params,
269339
270 pub fn build(
271 irb: *Builder,
272 scope: *Scope,
340 const Params = struct {
273341 dest_block: *BasicBlock,
274342 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 }
343 };
344
345 const ir_val_init = IrVal.Init.NoReturn;
291346
292347 pub fn dump(inst: *const Br) void {}
348
349 pub fn hasSideEffects(inst: *const Br) bool {
350 return true;
351 }
352
353 pub fn analyze(self: *const Br, ira: *Analyze) !*Instruction {
354 return error.Unimplemented; // TODO
355 }
356 };
357
358 pub const AddImplicitReturnType = struct {
359 base: Instruction,
360 params: Params,
361
362 pub const Params = struct {
363 target: *Instruction,
364 };
365
366 const ir_val_init = IrVal.Init.Unknown;
367
368 pub fn dump(inst: *const AddImplicitReturnType) void {
369 std.debug.warn("#{}", inst.params.target.debug_id);
370 }
371
372 pub fn hasSideEffects(inst: *const AddImplicitReturnType) bool {
373 return true;
374 }
375
376 pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction {
377 const target = try self.params.target.getAsParam();
378
379 try ira.src_implicit_return_type_list.append(target);
380
381 return ira.irb.build(
382 AddImplicitReturnType,
383 self.base.scope,
384 self.base.span,
385 Params{ .target = target },
386 );
387 }
293388 };
294389};
295390
......@@ -303,16 +398,31 @@ pub const BasicBlock = struct {
303398 debug_id: usize,
304399 scope: *Scope,
305400 instruction_list: std.ArrayList(*Instruction),
401 ref_instruction: ?*Instruction,
402
403 /// the basic block that is derived from this one in analysis
404 child: ?*BasicBlock,
405
406 /// the basic block that this one derives from in analysis
407 parent: ?*BasicBlock,
306408
307409 pub fn ref(self: *BasicBlock) void {
308410 self.ref_count += 1;
309411 }
412
413 pub fn linkToParent(self: *BasicBlock, parent: *BasicBlock) void {
414 assert(self.parent == null);
415 assert(parent.child == null);
416 self.parent = parent;
417 parent.child = self;
418 }
310419};
311420
312421/// Stuff that survives longer than Builder
313422pub const Code = struct {
314423 basic_block_list: std.ArrayList(*BasicBlock),
315424 arena: std.heap.ArenaAllocator,
425 return_type: ?*Type,
316426
317427 /// allocator is module.a()
318428 pub fn destroy(self: *Code, allocator: *Allocator) void {
......@@ -341,15 +451,13 @@ pub const Builder = struct {
341451 parsed_file: *ParsedFile,
342452 is_comptime: bool,
343453
344 pub const Error = error{
345 OutOfMemory,
346 Unimplemented,
347 };
454 pub const Error = Analyze.Error;
348455
349456 pub fn init(module: *Module, parsed_file: *ParsedFile) !Builder {
350457 const code = try module.a().create(Code{
351458 .basic_block_list = undefined,
352459 .arena = std.heap.ArenaAllocator.init(module.a()),
460 .return_type = null,
353461 });
354462 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
355463 errdefer code.destroy(module.a());
......@@ -381,6 +489,9 @@ pub const Builder = struct {
381489 .debug_id = self.next_debug_id,
382490 .scope = scope,
383491 .instruction_list = std.ArrayList(*Instruction).init(self.arena()),
492 .child = null,
493 .parent = null,
494 .ref_instruction = null,
384495 });
385496 self.next_debug_id += 1;
386497 return basic_block;
......@@ -490,14 +601,18 @@ pub const Builder = struct {
490601
491602 if (block.statements.len == 0) {
492603 // {}
493 return Instruction.Const.buildVoid(irb, child_scope, false);
604 return irb.buildConstVoid(child_scope, Span.token(block.lbrace), false);
494605 }
495606
496607 if (block.label) |label| {
497608 block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena());
498609 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());
499610 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));
611 block_scope.is_comptime = try irb.buildConstBool(
612 parent_scope,
613 Span.token(block.lbrace),
614 irb.isCompTime(parent_scope),
615 );
501616 }
502617
503618 var is_continuation_unreachable = false;
......@@ -530,10 +645,15 @@ pub const Builder = struct {
530645
531646 if (statement_value.cast(Instruction.DeclVar)) |decl_var| {
532647 // variable declarations start a new scope
533 child_scope = decl_var.variable.child_scope;
648 child_scope = decl_var.params.variable.child_scope;
534649 } else if (!is_continuation_unreachable) {
535650 // this statement's value must be void
536 _ = Instruction.CheckVoidStmt.build(irb, child_scope, statement_value);
651 _ = irb.build(
652 Instruction.CheckVoidStmt,
653 child_scope,
654 statement_value.span,
655 Instruction.CheckVoidStmt.Params{ .target = statement_value },
656 );
537657 }
538658 }
539659
......@@ -544,37 +664,34 @@ pub const Builder = struct {
544664 }
545665
546666 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 );
667 return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{
668 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
669 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
670 });
553671 }
554672
555673 if (block.label) |label| {
556674 try block_scope.incoming_blocks.append(irb.current_basic_block);
557675 try block_scope.incoming_values.append(
558 try Instruction.Const.buildVoid(irb, parent_scope, true),
676 try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true),
559677 );
560678 _ = 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();
679
680 _ = try irb.buildGen(Instruction.Br, parent_scope, Span.token(block.rbrace), Instruction.Br.Params{
681 .dest_block = block_scope.end_block,
682 .is_comptime = block_scope.is_comptime,
683 });
684
567685 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 );
686
687 return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{
688 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
689 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
690 });
574691 }
575692
576693 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
577 return try Instruction.Const.buildVoid(irb, child_scope, true);
694 return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
578695 }
579696
580697 fn genDefersForBlock(
......@@ -603,7 +720,12 @@ pub const Builder = struct {
603720 if (instruction.isNoReturn()) {
604721 is_noreturn = true;
605722 } else {
606 _ = Instruction.CheckVoidStmt.build(irb, &defer_expr_scope.base, instruction);
723 _ = try irb.build(
724 Instruction.CheckVoidStmt,
725 &defer_expr_scope.base,
726 Span.token(defer_expr_scope.expr_node.lastToken()),
727 Instruction.CheckVoidStmt.Params{ .target = instruction },
728 );
607729 }
608730 }
609731 },
......@@ -626,7 +748,11 @@ pub const Builder = struct {
626748 LVal.Ptr => {
627749 // We needed a pointer to a value, but we got a value. So we create
628750 // an instruction which just makes a const pointer of it.
629 return Instruction.Ref.build(irb, scope, instruction, Mut.Const, Volatility.NonVolatile);
751 return irb.build(Instruction.Ref, scope, instruction.span, Instruction.Ref.Params{
752 .target = instruction,
753 .mut = Type.Pointer.Mut.Const,
754 .volatility = Type.Pointer.Vol.Non,
755 });
630756 },
631757 }
632758 }
......@@ -634,9 +760,218 @@ pub const Builder = struct {
634760 fn arena(self: *Builder) *Allocator {
635761 return &self.code.arena.allocator;
636762 }
763
764 fn buildExtra(
765 self: *Builder,
766 comptime I: type,
767 scope: *Scope,
768 span: Span,
769 params: I.Params,
770 is_generated: bool,
771 ) !*Instruction {
772 const inst = try self.arena().create(I{
773 .base = Instruction{
774 .id = Instruction.typeToId(I),
775 .is_generated = is_generated,
776 .scope = scope,
777 .debug_id = self.next_debug_id,
778 .val = switch (I.ir_val_init) {
779 IrVal.Init.Unknown => IrVal.Unknown,
780 IrVal.Init.NoReturn => IrVal{ .KnownValue = &Value.NoReturn.get(self.module).base },
781 IrVal.Init.Void => IrVal{ .KnownValue = &Value.Void.get(self.module).base },
782 },
783 .ref_count = 0,
784 .span = span,
785 .child = null,
786 .parent = null,
787 },
788 .params = params,
789 });
790
791 // Look at the params and ref() other instructions
792 comptime var i = 0;
793 inline while (i < @memberCount(I.Params)) : (i += 1) {
794 const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));
795 switch (FieldType) {
796 *Instruction => @field(inst.params, @memberName(I.Params, i)).ref_count += 1,
797 ?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref_count += 1,
798 else => {},
799 }
800 }
801
802 self.next_debug_id += 1;
803 try self.current_basic_block.instruction_list.append(&inst.base);
804 return &inst.base;
805 }
806
807 fn build(
808 self: *Builder,
809 comptime I: type,
810 scope: *Scope,
811 span: Span,
812 params: I.Params,
813 ) !*Instruction {
814 return self.buildExtra(I, scope, span, params, false);
815 }
816
817 fn buildGen(
818 self: *Builder,
819 comptime I: type,
820 scope: *Scope,
821 span: Span,
822 params: I.Params,
823 ) !*Instruction {
824 return self.buildExtra(I, scope, span, params, true);
825 }
826
827 fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Instruction {
828 const inst = try self.build(Instruction.Const, scope, span, Instruction.Const.Params{});
829 inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.module, x).base };
830 return inst;
831 }
832
833 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Instruction {
834 const inst = try self.buildExtra(Instruction.Const, scope, span, Instruction.Const.Params{}, is_generated);
835 inst.val = IrVal{ .KnownValue = &Value.Void.get(self.module).base };
836 return inst;
837 }
838};
839
840const Analyze = struct {
841 irb: Builder,
842 old_bb_index: usize,
843 const_predecessor_bb: ?*BasicBlock,
844 parent_basic_block: *BasicBlock,
845 instruction_index: usize,
846 src_implicit_return_type_list: std.ArrayList(*Instruction),
847 explicit_return_type: ?*Type,
848
849 pub const Error = error{
850 /// This is only for when we have already reported a compile error. It is the poison value.
851 SemanticAnalysisFailed,
852
853 /// This is a placeholder - it is useful to use instead of panicking but once the compiler is
854 /// done this error code will be removed.
855 Unimplemented,
856
857 OutOfMemory,
858 };
859
860 pub fn init(module: *Module, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze {
861 var irb = try Builder.init(module, parsed_file);
862 errdefer irb.abort();
863
864 return Analyze{
865 .irb = irb,
866 .old_bb_index = 0,
867 .const_predecessor_bb = null,
868 .parent_basic_block = undefined, // initialized with startBasicBlock
869 .instruction_index = undefined, // initialized with startBasicBlock
870 .src_implicit_return_type_list = std.ArrayList(*Instruction).init(irb.arena()),
871 .explicit_return_type = explicit_return_type,
872 };
873 }
874
875 pub fn abort(self: *Analyze) void {
876 self.irb.abort();
877 }
878
879 pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Instruction) !*BasicBlock {
880 if (old_bb.child) |child| {
881 if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction)
882 return child;
883 }
884
885 const new_bb = try self.irb.createBasicBlock(old_bb.scope, old_bb.name_hint);
886 new_bb.linkToParent(old_bb);
887 new_bb.ref_instruction = ref_old_instruction;
888 return new_bb;
889 }
890
891 pub fn startBasicBlock(self: *Analyze, old_bb: *BasicBlock, const_predecessor_bb: ?*BasicBlock) void {
892 self.instruction_index = 0;
893 self.parent_basic_block = old_bb;
894 self.const_predecessor_bb = const_predecessor_bb;
895 }
896
897 pub fn finishBasicBlock(ira: *Analyze, old_code: *Code) !void {
898 try ira.irb.code.basic_block_list.append(ira.irb.current_basic_block);
899 ira.instruction_index += 1;
900
901 while (ira.instruction_index < ira.parent_basic_block.instruction_list.len) {
902 const next_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);
903
904 if (!next_instruction.is_generated) {
905 try ira.addCompileError(next_instruction.span, "unreachable code");
906 break;
907 }
908 ira.instruction_index += 1;
909 }
910
911 ira.old_bb_index += 1;
912
913 var need_repeat = true;
914 while (true) {
915 while (ira.old_bb_index < old_code.basic_block_list.len) {
916 const old_bb = old_code.basic_block_list.at(ira.old_bb_index);
917 const new_bb = old_bb.child orelse {
918 ira.old_bb_index += 1;
919 continue;
920 };
921 if (new_bb.instruction_list.len != 0) {
922 ira.old_bb_index += 1;
923 continue;
924 }
925 ira.irb.current_basic_block = new_bb;
926
927 ira.startBasicBlock(old_bb, null);
928 return;
929 }
930 if (!need_repeat)
931 return;
932 need_repeat = false;
933 ira.old_bb_index = 0;
934 continue;
935 }
936 }
937
938 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
939 return self.irb.module.addCompileError(self.irb.parsed_file, span, fmt, args);
940 }
941
942 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Instruction) Analyze.Error!*Type {
943 // TODO actual implementation
944 return &Type.Void.get(self.irb.module).base;
945 }
946
947 fn implicitCast(self: *Analyze, target: *Instruction, optional_dest_type: ?*Type) Analyze.Error!*Instruction {
948 const dest_type = optional_dest_type orelse return target;
949 @panic("TODO implicitCast");
950 }
951
952 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Instruction) ?*Value {
953 @panic("TODO getCompTimeValOrNullUndefOk");
954 }
955
956 fn getCompTimeRef(
957 self: *Analyze,
958 value: *Value,
959 ptr_mut: Value.Ptr.Mut,
960 mut: Type.Pointer.Mut,
961 volatility: Type.Pointer.Vol,
962 ptr_align: u32,
963 ) Analyze.Error!*Instruction {
964 @panic("TODO getCompTimeRef");
965 }
637966};
638967
639pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_file: *ParsedFile) !*Code {
968pub async fn gen(
969 module: *Module,
970 body_node: *ast.Node,
971 scope: *Scope,
972 end_span: Span,
973 parsed_file: *ParsedFile,
974) !*Code {
640975 var irb = try Builder.init(module, parsed_file);
641976 errdefer irb.abort();
642977
......@@ -646,8 +981,61 @@ pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_fi
646981
647982 const result = try irb.genNode(body_node, scope, LVal.None);
648983 if (!result.isNoReturn()) {
649 (try Instruction.Return.build(&irb, scope, result)).setGenerated();
984 _ = irb.buildGen(
985 Instruction.AddImplicitReturnType,
986 scope,
987 end_span,
988 Instruction.AddImplicitReturnType.Params{ .target = result },
989 );
990 _ = irb.buildGen(
991 Instruction.Return,
992 scope,
993 end_span,
994 Instruction.Return.Params{ .return_value = result },
995 );
650996 }
651997
652998 return irb.finish();
653999}
1000
1001pub async fn analyze(module: *Module, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code {
1002 var ira = try Analyze.init(module, parsed_file, expected_type);
1003 errdefer ira.abort();
1004
1005 const old_entry_bb = old_code.basic_block_list.at(0);
1006
1007 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
1008 new_entry_bb.ref();
1009
1010 ira.irb.current_basic_block = new_entry_bb;
1011
1012 ira.startBasicBlock(old_entry_bb, null);
1013
1014 while (ira.old_bb_index < old_code.basic_block_list.len) {
1015 const old_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);
1016
1017 if (old_instruction.ref_count == 0 and !old_instruction.hasSideEffects()) {
1018 ira.instruction_index += 1;
1019 continue;
1020 }
1021
1022 const return_inst = try old_instruction.analyze(&ira);
1023 // Note: if we ever modify the above to handle error.CompileError by continuing analysis,
1024 // then here we want to check if ira.isCompTime() and return early if true
1025
1026 if (return_inst.isNoReturn()) {
1027 try ira.finishBasicBlock(old_code);
1028 continue;
1029 }
1030
1031 ira.instruction_index += 1;
1032 }
1033
1034 if (ira.src_implicit_return_type_list.len == 0) {
1035 ira.irb.code.return_type = &Type.NoReturn.get(module).base;
1036 return ira.irb.finish();
1037 }
1038
1039 ira.irb.code.return_type = try ira.resolvePeerTypes(expected_type, ira.src_implicit_return_type_list.toSliceConst());
1040 return ira.irb.finish();
1041}
src-self-hosted/main.zig+1-1
......@@ -497,7 +497,7 @@ async fn processBuildEvents(module: *Module, color: errmsg.Color) void {
497497 },
498498 Module.Event.Error => |err| {
499499 std.debug.warn("build failed: {}\n", @errorName(err));
500 @panic("TODO error return trace");
500 os.exit(1);
501501 },
502502 Module.Event.Fail => |msgs| {
503503 for (msgs) |msg| {
src-self-hosted/module.zig+62-23
......@@ -24,6 +24,7 @@ const Visib = @import("visib.zig").Visib;
2424const ParsedFile = @import("parsed_file.zig").ParsedFile;
2525const Value = @import("value.zig").Value;
2626const Type = Value.Type;
27const Span = errmsg.Span;
2728
2829pub const Module = struct {
2930 loop: *event.Loop,
......@@ -148,13 +149,14 @@ pub const Module = struct {
148149 Overflow,
149150 NotSupported,
150151 BufferTooSmall,
151 Unimplemented,
152 Unimplemented, // TODO remove this one
153 SemanticAnalysisFailed, // TODO remove this one
152154 };
153155
154156 pub const Event = union(enum) {
155157 Ok,
156 Fail: []*errmsg.Msg,
157158 Error: BuildError,
159 Fail: []*errmsg.Msg,
158160 };
159161
160162 pub const DarwinVersionMin = union(enum) {
......@@ -413,21 +415,32 @@ pub const Module = struct {
413415 while (true) {
414416 // TODO directly awaiting async should guarantee memory allocation elision
415417 // TODO also async before suspending should guarantee memory allocation elision
416 (await (async self.addRootSrc() catch unreachable)) catch |err| {
417 await (async self.events.put(Event{ .Error = err }) catch unreachable);
418 return;
419 };
418 const build_result = await (async self.addRootSrc() catch unreachable);
419
420 // this makes a handy error return trace and stack trace in debug mode
421 if (std.debug.runtime_safety) {
422 build_result catch unreachable;
423 }
424
420425 const compile_errors = blk: {
421426 const held = await (async self.compile_errors.acquire() catch unreachable);
422427 defer held.release();
423428 break :blk held.value.toOwnedSlice();
424429 };
425430
426 if (compile_errors.len == 0) {
427 await (async self.events.put(Event.Ok) catch unreachable);
428 } else {
429 await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable);
431 if (build_result) |_| {
432 if (compile_errors.len == 0) {
433 await (async self.events.put(Event.Ok) catch unreachable);
434 } else {
435 await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable);
436 }
437 } else |err| {
438 // if there's an error then the compile errors have dangling references
439 self.a().free(compile_errors);
440
441 await (async self.events.put(Event{ .Error = err }) catch unreachable);
430442 }
443
431444 // for now we stop after 1
432445 return;
433446 }
......@@ -477,7 +490,7 @@ pub const Module = struct {
477490 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
478491
479492 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
480 try self.addCompileError(parsed_file, errmsg.Span{
493 try self.addCompileError(parsed_file, Span{
481494 .first = fn_proto.fn_token,
482495 .last = fn_proto.fn_token + 1,
483496 }, "missing function name");
......@@ -518,27 +531,23 @@ pub const Module = struct {
518531 }
519532 }
520533
521 fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: errmsg.Span, comptime fmt: []const u8, args: ...) !void {
534 fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void {
522535 const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);
523536 errdefer self.loop.allocator.free(text);
524537
525 try self.build_group.call(addCompileErrorAsync, self, parsed_file, span.first, span.last, text);
538 try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text);
526539 }
527540
528541 async fn addCompileErrorAsync(
529542 self: *Module,
530543 parsed_file: *ParsedFile,
531 first_token: ast.TokenIndex,
532 last_token: ast.TokenIndex,
544 span: Span,
533545 text: []u8,
534546 ) !void {
535547 const msg = try self.loop.allocator.create(errmsg.Msg{
536548 .path = parsed_file.realpath,
537549 .text = text,
538 .span = errmsg.Span{
539 .first = first_token,
540 .last = last_token,
541 },
550 .span = span,
542551 .tree = &parsed_file.tree,
543552 });
544553 errdefer self.loop.allocator.destroy(msg);
......@@ -624,6 +633,7 @@ pub async fn resolveDecl(module: *Module, decl: *Decl) !void {
624633 if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
625634 decl.resolution.data = await (async generateDecl(module, decl) catch unreachable);
626635 decl.resolution.resolve();
636 return decl.resolution.data;
627637 } else {
628638 return (await (async decl.resolution.get() catch unreachable)).*;
629639 }
......@@ -655,12 +665,41 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void {
655665
656666 fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
657667
658 const code = try await (async ir.gen(
668 const unanalyzed_code = (await (async ir.gen(
659669 module,
660670 body_node,
661671 &fndef_scope.base,
672 Span.token(body_node.lastToken()),
673 fn_decl.base.parsed_file,
674 ) catch unreachable)) catch |err| switch (err) {
675 // This poison value should not cause the errdefers to run. It simply means
676 // that self.compile_errors is populated.
677 error.SemanticAnalysisFailed => return {},
678 else => return err,
679 };
680 defer unanalyzed_code.destroy(module.a());
681
682 if (module.verbose_ir) {
683 std.debug.warn("unanalyzed:\n");
684 unanalyzed_code.dump();
685 }
686
687 const analyzed_code = (await (async ir.analyze(
688 module,
662689 fn_decl.base.parsed_file,
663 ) catch unreachable);
664 //code.dump();
665 //try await (async irAnalyze(module, func) catch unreachable);
690 unanalyzed_code,
691 null,
692 ) catch unreachable)) catch |err| switch (err) {
693 // This poison value should not cause the errdefers to run. It simply means
694 // that self.compile_errors is populated.
695 error.SemanticAnalysisFailed => return {},
696 else => return err,
697 };
698 defer analyzed_code.destroy(module.a());
699
700 if (module.verbose_ir) {
701 std.debug.warn("analyzed:\n");
702 analyzed_code.dump();
703 }
704 // TODO now render to LLVM module
666705}
src-self-hosted/type.zig+33
......@@ -39,6 +39,14 @@ pub const Type = struct {
3939 }
4040 }
4141
42 pub fn dump(base: *const Type) void {
43 std.debug.warn("{}", @tagName(base.id));
44 }
45
46 pub fn getAbiAlignment(base: *Type, module: *Module) u32 {
47 @panic("TODO getAbiAlignment");
48 }
49
4250 pub const Struct = struct {
4351 base: Type,
4452 decls: *Scope.Decls,
......@@ -143,10 +151,35 @@ pub const Type = struct {
143151 };
144152 pub const Pointer = struct {
145153 base: Type,
154 mut: Mut,
155 vol: Vol,
156 size: Size,
157 alignment: u32,
158
159 pub const Mut = enum {
160 Mut,
161 Const,
162 };
163 pub const Vol = enum {
164 Non,
165 Volatile,
166 };
167 pub const Size = builtin.TypeInfo.Pointer.Size;
146168
147169 pub fn destroy(self: *Pointer, module: *Module) void {
148170 module.a().destroy(self);
149171 }
172
173 pub fn get(
174 module: *Module,
175 elem_type: *Type,
176 mut: Mut,
177 vol: Vol,
178 size: Size,
179 alignment: u32,
180 ) *Pointer {
181 @panic("TODO get pointer");
182 }
150183 };
151184 pub const Array = struct {
152185 base: Type,
src-self-hosted/value.zig+21
......@@ -24,10 +24,16 @@ pub const Value = struct {
2424 Id.Void => @fieldParentPtr(Void, "base", base).destroy(module),
2525 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module),
2626 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module),
27 Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(module),
2728 }
2829 }
2930 }
3031
32 pub fn getRef(base: *Value) *Value {
33 base.ref();
34 return base;
35 }
36
3137 pub fn dump(base: *const Value) void {
3238 std.debug.warn("{}", @tagName(base.id));
3339 }
......@@ -38,6 +44,7 @@ pub const Value = struct {
3844 Void,
3945 Bool,
4046 NoReturn,
47 Ptr,
4148 };
4249
4350 pub const Type = @import("type.zig").Type;
......@@ -122,4 +129,18 @@ pub const Value = struct {
122129 module.a().destroy(self);
123130 }
124131 };
132
133 pub const Ptr = struct {
134 base: Value,
135
136 pub const Mut = enum {
137 CompTimeConst,
138 CompTimeVar,
139 RunTime,
140 };
141
142 pub fn destroy(self: *Ptr, module: *Module) void {
143 module.a().destroy(self);
144 }
145 };
125146};
std/event/loop.zig+15
......@@ -382,6 +382,21 @@ pub const Loop = struct {
382382 return async<self.allocator> S.asyncFunc(self, &handle, args);
383383 }
384384
385 /// Awaiting a yield lets the event loop run, starting any unstarted async operations.
386 /// Note that async operations automatically start when a function yields for any other reason,
387 /// for example, when async I/O is performed. This function is intended to be used only when
388 /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O
389 /// is performed.
390 pub async fn yield(self: *Loop) void {
391 suspend |p| {
392 var my_tick_node = Loop.NextTickNode{
393 .next = undefined,
394 .data = p,
395 };
396 loop.onNextTick(&my_tick_node);
397 }
398 }
399
385400 fn workerRun(self: *Loop) void {
386401 start_over: while (true) {
387402 if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {