authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-20 17:28:35-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-20 17:28:35-07:00
log460a9789a37e51de9c6f7067f26c4dd4a680d3ce
tree4cd1da5a62cf6ab15fd036ad4fc23e3d1c91b3b4
parentadc3371e964920ff856589de0621665f99f87580
signature Commit is signed but in an unrecognized format.

add var decls and comptime math


14 files changed, 428 insertions(+), 95 deletions(-)

CHANGELOG.md+8-4
......@@ -1,8 +1,12 @@
1## 0.1.1-alpha
1## 0.1.1
22
3- introduce `Report` and show errors during Tokenize, Parse, and Transform.
4- add flag --dump-ir for enabling ir dumps
3- feat: `var` declarations / runtime memory
4- feat: comptime binary operators
5- feat: introduce `Report` and show error reports
6- feat: show warnings for unused vars/lets/fns/params
7- feat: add flag --dump-ir for enabling ir dumps
8- fix: fix AIR generation for a block with a variable name
59
6## 0.1.0-alpha
10## 0.1.0
711
812initial demo of compiler pipeline in zig.
demo.txt deleted-25
......@@ -1,25 +0,0 @@
1let print = @js("console").log;
2
3fn doThing(x: float, y: float) -> float {
4 let math = if x < y {
5 x - y
6 } else b: {
7 let sum = x + y;
8 if sum > 100 {
9 break :b sum;
10 }
11 if x == y {
12 return 5;
13 }
14 sum * -sum
15 };
16 if x == y {
17 return 5;
18 }
19 let divisor = if x > 10 { 2 } else { 3 };
20 math / divisor
21}
22
23print(doThing(1, 6));
24print(doThing(6, 4));
25print(doThing(100, 52));
src/compiler/Air.Dump.zig+21-3
......@@ -77,6 +77,14 @@ const State = struct {
7777 try s.dumpRef(w, brk.value);
7878 try w.writeAll(")");
7979 },
80 .store => {
81 const store: Air.Store = air.instData(.store, inst);
82 try w.writeAll("(");
83 try s.dumpInstRef(w, store.memory);
84 try w.writeAll(", ");
85 try s.dumpRef(w, store.operand);
86 try w.writeAll(")");
87 },
8088 inline .add,
8189 .mul,
8290 .sub,
......@@ -92,12 +100,22 @@ const State = struct {
92100 try s.dumpRef(w, bin.rhs);
93101 try w.writeAll(")");
94102 },
95 inline .negate => |comptime_tag| {
103 inline .negate,
104 .bool_not,
105 => |comptime_tag| {
96106 const bin: Air.Un = air.instData(comptime_tag, inst);
97107 try w.writeAll("(");
98108 try s.dumpRef(w, bin.operand);
99109 try w.writeAll(")");
100110 },
111 .memory => {
112 const memory: Air.Memory = air.instData(.memory, inst);
113 try w.writeAll("(");
114 try s.dumpRef(w, memory.init);
115 try w.writeAll(", type: ");
116 try s.dumpValue(w, memory.ty);
117 try w.writeAll(")");
118 },
101119 .field => {
102120 const field: Air.Field = air.instData(.field, inst);
103121 try w.writeAll("(");
......@@ -138,14 +156,14 @@ const State = struct {
138156 }
139157
140158 fn dumpRef(s: *State, w: anytype, ref: Inst.Ref) !void {
141 switch (ref.unwrap()) {
159 switch (ref.decode()) {
142160 .inst => |i| try s.dumpInstRef(w, i),
143161 .value => |v| try s.dumpValue(w, v),
144162 }
145163 }
146164
147165 fn dumpRefRecurse(s: *State, w: anytype, ref: Inst.Ref) !void {
148 switch (ref.unwrap()) {
166 switch (ref.decode()) {
149167 .inst => |i| try s.dumpInst(w, i),
150168 .value => |v| try s.dumpValue(w, v),
151169 }
src/compiler/Air.zig+34-7
......@@ -21,6 +21,9 @@ pub const Inst = struct {
2121 arg,
2222 field,
2323
24 memory,
25 store,
26
2427 // bin
2528 add,
2629 sub,
......@@ -34,6 +37,7 @@ pub const Inst = struct {
3437
3538 // un
3639 negate,
40 bool_not,
3741
3842 pub fn Data(comptime tag: Tag) type {
3943 return switch (tag) {
......@@ -43,6 +47,9 @@ pub const Inst = struct {
4347 .@"if" => If,
4448 .call => Call,
4549 .arg => Arg,
50 .field => Field,
51 .memory => Memory,
52 .store => Store,
4653 .add,
4754 .sub,
4855 .mul,
......@@ -51,10 +58,7 @@ pub const Inst = struct {
5158 .gt,
5259 .eq,
5360 => Bin,
54 .negate,
55 => Un,
56 .field,
57 => Field,
61 .negate, .bool_not => Un,
5862 };
5963 }
6064 };
......@@ -71,7 +75,7 @@ pub const Inst = struct {
7175 inst: Index,
7276 };
7377
74 pub fn unwrap(ref: Ref) Decoded {
78 pub fn decode(ref: Ref) Decoded {
7579 return switch (ref.kind) {
7680 .value => .{ .value = Value.fromInterned(@enumFromInt(ref.data)) },
7781 .inst => .{ .inst = @enumFromInt(ref.data) },
......@@ -90,6 +94,15 @@ pub const Inst = struct {
9094 return if (opt != .none) @bitCast(@intFromEnum(opt)) else null;
9195 }
9296 };
97
98 pub fn isVoid(ref: Ref) bool {
99 return ref.kind == .value and
100 ref.decode().value.toIntern() == .void_value;
101 }
102
103 pub fn eql(ref: Ref, other: Ref) bool {
104 return @as(u32, @bitCast(ref)) == @as(u32, @bitCast(other));
105 }
93106 };
94107
95108 /// reference to another instruction in this air
......@@ -165,6 +178,11 @@ pub const Bin = struct {
165178 rhs: Inst.Ref,
166179};
167180
181pub const Store = struct {
182 memory: Inst.Index,
183 operand: Inst.Ref,
184};
185
168186pub const Un = struct {
169187 operand: Inst.Ref,
170188 unused: Unused = .zero,
......@@ -200,6 +218,11 @@ pub const If = struct {
200218 };
201219};
202220
221pub const Memory = struct {
222 init: Inst.Ref,
223 ty: Value,
224};
225
203226pub fn deinit(air: *Air, gpa: std.mem.Allocator) void {
204227 defer air.* = undefined;
205228 air.insts.deinit(gpa);
......@@ -227,7 +250,7 @@ pub fn instPayload(air: *const Air, comptime tag: Inst.Tag, index: Inst.Index) t
227250}
228251
229252pub fn typeOf(air: *const Air, ref: Inst.Ref, ip: *InternPool) InternPool.Index {
230 return switch (ref.unwrap()) {
253 return switch (ref.decode()) {
231254 .value => |v| v.type(ip).toIntern(),
232255 .inst => |i| air.typeOfIndex(i, ip),
233256 };
......@@ -244,7 +267,7 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPo
244267
245268 inline .add, .sub, .mul, .div => |t| air.typeOf(air.instData(t, index).lhs, ip),
246269
247 .lt, .gt, .eq => .bool_type,
270 .lt, .gt, .eq, .bool_not => .bool_type,
248271
249272 .arg => air.instData(.arg, index).type.toIntern(),
250273
......@@ -261,6 +284,10 @@ pub fn typeOfIndex(air: *const Air, index: Inst.Index, ip: *InternPool) InternPo
261284 return base_type.toKey(ip).type_function.return_type;
262285 },
263286
287 .memory => air.instData(.memory, index).ty.toIntern(),
288
289 .store => air.instData(.memory, air.instData(.store, index).memory).ty.toIntern(),
290
264291 .@"if" => air.instData(.@"if", index).data.get(air).union_ty.toIntern(),
265292 };
266293}
src/compiler/InternPool.zig+4
......@@ -202,6 +202,10 @@ pub const Index = enum(u32) {
202202 return .{ .kind = .value, .data = @intCast(@intFromEnum(index)) };
203203 }
204204
205 pub fn toValue(index: Index) @import("Value.zig") {
206 return .{ .ip_index = index };
207 }
208
205209 pub fn toKey(index: Index, ip: *const InternPool) Key {
206210 return ip.indexToKey(index);
207211 }
src/compiler/JsLink.zig+26-4
......@@ -198,7 +198,6 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty
198198 defer t.end();
199199
200200 switch (tree.nodeTag(node)) {
201 else => |tag| std.debug.panic("TODO: printExpr .{s}", .{@tagName(tag)}),
202201 .root => unreachable,
203202
204203 .plain_value => {
......@@ -302,20 +301,28 @@ fn printExpr(link: *JsLink, tree: *const JsTree, node: Node.Index, writer: anyty
302301 .eq,
303302 .lt,
304303 .gt,
304 .assign_add,
305 .assign_sub,
306 .assign_mul,
307 .assign_div,
305308 => |tag| {
306309 const bin: JsTree.Bin = tree.nodeData(tag, node);
307310 const op: Operator = @field(Operator, @tagName(tag));
308311 try link.printExpr(tree, bin.lhs, writer);
309312 try writer.writeAll(" ");
310 try writer.writeAll(op.symbol());
313 try writer.writeAll(comptime op.symbol());
311314 try writer.writeAll(" ");
312315 try link.printExpr(tree, bin.rhs, writer);
313316 },
314317
315 inline .negate => |tag| {
318 inline .negate,
319 .bool_not,
320 .pre_dec,
321 .pre_inc,
322 => |tag| {
316323 const un: JsTree.Un = tree.nodeData(tag, node);
317324 const op: Operator = @field(Operator, @tagName(tag));
318 try writer.writeAll(op.symbol());
325 try writer.writeAll(comptime op.symbol());
319326 try link.printExpr(tree, un.operand, writer);
320327 },
321328
......@@ -402,6 +409,10 @@ const Level = enum {
402409
403410const Operator = enum {
404411 assign,
412 assign_add,
413 assign_sub,
414 assign_mul,
415 assign_div,
405416 add,
406417 sub,
407418 mul,
......@@ -410,10 +421,17 @@ const Operator = enum {
410421 lt,
411422 gt,
412423 negate,
424 bool_not,
425 pre_inc,
426 pre_dec,
413427
414428 pub fn symbol(op: Operator) []const u8 {
415429 return switch (op) {
416430 .assign => "=",
431 .assign_add => "+=",
432 .assign_sub => "-=",
433 .assign_mul => "*=",
434 .assign_div => "/=",
417435 .add => "+",
418436 .sub => "-",
419437 .mul => "*",
......@@ -422,7 +440,11 @@ const Operator = enum {
422440 .lt => "<",
423441 .gt => ">",
424442
443 .pre_inc => "++",
444 .pre_dec => "++",
445
425446 .negate => "-",
447 .bool_not => "!",
426448 };
427449 }
428450};
src/compiler/JsNodeGen.zig+129-20
......@@ -145,6 +145,23 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void {
145145 };
146146 },
147147
148 .memory => {
149 const i = @intFromEnum(inst);
150 const data: Air.Memory = c.air.instData(.memory, inst);
151 const value: JsTree.Node.Index.Optional = if (data.init.isVoid())
152 .none
153 else
154 (try c.fetchExpression(data.init)).toOptional();
155
156 try c.addStmt(try c.add(.stmt_declare_local, .{
157 .identifier_index = c.next_id,
158 .init = value,
159 }));
160 const id = try c.add(.local, .{ .id = c.next_id });
161 c.next_id += 1;
162 c.air_node_map[i] = .{ .node = id };
163 },
164
148165 inline .gt,
149166 .lt,
150167 .eq,
......@@ -161,7 +178,69 @@ fn codeGenInst(c: *JsNodeGen, inst: Air.Inst.Index) Error!void {
161178 try c.markExpression(inst, node);
162179 },
163180
181 .store => {
182 const data: Air.Store = c.air.instData(.store, inst);
183
184 const target = try c.fetchInstExpression(data.memory);
185
186 if (data.operand.kind == .inst) {
187 const index = data.operand.decode().inst;
188 switch (c.air.instTag(index)) {
189 inline .add,
190 .sub,
191 .mul,
192 .div,
193 => |tag| {
194 const inner_data: Air.Bin = c.air.instData(tag, index);
195 if (inner_data.lhs.eql(data.memory.toRef())) {
196 if ((tag == .add or tag == .sub) and
197 inner_data.rhs.kind == .value)
198 {
199 if (inner_data.rhs.decode().value.toFloat(c.ip)) |f| {
200 if (f == 1) {
201 // convert this into ++ or --
202 const node = try c.add(
203 if (tag == .add)
204 .pre_inc
205 else
206 .pre_dec,
207 .{
208 .operand = target,
209 },
210 );
211 try c.markExpression(inst, node);
212
213 return;
214 }
215 }
216 }
217
218 // convert into a compound assignment
219 const node = try c.add(
220 @field(JsTree.Tag, "assign_" ++ @tagName(tag)),
221 .{
222 .lhs = target,
223 .rhs = try c.fetchExpression(inner_data.rhs),
224 },
225 );
226 try c.markExpression(inst, node);
227
228 return;
229 }
230 },
231 else => {},
232 }
233 }
234
235 const node = try c.add(.assign, .{
236 .lhs = target,
237 .rhs = try c.fetchExpression(data.operand),
238 });
239 try c.markExpression(inst, node);
240 },
241
164242 inline .negate,
243 .bool_not,
165244 => |tag| {
166245 const data: Air.Un = c.air.instData(tag, inst);
167246 const node = try c.add(@field(JsTree.Tag, @tagName(tag)), .{
......@@ -331,27 +410,32 @@ fn markExpression(c: *JsNodeGen, inst: Air.Inst.Index, node: Node.Index) !void {
331410 }
332411}
333412
334fn fetchExpression(c: *JsNodeGen, inst: Air.Inst.Ref) !Node.Index {
413fn fetchExpression(c: *JsNodeGen, ref: Air.Inst.Ref) !Node.Index {
335414 const t = tracer.trace(@src());
336415 defer t.end();
337416
338 return switch (inst.unwrap()) {
417 return switch (ref.decode()) {
339418 .value => |v| c.valueToExpression(v),
340 .inst => |index| {
341 const data = c.air_node_map[@intFromEnum(index)];
342 if (std.debug.runtime_safety) {
343 const flags: Air.Flags = c.air.instFlags(index);
344 switch (flags.uses) {
345 .none => unreachable, // incorrectly marked instruction as unused,
346 .single => {},
347 .multi => assert(c.nodes.items(.tag)[@intFromEnum(data.node)] == .local),
348 }
349 }
350 return data.node;
351 },
419 .inst => |index| fetchInstExpression(c, index),
352420 };
353421}
354422
423fn fetchInstExpression(c: *JsNodeGen, index: Air.Inst.Index) !Node.Index {
424 const t = tracer.trace(@src());
425 defer t.end();
426
427 const data = c.air_node_map[@intFromEnum(index)];
428 if (std.debug.runtime_safety) {
429 const flags: Air.Flags = c.air.instFlags(index);
430 switch (flags.uses) {
431 .none => unreachable, // incorrectly marked instruction as unused,
432 .single => {},
433 .multi => assert(c.nodes.items(.tag)[@intFromEnum(data.node)] == .local),
434 }
435 }
436 return data.node;
437}
438
355439fn valueToExpression(c: *JsNodeGen, v: Value) !Node.Index {
356440 const t = tracer.trace(@src());
357441 defer t.end();
......@@ -405,7 +489,7 @@ fn codeGenBranch(c: *JsNodeGen, inst: Air.Inst.Ref, temporary: ?Node.Index) !?Br
405489 const this_label = c.next_label;
406490
407491 var requires_label = false;
408 var items = switch (inst.unwrap()) {
492 var items = switch (inst.decode()) {
409493 .value => |v| items: {
410494 if (v.ip_index == .void_value)
411495 return null;
......@@ -499,11 +583,15 @@ fn codeGenBlock(c: *JsNodeGen, inst: Air.Inst.Index) !void {
499583 }));
500584}
501585
586// Ideally, we convert everything to an expression. This allows for much
587// more compact code. When that isnt possible (`return`, `while`, etc),
588// we detect that here and ensure to lower `if` to actual if statements,
589// and so on.
502590fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool {
503591 const t = tracer.trace(@src());
504592 defer t.end();
505593
506 return switch (inst.unwrap()) {
594 return switch (inst.decode()) {
507595 .value => true,
508596 .inst => |index| switch (c.air.instTag(index)) {
509597 .js_global_ref,
......@@ -518,12 +606,33 @@ fn canLowerToExpression(c: *const JsNodeGen, inst: Air.Inst.Ref) bool {
518606 .lt,
519607 .gt,
520608 .negate,
609 .bool_not,
610 .store,
521611 => true,
612
613 // JavaScript variable declarations require statements.
614 // Later, this could be made true if we can hoist temporaries up.
615 .memory => false,
616
617 // break/return must be a js statement
522618 .@"break",
523619 => false,
524 // TODO: these depend
525 .block => false,
526 .@"if" => false,
620
621 .block => {
622 // TODO: blocks that are
623 // - all statements except last are lowerable
624 // - the last statement is lowerable OR a break of this block
625 return false;
626 },
627
628 .@"if" => {
629 const call = c.air.instData(.@"if", index);
630 const data = call.data.get(c.air);
631
632 return c.canLowerToExpression(call.cond) and
633 c.canLowerToExpression(data.yes) and
634 c.canLowerToExpression(data.no);
635 },
527636 },
528637 };
529638}
......@@ -533,7 +642,7 @@ fn lowerToExpression(c: *JsNodeGen, ref: Air.Inst.Ref) !Node.Index {
533642 defer t.end();
534643
535644 assert(c.canLowerToExpression(ref));
536 return switch (ref.unwrap()) {
645 return switch (ref.decode()) {
537646 .value => |v| try c.valueToExpression(v),
538647 .inst => |index| {
539648 try c.codeGenInst(index);
src/compiler/JsTree.zig+13-1
......@@ -26,6 +26,10 @@ pub const Tag = enum {
2626 decl,
2727 // bin ops
2828 assign,
29 assign_add,
30 assign_sub,
31 assign_mul,
32 assign_div,
2933 add,
3034 sub,
3135 mul,
......@@ -37,9 +41,11 @@ pub const Tag = enum {
3741 call,
3842 field,
3943 ternary,
40 // un ops
44 // prefix ops
4145 negate,
4246 bool_not,
47 pre_inc,
48 pre_dec,
4349 // stmts
4450 /// `let ... = ...`
4551 stmt_declare_local,
......@@ -60,6 +66,10 @@ pub const Tag = enum {
6066 .global => GlobalRef,
6167 .decl => DeclRef,
6268 .assign,
69 .assign_add,
70 .assign_sub,
71 .assign_mul,
72 .assign_div,
6373 .add,
6474 .sub,
6575 .mul,
......@@ -71,6 +81,8 @@ pub const Tag = enum {
7181 .ternary => Ternary,
7282 .negate,
7383 .bool_not,
84 .pre_inc,
85 .pre_dec,
7486 => Un,
7587 .call => Call,
7688 .field => Field,
src/compiler/Sema.zig+137-17
......@@ -50,7 +50,7 @@ const InstMap = struct {
5050 map.* = undefined;
5151 }
5252
53 pub fn get(map: *InstMap, teg_code: Teg.Inst.Index) ?Air.Inst.Ref {
53 pub fn get(map: *const InstMap, teg_code: Teg.Inst.Index) ?Air.Inst.Ref {
5454 const source_i = @intFromEnum(teg_code);
5555 const start_i = @intFromEnum(map.start);
5656 assert(source_i >= start_i and source_i <= (start_i + map.items.len));
......@@ -58,7 +58,12 @@ const InstMap = struct {
5858 }
5959
6060 pub fn add(map: *InstMap, from: Teg.Inst.Index, to: Air.Inst.Ref) !void {
61 assert(map.get(from) == null);
61 if (assertWithMessage(map.get(from) == null)) {
62 std.debug.panic("%{d} is already mapped (to #{d})", .{
63 @intFromEnum(from),
64 @intFromEnum(map.get(from).?.decode().inst),
65 });
66 }
6267 const source_i = @intFromEnum(from);
6368 const start_i = @intFromEnum(map.start);
6469 map.items[source_i - start_i] = to.toOptional();
......@@ -128,7 +133,7 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {
128133 const root = (try s.analyzeBlock(
129134 fn_decl.body.toIndex() orelse
130135 @panic("TODO: please change this to Index. Ref is not allowed here anymore"),
131 )).unwrap().inst;
136 )).decode().inst;
132137 assert(@intFromEnum(root) == 0);
133138
134139 const func_prototype = try s.ip.get(s.gpa, .{
......@@ -153,13 +158,12 @@ pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) anyerror!Air.Inst.Ref {
153158 const t = tracer.trace(@src());
154159 defer t.end();
155160
156 const block = try s.addBlock();
157
158 try s.inst_map.add(in, block.block_index.toRef());
159
160161 const scratch_top = s.scratch.items.len;
161162 defer s.scratch.shrinkRetainingCapacity(scratch_top);
162163
164 const block = try s.addBlock();
165 try s.inst_map.add(in, block.block_index.toRef());
166
163167 var last: Air.Inst.Ref = undefined;
164168 const instructions = s.code.blockInstructions(in);
165169 assert(instructions.len != 0);
......@@ -194,8 +198,13 @@ fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
194198 defer t.end();
195199
196200 const out = try s.analyzeCodeInner(in);
197 if (s.code.instTag(in) != .block)
201
202 // Block refs are added automatically
203 if (s.code.instTag(in) == .block) {
204 assert(s.inst_map.get(in) != null);
205 } else {
198206 try s.inst_map.add(in, out);
207 }
199208
200209 return out;
201210}
......@@ -269,7 +278,11 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
269278 const rhs = try s.resolveRefRuntime(bin.rhs);
270279
271280 if (lhs.kind == .value and rhs.kind == .value) {
272 @panic("TODO: comptime math operation");
281 return (try s.comptimeBin(
282 @field(BinaryOp, @tagName(tag)),
283 lhs.decode().value,
284 rhs.decode().value,
285 ));
273286 }
274287
275288 const left_type = s.typeOfRef(lhs);
......@@ -284,7 +297,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
284297 .rhs = rhs,
285298 });
286299 },
287 inline .negate => |tag| {
300 inline .negate, .bool_not => |tag| {
288301 const un: Teg.Inst.RefNode = s.code.instData(tag, in);
289302
290303 const operand = try s.resolveRefRuntime(un.operand);
......@@ -301,7 +314,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
301314 .@"break" => {
302315 const brk: Teg.Inst.Break = s.code.instPayload(.@"break", in);
303316 const operand = try s.resolveRefRuntime(brk.operand);
304 const block_ref = s.inst_map.get(brk.block).?.unwrap().inst; // block ref is resolved by teg
317 const block_ref = s.inst_map.get(brk.block).?.decode().inst; // block ref is resolved by teg
305318 return s.addInstRef(.@"break", .{
306319 .block_ref = block_ref,
307320 .value = operand,
......@@ -398,12 +411,41 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
398411 },
399412 .block => {
400413 const block = try s.analyzeBlock(in);
401 switch (block.unwrap()) {
402 .inst => |t| try s.scratch.append(s.gpa, @intFromEnum(t)),
414 switch (block.decode()) {
415 .inst => |t| if (s.tempAir().instTag(t) == .block) {
416 try s.scratch.append(s.gpa, @intFromEnum(t));
417 },
403418 else => {},
404419 }
405420 return block;
406421 },
422 .memory => {
423 const data = s.code.instData(.memory, in);
424
425 const initial_value = try s.resolveRefRuntime(data.operand);
426
427 return try s.addInstRef(.memory, .{
428 .init = initial_value,
429 .ty = s.typeOfRef(initial_value).toValue(),
430 });
431 },
432 .store => {
433 const data: Teg.Inst.Store = s.code.instPayload(.store, in);
434 assert(s.code.instTag(data.memory) == .memory); // corrupt store instruction
435
436 const location = s.mapTeg(data.memory.toRef()).decode().inst;
437 const operand = try s.resolveRefRuntime(data.operand);
438
439 try s.validateType(
440 s.typeOfRef(operand).toValue(),
441 s.tempAir().instData(.memory, location).ty.ip_index,
442 );
443
444 return try s.addInstRef(.store, .{
445 .memory = location,
446 .operand = operand,
447 });
448 },
407449 else => |tag| std.debug.panic("TODO: analyze of .{s}", .{@tagName(tag)}),
408450 }
409451}
......@@ -420,19 +462,25 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref {
420462 const scratch_top = s.scratch.items.len;
421463 defer s.scratch.shrinkRetainingCapacity(scratch_top);
422464
465 std.debug.print("{d} {d}\n", .{ scratch_top, s.scratch.items.len });
423466 const visited = try s.resolveRefRuntime(in);
467 std.debug.print("{d} {d}\n", .{ scratch_top, s.scratch.items.len });
424468
425 if (scratch_top == s.scratch.items.len) {
469 if (scratch_top == s.scratch.items.len and visited.kind == .value) {
426470 return visited;
427471 }
428472
429473 if (visited.kind == .inst and
430 s.air_instructions.items(.tag)[@intFromEnum(visited.unwrap().inst)] == .block)
474 s.air_instructions.items(.tag)[@intFromEnum(visited.decode().inst)] == .block)
431475 {
432476 return visited;
433477 }
434478
435479 const block = try s.addBlock();
480 _ = try s.addInstRef(.@"break", .{
481 .block_ref = block.block_index,
482 .value = visited,
483 });
436484 return (try block.finish(s, scratch_top)).toRef();
437485}
438486
......@@ -443,7 +491,7 @@ fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air.
443491
444492 if (s.inst_map.get(index)) |air| {
445493 if (mark) {
446 switch (air.unwrap()) {
494 switch (air.decode()) {
447495 .inst => |i| s.markInst(i),
448496 else => {},
449497 }
......@@ -464,7 +512,7 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value {
464512 };
465513
466514 if (s.inst_map.get(in)) |air| {
467 switch (air.unwrap()) {
515 switch (air.decode()) {
468516 .value => |value| return value,
469517 else => {},
470518 }
......@@ -545,6 +593,70 @@ fn unionType(s: *Sema, a: Value, b: Value) Value {
545593 // return s.ip.get(s.gpa, .{ .@"union" = &.{ a, b } });
546594}
547595
596const BinaryOp = enum {
597 add,
598 sub,
599 mul,
600 div,
601 lt,
602 gt,
603 eq,
604};
605
606fn comptimeBin(s: *Sema, op: BinaryOp, lhs: Value, rhs: Value) !Air.Inst.Ref {
607 switch (op) {
608 .add => {
609 const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck / string add");
610 const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck / string add");
611 return (try s.ip.get(s.gpa, .{
612 .float = l + r,
613 })).toAir();
614 },
615 inline .sub, .mul, .div => |tag| {
616 const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck");
617 const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck");
618
619 const result = switch (tag) {
620 .sub => l - r,
621 .mul => l * r,
622 .div => l / r,
623 else => comptime unreachable,
624 };
625
626 return (try s.ip.get(s.gpa, .{
627 .float = result,
628 })).toAir();
629 },
630
631 inline .lt,
632 .gt,
633 => |tag| {
634 const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck");
635 const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck");
636 const result = switch (tag) {
637 .lt => l < r,
638 .gt => l > r,
639 else => comptime unreachable,
640 };
641 return @as(
642 InternPool.Index,
643 if (result) .bool_true else .bool_false,
644 ).toAir();
645 },
646
647 .eq,
648 => {
649 const l = lhs.toFloat(s.ip) orelse @panic("TODO: typecheck / non-numeric eq");
650 const r = rhs.toFloat(s.ip) orelse @panic("TODO: typecheck / non-numeric eq");
651 const result = l == r;
652 return @as(
653 InternPool.Index,
654 if (result) .bool_true else .bool_false,
655 ).toAir();
656 },
657 }
658}
659
548660/// --- helper ---
549661const IncompleteBlock = struct {
550662 block_index: Air.Inst.Index,
......@@ -655,6 +767,14 @@ const Allocator = std.mem.Allocator;
655767const assert = std.debug.assert;
656768const log = std.log.scoped(.sema);
657769
770inline fn assertWithMessage(cond: bool) bool {
771 if (std.debug.runtime_safety) {
772 return !cond;
773 } else if (!cond) {
774 unreachable; // assertion failure
775 }
776}
777
658778const Comp = @import("Comp.zig");
659779const File = Comp.File;
660780const Decl = Comp.Decl;
src/compiler/Teg.Dump.zig+8-1
......@@ -93,7 +93,6 @@ const State = struct {
9393 try tty.setColor(writer, .reset);
9494 },
9595 inline .field,
96 .store,
9796 .add,
9897 .sub,
9998 .mul,
......@@ -129,6 +128,14 @@ const State = struct {
129128 try s.dumpRefMarker(writer, data.operand);
130129 try writer.writeAll(")");
131130 },
131 .store => {
132 const data: Teg.Inst.Store = teg.instData(.store, inst).extra_index.get(teg);
133 try writer.writeAll("(");
134 try s.dumpRefMarker(writer, data.memory.toRef());
135 try writer.writeAll(", ");
136 try s.dumpRefMarker(writer, data.operand);
137 try writer.writeAll(")");
138 },
132139 .string => {
133140 const data: Teg.SlicedString = teg.instData(.string, inst).extra_index.get(teg);
134141 try tty.setColor(writer, .green);
src/compiler/Teg.zig+6-1
......@@ -91,6 +91,7 @@ pub const Inst = struct {
9191 .@"if" => PlNode(If),
9292 .load_decl => StrNode,
9393 .param => DataNode,
94 .store => PlNode(Store),
9495
9596 // Single instruction operand
9697 .memory,
......@@ -108,7 +109,6 @@ pub const Inst = struct {
108109
109110 // Two instruction operands
110111 .field,
111 .store,
112112 .add,
113113 .sub,
114114 .mul,
......@@ -258,6 +258,11 @@ pub const Inst = struct {
258258 rhs: Inst.Ref,
259259 };
260260
261 pub const Store = struct {
262 memory: Inst.Index,
263 operand: Inst.Ref,
264 };
265
261266 pub const As = struct {
262267 type: Inst.Ref,
263268 operand: Inst.Ref,
src/compiler/Transform.zig+6-6
......@@ -656,9 +656,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
656656 .tag = .store,
657657 .data = EncodedData.encode(.store, .{
658658 .src_node = decl.nodeOffset(node),
659 .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{
660 .lhs = lhs,
661 .rhs = rhs,
659 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{
660 .memory = lhs.toIndex() orelse @panic("TODO: convert this into Ref"),
661 .operand = rhs,
662662 }),
663663 }),
664664 });
......@@ -728,9 +728,9 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
728728 .tag = .store,
729729 .data = EncodedData.encode(.store, .{
730730 .src_node = decl.nodeOffset(node),
731 .extra_index = try t.extra.append(t.gpa, Inst.Bin, .{
732 .lhs = lhs,
733 .rhs = compute_inst,
731 .extra_index = try t.extra.append(t.gpa, Inst.Store, .{
732 .memory = lhs.toIndex() orelse @panic("TODO: convert this into Ref"),
733 .operand = compute_inst,
734734 }),
735735 }),
736736 });
src/compiler/Value.zig+11-3
......@@ -22,7 +22,7 @@ pub fn asBool(val: Value) i32 {
2222 };
2323}
2424
25pub fn asString(val: Value, ip: *InternPool) InternPool.String.WithLen {
25pub fn asString(val: Value, ip: *const InternPool) InternPool.String.WithLen {
2626 const index = val.toIntern();
2727 return ip.items.items(.data)[@intFromEnum(index)].decode(
2828 .string,
......@@ -31,7 +31,7 @@ pub fn asString(val: Value, ip: *InternPool) InternPool.String.WithLen {
3131 .get(ip);
3232}
3333
34pub fn @"type"(val: Value, ip: *InternPool) Value {
34pub fn @"type"(val: Value, ip: *const InternPool) Value {
3535 const v: InternPool.Index = switch (val.toKey(ip)) {
3636 .simple_type, .type_function => .type_type,
3737
......@@ -47,8 +47,16 @@ pub fn @"type"(val: Value, ip: *InternPool) Value {
4747 return Value.fromInterned(v);
4848}
4949
50pub fn toFloat(val: Value, ip: *const InternPool) ?f64 {
51 return switch (val.toKey(ip)) {
52 .int32 => |i| @floatFromInt(i),
53 .float => |f| f,
54 else => null,
55 };
56}
57
5058/// used for validating ensure_void instructions
51pub fn isEnsureVoid(val: Value, ip: *InternPool) bool {
59pub fn isEnsureVoid(val: Value, ip: *const InternPool) bool {
5260 const intern = val.toIntern();
5361 const i = @intFromEnum(intern);
5462
src/compiler/main.zig+25-3
......@@ -43,6 +43,7 @@ pub fn main() !void {
4343 const cwd = try std.posix.getcwd(&cwd_buf);
4444
4545 var exe_name: []const u8 = undefined;
46 var dump_ir = false;
4647
4748 {
4849 var args = try std.process.argsWithAllocator(alloc);
......@@ -52,8 +53,24 @@ pub fn main() !void {
5253 errdefer alloc.free(exe_name);
5354
5455 while (args.next()) |arg| {
55 maybe_file_path = try std.fs.path.resolve(alloc, &.{ cwd, arg });
56 break;
56 if (std.mem.startsWith(u8, arg, "-")) {
57 if (std.mem.eql(u8, arg, "--dump-ir")) {
58 dump_ir = true;
59 } else {
60 std.debug.print("error: unknown flag '{s}'\n", .{arg});
61 maybe_file_path = null;
62 break;
63 }
64 continue;
65 }
66
67 if (maybe_file_path == null) {
68 maybe_file_path = try std.fs.path.resolve(alloc, &.{ cwd, arg });
69 } else {
70 std.debug.print("error: multiple files specified\n", .{});
71 maybe_file_path = null;
72 break;
73 }
5774 }
5875 }
5976 defer alloc.free(exe_name);
......@@ -70,7 +87,12 @@ pub fn main() !void {
7087 std.process.exit(1);
7188 };
7289
73 var comp: Comp = .{ .gpa = tracer.tracingAllocator(&alloc) };
90 var comp: Comp = .{
91 .gpa = tracer.tracingAllocator(&alloc),
92 .options = .{
93 .dump_ir = dump_ir,
94 },
95 };
7496 try comp.init();
7597 defer comp.deinit();
7698 const main_file = try comp.getFile(file_path);