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 {...@@ -14,6 +14,13 @@ pub const Color = enum {
14pub const Span = struct {14pub const Span = struct {
15 first: ast.TokenIndex,15 first: ast.TokenIndex,
16 last: ast.TokenIndex,16 last: ast.TokenIndex,
17
18 pub fn token(i: TokenIndex) Span {
19 return Span {
20 .first = i,
21 .last = i,
22 };
23 }
17};24};
1825
19pub const Msg = struct {26pub const Msg = struct {
src-self-hosted/ir.zig+569-181
...@@ -9,31 +9,34 @@ const Type = Value.Type;...@@ -9,31 +9,34 @@ const Type = Value.Type;
9const assert = std.debug.assert;9const assert = std.debug.assert;
10const Token = std.zig.Token;10const Token = std.zig.Token;
11const ParsedFile = @import("parsed_file.zig").ParsedFile;11const ParsedFile = @import("parsed_file.zig").ParsedFile;
12const Span = @import("errmsg.zig").Span;
1213
13pub const LVal = enum {14pub const LVal = enum {
14 None,15 None,
15 Ptr,16 Ptr,
16};17};
1718
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) {19pub const IrVal = union(enum) {
29 Unknown,20 Unknown,
30 Known: *Value,21 KnownType: *Type,
22 KnownValue: *Value,
23
24 const Init = enum {
25 Unknown,
26 NoReturn,
27 Void,
28 };
3129
32 pub fn dump(self: IrVal) void {30 pub fn dump(self: IrVal) void {
33 switch (self) {31 switch (self) {
34 IrVal.Unknown => std.debug.warn("Unknown"),32 IrVal.Unknown => typeof.dump(),
35 IrVal.Known => |value| {33 IrVal.KnownType => |typeof| {
36 std.debug.warn("Known(");34 std.debug.warn("KnownType(");
35 typeof.dump();
36 std.debug.warn(")");
37 },
38 IrVal.KnownValue => |value| {
39 std.debug.warn("KnownValue(");
37 value.dump();40 value.dump();
38 std.debug.warn(")");41 std.debug.warn(")");
39 },42 },
...@@ -46,10 +49,18 @@ pub const Instruction = struct {...@@ -46,10 +49,18 @@ pub const Instruction = struct {
46 scope: *Scope,49 scope: *Scope,
47 debug_id: usize,50 debug_id: usize,
48 val: IrVal,51 val: IrVal,
52 ref_count: usize,
53 span: Span,
4954
50 /// true if this instruction was generated by zig and not from user code55 /// true if this instruction was generated by zig and not from user code
51 is_generated: bool,56 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
53 pub fn cast(base: *Instruction, comptime T: type) ?*T {64 pub fn cast(base: *Instruction, comptime T: type) ?*T {
54 if (base.id == comptime typeToId(T)) {65 if (base.id == comptime typeToId(T)) {
55 return @fieldParentPtr(T, "base", base);66 return @fieldParentPtr(T, "base", base);
...@@ -81,6 +92,47 @@ pub const Instruction = struct {...@@ -81,6 +92,47 @@ pub const Instruction = struct {
81 unreachable;92 unreachable;
82 }93 }
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
84 pub fn setGenerated(base: *Instruction) void {136 pub fn setGenerated(base: *Instruction) void {
85 base.is_generated = true;137 base.is_generated = true;
86 }138 }
...@@ -88,10 +140,18 @@ pub const Instruction = struct {...@@ -88,10 +140,18 @@ pub const Instruction = struct {
88 pub fn isNoReturn(base: *const Instruction) bool {140 pub fn isNoReturn(base: *const Instruction) bool {
89 switch (base.val) {141 switch (base.val) {
90 IrVal.Unknown => return false,142 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,
92 }145 }
93 }146 }
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
95 pub const Id = enum {155 pub const Id = enum {
96 Return,156 Return,
97 Const,157 Const,
...@@ -100,196 +160,231 @@ pub const Instruction = struct {...@@ -100,196 +160,231 @@ pub const Instruction = struct {
100 CheckVoidStmt,160 CheckVoidStmt,
101 Phi,161 Phi,
102 Br,162 Br,
163 AddImplicitReturnType,
103 };164 };
104165
105 pub const Const = struct {166 pub const Const = struct {
106 base: Instruction,167 base: Instruction,
168 params: Params,
107169
108 pub fn buildBool(irb: *Builder, scope: *Scope, val: bool) !*Instruction {170 const Params = struct {};
109 const inst = try irb.arena().create(Const{171
110 .base = Instruction{172 // Use Builder.buildConst* methods, or, after building a Const instruction,
111 .id = Instruction.Id.Const,173 // manually set the ir_val field.
112 .is_generated = false,174 const ir_val_init = IrVal.Init.Unknown;
113 .scope = scope,175
114 .debug_id = irb.next_debug_id,176 pub fn dump(self: *const Const) void {
115 .val = IrVal{ .Known = &Value.Bool.get(irb.module, val).base },177 self.base.val.KnownValue.dump();
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 }178 }
137179
138 pub fn dump(inst: *const Const) void {180 pub fn hasSideEffects(self: *const Const) bool {
139 inst.base.val.Known.dump();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;
140 }188 }
141 };189 };
142190
143 pub const Return = struct {191 pub const Return = struct {
144 base: Instruction,192 base: Instruction,
145 return_value: *Instruction,193 params: Params,
146194
147 pub fn build(irb: *Builder, scope: *Scope, return_value: *Instruction) !*Instruction {195 const Params = struct {
148 const inst = try irb.arena().create(Return{196 return_value: *Instruction,
149 .base = Instruction{197 };
150 .id = Instruction.Id.Return,198
151 .is_generated = false,199 const ir_val_init = IrVal.Init.NoReturn;
152 .scope = scope,200
153 .debug_id = irb.next_debug_id,201 pub fn dump(self: *const Return) void {
154 .val = IrVal{ .Known = &Value.Void.get(irb.module).base },202 std.debug.warn("#{}", self.params.return_value.debug_id);
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 }203 }
162204
163 pub fn dump(inst: *const Return) void {205 pub fn hasSideEffects(self: *const Return) bool {
164 std.debug.warn("#{}", inst.return_value.debug_id);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 });
165 }216 }
166 };217 };
167218
168 pub const Ref = struct {219 pub const Ref = struct {
169 base: Instruction,220 base: Instruction,
170 target: *Instruction,221 params: Params,
171 mut: Mut,
172 volatility: Volatility,
173222
174 pub fn build(223 const Params = struct {
175 irb: *Builder,
176 scope: *Scope,
177 target: *Instruction,224 target: *Instruction,
178 mut: Mut,225 mut: Type.Pointer.Mut,
179 volatility: Volatility,226 volatility: Type.Pointer.Vol,
180 ) !*Instruction {227 };
181 const inst = try irb.arena().create(Ref{228
182 .base = Instruction{229 const ir_val_init = IrVal.Init.Unknown;
183 .id = Instruction.Id.Ref,230
184 .is_generated = false,231 pub fn dump(inst: *const Ref) void {}
185 .scope = scope,232
186 .debug_id = irb.next_debug_id,233 pub fn hasSideEffects(inst: *const Ref) bool {
187 .val = IrVal.Unknown,234 return false;
188 },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{
189 .target = target,251 .target = target,
190 .mut = mut,252 .mut = self.params.mut,
191 .volatility = volatility,253 .volatility = self.params.volatility,
192 });254 });
193 irb.next_debug_id += 1;255 const elem_type = target.getKnownType();
194 try irb.current_basic_block.instruction_list.append(&inst.base);256 const ptr_type = Type.Pointer.get(
195 return &inst.base;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;
196 }269 }
197
198 pub fn dump(inst: *const Ref) void {}
199 };270 };
200271
201 pub const DeclVar = struct {272 pub const DeclVar = struct {
202 base: Instruction,273 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
205 pub fn dump(inst: *const DeclVar) void {}282 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 }
206 };291 };
207292
208 pub const CheckVoidStmt = struct {293 pub const CheckVoidStmt = struct {
209 base: Instruction,294 base: Instruction,
210 target: *Instruction,295 params: Params,
211296
212 pub fn build(297 const Params = struct {
213 irb: *Builder,
214 scope: *Scope,
215 target: *Instruction,298 target: *Instruction,
216 ) !*Instruction {299 };
217 const inst = try irb.arena().create(CheckVoidStmt{300
218 .base = Instruction{301 const ir_val_init = IrVal.Init.Unknown;
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 }
231302
232 pub fn dump(inst: *const CheckVoidStmt) void {}303 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 }
233 };312 };
234313
235 pub const Phi = struct {314 pub const Phi = struct {
236 base: Instruction,315 base: Instruction,
237 incoming_blocks: []*BasicBlock,316 params: Params,
238 incoming_values: []*Instruction,
239317
240 pub fn build(318 const Params = struct {
241 irb: *Builder,
242 scope: *Scope,
243 incoming_blocks: []*BasicBlock,319 incoming_blocks: []*BasicBlock,
244 incoming_values: []*Instruction,320 incoming_values: []*Instruction,
245 ) !*Instruction {321 };
246 const inst = try irb.arena().create(Phi{322
247 .base = Instruction{323 const ir_val_init = IrVal.Init.Unknown;
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 }
261324
262 pub fn dump(inst: *const Phi) void {}325 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 }
263 };334 };
264335
265 pub const Br = struct {336 pub const Br = struct {
266 base: Instruction,337 base: Instruction,
267 dest_block: *BasicBlock,338 params: Params,
268 is_comptime: *Instruction,
269339
270 pub fn build(340 const Params = struct {
271 irb: *Builder,
272 scope: *Scope,
273 dest_block: *BasicBlock,341 dest_block: *BasicBlock,
274 is_comptime: *Instruction,342 is_comptime: *Instruction,
275 ) !*Instruction {343 };
276 const inst = try irb.arena().create(Br{344
277 .base = Instruction{345 const ir_val_init = IrVal.Init.NoReturn;
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 }
291346
292 pub fn dump(inst: *const Br) void {}347 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 }
293 };388 };
294};389};
295390
...@@ -303,16 +398,31 @@ pub const BasicBlock = struct {...@@ -303,16 +398,31 @@ pub const BasicBlock = struct {
303 debug_id: usize,398 debug_id: usize,
304 scope: *Scope,399 scope: *Scope,
305 instruction_list: std.ArrayList(*Instruction),400 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
307 pub fn ref(self: *BasicBlock) void {409 pub fn ref(self: *BasicBlock) void {
308 self.ref_count += 1;410 self.ref_count += 1;
309 }411 }
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 }
310};419};
311420
312/// Stuff that survives longer than Builder421/// Stuff that survives longer than Builder
313pub const Code = struct {422pub const Code = struct {
314 basic_block_list: std.ArrayList(*BasicBlock),423 basic_block_list: std.ArrayList(*BasicBlock),
315 arena: std.heap.ArenaAllocator,424 arena: std.heap.ArenaAllocator,
425 return_type: ?*Type,
316426
317 /// allocator is module.a()427 /// allocator is module.a()
318 pub fn destroy(self: *Code, allocator: *Allocator) void {428 pub fn destroy(self: *Code, allocator: *Allocator) void {
...@@ -341,15 +451,13 @@ pub const Builder = struct {...@@ -341,15 +451,13 @@ pub const Builder = struct {
341 parsed_file: *ParsedFile,451 parsed_file: *ParsedFile,
342 is_comptime: bool,452 is_comptime: bool,
343453
344 pub const Error = error{454 pub const Error = Analyze.Error;
345 OutOfMemory,
346 Unimplemented,
347 };
348455
349 pub fn init(module: *Module, parsed_file: *ParsedFile) !Builder {456 pub fn init(module: *Module, parsed_file: *ParsedFile) !Builder {
350 const code = try module.a().create(Code{457 const code = try module.a().create(Code{
351 .basic_block_list = undefined,458 .basic_block_list = undefined,
352 .arena = std.heap.ArenaAllocator.init(module.a()),459 .arena = std.heap.ArenaAllocator.init(module.a()),
460 .return_type = null,
353 });461 });
354 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);462 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
355 errdefer code.destroy(module.a());463 errdefer code.destroy(module.a());
...@@ -381,6 +489,9 @@ pub const Builder = struct {...@@ -381,6 +489,9 @@ pub const Builder = struct {
381 .debug_id = self.next_debug_id,489 .debug_id = self.next_debug_id,
382 .scope = scope,490 .scope = scope,
383 .instruction_list = std.ArrayList(*Instruction).init(self.arena()),491 .instruction_list = std.ArrayList(*Instruction).init(self.arena()),
492 .child = null,
493 .parent = null,
494 .ref_instruction = null,
384 });495 });
385 self.next_debug_id += 1;496 self.next_debug_id += 1;
386 return basic_block;497 return basic_block;
...@@ -490,14 +601,18 @@ pub const Builder = struct {...@@ -490,14 +601,18 @@ pub const Builder = struct {
490601
491 if (block.statements.len == 0) {602 if (block.statements.len == 0) {
492 // {}603 // {}
493 return Instruction.Const.buildVoid(irb, child_scope, false);604 return irb.buildConstVoid(child_scope, Span.token(block.lbrace), false);
494 }605 }
495606
496 if (block.label) |label| {607 if (block.label) |label| {
497 block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena());608 block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena());
498 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());609 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());
499 block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd");610 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 );
501 }616 }
502617
503 var is_continuation_unreachable = false;618 var is_continuation_unreachable = false;
...@@ -530,10 +645,15 @@ pub const Builder = struct {...@@ -530,10 +645,15 @@ pub const Builder = struct {
530645
531 if (statement_value.cast(Instruction.DeclVar)) |decl_var| {646 if (statement_value.cast(Instruction.DeclVar)) |decl_var| {
532 // variable declarations start a new scope647 // variable declarations start a new scope
533 child_scope = decl_var.variable.child_scope;648 child_scope = decl_var.params.variable.child_scope;
534 } else if (!is_continuation_unreachable) {649 } else if (!is_continuation_unreachable) {
535 // this statement's value must be void650 // 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 );
537 }657 }
538 }658 }
539659
...@@ -544,37 +664,34 @@ pub const Builder = struct {...@@ -544,37 +664,34 @@ pub const Builder = struct {
544 }664 }
545665
546 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);666 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
547 return Instruction.Phi.build(667 return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{
548 irb,668 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
549 parent_scope,669 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
550 block_scope.incoming_blocks.toOwnedSlice(),670 });
551 block_scope.incoming_values.toOwnedSlice(),
552 );
553 }671 }
554672
555 if (block.label) |label| {673 if (block.label) |label| {
556 try block_scope.incoming_blocks.append(irb.current_basic_block);674 try block_scope.incoming_blocks.append(irb.current_basic_block);
557 try block_scope.incoming_values.append(675 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),
559 );677 );
560 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);678 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
561 (try Instruction.Br.build(679
562 irb,680 _ = try irb.buildGen(Instruction.Br, parent_scope, Span.token(block.rbrace), Instruction.Br.Params{
563 parent_scope,681 .dest_block = block_scope.end_block,
564 block_scope.end_block,682 .is_comptime = block_scope.is_comptime,
565 block_scope.is_comptime,683 });
566 )).setGenerated();684
567 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);685 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
568 return Instruction.Phi.build(686
569 irb,687 return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{
570 parent_scope,688 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
571 block_scope.incoming_blocks.toOwnedSlice(),689 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
572 block_scope.incoming_values.toOwnedSlice(),690 });
573 );
574 }691 }
575692
576 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);693 _ = 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);
578 }695 }
579696
580 fn genDefersForBlock(697 fn genDefersForBlock(
...@@ -603,7 +720,12 @@ pub const Builder = struct {...@@ -603,7 +720,12 @@ pub const Builder = struct {
603 if (instruction.isNoReturn()) {720 if (instruction.isNoReturn()) {
604 is_noreturn = true;721 is_noreturn = true;
605 } else {722 } 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 );
607 }729 }
608 }730 }
609 },731 },
...@@ -626,7 +748,11 @@ pub const Builder = struct {...@@ -626,7 +748,11 @@ pub const Builder = struct {
626 LVal.Ptr => {748 LVal.Ptr => {
627 // We needed a pointer to a value, but we got a value. So we create749 // We needed a pointer to a value, but we got a value. So we create
628 // an instruction which just makes a const pointer of it.750 // 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 });
630 },756 },
631 }757 }
632 }758 }
...@@ -634,9 +760,218 @@ pub const Builder = struct {...@@ -634,9 +760,218 @@ pub const Builder = struct {
634 fn arena(self: *Builder) *Allocator {760 fn arena(self: *Builder) *Allocator {
635 return &self.code.arena.allocator;761 return &self.code.arena.allocator;
636 }762 }
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 }
637};966};
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 {
640 var irb = try Builder.init(module, parsed_file);975 var irb = try Builder.init(module, parsed_file);
641 errdefer irb.abort();976 errdefer irb.abort();
642977
...@@ -646,8 +981,61 @@ pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_fi...@@ -646,8 +981,61 @@ pub async fn gen(module: *Module, body_node: *ast.Node, scope: *Scope, parsed_fi
646981
647 const result = try irb.genNode(body_node, scope, LVal.None);982 const result = try irb.genNode(body_node, scope, LVal.None);
648 if (!result.isNoReturn()) {983 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 );
650 }996 }
651997
652 return irb.finish();998 return irb.finish();
653}999}
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 {...@@ -497,7 +497,7 @@ async fn processBuildEvents(module: *Module, color: errmsg.Color) void {
497 },497 },
498 Module.Event.Error => |err| {498 Module.Event.Error => |err| {
499 std.debug.warn("build failed: {}\n", @errorName(err));499 std.debug.warn("build failed: {}\n", @errorName(err));
500 @panic("TODO error return trace");500 os.exit(1);
501 },501 },
502 Module.Event.Fail => |msgs| {502 Module.Event.Fail => |msgs| {
503 for (msgs) |msg| {503 for (msgs) |msg| {
src-self-hosted/module.zig+62-23
...@@ -24,6 +24,7 @@ const Visib = @import("visib.zig").Visib;...@@ -24,6 +24,7 @@ const Visib = @import("visib.zig").Visib;
24const ParsedFile = @import("parsed_file.zig").ParsedFile;24const ParsedFile = @import("parsed_file.zig").ParsedFile;
25const Value = @import("value.zig").Value;25const Value = @import("value.zig").Value;
26const Type = Value.Type;26const Type = Value.Type;
27const Span = errmsg.Span;
2728
28pub const Module = struct {29pub const Module = struct {
29 loop: *event.Loop,30 loop: *event.Loop,
...@@ -148,13 +149,14 @@ pub const Module = struct {...@@ -148,13 +149,14 @@ pub const Module = struct {
148 Overflow,149 Overflow,
149 NotSupported,150 NotSupported,
150 BufferTooSmall,151 BufferTooSmall,
151 Unimplemented,152 Unimplemented, // TODO remove this one
153 SemanticAnalysisFailed, // TODO remove this one
152 };154 };
153155
154 pub const Event = union(enum) {156 pub const Event = union(enum) {
155 Ok,157 Ok,
156 Fail: []*errmsg.Msg,
157 Error: BuildError,158 Error: BuildError,
159 Fail: []*errmsg.Msg,
158 };160 };
159161
160 pub const DarwinVersionMin = union(enum) {162 pub const DarwinVersionMin = union(enum) {
...@@ -413,21 +415,32 @@ pub const Module = struct {...@@ -413,21 +415,32 @@ pub const Module = struct {
413 while (true) {415 while (true) {
414 // TODO directly awaiting async should guarantee memory allocation elision416 // TODO directly awaiting async should guarantee memory allocation elision
415 // TODO also async before suspending should guarantee memory allocation elision417 // TODO also async before suspending should guarantee memory allocation elision
416 (await (async self.addRootSrc() catch unreachable)) catch |err| {418 const build_result = await (async self.addRootSrc() catch unreachable);
417 await (async self.events.put(Event{ .Error = err }) catch unreachable);419
418 return;420 // this makes a handy error return trace and stack trace in debug mode
419 };421 if (std.debug.runtime_safety) {
422 build_result catch unreachable;
423 }
424
420 const compile_errors = blk: {425 const compile_errors = blk: {
421 const held = await (async self.compile_errors.acquire() catch unreachable);426 const held = await (async self.compile_errors.acquire() catch unreachable);
422 defer held.release();427 defer held.release();
423 break :blk held.value.toOwnedSlice();428 break :blk held.value.toOwnedSlice();
424 };429 };
425430
426 if (compile_errors.len == 0) {431 if (build_result) |_| {
427 await (async self.events.put(Event.Ok) catch unreachable);432 if (compile_errors.len == 0) {
428 } else {433 await (async self.events.put(Event.Ok) catch unreachable);
429 await (async self.events.put(Event{ .Fail = compile_errors }) 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);
430 }442 }
443
431 // for now we stop after 1444 // for now we stop after 1
432 return;445 return;
433 }446 }
...@@ -477,7 +490,7 @@ pub const Module = struct {...@@ -477,7 +490,7 @@ pub const Module = struct {
477 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);490 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
478491
479 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {492 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{
481 .first = fn_proto.fn_token,494 .first = fn_proto.fn_token,
482 .last = fn_proto.fn_token + 1,495 .last = fn_proto.fn_token + 1,
483 }, "missing function name");496 }, "missing function name");
...@@ -518,27 +531,23 @@ pub const Module = struct {...@@ -518,27 +531,23 @@ pub const Module = struct {
518 }531 }
519 }532 }
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 {
522 const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);535 const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);
523 errdefer self.loop.allocator.free(text);536 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);
526 }539 }
527540
528 async fn addCompileErrorAsync(541 async fn addCompileErrorAsync(
529 self: *Module,542 self: *Module,
530 parsed_file: *ParsedFile,543 parsed_file: *ParsedFile,
531 first_token: ast.TokenIndex,544 span: Span,
532 last_token: ast.TokenIndex,
533 text: []u8,545 text: []u8,
534 ) !void {546 ) !void {
535 const msg = try self.loop.allocator.create(errmsg.Msg{547 const msg = try self.loop.allocator.create(errmsg.Msg{
536 .path = parsed_file.realpath,548 .path = parsed_file.realpath,
537 .text = text,549 .text = text,
538 .span = errmsg.Span{550 .span = span,
539 .first = first_token,
540 .last = last_token,
541 },
542 .tree = &parsed_file.tree,551 .tree = &parsed_file.tree,
543 });552 });
544 errdefer self.loop.allocator.destroy(msg);553 errdefer self.loop.allocator.destroy(msg);
...@@ -624,6 +633,7 @@ pub async fn resolveDecl(module: *Module, decl: *Decl) !void {...@@ -624,6 +633,7 @@ pub async fn resolveDecl(module: *Module, decl: *Decl) !void {
624 if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {633 if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
625 decl.resolution.data = await (async generateDecl(module, decl) catch unreachable);634 decl.resolution.data = await (async generateDecl(module, decl) catch unreachable);
626 decl.resolution.resolve();635 decl.resolution.resolve();
636 return decl.resolution.data;
627 } else {637 } else {
628 return (await (async decl.resolution.get() catch unreachable)).*;638 return (await (async decl.resolution.get() catch unreachable)).*;
629 }639 }
...@@ -655,12 +665,41 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void {...@@ -655,12 +665,41 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void {
655665
656 fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };666 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(
659 module,669 module,
660 body_node,670 body_node,
661 &fndef_scope.base,671 &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,
662 fn_decl.base.parsed_file,689 fn_decl.base.parsed_file,
663 ) catch unreachable);690 unanalyzed_code,
664 //code.dump();691 null,
665 //try await (async irAnalyze(module, func) catch unreachable);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
666}705}
src-self-hosted/type.zig+33
...@@ -39,6 +39,14 @@ pub const Type = struct {...@@ -39,6 +39,14 @@ pub const Type = struct {
39 }39 }
40 }40 }
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
42 pub const Struct = struct {50 pub const Struct = struct {
43 base: Type,51 base: Type,
44 decls: *Scope.Decls,52 decls: *Scope.Decls,
...@@ -143,10 +151,35 @@ pub const Type = struct {...@@ -143,10 +151,35 @@ pub const Type = struct {
143 };151 };
144 pub const Pointer = struct {152 pub const Pointer = struct {
145 base: Type,153 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
147 pub fn destroy(self: *Pointer, module: *Module) void {169 pub fn destroy(self: *Pointer, module: *Module) void {
148 module.a().destroy(self);170 module.a().destroy(self);
149 }171 }
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 }
150 };183 };
151 pub const Array = struct {184 pub const Array = struct {
152 base: Type,185 base: Type,
src-self-hosted/value.zig+21
...@@ -24,10 +24,16 @@ pub const Value = struct {...@@ -24,10 +24,16 @@ pub const Value = struct {
24 Id.Void => @fieldParentPtr(Void, "base", base).destroy(module),24 Id.Void => @fieldParentPtr(Void, "base", base).destroy(module),
25 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module),25 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(module),
26 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module),26 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(module),
27 Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(module),
27 }28 }
28 }29 }
29 }30 }
3031
32 pub fn getRef(base: *Value) *Value {
33 base.ref();
34 return base;
35 }
36
31 pub fn dump(base: *const Value) void {37 pub fn dump(base: *const Value) void {
32 std.debug.warn("{}", @tagName(base.id));38 std.debug.warn("{}", @tagName(base.id));
33 }39 }
...@@ -38,6 +44,7 @@ pub const Value = struct {...@@ -38,6 +44,7 @@ pub const Value = struct {
38 Void,44 Void,
39 Bool,45 Bool,
40 NoReturn,46 NoReturn,
47 Ptr,
41 };48 };
4249
43 pub const Type = @import("type.zig").Type;50 pub const Type = @import("type.zig").Type;
...@@ -122,4 +129,18 @@ pub const Value = struct {...@@ -122,4 +129,18 @@ pub const Value = struct {
122 module.a().destroy(self);129 module.a().destroy(self);
123 }130 }
124 };131 };
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 };
125};146};
std/event/loop.zig+15
...@@ -382,6 +382,21 @@ pub const Loop = struct {...@@ -382,6 +382,21 @@ pub const Loop = struct {
382 return async<self.allocator> S.asyncFunc(self, &handle, args);382 return async<self.allocator> S.asyncFunc(self, &handle, args);
383 }383 }
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
385 fn workerRun(self: *Loop) void {400 fn workerRun(self: *Loop) void {
386 start_over: while (true) {401 start_over: while (true) {
387 if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {402 if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {