authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-18 00:09:43-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-19 19:31:50-04:00
logbd4280decfd08b28041f96f552b3fec5087cbcd3
treec10e0bfa1d57d1af7c2fbe2f3fe86e152948cdd0
parent328eb8ed8dfdc646df28f899267c76e18645da40

beginnings of zig ir parser


3 files changed, 302 insertions(+), 3106 deletions(-)

src-self-hosted/ir.zig+202-2542
...@@ -1,2590 +1,250 @@...@@ -1,2590 +1,250 @@
1const std = @import("std");1const std = @import("std");
2const Compilation = @import("compilation.zig").Compilation;2const mem = std.mem;
3const Scope = @import("scope.zig").Scope;
4const ast = std.zig.ast;
5const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
6const Value = @import("value.zig").Value;4const Value = @import("value.zig").Value;
7const Type = Value.Type;
8const assert = std.debug.assert;5const assert = std.debug.assert;
9const Token = std.zig.Token;
10const Span = @import("errmsg.zig").Span;
11const llvm = @import("llvm.zig");
12const codegen = @import("codegen.zig");
13const ObjectFile = codegen.ObjectFile;
14const Decl = @import("decl.zig").Decl;
15const mem = std.mem;
16
17pub const LVal = enum {
18 None,
19 Ptr,
20};
21
22pub const IrVal = union(enum) {
23 Unknown,
24 KnownType: *Type,
25 KnownValue: *Value,
26
27 const Init = enum {
28 Unknown,
29 NoReturn,
30 Void,
31 };
32
33 pub fn dump(self: IrVal) void {
34 switch (self) {
35 .Unknown => std.debug.warn("Unknown", .{}),
36 .KnownType => |typ| {
37 std.debug.warn("KnownType(", .{});
38 typ.dump();
39 std.debug.warn(")", .{});
40 },
41 .KnownValue => |value| {
42 std.debug.warn("KnownValue(", .{});
43 value.dump();
44 std.debug.warn(")", .{});
45 },
46 }
47 }
48};
496
50pub const Inst = struct {7pub const Inst = struct {
51 id: Id,8 tag: Tag,
52 scope: *Scope,9
53 debug_id: usize,10 pub const all_types = .{
54 val: IrVal,11 Constant,
55 ref_count: usize,12 PtrToInt,
56 span: Span,13 FieldPtr,
57 owner_bb: *BasicBlock,14 Deref,
5815 Assembly,
59 /// true if this instruction was generated by zig and not from user code16 Unreach,
60 is_generated: bool,17 };
6118
62 /// the instruction that is derived from this one in analysis19 pub const Tag = enum {
63 child: ?*Inst,20 constant,
6421 ptrtoint,
65 /// the instruction that this one derives from in analysis22 fieldptr,
66 parent: ?*Inst,23 deref,
6724 @"asm",
68 /// populated durign codegen25 unreach,
69 llvm_value: ?*llvm.Value,26 };
7027
71 pub fn cast(base: *Inst, comptime T: type) ?*T {28 /// This struct owns the `Value` memory. When the struct is deallocated,
72 if (base.id == comptime typeToId(T)) {29 /// so is the `Value`. The value of a constant must be copied into
73 return @fieldParentPtr(T, "base", base);30 /// a memory location for the value to survive after a const instruction.
74 }31 pub const Constant = struct {
75 return null;32 base: Inst = Inst{ .tag = .constant },
76 }33 value: *Value,
77
78 pub fn typeToId(comptime T: type) Id {
79 inline for (@typeInfo(Id).Enum.fields) |f| {
80 if (T == @field(Inst, f.name)) {
81 return @field(Id, f.name);
82 }
83 }
84 unreachable;
85 }
86
87 pub fn dump(base: *const Inst) void {
88 inline for (@typeInfo(Id).Enum.fields) |f| {
89 if (base.id == @field(Id, f.name)) {
90 const T = @field(Inst, f.name);
91 std.debug.warn("#{} = {}(", .{ base.debug_id, @tagName(base.id) });
92 @fieldParentPtr(T, "base", base).dump();
93 std.debug.warn(")", .{});
94 return;
95 }
96 }
97 unreachable;
98 }
99
100 pub fn hasSideEffects(base: *const Inst) bool {
101 inline for (@typeInfo(Id).Enum.fields) |f| {
102 if (base.id == @field(Id, f.name)) {
103 const T = @field(Inst, f.name);
104 return @fieldParentPtr(T, "base", base).hasSideEffects();
105 }
106 }
107 unreachable;
108 }
109
110 pub fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
111 switch (base.id) {
112 .Return => return @fieldParentPtr(Return, "base", base).analyze(ira),
113 .Const => return @fieldParentPtr(Const, "base", base).analyze(ira),
114 .Call => return @fieldParentPtr(Call, "base", base).analyze(ira),
115 .DeclRef => return @fieldParentPtr(DeclRef, "base", base).analyze(ira),
116 .Ref => return @fieldParentPtr(Ref, "base", base).analyze(ira),
117 .DeclVar => return @fieldParentPtr(DeclVar, "base", base).analyze(ira),
118 .CheckVoidStmt => return @fieldParentPtr(CheckVoidStmt, "base", base).analyze(ira),
119 .Phi => return @fieldParentPtr(Phi, "base", base).analyze(ira),
120 .Br => return @fieldParentPtr(Br, "base", base).analyze(ira),
121 .AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira),
122 .PtrType => return @fieldParentPtr(PtrType, "base", base).analyze(ira),
123 .VarPtr => return @fieldParentPtr(VarPtr, "base", base).analyze(ira),
124 .LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).analyze(ira),
125 }
126 }
127
128 pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?*llvm.Value) {
129 switch (base.id) {
130 .Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
131 .Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
132 .Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val),
133 .VarPtr => return @fieldParentPtr(VarPtr, "base", base).render(ofile, fn_val),
134 .LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).render(ofile, fn_val),
135 .DeclRef => unreachable,
136 .PtrType => unreachable,
137 .Ref => @panic("TODO"),
138 .DeclVar => @panic("TODO"),
139 .CheckVoidStmt => @panic("TODO"),
140 .Phi => @panic("TODO"),
141 .Br => @panic("TODO"),
142 .AddImplicitReturnType => @panic("TODO"),
143 }
144 }
145
146 fn ref(base: *Inst, builder: *Builder) void {
147 base.ref_count += 1;
148 if (base.owner_bb != builder.current_basic_block and !base.isCompTime()) {
149 base.owner_bb.ref(builder);
150 }
151 }
152
153 fn copyVal(base: *Inst, comp: *Compilation) !*Value {
154 if (base.parent.?.ref_count == 0) {
155 return base.val.KnownValue.derefAndCopy(comp);
156 }
157 return base.val.KnownValue.copy(comp);
158 }
159
160 fn getAsParam(param: *Inst) !*Inst {
161 param.ref_count -= 1;
162 const child = param.child orelse return error.SemanticAnalysisFailed;
163 switch (child.val) {
164 .Unknown => return error.SemanticAnalysisFailed,
165 else => return child,
166 }
167 }
168
169 fn getConstVal(self: *Inst, ira: *Analyze) !*Value {
170 if (self.isCompTime()) {
171 return self.val.KnownValue;
172 } else {
173 try ira.addCompileError(self.span, "unable to evaluate constant expression", .{});
174 return error.SemanticAnalysisFailed;
175 }
176 }
177
178 fn getAsConstType(param: *Inst, ira: *Analyze) !*Type {
179 const meta_type = Type.MetaType.get(ira.irb.comp);
180 meta_type.base.base.deref(ira.irb.comp);
181
182 const inst = try param.getAsParam();
183 const casted = try ira.implicitCast(inst, &meta_type.base);
184 const val = try casted.getConstVal(ira);
185 return val.cast(Value.Type).?;
186 }
187
188 fn getAsConstAlign(param: *Inst, ira: *Analyze) !u32 {
189 return error.Unimplemented;
190 //const align_type = Type.Int.get_align(ira.irb.comp);
191 //align_type.base.base.deref(ira.irb.comp);
192
193 //const inst = try param.getAsParam();
194 //const casted = try ira.implicitCast(inst, align_type);
195 //const val = try casted.getConstVal(ira);
196
197 //uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint);
198 //if (align_bytes == 0) {
199 // ir_add_error(ira, value, buf_sprintf("alignment must be >= 1"));
200 // return false;
201 //}
202
203 //if (!is_power_of_2(align_bytes)) {
204 // ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
205 // return false;
206 //}
207 }
208
209 /// asserts that the type is known
210 fn getKnownType(self: *Inst) *Type {
211 switch (self.val) {
212 .KnownType => |typ| return typ,
213 .KnownValue => |value| return value.typ,
214 .Unknown => unreachable,
215 }
216 }
217
218 pub fn setGenerated(base: *Inst) void {
219 base.is_generated = true;
220 }
221
222 pub fn isNoReturn(base: *const Inst) bool {
223 switch (base.val) {
224 .Unknown => return false,
225 .KnownValue => |x| return x.typ.id == .NoReturn,
226 .KnownType => |typ| return typ.id == .NoReturn,
227 }
228 }
229
230 pub fn isCompTime(base: *const Inst) bool {
231 return base.val == .KnownValue;
232 }
233
234 pub fn linkToParent(self: *Inst, parent: *Inst) void {
235 assert(self.parent == null);
236 assert(parent.child == null);
237 self.parent = parent;
238 parent.child = self;
239 }
240
241 pub const Id = enum {
242 Return,
243 Const,
244 Ref,
245 DeclVar,
246 CheckVoidStmt,
247 Phi,
248 Br,
249 AddImplicitReturnType,
250 Call,
251 DeclRef,
252 PtrType,
253 VarPtr,
254 LoadPtr,
255 };
256
257 pub const Call = struct {
258 base: Inst,
259 params: Params,
260
261 const Params = struct {
262 fn_ref: *Inst,
263 args: []*Inst,
264 };
265
266 const ir_val_init = IrVal.Init.Unknown;
267
268 pub fn dump(self: *const Call) void {
269 std.debug.warn("#{}(", .{self.params.fn_ref.debug_id});
270 for (self.params.args) |arg| {
271 std.debug.warn("#{},", .{arg.debug_id});
272 }
273 std.debug.warn(")", .{});
274 }
275
276 pub fn hasSideEffects(self: *const Call) bool {
277 return true;
278 }
279
280 pub fn analyze(self: *const Call, ira: *Analyze) !*Inst {
281 const fn_ref = try self.params.fn_ref.getAsParam();
282 const fn_ref_type = fn_ref.getKnownType();
283 const fn_type = fn_ref_type.cast(Type.Fn) orelse {
284 try ira.addCompileError(fn_ref.span, "type '{}' not a function", .{fn_ref_type.name});
285 return error.SemanticAnalysisFailed;
286 };
287
288 const fn_type_param_count = fn_type.paramCount();
289
290 if (fn_type_param_count != self.params.args.len) {
291 try ira.addCompileError(self.base.span, "expected {} arguments, found {}", .{
292 fn_type_param_count,
293 self.params.args.len,
294 });
295 return error.SemanticAnalysisFailed;
296 }
297
298 const args = try ira.irb.arena().alloc(*Inst, self.params.args.len);
299 for (self.params.args) |arg, i| {
300 args[i] = try arg.getAsParam();
301 }
302 const new_inst = try ira.irb.build(Call, self.base.scope, self.base.span, Params{
303 .fn_ref = fn_ref,
304 .args = args,
305 });
306 new_inst.val = IrVal{ .KnownType = fn_type.key.data.Normal.return_type };
307 return new_inst;
308 }
309
310 pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
311 const fn_ref = self.params.fn_ref.llvm_value.?;
312
313 const args = try ofile.arena.alloc(*llvm.Value, self.params.args.len);
314 for (self.params.args) |arg, i| {
315 args[i] = arg.llvm_value.?;
316 }
317
318 const llvm_cc = llvm.CCallConv;
319 const call_attr = llvm.CallAttr.Auto;
320
321 return llvm.BuildCall(
322 ofile.builder,
323 fn_ref,
324 args.ptr,
325 @intCast(c_uint, args.len),
326 llvm_cc,
327 call_attr,
328 "",
329 ) orelse error.OutOfMemory;
330 }
331 };
332
333 pub const Const = struct {
334 base: Inst,
335 params: Params,
336
337 const Params = struct {};
338
339 // Use Builder.buildConst* methods, or, after building a Const instruction,
340 // manually set the ir_val field.
341 const ir_val_init = IrVal.Init.Unknown;
342
343 pub fn dump(self: *const Const) void {
344 self.base.val.KnownValue.dump();
345 }
346
347 pub fn hasSideEffects(self: *const Const) bool {
348 return false;
349 }
350
351 pub fn analyze(self: *const Const, ira: *Analyze) !*Inst {
352 const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{});
353 new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() };
354 return new_inst;
355 }
356
357 pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
358 return self.base.val.KnownValue.getLlvmConst(ofile);
359 }
360 };34 };
36135
362 pub const Return = struct {36 pub const PtrToInt = struct {
363 base: Inst,37 base: Inst = Inst{ .tag = .ptrtoint },
364 params: Params,
365
366 const Params = struct {
367 return_value: *Inst,
368 };
369
370 const ir_val_init = IrVal.Init.NoReturn;
371
372 pub fn dump(self: *const Return) void {
373 std.debug.warn("#{}", .{self.params.return_value.debug_id});
374 }
375
376 pub fn hasSideEffects(self: *const Return) bool {
377 return true;
378 }
379
380 pub fn analyze(self: *const Return, ira: *Analyze) !*Inst {
381 const value = try self.params.return_value.getAsParam();
382 const casted_value = try ira.implicitCast(value, ira.explicit_return_type);
383
384 // TODO detect returning local variable address
385
386 return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
387 }
388
389 pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
390 const value = self.params.return_value.llvm_value;
391 const return_type = self.params.return_value.getKnownType();
392
393 if (return_type.handleIsPtr()) {
394 @panic("TODO");
395 } else {
396 _ = llvm.BuildRet(ofile.builder, value) orelse return error.OutOfMemory;
397 }
398 return null;
399 }
400 };38 };
40139
402 pub const Ref = struct {40 pub const FieldPtr = struct {
403 base: Inst,41 base: Inst = Inst{ .tag = .fieldptr },
404 params: Params,
405
406 const Params = struct {
407 target: *Inst,
408 mut: Type.Pointer.Mut,
409 volatility: Type.Pointer.Vol,
410 };
411
412 const ir_val_init = IrVal.Init.Unknown;
413
414 pub fn dump(inst: *const Ref) void {}
415
416 pub fn hasSideEffects(inst: *const Ref) bool {
417 return false;
418 }
419
420 pub fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
421 const target = try self.params.target.getAsParam();
422
423 if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
424 return ira.getCompTimeRef(
425 val,
426 Value.Ptr.Mut.CompTimeConst,
427 self.params.mut,
428 self.params.volatility,
429 );
430 }
431
432 const new_inst = try ira.irb.build(Ref, self.base.scope, self.base.span, Params{
433 .target = target,
434 .mut = self.params.mut,
435 .volatility = self.params.volatility,
436 });
437 const elem_type = target.getKnownType();
438 const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
439 .child_type = elem_type,
440 .mut = self.params.mut,
441 .vol = self.params.volatility,
442 .size = .One,
443 .alignment = .Abi,
444 });
445 // TODO: potentially set the hint that this is a stack pointer. But it might not be - this
446 // could be a ref of a global, for example
447 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
448 // TODO potentially add an alloca entry here
449 return new_inst;
450 }
451 };42 };
45243
453 pub const DeclRef = struct {44 pub const Deref = struct {
454 base: Inst,45 base: Inst = Inst{ .tag = .deref },
455 params: Params,
456
457 const Params = struct {
458 decl: *Decl,
459 lval: LVal,
460 };
461
462 const ir_val_init = IrVal.Init.Unknown;
463
464 pub fn dump(inst: *const DeclRef) void {}
465
466 pub fn hasSideEffects(inst: *const DeclRef) bool {
467 return false;
468 }
469
470 pub fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
471 (ira.irb.comp.resolveDecl(self.params.decl)) catch |err| switch (err) {
472 error.OutOfMemory => return error.OutOfMemory,
473 else => return error.SemanticAnalysisFailed,
474 };
475 switch (self.params.decl.id) {
476 .CompTime => unreachable,
477 .Var => return error.Unimplemented,
478 .Fn => {
479 const fn_decl = @fieldParentPtr(Decl.Fn, "base", self.params.decl);
480 const decl_val = switch (fn_decl.value) {
481 .Unresolved => unreachable,
482 .Fn => |fn_val| &fn_val.base,
483 .FnProto => |fn_proto| &fn_proto.base,
484 };
485 switch (self.params.lval) {
486 .None => {
487 return ira.irb.buildConstValue(self.base.scope, self.base.span, decl_val);
488 },
489 .Ptr => return error.Unimplemented,
490 }
491 },
492 }
493 }
494 };46 };
49547
496 pub const VarPtr = struct {48 pub const Assembly = struct {
497 base: Inst,49 base: Inst = Inst{ .tag = .@"asm" },
498 params: Params,
499
500 const Params = struct {
501 var_scope: *Scope.Var,
502 };
503
504 const ir_val_init = IrVal.Init.Unknown;
505
506 pub fn dump(inst: *const VarPtr) void {
507 std.debug.warn("{}", .{inst.params.var_scope.name});
508 }
509
510 pub fn hasSideEffects(inst: *const VarPtr) bool {
511 return false;
512 }
513
514 pub fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
515 switch (self.params.var_scope.data) {
516 .Const => @panic("TODO"),
517 .Param => |param| {
518 const new_inst = try ira.irb.build(
519 Inst.VarPtr,
520 self.base.scope,
521 self.base.span,
522 Inst.VarPtr.Params{ .var_scope = self.params.var_scope },
523 );
524 const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
525 .child_type = param.typ,
526 .mut = .Const,
527 .vol = .Non,
528 .size = .One,
529 .alignment = .Abi,
530 });
531 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
532 return new_inst;
533 },
534 }
535 }
536
537 pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) *llvm.Value {
538 switch (self.params.var_scope.data) {
539 .Const => unreachable, // turned into Inst.Const in analyze pass
540 .Param => |param| return param.llvm_value,
541 }
542 }
543 };50 };
54451
545 pub const LoadPtr = struct {52 pub const Unreach = struct {
546 base: Inst,53 base: Inst = Inst{ .tag = .unreach },
547 params: Params,
548
549 const Params = struct {
550 target: *Inst,
551 };
552
553 const ir_val_init = IrVal.Init.Unknown;
554
555 pub fn dump(inst: *const LoadPtr) void {}
556
557 pub fn hasSideEffects(inst: *const LoadPtr) bool {
558 return false;
559 }
560
561 pub fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
562 const target = try self.params.target.getAsParam();
563 const target_type = target.getKnownType();
564 if (target_type.id != .Pointer) {
565 try ira.addCompileError(self.base.span, "dereference of non pointer type '{}'", .{target_type.name});
566 return error.SemanticAnalysisFailed;
567 }
568 const ptr_type = @fieldParentPtr(Type.Pointer, "base", target_type);
569 // if (instr_is_comptime(ptr)) {
570 // if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst ||
571 // ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
572 // {
573 // ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &ptr->value);
574 // if (pointee->special != ConstValSpecialRuntime) {
575 // IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
576 // source_instruction->source_node, child_type);
577 // copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst);
578 // result->value.type = child_type;
579 // return result;
580 // }
581 // }
582 // }
583 const new_inst = try ira.irb.build(
584 Inst.LoadPtr,
585 self.base.scope,
586 self.base.span,
587 Inst.LoadPtr.Params{ .target = target },
588 );
589 new_inst.val = IrVal{ .KnownType = ptr_type.key.child_type };
590 return new_inst;
591 }
592
593 pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
594 const child_type = self.base.getKnownType();
595 if (!child_type.hasBits()) {
596 return null;
597 }
598 const ptr = self.params.target.llvm_value.?;
599 const ptr_type = self.params.target.getKnownType().cast(Type.Pointer).?;
600
601 return try codegen.getHandleValue(ofile, ptr, ptr_type);
602
603 //uint32_t unaligned_bit_count = ptr_type->data.pointer.unaligned_bit_count;
604 //if (unaligned_bit_count == 0)
605 // return get_handle_value(g, ptr, child_type, ptr_type);
606
607 //bool big_endian = g->is_big_endian;
608
609 //assert(!handle_is_ptr(child_type));
610 //LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
611
612 //uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
613 //uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
614 //uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset;
615
616 //LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
617 //LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, "");
618
619 //return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
620 }
621 };54 };
55};
62256
623 pub const PtrType = struct {57pub const ErrorMsg = struct {
624 base: Inst,58 byte_offset: usize,
625 params: Params,59 msg: []const u8,
62660};
627 const Params = struct {
628 child_type: *Inst,
629 mut: Type.Pointer.Mut,
630 vol: Type.Pointer.Vol,
631 size: Type.Pointer.Size,
632 alignment: ?*Inst,
633 };
634
635 const ir_val_init = IrVal.Init.Unknown;
636
637 pub fn dump(inst: *const PtrType) void {}
63861
639 pub fn hasSideEffects(inst: *const PtrType) bool {62pub const Tree = struct {
640 return false;63 decls: std.ArrayList(*Inst),
641 }64 errors: std.ArrayList(ErrorMsg),
65};
64266
643 pub fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {67const ParseContext = struct {
644 const child_type = try self.params.child_type.getAsConstType(ira);68 allocator: *Allocator,
645 // if (child_type->id == TypeTableEntryIdUnreachable) {69 i: usize,
646 // ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));70 source: []const u8,
647 // return ira->codegen->builtin_types.entry_invalid;71 errors: *std.ArrayList(ErrorMsg),
648 // } else if (child_type->id == TypeTableEntryIdOpaque && instruction->ptr_len == PtrLenUnknown) {72};
649 // ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
650 // return ira->codegen->builtin_types.entry_invalid;
651 // }
652 const alignment = if (self.params.alignment) |align_inst| blk: {
653 const amt = try align_inst.getAsConstAlign(ira);
654 break :blk Type.Pointer.Align{ .Override = amt };
655 } else blk: {
656 break :blk .Abi;
657 };
658 const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
659 .child_type = child_type,
660 .mut = self.params.mut,
661 .vol = self.params.vol,
662 .size = self.params.size,
663 .alignment = alignment,
664 });
665 ptr_type.base.base.deref(ira.irb.comp);
66673
667 return ira.irb.buildConstValue(self.base.scope, self.base.span, &ptr_type.base.base);74pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!Tree {
668 }75 var tree: Tree = .{
76 .decls = std.ArrayList(*Inst).init(allocator),
77 .errors = std.ArrayList(ErrorMsg).init(allocator),
669 };78 };
67079 var ctx: ParseContext = .{
671 pub const DeclVar = struct {80 .allocator = allocator,
672 base: Inst,81 .i = 0,
673 params: Params,82 .source = source,
67483 .errors = &tree.errors,
675 const Params = struct {
676 variable: *Variable,
677 };
678
679 const ir_val_init = IrVal.Init.Unknown;
680
681 pub fn dump(inst: *const DeclVar) void {}
682
683 pub fn hasSideEffects(inst: *const DeclVar) bool {
684 return true;
685 }
686
687 pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Inst {
688 return error.Unimplemented; // TODO
689 }
690 };84 };
69185 parseRoot(&ctx, &tree) catch |err| switch (err) {
692 pub const CheckVoidStmt = struct {86 error.ParseFailure => {
693 base: Inst,87 assert(tree.errors.items.len != 0);
694 params: Params,88 },
69589 else => |e| return e,
696 const Params = struct {
697 target: *Inst,
698 };
699
700 const ir_val_init = IrVal.Init.Unknown;
701
702 pub fn dump(self: *const CheckVoidStmt) void {
703 std.debug.warn("#{}", .{self.params.target.debug_id});
704 }
705
706 pub fn hasSideEffects(inst: *const CheckVoidStmt) bool {
707 return true;
708 }
709
710 pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Inst {
711 const target = try self.params.target.getAsParam();
712 if (target.getKnownType().id != .Void) {
713 try ira.addCompileError(self.base.span, "expression value is ignored", .{});
714 return error.SemanticAnalysisFailed;
715 }
716 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
717 }
718 };90 };
91 return tree;
92}
71993
720 pub const Phi = struct {94pub fn parseRoot(ctx: *ParseContext, tree: *Tree) !void {
721 base: Inst,95 // The IR format is designed so that it can be tokenized and parsed at the same time.
722 params: Params,96 var global_name_map = std.StringHashMap(usize).init(ctx.allocator);
72397 while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) {
724 const Params = struct {98 ';' => _ = try skipToAndOver(ctx, '\n'),
725 incoming_blocks: []*BasicBlock,99 '@' => {
726 incoming_values: []*Inst,100 const at_start = ctx.i;
727 };101 const ident = try skipToAndOver(ctx, ' ');
728102 var ty: ?*Value = null;
729 const ir_val_init = IrVal.Init.Unknown;103 if (eatByte(ctx, ':')) {
730104 ty = try parseType(ctx);
731 pub fn dump(inst: *const Phi) void {}105 skipSpace(ctx);
732106 }
733 pub fn hasSideEffects(inst: *const Phi) bool {107 try requireEatBytes(ctx, "= ");
734 return false;108 const inst = try parseInstruction(ctx);
735 }109 const ident_index = tree.decls.items.len;
736110 if (try global_name_map.put(ident, ident_index)) |_| {
737 pub fn analyze(self: *const Phi, ira: *Analyze) !*Inst {111 return parseError(ctx, "redefinition of identifier '{}'", .{ident});
738 return error.Unimplemented; // TODO112 }
739 }113 try tree.decls.append(inst);
114 continue;
115 },
116 ' ', '\n' => continue,
117 else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}),
740 };118 };
119}
741120
742 pub const Br = struct {121fn eatByte(ctx: *ParseContext, byte: u8) bool {
743 base: Inst,122 if (ctx.i >= ctx.source.len) return false;
744 params: Params,123 if (ctx.source[ctx.i] != byte) return false;
745124 ctx.i += 1;
746 const Params = struct {125 return true;
747 dest_block: *BasicBlock,126}
748 is_comptime: *Inst,
749 };
750127
751 const ir_val_init = IrVal.Init.NoReturn;128fn skipSpace(ctx: *ParseContext) void {
129 while (ctx.i < ctx.source.len and ctx.source[ctx.i] == ' ') : (ctx.i += 1) {}
130}
752131
753 pub fn dump(inst: *const Br) void {}132fn requireEatBytes(ctx: *ParseContext, bytes: []const u8) !void {
133 if (ctx.i + bytes.len > ctx.source.len)
134 return parseError(ctx, "unexpected EOF", .{});
135 if (!mem.eql(u8, ctx.source[ctx.i..][0..bytes.len], bytes))
136 return parseError(ctx, "expected '{}'", .{bytes});
137 ctx.i += bytes.len;
138}
754139
755 pub fn hasSideEffects(inst: *const Br) bool {140fn skipToAndOver(ctx: *ParseContext, byte: u8) ![]const u8 {
756 return true;141 const start_i = ctx.i;
142 while (ctx.i < ctx.source.len) : (ctx.i += 1) {
143 if (ctx.source[ctx.i] == byte) {
144 const result = ctx.source[start_i..ctx.i];
145 ctx.i += 1;
146 return result;
757 }147 }
148 }
149 return parseError(ctx, "unexpected EOF", .{});
150}
758151
759 pub fn analyze(self: *const Br, ira: *Analyze) !*Inst {152fn parseError(ctx: *ParseContext, comptime format: []const u8, args: var) error{ ParseFailure, OutOfMemory } {
760 return error.Unimplemented; // TODO153 const msg = try std.fmt.allocPrint(ctx.allocator, format, args);
761 }154 (try ctx.errors.addOne()).* = .{
155 .byte_offset = ctx.i,
156 .msg = msg,
762 };157 };
158 return error.ParseFailure;
159}
763160
764 pub const CondBr = struct {161fn parseType(ctx: *ParseContext) !*Value {
765 base: Inst,162 return parseError(ctx, "TODO parse type", .{});
766 params: Params,163}
767
768 const Params = struct {
769 condition: *Inst,
770 then_block: *BasicBlock,
771 else_block: *BasicBlock,
772 is_comptime: *Inst,
773 };
774
775 const ir_val_init = IrVal.Init.NoReturn;
776
777 pub fn dump(inst: *const CondBr) void {}
778164
779 pub fn hasSideEffects(inst: *const CondBr) bool {165fn parseInstruction(ctx: *ParseContext) !*Inst {
780 return true;166 switch (ctx.source[ctx.i]) {
781 }167 '"' => return parseStringLiteralConst(ctx),
168 '0'...'9' => return parseIntegerLiteralConst(ctx),
169 else => {},
170 }
171 const fn_name = skipToAndOver(ctx, '(');
172 return parseError(ctx, "TODO parse instruction '{}'", .{fn_name});
173}
782174
783 pub fn analyze(self: *const CondBr, ira: *Analyze) !*Inst {175fn parseStringLiteralConst(ctx: *ParseContext) !*Inst {
784 return error.Unimplemented; // TODO176 const start = ctx.i;
785 }177 ctx.i += 1; // skip over '"'
178
179 while (ctx.i < ctx.source.len) : (ctx.i += 1) switch (ctx.source[ctx.i]) {
180 '"' => {
181 ctx.i += 1;
182 const span = ctx.source[start..ctx.i];
183 var bad_index: usize = undefined;
184 const parsed = std.zig.parseStringLiteral(ctx.allocator, span, &bad_index) catch |err| switch (err) {
185 error.InvalidCharacter => {
186 ctx.i = start + bad_index;
187 const bad_byte = ctx.source[ctx.i];
188 return parseError(ctx, "invalid string literal character: '{c}'\n", .{bad_byte});
189 },
190 else => |e| return e,
191 };
192 const bytes_val = try ctx.allocator.create(Value.Bytes);
193 bytes_val.* = .{ .data = parsed };
194 const const_inst = try ctx.allocator.create(Inst.Constant);
195 const_inst.* = .{ .value = &bytes_val.base };
196 return &const_inst.base;
197 },
198 '\\' => {
199 ctx.i += 1;
200 if (ctx.i >= ctx.source.len) break;
201 continue;
202 },
203 else => continue,
786 };204 };
205 return parseError(ctx, "unexpected EOF in string literal", .{});
206}
787207
788 pub const AddImplicitReturnType = struct {208fn parseIntegerLiteralConst(ctx: *ParseContext) !*Inst {
789 base: Inst,209 return parseError(ctx, "TODO parse integer literal", .{});
790 params: Params,210}
791
792 pub const Params = struct {
793 target: *Inst,
794 };
795
796 const ir_val_init = IrVal.Init.Unknown;
797
798 pub fn dump(inst: *const AddImplicitReturnType) void {
799 std.debug.warn("#{}", .{inst.params.target.debug_id});
800 }
801
802 pub fn hasSideEffects(inst: *const AddImplicitReturnType) bool {
803 return true;
804 }
805
806 pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Inst {
807 const target = try self.params.target.getAsParam();
808 try ira.src_implicit_return_type_list.append(target);
809 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
810 }
811 };
812211
813 pub const TestErr = struct {212pub fn main() anyerror!void {
814 base: Inst,213 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
815 params: Params,214 defer arena.deinit();
215 const allocator = &arena.allocator;
816216
817 pub const Params = struct {217 const args = try std.process.argsAlloc(allocator);
818 target: *Inst,
819 };
820218
821 const ir_val_init = IrVal.Init.Unknown;219 const src_path = args[1];
220 const debug_error_trace = true;
822221
823 pub fn dump(inst: *const TestErr) void {222 const source = try std.fs.cwd().readFileAlloc(allocator, src_path, std.math.maxInt(u32));
824 std.debug.warn("#{}", .{inst.params.target.debug_id});
825 }
826223
827 pub fn hasSideEffects(inst: *const TestErr) bool {224 const tree = try parse(allocator, source);
828 return false;225 if (tree.errors.items.len != 0) {
226 for (tree.errors.items) |err_msg| {
227 const loc = findLineColumn(source, err_msg.byte_offset);
228 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
829 }229 }
230 if (debug_error_trace) return error.ParseFailure;
231 std.process.exit(1);
232 }
233}
830234
831 pub fn analyze(self: *const TestErr, ira: *Analyze) !*Inst {235fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
832 const target = try self.params.target.getAsParam();236 var line: usize = 0;
833 const target_type = target.getKnownType();237 var column: usize = 0;
834 switch (target_type.id) {238 for (source[0..byte_offset]) |byte| {
835 .ErrorUnion => {239 switch (byte) {
836 return error.Unimplemented;240 '\n' => {
837 // if (instr_is_comptime(value)) {241 line += 1;
838 // ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad);242 column = 0;
839 // if (!err_union_val)243 },
840 // return ira->codegen->builtin_types.entry_invalid;244 else => {
841245 column += 1;
842 // if (err_union_val->special != ConstValSpecialRuntime) {246 },
843 // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
844 // out_val->data.x_bool = (err_union_val->data.x_err_union.err != nullptr);
845 // return ira->codegen->builtin_types.entry_bool;
846 // }
847 // }
848
849 // TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type;
850 // if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.source_node)) {
851 // return ira->codegen->builtin_types.entry_invalid;
852 // }
853 // if (!type_is_global_error_set(err_set_type) &&
854 // err_set_type->data.error_set.err_count == 0)
855 // {
856 // assert(err_set_type->data.error_set.infer_fn == nullptr);
857 // ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
858 // out_val->data.x_bool = false;
859 // return ira->codegen->builtin_types.entry_bool;
860 // }
861
862 // ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
863 // return ira->codegen->builtin_types.entry_bool;
864 },
865 .ErrorSet => {
866 return ira.irb.buildConstBool(self.base.scope, self.base.span, true);
867 },
868 else => {
869 return ira.irb.buildConstBool(self.base.scope, self.base.span, false);
870 },
871 }
872 }247 }
873 };
874
875 pub const TestCompTime = struct {
876 base: Inst,
877 params: Params,
878
879 pub const Params = struct {
880 target: *Inst,
881 };
882
883 const ir_val_init = IrVal.Init.Unknown;
884
885 pub fn dump(inst: *const TestCompTime) void {
886 std.debug.warn("#{}", .{inst.params.target.debug_id});
887 }
888
889 pub fn hasSideEffects(inst: *const TestCompTime) bool {
890 return false;
891 }
892
893 pub fn analyze(self: *const TestCompTime, ira: *Analyze) !*Inst {
894 const target = try self.params.target.getAsParam();
895 return ira.irb.buildConstBool(self.base.scope, self.base.span, target.isCompTime());
896 }
897 };
898
899 pub const SaveErrRetAddr = struct {
900 base: Inst,
901 params: Params,
902
903 const Params = struct {};
904
905 const ir_val_init = IrVal.Init.Unknown;
906
907 pub fn dump(inst: *const SaveErrRetAddr) void {}
908
909 pub fn hasSideEffects(inst: *const SaveErrRetAddr) bool {
910 return true;
911 }
912
913 pub fn analyze(self: *const SaveErrRetAddr, ira: *Analyze) !*Inst {
914 return ira.irb.build(Inst.SaveErrRetAddr, self.base.scope, self.base.span, Params{});
915 }
916 };
917};
918
919pub const Variable = struct {
920 child_scope: *Scope,
921};
922
923pub const BasicBlock = struct {
924 ref_count: usize,
925 name_hint: [*:0]const u8,
926 debug_id: usize,
927 scope: *Scope,
928 instruction_list: std.ArrayList(*Inst),
929 ref_instruction: ?*Inst,
930
931 /// for codegen
932 llvm_block: *llvm.BasicBlock,
933 llvm_exit_block: *llvm.BasicBlock,
934
935 /// the basic block that is derived from this one in analysis
936 child: ?*BasicBlock,
937
938 /// the basic block that this one derives from in analysis
939 parent: ?*BasicBlock,
940
941 pub fn ref(self: *BasicBlock, builder: *Builder) void {
942 self.ref_count += 1;
943 }
944
945 pub fn linkToParent(self: *BasicBlock, parent: *BasicBlock) void {
946 assert(self.parent == null);
947 assert(parent.child == null);
948 self.parent = parent;
949 parent.child = self;
950 }
951};
952
953/// Stuff that survives longer than Builder
954pub const Code = struct {
955 basic_block_list: std.ArrayList(*BasicBlock),
956 arena: std.heap.ArenaAllocator,
957 return_type: ?*Type,
958 tree_scope: *Scope.AstTree,
959
960 /// allocator is comp.gpa()
961 pub fn destroy(self: *Code, allocator: *Allocator) void {
962 self.arena.deinit();
963 allocator.destroy(self);
964 }
965
966 pub fn dump(self: *Code) void {
967 var bb_i: usize = 0;
968 for (self.basic_block_list.span()) |bb| {
969 std.debug.warn("{s}_{}:\n", .{ bb.name_hint, bb.debug_id });
970 for (bb.instruction_list.span()) |instr| {
971 std.debug.warn(" ", .{});
972 instr.dump();
973 std.debug.warn("\n", .{});
974 }
975 }
976 }
977
978 /// returns a ref-incremented value, or adds a compile error
979 pub fn getCompTimeResult(self: *Code, comp: *Compilation) !*Value {
980 const bb = self.basic_block_list.at(0);
981 for (bb.instruction_list.span()) |inst| {
982 if (inst.cast(Inst.Return)) |ret_inst| {
983 const ret_value = ret_inst.params.return_value;
984 if (ret_value.isCompTime()) {
985 return ret_value.val.KnownValue.getRef();
986 }
987 try comp.addCompileError(
988 self.tree_scope,
989 ret_value.span,
990 "unable to evaluate constant expression",
991 .{},
992 );
993 return error.SemanticAnalysisFailed;
994 } else if (inst.hasSideEffects()) {
995 try comp.addCompileError(
996 self.tree_scope,
997 inst.span,
998 "unable to evaluate constant expression",
999 .{},
1000 );
1001 return error.SemanticAnalysisFailed;
1002 }
1003 }
1004 unreachable;
1005 }
1006};
1007
1008pub const Builder = struct {
1009 comp: *Compilation,
1010 code: *Code,
1011 current_basic_block: *BasicBlock,
1012 next_debug_id: usize,
1013 is_comptime: bool,
1014 is_async: bool,
1015 begin_scope: ?*Scope,
1016
1017 pub const Error = Analyze.Error;
1018
1019 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, begin_scope: ?*Scope) !Builder {
1020 const code = try comp.gpa().create(Code);
1021 code.* = Code{
1022 .basic_block_list = undefined,
1023 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
1024 .return_type = null,
1025 .tree_scope = tree_scope,
1026 };
1027 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
1028 errdefer code.destroy(comp.gpa());
1029
1030 return Builder{
1031 .comp = comp,
1032 .current_basic_block = undefined,
1033 .code = code,
1034 .next_debug_id = 0,
1035 .is_comptime = false,
1036 .is_async = false,
1037 .begin_scope = begin_scope,
1038 };
1039 }
1040
1041 pub fn abort(self: *Builder) void {
1042 self.code.destroy(self.comp.gpa());
1043 }
1044
1045 /// Call code.destroy() when done
1046 pub fn finish(self: *Builder) *Code {
1047 return self.code;
1048 }
1049
1050 /// No need to clean up resources thanks to the arena allocator.
1051 pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*:0]const u8) !*BasicBlock {
1052 const basic_block = try self.arena().create(BasicBlock);
1053 basic_block.* = BasicBlock{
1054 .ref_count = 0,
1055 .name_hint = name_hint,
1056 .debug_id = self.next_debug_id,
1057 .scope = scope,
1058 .instruction_list = std.ArrayList(*Inst).init(self.arena()),
1059 .child = null,
1060 .parent = null,
1061 .ref_instruction = null,
1062 .llvm_block = undefined,
1063 .llvm_exit_block = undefined,
1064 };
1065 self.next_debug_id += 1;
1066 return basic_block;
1067 }
1068
1069 pub fn setCursorAtEndAndAppendBlock(self: *Builder, basic_block: *BasicBlock) !void {
1070 try self.code.basic_block_list.append(basic_block);
1071 self.setCursorAtEnd(basic_block);
1072 }
1073
1074 pub fn setCursorAtEnd(self: *Builder, basic_block: *BasicBlock) void {
1075 self.current_basic_block = basic_block;
1076 }
1077
1078 pub fn genNodeRecursive(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
1079 const alloc = irb.comp.gpa();
1080 var frame = try alloc.create(@Frame(genNode));
1081 defer alloc.destroy(frame);
1082 frame.* = async irb.genNode(node, scope, lval);
1083 return await frame;
1084 }
1085
1086 pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
1087 switch (node.id) {
1088 .Root => unreachable,
1089 .Use => unreachable,
1090 .TestDecl => unreachable,
1091 .VarDecl => return error.Unimplemented,
1092 .Defer => return error.Unimplemented,
1093 .InfixOp => return error.Unimplemented,
1094 .PrefixOp => {
1095 const prefix_op = @fieldParentPtr(ast.Node.PrefixOp, "base", node);
1096 switch (prefix_op.op) {
1097 .AddressOf => return error.Unimplemented,
1098 .ArrayType => |n| return error.Unimplemented,
1099 .Await => return error.Unimplemented,
1100 .BitNot => return error.Unimplemented,
1101 .BoolNot => return error.Unimplemented,
1102 .OptionalType => return error.Unimplemented,
1103 .Negation => return error.Unimplemented,
1104 .NegationWrap => return error.Unimplemented,
1105 .Resume => return error.Unimplemented,
1106 .PtrType => |ptr_info| {
1107 const inst = try irb.genPtrType(prefix_op, ptr_info, scope);
1108 return irb.lvalWrap(scope, inst, lval);
1109 },
1110 .SliceType => |ptr_info| return error.Unimplemented,
1111 .Try => return error.Unimplemented,
1112 }
1113 },
1114 .SuffixOp => {
1115 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
1116 switch (suffix_op.op) {
1117 .Call => |*call| {
1118 const inst = try irb.genCall(suffix_op, call, scope);
1119 return irb.lvalWrap(scope, inst, lval);
1120 },
1121 .ArrayAccess => |n| return error.Unimplemented,
1122 .Slice => |slice| return error.Unimplemented,
1123 .ArrayInitializer => |init_list| return error.Unimplemented,
1124 .StructInitializer => |init_list| return error.Unimplemented,
1125 .Deref => return error.Unimplemented,
1126 .UnwrapOptional => return error.Unimplemented,
1127 }
1128 },
1129 .Switch => return error.Unimplemented,
1130 .While => return error.Unimplemented,
1131 .For => return error.Unimplemented,
1132 .If => return error.Unimplemented,
1133 .ControlFlowExpression => {
1134 const control_flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", node);
1135 return irb.genControlFlowExpr(control_flow_expr, scope, lval);
1136 },
1137 .Suspend => return error.Unimplemented,
1138 .VarType => return error.Unimplemented,
1139 .ErrorType => return error.Unimplemented,
1140 .FnProto => return error.Unimplemented,
1141 .AnyFrameType => return error.Unimplemented,
1142 .IntegerLiteral => {
1143 const int_lit = @fieldParentPtr(ast.Node.IntegerLiteral, "base", node);
1144 return irb.lvalWrap(scope, try irb.genIntLit(int_lit, scope), lval);
1145 },
1146 .FloatLiteral => return error.Unimplemented,
1147 .StringLiteral => {
1148 const str_lit = @fieldParentPtr(ast.Node.StringLiteral, "base", node);
1149 const inst = try irb.genStrLit(str_lit, scope);
1150 return irb.lvalWrap(scope, inst, lval);
1151 },
1152 .MultilineStringLiteral => return error.Unimplemented,
1153 .CharLiteral => return error.Unimplemented,
1154 .BoolLiteral => return error.Unimplemented,
1155 .NullLiteral => return error.Unimplemented,
1156 .UndefinedLiteral => return error.Unimplemented,
1157 .Unreachable => return error.Unimplemented,
1158 .Identifier => {
1159 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", node);
1160 return irb.genIdentifier(identifier, scope, lval);
1161 },
1162 .GroupedExpression => {
1163 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
1164 return irb.genNodeRecursive(grouped_expr.expr, scope, lval);
1165 },
1166 .BuiltinCall => return error.Unimplemented,
1167 .ErrorSetDecl => return error.Unimplemented,
1168 .ContainerDecl => return error.Unimplemented,
1169 .Asm => return error.Unimplemented,
1170 .Comptime => return error.Unimplemented,
1171 .Block => {
1172 const block = @fieldParentPtr(ast.Node.Block, "base", node);
1173 const inst = try irb.genBlock(block, scope);
1174 return irb.lvalWrap(scope, inst, lval);
1175 },
1176 .DocComment => return error.Unimplemented,
1177 .SwitchCase => return error.Unimplemented,
1178 .SwitchElse => return error.Unimplemented,
1179 .Else => return error.Unimplemented,
1180 .Payload => return error.Unimplemented,
1181 .PointerPayload => return error.Unimplemented,
1182 .PointerIndexPayload => return error.Unimplemented,
1183 .ContainerField => return error.Unimplemented,
1184 .ErrorTag => return error.Unimplemented,
1185 .AsmInput => return error.Unimplemented,
1186 .AsmOutput => return error.Unimplemented,
1187 .ParamDecl => return error.Unimplemented,
1188 .FieldInitializer => return error.Unimplemented,
1189 .EnumLiteral => return error.Unimplemented,
1190 .Noasync => return error.Unimplemented,
1191 }
1192 }
1193
1194 fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
1195 const fn_ref = try irb.genNodeRecursive(suffix_op.lhs.node, scope, .None);
1196
1197 const args = try irb.arena().alloc(*Inst, call.params.len);
1198 var it = call.params.iterator(0);
1199 var i: usize = 0;
1200 while (it.next()) |arg_node_ptr| : (i += 1) {
1201 args[i] = try irb.genNodeRecursive(arg_node_ptr.*, scope, .None);
1202 }
1203
1204 //bool is_async = node->data.fn_call_expr.is_async;
1205 //IrInstruction *async_allocator = nullptr;
1206 //if (is_async) {
1207 // if (node->data.fn_call_expr.async_allocator) {
1208 // async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope);
1209 // if (async_allocator == irb->codegen->invalid_instruction)
1210 // return async_allocator;
1211 // }
1212 //}
1213
1214 return irb.build(Inst.Call, scope, Span.token(suffix_op.rtoken), Inst.Call.Params{
1215 .fn_ref = fn_ref,
1216 .args = args,
1217 });
1218 //IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr);
1219 //return ir_lval_wrap(irb, scope, fn_call, lval);
1220 }
1221
1222 fn genPtrType(
1223 irb: *Builder,
1224 prefix_op: *ast.Node.PrefixOp,
1225 ptr_info: ast.Node.PrefixOp.PtrInfo,
1226 scope: *Scope,
1227 ) !*Inst {
1228 // TODO port more logic
1229
1230 //assert(node->type == NodeTypePointerType);
1231 //PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar ||
1232 // node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown;
1233 //bool is_const = node->data.pointer_type.is_const;
1234 //bool is_volatile = node->data.pointer_type.is_volatile;
1235 //AstNode *expr_node = node->data.pointer_type.op_expr;
1236 //AstNode *align_expr = node->data.pointer_type.align_expr;
1237
1238 //IrInstruction *align_value;
1239 //if (align_expr != nullptr) {
1240 // align_value = ir_gen_node(irb, align_expr, scope);
1241 // if (align_value == irb->codegen->invalid_instruction)
1242 // return align_value;
1243 //} else {
1244 // align_value = nullptr;
1245 //}
1246 const child_type = try irb.genNodeRecursive(prefix_op.rhs, scope, .None);
1247
1248 //uint32_t bit_offset_start = 0;
1249 //if (node->data.pointer_type.bit_offset_start != nullptr) {
1250 // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) {
1251 // Buf *val_buf = buf_alloc();
1252 // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10);
1253 // exec_add_error_node(irb->codegen, irb->exec, node,
1254 // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
1255 // return irb->codegen->invalid_instruction;
1256 // }
1257 // bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start);
1258 //}
1259
1260 //uint32_t bit_offset_end = 0;
1261 //if (node->data.pointer_type.bit_offset_end != nullptr) {
1262 // if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_end, 32, false)) {
1263 // Buf *val_buf = buf_alloc();
1264 // bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_end, 10);
1265 // exec_add_error_node(irb->codegen, irb->exec, node,
1266 // buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
1267 // return irb->codegen->invalid_instruction;
1268 // }
1269 // bit_offset_end = bigint_as_unsigned(node->data.pointer_type.bit_offset_end);
1270 //}
1271
1272 //if ((bit_offset_start != 0 || bit_offset_end != 0) && bit_offset_start >= bit_offset_end) {
1273 // exec_add_error_node(irb->codegen, irb->exec, node,
1274 // buf_sprintf("bit offset start must be less than bit offset end"));
1275 // return irb->codegen->invalid_instruction;
1276 //}
1277
1278 return irb.build(Inst.PtrType, scope, Span.node(&prefix_op.base), Inst.PtrType.Params{
1279 .child_type = child_type,
1280 .mut = .Mut,
1281 .vol = .Non,
1282 .size = .Many,
1283 .alignment = null,
1284 });
1285 }
1286
1287 fn isCompTime(irb: *Builder, target_scope: *Scope) bool {
1288 if (irb.is_comptime)
1289 return true;
1290
1291 var scope = target_scope;
1292 while (true) {
1293 switch (scope.id) {
1294 .CompTime => return true,
1295 .FnDef => return false,
1296 .Decls => unreachable,
1297 .Root => unreachable,
1298 .AstTree => unreachable,
1299 .Block,
1300 .Defer,
1301 .DeferExpr,
1302 .Var,
1303 => scope = scope.parent.?,
1304 }
1305 }
1306 }
1307
1308 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {
1309 const int_token = irb.code.tree_scope.tree.tokenSlice(int_lit.token);
1310
1311 var base: u8 = undefined;
1312 var rest: []const u8 = undefined;
1313 if (int_token.len >= 3 and int_token[0] == '0') {
1314 rest = int_token[2..];
1315 switch (int_token[1]) {
1316 'b' => base = 2,
1317 'o' => base = 8,
1318 'x' => base = 16,
1319 else => {
1320 base = 10;
1321 rest = int_token;
1322 },
1323 }
1324 } else {
1325 base = 10;
1326 rest = int_token;
1327 }
1328
1329 const comptime_int_type = Type.ComptimeInt.get(irb.comp);
1330 defer comptime_int_type.base.base.deref(irb.comp);
1331
1332 const int_val = Value.Int.createFromString(
1333 irb.comp,
1334 &comptime_int_type.base,
1335 base,
1336 rest,
1337 ) catch |err| switch (err) {
1338 error.OutOfMemory => return error.OutOfMemory,
1339 error.InvalidBase => unreachable,
1340 error.InvalidCharForDigit => unreachable,
1341 error.DigitTooLargeForBase => unreachable,
1342 };
1343 errdefer int_val.base.deref(irb.comp);
1344
1345 const inst = try irb.build(Inst.Const, scope, Span.token(int_lit.token), Inst.Const.Params{});
1346 inst.val = IrVal{ .KnownValue = &int_val.base };
1347 return inst;
1348 }
1349
1350 pub fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
1351 const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token);
1352 const src_span = Span.token(str_lit.token);
1353
1354 var bad_index: usize = undefined;
1355 var buf = std.zig.parseStringLiteral(irb.comp.gpa(), str_token, &bad_index) catch |err| switch (err) {
1356 error.OutOfMemory => return error.OutOfMemory,
1357 error.InvalidCharacter => {
1358 try irb.comp.addCompileError(
1359 irb.code.tree_scope,
1360 src_span,
1361 "invalid character in string literal: '{c}'",
1362 .{str_token[bad_index]},
1363 );
1364 return error.SemanticAnalysisFailed;
1365 },
1366 };
1367 var buf_cleaned = false;
1368 errdefer if (!buf_cleaned) irb.comp.gpa().free(buf);
1369
1370 if (str_token[0] == 'c') {
1371 // first we add a null
1372 buf = try irb.comp.gpa().realloc(buf, buf.len + 1);
1373 buf[buf.len - 1] = 0;
1374
1375 // next make an array value
1376 const array_val = try Value.Array.createOwnedBuffer(irb.comp, buf);
1377 buf_cleaned = true;
1378 defer array_val.base.deref(irb.comp);
1379
1380 // then make a pointer value pointing at the first element
1381 const ptr_val = try Value.Ptr.createArrayElemPtr(
1382 irb.comp,
1383 array_val,
1384 .Const,
1385 .Many,
1386 0,
1387 );
1388 defer ptr_val.base.deref(irb.comp);
1389
1390 return irb.buildConstValue(scope, src_span, &ptr_val.base);
1391 } else {
1392 const array_val = try Value.Array.createOwnedBuffer(irb.comp, buf);
1393 buf_cleaned = true;
1394 defer array_val.base.deref(irb.comp);
1395
1396 return irb.buildConstValue(scope, src_span, &array_val.base);
1397 }
1398 }
1399
1400 pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
1401 const block_scope = try Scope.Block.create(irb.comp, parent_scope);
1402
1403 const outer_block_scope = &block_scope.base;
1404 var child_scope = outer_block_scope;
1405
1406 if (parent_scope.findFnDef()) |fndef_scope| {
1407 if (fndef_scope.fn_val.?.block_scope == null) {
1408 fndef_scope.fn_val.?.block_scope = block_scope;
1409 }
1410 }
1411
1412 if (block.statements.len == 0) {
1413 // {}
1414 return irb.buildConstVoid(child_scope, Span.token(block.lbrace), false);
1415 }
1416
1417 if (block.label) |label| {
1418 block_scope.incoming_values = std.ArrayList(*Inst).init(irb.arena());
1419 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());
1420 block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd");
1421 block_scope.is_comptime = try irb.buildConstBool(
1422 parent_scope,
1423 Span.token(block.lbrace),
1424 irb.isCompTime(parent_scope),
1425 );
1426 }
1427
1428 var is_continuation_unreachable = false;
1429 var noreturn_return_value: ?*Inst = null;
1430
1431 var stmt_it = block.statements.iterator(0);
1432 while (stmt_it.next()) |statement_node_ptr| {
1433 const statement_node = statement_node_ptr.*;
1434
1435 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
1436 // defer starts a new scope
1437 const defer_token = irb.code.tree_scope.tree.tokens.at(defer_node.defer_token);
1438 const kind = switch (defer_token.id) {
1439 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
1440 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
1441 else => unreachable,
1442 };
1443 const defer_expr_scope = try Scope.DeferExpr.create(irb.comp, parent_scope, defer_node.expr);
1444 const defer_child_scope = try Scope.Defer.create(irb.comp, parent_scope, kind, defer_expr_scope);
1445 child_scope = &defer_child_scope.base;
1446 continue;
1447 }
1448 const statement_value = try irb.genNodeRecursive(statement_node, child_scope, .None);
1449
1450 is_continuation_unreachable = statement_value.isNoReturn();
1451 if (is_continuation_unreachable) {
1452 // keep the last noreturn statement value around in case we need to return it
1453 noreturn_return_value = statement_value;
1454 }
1455
1456 if (statement_value.cast(Inst.DeclVar)) |decl_var| {
1457 // variable declarations start a new scope
1458 child_scope = decl_var.params.variable.child_scope;
1459 } else if (!is_continuation_unreachable) {
1460 // this statement's value must be void
1461 _ = try irb.build(
1462 Inst.CheckVoidStmt,
1463 child_scope,
1464 Span{
1465 .first = statement_node.firstToken(),
1466 .last = statement_node.lastToken(),
1467 },
1468 Inst.CheckVoidStmt.Params{ .target = statement_value },
1469 );
1470 }
1471 }
1472
1473 if (is_continuation_unreachable) {
1474 assert(noreturn_return_value != null);
1475 if (block.label == null or block_scope.incoming_blocks.len == 0) {
1476 return noreturn_return_value.?;
1477 }
1478
1479 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
1480 return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{
1481 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
1482 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
1483 });
1484 }
1485
1486 if (block.label) |label| {
1487 try block_scope.incoming_blocks.append(irb.current_basic_block);
1488 try block_scope.incoming_values.append(
1489 try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true),
1490 );
1491 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, .ScopeExit);
1492
1493 _ = try irb.buildGen(Inst.Br, parent_scope, Span.token(block.rbrace), Inst.Br.Params{
1494 .dest_block = block_scope.end_block,
1495 .is_comptime = block_scope.is_comptime,
1496 });
1497
1498 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
1499
1500 return irb.build(Inst.Phi, parent_scope, Span.token(block.rbrace), Inst.Phi.Params{
1501 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
1502 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
1503 });
1504 }
1505
1506 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, .ScopeExit);
1507 return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
1508 }
1509
1510 pub fn genControlFlowExpr(
1511 irb: *Builder,
1512 control_flow_expr: *ast.Node.ControlFlowExpression,
1513 scope: *Scope,
1514 lval: LVal,
1515 ) !*Inst {
1516 switch (control_flow_expr.kind) {
1517 .Break => |arg| return error.Unimplemented,
1518 .Continue => |arg| return error.Unimplemented,
1519 .Return => {
1520 const src_span = Span.token(control_flow_expr.ltoken);
1521 if (scope.findFnDef() == null) {
1522 try irb.comp.addCompileError(
1523 irb.code.tree_scope,
1524 src_span,
1525 "return expression outside function definition",
1526 .{},
1527 );
1528 return error.SemanticAnalysisFailed;
1529 }
1530
1531 if (scope.findDeferExpr()) |scope_defer_expr| {
1532 if (!scope_defer_expr.reported_err) {
1533 try irb.comp.addCompileError(
1534 irb.code.tree_scope,
1535 src_span,
1536 "cannot return from defer expression",
1537 .{},
1538 );
1539 scope_defer_expr.reported_err = true;
1540 }
1541 return error.SemanticAnalysisFailed;
1542 }
1543
1544 const outer_scope = irb.begin_scope.?;
1545 const return_value = if (control_flow_expr.rhs) |rhs| blk: {
1546 break :blk try irb.genNodeRecursive(rhs, scope, .None);
1547 } else blk: {
1548 break :blk try irb.buildConstVoid(scope, src_span, true);
1549 };
1550
1551 const defer_counts = irb.countDefers(scope, outer_scope);
1552 const have_err_defers = defer_counts.error_exit != 0;
1553 if (have_err_defers or irb.comp.have_err_ret_tracing) {
1554 const err_block = try irb.createBasicBlock(scope, "ErrRetErr");
1555 const ok_block = try irb.createBasicBlock(scope, "ErrRetOk");
1556 if (!have_err_defers) {
1557 _ = try irb.genDefersForBlock(scope, outer_scope, .ScopeExit);
1558 }
1559
1560 const is_err = try irb.build(
1561 Inst.TestErr,
1562 scope,
1563 src_span,
1564 Inst.TestErr.Params{ .target = return_value },
1565 );
1566
1567 const err_is_comptime = try irb.buildTestCompTime(scope, src_span, is_err);
1568
1569 _ = try irb.buildGen(Inst.CondBr, scope, src_span, Inst.CondBr.Params{
1570 .condition = is_err,
1571 .then_block = err_block,
1572 .else_block = ok_block,
1573 .is_comptime = err_is_comptime,
1574 });
1575
1576 const ret_stmt_block = try irb.createBasicBlock(scope, "RetStmt");
1577
1578 try irb.setCursorAtEndAndAppendBlock(err_block);
1579 if (have_err_defers) {
1580 _ = try irb.genDefersForBlock(scope, outer_scope, .ErrorExit);
1581 }
1582 if (irb.comp.have_err_ret_tracing and !irb.isCompTime(scope)) {
1583 _ = try irb.build(Inst.SaveErrRetAddr, scope, src_span, Inst.SaveErrRetAddr.Params{});
1584 }
1585 _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{
1586 .dest_block = ret_stmt_block,
1587 .is_comptime = err_is_comptime,
1588 });
1589
1590 try irb.setCursorAtEndAndAppendBlock(ok_block);
1591 if (have_err_defers) {
1592 _ = try irb.genDefersForBlock(scope, outer_scope, .ScopeExit);
1593 }
1594 _ = try irb.build(Inst.Br, scope, src_span, Inst.Br.Params{
1595 .dest_block = ret_stmt_block,
1596 .is_comptime = err_is_comptime,
1597 });
1598
1599 try irb.setCursorAtEndAndAppendBlock(ret_stmt_block);
1600 return irb.genAsyncReturn(scope, src_span, return_value, false);
1601 } else {
1602 _ = try irb.genDefersForBlock(scope, outer_scope, .ScopeExit);
1603 return irb.genAsyncReturn(scope, src_span, return_value, false);
1604 }
1605 },
1606 }
1607 }
1608
1609 pub fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
1610 const src_span = Span.token(identifier.token);
1611 const name = irb.code.tree_scope.tree.tokenSlice(identifier.token);
1612
1613 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
1614 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
1615 // const_instruction->base.value.type = get_pointer_to_type(irb->codegen,
1616 // irb->codegen->builtin_types.entry_void, false);
1617 // const_instruction->base.value.special = ConstValSpecialStatic;
1618 // const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard;
1619 // return &const_instruction->base;
1620 //}
1621
1622 if (irb.comp.getPrimitiveType(name)) |result| {
1623 if (result) |primitive_type| {
1624 defer primitive_type.base.deref(irb.comp);
1625 switch (lval) {
1626 // if (lval == LValPtr) {
1627 // return ir_build_ref(irb, scope, node, value, false, false);
1628 .Ptr => return error.Unimplemented,
1629 .None => return irb.buildConstValue(scope, src_span, &primitive_type.base),
1630 }
1631 }
1632 } else |err| switch (err) {
1633 error.Overflow => {
1634 try irb.comp.addCompileError(irb.code.tree_scope, src_span, "integer too large", .{});
1635 return error.SemanticAnalysisFailed;
1636 },
1637 error.OutOfMemory => return error.OutOfMemory,
1638 }
1639
1640 switch (irb.findIdent(scope, name)) {
1641 .Decl => |decl| {
1642 return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
1643 .decl = decl,
1644 .lval = lval,
1645 });
1646 },
1647 .VarScope => |var_scope| {
1648 const var_ptr = try irb.build(Inst.VarPtr, scope, src_span, Inst.VarPtr.Params{ .var_scope = var_scope });
1649 switch (lval) {
1650 .Ptr => return var_ptr,
1651 .None => {
1652 return irb.build(Inst.LoadPtr, scope, src_span, Inst.LoadPtr.Params{ .target = var_ptr });
1653 },
1654 }
1655 },
1656 .NotFound => {},
1657 }
1658
1659 //if (node->owner->any_imports_failed) {
1660 // // skip the error message since we had a failing import in this file
1661 // // if an import breaks we don't need redundant undeclared identifier errors
1662 // return irb->codegen->invalid_instruction;
1663 //}
1664
1665 // TODO put a variable of same name with invalid type in global scope
1666 // so that future references to this same name will find a variable with an invalid type
1667
1668 try irb.comp.addCompileError(irb.code.tree_scope, src_span, "unknown identifier '{}'", .{name});
1669 return error.SemanticAnalysisFailed;
1670 }
1671
1672 const DeferCounts = struct {
1673 scope_exit: usize,
1674 error_exit: usize,
1675 };
1676
1677 fn countDefers(irb: *Builder, inner_scope: *Scope, outer_scope: *Scope) DeferCounts {
1678 var result = DeferCounts{ .scope_exit = 0, .error_exit = 0 };
1679
1680 var scope = inner_scope;
1681 while (scope != outer_scope) {
1682 switch (scope.id) {
1683 .Defer => {
1684 const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope);
1685 switch (defer_scope.kind) {
1686 .ScopeExit => result.scope_exit += 1,
1687 .ErrorExit => result.error_exit += 1,
1688 }
1689 scope = scope.parent orelse break;
1690 },
1691 .FnDef => break,
1692
1693 .CompTime,
1694 .Block,
1695 .Decls,
1696 .Root,
1697 .Var,
1698 => scope = scope.parent orelse break,
1699
1700 .DeferExpr => unreachable,
1701 .AstTree => unreachable,
1702 }
1703 }
1704 return result;
1705 }
1706
1707 fn genDefersForBlock(
1708 irb: *Builder,
1709 inner_scope: *Scope,
1710 outer_scope: *Scope,
1711 gen_kind: Scope.Defer.Kind,
1712 ) !bool {
1713 var scope = inner_scope;
1714 var is_noreturn = false;
1715 while (true) {
1716 switch (scope.id) {
1717 .Defer => {
1718 const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope);
1719 const generate = switch (defer_scope.kind) {
1720 .ScopeExit => true,
1721 .ErrorExit => gen_kind == .ErrorExit,
1722 };
1723 if (generate) {
1724 const defer_expr_scope = defer_scope.defer_expr_scope;
1725 const instruction = try irb.genNodeRecursive(
1726 defer_expr_scope.expr_node,
1727 &defer_expr_scope.base,
1728 .None,
1729 );
1730 if (instruction.isNoReturn()) {
1731 is_noreturn = true;
1732 } else {
1733 _ = try irb.build(
1734 Inst.CheckVoidStmt,
1735 &defer_expr_scope.base,
1736 Span.token(defer_expr_scope.expr_node.lastToken()),
1737 Inst.CheckVoidStmt.Params{ .target = instruction },
1738 );
1739 }
1740 }
1741 },
1742 .FnDef,
1743 .Decls,
1744 .Root,
1745 => return is_noreturn,
1746
1747 .CompTime,
1748 .Block,
1749 .Var,
1750 => scope = scope.parent orelse return is_noreturn,
1751
1752 .DeferExpr => unreachable,
1753 .AstTree => unreachable,
1754 }
1755 }
1756 }
1757
1758 pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Inst, lval: LVal) !*Inst {
1759 switch (lval) {
1760 .None => return instruction,
1761 .Ptr => {
1762 // We needed a pointer to a value, but we got a value. So we create
1763 // an instruction which just makes a const pointer of it.
1764 return irb.build(Inst.Ref, scope, instruction.span, Inst.Ref.Params{
1765 .target = instruction,
1766 .mut = .Const,
1767 .volatility = .Non,
1768 });
1769 },
1770 }
1771 }
1772
1773 fn arena(self: *Builder) *Allocator {
1774 return &self.code.arena.allocator;
1775 }
1776
1777 fn buildExtra(
1778 self: *Builder,
1779 comptime I: type,
1780 scope: *Scope,
1781 span: Span,
1782 params: I.Params,
1783 is_generated: bool,
1784 ) !*Inst {
1785 const inst = try self.arena().create(I);
1786 inst.* = I{
1787 .base = Inst{
1788 .id = Inst.typeToId(I),
1789 .is_generated = is_generated,
1790 .scope = scope,
1791 .debug_id = self.next_debug_id,
1792 .val = switch (I.ir_val_init) {
1793 .Unknown => IrVal.Unknown,
1794 .NoReturn => IrVal{ .KnownValue = &Value.NoReturn.get(self.comp).base },
1795 .Void => IrVal{ .KnownValue = &Value.Void.get(self.comp).base },
1796 },
1797 .ref_count = 0,
1798 .span = span,
1799 .child = null,
1800 .parent = null,
1801 .llvm_value = undefined,
1802 .owner_bb = self.current_basic_block,
1803 },
1804 .params = params,
1805 };
1806
1807 // Look at the params and ref() other instructions
1808 inline for (@typeInfo(I.Params).Struct.fields) |f| {
1809 switch (f.field_type) {
1810 *Inst => @field(inst.params, f.name).ref(self),
1811 *BasicBlock => @field(inst.params, f.name).ref(self),
1812 ?*Inst => if (@field(inst.params, f.name)) |other| other.ref(self),
1813 []*Inst => {
1814 // TODO https://github.com/ziglang/zig/issues/1269
1815 for (@field(inst.params, f.name)) |other|
1816 other.ref(self);
1817 },
1818 []*BasicBlock => {
1819 // TODO https://github.com/ziglang/zig/issues/1269
1820 for (@field(inst.params, f.name)) |other|
1821 other.ref(self);
1822 },
1823 Type.Pointer.Mut,
1824 Type.Pointer.Vol,
1825 Type.Pointer.Size,
1826 LVal,
1827 *Decl,
1828 *Scope.Var,
1829 => {},
1830 // it's ok to add more types here, just make sure that
1831 // any instructions and basic blocks are ref'd appropriately
1832 else => @compileError("unrecognized type in Params: " ++ @typeName(f.field_type)),
1833 }
1834 }
1835
1836 self.next_debug_id += 1;
1837 try self.current_basic_block.instruction_list.append(&inst.base);
1838 return &inst.base;
1839 }
1840
1841 fn build(
1842 self: *Builder,
1843 comptime I: type,
1844 scope: *Scope,
1845 span: Span,
1846 params: I.Params,
1847 ) !*Inst {
1848 return self.buildExtra(I, scope, span, params, false);
1849 }
1850
1851 fn buildGen(
1852 self: *Builder,
1853 comptime I: type,
1854 scope: *Scope,
1855 span: Span,
1856 params: I.Params,
1857 ) !*Inst {
1858 return self.buildExtra(I, scope, span, params, true);
1859 }
1860
1861 fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Inst {
1862 const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{});
1863 inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base };
1864 return inst;
1865 }
1866
1867 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Inst {
1868 const inst = try self.buildExtra(Inst.Const, scope, span, Inst.Const.Params{}, is_generated);
1869 inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base };
1870 return inst;
1871 }
1872
1873 fn buildConstValue(self: *Builder, scope: *Scope, span: Span, v: *Value) !*Inst {
1874 const inst = try self.build(Inst.Const, scope, span, Inst.Const.Params{});
1875 inst.val = IrVal{ .KnownValue = v.getRef() };
1876 return inst;
1877 }
1878
1879 /// If the code is explicitly set to be comptime, then builds a const bool,
1880 /// otherwise builds a TestCompTime instruction.
1881 fn buildTestCompTime(self: *Builder, scope: *Scope, span: Span, target: *Inst) !*Inst {
1882 if (self.isCompTime(scope)) {
1883 return self.buildConstBool(scope, span, true);
1884 } else {
1885 return self.build(
1886 Inst.TestCompTime,
1887 scope,
1888 span,
1889 Inst.TestCompTime.Params{ .target = target },
1890 );
1891 }
1892 }
1893
1894 fn genAsyncReturn(irb: *Builder, scope: *Scope, span: Span, result: *Inst, is_gen: bool) !*Inst {
1895 _ = try irb.buildGen(
1896 Inst.AddImplicitReturnType,
1897 scope,
1898 span,
1899 Inst.AddImplicitReturnType.Params{ .target = result },
1900 );
1901
1902 if (!irb.is_async) {
1903 return irb.buildExtra(
1904 Inst.Return,
1905 scope,
1906 span,
1907 Inst.Return.Params{ .return_value = result },
1908 is_gen,
1909 );
1910 }
1911 return error.Unimplemented;
1912 }248 }
1913249 return .{ .line = line, .column = column };
1914 const Ident = union(enum) {
1915 NotFound,
1916 Decl: *Decl,
1917 VarScope: *Scope.Var,
1918 };
1919
1920 fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
1921 var s = scope;
1922 while (true) {
1923 switch (s.id) {
1924 .Root => return .NotFound,
1925 .Decls => {
1926 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1927 const locked_table = decls.table.acquireRead();
1928 defer locked_table.release();
1929 if (locked_table.value.get(name)) |entry| {
1930 return Ident{ .Decl = entry.value };
1931 }
1932 },
1933 .Var => {
1934 const var_scope = @fieldParentPtr(Scope.Var, "base", s);
1935 if (mem.eql(u8, var_scope.name, name)) {
1936 return Ident{ .VarScope = var_scope };
1937 }
1938 },
1939 else => {},
1940 }
1941 s = s.parent.?;
1942 }
1943 }
1944};
1945
1946const Analyze = struct {
1947 irb: Builder,
1948 old_bb_index: usize,
1949 const_predecessor_bb: ?*BasicBlock,
1950 parent_basic_block: *BasicBlock,
1951 instruction_index: usize,
1952 src_implicit_return_type_list: std.ArrayList(*Inst),
1953 explicit_return_type: ?*Type,
1954
1955 pub const Error = error{
1956 /// This is only for when we have already reported a compile error. It is the poison value.
1957 SemanticAnalysisFailed,
1958
1959 /// This is a placeholder - it is useful to use instead of panicking but once the compiler is
1960 /// done this error code will be removed.
1961 Unimplemented,
1962
1963 OutOfMemory,
1964 };
1965
1966 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, explicit_return_type: ?*Type) !Analyze {
1967 var irb = try Builder.init(comp, tree_scope, null);
1968 errdefer irb.abort();
1969
1970 return Analyze{
1971 .irb = irb,
1972 .old_bb_index = 0,
1973 .const_predecessor_bb = null,
1974 .parent_basic_block = undefined, // initialized with startBasicBlock
1975 .instruction_index = undefined, // initialized with startBasicBlock
1976 .src_implicit_return_type_list = std.ArrayList(*Inst).init(irb.arena()),
1977 .explicit_return_type = explicit_return_type,
1978 };
1979 }
1980
1981 pub fn abort(self: *Analyze) void {
1982 self.irb.abort();
1983 }
1984
1985 pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Inst) !*BasicBlock {
1986 if (old_bb.child) |child| {
1987 if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction)
1988 return child;
1989 }
1990
1991 const new_bb = try self.irb.createBasicBlock(old_bb.scope, old_bb.name_hint);
1992 new_bb.linkToParent(old_bb);
1993 new_bb.ref_instruction = ref_old_instruction;
1994 return new_bb;
1995 }
1996
1997 pub fn startBasicBlock(self: *Analyze, old_bb: *BasicBlock, const_predecessor_bb: ?*BasicBlock) void {
1998 self.instruction_index = 0;
1999 self.parent_basic_block = old_bb;
2000 self.const_predecessor_bb = const_predecessor_bb;
2001 }
2002
2003 pub fn finishBasicBlock(ira: *Analyze, old_code: *Code) !void {
2004 try ira.irb.code.basic_block_list.append(ira.irb.current_basic_block);
2005 ira.instruction_index += 1;
2006
2007 while (ira.instruction_index < ira.parent_basic_block.instruction_list.len) {
2008 const next_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);
2009
2010 if (!next_instruction.is_generated) {
2011 try ira.addCompileError(next_instruction.span, "unreachable code", .{});
2012 break;
2013 }
2014 ira.instruction_index += 1;
2015 }
2016
2017 ira.old_bb_index += 1;
2018
2019 var need_repeat = true;
2020 while (true) {
2021 while (ira.old_bb_index < old_code.basic_block_list.len) {
2022 const old_bb = old_code.basic_block_list.at(ira.old_bb_index);
2023 const new_bb = old_bb.child orelse {
2024 ira.old_bb_index += 1;
2025 continue;
2026 };
2027 if (new_bb.instruction_list.len != 0) {
2028 ira.old_bb_index += 1;
2029 continue;
2030 }
2031 ira.irb.current_basic_block = new_bb;
2032
2033 ira.startBasicBlock(old_bb, null);
2034 return;
2035 }
2036 if (!need_repeat)
2037 return;
2038 need_repeat = false;
2039 ira.old_bb_index = 0;
2040 continue;
2041 }
2042 }
2043
2044 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: var) !void {
2045 return self.irb.comp.addCompileError(self.irb.code.tree_scope, span, fmt, args);
2046 }
2047
2048 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
2049 // TODO actual implementation
2050 return &Type.Void.get(self.irb.comp).base;
2051 }
2052
2053 fn implicitCast(self: *Analyze, target: *Inst, optional_dest_type: ?*Type) Analyze.Error!*Inst {
2054 const dest_type = optional_dest_type orelse return target;
2055 const from_type = target.getKnownType();
2056 if (from_type == dest_type or from_type.id == .NoReturn) return target;
2057 return self.analyzeCast(target, target, dest_type);
2058 }
2059
2060 fn analyzeCast(ira: *Analyze, source_instr: *Inst, target: *Inst, dest_type: *Type) !*Inst {
2061 const from_type = target.getKnownType();
2062
2063 //if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {
2064 // return ira->codegen->invalid_instruction;
2065 //}
2066
2067 //// perfect match or non-const to const
2068 //ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type,
2069 // source_node, false);
2070 //if (const_cast_result.id == ConstCastResultIdOk) {
2071 // return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
2072 //}
2073
2074 //// widening conversion
2075 //if (wanted_type->id == TypeTableEntryIdInt &&
2076 // actual_type->id == TypeTableEntryIdInt &&
2077 // wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed &&
2078 // wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count)
2079 //{
2080 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
2081 //}
2082
2083 //// small enough unsigned ints can get casted to large enough signed ints
2084 //if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed &&
2085 // actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed &&
2086 // wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count)
2087 //{
2088 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
2089 //}
2090
2091 //// float widening conversion
2092 //if (wanted_type->id == TypeTableEntryIdFloat &&
2093 // actual_type->id == TypeTableEntryIdFloat &&
2094 // wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count)
2095 //{
2096 // return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
2097 //}
2098
2099 //// cast from [N]T to []const T
2100 //if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) {
2101 // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
2102 // assert(ptr_type->id == TypeTableEntryIdPointer);
2103 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2104 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2105 // source_node, false).id == ConstCastResultIdOk)
2106 // {
2107 // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
2108 // }
2109 //}
2110
2111 //// cast from *const [N]T to []const T
2112 //if (is_slice(wanted_type) &&
2113 // actual_type->id == TypeTableEntryIdPointer &&
2114 // actual_type->data.pointer.is_const &&
2115 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
2116 //{
2117 // TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
2118 // assert(ptr_type->id == TypeTableEntryIdPointer);
2119
2120 // TypeTableEntry *array_type = actual_type->data.pointer.child_type;
2121
2122 // if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
2123 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type,
2124 // source_node, false).id == ConstCastResultIdOk)
2125 // {
2126 // return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
2127 // }
2128 //}
2129
2130 //// cast from [N]T to *const []const T
2131 //if (wanted_type->id == TypeTableEntryIdPointer &&
2132 // wanted_type->data.pointer.is_const &&
2133 // is_slice(wanted_type->data.pointer.child_type) &&
2134 // actual_type->id == TypeTableEntryIdArray)
2135 //{
2136 // TypeTableEntry *ptr_type =
2137 // wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
2138 // assert(ptr_type->id == TypeTableEntryIdPointer);
2139 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2140 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2141 // source_node, false).id == ConstCastResultIdOk)
2142 // {
2143 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
2144 // if (type_is_invalid(cast1->value.type))
2145 // return ira->codegen->invalid_instruction;
2146
2147 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2148 // if (type_is_invalid(cast2->value.type))
2149 // return ira->codegen->invalid_instruction;
2150
2151 // return cast2;
2152 // }
2153 //}
2154
2155 //// cast from [N]T to ?[]const T
2156 //if (wanted_type->id == TypeTableEntryIdOptional &&
2157 // is_slice(wanted_type->data.maybe.child_type) &&
2158 // actual_type->id == TypeTableEntryIdArray)
2159 //{
2160 // TypeTableEntry *ptr_type =
2161 // wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
2162 // assert(ptr_type->id == TypeTableEntryIdPointer);
2163 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2164 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2165 // source_node, false).id == ConstCastResultIdOk)
2166 // {
2167 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);
2168 // if (type_is_invalid(cast1->value.type))
2169 // return ira->codegen->invalid_instruction;
2170
2171 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2172 // if (type_is_invalid(cast2->value.type))
2173 // return ira->codegen->invalid_instruction;
2174
2175 // return cast2;
2176 // }
2177 //}
2178
2179 //// *[N]T to [*]T
2180 //if (wanted_type->id == TypeTableEntryIdPointer &&
2181 // wanted_type->data.pointer.ptr_len == PtrLenUnknown &&
2182 // actual_type->id == TypeTableEntryIdPointer &&
2183 // actual_type->data.pointer.ptr_len == PtrLenSingle &&
2184 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray &&
2185 // actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment &&
2186 // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
2187 // actual_type->data.pointer.child_type->data.array.child_type, source_node,
2188 // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2189 //{
2190 // return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type);
2191 //}
2192
2193 //// *[N]T to []T
2194 //if (is_slice(wanted_type) &&
2195 // actual_type->id == TypeTableEntryIdPointer &&
2196 // actual_type->data.pointer.ptr_len == PtrLenSingle &&
2197 // actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
2198 //{
2199 // TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
2200 // assert(slice_ptr_type->id == TypeTableEntryIdPointer);
2201 // if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
2202 // actual_type->data.pointer.child_type->data.array.child_type, source_node,
2203 // !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
2204 // {
2205 // return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type);
2206 // }
2207 //}
2208
2209 //// cast from T to ?T
2210 //// note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
2211 //if (wanted_type->id == TypeTableEntryIdOptional) {
2212 // TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;
2213 // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node,
2214 // false).id == ConstCastResultIdOk)
2215 // {
2216 // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
2217 // } else if (actual_type->id == TypeTableEntryIdComptimeInt ||
2218 // actual_type->id == TypeTableEntryIdComptimeFloat)
2219 // {
2220 // if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) {
2221 // return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
2222 // } else {
2223 // return ira->codegen->invalid_instruction;
2224 // }
2225 // } else if (wanted_child_type->id == TypeTableEntryIdPointer &&
2226 // wanted_child_type->data.pointer.is_const &&
2227 // (actual_type->id == TypeTableEntryIdPointer || is_container(actual_type)))
2228 // {
2229 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_child_type, value);
2230 // if (type_is_invalid(cast1->value.type))
2231 // return ira->codegen->invalid_instruction;
2232
2233 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2234 // if (type_is_invalid(cast2->value.type))
2235 // return ira->codegen->invalid_instruction;
2236
2237 // return cast2;
2238 // }
2239 //}
2240
2241 //// cast from null literal to maybe type
2242 //if (wanted_type->id == TypeTableEntryIdOptional &&
2243 // actual_type->id == TypeTableEntryIdNull)
2244 //{
2245 // return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
2246 //}
2247
2248 //// cast from child type of error type to error type
2249 //if (wanted_type->id == TypeTableEntryIdErrorUnion) {
2250 // if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type,
2251 // source_node, false).id == ConstCastResultIdOk)
2252 // {
2253 // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
2254 // } else if (actual_type->id == TypeTableEntryIdComptimeInt ||
2255 // actual_type->id == TypeTableEntryIdComptimeFloat)
2256 // {
2257 // if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) {
2258 // return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
2259 // } else {
2260 // return ira->codegen->invalid_instruction;
2261 // }
2262 // }
2263 //}
2264
2265 //// cast from [N]T to E![]const T
2266 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2267 // is_slice(wanted_type->data.error_union.payload_type) &&
2268 // actual_type->id == TypeTableEntryIdArray)
2269 //{
2270 // TypeTableEntry *ptr_type =
2271 // wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry;
2272 // assert(ptr_type->id == TypeTableEntryIdPointer);
2273 // if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
2274 // types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
2275 // source_node, false).id == ConstCastResultIdOk)
2276 // {
2277 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
2278 // if (type_is_invalid(cast1->value.type))
2279 // return ira->codegen->invalid_instruction;
2280
2281 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2282 // if (type_is_invalid(cast2->value.type))
2283 // return ira->codegen->invalid_instruction;
2284
2285 // return cast2;
2286 // }
2287 //}
2288
2289 //// cast from error set to error union type
2290 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2291 // actual_type->id == TypeTableEntryIdErrorSet)
2292 //{
2293 // return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
2294 //}
2295
2296 //// cast from T to E!?T
2297 //if (wanted_type->id == TypeTableEntryIdErrorUnion &&
2298 // wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional &&
2299 // actual_type->id != TypeTableEntryIdOptional)
2300 //{
2301 // TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;
2302 // if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk ||
2303 // actual_type->id == TypeTableEntryIdNull ||
2304 // actual_type->id == TypeTableEntryIdComptimeInt ||
2305 // actual_type->id == TypeTableEntryIdComptimeFloat)
2306 // {
2307 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
2308 // if (type_is_invalid(cast1->value.type))
2309 // return ira->codegen->invalid_instruction;
2310
2311 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2312 // if (type_is_invalid(cast2->value.type))
2313 // return ira->codegen->invalid_instruction;
2314
2315 // return cast2;
2316 // }
2317 //}
2318
2319 // cast from comptime-known integer to another integer where the value fits
2320 if (target.isCompTime() and (from_type.id == .Int or from_type.id == .ComptimeInt)) cast: {
2321 const target_val = target.val.KnownValue;
2322 const from_int = &target_val.cast(Value.Int).?.big_int;
2323 const fits = fits: {
2324 if (dest_type.cast(Type.ComptimeInt)) |ctint| {
2325 break :fits true;
2326 }
2327 if (dest_type.cast(Type.Int)) |int| {
2328 break :fits from_int.fitsInTwosComp(int.key.is_signed, int.key.bit_count);
2329 }
2330 break :cast;
2331 };
2332 if (!fits) {
2333 try ira.addCompileError(source_instr.span, "integer value '{}' cannot be stored in type '{}'", .{
2334 from_int,
2335 dest_type.name,
2336 });
2337 return error.SemanticAnalysisFailed;
2338 }
2339
2340 const new_val = try target.copyVal(ira.irb.comp);
2341 new_val.setType(dest_type, ira.irb.comp);
2342 return ira.irb.buildConstValue(source_instr.scope, source_instr.span, new_val);
2343 }
2344
2345 // cast from number literal to another type
2346 // cast from number literal to *const integer
2347 //if (actual_type->id == TypeTableEntryIdComptimeFloat ||
2348 // actual_type->id == TypeTableEntryIdComptimeInt)
2349 //{
2350 // ensure_complete_type(ira->codegen, wanted_type);
2351 // if (type_is_invalid(wanted_type))
2352 // return ira->codegen->invalid_instruction;
2353 // if (wanted_type->id == TypeTableEntryIdEnum) {
2354 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value);
2355 // if (type_is_invalid(cast1->value.type))
2356 // return ira->codegen->invalid_instruction;
2357
2358 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2359 // if (type_is_invalid(cast2->value.type))
2360 // return ira->codegen->invalid_instruction;
2361
2362 // return cast2;
2363 // } else if (wanted_type->id == TypeTableEntryIdPointer &&
2364 // wanted_type->data.pointer.is_const)
2365 // {
2366 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
2367 // if (type_is_invalid(cast1->value.type))
2368 // return ira->codegen->invalid_instruction;
2369
2370 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2371 // if (type_is_invalid(cast2->value.type))
2372 // return ira->codegen->invalid_instruction;
2373
2374 // return cast2;
2375 // } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) {
2376 // CastOp op;
2377 // if ((actual_type->id == TypeTableEntryIdComptimeFloat &&
2378 // wanted_type->id == TypeTableEntryIdFloat) ||
2379 // (actual_type->id == TypeTableEntryIdComptimeInt &&
2380 // wanted_type->id == TypeTableEntryIdInt))
2381 // {
2382 // op = CastOpNumLitToConcrete;
2383 // } else if (wanted_type->id == TypeTableEntryIdInt) {
2384 // op = CastOpFloatToInt;
2385 // } else if (wanted_type->id == TypeTableEntryIdFloat) {
2386 // op = CastOpIntToFloat;
2387 // } else {
2388 // zig_unreachable();
2389 // }
2390 // return ir_resolve_cast(ira, source_instr, value, wanted_type, op, false);
2391 // } else {
2392 // return ira->codegen->invalid_instruction;
2393 // }
2394 //}
2395
2396 //// cast from typed number to integer or float literal.
2397 //// works when the number is known at compile time
2398 //if (instr_is_comptime(value) &&
2399 // ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) ||
2400 // (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat)))
2401 //{
2402 // return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
2403 //}
2404
2405 //// cast from union to the enum type of the union
2406 //if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) {
2407 // type_ensure_zero_bits_known(ira->codegen, actual_type);
2408 // if (type_is_invalid(actual_type))
2409 // return ira->codegen->invalid_instruction;
2410
2411 // if (actual_type->data.unionation.tag_type == wanted_type) {
2412 // return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type);
2413 // }
2414 //}
2415
2416 //// enum to union which has the enum as the tag type
2417 //if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
2418 // (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
2419 // wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
2420 //{
2421 // type_ensure_zero_bits_known(ira->codegen, wanted_type);
2422 // if (wanted_type->data.unionation.tag_type == actual_type) {
2423 // return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
2424 // }
2425 //}
2426
2427 //// enum to &const union which has the enum as the tag type
2428 //if (actual_type->id == TypeTableEntryIdEnum && wanted_type->id == TypeTableEntryIdPointer) {
2429 // TypeTableEntry *union_type = wanted_type->data.pointer.child_type;
2430 // if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||
2431 // union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)
2432 // {
2433 // type_ensure_zero_bits_known(ira->codegen, union_type);
2434 // if (union_type->data.unionation.tag_type == actual_type) {
2435 // IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_type, value);
2436 // if (type_is_invalid(cast1->value.type))
2437 // return ira->codegen->invalid_instruction;
2438
2439 // IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
2440 // if (type_is_invalid(cast2->value.type))
2441 // return ira->codegen->invalid_instruction;
2442
2443 // return cast2;
2444 // }
2445 // }
2446 //}
2447
2448 //// cast from *T to *[1]T
2449 //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
2450 // actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle)
2451 //{
2452 // TypeTableEntry *array_type = wanted_type->data.pointer.child_type;
2453 // if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 &&
2454 // types_match_const_cast_only(ira, array_type->data.array.child_type,
2455 // actual_type->data.pointer.child_type, source_node,
2456 // !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2457 // {
2458 // if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) {
2459 // ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
2460 // add_error_note(ira->codegen, msg, value->source_node,
2461 // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name),
2462 // actual_type->data.pointer.alignment));
2463 // add_error_note(ira->codegen, msg, source_instr->source_node,
2464 // buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name),
2465 // wanted_type->data.pointer.alignment));
2466 // return ira->codegen->invalid_instruction;
2467 // }
2468 // return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type);
2469 // }
2470 //}
2471
2472 //// cast from T to *T where T is zero bits
2473 //if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
2474 // types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
2475 // actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
2476 //{
2477 // type_ensure_zero_bits_known(ira->codegen, actual_type);
2478 // if (type_is_invalid(actual_type)) {
2479 // return ira->codegen->invalid_instruction;
2480 // }
2481 // if (!type_has_bits(actual_type)) {
2482 // return ir_get_ref(ira, source_instr, value, false, false);
2483 // }
2484 //}
2485
2486 //// cast from undefined to anything
2487 //if (actual_type->id == TypeTableEntryIdUndefined) {
2488 // return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);
2489 //}
2490
2491 //// cast from something to const pointer of it
2492 //if (!type_requires_comptime(actual_type)) {
2493 // TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
2494 // if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) {
2495 // return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);
2496 // }
2497 //}
2498
2499 try ira.addCompileError(source_instr.span, "expected type '{}', found '{}'", .{
2500 dest_type.name,
2501 from_type.name,
2502 });
2503 //ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node,
2504 // buf_sprintf("expected type '%s', found '%s'",
2505 // buf_ptr(&wanted_type->name),
2506 // buf_ptr(&actual_type->name)));
2507 //report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg);
2508 return error.SemanticAnalysisFailed;
2509 }
2510
2511 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Inst) ?*Value {
2512 @panic("TODO");
2513 }
2514
2515 fn getCompTimeRef(
2516 self: *Analyze,
2517 value: *Value,
2518 ptr_mut: Value.Ptr.Mut,
2519 mut: Type.Pointer.Mut,
2520 volatility: Type.Pointer.Vol,
2521 ) Analyze.Error!*Inst {
2522 return error.Unimplemented;
2523 }
2524};
2525
2526pub fn gen(
2527 comp: *Compilation,
2528 body_node: *ast.Node,
2529 tree_scope: *Scope.AstTree,
2530 scope: *Scope,
2531) !*Code {
2532 var irb = try Builder.init(comp, tree_scope, scope);
2533 errdefer irb.abort();
2534
2535 const entry_block = try irb.createBasicBlock(scope, "Entry");
2536 entry_block.ref(&irb); // Entry block gets a reference because we enter it to begin.
2537 try irb.setCursorAtEndAndAppendBlock(entry_block);
2538
2539 const result = try irb.genNode(body_node, scope, .None);
2540 if (!result.isNoReturn()) {
2541 // no need for save_err_ret_addr because this cannot return error
2542 _ = try irb.genAsyncReturn(scope, Span.token(body_node.lastToken()), result, true);
2543 }
2544
2545 return irb.finish();
2546}
2547
2548pub fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
2549 const old_entry_bb = old_code.basic_block_list.at(0);
2550
2551 var ira = try Analyze.init(comp, old_code.tree_scope, expected_type);
2552 errdefer ira.abort();
2553
2554 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
2555 new_entry_bb.ref(&ira.irb);
2556
2557 ira.irb.current_basic_block = new_entry_bb;
2558
2559 ira.startBasicBlock(old_entry_bb, null);
2560
2561 while (ira.old_bb_index < old_code.basic_block_list.len) {
2562 const old_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);
2563
2564 if (old_instruction.ref_count == 0 and !old_instruction.hasSideEffects()) {
2565 ira.instruction_index += 1;
2566 continue;
2567 }
2568
2569 const return_inst = try old_instruction.analyze(&ira);
2570 assert(return_inst.val != IrVal.Unknown); // at least the type should be known at this point
2571 return_inst.linkToParent(old_instruction);
2572 // Note: if we ever modify the above to handle error.CompileError by continuing analysis,
2573 // then here we want to check if ira.isCompTime() and return early if true
2574
2575 if (return_inst.isNoReturn()) {
2576 try ira.finishBasicBlock(old_code);
2577 continue;
2578 }
2579
2580 ira.instruction_index += 1;
2581 }
2582
2583 if (ira.src_implicit_return_type_list.len == 0) {
2584 ira.irb.code.return_type = &Type.NoReturn.get(comp).base;
2585 return ira.irb.finish();
2586 }
2587
2588 ira.irb.code.return_type = try ira.resolvePeerTypes(expected_type, ira.src_implicit_return_type_list.span());
2589 return ira.irb.finish();
2590}250}
src-self-hosted/value.zig+63-564
...@@ -1,587 +1,86 @@...@@ -1,587 +1,86 @@
1const std = @import("std");1const std = @import("std");
2const Scope = @import("scope.zig").Scope;
3const Compilation = @import("compilation.zig").Compilation;
4const ObjectFile = @import("codegen.zig").ObjectFile;
5const llvm = @import("llvm.zig");
6const ArrayListSentineled = std.ArrayListSentineled;
7const assert = std.debug.assert;
82
9/// Values are ref-counted, heap-allocated, and copy-on-write3/// This is the raw data, with no bookkeeping, no memory awareness,
10/// If there is only 1 ref then write need not copy4/// no de-duplication, and no type system awareness.
5/// It's important for this struct to be small.
6/// It is not copyable since it may contain references to its inner data.
11pub const Value = struct {7pub const Value = struct {
12 id: Id,8 tag: Tag,
13 typ: *Type,9
14 ref_count: std.atomic.Int(usize),10 pub const Tag = enum {
1511 void_type,
16 /// Thread-safe12 noreturn_type,
17 pub fn ref(base: *Value) void {13 bool_type,
18 _ = base.ref_count.incr();14 usize_type,
19 }15
2016 void_value,
21 /// Thread-safe17 noreturn_value,
22 pub fn deref(base: *Value, comp: *Compilation) void {18 bool_true,
23 if (base.ref_count.decr() == 1) {19 bool_false,
24 base.typ.base.deref(comp);20
25 switch (base.id) {21 array_sentinel_0_u8_type,
26 .Type => @fieldParentPtr(Type, "base", base).destroy(comp),22 single_const_ptr_type,
27 .Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),23
28 .FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp),24 int_u64,
29 .Void => @fieldParentPtr(Void, "base", base).destroy(comp),25 int_i64,
30 .Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),26 function,
31 .NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),27 ref,
32 .Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),28 bytes,
33 .Int => @fieldParentPtr(Int, "base", base).destroy(comp),
34 .Array => @fieldParentPtr(Array, "base", base).destroy(comp),
35 }
36 }
37 }
38
39 pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
40 base.typ.base.deref(comp);
41 new_type.base.ref();
42 base.typ = new_type;
43 }
44
45 pub fn getRef(base: *Value) *Value {
46 base.ref();
47 return base;
48 }
49
50 pub fn cast(base: *Value, comptime T: type) ?*T {
51 if (base.id != @field(Id, @typeName(T))) return null;
52 return @fieldParentPtr(T, "base", base);
53 }
54
55 pub fn dump(base: *const Value) void {
56 std.debug.warn("{}", .{@tagName(base.id)});
57 }
58
59 pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?*llvm.Value) {
60 switch (base.id) {
61 .Type => unreachable,
62 .Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile),
63 .FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
64 .Void => return null,
65 .Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
66 .NoReturn => unreachable,
67 .Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile),
68 .Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile),
69 .Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile),
70 }
71 }
72
73 pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
74 if (self.ref_count.get() == 1) {
75 // ( Í¡° ͜ʖ Í¡°)
76 return self;
77 }
78
79 assert(self.ref_count.decr() != 1);
80 return self.copy(comp);
81 }
82
83 pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
84 switch (base.id) {
85 .Type => unreachable,
86 .Fn => unreachable,
87 .FnProto => unreachable,
88 .Void => unreachable,
89 .Bool => unreachable,
90 .NoReturn => unreachable,
91 .Ptr => unreachable,
92 .Array => unreachable,
93 .Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
94 }
95 }
96
97 pub const Parent = union(enum) {
98 None,
99 BaseStruct: BaseStruct,
100 BaseArray: BaseArray,
101 BaseUnion: *Value,
102 BaseScalar: *Value,
103
104 pub const BaseStruct = struct {
105 val: *Value,
106 field_index: usize,
107 };
108
109 pub const BaseArray = struct {
110 val: *Value,
111 elem_index: usize,
112 };
113 };29 };
11430
115 pub const Id = enum {31 pub const Int_u64 = struct {
116 Type,32 base: Value = Value{ .tag = .int_u64 },
117 Fn,33 int: u64,
118 Void,
119 Bool,
120 NoReturn,
121 Array,
122 Ptr,
123 Int,
124 FnProto,
125 };34 };
12635
127 pub const Type = @import("type.zig").Type;36 pub const Int_i64 = struct {
12837 base: Value = Value{ .tag = .int_i64 },
129 pub const FnProto = struct {38 int: i64,
130 base: Value,
131
132 /// The main external name that is used in the .o file.
133 /// TODO https://github.com/ziglang/zig/issues/265
134 symbol_name: ArrayListSentineled(u8, 0),
135
136 pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: ArrayListSentineled(u8, 0)) !*FnProto {
137 const self = try comp.gpa().create(FnProto);
138 self.* = FnProto{
139 .base = Value{
140 .id = .FnProto,
141 .typ = &fn_type.base,
142 .ref_count = std.atomic.Int(usize).init(1),
143 },
144 .symbol_name = symbol_name,
145 };
146 fn_type.base.base.ref();
147 return self;
148 }
149
150 pub fn destroy(self: *FnProto, comp: *Compilation) void {
151 self.symbol_name.deinit();
152 comp.gpa().destroy(self);
153 }
154
155 pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?*llvm.Value {
156 const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
157 const llvm_fn = llvm.AddFunction(
158 ofile.module,
159 self.symbol_name.span(),
160 llvm_fn_type,
161 ) orelse return error.OutOfMemory;
162
163 // TODO port more logic from codegen.cpp:fn_llvm_value
164
165 return llvm_fn;
166 }
167 };39 };
16840
169 pub const Fn = struct {41 pub const Function = struct {
170 base: Value,42 base: Value = Value{ .tag = .function },
171
172 /// The main external name that is used in the .o file.
173 /// TODO https://github.com/ziglang/zig/issues/265
174 symbol_name: ArrayListSentineled(u8, 0),
175
176 /// parent should be the top level decls or container decls
177 fndef_scope: *Scope.FnDef,
178
179 /// parent is scope for last parameter
180 child_scope: *Scope,
181
182 /// parent is child_scope
183 block_scope: ?*Scope.Block,
184
185 /// Path to the object file that contains this function
186 containing_object: ArrayListSentineled(u8, 0),
187
188 link_set_node: *std.TailQueue(?*Value.Fn).Node,
189
190 /// Creates a Fn value with 1 ref
191 /// Takes ownership of symbol_name
192 pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: ArrayListSentineled(u8, 0)) !*Fn {
193 const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node);
194 link_set_node.* = Compilation.FnLinkSet.Node{
195 .data = null,
196 .next = undefined,
197 .prev = undefined,
198 };
199 errdefer comp.gpa().destroy(link_set_node);
200
201 const self = try comp.gpa().create(Fn);
202 self.* = Fn{
203 .base = Value{
204 .id = .Fn,
205 .typ = &fn_type.base,
206 .ref_count = std.atomic.Int(usize).init(1),
207 },
208 .fndef_scope = fndef_scope,
209 .child_scope = &fndef_scope.base,
210 .block_scope = null,
211 .symbol_name = symbol_name,
212 .containing_object = ArrayListSentineled(u8, 0).initNull(comp.gpa()),
213 .link_set_node = link_set_node,
214 };
215 fn_type.base.base.ref();
216 fndef_scope.fn_val = self;
217 fndef_scope.base.ref();
218 return self;
219 }
220
221 pub fn destroy(self: *Fn, comp: *Compilation) void {
222 // remove with a tombstone so that we do not have to grab a lock
223 if (self.link_set_node.data != null) {
224 // it's now the job of the link step to find this tombstone and
225 // deallocate it.
226 self.link_set_node.data = null;
227 } else {
228 comp.gpa().destroy(self.link_set_node);
229 }
230
231 self.containing_object.deinit();
232 self.fndef_scope.base.deref(comp);
233 self.symbol_name.deinit();
234 comp.gpa().destroy(self);
235 }
236
237 /// We know that the function definition will end up in an .o file somewhere.
238 /// Here, all we have to do is generate a global prototype.
239 /// TODO cache the prototype per ObjectFile
240 pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?*llvm.Value {
241 const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
242 const llvm_fn = llvm.AddFunction(
243 ofile.module,
244 self.symbol_name.span(),
245 llvm_fn_type,
246 ) orelse return error.OutOfMemory;
247
248 // TODO port more logic from codegen.cpp:fn_llvm_value
249
250 return llvm_fn;
251 }
252 };43 };
25344
254 pub const Void = struct {45 pub const ArraySentinel0_u8_Type = struct {
255 base: Value,46 base: Value = Value{ .tag = .array_sentinel_0_u8_type },
25647 len: u64,
257 pub fn get(comp: *Compilation) *Void {
258 comp.void_value.base.ref();
259 return comp.void_value;
260 }
261
262 pub fn destroy(self: *Void, comp: *Compilation) void {
263 comp.gpa().destroy(self);
264 }
265 };48 };
26649
267 pub const Bool = struct {50 pub const SingleConstPtrType = struct {
268 base: Value,51 base: Value = Value{ .tag = .single_const_ptr_type },
269 x: bool,52 elem_type: *Value,
270
271 pub fn get(comp: *Compilation, x: bool) *Bool {
272 if (x) {
273 comp.true_value.base.ref();
274 return comp.true_value;
275 } else {
276 comp.false_value.base.ref();
277 return comp.false_value;
278 }
279 }
280
281 pub fn destroy(self: *Bool, comp: *Compilation) void {
282 comp.gpa().destroy(self);
283 }
284
285 pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) !?*llvm.Value {
286 const llvm_type = llvm.Int1TypeInContext(ofile.context) orelse return error.OutOfMemory;
287 if (self.x) {
288 return llvm.ConstAllOnes(llvm_type);
289 } else {
290 return llvm.ConstNull(llvm_type);
291 }
292 }
293 };53 };
29454
295 pub const NoReturn = struct {55 pub const Ref = struct {
296 base: Value,56 base: Value = Value{ .tag = .ref },
29757 pointee: *MemoryCell,
298 pub fn get(comp: *Compilation) *NoReturn {
299 comp.noreturn_value.base.ref();
300 return comp.noreturn_value;
301 }
302
303 pub fn destroy(self: *NoReturn, comp: *Compilation) void {
304 comp.gpa().destroy(self);
305 }
306 };58 };
30759
308 pub const Ptr = struct {60 pub const Bytes = struct {
309 base: Value,61 base: Value = Value{ .tag = .bytes },
310 special: Special,62 data: []u8,
311 mut: Mut,63 };
31264};
313 pub const Mut = enum {
314 CompTimeConst,
315 CompTimeVar,
316 RunTime,
317 };
318
319 pub const Special = union(enum) {
320 Scalar: *Value,
321 BaseArray: BaseArray,
322 BaseStruct: BaseStruct,
323 HardCodedAddr: u64,
324 Discard,
325 };
32665
327 pub const BaseArray = struct {66pub const MemoryCell = struct {
328 val: *Value,67 parent: Parent,
329 elem_index: usize,68 contents: *Value,
330 };
33169
332 pub const BaseStruct = struct {70 pub const Parent = union(enum) {
333 val: *Value,71 none,
72 struct_field: struct {
73 struct_base: *MemoryCell,
334 field_index: usize,74 field_index: usize,
335 };75 },
33676 array_elem: struct {
337 pub fn createArrayElemPtr(77 array_base: *MemoryCell,
338 comp: *Compilation,
339 array_val: *Array,
340 mut: Type.Pointer.Mut,
341 size: Type.Pointer.Size,
342 elem_index: usize,78 elem_index: usize,
343 ) !*Ptr {79 },
344 array_val.base.ref();80 union_field: *MemoryCell,
345 errdefer array_val.base.deref(comp);81 err_union_code: *MemoryCell,
34682 err_union_payload: *MemoryCell,
347 const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type;83 optional_payload: *MemoryCell,
348 const ptr_type = try Type.Pointer.get(comp, Type.Pointer.Key{84 optional_flag: *MemoryCell,
349 .child_type = elem_type,
350 .mut = mut,
351 .vol = Type.Pointer.Vol.Non,
352 .size = size,
353 .alignment = .Abi,
354 });
355 var ptr_type_consumed = false;
356 errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
357
358 const self = try comp.gpa().create(Value.Ptr);
359 self.* = Value.Ptr{
360 .base = Value{
361 .id = .Ptr,
362 .typ = &ptr_type.base,
363 .ref_count = std.atomic.Int(usize).init(1),
364 },
365 .special = Special{
366 .BaseArray = BaseArray{
367 .val = &array_val.base,
368 .elem_index = 0,
369 },
370 },
371 .mut = Mut.CompTimeConst,
372 };
373 ptr_type_consumed = true;
374 errdefer comp.gpa().destroy(self);
375
376 return self;
377 }
378
379 pub fn destroy(self: *Ptr, comp: *Compilation) void {
380 comp.gpa().destroy(self);
381 }
382
383 pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?*llvm.Value {
384 const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
385 // TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
386 switch (self.special) {
387 .Scalar => |scalar| @panic("TODO"),
388 .BaseArray => |base_array| {
389 // TODO put this in one .o file only, and after that, generate extern references to it
390 const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
391 const ptr_bit_count = ofile.comp.target_ptr_bits;
392 const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
393 var indices = [_]*llvm.Value{
394 llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
395 llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
396 };
397 return llvm.ConstInBoundsGEP(
398 array_llvm_value,
399 @ptrCast([*]*llvm.Value, &indices),
400 @intCast(c_uint, indices.len),
401 ) orelse return error.OutOfMemory;
402 },
403 .BaseStruct => |base_struct| @panic("TODO"),
404 .HardCodedAddr => |addr| @panic("TODO"),
405 .Discard => unreachable,
406 }
407 }
408 };
409
410 pub const Array = struct {
411 base: Value,
412 special: Special,
413
414 pub const Special = union(enum) {
415 Undefined,
416 OwnedBuffer: []u8,
417 Explicit: Data,
418 };
419
420 pub const Data = struct {
421 parent: Parent,
422 elements: []*Value,
423 };
424
425 /// Takes ownership of buffer
426 pub fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
427 const u8_type = Type.Int.get_u8(comp);
428 defer u8_type.base.base.deref(comp);
429
430 const array_type = try Type.Array.get(comp, Type.Array.Key{
431 .elem_type = &u8_type.base,
432 .len = buffer.len,
433 });
434 errdefer array_type.base.base.deref(comp);
435
436 const self = try comp.gpa().create(Value.Array);
437 self.* = Value.Array{
438 .base = Value{
439 .id = .Array,
440 .typ = &array_type.base,
441 .ref_count = std.atomic.Int(usize).init(1),
442 },
443 .special = Special{ .OwnedBuffer = buffer },
444 };
445 errdefer comp.gpa().destroy(self);
446
447 return self;
448 }
449
450 pub fn destroy(self: *Array, comp: *Compilation) void {
451 switch (self.special) {
452 .Undefined => {},
453 .OwnedBuffer => |buf| {
454 comp.gpa().free(buf);
455 },
456 .Explicit => {},
457 }
458 comp.gpa().destroy(self);
459 }
460
461 pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?*llvm.Value {
462 switch (self.special) {
463 .Undefined => {
464 const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
465 return llvm.GetUndef(llvm_type);
466 },
467 .OwnedBuffer => |buf| {
468 const dont_null_terminate = 1;
469 const llvm_str_init = llvm.ConstStringInContext(
470 ofile.context,
471 buf.ptr,
472 @intCast(c_uint, buf.len),
473 dont_null_terminate,
474 ) orelse return error.OutOfMemory;
475 const str_init_type = llvm.TypeOf(llvm_str_init);
476 const global = llvm.AddGlobal(ofile.module, str_init_type, "") orelse return error.OutOfMemory;
477 llvm.SetInitializer(global, llvm_str_init);
478 llvm.SetLinkage(global, llvm.PrivateLinkage);
479 llvm.SetGlobalConstant(global, 1);
480 llvm.SetUnnamedAddr(global, 1);
481 llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type));
482 return global;
483 },
484 .Explicit => @panic("TODO"),
485 }
486
487 //{
488 // uint64_t len = type_entry->data.array.len;
489 // if (const_val->data.x_array.special == ConstArraySpecialUndef) {
490 // return LLVMGetUndef(type_entry->type_ref);
491 // }
492
493 // LLVMValueRef *values = allocate<LLVMValueRef>(len);
494 // LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
495 // bool make_unnamed_struct = false;
496 // for (uint64_t i = 0; i < len; i += 1) {
497 // ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
498 // LLVMValueRef val = gen_const_val(g, elem_value, "");
499 // values[i] = val;
500 // make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
501 // }
502 // if (make_unnamed_struct) {
503 // return LLVMConstStruct(values, len, true);
504 // } else {
505 // return LLVMConstArray(element_type_ref, values, (unsigned)len);
506 // }
507 //}
508 }
509 };
510
511 pub const Int = struct {
512 base: Value,
513 big_int: std.math.big.Int,
514
515 pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int {
516 const self = try comp.gpa().create(Value.Int);
517 self.* = Value.Int{
518 .base = Value{
519 .id = .Int,
520 .typ = typ,
521 .ref_count = std.atomic.Int(usize).init(1),
522 },
523 .big_int = undefined,
524 };
525 typ.base.ref();
526 errdefer comp.gpa().destroy(self);
527
528 self.big_int = try std.math.big.Int.init(comp.gpa());
529 errdefer self.big_int.deinit();
530
531 try self.big_int.setString(base, value);
532
533 return self;
534 }
535
536 pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?*llvm.Value {
537 switch (self.base.typ.id) {
538 .Int => {
539 const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
540 if (self.big_int.len() == 0) {
541 return llvm.ConstNull(type_ref);
542 }
543 const unsigned_val = if (self.big_int.len() == 1) blk: {
544 break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false));
545 } else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: {
546 break :blk llvm.ConstIntOfArbitraryPrecision(
547 type_ref,
548 @intCast(c_uint, self.big_int.len()),
549 @ptrCast([*]u64, self.big_int.limbs.ptr),
550 );
551 } else {
552 @compileError("std.math.Big.Int.Limb size does not match LLVM");
553 };
554 return if (self.big_int.isPositive()) unsigned_val else llvm.ConstNeg(unsigned_val);
555 },
556 .ComptimeInt => unreachable,
557 else => unreachable,
558 }
559 }
560
561 pub fn copy(old: *Int, comp: *Compilation) !*Int {
562 old.base.typ.base.ref();
563 errdefer old.base.typ.base.deref(comp);
564
565 const new = try comp.gpa().create(Value.Int);
566 new.* = Value.Int{
567 .base = Value{
568 .id = .Int,
569 .typ = old.base.typ,
570 .ref_count = std.atomic.Int(usize).init(1),
571 },
572 .big_int = undefined,
573 };
574 errdefer comp.gpa().destroy(new);
575
576 new.big_int = try old.big_int.clone();
577 errdefer new.big_int.deinit();
578
579 return new;
580 }
581
582 pub fn destroy(self: *Int, comp: *Compilation) void {
583 self.big_int.deinit();
584 comp.gpa().destroy(self);
585 }
586 };85 };
587};86};
test/stage2/ir.zig created+37
...@@ -0,0 +1,37 @@
1test "hello world IR" {
2 exeCmp(
3 \\@0 = "Hello, world!\n"
4 \\
5 \\@1 = fn({
6 \\ %0 : usize = 1 ;SYS_write
7 \\ %1 : usize = 1 ;STDOUT_FILENO
8 \\ %2 = ptrtoint(@0) ; msg ptr
9 \\ %3 = fieldptr(@0, "len") ; msg len ptr
10 \\ %4 = deref(%3) ; msg len
11 \\ %5 = asm("syscall",
12 \\ volatile=1,
13 \\ output="={rax}",
14 \\ inputs=["{rax}", "{rdi}", "{rsi}", "{rdx}"],
15 \\ clobbers=["rcx", "r11", "memory"],
16 \\ args=[%0, %1, %2, %4])
17 \\
18 \\ %6 : usize = 231 ;SYS_exit_group
19 \\ %7 : usize = 0 ;exit code
20 \\ %8 = asm("syscall",
21 \\ volatile=1,
22 \\ output="={rax}",
23 \\ inputs=["{rax}", "{rdi}"],
24 \\ clobbers=["rcx", "r11", "memory"],
25 \\ args=[%6, %7])
26 \\
27 \\ %9 = unreachable()
28 \\}, cc=naked);
29 \\
30 \\@2 = export("_start", @1)
31 ,
32 \\Hello, world!
33 \\
34 );
35}
36
37fn exeCmp(src: []const u8, expected_stdout: []const u8) void {}