authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-20 13:11:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-20 13:12:20-07:00
logef91b11295a549a8173c488d9fd5b3f69b419829
tree4b2ef95d5e1dba821b77bfdc4f5e25db127249cc
parenta8065a05a5bc3df4036f1d7abe0928901cf7f5df

stage2: register allocator processes operand deaths

also rework the IR data structures

8 files changed, 698 insertions(+), 517 deletions(-)

src-self-hosted/Module.zig+162-61
......@@ -1349,8 +1349,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13491349fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
13501350 try self.analyzeBody(&block_scope.base, body);
13511351 for (block_scope.instructions.items) |inst| {
1352 if (inst.cast(Inst.Ret)) |ret| {
1353 const val = try self.resolveConstValue(&block_scope.base, ret.args.operand);
1352 if (inst.castTag(.ret)) |ret| {
1353 const val = try self.resolveConstValue(&block_scope.base, ret.operand);
13541354 return val.toType();
13551355 } else {
13561356 return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
......@@ -1938,16 +1938,132 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
19381938 };
19391939}
19401940
1941fn addNewInstArgs(
1941fn addNoOp(
19421942 self: *Module,
19431943 block: *Scope.Block,
19441944 src: usize,
19451945 ty: Type,
1946 comptime T: type,
1947 args: Inst.Args(T),
1946 comptime tag: Inst.Tag,
1947) !*Inst {
1948 const inst = try block.arena.create(tag.Type());
1949 inst.* = .{
1950 .base = .{
1951 .tag = tag,
1952 .ty = ty,
1953 .src = src,
1954 },
1955 };
1956 try block.instructions.append(self.gpa, &inst.base);
1957 return &inst.base;
1958}
1959
1960fn addUnOp(
1961 self: *Module,
1962 block: *Scope.Block,
1963 src: usize,
1964 ty: Type,
1965 tag: Inst.Tag,
1966 operand: *Inst,
1967) !*Inst {
1968 const inst = try block.arena.create(Inst.UnOp);
1969 inst.* = .{
1970 .base = .{
1971 .tag = tag,
1972 .ty = ty,
1973 .src = src,
1974 },
1975 .operand = operand,
1976 };
1977 try block.instructions.append(self.gpa, &inst.base);
1978 return &inst.base;
1979}
1980
1981fn addBinOp(
1982 self: *Module,
1983 block: *Scope.Block,
1984 src: usize,
1985 ty: Type,
1986 tag: Inst.Tag,
1987 lhs: *Inst,
1988 rhs: *Inst,
1989) !*Inst {
1990 const inst = try block.arena.create(Inst.BinOp);
1991 inst.* = .{
1992 .base = .{
1993 .tag = tag,
1994 .ty = ty,
1995 .src = src,
1996 },
1997 .lhs = lhs,
1998 .rhs = rhs,
1999 };
2000 try block.instructions.append(self.gpa, &inst.base);
2001 return &inst.base;
2002}
2003
2004fn addBr(
2005 self: *Module,
2006 scope_block: *Scope.Block,
2007 src: usize,
2008 target_block: *Inst.Block,
2009 operand: *Inst,
2010) !*Inst {
2011 const inst = try scope_block.arena.create(Inst.Br);
2012 inst.* = .{
2013 .base = .{
2014 .tag = .br,
2015 .ty = Type.initTag(.noreturn),
2016 .src = src,
2017 },
2018 .operand = operand,
2019 .block = target_block,
2020 };
2021 try scope_block.instructions.append(self.gpa, &inst.base);
2022 return &inst.base;
2023}
2024
2025fn addCondBr(
2026 self: *Module,
2027 block: *Scope.Block,
2028 src: usize,
2029 condition: *Inst,
2030 then_body: ir.Body,
2031 else_body: ir.Body,
19482032) !*Inst {
1949 const inst = try self.addNewInst(block, src, ty, T);
1950 inst.args = args;
2033 const inst = try block.arena.create(Inst.CondBr);
2034 inst.* = .{
2035 .base = .{
2036 .tag = .condbr,
2037 .ty = Type.initTag(.noreturn),
2038 .src = src,
2039 },
2040 .condition = condition,
2041 .then_body = then_body,
2042 .else_body = else_body,
2043 };
2044 try block.instructions.append(self.gpa, &inst.base);
2045 return &inst.base;
2046}
2047
2048fn addCall(
2049 self: *Module,
2050 block: *Scope.Block,
2051 src: usize,
2052 ty: Type,
2053 func: *Inst,
2054 args: []const *Inst,
2055) !*Inst {
2056 const inst = try block.arena.create(Inst.Call);
2057 inst.* = .{
2058 .base = .{
2059 .tag = .call,
2060 .ty = ty,
2061 .src = src,
2062 },
2063 .func = func,
2064 .args = args,
2065 };
2066 try block.instructions.append(self.gpa, &inst.base);
19512067 return &inst.base;
19522068}
19532069
......@@ -2017,7 +2133,6 @@ fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime
20172133 .ty = ty,
20182134 .src = src,
20192135 },
2020 .args = undefined,
20212136 };
20222137 try block.instructions.append(self.gpa, &inst.base);
20232138 return inst;
......@@ -2269,7 +2384,7 @@ fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!
22692384 });
22702385 }
22712386 const param_type = fn_ty.fnParamType(param_index);
2272 return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, {});
2387 return self.addNoOp(b, inst.base.src, param_type, .arg);
22732388}
22742389
22752390fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
......@@ -2285,7 +2400,7 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr
22852400 .ty = undefined, // Set after analysis.
22862401 .src = inst.base.src,
22872402 },
2288 .args = undefined,
2403 .body = undefined,
22892404 };
22902405
22912406 var child_block: Scope.Block = .{
......@@ -2316,13 +2431,13 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr
23162431 // to emit a jump instruction to after the block when it encounters the break.
23172432 try parent_block.instructions.append(self.gpa, &block_inst.base);
23182433 block_inst.base.ty = try self.resolvePeerTypes(scope, label.results.items);
2319 block_inst.args.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
2434 block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
23202435 return &block_inst.base;
23212436}
23222437
23232438fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
23242439 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2325 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, {});
2440 return self.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint);
23262441}
23272442
23282443fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
......@@ -2350,10 +2465,7 @@ fn analyzeBreak(
23502465 if (label.zir_block == zir_block) {
23512466 try label.results.append(self.gpa, operand);
23522467 const b = try self.requireRuntimeBlock(scope, src);
2353 return self.addNewInstArgs(b, src, Type.initTag(.noreturn), Inst.Br, .{
2354 .block = label.block_inst,
2355 .operand = operand,
2356 });
2468 return self.addBr(b, src, label.block_inst, operand);
23572469 }
23582470 }
23592471 opt_block = block.parent;
......@@ -2484,10 +2596,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
24842596 }
24852597
24862598 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2487 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, .{
2488 .func = func,
2489 .args = casted_args,
2490 });
2599 return self.addCall(b, inst.base.src, Type.initTag(.void), func, casted_args);
24912600}
24922601
24932602fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
......@@ -2570,14 +2679,14 @@ fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Ins
25702679}
25712680
25722681fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToInt) InnerError!*Inst {
2573 const ptr = try self.resolveInst(scope, ptrtoint.positionals.ptr);
2682 const ptr = try self.resolveInst(scope, ptrtoint.positionals.operand);
25742683 if (ptr.ty.zigTypeTag() != .Pointer) {
2575 return self.fail(scope, ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
2684 return self.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty});
25762685 }
25772686 // TODO handle known-pointer-address
25782687 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
25792688 const ty = Type.initTag(.usize);
2580 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, .{ .ptr = ptr });
2689 return self.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr);
25812690}
25822691
25832692fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
......@@ -2734,10 +2843,7 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!
27342843 }
27352844
27362845 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2737 return self.addNewInstArgs(b, inst.base.src, lhs.ty, Inst.Add, .{
2738 .lhs = lhs,
2739 .rhs = rhs,
2740 });
2846 return self.addBinOp(b, inst.base.src, lhs.ty, .add, lhs, rhs);
27412847 }
27422848 return self.fail(scope, inst.base.src, "TODO analyze add for {} + {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() });
27432849}
......@@ -2783,14 +2889,22 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr
27832889 }
27842890
27852891 const b = try self.requireRuntimeBlock(scope, assembly.base.src);
2786 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, .{
2892 const inst = try b.arena.create(Inst.Assembly);
2893 inst.* = .{
2894 .base = .{
2895 .tag = .assembly,
2896 .ty = return_type,
2897 .src = assembly.base.src,
2898 },
27872899 .asm_source = asm_source,
27882900 .is_volatile = assembly.kw_args.@"volatile",
27892901 .output = output,
27902902 .inputs = inputs,
27912903 .clobbers = clobbers,
27922904 .args = args,
2793 });
2905 };
2906 try b.instructions.append(self.gpa, &inst.base);
2907 return &inst.base;
27942908}
27952909
27962910fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!*Inst {
......@@ -2818,15 +2932,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
28182932 return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);
28192933 }
28202934 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2821 switch (op) {
2822 .eq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNull, .{
2823 .operand = opt_operand,
2824 }),
2825 .neq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNonNull, .{
2826 .operand = opt_operand,
2827 }),
2935 const inst_tag: Inst.Tag = switch (op) {
2936 .eq => .isnull,
2937 .neq => .isnonnull,
28282938 else => unreachable,
2829 }
2939 };
2940 return self.addUnOp(b, inst.base.src, Type.initTag(.bool), inst_tag, opt_operand);
28302941 } else if (is_equality_cmp and
28312942 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
28322943 {
......@@ -2861,7 +2972,7 @@ fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.BoolNot) Inn
28612972 return self.constBool(scope, inst.base.src, !val.toBool());
28622973 }
28632974 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2864 return self.addNewInstArgs(b, inst.base.src, bool_type, Inst.Not, .{ .operand = operand });
2975 return self.addUnOp(b, inst.base.src, bool_type, .not, operand);
28652976}
28662977
28672978fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {
......@@ -2879,7 +2990,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
28792990 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);
28802991
28812992 if (try self.resolveDefinedValue(scope, cond)) |cond_val| {
2882 const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body;
2993 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
28832994 try self.analyzeBody(scope, body.*);
28842995 return self.constVoid(scope, inst.base.src);
28852996 }
......@@ -2894,7 +3005,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
28943005 .arena = parent_block.arena,
28953006 };
28963007 defer true_block.instructions.deinit(self.gpa);
2897 try self.analyzeBody(&true_block.base, inst.positionals.true_body);
3008 try self.analyzeBody(&true_block.base, inst.positionals.then_body);
28983009
28993010 var false_block: Scope.Block = .{
29003011 .parent = parent_block,
......@@ -2904,13 +3015,11 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
29043015 .arena = parent_block.arena,
29053016 };
29063017 defer false_block.instructions.deinit(self.gpa);
2907 try self.analyzeBody(&false_block.base, inst.positionals.false_body);
3018 try self.analyzeBody(&false_block.base, inst.positionals.else_body);
29083019
2909 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.noreturn), Inst.CondBr, Inst.Args(Inst.CondBr){
2910 .condition = cond,
2911 .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },
2912 .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },
2913 });
3020 const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
3021 const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
3022 return self.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
29143023}
29153024
29163025fn wantSafety(self: *Module, scope: *Scope) bool {
......@@ -2926,20 +3035,20 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea
29263035 const b = try self.requireRuntimeBlock(scope, unreach.base.src);
29273036 if (self.wantSafety(scope)) {
29283037 // TODO Once we have a panic function to call, call it here instead of this.
2929 _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {});
3038 _ = try self.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);
29303039 }
2931 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
3040 return self.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
29323041}
29333042
29343043fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {
29353044 const operand = try self.resolveInst(scope, inst.positionals.operand);
29363045 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2937 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, .{ .operand = operand });
3046 return self.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
29383047}
29393048
29403049fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst {
29413050 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2942 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.RetVoid, {});
3051 return self.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
29433052}
29443053
29453054fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {
......@@ -3027,11 +3136,7 @@ fn cmpNumeric(
30273136 };
30283137 const casted_lhs = try self.coerce(scope, dest_type, lhs);
30293138 const casted_rhs = try self.coerce(scope, dest_type, rhs);
3030 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{
3031 .lhs = casted_lhs,
3032 .rhs = casted_rhs,
3033 .op = op,
3034 });
3139 return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
30353140 }
30363141 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
30373142 // For mixed signed and unsigned integers, implicit cast both operands to a signed
......@@ -3131,11 +3236,7 @@ fn cmpNumeric(
31313236 const casted_lhs = try self.coerce(scope, dest_type, lhs);
31323237 const casted_rhs = try self.coerce(scope, dest_type, rhs);
31333238
3134 return self.addNewInstArgs(b, src, Type.initTag(.bool), Inst.Cmp, .{
3135 .lhs = casted_lhs,
3136 .rhs = casted_rhs,
3137 .op = op,
3138 });
3239 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
31393240}
31403241
31413242fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
......@@ -3236,7 +3337,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
32363337 }
32373338 // TODO validate the type size and other compile errors
32383339 const b = try self.requireRuntimeBlock(scope, inst.src);
3239 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, .{ .operand = inst });
3340 return self.addUnOp(b, inst.src, dest_type, .bitcast, inst);
32403341}
32413342
32423343fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
src-self-hosted/astgen.zig+4-4
......@@ -173,8 +173,8 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
173173 const if_src = tree.token_locs[if_node.if_token].start;
174174 const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
175175 .condition = cond,
176 .true_body = undefined, // populated below
177 .false_body = undefined, // populated below
176 .then_body = undefined, // populated below
177 .else_body = undefined, // populated below
178178 }, .{});
179179
180180 const block = try mod.addZIRInstBlock(scope, if_src, .{
......@@ -196,7 +196,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
196196 .operand = then_result,
197197 }, .{});
198198 }
199 condbr.positionals.true_body = .{
199 condbr.positionals.then_body = .{
200200 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
201201 };
202202
......@@ -225,7 +225,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
225225 .block = block,
226226 }, .{});
227227 }
228 condbr.positionals.false_body = .{
228 condbr.positionals.else_body = .{
229229 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
230230 };
231231
src-self-hosted/codegen.zig+88-62
......@@ -290,6 +290,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
290290 next_stack_offset: u32 = 0,
291291
292292 fn markRegUsed(self: *Branch, reg: Register) void {
293 if (FreeRegInt == u0) return;
293294 const index = reg.allocIndex() orelse return;
294295 const ShiftInt = std.math.Log2Int(FreeRegInt);
295296 const shift = @intCast(ShiftInt, index);
......@@ -297,6 +298,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
297298 }
298299
299300 fn markRegFree(self: *Branch, reg: Register) void {
301 if (FreeRegInt == u0) return;
300302 const index = reg.allocIndex() orelse return;
301303 const ShiftInt = std.math.Log2Int(FreeRegInt);
302304 const shift = @intCast(ShiftInt, index);
......@@ -407,40 +409,64 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
407409 for (body.instructions) |inst| {
408410 const new_inst = try self.genFuncInst(inst);
409411 try inst_table.putNoClobber(self.gpa, inst, new_inst);
410 // TODO process operand deaths
412
413 var i: ir.Inst.DeathsBitIndex = 0;
414 while (inst.getOperand(i)) |operand| : (i += 1) {
415 if (inst.operandDies(i))
416 self.processDeath(operand);
417 }
418 }
419 }
420
421 fn processDeath(self: *Self, inst: *ir.Inst) void {
422 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
423 const entry = branch.inst_table.getEntry(inst) orelse return;
424 const prev_value = entry.value;
425 entry.value = .dead;
426 switch (prev_value) {
427 .register => |reg| {
428 _ = branch.registers.remove(reg);
429 branch.markRegFree(reg);
430 },
431 else => {}, // TODO process stack allocation death
411432 }
412433 }
413434
414435 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
415436 switch (inst.tag) {
416 .add => return self.genAdd(inst.cast(ir.Inst.Add).?),
417 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),
418 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
419 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
420 .block => return self.genBlock(inst.cast(ir.Inst.Block).?),
421 .br => return self.genBr(inst.cast(ir.Inst.Br).?),
437 .add => return self.genAdd(inst.castTag(.add).?),
438 .arg => return self.genArg(inst.castTag(.arg).?),
439 .assembly => return self.genAsm(inst.castTag(.assembly).?),
440 .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
441 .block => return self.genBlock(inst.castTag(.block).?),
442 .br => return self.genBr(inst.castTag(.br).?),
422443 .breakpoint => return self.genBreakpoint(inst.src),
423 .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?),
424 .call => return self.genCall(inst.cast(ir.Inst.Call).?),
425 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
426 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
444 .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?),
445 .call => return self.genCall(inst.castTag(.call).?),
446 .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
447 .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),
448 .cmp_eq => return self.genCmp(inst.castTag(.cmp_eq).?, .eq),
449 .cmp_gte => return self.genCmp(inst.castTag(.cmp_gte).?, .gte),
450 .cmp_gt => return self.genCmp(inst.castTag(.cmp_gt).?, .gt),
451 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
452 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
427453 .constant => unreachable, // excluded from function bodies
428 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),
429 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
430 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
431 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
432 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?),
433 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?),
454 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
455 .isnull => return self.genIsNull(inst.castTag(.isnull).?),
456 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
457 .ret => return self.genRet(inst.castTag(.ret).?),
458 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
459 .sub => return self.genSub(inst.castTag(.sub).?),
434460 .unreach => return MCValue{ .unreach = {} },
435 .not => return self.genNot(inst.cast(ir.Inst.Not).?),
461 .not => return self.genNot(inst.castTag(.not).?),
436462 }
437463 }
438464
439 fn genNot(self: *Self, inst: *ir.Inst.Not) !MCValue {
465 fn genNot(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
440466 // No side effects, so if it's unreferenced, do nothing.
441467 if (inst.base.isUnused())
442468 return MCValue.dead;
443 const operand = try self.resolveInst(inst.args.operand);
469 const operand = try self.resolveInst(inst.operand);
444470 switch (operand) {
445471 .dead => unreachable,
446472 .unreach => unreachable,
......@@ -473,36 +499,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
473499 .base = .{
474500 .tag = .constant,
475501 .deaths = 0,
476 .ty = inst.args.operand.ty,
477 .src = inst.args.operand.src,
502 .ty = inst.operand.ty,
503 .src = inst.operand.src,
478504 },
479505 .val = Value.initTag(.bool_true),
480506 };
481 return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);
507 return try self.genX8664BinMath(&inst.base, inst.operand, &imm.base, 6, 0x30);
482508 },
483509 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
484510 }
485511 }
486512
487 fn genAdd(self: *Self, inst: *ir.Inst.Add) !MCValue {
513 fn genAdd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
488514 // No side effects, so if it's unreferenced, do nothing.
489515 if (inst.base.isUnused())
490516 return MCValue.dead;
491517 switch (arch) {
492518 .x86_64 => {
493 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);
519 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 0, 0x00);
494520 },
495521 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
496522 }
497523 }
498524
499 fn genSub(self: *Self, inst: *ir.Inst.Sub) !MCValue {
525 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
500526 // No side effects, so if it's unreferenced, do nothing.
501527 if (inst.base.isUnused())
502528 return MCValue.dead;
503529 switch (arch) {
504530 .x86_64 => {
505 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);
531 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 5, 0x28);
506532 },
507533 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
508534 }
......@@ -625,7 +651,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
625651 }
626652 }
627653
628 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
654 fn genArg(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
629655 if (FreeRegInt == u0) {
630656 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
631657 }
......@@ -659,7 +685,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
659685 }
660686
661687 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
662 const fn_ty = inst.args.func.ty;
688 const fn_ty = inst.func.ty;
663689 const cc = fn_ty.fnCallingConvention();
664690 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
665691 defer self.gpa.free(param_types);
......@@ -671,8 +697,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
671697 switch (arch) {
672698 .x86_64 => {
673699 for (mc_args) |mc_arg, arg_i| {
674 const arg = inst.args.args[arg_i];
675 const arg_mcv = try self.resolveInst(inst.args.args[arg_i]);
700 const arg = inst.args[arg_i];
701 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
676702 switch (mc_arg) {
677703 .none => continue,
678704 .register => |reg| {
......@@ -694,7 +720,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
694720 }
695721 }
696722
697 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
723 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
698724 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
699725 const func = func_val.func;
700726 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
......@@ -742,16 +768,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
742768 return .unreach;
743769 }
744770
745 fn genRet(self: *Self, inst: *ir.Inst.Ret) !MCValue {
746 const operand = try self.resolveInst(inst.args.operand);
771 fn genRet(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
772 const operand = try self.resolveInst(inst.operand);
747773 return self.ret(inst.base.src, operand);
748774 }
749775
750 fn genRetVoid(self: *Self, inst: *ir.Inst.RetVoid) !MCValue {
776 fn genRetVoid(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
751777 return self.ret(inst.base.src, .none);
752778 }
753779
754 fn genCmp(self: *Self, inst: *ir.Inst.Cmp) !MCValue {
780 fn genCmp(self: *Self, inst: *ir.Inst.BinOp, op: std.math.CompareOperator) !MCValue {
755781 // No side effects, so if it's unreferenced, do nothing.
756782 if (inst.base.isUnused())
757783 return MCValue.dead;
......@@ -759,25 +785,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
759785 .x86_64 => {
760786 try self.code.ensureCapacity(self.code.items.len + 8);
761787
762 const lhs = try self.resolveInst(inst.args.lhs);
763 const rhs = try self.resolveInst(inst.args.rhs);
788 const lhs = try self.resolveInst(inst.lhs);
789 const rhs = try self.resolveInst(inst.rhs);
764790
765791 // There are 2 operands, destination and source.
766792 // Either one, but not both, can be a memory operand.
767793 // Source operand can be an immediate, 8 bits or 32 bits.
768794 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
769 try self.copyToNewRegister(inst.args.lhs)
795 try self.copyToNewRegister(inst.lhs)
770796 else
771797 lhs;
772798 // This instruction supports only signed 32-bit immediates at most.
773 const src_mcv = try self.limitImmediateType(inst.args.rhs, i32);
799 const src_mcv = try self.limitImmediateType(inst.rhs, i32);
774800
775801 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
776 const info = inst.args.lhs.ty.intInfo(self.target.*);
802 const info = inst.lhs.ty.intInfo(self.target.*);
777803 if (info.signed) {
778 return MCValue{ .compare_flags_signed = inst.args.op };
804 return MCValue{ .compare_flags_signed = op };
779805 } else {
780 return MCValue{ .compare_flags_unsigned = inst.args.op };
806 return MCValue{ .compare_flags_unsigned = op };
781807 }
782808 },
783809 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
......@@ -789,7 +815,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
789815 .x86_64 => {
790816 try self.code.ensureCapacity(self.code.items.len + 6);
791817
792 const cond = try self.resolveInst(inst.args.condition);
818 const cond = try self.resolveInst(inst.condition);
793819 switch (cond) {
794820 .compare_flags_signed => |cmp_op| {
795821 // Here we map to the opposite opcode because the jump is to the false branch.
......@@ -838,19 +864,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
838864 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
839865 const reloc = Reloc{ .rel32 = self.code.items.len };
840866 self.code.items.len += 4;
841 try self.genBody(inst.args.true_body);
867 try self.genBody(inst.then_body);
842868 try self.performReloc(inst.base.src, reloc);
843 try self.genBody(inst.args.false_body);
869 try self.genBody(inst.else_body);
844870 return MCValue.unreach;
845871 }
846872
847 fn genIsNull(self: *Self, inst: *ir.Inst.IsNull) !MCValue {
873 fn genIsNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
848874 switch (arch) {
849875 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
850876 }
851877 }
852878
853 fn genIsNonNull(self: *Self, inst: *ir.Inst.IsNonNull) !MCValue {
879 fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
854880 // Here you can specialize this instruction if it makes sense to, otherwise the default
855881 // will call genIsNull and invert the result.
856882 switch (arch) {
......@@ -864,7 +890,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
864890 }
865891 // A block is nothing but a setup to be able to jump to the end.
866892 defer inst.codegen.relocs.deinit(self.gpa);
867 try self.genBody(inst.args.body);
893 try self.genBody(inst.body);
868894
869895 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
870896
......@@ -883,17 +909,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
883909 }
884910
885911 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
886 if (!inst.args.operand.ty.hasCodeGenBits())
887 return self.brVoid(inst.base.src, inst.args.block);
912 if (!inst.operand.ty.hasCodeGenBits())
913 return self.brVoid(inst.base.src, inst.block);
888914
889 const operand = try self.resolveInst(inst.args.operand);
915 const operand = try self.resolveInst(inst.operand);
890916 switch (arch) {
891917 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
892918 }
893919 }
894920
895921 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
896 return self.brVoid(inst.base.src, inst.args.block);
922 return self.brVoid(inst.base.src, inst.block);
897923 }
898924
899925 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
......@@ -915,29 +941,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
915941 }
916942
917943 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
918 if (!inst.args.is_volatile and inst.base.isUnused())
944 if (!inst.is_volatile and inst.base.isUnused())
919945 return MCValue.dead;
920946 if (arch != .x86_64 and arch != .i386) {
921947 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
922948 }
923 for (inst.args.inputs) |input, i| {
949 for (inst.inputs) |input, i| {
924950 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
925951 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
926952 }
927953 const reg_name = input[1 .. input.len - 1];
928954 const reg = parseRegName(reg_name) orelse
929955 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
930 const arg = try self.resolveInst(inst.args.args[i]);
956 const arg = try self.resolveInst(inst.args[i]);
931957 try self.genSetReg(inst.base.src, reg, arg);
932958 }
933959
934 if (mem.eql(u8, inst.args.asm_source, "syscall")) {
960 if (mem.eql(u8, inst.asm_source, "syscall")) {
935961 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
936962 } else {
937963 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
938964 }
939965
940 if (inst.args.output) |output| {
966 if (inst.output) |output| {
941967 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
942968 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
943969 }
......@@ -1169,13 +1195,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11691195 }
11701196 }
11711197
1172 fn genPtrToInt(self: *Self, inst: *ir.Inst.PtrToInt) !MCValue {
1198 fn genPtrToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
11731199 // no-op
1174 return self.resolveInst(inst.args.ptr);
1200 return self.resolveInst(inst.operand);
11751201 }
11761202
1177 fn genBitCast(self: *Self, inst: *ir.Inst.BitCast) !MCValue {
1178 const operand = try self.resolveInst(inst.args.operand);
1203 fn genBitCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1204 const operand = try self.resolveInst(inst.operand);
11791205 return operand;
11801206 }
11811207
src-self-hosted/codegen/c.zig+10-11
......@@ -92,9 +92,9 @@ fn genFn(file: *C, decl: *Decl) !void {
9292 for (instructions) |inst| {
9393 try writer.writeAll("\n\t");
9494 switch (inst.tag) {
95 .assembly => try genAsm(file, inst.cast(Inst.Assembly).?, decl),
96 .call => try genCall(file, inst.cast(Inst.Call).?, decl),
97 .ret => try genRet(file, inst.cast(Inst.Ret).?, decl, tv.ty.fnReturnType()),
95 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
96 .call => try genCall(file, inst.castTag(.call).?, decl),
97 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),
9898 .retvoid => try file.main.writer().print("return;", .{}),
9999 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100100 }
......@@ -105,9 +105,9 @@ fn genFn(file: *C, decl: *Decl) !void {
105105 try writer.writeAll("}\n\n");
106106}
107107
108fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !void {
108fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !void {
109109 const writer = file.main.writer();
110 const ret_value = inst.args.operand;
110 const ret_value = inst.operand;
111111 const value = ret_value.value().?;
112112 if (expected_return_type.eql(ret_value.ty))
113113 return file.fail(decl.src(), "TODO return {}", .{expected_return_type})
......@@ -126,7 +126,7 @@ fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !v
126126fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
127127 const writer = file.main.writer();
128128 const header = file.header.writer();
129 if (inst.args.func.cast(Inst.Constant)) |func_inst| {
129 if (inst.func.castTag(.constant)) |func_inst| {
130130 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
131131 const target = func_val.func.owner_decl;
132132 const target_ty = target.typed_value.most_recent.typed_value.ty;
......@@ -144,7 +144,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
144144 } else {
145145 return file.fail(decl.src(), "TODO non-function call target?", .{});
146146 }
147 if (inst.args.args.len != 0) {
147 if (inst.args.len != 0) {
148148 return file.fail(decl.src(), "TODO function arguments", .{});
149149 }
150150 } else {
......@@ -152,14 +152,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
152152 }
153153}
154154
155fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {
156 const as = inst.args;
155fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
157156 const writer = file.main.writer();
158157 for (as.inputs) |i, index| {
159158 if (i[0] == '{' and i[i.len - 1] == '}') {
160159 const reg = i[1 .. i.len - 1];
161160 const arg = as.args[index];
162 if (arg.cast(Inst.Constant)) |c| {
161 if (arg.castTag(.constant)) |c| {
163162 if (c.val.tag() == .int_u64) {
164163 try writer.writeAll("register ");
165164 try renderType(file, writer, arg.ty, decl.src());
......@@ -190,7 +189,7 @@ fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {
190189 if (index > 0) {
191190 try writer.writeAll(", ");
192191 }
193 if (arg.cast(Inst.Constant)) |c| {
192 if (arg.castTag(.constant)) |c| {
194193 try writer.print("\"\"({}_constant)", .{reg});
195194 } else {
196195 // This is blocked by the earlier test
src-self-hosted/ir.zig+230-125
......@@ -55,7 +55,12 @@ pub const Inst = struct {
5555 breakpoint,
5656 brvoid,
5757 call,
58 cmp,
58 cmp_lt,
59 cmp_lte,
60 cmp_eq,
61 cmp_gte,
62 cmp_gt,
63 cmp_neq,
5964 condbr,
6065 constant,
6166 isnonnull,
......@@ -66,13 +71,80 @@ pub const Inst = struct {
6671 sub,
6772 unreach,
6873 not,
74
75 /// There is one-to-one correspondence between tag and type for now,
76 /// but this will not always be the case. For example, binary operations
77 /// such as + and - will have different tags but the same type.
78 pub fn Type(tag: Tag) type {
79 return switch (tag) {
80 .retvoid,
81 .unreach,
82 .arg,
83 .breakpoint,
84 => NoOp,
85
86 .ret,
87 .bitcast,
88 .not,
89 .isnonnull,
90 .isnull,
91 .ptrtoint,
92 => UnOp,
93
94 .add,
95 .sub,
96 .cmp_lt,
97 .cmp_lte,
98 .cmp_eq,
99 .cmp_gte,
100 .cmp_gt,
101 .cmp_neq,
102 => BinOp,
103
104 .assembly => Assembly,
105 .block => Block,
106 .br => Br,
107 .brvoid => BrVoid,
108 .call => Call,
109 .condbr => CondBr,
110 .constant => Constant,
111 };
112 }
113
114 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
115 return switch (op) {
116 .lt => .cmp_lt,
117 .lte => .cmp_lte,
118 .eq => .cmp_eq,
119 .gte => .cmp_gte,
120 .gt => .cmp_gt,
121 .neq => .cmp_neq,
122 };
123 }
69124 };
70125
126 /// Prefer `castTag` to this.
71127 pub fn cast(base: *Inst, comptime T: type) ?*T {
72 if (base.tag != T.base_tag)
73 return null;
128 if (@hasField(T, "base_tag")) {
129 return base.castTag(T.base_tag);
130 }
131 inline for (@typeInfo(Tag).Enum.fields) |field| {
132 const tag = @intToEnum(Tag, field.value);
133 if (base.tag == tag) {
134 if (T == tag.Type()) {
135 return @fieldParentPtr(T, "base", base);
136 }
137 return null;
138 }
139 }
140 unreachable;
141 }
74142
75 return @fieldParentPtr(T, "base", base);
143 pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
144 if (base.tag == tag) {
145 return @fieldParentPtr(tag.Type(), "base", base);
146 }
147 return null;
76148 }
77149
78150 pub fn Args(comptime T: type) type {
......@@ -88,186 +160,219 @@ pub const Inst = struct {
88160 return inst.val;
89161 }
90162
91 pub const Add = struct {
92 pub const base_tag = Tag.add;
163 pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
164 return switch (self.base.tag) {
165 .cmp_lt => .lt,
166 .cmp_lte => .lte,
167 .cmp_eq => .eq,
168 .cmp_gte => .gte,
169 .cmp_gt => .gt,
170 .cmp_neq => .neq,
171 else => null,
172 };
173 }
174
175 pub fn operandCount(base: *Inst) usize {
176 inline for (@typeInfo(Tag).Enum.fields) |field| {
177 const tag = @intToEnum(Tag, field.value);
178 if (tag == base.tag) {
179 return @fieldParentPtr(tag.Type(), "base", base).operandCount();
180 }
181 }
182 unreachable;
183 }
184
185 pub fn getOperand(base: *Inst, index: usize) ?*Inst {
186 inline for (@typeInfo(Tag).Enum.fields) |field| {
187 const tag = @intToEnum(Tag, field.value);
188 if (tag == base.tag) {
189 return @fieldParentPtr(tag.Type(), "base", base).getOperand(index);
190 }
191 }
192 unreachable;
193 }
194
195 pub const NoOp = struct {
93196 base: Inst,
94197
95 args: struct {
96 lhs: *Inst,
97 rhs: *Inst,
98 },
198 pub fn operandCount(self: *const NoOp) usize {
199 return 0;
200 }
201 pub fn getOperand(self: *const NoOp, index: usize) ?*Inst {
202 return null;
203 }
99204 };
100205
101 pub const Arg = struct {
102 pub const base_tag = Tag.arg;
206 pub const UnOp = struct {
103207 base: Inst,
104 args: void,
208 operand: *Inst,
209
210 pub fn operandCount(self: *const UnOp) usize {
211 return 1;
212 }
213 pub fn getOperand(self: *const UnOp, index: usize) ?*Inst {
214 if (index == 0)
215 return self.operand;
216 return null;
217 }
105218 };
106219
107 pub const Assembly = struct {
108 pub const base_tag = Tag.assembly;
220 pub const BinOp = struct {
109221 base: Inst,
222 lhs: *Inst,
223 rhs: *Inst,
110224
111 args: struct {
112 asm_source: []const u8,
113 is_volatile: bool,
114 output: ?[]const u8,
115 inputs: []const []const u8,
116 clobbers: []const []const u8,
117 args: []const *Inst,
118 },
225 pub fn operandCount(self: *const BinOp) usize {
226 return 2;
227 }
228 pub fn getOperand(self: *const BinOp, index: usize) ?*Inst {
229 var i = index;
230
231 if (i < 1)
232 return self.lhs;
233 i -= 1;
234
235 if (i < 1)
236 return self.rhs;
237 i -= 1;
238
239 return null;
240 }
119241 };
120242
121 pub const BitCast = struct {
122 pub const base_tag = Tag.bitcast;
243 pub const Assembly = struct {
244 pub const base_tag = Tag.assembly;
123245
124246 base: Inst,
125 args: struct {
126 operand: *Inst,
127 },
247 asm_source: []const u8,
248 is_volatile: bool,
249 output: ?[]const u8,
250 inputs: []const []const u8,
251 clobbers: []const []const u8,
252 args: []const *Inst,
253
254 pub fn operandCount(self: *const Assembly) usize {
255 return self.args.len;
256 }
257 pub fn getOperand(self: *const Assembly, index: usize) ?*Inst {
258 if (index < self.args.len)
259 return self.args[index];
260 return null;
261 }
128262 };
129263
130264 pub const Block = struct {
131265 pub const base_tag = Tag.block;
266
132267 base: Inst,
133 args: struct {
134 body: Body,
135 },
268 body: Body,
136269 /// This memory is reserved for codegen code to do whatever it needs to here.
137270 codegen: codegen.BlockData = .{},
271
272 pub fn operandCount(self: *const Block) usize {
273 return 0;
274 }
275 pub fn getOperand(self: *const Block, index: usize) ?*Inst {
276 return null;
277 }
138278 };
139279
140280 pub const Br = struct {
141281 pub const base_tag = Tag.br;
142 base: Inst,
143 args: struct {
144 block: *Block,
145 operand: *Inst,
146 },
147 };
148282
149 pub const Breakpoint = struct {
150 pub const base_tag = Tag.breakpoint;
151283 base: Inst,
152 args: void,
284 block: *Block,
285 operand: *Inst,
286
287 pub fn operandCount(self: *const Br) usize {
288 return 0;
289 }
290 pub fn getOperand(self: *const Br, index: usize) ?*Inst {
291 if (index == 0)
292 return self.operand;
293 return null;
294 }
153295 };
154296
155297 pub const BrVoid = struct {
156298 pub const base_tag = Tag.brvoid;
299
157300 base: Inst,
158 args: struct {
159 block: *Block,
160 },
301 block: *Block,
302
303 pub fn operandCount(self: *const BrVoid) usize {
304 return 0;
305 }
306 pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst {
307 return null;
308 }
161309 };
162310
163311 pub const Call = struct {
164312 pub const base_tag = Tag.call;
313
165314 base: Inst,
166 args: struct {
167 func: *Inst,
168 args: []const *Inst,
169 },
170 };
315 func: *Inst,
316 args: []const *Inst,
171317
172 pub const Cmp = struct {
173 pub const base_tag = Tag.cmp;
318 pub fn operandCount(self: *const Call) usize {
319 return self.args.len + 1;
320 }
321 pub fn getOperand(self: *const Call, index: usize) ?*Inst {
322 var i = index;
174323
175 base: Inst,
176 args: struct {
177 lhs: *Inst,
178 op: std.math.CompareOperator,
179 rhs: *Inst,
180 },
324 if (i < 1)
325 return self.func;
326 i -= 1;
327
328 if (i < self.args.len)
329 return self.args[i];
330 i -= self.args.len;
331
332 return null;
333 }
181334 };
182335
183336 pub const CondBr = struct {
184337 pub const base_tag = Tag.condbr;
185338
186339 base: Inst,
187 args: struct {
188 condition: *Inst,
189 true_body: Body,
190 false_body: Body,
191 },
340 condition: *Inst,
341 then_body: Body,
342 else_body: Body,
192343 /// Set of instructions whose lifetimes end at the start of one of the branches.
193344 /// The `true` branch is first: `deaths[0..true_death_count]`.
194345 /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`.
195346 deaths: [*]*Inst = undefined,
196347 true_death_count: u32 = 0,
197348 false_death_count: u32 = 0,
198 };
199349
200 pub const Not = struct {
201 pub const base_tag = Tag.not;
350 pub fn operandCount(self: *const CondBr) usize {
351 return 1;
352 }
353 pub fn getOperand(self: *const CondBr, index: usize) ?*Inst {
354 var i = index;
202355
203 base: Inst,
204 args: struct {
205 operand: *Inst,
206 },
356 if (i < 1)
357 return self.condition;
358 i -= 1;
359
360 return null;
361 }
207362 };
208363
209364 pub const Constant = struct {
210365 pub const base_tag = Tag.constant;
211 base: Inst,
212
213 val: Value,
214 };
215
216 pub const IsNonNull = struct {
217 pub const base_tag = Tag.isnonnull;
218
219 base: Inst,
220 args: struct {
221 operand: *Inst,
222 },
223 };
224
225 pub const IsNull = struct {
226 pub const base_tag = Tag.isnull;
227366
228367 base: Inst,
229 args: struct {
230 operand: *Inst,
231 },
232 };
233
234 pub const PtrToInt = struct {
235 pub const base_tag = Tag.ptrtoint;
236
237 base: Inst,
238 args: struct {
239 ptr: *Inst,
240 },
241 };
242
243 pub const Ret = struct {
244 pub const base_tag = Tag.ret;
245 base: Inst,
246 args: struct {
247 operand: *Inst,
248 },
249 };
250
251 pub const RetVoid = struct {
252 pub const base_tag = Tag.retvoid;
253 base: Inst,
254 args: void,
255 };
256
257 pub const Sub = struct {
258 pub const base_tag = Tag.sub;
259 base: Inst,
260
261 args: struct {
262 lhs: *Inst,
263 rhs: *Inst,
264 },
265 };
368 val: Value,
266369
267 pub const Unreach = struct {
268 pub const base_tag = Tag.unreach;
269 base: Inst,
270 args: void,
370 pub fn operandCount(self: *const Constant) usize {
371 return 0;
372 }
373 pub fn getOperand(self: *const Constant, index: usize) ?*Inst {
374 return null;
375 }
271376 };
272377};
273378
src-self-hosted/liveness.zig+25-65
......@@ -25,53 +25,38 @@ fn analyzeWithTable(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst,
2525 while (i != 0) {
2626 i -= 1;
2727 const base = body.instructions[i];
28 try analyzeInstGeneric(arena, table, base);
28 try analyzeInst(arena, table, base);
2929 }
3030}
3131
32fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void {
33 // Obtain the corresponding instruction type based on the tag type.
34 inline for (std.meta.declarations(ir.Inst)) |decl| {
35 switch (decl.data) {
36 .Type => |T| {
37 if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) {
38 if (T.base_tag == base.tag) {
39 return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base));
40 }
41 }
42 },
43 else => {},
44 }
45 }
46 unreachable;
47}
48
49fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void {
50 if (table.contains(&inst.base)) {
51 inst.base.deaths = 0;
32fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void {
33 if (table.contains(base)) {
34 base.deaths = 0;
5235 } else {
5336 // No tombstone for this instruction means it is never referenced,
5437 // and its birth marks its own death. Very metal 🤘
55 inst.base.deaths = 1 << ir.Inst.unreferenced_bit_index;
38 base.deaths = 1 << ir.Inst.unreferenced_bit_index;
5639 }
5740
58 switch (T) {
59 ir.Inst.Constant => return,
60 ir.Inst.Block => {
61 try analyzeWithTable(arena, table, inst.args.body);
41 switch (base.tag) {
42 .constant => return,
43 .block => {
44 const inst = base.castTag(.block).?;
45 try analyzeWithTable(arena, table, inst.body);
6246 // We let this continue so that it can possibly mark the block as
6347 // unreferenced below.
6448 },
65 ir.Inst.CondBr => {
49 .condbr => {
50 const inst = base.castTag(.condbr).?;
6651 var true_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
6752 defer true_table.deinit();
68 try true_table.ensureCapacity(inst.args.true_body.instructions.len);
69 try analyzeWithTable(arena, &true_table, inst.args.true_body);
53 try true_table.ensureCapacity(inst.then_body.instructions.len);
54 try analyzeWithTable(arena, &true_table, inst.then_body);
7055
7156 var false_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
7257 defer false_table.deinit();
73 try false_table.ensureCapacity(inst.args.false_body.instructions.len);
74 try analyzeWithTable(arena, &false_table, inst.args.false_body);
58 try false_table.ensureCapacity(inst.else_body.instructions.len);
59 try analyzeWithTable(arena, &false_table, inst.else_body);
7560
7661 // Each death that occurs inside one branch, but not the other, needs
7762 // to be added as a death immediately upon entering the other branch.
......@@ -112,47 +97,22 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
11297 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
11398 // condition's lifetime ends immediately before entering any branch.
11499 },
115 ir.Inst.Call => {
116 // Call instructions have a runtime-known number of operands so we have to handle them ourselves here.
117 const needed_bits = 1 + inst.args.args.len;
118 if (needed_bits <= ir.Inst.deaths_bits) {
119 var bit_i: ir.Inst.DeathsBitIndex = 0;
120 {
121 const prev = try table.fetchPut(inst.args.func, {});
122 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
123 bit_i += 1;
124 }
125 for (inst.args.args) |arg| {
126 const prev = try table.fetchPut(arg, {});
127 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
128 bit_i += 1;
129 }
130 } else {
131 @panic("Handle liveness analysis for function calls with many parameters");
132 }
133 },
134100 else => {},
135101 }
136102
137 const Args = ir.Inst.Args(T);
138 if (Args == void) {
139 return;
140 }
141
142 comptime var arg_index: usize = 0;
143 inline for (std.meta.fields(Args)) |field| {
144 if (field.field_type == *ir.Inst) {
145 if (arg_index >= 6) {
146 @compileError("out of bits to mark deaths of operands");
147 }
148 const prev = try table.fetchPut(@field(inst.args, field.name), {});
103 const needed_bits = base.operandCount();
104 if (needed_bits <= ir.Inst.deaths_bits) {
105 var bit_i: ir.Inst.DeathsBitIndex = 0;
106 while (base.getOperand(bit_i)) |operand| : (bit_i += 1) {
107 const prev = try table.fetchPut(operand, {});
149108 if (prev == null) {
150109 // Death.
151 inst.base.deaths |= 1 << arg_index;
110 base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
152111 }
153 arg_index += 1;
154112 }
113 } else {
114 @panic("Handle liveness analysis for instructions with many parameters");
155115 }
156116
157 std.log.debug(.liveness, "analyze {}: 0b{b}\n", .{ inst.base.tag, inst.base.deaths });
117 std.log.debug(.liveness, "analyze {}: 0b{b}\n", .{ base.tag, base.deaths });
158118}
src-self-hosted/zir.zig+142-189
......@@ -337,7 +337,7 @@ pub const Inst = struct {
337337 base: Inst,
338338
339339 positionals: struct {
340 ptr: *Inst,
340 operand: *Inst,
341341 },
342342 kw_args: struct {},
343343 };
......@@ -629,8 +629,8 @@ pub const Inst = struct {
629629
630630 positionals: struct {
631631 condition: *Inst,
632 true_body: Module.Body,
633 false_body: Module.Body,
632 then_body: Module.Body,
633 else_body: Module.Body,
634634 },
635635 kw_args: struct {},
636636 };
......@@ -1615,7 +1615,7 @@ const EmitZIR = struct {
16151615 }
16161616 }
16171617
1618 fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
1618 fn emitNoOp(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
16191619 const new_inst = try self.arena.allocator.create(T);
16201620 new_inst.* = .{
16211621 .base = .{
......@@ -1628,6 +1628,72 @@ const EmitZIR = struct {
16281628 return &new_inst.base;
16291629 }
16301630
1631 fn emitCmp(
1632 self: *EmitZIR,
1633 src: usize,
1634 new_body: ZirBody,
1635 old_inst: *ir.Inst.BinOp,
1636 op: std.math.CompareOperator,
1637 ) Allocator.Error!*Inst {
1638 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1639 new_inst.* = .{
1640 .base = .{
1641 .src = src,
1642 .tag = Inst.Cmp.base_tag,
1643 },
1644 .positionals = .{
1645 .lhs = try self.resolveInst(new_body, old_inst.lhs),
1646 .rhs = try self.resolveInst(new_body, old_inst.rhs),
1647 .op = op,
1648 },
1649 .kw_args = .{},
1650 };
1651 return &new_inst.base;
1652 }
1653
1654 fn emitUnOp(
1655 self: *EmitZIR,
1656 src: usize,
1657 new_body: ZirBody,
1658 old_inst: *ir.Inst.UnOp,
1659 comptime I: type,
1660 ) Allocator.Error!*Inst {
1661 const new_inst = try self.arena.allocator.create(I);
1662 new_inst.* = .{
1663 .base = .{
1664 .src = src,
1665 .tag = I.base_tag,
1666 },
1667 .positionals = .{
1668 .operand = try self.resolveInst(new_body, old_inst.operand),
1669 },
1670 .kw_args = .{},
1671 };
1672 return &new_inst.base;
1673 }
1674
1675 fn emitBinOp(
1676 self: *EmitZIR,
1677 src: usize,
1678 new_body: ZirBody,
1679 old_inst: *ir.Inst.BinOp,
1680 comptime I: type,
1681 ) Allocator.Error!*Inst {
1682 const new_inst = try self.arena.allocator.create(I);
1683 new_inst.* = .{
1684 .base = .{
1685 .src = src,
1686 .tag = I.base_tag,
1687 },
1688 .positionals = .{
1689 .lhs = try self.resolveInst(new_body, old_inst.lhs),
1690 .rhs = try self.resolveInst(new_body, old_inst.rhs),
1691 },
1692 .kw_args = .{},
1693 };
1694 return &new_inst.base;
1695 }
1696
16311697 fn emitBody(
16321698 self: *EmitZIR,
16331699 body: ir.Body,
......@@ -1640,69 +1706,48 @@ const EmitZIR = struct {
16401706 };
16411707 for (body.instructions) |inst| {
16421708 const new_inst = switch (inst.tag) {
1643 .not => blk: {
1644 const old_inst = inst.cast(ir.Inst.Not).?;
1645 assert(inst.ty.zigTypeTag() == .Bool);
1646 const new_inst = try self.arena.allocator.create(Inst.BoolNot);
1647 new_inst.* = .{
1648 .base = .{
1649 .src = inst.src,
1650 .tag = Inst.BoolNot.base_tag,
1651 },
1652 .positionals = .{
1653 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1654 },
1655 .kw_args = .{},
1656 };
1657 break :blk &new_inst.base;
1658 },
1659 .add => blk: {
1660 const old_inst = inst.cast(ir.Inst.Add).?;
1661 const new_inst = try self.arena.allocator.create(Inst.Add);
1662 new_inst.* = .{
1663 .base = .{
1664 .src = inst.src,
1665 .tag = Inst.Add.base_tag,
1666 },
1667 .positionals = .{
1668 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1669 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1670 },
1671 .kw_args = .{},
1672 };
1673 break :blk &new_inst.base;
1674 },
1675 .sub => blk: {
1676 const old_inst = inst.cast(ir.Inst.Sub).?;
1677 const new_inst = try self.arena.allocator.create(Inst.Sub);
1709 .constant => unreachable, // excluded from function bodies
1710
1711 .arg => try self.emitNoOp(inst.src, Inst.Arg),
1712 .breakpoint => try self.emitNoOp(inst.src, Inst.Breakpoint),
1713 .unreach => try self.emitNoOp(inst.src, Inst.Unreachable),
1714 .retvoid => try self.emitNoOp(inst.src, Inst.ReturnVoid),
1715
1716 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, Inst.BoolNot),
1717 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, Inst.Return),
1718 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, Inst.PtrToInt),
1719 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, Inst.IsNull),
1720 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, Inst.IsNonNull),
1721
1722 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, Inst.Add),
1723 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, Inst.Sub),
1724
1725 .cmp_lt => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_lt).?, .lt),
1726 .cmp_lte => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_lte).?, .lte),
1727 .cmp_eq => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_eq).?, .eq),
1728 .cmp_gte => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_gte).?, .gte),
1729 .cmp_gt => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_gt).?, .gt),
1730 .cmp_neq => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_neq).?, .neq),
1731
1732 .bitcast => blk: {
1733 const old_inst = inst.castTag(.bitcast).?;
1734 const new_inst = try self.arena.allocator.create(Inst.BitCast);
16781735 new_inst.* = .{
16791736 .base = .{
16801737 .src = inst.src,
1681 .tag = Inst.Sub.base_tag,
1738 .tag = Inst.BitCast.base_tag,
16821739 },
16831740 .positionals = .{
1684 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1685 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1686 },
1687 .kw_args = .{},
1688 };
1689 break :blk &new_inst.base;
1690 },
1691 .arg => blk: {
1692 const old_inst = inst.cast(ir.Inst.Arg).?;
1693 const new_inst = try self.arena.allocator.create(Inst.Arg);
1694 new_inst.* = .{
1695 .base = .{
1696 .src = inst.src,
1697 .tag = Inst.Arg.base_tag,
1741 .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
1742 .operand = try self.resolveInst(new_body, old_inst.operand),
16981743 },
1699 .positionals = .{},
17001744 .kw_args = .{},
17011745 };
17021746 break :blk &new_inst.base;
17031747 },
1748
17041749 .block => blk: {
1705 const old_inst = inst.cast(ir.Inst.Block).?;
1750 const old_inst = inst.castTag(.block).?;
17061751 const new_inst = try self.arena.allocator.create(Inst.Block);
17071752
17081753 try self.block_table.put(old_inst, new_inst);
......@@ -1710,7 +1755,7 @@ const EmitZIR = struct {
17101755 var block_body = std.ArrayList(*Inst).init(self.allocator);
17111756 defer block_body.deinit();
17121757
1713 try self.emitBody(old_inst.args.body, inst_table, &block_body);
1758 try self.emitBody(old_inst.body, inst_table, &block_body);
17141759
17151760 new_inst.* = .{
17161761 .base = .{
......@@ -1725,47 +1770,49 @@ const EmitZIR = struct {
17251770
17261771 break :blk &new_inst.base;
17271772 },
1728 .br => blk: {
1729 const old_inst = inst.cast(ir.Inst.Br).?;
1730 const new_block = self.block_table.get(old_inst.args.block).?;
1731 const new_inst = try self.arena.allocator.create(Inst.Break);
1773
1774 .brvoid => blk: {
1775 const old_inst = inst.cast(ir.Inst.BrVoid).?;
1776 const new_block = self.block_table.get(old_inst.block).?;
1777 const new_inst = try self.arena.allocator.create(Inst.BreakVoid);
17321778 new_inst.* = .{
17331779 .base = .{
17341780 .src = inst.src,
1735 .tag = Inst.Break.base_tag,
1781 .tag = Inst.BreakVoid.base_tag,
17361782 },
17371783 .positionals = .{
17381784 .block = new_block,
1739 .operand = try self.resolveInst(new_body, old_inst.args.operand),
17401785 },
17411786 .kw_args = .{},
17421787 };
17431788 break :blk &new_inst.base;
17441789 },
1745 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1746 .brvoid => blk: {
1747 const old_inst = inst.cast(ir.Inst.BrVoid).?;
1748 const new_block = self.block_table.get(old_inst.args.block).?;
1749 const new_inst = try self.arena.allocator.create(Inst.BreakVoid);
1790
1791 .br => blk: {
1792 const old_inst = inst.castTag(.br).?;
1793 const new_block = self.block_table.get(old_inst.block).?;
1794 const new_inst = try self.arena.allocator.create(Inst.Break);
17501795 new_inst.* = .{
17511796 .base = .{
17521797 .src = inst.src,
1753 .tag = Inst.BreakVoid.base_tag,
1798 .tag = Inst.Break.base_tag,
17541799 },
17551800 .positionals = .{
17561801 .block = new_block,
1802 .operand = try self.resolveInst(new_body, old_inst.operand),
17571803 },
17581804 .kw_args = .{},
17591805 };
17601806 break :blk &new_inst.base;
17611807 },
1808
17621809 .call => blk: {
1763 const old_inst = inst.cast(ir.Inst.Call).?;
1810 const old_inst = inst.castTag(.call).?;
17641811 const new_inst = try self.arena.allocator.create(Inst.Call);
17651812
1766 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1813 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len);
17671814 for (args) |*elem, i| {
1768 elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
1815 elem.* = try self.resolveInst(new_body, old_inst.args[i]);
17691816 }
17701817 new_inst.* = .{
17711818 .base = .{
......@@ -1773,48 +1820,31 @@ const EmitZIR = struct {
17731820 .tag = Inst.Call.base_tag,
17741821 },
17751822 .positionals = .{
1776 .func = try self.resolveInst(new_body, old_inst.args.func),
1823 .func = try self.resolveInst(new_body, old_inst.func),
17771824 .args = args,
17781825 },
17791826 .kw_args = .{},
17801827 };
17811828 break :blk &new_inst.base;
17821829 },
1783 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
1784 .ret => blk: {
1785 const old_inst = inst.cast(ir.Inst.Ret).?;
1786 const new_inst = try self.arena.allocator.create(Inst.Return);
1787 new_inst.* = .{
1788 .base = .{
1789 .src = inst.src,
1790 .tag = Inst.Return.base_tag,
1791 },
1792 .positionals = .{
1793 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1794 },
1795 .kw_args = .{},
1796 };
1797 break :blk &new_inst.base;
1798 },
1799 .retvoid => try self.emitTrivial(inst.src, Inst.ReturnVoid),
1800 .constant => unreachable, // excluded from function bodies
1830
18011831 .assembly => blk: {
1802 const old_inst = inst.cast(ir.Inst.Assembly).?;
1832 const old_inst = inst.castTag(.assembly).?;
18031833 const new_inst = try self.arena.allocator.create(Inst.Asm);
18041834
1805 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
1835 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.inputs.len);
18061836 for (inputs) |*elem, i| {
1807 elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.inputs[i])).inst;
1837 elem.* = (try self.emitStringLiteral(inst.src, old_inst.inputs[i])).inst;
18081838 }
18091839
1810 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
1840 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.clobbers.len);
18111841 for (clobbers) |*elem, i| {
1812 elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i])).inst;
1842 elem.* = (try self.emitStringLiteral(inst.src, old_inst.clobbers[i])).inst;
18131843 }
18141844
1815 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1845 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len);
18161846 for (args) |*elem, i| {
1817 elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
1847 elem.* = try self.resolveInst(new_body, old_inst.args[i]);
18181848 }
18191849
18201850 new_inst.* = .{
......@@ -1823,12 +1853,12 @@ const EmitZIR = struct {
18231853 .tag = Inst.Asm.base_tag,
18241854 },
18251855 .positionals = .{
1826 .asm_source = (try self.emitStringLiteral(inst.src, old_inst.args.asm_source)).inst,
1856 .asm_source = (try self.emitStringLiteral(inst.src, old_inst.asm_source)).inst,
18271857 .return_type = (try self.emitType(inst.src, inst.ty)).inst,
18281858 },
18291859 .kw_args = .{
1830 .@"volatile" = old_inst.args.is_volatile,
1831 .output = if (old_inst.args.output) |o|
1860 .@"volatile" = old_inst.is_volatile,
1861 .output = if (old_inst.output) |o|
18321862 (try self.emitStringLiteral(inst.src, o)).inst
18331863 else
18341864 null,
......@@ -1839,65 +1869,18 @@ const EmitZIR = struct {
18391869 };
18401870 break :blk &new_inst.base;
18411871 },
1842 .ptrtoint => blk: {
1843 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
1844 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
1845 new_inst.* = .{
1846 .base = .{
1847 .src = inst.src,
1848 .tag = Inst.PtrToInt.base_tag,
1849 },
1850 .positionals = .{
1851 .ptr = try self.resolveInst(new_body, old_inst.args.ptr),
1852 },
1853 .kw_args = .{},
1854 };
1855 break :blk &new_inst.base;
1856 },
1857 .bitcast => blk: {
1858 const old_inst = inst.cast(ir.Inst.BitCast).?;
1859 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1860 new_inst.* = .{
1861 .base = .{
1862 .src = inst.src,
1863 .tag = Inst.BitCast.base_tag,
1864 },
1865 .positionals = .{
1866 .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
1867 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1868 },
1869 .kw_args = .{},
1870 };
1871 break :blk &new_inst.base;
1872 },
1873 .cmp => blk: {
1874 const old_inst = inst.cast(ir.Inst.Cmp).?;
1875 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1876 new_inst.* = .{
1877 .base = .{
1878 .src = inst.src,
1879 .tag = Inst.Cmp.base_tag,
1880 },
1881 .positionals = .{
1882 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1883 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1884 .op = old_inst.args.op,
1885 },
1886 .kw_args = .{},
1887 };
1888 break :blk &new_inst.base;
1889 },
1872
18901873 .condbr => blk: {
1891 const old_inst = inst.cast(ir.Inst.CondBr).?;
1874 const old_inst = inst.castTag(.condbr).?;
18921875
1893 var true_body = std.ArrayList(*Inst).init(self.allocator);
1894 var false_body = std.ArrayList(*Inst).init(self.allocator);
1876 var then_body = std.ArrayList(*Inst).init(self.allocator);
1877 var else_body = std.ArrayList(*Inst).init(self.allocator);
18951878
1896 defer true_body.deinit();
1897 defer false_body.deinit();
1879 defer then_body.deinit();
1880 defer else_body.deinit();
18981881
1899 try self.emitBody(old_inst.args.true_body, inst_table, &true_body);
1900 try self.emitBody(old_inst.args.false_body, inst_table, &false_body);
1882 try self.emitBody(old_inst.then_body, inst_table, &then_body);
1883 try self.emitBody(old_inst.else_body, inst_table, &else_body);
19011884
19021885 const new_inst = try self.arena.allocator.create(Inst.CondBr);
19031886 new_inst.* = .{
......@@ -1906,39 +1889,9 @@ const EmitZIR = struct {
19061889 .tag = Inst.CondBr.base_tag,
19071890 },
19081891 .positionals = .{
1909 .condition = try self.resolveInst(new_body, old_inst.args.condition),
1910 .true_body = .{ .instructions = true_body.toOwnedSlice() },
1911 .false_body = .{ .instructions = false_body.toOwnedSlice() },
1912 },
1913 .kw_args = .{},
1914 };
1915 break :blk &new_inst.base;
1916 },
1917 .isnull => blk: {
1918 const old_inst = inst.cast(ir.Inst.IsNull).?;
1919 const new_inst = try self.arena.allocator.create(Inst.IsNull);
1920 new_inst.* = .{
1921 .base = .{
1922 .src = inst.src,
1923 .tag = Inst.IsNull.base_tag,
1924 },
1925 .positionals = .{
1926 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1927 },
1928 .kw_args = .{},
1929 };
1930 break :blk &new_inst.base;
1931 },
1932 .isnonnull => blk: {
1933 const old_inst = inst.cast(ir.Inst.IsNonNull).?;
1934 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
1935 new_inst.* = .{
1936 .base = .{
1937 .src = inst.src,
1938 .tag = Inst.IsNonNull.base_tag,
1939 },
1940 .positionals = .{
1941 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1892 .condition = try self.resolveInst(new_body, old_inst.condition),
1893 .then_body = .{ .instructions = then_body.toOwnedSlice() },
1894 .else_body = .{ .instructions = else_body.toOwnedSlice() },
19421895 },
19431896 .kw_args = .{},
19441897 };
test/stage2/compare_output.zig+37
......@@ -267,5 +267,42 @@ pub fn addCases(ctx: *TestContext) !void {
267267 ,
268268 "",
269269 );
270
271 // Requires a second move. The register allocator should figure out to re-use rax.
272 case.addCompareOutput(
273 \\export fn _start() noreturn {
274 \\ add(3, 4);
275 \\
276 \\ exit();
277 \\}
278 \\
279 \\fn add(a: u32, b: u32) void {
280 \\ const c = a + b; // 7
281 \\ const d = a + c; // 10
282 \\ const e = d + b; // 14
283 \\ const f = d + e; // 24
284 \\ const g = e + f; // 38
285 \\ const h = f + g; // 62
286 \\ const i = g + h; // 100
287 \\ const j = i + d; // 110
288 \\ assert(j == 110);
289 \\}
290 \\
291 \\pub fn assert(ok: bool) void {
292 \\ if (!ok) unreachable; // assertion failure
293 \\}
294 \\
295 \\fn exit() noreturn {
296 \\ asm volatile ("syscall"
297 \\ :
298 \\ : [number] "{rax}" (231),
299 \\ [arg1] "{rdi}" (0)
300 \\ : "rcx", "r11", "memory"
301 \\ );
302 \\ unreachable;
303 \\}
304 ,
305 "",
306 );
270307 }
271308}