| author | |
| committer | |
| log | ef91b11295a549a8173c488d9fd5b3f69b419829 |
| tree | 4b2ef95d5e1dba821b77bfdc4f5e25db127249cc |
| parent | a8065a05a5bc3df4036f1d7abe0928901cf7f5df |
also rework the IR data structures8 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 { |
| 1349 | fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type { | 1349 | fn 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 | } |
| 1940 | 1940 | ||
| 1941 | fn addNewInstArgs( | 1941 | fn 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 | |||
| 1960 | fn 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 | |||
| 1981 | fn 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 | |||
| 2004 | fn 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 | |||
| 2025 | fn 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 | |||
| 2048 | fn 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 | } |
| 1953 | 2069 | ||
| ... | @@ -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 | } |
| 2274 | 2389 | ||
| 2275 | fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst { | 2390 | fn 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 | }; |
| 2290 | 2405 | ||
| 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 | } |
| 2322 | 2437 | ||
| 2323 | fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst { | 2438 | fn 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 | } |
| 2327 | 2442 | ||
| 2328 | fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst { | 2443 | fn 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 | } |
| 2485 | 2597 | ||
| 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 | } |
| 2492 | 2601 | ||
| 2493 | fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { | 2602 | fn 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 | } |
| 2571 | 2680 | ||
| 2572 | fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToInt) InnerError!*Inst { | 2681 | fn 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-address | 2686 | // 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 | } |
| 2582 | 2691 | ||
| 2583 | fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst { | 2692 | fn 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 | } |
| 2735 | 2844 | ||
| 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 | } |
| 2784 | 2890 | ||
| 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 | } |
| 2795 | 2909 | ||
| 2796 | fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!*Inst { | 2910 | fn 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 and | 2941 | } 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 | } |
| 2866 | 2977 | ||
| 2867 | fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst { | 2978 | fn 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); |
| 2880 | 2991 | ||
| 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); |
| 2898 | 3009 | ||
| 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); |
| 2908 | 3019 | ||
| 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 | } |
| 2915 | 3024 | ||
| 2916 | fn wantSafety(self: *Module, scope: *Scope) bool { | 3025 | fn 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 | } |
| 2933 | 3042 | ||
| 2934 | fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst { | 3043 | fn 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 | } |
| 2939 | 3048 | ||
| 2940 | fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst { | 3049 | fn 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 | } |
| 2944 | 3053 | ||
| 2945 | fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void { | 3054 | fn 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 signed | 3142 | // 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); |
| 3133 | 3238 | ||
| 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 | } |
| 3140 | 3241 | ||
| 3141 | fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type { | 3242 | fn 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 errors | 3338 | // 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 | } |
| 3241 | 3342 | ||
| 3242 | fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | 3343 | fn 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 below | 176 | .then_body = undefined, // populated below |
| 177 | .false_body = undefined, // populated below | 177 | .else_body = undefined, // populated below |
| 178 | }, .{}); | 178 | }, .{}); |
| 179 | 179 | ||
| 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 | }; |
| 202 | 202 | ||
| ... | @@ -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 | }; |
| 231 | 231 |
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, |
| 291 | 291 | ||
| 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 | } |
| 298 | 299 | ||
| 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 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 | ||
| 411 | } | 432 | } |
| 412 | } | 433 | } |
| 413 | 434 | ||
| 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 bodies | 453 | .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 | } |
| 438 | 464 | ||
| 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 | } |
| 486 | 512 | ||
| 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 | } |
| 498 | 524 | ||
| 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 | } |
| 627 | 653 | ||
| 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 | } |
| 660 | 686 | ||
| 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 | } |
| 696 | 722 | ||
| 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 | } |
| 744 | 770 | ||
| 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 | } |
| 749 | 775 | ||
| 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 | } |
| 753 | 779 | ||
| 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); |
| 761 | 787 | ||
| 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); |
| 764 | 790 | ||
| 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 | else | 796 | 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); |
| 774 | 800 | ||
| 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); |
| 791 | 817 | ||
| 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 | } |
| 846 | 872 | ||
| 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 | } |
| 852 | 878 | ||
| 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 default | 880 | // 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); |
| 868 | 894 | ||
| 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); |
| 870 | 896 | ||
| ... | @@ -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 | } |
| 884 | 910 | ||
| 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); |
| 888 | 914 | ||
| 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 | } |
| 894 | 920 | ||
| 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 | } |
| 898 | 924 | ||
| 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 | } |
| 916 | 942 | ||
| 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) orelse | 954 | 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 | } |
| 933 | 959 | ||
| 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 | } |
| 939 | 965 | ||
| 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 | } |
| 1171 | 1197 | ||
| 1172 | fn genPtrToInt(self: *Self, inst: *ir.Inst.PtrToInt) !MCValue { | 1198 | fn genPtrToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| 1173 | // no-op | 1199 | // no-op |
| 1174 | return self.resolveInst(inst.args.ptr); | 1200 | return self.resolveInst(inst.operand); |
| 1175 | } | 1201 | } |
| 1176 | 1202 | ||
| 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 | } |
| 1181 | 1207 |
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 | } |
| 107 | 107 | ||
| 108 | fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !void { | 108 | fn 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 |
| 126 | fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void { | 126 | fn 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 | } |
| 154 | 154 | ||
| 155 | fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void { | 155 | fn 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 test | 195 | // 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 | }; |
| 70 | 125 | ||
| 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 | } | ||
| 74 | 142 | ||
| 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 | } |
| 77 | 149 | ||
| 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 | } |
| 90 | 162 | ||
| 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, |
| 94 | 197 | ||
| 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 | }; |
| 100 | 205 | ||
| 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 | }; |
| 106 | 219 | ||
| 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, | ||
| 110 | 224 | ||
| 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 | }; |
| 120 | 242 | ||
| 121 | pub const BitCast = struct { | 243 | pub const Assembly = struct { |
| 122 | pub const base_tag = Tag.bitcast; | 244 | pub const base_tag = Tag.assembly; |
| 123 | 245 | ||
| 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 | }; |
| 129 | 263 | ||
| 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 | }; |
| 139 | 279 | ||
| 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 | }; | ||
| 148 | 282 | ||
| 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 | }; |
| 154 | 296 | ||
| 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 | }; |
| 162 | 310 | ||
| 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 | }; | ||
| 171 | 317 | ||
| 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; | ||
| 174 | 323 | ||
| 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 | }; |
| 182 | 335 | ||
| 183 | pub const CondBr = struct { | 336 | pub const CondBr = struct { |
| 184 | pub const base_tag = Tag.condbr; | 337 | pub const base_tag = Tag.condbr; |
| 185 | 338 | ||
| 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 | }; | ||
| 199 | 349 | ||
| 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; | ||
| 202 | 355 | ||
| 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 | }; |
| 208 | 363 | ||
| 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; | ||
| 227 | 366 | ||
| 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 | }; | ||
| 266 | 369 | ||
| 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 | }; |
| 273 | 378 |
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 | } |
| 31 | 31 | ||
| 32 | fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void { | 32 | fn 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 | |||
| 49 | fn 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 | } |
| 57 | 40 | ||
| 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 as | 46 | // 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); |
| 70 | 55 | ||
| 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); |
| 75 | 60 | ||
| 76 | // Each death that occurs inside one branch, but not the other, needs | 61 | // 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 the | 97 | // 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 | } |
| 136 | 102 | ||
| 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) { |
| 141 | 107 | 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 | } |
| 156 | 116 | ||
| 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, |
| 338 | 338 | ||
| 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 { |
| 629 | 629 | ||
| 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 | } |
| 1617 | 1617 | ||
| 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 | } |
| 1630 | 1630 | ||
| 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); |
| 1707 | 1752 | ||
| 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(); |
| 1712 | 1757 | ||
| 1713 | try self.emitBody(old_inst.args.body, inst_table, &block_body); | 1758 | try self.emitBody(old_inst.body, inst_table, &block_body); |
| 1714 | 1759 | ||
| 1715 | new_inst.* = .{ | 1760 | new_inst.* = .{ |
| 1716 | .base = .{ | 1761 | .base = .{ |
| ... | @@ -1725,47 +1770,49 @@ const EmitZIR = struct { | ... | @@ -1725,47 +1770,49 @@ const EmitZIR = struct { |
| 1725 | 1770 | ||
| 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); |
| 1765 | 1812 | ||
| 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); |
| 1804 | 1834 | ||
| 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 | } |
| 1809 | 1839 | ||
| 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 | } |
| 1814 | 1844 | ||
| 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 | } |
| 1819 | 1849 | ||
| 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)).inst | 1862 | (try self.emitStringLiteral(inst.src, o)).inst |
| 1833 | else | 1863 | 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).?; |
| 1892 | 1875 | ||
| 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); |
| 1895 | 1878 | ||
| 1896 | defer true_body.deinit(); | 1879 | defer then_body.deinit(); |
| 1897 | defer false_body.deinit(); | 1880 | defer else_body.deinit(); |
| 1898 | 1881 | ||
| 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); |
| 1901 | 1884 | ||
| 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 | } |