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 {...@@ -1349,8 +1349,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1349fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {1349fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
1350 try self.analyzeBody(&block_scope.base, body);1350 try self.analyzeBody(&block_scope.base, body);
1351 for (block_scope.instructions.items) |inst| {1351 for (block_scope.instructions.items) |inst| {
1352 if (inst.cast(Inst.Ret)) |ret| {1352 if (inst.castTag(.ret)) |ret| {
1353 const val = try self.resolveConstValue(&block_scope.base, ret.args.operand);1353 const val = try self.resolveConstValue(&block_scope.base, ret.operand);
1354 return val.toType();1354 return val.toType();
1355 } else {1355 } else {
1356 return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});1356 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...@@ -1938,16 +1938,132 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
1938 };1938 };
1939}1939}
19401940
1941fn addNewInstArgs(1941fn addNoOp(
1942 self: *Module,1942 self: *Module,
1943 block: *Scope.Block,1943 block: *Scope.Block,
1944 src: usize,1944 src: usize,
1945 ty: Type,1945 ty: Type,
1946 comptime T: type,1946 comptime tag: Inst.Tag,
1947 args: Inst.Args(T),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,
1948) !*Inst {2032) !*Inst {
1949 const inst = try self.addNewInst(block, src, ty, T);2033 const inst = try block.arena.create(Inst.CondBr);
1950 inst.args = args;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);
1951 return &inst.base;2067 return &inst.base;
1952}2068}
19532069
...@@ -2017,7 +2133,6 @@ fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime...@@ -2017,7 +2133,6 @@ fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime
2017 .ty = ty,2133 .ty = ty,
2018 .src = src,2134 .src = src,
2019 },2135 },
2020 .args = undefined,
2021 };2136 };
2022 try block.instructions.append(self.gpa, &inst.base);2137 try block.instructions.append(self.gpa, &inst.base);
2023 return inst;2138 return inst;
...@@ -2269,7 +2384,7 @@ fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!...@@ -2269,7 +2384,7 @@ fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!
2269 });2384 });
2270 }2385 }
2271 const param_type = fn_ty.fnParamType(param_index);2386 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);
2273}2388}
22742389
2275fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {2390fn 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...@@ -2285,7 +2400,7 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr
2285 .ty = undefined, // Set after analysis.2400 .ty = undefined, // Set after analysis.
2286 .src = inst.base.src,2401 .src = inst.base.src,
2287 },2402 },
2288 .args = undefined,2403 .body = undefined,
2289 };2404 };
22902405
2291 var child_block: Scope.Block = .{2406 var child_block: Scope.Block = .{
...@@ -2316,13 +2431,13 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr...@@ -2316,13 +2431,13 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr
2316 // to emit a jump instruction to after the block when it encounters the break.2431 // to emit a jump instruction to after the block when it encounters the break.
2317 try parent_block.instructions.append(self.gpa, &block_inst.base);2432 try parent_block.instructions.append(self.gpa, &block_inst.base);
2318 block_inst.base.ty = try self.resolvePeerTypes(scope, label.results.items);2433 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) };
2320 return &block_inst.base;2435 return &block_inst.base;
2321}2436}
23222437
2323fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {2438fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
2324 const b = try self.requireRuntimeBlock(scope, inst.base.src);2439 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);
2326}2441}
23272442
2328fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {2443fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
...@@ -2350,10 +2465,7 @@ fn analyzeBreak(...@@ -2350,10 +2465,7 @@ fn analyzeBreak(
2350 if (label.zir_block == zir_block) {2465 if (label.zir_block == zir_block) {
2351 try label.results.append(self.gpa, operand);2466 try label.results.append(self.gpa, operand);
2352 const b = try self.requireRuntimeBlock(scope, src);2467 const b = try self.requireRuntimeBlock(scope, src);
2353 return self.addNewInstArgs(b, src, Type.initTag(.noreturn), Inst.Br, .{2468 return self.addBr(b, src, label.block_inst, operand);
2354 .block = label.block_inst,
2355 .operand = operand,
2356 });
2357 }2469 }
2358 }2470 }
2359 opt_block = block.parent;2471 opt_block = block.parent;
...@@ -2484,10 +2596,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro...@@ -2484,10 +2596,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
2484 }2596 }
24852597
2486 const b = try self.requireRuntimeBlock(scope, inst.base.src);2598 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2487 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, .{2599 return self.addCall(b, inst.base.src, Type.initTag(.void), func, casted_args);
2488 .func = func,
2489 .args = casted_args,
2490 });
2491}2600}
24922601
2493fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {2602fn 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...@@ -2570,14 +2679,14 @@ fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Ins
2570}2679}
25712680
2572fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToInt) InnerError!*Inst {2681fn 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);
2574 if (ptr.ty.zigTypeTag() != .Pointer) {2683 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});
2576 }2685 }
2577 // TODO handle known-pointer-address2686 // TODO handle known-pointer-address
2578 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);2687 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
2579 const ty = Type.initTag(.usize);2688 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);
2581}2690}
25822691
2583fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {2692fn 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!...@@ -2734,10 +2843,7 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!
2734 }2843 }
27352844
2736 const b = try self.requireRuntimeBlock(scope, inst.base.src);2845 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2737 return self.addNewInstArgs(b, inst.base.src, lhs.ty, Inst.Add, .{2846 return self.addBinOp(b, inst.base.src, lhs.ty, .add, lhs, rhs);
2738 .lhs = lhs,
2739 .rhs = rhs,
2740 });
2741 }2847 }
2742 return self.fail(scope, inst.base.src, "TODO analyze add for {} + {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() });2848 return self.fail(scope, inst.base.src, "TODO analyze add for {} + {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() });
2743}2849}
...@@ -2783,14 +2889,22 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr...@@ -2783,14 +2889,22 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr
2783 }2889 }
27842890
2785 const b = try self.requireRuntimeBlock(scope, assembly.base.src);2891 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 },
2787 .asm_source = asm_source,2899 .asm_source = asm_source,
2788 .is_volatile = assembly.kw_args.@"volatile",2900 .is_volatile = assembly.kw_args.@"volatile",
2789 .output = output,2901 .output = output,
2790 .inputs = inputs,2902 .inputs = inputs,
2791 .clobbers = clobbers,2903 .clobbers = clobbers,
2792 .args = args,2904 .args = args,
2793 });2905 };
2906 try b.instructions.append(self.gpa, &inst.base);
2907 return &inst.base;
2794}2908}
27952909
2796fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!*Inst {2910fn 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!...@@ -2818,15 +2932,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
2818 return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);2932 return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);
2819 }2933 }
2820 const b = try self.requireRuntimeBlock(scope, inst.base.src);2934 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2821 switch (op) {2935 const inst_tag: Inst.Tag = switch (op) {
2822 .eq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNull, .{2936 .eq => .isnull,
2823 .operand = opt_operand,2937 .neq => .isnonnull,
2824 }),
2825 .neq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNonNull, .{
2826 .operand = opt_operand,
2827 }),
2828 else => unreachable,2938 else => unreachable,
2829 }2939 };
2940 return self.addUnOp(b, inst.base.src, Type.initTag(.bool), inst_tag, opt_operand);
2830 } else if (is_equality_cmp and2941 } else if (is_equality_cmp and
2831 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))2942 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
2832 {2943 {
...@@ -2861,7 +2972,7 @@ fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.BoolNot) Inn...@@ -2861,7 +2972,7 @@ fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.BoolNot) Inn
2861 return self.constBool(scope, inst.base.src, !val.toBool());2972 return self.constBool(scope, inst.base.src, !val.toBool());
2862 }2973 }
2863 const b = try self.requireRuntimeBlock(scope, inst.base.src);2974 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);
2865}2976}
28662977
2867fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {2978fn 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...@@ -2879,7 +2990,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
2879 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);2990 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);
28802991
2881 if (try self.resolveDefinedValue(scope, cond)) |cond_val| {2992 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;
2883 try self.analyzeBody(scope, body.*);2994 try self.analyzeBody(scope, body.*);
2884 return self.constVoid(scope, inst.base.src);2995 return self.constVoid(scope, inst.base.src);
2885 }2996 }
...@@ -2894,7 +3005,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner...@@ -2894,7 +3005,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
2894 .arena = parent_block.arena,3005 .arena = parent_block.arena,
2895 };3006 };
2896 defer true_block.instructions.deinit(self.gpa);3007 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
2899 var false_block: Scope.Block = .{3010 var false_block: Scope.Block = .{
2900 .parent = parent_block,3011 .parent = parent_block,
...@@ -2904,13 +3015,11 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner...@@ -2904,13 +3015,11 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
2904 .arena = parent_block.arena,3015 .arena = parent_block.arena,
2905 };3016 };
2906 defer false_block.instructions.deinit(self.gpa);3017 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){3020 const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
2910 .condition = cond,3021 const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
2911 .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },3022 return self.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
2912 .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },
2913 });
2914}3023}
29153024
2916fn wantSafety(self: *Module, scope: *Scope) bool {3025fn wantSafety(self: *Module, scope: *Scope) bool {
...@@ -2926,20 +3035,20 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea...@@ -2926,20 +3035,20 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea
2926 const b = try self.requireRuntimeBlock(scope, unreach.base.src);3035 const b = try self.requireRuntimeBlock(scope, unreach.base.src);
2927 if (self.wantSafety(scope)) {3036 if (self.wantSafety(scope)) {
2928 // TODO Once we have a panic function to call, call it here instead of this.3037 // 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);
2930 }3039 }
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);
2932}3041}
29333042
2934fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {3043fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {
2935 const operand = try self.resolveInst(scope, inst.positionals.operand);3044 const operand = try self.resolveInst(scope, inst.positionals.operand);
2936 const b = try self.requireRuntimeBlock(scope, inst.base.src);3045 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);
2938}3047}
29393048
2940fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst {3049fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst {
2941 const b = try self.requireRuntimeBlock(scope, inst.base.src);3050 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);
2943}3052}
29443053
2945fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {3054fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {
...@@ -3027,11 +3136,7 @@ fn cmpNumeric(...@@ -3027,11 +3136,7 @@ fn cmpNumeric(
3027 };3136 };
3028 const casted_lhs = try self.coerce(scope, dest_type, lhs);3137 const casted_lhs = try self.coerce(scope, dest_type, lhs);
3029 const casted_rhs = try self.coerce(scope, dest_type, rhs);3138 const casted_rhs = try self.coerce(scope, dest_type, rhs);
3030 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{3139 return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
3031 .lhs = casted_lhs,
3032 .rhs = casted_rhs,
3033 .op = op,
3034 });
3035 }3140 }
3036 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.3141 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
3037 // For mixed signed and unsigned integers, implicit cast both operands to a signed3142 // For mixed signed and unsigned integers, implicit cast both operands to a signed
...@@ -3131,11 +3236,7 @@ fn cmpNumeric(...@@ -3131,11 +3236,7 @@ fn cmpNumeric(
3131 const casted_lhs = try self.coerce(scope, dest_type, lhs);3236 const casted_lhs = try self.coerce(scope, dest_type, lhs);
3132 const casted_rhs = try self.coerce(scope, dest_type, rhs);3237 const casted_rhs = try self.coerce(scope, dest_type, rhs);
31333238
3134 return self.addNewInstArgs(b, src, Type.initTag(.bool), Inst.Cmp, .{3239 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
3135 .lhs = casted_lhs,
3136 .rhs = casted_rhs,
3137 .op = op,
3138 });
3139}3240}
31403241
3141fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {3242fn 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 {...@@ -3236,7 +3337,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3236 }3337 }
3237 // TODO validate the type size and other compile errors3338 // TODO validate the type size and other compile errors
3238 const b = try self.requireRuntimeBlock(scope, inst.src);3339 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);
3240}3341}
32413342
3242fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {3343fn 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...@@ -173,8 +173,8 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
173 const if_src = tree.token_locs[if_node.if_token].start;173 const if_src = tree.token_locs[if_node.if_token].start;
174 const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{174 const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
175 .condition = cond,175 .condition = cond,
176 .true_body = undefined, // populated below176 .then_body = undefined, // populated below
177 .false_body = undefined, // populated below177 .else_body = undefined, // populated below
178 }, .{});178 }, .{});
179179
180 const block = try mod.addZIRInstBlock(scope, if_src, .{180 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...@@ -196,7 +196,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
196 .operand = then_result,196 .operand = then_result,
197 }, .{});197 }, .{});
198 }198 }
199 condbr.positionals.true_body = .{199 condbr.positionals.then_body = .{
200 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),200 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
201 };201 };
202202
...@@ -225,7 +225,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In...@@ -225,7 +225,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
225 .block = block,225 .block = block,
226 }, .{});226 }, .{});
227 }227 }
228 condbr.positionals.false_body = .{228 condbr.positionals.else_body = .{
229 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),229 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
230 };230 };
231231
src-self-hosted/codegen.zig+88-62
...@@ -290,6 +290,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -290,6 +290,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
290 next_stack_offset: u32 = 0,290 next_stack_offset: u32 = 0,
291291
292 fn markRegUsed(self: *Branch, reg: Register) void {292 fn markRegUsed(self: *Branch, reg: Register) void {
293 if (FreeRegInt == u0) return;
293 const index = reg.allocIndex() orelse return;294 const index = reg.allocIndex() orelse return;
294 const ShiftInt = std.math.Log2Int(FreeRegInt);295 const ShiftInt = std.math.Log2Int(FreeRegInt);
295 const shift = @intCast(ShiftInt, index);296 const shift = @intCast(ShiftInt, index);
...@@ -297,6 +298,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -297,6 +298,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
297 }298 }
298299
299 fn markRegFree(self: *Branch, reg: Register) void {300 fn markRegFree(self: *Branch, reg: Register) void {
301 if (FreeRegInt == u0) return;
300 const index = reg.allocIndex() orelse return;302 const index = reg.allocIndex() orelse return;
301 const ShiftInt = std.math.Log2Int(FreeRegInt);303 const ShiftInt = std.math.Log2Int(FreeRegInt);
302 const shift = @intCast(ShiftInt, index);304 const shift = @intCast(ShiftInt, index);
...@@ -407,40 +409,64 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -407,40 +409,64 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
407 for (body.instructions) |inst| {409 for (body.instructions) |inst| {
408 const new_inst = try self.genFuncInst(inst);410 const new_inst = try self.genFuncInst(inst);
409 try inst_table.putNoClobber(self.gpa, inst, new_inst);411 try inst_table.putNoClobber(self.gpa, inst, new_inst);
410 // TODO process operand deaths412
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
411 }432 }
412 }433 }
413434
414 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {435 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
415 switch (inst.tag) {436 switch (inst.tag) {
416 .add => return self.genAdd(inst.cast(ir.Inst.Add).?),437 .add => return self.genAdd(inst.castTag(.add).?),
417 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),438 .arg => return self.genArg(inst.castTag(.arg).?),
418 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),439 .assembly => return self.genAsm(inst.castTag(.assembly).?),
419 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),440 .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
420 .block => return self.genBlock(inst.cast(ir.Inst.Block).?),441 .block => return self.genBlock(inst.castTag(.block).?),
421 .br => return self.genBr(inst.cast(ir.Inst.Br).?),442 .br => return self.genBr(inst.castTag(.br).?),
422 .breakpoint => return self.genBreakpoint(inst.src),443 .breakpoint => return self.genBreakpoint(inst.src),
423 .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?),444 .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?),
424 .call => return self.genCall(inst.cast(ir.Inst.Call).?),445 .call => return self.genCall(inst.castTag(.call).?),
425 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),446 .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
426 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),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).?),
427 .constant => unreachable, // excluded from function bodies453 .constant => unreachable, // excluded from function bodies
428 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),454 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
429 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),455 .isnull => return self.genIsNull(inst.castTag(.isnull).?),
430 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),456 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
431 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?),457 .ret => return self.genRet(inst.castTag(.ret).?),
432 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?),458 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
433 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?),459 .sub => return self.genSub(inst.castTag(.sub).?),
434 .unreach => return MCValue{ .unreach = {} },460 .unreach => return MCValue{ .unreach = {} },
435 .not => return self.genNot(inst.cast(ir.Inst.Not).?),461 .not => return self.genNot(inst.castTag(.not).?),
436 }462 }
437 }463 }
438464
439 fn genNot(self: *Self, inst: *ir.Inst.Not) !MCValue {465 fn genNot(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
440 // No side effects, so if it's unreferenced, do nothing.466 // No side effects, so if it's unreferenced, do nothing.
441 if (inst.base.isUnused())467 if (inst.base.isUnused())
442 return MCValue.dead;468 return MCValue.dead;
443 const operand = try self.resolveInst(inst.args.operand);469 const operand = try self.resolveInst(inst.operand);
444 switch (operand) {470 switch (operand) {
445 .dead => unreachable,471 .dead => unreachable,
446 .unreach => unreachable,472 .unreach => unreachable,
...@@ -473,36 +499,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -473,36 +499,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
473 .base = .{499 .base = .{
474 .tag = .constant,500 .tag = .constant,
475 .deaths = 0,501 .deaths = 0,
476 .ty = inst.args.operand.ty,502 .ty = inst.operand.ty,
477 .src = inst.args.operand.src,503 .src = inst.operand.src,
478 },504 },
479 .val = Value.initTag(.bool_true),505 .val = Value.initTag(.bool_true),
480 };506 };
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);
482 },508 },
483 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),509 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
484 }510 }
485 }511 }
486512
487 fn genAdd(self: *Self, inst: *ir.Inst.Add) !MCValue {513 fn genAdd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
488 // No side effects, so if it's unreferenced, do nothing.514 // No side effects, so if it's unreferenced, do nothing.
489 if (inst.base.isUnused())515 if (inst.base.isUnused())
490 return MCValue.dead;516 return MCValue.dead;
491 switch (arch) {517 switch (arch) {
492 .x86_64 => {518 .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);
494 },520 },
495 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),521 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
496 }522 }
497 }523 }
498524
499 fn genSub(self: *Self, inst: *ir.Inst.Sub) !MCValue {525 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
500 // No side effects, so if it's unreferenced, do nothing.526 // No side effects, so if it's unreferenced, do nothing.
501 if (inst.base.isUnused())527 if (inst.base.isUnused())
502 return MCValue.dead;528 return MCValue.dead;
503 switch (arch) {529 switch (arch) {
504 .x86_64 => {530 .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);
506 },532 },
507 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),533 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
508 }534 }
...@@ -625,7 +651,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -625,7 +651,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
625 }651 }
626 }652 }
627653
628 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {654 fn genArg(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
629 if (FreeRegInt == u0) {655 if (FreeRegInt == u0) {
630 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});656 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
631 }657 }
...@@ -659,7 +685,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -659,7 +685,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
659 }685 }
660686
661 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {687 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
662 const fn_ty = inst.args.func.ty;688 const fn_ty = inst.func.ty;
663 const cc = fn_ty.fnCallingConvention();689 const cc = fn_ty.fnCallingConvention();
664 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());690 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
665 defer self.gpa.free(param_types);691 defer self.gpa.free(param_types);
...@@ -671,8 +697,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -671,8 +697,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
671 switch (arch) {697 switch (arch) {
672 .x86_64 => {698 .x86_64 => {
673 for (mc_args) |mc_arg, arg_i| {699 for (mc_args) |mc_arg, arg_i| {
674 const arg = inst.args.args[arg_i];700 const arg = inst.args[arg_i];
675 const arg_mcv = try self.resolveInst(inst.args.args[arg_i]);701 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
676 switch (mc_arg) {702 switch (mc_arg) {
677 .none => continue,703 .none => continue,
678 .register => |reg| {704 .register => |reg| {
...@@ -694,7 +720,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -694,7 +720,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
694 }720 }
695 }721 }
696722
697 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {723 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
698 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {724 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
699 const func = func_val.func;725 const func = func_val.func;
700 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];726 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 {...@@ -742,16 +768,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
742 return .unreach;768 return .unreach;
743 }769 }
744770
745 fn genRet(self: *Self, inst: *ir.Inst.Ret) !MCValue {771 fn genRet(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
746 const operand = try self.resolveInst(inst.args.operand);772 const operand = try self.resolveInst(inst.operand);
747 return self.ret(inst.base.src, operand);773 return self.ret(inst.base.src, operand);
748 }774 }
749775
750 fn genRetVoid(self: *Self, inst: *ir.Inst.RetVoid) !MCValue {776 fn genRetVoid(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
751 return self.ret(inst.base.src, .none);777 return self.ret(inst.base.src, .none);
752 }778 }
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 {
755 // No side effects, so if it's unreferenced, do nothing.781 // No side effects, so if it's unreferenced, do nothing.
756 if (inst.base.isUnused())782 if (inst.base.isUnused())
757 return MCValue.dead;783 return MCValue.dead;
...@@ -759,25 +785,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -759,25 +785,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
759 .x86_64 => {785 .x86_64 => {
760 try self.code.ensureCapacity(self.code.items.len + 8);786 try self.code.ensureCapacity(self.code.items.len + 8);
761787
762 const lhs = try self.resolveInst(inst.args.lhs);788 const lhs = try self.resolveInst(inst.lhs);
763 const rhs = try self.resolveInst(inst.args.rhs);789 const rhs = try self.resolveInst(inst.rhs);
764790
765 // There are 2 operands, destination and source.791 // There are 2 operands, destination and source.
766 // Either one, but not both, can be a memory operand.792 // Either one, but not both, can be a memory operand.
767 // Source operand can be an immediate, 8 bits or 32 bits.793 // Source operand can be an immediate, 8 bits or 32 bits.
768 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))794 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)
770 else796 else
771 lhs;797 lhs;
772 // This instruction supports only signed 32-bit immediates at most.798 // 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
775 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);801 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.*);
777 if (info.signed) {803 if (info.signed) {
778 return MCValue{ .compare_flags_signed = inst.args.op };804 return MCValue{ .compare_flags_signed = op };
779 } else {805 } else {
780 return MCValue{ .compare_flags_unsigned = inst.args.op };806 return MCValue{ .compare_flags_unsigned = op };
781 }807 }
782 },808 },
783 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),809 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 {...@@ -789,7 +815,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
789 .x86_64 => {815 .x86_64 => {
790 try self.code.ensureCapacity(self.code.items.len + 6);816 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);
793 switch (cond) {819 switch (cond) {
794 .compare_flags_signed => |cmp_op| {820 .compare_flags_signed => |cmp_op| {
795 // Here we map to the opposite opcode because the jump is to the false branch.821 // 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 {...@@ -838,19 +864,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
838 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });864 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
839 const reloc = Reloc{ .rel32 = self.code.items.len };865 const reloc = Reloc{ .rel32 = self.code.items.len };
840 self.code.items.len += 4;866 self.code.items.len += 4;
841 try self.genBody(inst.args.true_body);867 try self.genBody(inst.then_body);
842 try self.performReloc(inst.base.src, reloc);868 try self.performReloc(inst.base.src, reloc);
843 try self.genBody(inst.args.false_body);869 try self.genBody(inst.else_body);
844 return MCValue.unreach;870 return MCValue.unreach;
845 }871 }
846872
847 fn genIsNull(self: *Self, inst: *ir.Inst.IsNull) !MCValue {873 fn genIsNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
848 switch (arch) {874 switch (arch) {
849 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),875 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
850 }876 }
851 }877 }
852878
853 fn genIsNonNull(self: *Self, inst: *ir.Inst.IsNonNull) !MCValue {879 fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
854 // Here you can specialize this instruction if it makes sense to, otherwise the default880 // Here you can specialize this instruction if it makes sense to, otherwise the default
855 // will call genIsNull and invert the result.881 // will call genIsNull and invert the result.
856 switch (arch) {882 switch (arch) {
...@@ -864,7 +890,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -864,7 +890,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
864 }890 }
865 // A block is nothing but a setup to be able to jump to the end.891 // A block is nothing but a setup to be able to jump to the end.
866 defer inst.codegen.relocs.deinit(self.gpa);892 defer inst.codegen.relocs.deinit(self.gpa);
867 try self.genBody(inst.args.body);893 try self.genBody(inst.body);
868894
869 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);895 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 {...@@ -883,17 +909,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
883 }909 }
884910
885 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {911 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
886 if (!inst.args.operand.ty.hasCodeGenBits())912 if (!inst.operand.ty.hasCodeGenBits())
887 return self.brVoid(inst.base.src, inst.args.block);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);
890 switch (arch) {916 switch (arch) {
891 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),917 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
892 }918 }
893 }919 }
894920
895 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {921 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);
897 }923 }
898924
899 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {925 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
...@@ -915,29 +941,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -915,29 +941,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
915 }941 }
916942
917 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {943 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())
919 return MCValue.dead;945 return MCValue.dead;
920 if (arch != .x86_64 and arch != .i386) {946 if (arch != .x86_64 and arch != .i386) {
921 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});947 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
922 }948 }
923 for (inst.args.inputs) |input, i| {949 for (inst.inputs) |input, i| {
924 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {950 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
925 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});951 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
926 }952 }
927 const reg_name = input[1 .. input.len - 1];953 const reg_name = input[1 .. input.len - 1];
928 const reg = parseRegName(reg_name) orelse954 const reg = parseRegName(reg_name) orelse
929 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});955 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]);
931 try self.genSetReg(inst.base.src, reg, arg);957 try self.genSetReg(inst.base.src, reg, arg);
932 }958 }
933959
934 if (mem.eql(u8, inst.args.asm_source, "syscall")) {960 if (mem.eql(u8, inst.asm_source, "syscall")) {
935 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });961 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
936 } else {962 } else {
937 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});963 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
938 }964 }
939965
940 if (inst.args.output) |output| {966 if (inst.output) |output| {
941 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {967 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
942 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});968 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
943 }969 }
...@@ -1169,13 +1195,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1169,13 +1195,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1169 }1195 }
1170 }1196 }
11711197
1172 fn genPtrToInt(self: *Self, inst: *ir.Inst.PtrToInt) !MCValue {1198 fn genPtrToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1173 // no-op1199 // no-op
1174 return self.resolveInst(inst.args.ptr);1200 return self.resolveInst(inst.operand);
1175 }1201 }
11761202
1177 fn genBitCast(self: *Self, inst: *ir.Inst.BitCast) !MCValue {1203 fn genBitCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1178 const operand = try self.resolveInst(inst.args.operand);1204 const operand = try self.resolveInst(inst.operand);
1179 return operand;1205 return operand;
1180 }1206 }
11811207
src-self-hosted/codegen/c.zig+10-11
...@@ -92,9 +92,9 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -92,9 +92,9 @@ fn genFn(file: *C, decl: *Decl) !void {
92 for (instructions) |inst| {92 for (instructions) |inst| {
93 try writer.writeAll("\n\t");93 try writer.writeAll("\n\t");
94 switch (inst.tag) {94 switch (inst.tag) {
95 .assembly => try genAsm(file, inst.cast(Inst.Assembly).?, decl),95 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
96 .call => try genCall(file, inst.cast(Inst.Call).?, decl),96 .call => try genCall(file, inst.castTag(.call).?, decl),
97 .ret => try genRet(file, inst.cast(Inst.Ret).?, decl, tv.ty.fnReturnType()),97 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),
98 .retvoid => try file.main.writer().print("return;", .{}),98 .retvoid => try file.main.writer().print("return;", .{}),
99 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),99 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100 }100 }
...@@ -105,9 +105,9 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -105,9 +105,9 @@ fn genFn(file: *C, decl: *Decl) !void {
105 try writer.writeAll("}\n\n");105 try writer.writeAll("}\n\n");
106}106}
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 {
109 const writer = file.main.writer();109 const writer = file.main.writer();
110 const ret_value = inst.args.operand;110 const ret_value = inst.operand;
111 const value = ret_value.value().?;111 const value = ret_value.value().?;
112 if (expected_return_type.eql(ret_value.ty))112 if (expected_return_type.eql(ret_value.ty))
113 return file.fail(decl.src(), "TODO return {}", .{expected_return_type})113 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...@@ -126,7 +126,7 @@ fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !v
126fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {126fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
127 const writer = file.main.writer();127 const writer = file.main.writer();
128 const header = file.header.writer();128 const header = file.header.writer();
129 if (inst.args.func.cast(Inst.Constant)) |func_inst| {129 if (inst.func.castTag(.constant)) |func_inst| {
130 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {130 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
131 const target = func_val.func.owner_decl;131 const target = func_val.func.owner_decl;
132 const target_ty = target.typed_value.most_recent.typed_value.ty;132 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 {...@@ -144,7 +144,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
144 } else {144 } else {
145 return file.fail(decl.src(), "TODO non-function call target?", .{});145 return file.fail(decl.src(), "TODO non-function call target?", .{});
146 }146 }
147 if (inst.args.args.len != 0) {147 if (inst.args.len != 0) {
148 return file.fail(decl.src(), "TODO function arguments", .{});148 return file.fail(decl.src(), "TODO function arguments", .{});
149 }149 }
150 } else {150 } else {
...@@ -152,14 +152,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {...@@ -152,14 +152,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
152 }152 }
153}153}
154154
155fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {155fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
156 const as = inst.args;
157 const writer = file.main.writer();156 const writer = file.main.writer();
158 for (as.inputs) |i, index| {157 for (as.inputs) |i, index| {
159 if (i[0] == '{' and i[i.len - 1] == '}') {158 if (i[0] == '{' and i[i.len - 1] == '}') {
160 const reg = i[1 .. i.len - 1];159 const reg = i[1 .. i.len - 1];
161 const arg = as.args[index];160 const arg = as.args[index];
162 if (arg.cast(Inst.Constant)) |c| {161 if (arg.castTag(.constant)) |c| {
163 if (c.val.tag() == .int_u64) {162 if (c.val.tag() == .int_u64) {
164 try writer.writeAll("register ");163 try writer.writeAll("register ");
165 try renderType(file, writer, arg.ty, decl.src());164 try renderType(file, writer, arg.ty, decl.src());
...@@ -190,7 +189,7 @@ fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {...@@ -190,7 +189,7 @@ fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {
190 if (index > 0) {189 if (index > 0) {
191 try writer.writeAll(", ");190 try writer.writeAll(", ");
192 }191 }
193 if (arg.cast(Inst.Constant)) |c| {192 if (arg.castTag(.constant)) |c| {
194 try writer.print("\"\"({}_constant)", .{reg});193 try writer.print("\"\"({}_constant)", .{reg});
195 } else {194 } else {
196 // This is blocked by the earlier test195 // This is blocked by the earlier test
src-self-hosted/ir.zig+230-125
...@@ -55,7 +55,12 @@ pub const Inst = struct {...@@ -55,7 +55,12 @@ pub const Inst = struct {
55 breakpoint,55 breakpoint,
56 brvoid,56 brvoid,
57 call,57 call,
58 cmp,58 cmp_lt,
59 cmp_lte,
60 cmp_eq,
61 cmp_gte,
62 cmp_gt,
63 cmp_neq,
59 condbr,64 condbr,
60 constant,65 constant,
61 isnonnull,66 isnonnull,
...@@ -66,13 +71,80 @@ pub const Inst = struct {...@@ -66,13 +71,80 @@ pub const Inst = struct {
66 sub,71 sub,
67 unreach,72 unreach,
68 not,73 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 }
69 };124 };
70125
126 /// Prefer `castTag` to this.
71 pub fn cast(base: *Inst, comptime T: type) ?*T {127 pub fn cast(base: *Inst, comptime T: type) ?*T {
72 if (base.tag != T.base_tag)128 if (@hasField(T, "base_tag")) {
73 return null;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;
76 }148 }
77149
78 pub fn Args(comptime T: type) type {150 pub fn Args(comptime T: type) type {
...@@ -88,186 +160,219 @@ pub const Inst = struct {...@@ -88,186 +160,219 @@ pub const Inst = struct {
88 return inst.val;160 return inst.val;
89 }161 }
90162
91 pub const Add = struct {163 pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
92 pub const base_tag = Tag.add;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 {
93 base: Inst,196 base: Inst,
94197
95 args: struct {198 pub fn operandCount(self: *const NoOp) usize {
96 lhs: *Inst,199 return 0;
97 rhs: *Inst,200 }
98 },201 pub fn getOperand(self: *const NoOp, index: usize) ?*Inst {
202 return null;
203 }
99 };204 };
100205
101 pub const Arg = struct {206 pub const UnOp = struct {
102 pub const base_tag = Tag.arg;
103 base: Inst,207 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 }
105 };218 };
106219
107 pub const Assembly = struct {220 pub const BinOp = struct {
108 pub const base_tag = Tag.assembly;
109 base: Inst,221 base: Inst,
222 lhs: *Inst,
223 rhs: *Inst,
110224
111 args: struct {225 pub fn operandCount(self: *const BinOp) usize {
112 asm_source: []const u8,226 return 2;
113 is_volatile: bool,227 }
114 output: ?[]const u8,228 pub fn getOperand(self: *const BinOp, index: usize) ?*Inst {
115 inputs: []const []const u8,229 var i = index;
116 clobbers: []const []const u8,230
117 args: []const *Inst,231 if (i < 1)
118 },232 return self.lhs;
233 i -= 1;
234
235 if (i < 1)
236 return self.rhs;
237 i -= 1;
238
239 return null;
240 }
119 };241 };
120242
121 pub const BitCast = struct {243 pub const Assembly = struct {
122 pub const base_tag = Tag.bitcast;244 pub const base_tag = Tag.assembly;
123245
124 base: Inst,246 base: Inst,
125 args: struct {247 asm_source: []const u8,
126 operand: *Inst,248 is_volatile: bool,
127 },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 }
128 };262 };
129263
130 pub const Block = struct {264 pub const Block = struct {
131 pub const base_tag = Tag.block;265 pub const base_tag = Tag.block;
266
132 base: Inst,267 base: Inst,
133 args: struct {268 body: Body,
134 body: Body,
135 },
136 /// This memory is reserved for codegen code to do whatever it needs to here.269 /// This memory is reserved for codegen code to do whatever it needs to here.
137 codegen: codegen.BlockData = .{},270 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 }
138 };278 };
139279
140 pub const Br = struct {280 pub const Br = struct {
141 pub const base_tag = Tag.br;281 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;
151 base: Inst,283 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 }
153 };295 };
154296
155 pub const BrVoid = struct {297 pub const BrVoid = struct {
156 pub const base_tag = Tag.brvoid;298 pub const base_tag = Tag.brvoid;
299
157 base: Inst,300 base: Inst,
158 args: struct {301 block: *Block,
159 block: *Block,302
160 },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 }
161 };309 };
162310
163 pub const Call = struct {311 pub const Call = struct {
164 pub const base_tag = Tag.call;312 pub const base_tag = Tag.call;
313
165 base: Inst,314 base: Inst,
166 args: struct {315 func: *Inst,
167 func: *Inst,316 args: []const *Inst,
168 args: []const *Inst,
169 },
170 };
171317
172 pub const Cmp = struct {318 pub fn operandCount(self: *const Call) usize {
173 pub const base_tag = Tag.cmp;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,324 if (i < 1)
176 args: struct {325 return self.func;
177 lhs: *Inst,326 i -= 1;
178 op: std.math.CompareOperator,327
179 rhs: *Inst,328 if (i < self.args.len)
180 },329 return self.args[i];
330 i -= self.args.len;
331
332 return null;
333 }
181 };334 };
182335
183 pub const CondBr = struct {336 pub const CondBr = struct {
184 pub const base_tag = Tag.condbr;337 pub const base_tag = Tag.condbr;
185338
186 base: Inst,339 base: Inst,
187 args: struct {340 condition: *Inst,
188 condition: *Inst,341 then_body: Body,
189 true_body: Body,342 else_body: Body,
190 false_body: Body,
191 },
192 /// Set of instructions whose lifetimes end at the start of one of the branches.343 /// Set of instructions whose lifetimes end at the start of one of the branches.
193 /// The `true` branch is first: `deaths[0..true_death_count]`.344 /// The `true` branch is first: `deaths[0..true_death_count]`.
194 /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`.345 /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`.
195 deaths: [*]*Inst = undefined,346 deaths: [*]*Inst = undefined,
196 true_death_count: u32 = 0,347 true_death_count: u32 = 0,
197 false_death_count: u32 = 0,348 false_death_count: u32 = 0,
198 };
199349
200 pub const Not = struct {350 pub fn operandCount(self: *const CondBr) usize {
201 pub const base_tag = Tag.not;351 return 1;
352 }
353 pub fn getOperand(self: *const CondBr, index: usize) ?*Inst {
354 var i = index;
202355
203 base: Inst,356 if (i < 1)
204 args: struct {357 return self.condition;
205 operand: *Inst,358 i -= 1;
206 },359
360 return null;
361 }
207 };362 };
208363
209 pub const Constant = struct {364 pub const Constant = struct {
210 pub const base_tag = Tag.constant;365 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
228 base: Inst,367 base: Inst,
229 args: struct {368 val: Value,
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 };
266369
267 pub const Unreach = struct {370 pub fn operandCount(self: *const Constant) usize {
268 pub const base_tag = Tag.unreach;371 return 0;
269 base: Inst,372 }
270 args: void,373 pub fn getOperand(self: *const Constant, index: usize) ?*Inst {
374 return null;
375 }
271 };376 };
272};377};
273378
src-self-hosted/liveness.zig+25-65
...@@ -25,53 +25,38 @@ fn analyzeWithTable(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst,...@@ -25,53 +25,38 @@ fn analyzeWithTable(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst,
25 while (i != 0) {25 while (i != 0) {
26 i -= 1;26 i -= 1;
27 const base = body.instructions[i];27 const base = body.instructions[i];
28 try analyzeInstGeneric(arena, table, base);28 try analyzeInst(arena, table, base);
29 }29 }
30}30}
3131
32fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void {32fn analyzeInst(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.33 if (table.contains(base)) {
34 inline for (std.meta.declarations(ir.Inst)) |decl| {34 base.deaths = 0;
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;
52 } else {35 } else {
53 // No tombstone for this instruction means it is never referenced,36 // No tombstone for this instruction means it is never referenced,
54 // and its birth marks its own death. Very metal 🤘37 // 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;
56 }39 }
5740
58 switch (T) {41 switch (base.tag) {
59 ir.Inst.Constant => return,42 .constant => return,
60 ir.Inst.Block => {43 .block => {
61 try analyzeWithTable(arena, table, inst.args.body);44 const inst = base.castTag(.block).?;
45 try analyzeWithTable(arena, table, inst.body);
62 // We let this continue so that it can possibly mark the block as46 // We let this continue so that it can possibly mark the block as
63 // unreferenced below.47 // unreferenced below.
64 },48 },
65 ir.Inst.CondBr => {49 .condbr => {
50 const inst = base.castTag(.condbr).?;
66 var true_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);51 var true_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
67 defer true_table.deinit();52 defer true_table.deinit();
68 try true_table.ensureCapacity(inst.args.true_body.instructions.len);53 try true_table.ensureCapacity(inst.then_body.instructions.len);
69 try analyzeWithTable(arena, &true_table, inst.args.true_body);54 try analyzeWithTable(arena, &true_table, inst.then_body);
7055
71 var false_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);56 var false_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
72 defer false_table.deinit();57 defer false_table.deinit();
73 try false_table.ensureCapacity(inst.args.false_body.instructions.len);58 try false_table.ensureCapacity(inst.else_body.instructions.len);
74 try analyzeWithTable(arena, &false_table, inst.args.false_body);59 try analyzeWithTable(arena, &false_table, inst.else_body);
7560
76 // Each death that occurs inside one branch, but not the other, needs61 // Each death that occurs inside one branch, but not the other, needs
77 // to be added as a death immediately upon entering the other branch.62 // 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...@@ -112,47 +97,22 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
112 // instruction, and the deaths flag for the CondBr instruction will indicate whether the97 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
113 // condition's lifetime ends immediately before entering any branch.98 // condition's lifetime ends immediately before entering any branch.
114 },99 },
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 },
134 else => {},100 else => {},
135 }101 }
136102
137 const Args = ir.Inst.Args(T);103 const needed_bits = base.operandCount();
138 if (Args == void) {104 if (needed_bits <= ir.Inst.deaths_bits) {
139 return;105 var bit_i: ir.Inst.DeathsBitIndex = 0;
140 }106 while (base.getOperand(bit_i)) |operand| : (bit_i += 1) {
141107 const prev = try table.fetchPut(operand, {});
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), {});
149 if (prev == null) {108 if (prev == null) {
150 // Death.109 // Death.
151 inst.base.deaths |= 1 << arg_index;110 base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
152 }111 }
153 arg_index += 1;
154 }112 }
113 } else {
114 @panic("Handle liveness analysis for instructions with many parameters");
155 }115 }
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 });
158}118}
src-self-hosted/zir.zig+142-189
...@@ -337,7 +337,7 @@ pub const Inst = struct {...@@ -337,7 +337,7 @@ pub const Inst = struct {
337 base: Inst,337 base: Inst,
338338
339 positionals: struct {339 positionals: struct {
340 ptr: *Inst,340 operand: *Inst,
341 },341 },
342 kw_args: struct {},342 kw_args: struct {},
343 };343 };
...@@ -629,8 +629,8 @@ pub const Inst = struct {...@@ -629,8 +629,8 @@ pub const Inst = struct {
629629
630 positionals: struct {630 positionals: struct {
631 condition: *Inst,631 condition: *Inst,
632 true_body: Module.Body,632 then_body: Module.Body,
633 false_body: Module.Body,633 else_body: Module.Body,
634 },634 },
635 kw_args: struct {},635 kw_args: struct {},
636 };636 };
...@@ -1615,7 +1615,7 @@ const EmitZIR = struct {...@@ -1615,7 +1615,7 @@ const EmitZIR = struct {
1615 }1615 }
1616 }1616 }
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 {
1619 const new_inst = try self.arena.allocator.create(T);1619 const new_inst = try self.arena.allocator.create(T);
1620 new_inst.* = .{1620 new_inst.* = .{
1621 .base = .{1621 .base = .{
...@@ -1628,6 +1628,72 @@ const EmitZIR = struct {...@@ -1628,6 +1628,72 @@ const EmitZIR = struct {
1628 return &new_inst.base;1628 return &new_inst.base;
1629 }1629 }
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
1631 fn emitBody(1697 fn emitBody(
1632 self: *EmitZIR,1698 self: *EmitZIR,
1633 body: ir.Body,1699 body: ir.Body,
...@@ -1640,69 +1706,48 @@ const EmitZIR = struct {...@@ -1640,69 +1706,48 @@ const EmitZIR = struct {
1640 };1706 };
1641 for (body.instructions) |inst| {1707 for (body.instructions) |inst| {
1642 const new_inst = switch (inst.tag) {1708 const new_inst = switch (inst.tag) {
1643 .not => blk: {1709 .constant => unreachable, // excluded from function bodies
1644 const old_inst = inst.cast(ir.Inst.Not).?;1710
1645 assert(inst.ty.zigTypeTag() == .Bool);1711 .arg => try self.emitNoOp(inst.src, Inst.Arg),
1646 const new_inst = try self.arena.allocator.create(Inst.BoolNot);1712 .breakpoint => try self.emitNoOp(inst.src, Inst.Breakpoint),
1647 new_inst.* = .{1713 .unreach => try self.emitNoOp(inst.src, Inst.Unreachable),
1648 .base = .{1714 .retvoid => try self.emitNoOp(inst.src, Inst.ReturnVoid),
1649 .src = inst.src,1715
1650 .tag = Inst.BoolNot.base_tag,1716 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, Inst.BoolNot),
1651 },1717 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, Inst.Return),
1652 .positionals = .{1718 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, Inst.PtrToInt),
1653 .operand = try self.resolveInst(new_body, old_inst.args.operand),1719 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, Inst.IsNull),
1654 },1720 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, Inst.IsNonNull),
1655 .kw_args = .{},1721
1656 };1722 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, Inst.Add),
1657 break :blk &new_inst.base;1723 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, Inst.Sub),
1658 },1724
1659 .add => blk: {1725 .cmp_lt => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_lt).?, .lt),
1660 const old_inst = inst.cast(ir.Inst.Add).?;1726 .cmp_lte => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_lte).?, .lte),
1661 const new_inst = try self.arena.allocator.create(Inst.Add);1727 .cmp_eq => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_eq).?, .eq),
1662 new_inst.* = .{1728 .cmp_gte => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_gte).?, .gte),
1663 .base = .{1729 .cmp_gt => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_gt).?, .gt),
1664 .src = inst.src,1730 .cmp_neq => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_neq).?, .neq),
1665 .tag = Inst.Add.base_tag,1731
1666 },1732 .bitcast => blk: {
1667 .positionals = .{1733 const old_inst = inst.castTag(.bitcast).?;
1668 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),1734 const new_inst = try self.arena.allocator.create(Inst.BitCast);
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);
1678 new_inst.* = .{1735 new_inst.* = .{
1679 .base = .{1736 .base = .{
1680 .src = inst.src,1737 .src = inst.src,
1681 .tag = Inst.Sub.base_tag,1738 .tag = Inst.BitCast.base_tag,
1682 },1739 },
1683 .positionals = .{1740 .positionals = .{
1684 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),1741 .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
1685 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),1742 .operand = try self.resolveInst(new_body, old_inst.operand),
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,
1698 },1743 },
1699 .positionals = .{},
1700 .kw_args = .{},1744 .kw_args = .{},
1701 };1745 };
1702 break :blk &new_inst.base;1746 break :blk &new_inst.base;
1703 },1747 },
1748
1704 .block => blk: {1749 .block => blk: {
1705 const old_inst = inst.cast(ir.Inst.Block).?;1750 const old_inst = inst.castTag(.block).?;
1706 const new_inst = try self.arena.allocator.create(Inst.Block);1751 const new_inst = try self.arena.allocator.create(Inst.Block);
17071752
1708 try self.block_table.put(old_inst, new_inst);1753 try self.block_table.put(old_inst, new_inst);
...@@ -1710,7 +1755,7 @@ const EmitZIR = struct {...@@ -1710,7 +1755,7 @@ const EmitZIR = struct {
1710 var block_body = std.ArrayList(*Inst).init(self.allocator);1755 var block_body = std.ArrayList(*Inst).init(self.allocator);
1711 defer block_body.deinit();1756 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
1715 new_inst.* = .{1760 new_inst.* = .{
1716 .base = .{1761 .base = .{
...@@ -1725,47 +1770,49 @@ const EmitZIR = struct {...@@ -1725,47 +1770,49 @@ const EmitZIR = struct {
17251770
1726 break :blk &new_inst.base;1771 break :blk &new_inst.base;
1727 },1772 },
1728 .br => blk: {1773
1729 const old_inst = inst.cast(ir.Inst.Br).?;1774 .brvoid => blk: {
1730 const new_block = self.block_table.get(old_inst.args.block).?;1775 const old_inst = inst.cast(ir.Inst.BrVoid).?;
1731 const new_inst = try self.arena.allocator.create(Inst.Break);1776 const new_block = self.block_table.get(old_inst.block).?;
1777 const new_inst = try self.arena.allocator.create(Inst.BreakVoid);
1732 new_inst.* = .{1778 new_inst.* = .{
1733 .base = .{1779 .base = .{
1734 .src = inst.src,1780 .src = inst.src,
1735 .tag = Inst.Break.base_tag,1781 .tag = Inst.BreakVoid.base_tag,
1736 },1782 },
1737 .positionals = .{1783 .positionals = .{
1738 .block = new_block,1784 .block = new_block,
1739 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1740 },1785 },
1741 .kw_args = .{},1786 .kw_args = .{},
1742 };1787 };
1743 break :blk &new_inst.base;1788 break :blk &new_inst.base;
1744 },1789 },
1745 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),1790
1746 .brvoid => blk: {1791 .br => blk: {
1747 const old_inst = inst.cast(ir.Inst.BrVoid).?;1792 const old_inst = inst.castTag(.br).?;
1748 const new_block = self.block_table.get(old_inst.args.block).?;1793 const new_block = self.block_table.get(old_inst.block).?;
1749 const new_inst = try self.arena.allocator.create(Inst.BreakVoid);1794 const new_inst = try self.arena.allocator.create(Inst.Break);
1750 new_inst.* = .{1795 new_inst.* = .{
1751 .base = .{1796 .base = .{
1752 .src = inst.src,1797 .src = inst.src,
1753 .tag = Inst.BreakVoid.base_tag,1798 .tag = Inst.Break.base_tag,
1754 },1799 },
1755 .positionals = .{1800 .positionals = .{
1756 .block = new_block,1801 .block = new_block,
1802 .operand = try self.resolveInst(new_body, old_inst.operand),
1757 },1803 },
1758 .kw_args = .{},1804 .kw_args = .{},
1759 };1805 };
1760 break :blk &new_inst.base;1806 break :blk &new_inst.base;
1761 },1807 },
1808
1762 .call => blk: {1809 .call => blk: {
1763 const old_inst = inst.cast(ir.Inst.Call).?;1810 const old_inst = inst.castTag(.call).?;
1764 const new_inst = try self.arena.allocator.create(Inst.Call);1811 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);
1767 for (args) |*elem, i| {1814 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]);
1769 }1816 }
1770 new_inst.* = .{1817 new_inst.* = .{
1771 .base = .{1818 .base = .{
...@@ -1773,48 +1820,31 @@ const EmitZIR = struct {...@@ -1773,48 +1820,31 @@ const EmitZIR = struct {
1773 .tag = Inst.Call.base_tag,1820 .tag = Inst.Call.base_tag,
1774 },1821 },
1775 .positionals = .{1822 .positionals = .{
1776 .func = try self.resolveInst(new_body, old_inst.args.func),1823 .func = try self.resolveInst(new_body, old_inst.func),
1777 .args = args,1824 .args = args,
1778 },1825 },
1779 .kw_args = .{},1826 .kw_args = .{},
1780 };1827 };
1781 break :blk &new_inst.base;1828 break :blk &new_inst.base;
1782 },1829 },
1783 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),1830
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
1801 .assembly => blk: {1831 .assembly => blk: {
1802 const old_inst = inst.cast(ir.Inst.Assembly).?;1832 const old_inst = inst.castTag(.assembly).?;
1803 const new_inst = try self.arena.allocator.create(Inst.Asm);1833 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);
1806 for (inputs) |*elem, i| {1836 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;
1808 }1838 }
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);
1811 for (clobbers) |*elem, i| {1841 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;
1813 }1843 }
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);
1816 for (args) |*elem, i| {1846 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]);
1818 }1848 }
18191849
1820 new_inst.* = .{1850 new_inst.* = .{
...@@ -1823,12 +1853,12 @@ const EmitZIR = struct {...@@ -1823,12 +1853,12 @@ const EmitZIR = struct {
1823 .tag = Inst.Asm.base_tag,1853 .tag = Inst.Asm.base_tag,
1824 },1854 },
1825 .positionals = .{1855 .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,
1827 .return_type = (try self.emitType(inst.src, inst.ty)).inst,1857 .return_type = (try self.emitType(inst.src, inst.ty)).inst,
1828 },1858 },
1829 .kw_args = .{1859 .kw_args = .{
1830 .@"volatile" = old_inst.args.is_volatile,1860 .@"volatile" = old_inst.is_volatile,
1831 .output = if (old_inst.args.output) |o|1861 .output = if (old_inst.output) |o|
1832 (try self.emitStringLiteral(inst.src, o)).inst1862 (try self.emitStringLiteral(inst.src, o)).inst
1833 else1863 else
1834 null,1864 null,
...@@ -1839,65 +1869,18 @@ const EmitZIR = struct {...@@ -1839,65 +1869,18 @@ const EmitZIR = struct {
1839 };1869 };
1840 break :blk &new_inst.base;1870 break :blk &new_inst.base;
1841 },1871 },
1842 .ptrtoint => blk: {1872
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 },
1890 .condbr => blk: {1873 .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);1876 var then_body = std.ArrayList(*Inst).init(self.allocator);
1894 var false_body = std.ArrayList(*Inst).init(self.allocator);1877 var else_body = std.ArrayList(*Inst).init(self.allocator);
18951878
1896 defer true_body.deinit();1879 defer then_body.deinit();
1897 defer false_body.deinit();1880 defer else_body.deinit();
18981881
1899 try self.emitBody(old_inst.args.true_body, inst_table, &true_body);1882 try self.emitBody(old_inst.then_body, inst_table, &then_body);
1900 try self.emitBody(old_inst.args.false_body, inst_table, &false_body);1883 try self.emitBody(old_inst.else_body, inst_table, &else_body);
19011884
1902 const new_inst = try self.arena.allocator.create(Inst.CondBr);1885 const new_inst = try self.arena.allocator.create(Inst.CondBr);
1903 new_inst.* = .{1886 new_inst.* = .{
...@@ -1906,39 +1889,9 @@ const EmitZIR = struct {...@@ -1906,39 +1889,9 @@ const EmitZIR = struct {
1906 .tag = Inst.CondBr.base_tag,1889 .tag = Inst.CondBr.base_tag,
1907 },1890 },
1908 .positionals = .{1891 .positionals = .{
1909 .condition = try self.resolveInst(new_body, old_inst.args.condition),1892 .condition = try self.resolveInst(new_body, old_inst.condition),
1910 .true_body = .{ .instructions = true_body.toOwnedSlice() },1893 .then_body = .{ .instructions = then_body.toOwnedSlice() },
1911 .false_body = .{ .instructions = false_body.toOwnedSlice() },1894 .else_body = .{ .instructions = else_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),
1942 },1895 },
1943 .kw_args = .{},1896 .kw_args = .{},
1944 };1897 };
test/stage2/compare_output.zig+37
...@@ -267,5 +267,42 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -267,5 +267,42 @@ pub fn addCases(ctx: *TestContext) !void {
267 ,267 ,
268 "",268 "",
269 );269 );
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 );
270 }307 }
271}308}