| 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 | 1349 | fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type { |
| 1350 | 1350 | try self.analyzeBody(&block_scope.base, body); |
| 1351 | 1351 | for (block_scope.instructions.items) |inst| { |
| 1352 | if (inst.cast(Inst.Ret)) |ret| { | |
| 1353 | const val = try self.resolveConstValue(&block_scope.base, ret.args.operand); | |
| 1352 | if (inst.castTag(.ret)) |ret| { | |
| 1353 | const val = try self.resolveConstValue(&block_scope.base, ret.operand); | |
| 1354 | 1354 | return val.toType(); |
| 1355 | 1355 | } else { |
| 1356 | 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 | 1938 | }; |
| 1939 | 1939 | } |
| 1940 | 1940 | |
| 1941 | fn addNewInstArgs( | |
| 1941 | fn addNoOp( | |
| 1942 | 1942 | self: *Module, |
| 1943 | 1943 | block: *Scope.Block, |
| 1944 | 1944 | src: usize, |
| 1945 | 1945 | ty: Type, |
| 1946 | comptime T: type, | |
| 1947 | args: Inst.Args(T), | |
| 1946 | comptime tag: Inst.Tag, | |
| 1947 | ) !*Inst { | |
| 1948 | const inst = try block.arena.create(tag.Type()); | |
| 1949 | inst.* = .{ | |
| 1950 | .base = .{ | |
| 1951 | .tag = tag, | |
| 1952 | .ty = ty, | |
| 1953 | .src = src, | |
| 1954 | }, | |
| 1955 | }; | |
| 1956 | try block.instructions.append(self.gpa, &inst.base); | |
| 1957 | return &inst.base; | |
| 1958 | } | |
| 1959 | ||
| 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 | 2032 | ) !*Inst { |
| 1949 | const inst = try self.addNewInst(block, src, ty, T); | |
| 1950 | inst.args = args; | |
| 2033 | const inst = try block.arena.create(Inst.CondBr); | |
| 2034 | inst.* = .{ | |
| 2035 | .base = .{ | |
| 2036 | .tag = .condbr, | |
| 2037 | .ty = Type.initTag(.noreturn), | |
| 2038 | .src = src, | |
| 2039 | }, | |
| 2040 | .condition = condition, | |
| 2041 | .then_body = then_body, | |
| 2042 | .else_body = else_body, | |
| 2043 | }; | |
| 2044 | try block.instructions.append(self.gpa, &inst.base); | |
| 2045 | return &inst.base; | |
| 2046 | } | |
| 2047 | ||
| 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 | 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 | 2133 | .ty = ty, |
| 2018 | 2134 | .src = src, |
| 2019 | 2135 | }, |
| 2020 | .args = undefined, | |
| 2021 | 2136 | }; |
| 2022 | 2137 | try block.instructions.append(self.gpa, &inst.base); |
| 2023 | 2138 | return inst; |
| ... | ... | @@ -2269,7 +2384,7 @@ fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError! |
| 2269 | 2384 | }); |
| 2270 | 2385 | } |
| 2271 | 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 | 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 | 2400 | .ty = undefined, // Set after analysis. |
| 2286 | 2401 | .src = inst.base.src, |
| 2287 | 2402 | }, |
| 2288 | .args = undefined, | |
| 2403 | .body = undefined, | |
| 2289 | 2404 | }; |
| 2290 | 2405 | |
| 2291 | 2406 | var child_block: Scope.Block = .{ |
| ... | ... | @@ -2316,13 +2431,13 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr |
| 2316 | 2431 | // to emit a jump instruction to after the block when it encounters the break. |
| 2317 | 2432 | try parent_block.instructions.append(self.gpa, &block_inst.base); |
| 2318 | 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 | 2435 | return &block_inst.base; |
| 2321 | 2436 | } |
| 2322 | 2437 | |
| 2323 | 2438 | fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst { |
| 2324 | 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 | 2443 | fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst { |
| ... | ... | @@ -2350,10 +2465,7 @@ fn analyzeBreak( |
| 2350 | 2465 | if (label.zir_block == zir_block) { |
| 2351 | 2466 | try label.results.append(self.gpa, operand); |
| 2352 | 2467 | const b = try self.requireRuntimeBlock(scope, src); |
| 2353 | return self.addNewInstArgs(b, src, Type.initTag(.noreturn), Inst.Br, .{ | |
| 2354 | .block = label.block_inst, | |
| 2355 | .operand = operand, | |
| 2356 | }); | |
| 2468 | return self.addBr(b, src, label.block_inst, operand); | |
| 2357 | 2469 | } |
| 2358 | 2470 | } |
| 2359 | 2471 | opt_block = block.parent; |
| ... | ... | @@ -2484,10 +2596,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro |
| 2484 | 2596 | } |
| 2485 | 2597 | |
| 2486 | 2598 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 2487 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, .{ | |
| 2488 | .func = func, | |
| 2489 | .args = casted_args, | |
| 2490 | }); | |
| 2599 | return self.addCall(b, inst.base.src, Type.initTag(.void), func, casted_args); | |
| 2491 | 2600 | } |
| 2492 | 2601 | |
| 2493 | 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 | 2679 | } |
| 2571 | 2680 | |
| 2572 | 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 | 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 | 2686 | // TODO handle known-pointer-address |
| 2578 | 2687 | const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src); |
| 2579 | 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 | 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 | 2843 | } |
| 2735 | 2844 | |
| 2736 | 2845 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 2737 | return self.addNewInstArgs(b, inst.base.src, lhs.ty, Inst.Add, .{ | |
| 2738 | .lhs = lhs, | |
| 2739 | .rhs = rhs, | |
| 2740 | }); | |
| 2846 | return self.addBinOp(b, inst.base.src, lhs.ty, .add, lhs, rhs); | |
| 2741 | 2847 | } |
| 2742 | 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 | 2889 | } |
| 2784 | 2890 | |
| 2785 | 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 | 2899 | .asm_source = asm_source, |
| 2788 | 2900 | .is_volatile = assembly.kw_args.@"volatile", |
| 2789 | 2901 | .output = output, |
| 2790 | 2902 | .inputs = inputs, |
| 2791 | 2903 | .clobbers = clobbers, |
| 2792 | 2904 | .args = args, |
| 2793 | }); | |
| 2905 | }; | |
| 2906 | try b.instructions.append(self.gpa, &inst.base); | |
| 2907 | return &inst.base; | |
| 2794 | 2908 | } |
| 2795 | 2909 | |
| 2796 | 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 | 2932 | return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null); |
| 2819 | 2933 | } |
| 2820 | 2934 | const b = try self.requireRuntimeBlock(scope, inst.base.src); |
| 2821 | switch (op) { | |
| 2822 | .eq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNull, .{ | |
| 2823 | .operand = opt_operand, | |
| 2824 | }), | |
| 2825 | .neq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNonNull, .{ | |
| 2826 | .operand = opt_operand, | |
| 2827 | }), | |
| 2935 | const inst_tag: Inst.Tag = switch (op) { | |
| 2936 | .eq => .isnull, | |
| 2937 | .neq => .isnonnull, | |
| 2828 | 2938 | else => unreachable, |
| 2829 | } | |
| 2939 | }; | |
| 2940 | return self.addUnOp(b, inst.base.src, Type.initTag(.bool), inst_tag, opt_operand); | |
| 2830 | 2941 | } else if (is_equality_cmp and |
| 2831 | 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 | 2972 | return self.constBool(scope, inst.base.src, !val.toBool()); |
| 2862 | 2973 | } |
| 2863 | 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 | 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 | 2990 | const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond); |
| 2880 | 2991 | |
| 2881 | 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 | 2994 | try self.analyzeBody(scope, body.*); |
| 2884 | 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 | 3005 | .arena = parent_block.arena, |
| 2895 | 3006 | }; |
| 2896 | 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 | 3010 | var false_block: Scope.Block = .{ |
| 2900 | 3011 | .parent = parent_block, |
| ... | ... | @@ -2904,13 +3015,11 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner |
| 2904 | 3015 | .arena = parent_block.arena, |
| 2905 | 3016 | }; |
| 2906 | 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){ | |
| 2910 | .condition = cond, | |
| 2911 | .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) }, | |
| 2912 | .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) }, | |
| 2913 | }); | |
| 3020 | const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) }; | |
| 3021 | const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) }; | |
| 3022 | return self.addCondBr(parent_block, inst.base.src, cond, then_body, else_body); | |
| 2914 | 3023 | } |
| 2915 | 3024 | |
| 2916 | 3025 | fn wantSafety(self: *Module, scope: *Scope) bool { |
| ... | ... | @@ -2926,20 +3035,20 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea |
| 2926 | 3035 | const b = try self.requireRuntimeBlock(scope, unreach.base.src); |
| 2927 | 3036 | if (self.wantSafety(scope)) { |
| 2928 | 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 | 3043 | fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst { |
| 2935 | 3044 | const operand = try self.resolveInst(scope, inst.positionals.operand); |
| 2936 | 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 | 3049 | fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst { |
| 2941 | 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 | 3054 | fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void { |
| ... | ... | @@ -3027,11 +3136,7 @@ fn cmpNumeric( |
| 3027 | 3136 | }; |
| 3028 | 3137 | const casted_lhs = try self.coerce(scope, dest_type, lhs); |
| 3029 | 3138 | const casted_rhs = try self.coerce(scope, dest_type, rhs); |
| 3030 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{ | |
| 3031 | .lhs = casted_lhs, | |
| 3032 | .rhs = casted_rhs, | |
| 3033 | .op = op, | |
| 3034 | }); | |
| 3139 | return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | |
| 3035 | 3140 | } |
| 3036 | 3141 | // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. |
| 3037 | 3142 | // For mixed signed and unsigned integers, implicit cast both operands to a signed |
| ... | ... | @@ -3131,11 +3236,7 @@ fn cmpNumeric( |
| 3131 | 3236 | const casted_lhs = try self.coerce(scope, dest_type, lhs); |
| 3132 | 3237 | const casted_rhs = try self.coerce(scope, dest_type, rhs); |
| 3133 | 3238 | |
| 3134 | return self.addNewInstArgs(b, src, Type.initTag(.bool), Inst.Cmp, .{ | |
| 3135 | .lhs = casted_lhs, | |
| 3136 | .rhs = casted_rhs, | |
| 3137 | .op = op, | |
| 3138 | }); | |
| 3239 | return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | |
| 3139 | 3240 | } |
| 3140 | 3241 | |
| 3141 | 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 | 3337 | } |
| 3237 | 3338 | // TODO validate the type size and other compile errors |
| 3238 | 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 | 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 | 173 | const if_src = tree.token_locs[if_node.if_token].start; |
| 174 | 174 | const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{ |
| 175 | 175 | .condition = cond, |
| 176 | .true_body = undefined, // populated below | |
| 177 | .false_body = undefined, // populated below | |
| 176 | .then_body = undefined, // populated below | |
| 177 | .else_body = undefined, // populated below | |
| 178 | 178 | }, .{}); |
| 179 | 179 | |
| 180 | 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 | 196 | .operand = then_result, |
| 197 | 197 | }, .{}); |
| 198 | 198 | } |
| 199 | condbr.positionals.true_body = .{ | |
| 199 | condbr.positionals.then_body = .{ | |
| 200 | 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 | 225 | .block = block, |
| 226 | 226 | }, .{}); |
| 227 | 227 | } |
| 228 | condbr.positionals.false_body = .{ | |
| 228 | condbr.positionals.else_body = .{ | |
| 229 | 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 | 290 | next_stack_offset: u32 = 0, |
| 291 | 291 | |
| 292 | 292 | fn markRegUsed(self: *Branch, reg: Register) void { |
| 293 | if (FreeRegInt == u0) return; | |
| 293 | 294 | const index = reg.allocIndex() orelse return; |
| 294 | 295 | const ShiftInt = std.math.Log2Int(FreeRegInt); |
| 295 | 296 | const shift = @intCast(ShiftInt, index); |
| ... | ... | @@ -297,6 +298,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 297 | 298 | } |
| 298 | 299 | |
| 299 | 300 | fn markRegFree(self: *Branch, reg: Register) void { |
| 301 | if (FreeRegInt == u0) return; | |
| 300 | 302 | const index = reg.allocIndex() orelse return; |
| 301 | 303 | const ShiftInt = std.math.Log2Int(FreeRegInt); |
| 302 | 304 | const shift = @intCast(ShiftInt, index); |
| ... | ... | @@ -407,40 +409,64 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 407 | 409 | for (body.instructions) |inst| { |
| 408 | 410 | const new_inst = try self.genFuncInst(inst); |
| 409 | 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 | 435 | fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue { |
| 415 | 436 | switch (inst.tag) { |
| 416 | .add => return self.genAdd(inst.cast(ir.Inst.Add).?), | |
| 417 | .arg => return self.genArg(inst.cast(ir.Inst.Arg).?), | |
| 418 | .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?), | |
| 419 | .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?), | |
| 420 | .block => return self.genBlock(inst.cast(ir.Inst.Block).?), | |
| 421 | .br => return self.genBr(inst.cast(ir.Inst.Br).?), | |
| 437 | .add => return self.genAdd(inst.castTag(.add).?), | |
| 438 | .arg => return self.genArg(inst.castTag(.arg).?), | |
| 439 | .assembly => return self.genAsm(inst.castTag(.assembly).?), | |
| 440 | .bitcast => return self.genBitCast(inst.castTag(.bitcast).?), | |
| 441 | .block => return self.genBlock(inst.castTag(.block).?), | |
| 442 | .br => return self.genBr(inst.castTag(.br).?), | |
| 422 | 443 | .breakpoint => return self.genBreakpoint(inst.src), |
| 423 | .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?), | |
| 424 | .call => return self.genCall(inst.cast(ir.Inst.Call).?), | |
| 425 | .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?), | |
| 426 | .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?), | |
| 444 | .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?), | |
| 445 | .call => return self.genCall(inst.castTag(.call).?), | |
| 446 | .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt), | |
| 447 | .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte), | |
| 448 | .cmp_eq => return self.genCmp(inst.castTag(.cmp_eq).?, .eq), | |
| 449 | .cmp_gte => return self.genCmp(inst.castTag(.cmp_gte).?, .gte), | |
| 450 | .cmp_gt => return self.genCmp(inst.castTag(.cmp_gt).?, .gt), | |
| 451 | .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq), | |
| 452 | .condbr => return self.genCondBr(inst.castTag(.condbr).?), | |
| 427 | 453 | .constant => unreachable, // excluded from function bodies |
| 428 | .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?), | |
| 429 | .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?), | |
| 430 | .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?), | |
| 431 | .ret => return self.genRet(inst.cast(ir.Inst.Ret).?), | |
| 432 | .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?), | |
| 433 | .sub => return self.genSub(inst.cast(ir.Inst.Sub).?), | |
| 454 | .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?), | |
| 455 | .isnull => return self.genIsNull(inst.castTag(.isnull).?), | |
| 456 | .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?), | |
| 457 | .ret => return self.genRet(inst.castTag(.ret).?), | |
| 458 | .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?), | |
| 459 | .sub => return self.genSub(inst.castTag(.sub).?), | |
| 434 | 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 | 466 | // No side effects, so if it's unreferenced, do nothing. |
| 441 | 467 | if (inst.base.isUnused()) |
| 442 | 468 | return MCValue.dead; |
| 443 | const operand = try self.resolveInst(inst.args.operand); | |
| 469 | const operand = try self.resolveInst(inst.operand); | |
| 444 | 470 | switch (operand) { |
| 445 | 471 | .dead => unreachable, |
| 446 | 472 | .unreach => unreachable, |
| ... | ... | @@ -473,36 +499,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 473 | 499 | .base = .{ |
| 474 | 500 | .tag = .constant, |
| 475 | 501 | .deaths = 0, |
| 476 | .ty = inst.args.operand.ty, | |
| 477 | .src = inst.args.operand.src, | |
| 502 | .ty = inst.operand.ty, | |
| 503 | .src = inst.operand.src, | |
| 478 | 504 | }, |
| 479 | 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 | 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 | 514 | // No side effects, so if it's unreferenced, do nothing. |
| 489 | 515 | if (inst.base.isUnused()) |
| 490 | 516 | return MCValue.dead; |
| 491 | 517 | switch (arch) { |
| 492 | 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 | 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 | 526 | // No side effects, so if it's unreferenced, do nothing. |
| 501 | 527 | if (inst.base.isUnused()) |
| 502 | 528 | return MCValue.dead; |
| 503 | 529 | switch (arch) { |
| 504 | 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 | 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 | 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 | 655 | if (FreeRegInt == u0) { |
| 630 | 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 | 685 | } |
| 660 | 686 | |
| 661 | 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 | 689 | const cc = fn_ty.fnCallingConvention(); |
| 664 | 690 | const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); |
| 665 | 691 | defer self.gpa.free(param_types); |
| ... | ... | @@ -671,8 +697,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 671 | 697 | switch (arch) { |
| 672 | 698 | .x86_64 => { |
| 673 | 699 | for (mc_args) |mc_arg, arg_i| { |
| 674 | const arg = inst.args.args[arg_i]; | |
| 675 | const arg_mcv = try self.resolveInst(inst.args.args[arg_i]); | |
| 700 | const arg = inst.args[arg_i]; | |
| 701 | const arg_mcv = try self.resolveInst(inst.args[arg_i]); | |
| 676 | 702 | switch (mc_arg) { |
| 677 | 703 | .none => continue, |
| 678 | 704 | .register => |reg| { |
| ... | ... | @@ -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 | 724 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { |
| 699 | 725 | const func = func_val.func; |
| 700 | 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 | 768 | return .unreach; |
| 743 | 769 | } |
| 744 | 770 | |
| 745 | fn genRet(self: *Self, inst: *ir.Inst.Ret) !MCValue { | |
| 746 | const operand = try self.resolveInst(inst.args.operand); | |
| 771 | fn genRet(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | |
| 772 | const operand = try self.resolveInst(inst.operand); | |
| 747 | 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 | 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 | 781 | // No side effects, so if it's unreferenced, do nothing. |
| 756 | 782 | if (inst.base.isUnused()) |
| 757 | 783 | return MCValue.dead; |
| ... | ... | @@ -759,25 +785,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 759 | 785 | .x86_64 => { |
| 760 | 786 | try self.code.ensureCapacity(self.code.items.len + 8); |
| 761 | 787 | |
| 762 | const lhs = try self.resolveInst(inst.args.lhs); | |
| 763 | const rhs = try self.resolveInst(inst.args.rhs); | |
| 788 | const lhs = try self.resolveInst(inst.lhs); | |
| 789 | const rhs = try self.resolveInst(inst.rhs); | |
| 764 | 790 | |
| 765 | 791 | // There are 2 operands, destination and source. |
| 766 | 792 | // Either one, but not both, can be a memory operand. |
| 767 | 793 | // Source operand can be an immediate, 8 bits or 32 bits. |
| 768 | 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 | 796 | else |
| 771 | 797 | lhs; |
| 772 | 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 | 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 | 803 | if (info.signed) { |
| 778 | return MCValue{ .compare_flags_signed = inst.args.op }; | |
| 804 | return MCValue{ .compare_flags_signed = op }; | |
| 779 | 805 | } else { |
| 780 | return MCValue{ .compare_flags_unsigned = inst.args.op }; | |
| 806 | return MCValue{ .compare_flags_unsigned = op }; | |
| 781 | 807 | } |
| 782 | 808 | }, |
| 783 | 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 | 815 | .x86_64 => { |
| 790 | 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 | 819 | switch (cond) { |
| 794 | 820 | .compare_flags_signed => |cmp_op| { |
| 795 | 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 | 864 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode }); |
| 839 | 865 | const reloc = Reloc{ .rel32 = self.code.items.len }; |
| 840 | 866 | self.code.items.len += 4; |
| 841 | try self.genBody(inst.args.true_body); | |
| 867 | try self.genBody(inst.then_body); | |
| 842 | 868 | try self.performReloc(inst.base.src, reloc); |
| 843 | try self.genBody(inst.args.false_body); | |
| 869 | try self.genBody(inst.else_body); | |
| 844 | 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 | 874 | switch (arch) { |
| 849 | 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 | 880 | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 855 | 881 | // will call genIsNull and invert the result. |
| 856 | 882 | switch (arch) { |
| ... | ... | @@ -864,7 +890,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 864 | 890 | } |
| 865 | 891 | // A block is nothing but a setup to be able to jump to the end. |
| 866 | 892 | defer inst.codegen.relocs.deinit(self.gpa); |
| 867 | try self.genBody(inst.args.body); | |
| 893 | try self.genBody(inst.body); | |
| 868 | 894 | |
| 869 | 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 | 909 | } |
| 884 | 910 | |
| 885 | 911 | fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue { |
| 886 | if (!inst.args.operand.ty.hasCodeGenBits()) | |
| 887 | return self.brVoid(inst.base.src, inst.args.block); | |
| 912 | if (!inst.operand.ty.hasCodeGenBits()) | |
| 913 | return self.brVoid(inst.base.src, inst.block); | |
| 888 | 914 | |
| 889 | const operand = try self.resolveInst(inst.args.operand); | |
| 915 | const operand = try self.resolveInst(inst.operand); | |
| 890 | 916 | switch (arch) { |
| 891 | 917 | else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}), |
| 892 | 918 | } |
| 893 | 919 | } |
| 894 | 920 | |
| 895 | 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 | 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 | 941 | } |
| 916 | 942 | |
| 917 | 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 | 945 | return MCValue.dead; |
| 920 | 946 | if (arch != .x86_64 and arch != .i386) { |
| 921 | 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 | 950 | if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { |
| 925 | 951 | return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); |
| 926 | 952 | } |
| 927 | 953 | const reg_name = input[1 .. input.len - 1]; |
| 928 | 954 | const reg = parseRegName(reg_name) orelse |
| 929 | 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 | 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 | 961 | try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 }); |
| 936 | 962 | } else { |
| 937 | 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 | 967 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 942 | 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 | 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 | 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 { | |
| 1178 | const operand = try self.resolveInst(inst.args.operand); | |
| 1203 | fn genBitCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | |
| 1204 | const operand = try self.resolveInst(inst.operand); | |
| 1179 | 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 | 92 | for (instructions) |inst| { |
| 93 | 93 | try writer.writeAll("\n\t"); |
| 94 | 94 | switch (inst.tag) { |
| 95 | .assembly => try genAsm(file, inst.cast(Inst.Assembly).?, decl), | |
| 96 | .call => try genCall(file, inst.cast(Inst.Call).?, decl), | |
| 97 | .ret => try genRet(file, inst.cast(Inst.Ret).?, decl, tv.ty.fnReturnType()), | |
| 95 | .assembly => try genAsm(file, inst.castTag(.assembly).?, decl), | |
| 96 | .call => try genCall(file, inst.castTag(.call).?, decl), | |
| 97 | .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()), | |
| 98 | 98 | .retvoid => try file.main.writer().print("return;", .{}), |
| 99 | 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 | 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 | 109 | const writer = file.main.writer(); |
| 110 | const ret_value = inst.args.operand; | |
| 110 | const ret_value = inst.operand; | |
| 111 | 111 | const value = ret_value.value().?; |
| 112 | 112 | if (expected_return_type.eql(ret_value.ty)) |
| 113 | 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 | 126 | fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void { |
| 127 | 127 | const writer = file.main.writer(); |
| 128 | 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 | 130 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { |
| 131 | 131 | const target = func_val.func.owner_decl; |
| 132 | 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 | 144 | } else { |
| 145 | 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 | 148 | return file.fail(decl.src(), "TODO function arguments", .{}); |
| 149 | 149 | } |
| 150 | 150 | } else { |
| ... | ... | @@ -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 { | |
| 156 | const as = inst.args; | |
| 155 | fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void { | |
| 157 | 156 | const writer = file.main.writer(); |
| 158 | 157 | for (as.inputs) |i, index| { |
| 159 | 158 | if (i[0] == '{' and i[i.len - 1] == '}') { |
| 160 | 159 | const reg = i[1 .. i.len - 1]; |
| 161 | 160 | const arg = as.args[index]; |
| 162 | if (arg.cast(Inst.Constant)) |c| { | |
| 161 | if (arg.castTag(.constant)) |c| { | |
| 163 | 162 | if (c.val.tag() == .int_u64) { |
| 164 | 163 | try writer.writeAll("register "); |
| 165 | 164 | try renderType(file, writer, arg.ty, decl.src()); |
| ... | ... | @@ -190,7 +189,7 @@ fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void { |
| 190 | 189 | if (index > 0) { |
| 191 | 190 | try writer.writeAll(", "); |
| 192 | 191 | } |
| 193 | if (arg.cast(Inst.Constant)) |c| { | |
| 192 | if (arg.castTag(.constant)) |c| { | |
| 194 | 193 | try writer.print("\"\"({}_constant)", .{reg}); |
| 195 | 194 | } else { |
| 196 | 195 | // This is blocked by the earlier test |
src-self-hosted/ir.zig+230-125| ... | ... | @@ -55,7 +55,12 @@ pub const Inst = struct { |
| 55 | 55 | breakpoint, |
| 56 | 56 | brvoid, |
| 57 | 57 | call, |
| 58 | cmp, | |
| 58 | cmp_lt, | |
| 59 | cmp_lte, | |
| 60 | cmp_eq, | |
| 61 | cmp_gte, | |
| 62 | cmp_gt, | |
| 63 | cmp_neq, | |
| 59 | 64 | condbr, |
| 60 | 65 | constant, |
| 61 | 66 | isnonnull, |
| ... | ... | @@ -66,13 +71,80 @@ pub const Inst = struct { |
| 66 | 71 | sub, |
| 67 | 72 | unreach, |
| 68 | 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 | 127 | pub fn cast(base: *Inst, comptime T: type) ?*T { |
| 72 | if (base.tag != T.base_tag) | |
| 73 | return null; | |
| 128 | if (@hasField(T, "base_tag")) { | |
| 129 | return base.castTag(T.base_tag); | |
| 130 | } | |
| 131 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 132 | const tag = @intToEnum(Tag, field.value); | |
| 133 | if (base.tag == tag) { | |
| 134 | if (T == tag.Type()) { | |
| 135 | return @fieldParentPtr(T, "base", base); | |
| 136 | } | |
| 137 | return null; | |
| 138 | } | |
| 139 | } | |
| 140 | unreachable; | |
| 141 | } | |
| 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 | 150 | pub fn Args(comptime T: type) type { |
| ... | ... | @@ -88,186 +160,219 @@ pub const Inst = struct { |
| 88 | 160 | return inst.val; |
| 89 | 161 | } |
| 90 | 162 | |
| 91 | pub const Add = struct { | |
| 92 | pub const base_tag = Tag.add; | |
| 163 | pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator { | |
| 164 | return switch (self.base.tag) { | |
| 165 | .cmp_lt => .lt, | |
| 166 | .cmp_lte => .lte, | |
| 167 | .cmp_eq => .eq, | |
| 168 | .cmp_gte => .gte, | |
| 169 | .cmp_gt => .gt, | |
| 170 | .cmp_neq => .neq, | |
| 171 | else => null, | |
| 172 | }; | |
| 173 | } | |
| 174 | ||
| 175 | pub fn operandCount(base: *Inst) usize { | |
| 176 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 177 | const tag = @intToEnum(Tag, field.value); | |
| 178 | if (tag == base.tag) { | |
| 179 | return @fieldParentPtr(tag.Type(), "base", base).operandCount(); | |
| 180 | } | |
| 181 | } | |
| 182 | unreachable; | |
| 183 | } | |
| 184 | ||
| 185 | pub fn getOperand(base: *Inst, index: usize) ?*Inst { | |
| 186 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 187 | const tag = @intToEnum(Tag, field.value); | |
| 188 | if (tag == base.tag) { | |
| 189 | return @fieldParentPtr(tag.Type(), "base", base).getOperand(index); | |
| 190 | } | |
| 191 | } | |
| 192 | unreachable; | |
| 193 | } | |
| 194 | ||
| 195 | pub const NoOp = struct { | |
| 93 | 196 | base: Inst, |
| 94 | 197 | |
| 95 | args: struct { | |
| 96 | lhs: *Inst, | |
| 97 | rhs: *Inst, | |
| 98 | }, | |
| 198 | pub fn operandCount(self: *const NoOp) usize { | |
| 199 | return 0; | |
| 200 | } | |
| 201 | pub fn getOperand(self: *const NoOp, index: usize) ?*Inst { | |
| 202 | return null; | |
| 203 | } | |
| 99 | 204 | }; |
| 100 | 205 | |
| 101 | pub const Arg = struct { | |
| 102 | pub const base_tag = Tag.arg; | |
| 206 | pub const UnOp = struct { | |
| 103 | 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 { | |
| 108 | pub const base_tag = Tag.assembly; | |
| 220 | pub const BinOp = struct { | |
| 109 | 221 | base: Inst, |
| 222 | lhs: *Inst, | |
| 223 | rhs: *Inst, | |
| 110 | 224 | |
| 111 | args: struct { | |
| 112 | asm_source: []const u8, | |
| 113 | is_volatile: bool, | |
| 114 | output: ?[]const u8, | |
| 115 | inputs: []const []const u8, | |
| 116 | clobbers: []const []const u8, | |
| 117 | args: []const *Inst, | |
| 118 | }, | |
| 225 | pub fn operandCount(self: *const BinOp) usize { | |
| 226 | return 2; | |
| 227 | } | |
| 228 | pub fn getOperand(self: *const BinOp, index: usize) ?*Inst { | |
| 229 | var i = index; | |
| 230 | ||
| 231 | if (i < 1) | |
| 232 | return self.lhs; | |
| 233 | i -= 1; | |
| 234 | ||
| 235 | if (i < 1) | |
| 236 | return self.rhs; | |
| 237 | i -= 1; | |
| 238 | ||
| 239 | return null; | |
| 240 | } | |
| 119 | 241 | }; |
| 120 | 242 | |
| 121 | pub const BitCast = struct { | |
| 122 | pub const base_tag = Tag.bitcast; | |
| 243 | pub const Assembly = struct { | |
| 244 | pub const base_tag = Tag.assembly; | |
| 123 | 245 | |
| 124 | 246 | base: Inst, |
| 125 | args: struct { | |
| 126 | operand: *Inst, | |
| 127 | }, | |
| 247 | asm_source: []const u8, | |
| 248 | is_volatile: bool, | |
| 249 | output: ?[]const u8, | |
| 250 | inputs: []const []const u8, | |
| 251 | clobbers: []const []const u8, | |
| 252 | args: []const *Inst, | |
| 253 | ||
| 254 | pub fn operandCount(self: *const Assembly) usize { | |
| 255 | return self.args.len; | |
| 256 | } | |
| 257 | pub fn getOperand(self: *const Assembly, index: usize) ?*Inst { | |
| 258 | if (index < self.args.len) | |
| 259 | return self.args[index]; | |
| 260 | return null; | |
| 261 | } | |
| 128 | 262 | }; |
| 129 | 263 | |
| 130 | 264 | pub const Block = struct { |
| 131 | 265 | pub const base_tag = Tag.block; |
| 266 | ||
| 132 | 267 | base: Inst, |
| 133 | args: struct { | |
| 134 | body: Body, | |
| 135 | }, | |
| 268 | body: Body, | |
| 136 | 269 | /// This memory is reserved for codegen code to do whatever it needs to here. |
| 137 | 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 | 280 | pub const Br = struct { |
| 141 | 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 | 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 | 297 | pub const BrVoid = struct { |
| 156 | 298 | pub const base_tag = Tag.brvoid; |
| 299 | ||
| 157 | 300 | base: Inst, |
| 158 | args: struct { | |
| 159 | block: *Block, | |
| 160 | }, | |
| 301 | block: *Block, | |
| 302 | ||
| 303 | pub fn operandCount(self: *const BrVoid) usize { | |
| 304 | return 0; | |
| 305 | } | |
| 306 | pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst { | |
| 307 | return null; | |
| 308 | } | |
| 161 | 309 | }; |
| 162 | 310 | |
| 163 | 311 | pub const Call = struct { |
| 164 | 312 | pub const base_tag = Tag.call; |
| 313 | ||
| 165 | 314 | base: Inst, |
| 166 | args: struct { | |
| 167 | func: *Inst, | |
| 168 | args: []const *Inst, | |
| 169 | }, | |
| 170 | }; | |
| 315 | func: *Inst, | |
| 316 | args: []const *Inst, | |
| 171 | 317 | |
| 172 | pub const Cmp = struct { | |
| 173 | pub const base_tag = Tag.cmp; | |
| 318 | pub fn operandCount(self: *const Call) usize { | |
| 319 | return self.args.len + 1; | |
| 320 | } | |
| 321 | pub fn getOperand(self: *const Call, index: usize) ?*Inst { | |
| 322 | var i = index; | |
| 174 | 323 | |
| 175 | base: Inst, | |
| 176 | args: struct { | |
| 177 | lhs: *Inst, | |
| 178 | op: std.math.CompareOperator, | |
| 179 | rhs: *Inst, | |
| 180 | }, | |
| 324 | if (i < 1) | |
| 325 | return self.func; | |
| 326 | i -= 1; | |
| 327 | ||
| 328 | if (i < self.args.len) | |
| 329 | return self.args[i]; | |
| 330 | i -= self.args.len; | |
| 331 | ||
| 332 | return null; | |
| 333 | } | |
| 181 | 334 | }; |
| 182 | 335 | |
| 183 | 336 | pub const CondBr = struct { |
| 184 | 337 | pub const base_tag = Tag.condbr; |
| 185 | 338 | |
| 186 | 339 | base: Inst, |
| 187 | args: struct { | |
| 188 | condition: *Inst, | |
| 189 | true_body: Body, | |
| 190 | false_body: Body, | |
| 191 | }, | |
| 340 | condition: *Inst, | |
| 341 | then_body: Body, | |
| 342 | else_body: Body, | |
| 192 | 343 | /// Set of instructions whose lifetimes end at the start of one of the branches. |
| 193 | 344 | /// The `true` branch is first: `deaths[0..true_death_count]`. |
| 194 | 345 | /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`. |
| 195 | 346 | deaths: [*]*Inst = undefined, |
| 196 | 347 | true_death_count: u32 = 0, |
| 197 | 348 | false_death_count: u32 = 0, |
| 198 | }; | |
| 199 | 349 | |
| 200 | pub const Not = struct { | |
| 201 | pub const base_tag = Tag.not; | |
| 350 | pub fn operandCount(self: *const CondBr) usize { | |
| 351 | return 1; | |
| 352 | } | |
| 353 | pub fn getOperand(self: *const CondBr, index: usize) ?*Inst { | |
| 354 | var i = index; | |
| 202 | 355 | |
| 203 | base: Inst, | |
| 204 | args: struct { | |
| 205 | operand: *Inst, | |
| 206 | }, | |
| 356 | if (i < 1) | |
| 357 | return self.condition; | |
| 358 | i -= 1; | |
| 359 | ||
| 360 | return null; | |
| 361 | } | |
| 207 | 362 | }; |
| 208 | 363 | |
| 209 | 364 | pub const Constant = struct { |
| 210 | 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 | 367 | base: Inst, |
| 229 | args: struct { | |
| 230 | operand: *Inst, | |
| 231 | }, | |
| 232 | }; | |
| 233 | ||
| 234 | pub const PtrToInt = struct { | |
| 235 | pub const base_tag = Tag.ptrtoint; | |
| 236 | ||
| 237 | base: Inst, | |
| 238 | args: struct { | |
| 239 | ptr: *Inst, | |
| 240 | }, | |
| 241 | }; | |
| 242 | ||
| 243 | pub const Ret = struct { | |
| 244 | pub const base_tag = Tag.ret; | |
| 245 | base: Inst, | |
| 246 | args: struct { | |
| 247 | operand: *Inst, | |
| 248 | }, | |
| 249 | }; | |
| 250 | ||
| 251 | pub const RetVoid = struct { | |
| 252 | pub const base_tag = Tag.retvoid; | |
| 253 | base: Inst, | |
| 254 | args: void, | |
| 255 | }; | |
| 256 | ||
| 257 | pub const Sub = struct { | |
| 258 | pub const base_tag = Tag.sub; | |
| 259 | base: Inst, | |
| 260 | ||
| 261 | args: struct { | |
| 262 | lhs: *Inst, | |
| 263 | rhs: *Inst, | |
| 264 | }, | |
| 265 | }; | |
| 368 | val: Value, | |
| 266 | 369 | |
| 267 | pub const Unreach = struct { | |
| 268 | pub const base_tag = Tag.unreach; | |
| 269 | base: Inst, | |
| 270 | args: void, | |
| 370 | pub fn operandCount(self: *const Constant) usize { | |
| 371 | return 0; | |
| 372 | } | |
| 373 | pub fn getOperand(self: *const Constant, index: usize) ?*Inst { | |
| 374 | return null; | |
| 375 | } | |
| 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 | 25 | while (i != 0) { |
| 26 | 26 | i -= 1; |
| 27 | 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 { | |
| 33 | // Obtain the corresponding instruction type based on the tag type. | |
| 34 | inline for (std.meta.declarations(ir.Inst)) |decl| { | |
| 35 | switch (decl.data) { | |
| 36 | .Type => |T| { | |
| 37 | if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) { | |
| 38 | if (T.base_tag == base.tag) { | |
| 39 | return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base)); | |
| 40 | } | |
| 41 | } | |
| 42 | }, | |
| 43 | else => {}, | |
| 44 | } | |
| 45 | } | |
| 46 | unreachable; | |
| 47 | } | |
| 48 | ||
| 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; | |
| 32 | fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void { | |
| 33 | if (table.contains(base)) { | |
| 34 | base.deaths = 0; | |
| 52 | 35 | } else { |
| 53 | 36 | // No tombstone for this instruction means it is never referenced, |
| 54 | 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) { | |
| 59 | ir.Inst.Constant => return, | |
| 60 | ir.Inst.Block => { | |
| 61 | try analyzeWithTable(arena, table, inst.args.body); | |
| 41 | switch (base.tag) { | |
| 42 | .constant => return, | |
| 43 | .block => { | |
| 44 | const inst = base.castTag(.block).?; | |
| 45 | try analyzeWithTable(arena, table, inst.body); | |
| 62 | 46 | // We let this continue so that it can possibly mark the block as |
| 63 | 47 | // unreferenced below. |
| 64 | 48 | }, |
| 65 | ir.Inst.CondBr => { | |
| 49 | .condbr => { | |
| 50 | const inst = base.castTag(.condbr).?; | |
| 66 | 51 | var true_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator); |
| 67 | 52 | defer true_table.deinit(); |
| 68 | try true_table.ensureCapacity(inst.args.true_body.instructions.len); | |
| 69 | try analyzeWithTable(arena, &true_table, inst.args.true_body); | |
| 53 | try true_table.ensureCapacity(inst.then_body.instructions.len); | |
| 54 | try analyzeWithTable(arena, &true_table, inst.then_body); | |
| 70 | 55 | |
| 71 | 56 | var false_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator); |
| 72 | 57 | defer false_table.deinit(); |
| 73 | try false_table.ensureCapacity(inst.args.false_body.instructions.len); | |
| 74 | try analyzeWithTable(arena, &false_table, inst.args.false_body); | |
| 58 | try false_table.ensureCapacity(inst.else_body.instructions.len); | |
| 59 | try analyzeWithTable(arena, &false_table, inst.else_body); | |
| 75 | 60 | |
| 76 | 61 | // Each death that occurs inside one branch, but not the other, needs |
| 77 | 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 | 97 | // instruction, and the deaths flag for the CondBr instruction will indicate whether the |
| 113 | 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 | 100 | else => {}, |
| 135 | 101 | } |
| 136 | 102 | |
| 137 | const Args = ir.Inst.Args(T); | |
| 138 | if (Args == void) { | |
| 139 | return; | |
| 140 | } | |
| 141 | ||
| 142 | comptime var arg_index: usize = 0; | |
| 143 | inline for (std.meta.fields(Args)) |field| { | |
| 144 | if (field.field_type == *ir.Inst) { | |
| 145 | if (arg_index >= 6) { | |
| 146 | @compileError("out of bits to mark deaths of operands"); | |
| 147 | } | |
| 148 | const prev = try table.fetchPut(@field(inst.args, field.name), {}); | |
| 103 | const needed_bits = base.operandCount(); | |
| 104 | if (needed_bits <= ir.Inst.deaths_bits) { | |
| 105 | var bit_i: ir.Inst.DeathsBitIndex = 0; | |
| 106 | while (base.getOperand(bit_i)) |operand| : (bit_i += 1) { | |
| 107 | const prev = try table.fetchPut(operand, {}); | |
| 149 | 108 | if (prev == null) { |
| 150 | 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 | 337 | base: Inst, |
| 338 | 338 | |
| 339 | 339 | positionals: struct { |
| 340 | ptr: *Inst, | |
| 340 | operand: *Inst, | |
| 341 | 341 | }, |
| 342 | 342 | kw_args: struct {}, |
| 343 | 343 | }; |
| ... | ... | @@ -629,8 +629,8 @@ pub const Inst = struct { |
| 629 | 629 | |
| 630 | 630 | positionals: struct { |
| 631 | 631 | condition: *Inst, |
| 632 | true_body: Module.Body, | |
| 633 | false_body: Module.Body, | |
| 632 | then_body: Module.Body, | |
| 633 | else_body: Module.Body, | |
| 634 | 634 | }, |
| 635 | 635 | kw_args: struct {}, |
| 636 | 636 | }; |
| ... | ... | @@ -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 | 1619 | const new_inst = try self.arena.allocator.create(T); |
| 1620 | 1620 | new_inst.* = .{ |
| 1621 | 1621 | .base = .{ |
| ... | ... | @@ -1628,6 +1628,72 @@ const EmitZIR = struct { |
| 1628 | 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 | 1697 | fn emitBody( |
| 1632 | 1698 | self: *EmitZIR, |
| 1633 | 1699 | body: ir.Body, |
| ... | ... | @@ -1640,69 +1706,48 @@ const EmitZIR = struct { |
| 1640 | 1706 | }; |
| 1641 | 1707 | for (body.instructions) |inst| { |
| 1642 | 1708 | const new_inst = switch (inst.tag) { |
| 1643 | .not => blk: { | |
| 1644 | const old_inst = inst.cast(ir.Inst.Not).?; | |
| 1645 | assert(inst.ty.zigTypeTag() == .Bool); | |
| 1646 | const new_inst = try self.arena.allocator.create(Inst.BoolNot); | |
| 1647 | new_inst.* = .{ | |
| 1648 | .base = .{ | |
| 1649 | .src = inst.src, | |
| 1650 | .tag = Inst.BoolNot.base_tag, | |
| 1651 | }, | |
| 1652 | .positionals = .{ | |
| 1653 | .operand = try self.resolveInst(new_body, old_inst.args.operand), | |
| 1654 | }, | |
| 1655 | .kw_args = .{}, | |
| 1656 | }; | |
| 1657 | break :blk &new_inst.base; | |
| 1658 | }, | |
| 1659 | .add => blk: { | |
| 1660 | const old_inst = inst.cast(ir.Inst.Add).?; | |
| 1661 | const new_inst = try self.arena.allocator.create(Inst.Add); | |
| 1662 | new_inst.* = .{ | |
| 1663 | .base = .{ | |
| 1664 | .src = inst.src, | |
| 1665 | .tag = Inst.Add.base_tag, | |
| 1666 | }, | |
| 1667 | .positionals = .{ | |
| 1668 | .lhs = try self.resolveInst(new_body, old_inst.args.lhs), | |
| 1669 | .rhs = try self.resolveInst(new_body, old_inst.args.rhs), | |
| 1670 | }, | |
| 1671 | .kw_args = .{}, | |
| 1672 | }; | |
| 1673 | break :blk &new_inst.base; | |
| 1674 | }, | |
| 1675 | .sub => blk: { | |
| 1676 | const old_inst = inst.cast(ir.Inst.Sub).?; | |
| 1677 | const new_inst = try self.arena.allocator.create(Inst.Sub); | |
| 1709 | .constant => unreachable, // excluded from function bodies | |
| 1710 | ||
| 1711 | .arg => try self.emitNoOp(inst.src, Inst.Arg), | |
| 1712 | .breakpoint => try self.emitNoOp(inst.src, Inst.Breakpoint), | |
| 1713 | .unreach => try self.emitNoOp(inst.src, Inst.Unreachable), | |
| 1714 | .retvoid => try self.emitNoOp(inst.src, Inst.ReturnVoid), | |
| 1715 | ||
| 1716 | .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, Inst.BoolNot), | |
| 1717 | .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, Inst.Return), | |
| 1718 | .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, Inst.PtrToInt), | |
| 1719 | .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, Inst.IsNull), | |
| 1720 | .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, Inst.IsNonNull), | |
| 1721 | ||
| 1722 | .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, Inst.Add), | |
| 1723 | .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, Inst.Sub), | |
| 1724 | ||
| 1725 | .cmp_lt => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_lt).?, .lt), | |
| 1726 | .cmp_lte => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_lte).?, .lte), | |
| 1727 | .cmp_eq => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_eq).?, .eq), | |
| 1728 | .cmp_gte => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_gte).?, .gte), | |
| 1729 | .cmp_gt => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_gt).?, .gt), | |
| 1730 | .cmp_neq => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_neq).?, .neq), | |
| 1731 | ||
| 1732 | .bitcast => blk: { | |
| 1733 | const old_inst = inst.castTag(.bitcast).?; | |
| 1734 | const new_inst = try self.arena.allocator.create(Inst.BitCast); | |
| 1678 | 1735 | new_inst.* = .{ |
| 1679 | 1736 | .base = .{ |
| 1680 | 1737 | .src = inst.src, |
| 1681 | .tag = Inst.Sub.base_tag, | |
| 1738 | .tag = Inst.BitCast.base_tag, | |
| 1682 | 1739 | }, |
| 1683 | 1740 | .positionals = .{ |
| 1684 | .lhs = try self.resolveInst(new_body, old_inst.args.lhs), | |
| 1685 | .rhs = try self.resolveInst(new_body, old_inst.args.rhs), | |
| 1686 | }, | |
| 1687 | .kw_args = .{}, | |
| 1688 | }; | |
| 1689 | break :blk &new_inst.base; | |
| 1690 | }, | |
| 1691 | .arg => blk: { | |
| 1692 | const old_inst = inst.cast(ir.Inst.Arg).?; | |
| 1693 | const new_inst = try self.arena.allocator.create(Inst.Arg); | |
| 1694 | new_inst.* = .{ | |
| 1695 | .base = .{ | |
| 1696 | .src = inst.src, | |
| 1697 | .tag = Inst.Arg.base_tag, | |
| 1741 | .dest_type = (try self.emitType(inst.src, inst.ty)).inst, | |
| 1742 | .operand = try self.resolveInst(new_body, old_inst.operand), | |
| 1698 | 1743 | }, |
| 1699 | .positionals = .{}, | |
| 1700 | 1744 | .kw_args = .{}, |
| 1701 | 1745 | }; |
| 1702 | 1746 | break :blk &new_inst.base; |
| 1703 | 1747 | }, |
| 1748 | ||
| 1704 | 1749 | .block => blk: { |
| 1705 | const old_inst = inst.cast(ir.Inst.Block).?; | |
| 1750 | const old_inst = inst.castTag(.block).?; | |
| 1706 | 1751 | const new_inst = try self.arena.allocator.create(Inst.Block); |
| 1707 | 1752 | |
| 1708 | 1753 | try self.block_table.put(old_inst, new_inst); |
| ... | ... | @@ -1710,7 +1755,7 @@ const EmitZIR = struct { |
| 1710 | 1755 | var block_body = std.ArrayList(*Inst).init(self.allocator); |
| 1711 | 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 | 1760 | new_inst.* = .{ |
| 1716 | 1761 | .base = .{ |
| ... | ... | @@ -1725,47 +1770,49 @@ const EmitZIR = struct { |
| 1725 | 1770 | |
| 1726 | 1771 | break :blk &new_inst.base; |
| 1727 | 1772 | }, |
| 1728 | .br => blk: { | |
| 1729 | const old_inst = inst.cast(ir.Inst.Br).?; | |
| 1730 | const new_block = self.block_table.get(old_inst.args.block).?; | |
| 1731 | const new_inst = try self.arena.allocator.create(Inst.Break); | |
| 1773 | ||
| 1774 | .brvoid => blk: { | |
| 1775 | const old_inst = inst.cast(ir.Inst.BrVoid).?; | |
| 1776 | const new_block = self.block_table.get(old_inst.block).?; | |
| 1777 | const new_inst = try self.arena.allocator.create(Inst.BreakVoid); | |
| 1732 | 1778 | new_inst.* = .{ |
| 1733 | 1779 | .base = .{ |
| 1734 | 1780 | .src = inst.src, |
| 1735 | .tag = Inst.Break.base_tag, | |
| 1781 | .tag = Inst.BreakVoid.base_tag, | |
| 1736 | 1782 | }, |
| 1737 | 1783 | .positionals = .{ |
| 1738 | 1784 | .block = new_block, |
| 1739 | .operand = try self.resolveInst(new_body, old_inst.args.operand), | |
| 1740 | 1785 | }, |
| 1741 | 1786 | .kw_args = .{}, |
| 1742 | 1787 | }; |
| 1743 | 1788 | break :blk &new_inst.base; |
| 1744 | 1789 | }, |
| 1745 | .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint), | |
| 1746 | .brvoid => blk: { | |
| 1747 | const old_inst = inst.cast(ir.Inst.BrVoid).?; | |
| 1748 | const new_block = self.block_table.get(old_inst.args.block).?; | |
| 1749 | const new_inst = try self.arena.allocator.create(Inst.BreakVoid); | |
| 1790 | ||
| 1791 | .br => blk: { | |
| 1792 | const old_inst = inst.castTag(.br).?; | |
| 1793 | const new_block = self.block_table.get(old_inst.block).?; | |
| 1794 | const new_inst = try self.arena.allocator.create(Inst.Break); | |
| 1750 | 1795 | new_inst.* = .{ |
| 1751 | 1796 | .base = .{ |
| 1752 | 1797 | .src = inst.src, |
| 1753 | .tag = Inst.BreakVoid.base_tag, | |
| 1798 | .tag = Inst.Break.base_tag, | |
| 1754 | 1799 | }, |
| 1755 | 1800 | .positionals = .{ |
| 1756 | 1801 | .block = new_block, |
| 1802 | .operand = try self.resolveInst(new_body, old_inst.operand), | |
| 1757 | 1803 | }, |
| 1758 | 1804 | .kw_args = .{}, |
| 1759 | 1805 | }; |
| 1760 | 1806 | break :blk &new_inst.base; |
| 1761 | 1807 | }, |
| 1808 | ||
| 1762 | 1809 | .call => blk: { |
| 1763 | const old_inst = inst.cast(ir.Inst.Call).?; | |
| 1810 | const old_inst = inst.castTag(.call).?; | |
| 1764 | 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 | 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 | 1817 | new_inst.* = .{ |
| 1771 | 1818 | .base = .{ |
| ... | ... | @@ -1773,48 +1820,31 @@ const EmitZIR = struct { |
| 1773 | 1820 | .tag = Inst.Call.base_tag, |
| 1774 | 1821 | }, |
| 1775 | 1822 | .positionals = .{ |
| 1776 | .func = try self.resolveInst(new_body, old_inst.args.func), | |
| 1823 | .func = try self.resolveInst(new_body, old_inst.func), | |
| 1777 | 1824 | .args = args, |
| 1778 | 1825 | }, |
| 1779 | 1826 | .kw_args = .{}, |
| 1780 | 1827 | }; |
| 1781 | 1828 | break :blk &new_inst.base; |
| 1782 | 1829 | }, |
| 1783 | .unreach => try self.emitTrivial(inst.src, Inst.Unreachable), | |
| 1784 | .ret => blk: { | |
| 1785 | const old_inst = inst.cast(ir.Inst.Ret).?; | |
| 1786 | const new_inst = try self.arena.allocator.create(Inst.Return); | |
| 1787 | new_inst.* = .{ | |
| 1788 | .base = .{ | |
| 1789 | .src = inst.src, | |
| 1790 | .tag = Inst.Return.base_tag, | |
| 1791 | }, | |
| 1792 | .positionals = .{ | |
| 1793 | .operand = try self.resolveInst(new_body, old_inst.args.operand), | |
| 1794 | }, | |
| 1795 | .kw_args = .{}, | |
| 1796 | }; | |
| 1797 | break :blk &new_inst.base; | |
| 1798 | }, | |
| 1799 | .retvoid => try self.emitTrivial(inst.src, Inst.ReturnVoid), | |
| 1800 | .constant => unreachable, // excluded from function bodies | |
| 1830 | ||
| 1801 | 1831 | .assembly => blk: { |
| 1802 | const old_inst = inst.cast(ir.Inst.Assembly).?; | |
| 1832 | const old_inst = inst.castTag(.assembly).?; | |
| 1803 | 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 | 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 | 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 | 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 | 1850 | new_inst.* = .{ |
| ... | ... | @@ -1823,12 +1853,12 @@ const EmitZIR = struct { |
| 1823 | 1853 | .tag = Inst.Asm.base_tag, |
| 1824 | 1854 | }, |
| 1825 | 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 | 1857 | .return_type = (try self.emitType(inst.src, inst.ty)).inst, |
| 1828 | 1858 | }, |
| 1829 | 1859 | .kw_args = .{ |
| 1830 | .@"volatile" = old_inst.args.is_volatile, | |
| 1831 | .output = if (old_inst.args.output) |o| | |
| 1860 | .@"volatile" = old_inst.is_volatile, | |
| 1861 | .output = if (old_inst.output) |o| | |
| 1832 | 1862 | (try self.emitStringLiteral(inst.src, o)).inst |
| 1833 | 1863 | else |
| 1834 | 1864 | null, |
| ... | ... | @@ -1839,65 +1869,18 @@ const EmitZIR = struct { |
| 1839 | 1869 | }; |
| 1840 | 1870 | break :blk &new_inst.base; |
| 1841 | 1871 | }, |
| 1842 | .ptrtoint => blk: { | |
| 1843 | const old_inst = inst.cast(ir.Inst.PtrToInt).?; | |
| 1844 | const new_inst = try self.arena.allocator.create(Inst.PtrToInt); | |
| 1845 | new_inst.* = .{ | |
| 1846 | .base = .{ | |
| 1847 | .src = inst.src, | |
| 1848 | .tag = Inst.PtrToInt.base_tag, | |
| 1849 | }, | |
| 1850 | .positionals = .{ | |
| 1851 | .ptr = try self.resolveInst(new_body, old_inst.args.ptr), | |
| 1852 | }, | |
| 1853 | .kw_args = .{}, | |
| 1854 | }; | |
| 1855 | break :blk &new_inst.base; | |
| 1856 | }, | |
| 1857 | .bitcast => blk: { | |
| 1858 | const old_inst = inst.cast(ir.Inst.BitCast).?; | |
| 1859 | const new_inst = try self.arena.allocator.create(Inst.BitCast); | |
| 1860 | new_inst.* = .{ | |
| 1861 | .base = .{ | |
| 1862 | .src = inst.src, | |
| 1863 | .tag = Inst.BitCast.base_tag, | |
| 1864 | }, | |
| 1865 | .positionals = .{ | |
| 1866 | .dest_type = (try self.emitType(inst.src, inst.ty)).inst, | |
| 1867 | .operand = try self.resolveInst(new_body, old_inst.args.operand), | |
| 1868 | }, | |
| 1869 | .kw_args = .{}, | |
| 1870 | }; | |
| 1871 | break :blk &new_inst.base; | |
| 1872 | }, | |
| 1873 | .cmp => blk: { | |
| 1874 | const old_inst = inst.cast(ir.Inst.Cmp).?; | |
| 1875 | const new_inst = try self.arena.allocator.create(Inst.Cmp); | |
| 1876 | new_inst.* = .{ | |
| 1877 | .base = .{ | |
| 1878 | .src = inst.src, | |
| 1879 | .tag = Inst.Cmp.base_tag, | |
| 1880 | }, | |
| 1881 | .positionals = .{ | |
| 1882 | .lhs = try self.resolveInst(new_body, old_inst.args.lhs), | |
| 1883 | .rhs = try self.resolveInst(new_body, old_inst.args.rhs), | |
| 1884 | .op = old_inst.args.op, | |
| 1885 | }, | |
| 1886 | .kw_args = .{}, | |
| 1887 | }; | |
| 1888 | break :blk &new_inst.base; | |
| 1889 | }, | |
| 1872 | ||
| 1890 | 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); | |
| 1894 | var false_body = std.ArrayList(*Inst).init(self.allocator); | |
| 1876 | var then_body = std.ArrayList(*Inst).init(self.allocator); | |
| 1877 | var else_body = std.ArrayList(*Inst).init(self.allocator); | |
| 1895 | 1878 | |
| 1896 | defer true_body.deinit(); | |
| 1897 | defer false_body.deinit(); | |
| 1879 | defer then_body.deinit(); | |
| 1880 | defer else_body.deinit(); | |
| 1898 | 1881 | |
| 1899 | try self.emitBody(old_inst.args.true_body, inst_table, &true_body); | |
| 1900 | try self.emitBody(old_inst.args.false_body, inst_table, &false_body); | |
| 1882 | try self.emitBody(old_inst.then_body, inst_table, &then_body); | |
| 1883 | try self.emitBody(old_inst.else_body, inst_table, &else_body); | |
| 1901 | 1884 | |
| 1902 | 1885 | const new_inst = try self.arena.allocator.create(Inst.CondBr); |
| 1903 | 1886 | new_inst.* = .{ |
| ... | ... | @@ -1906,39 +1889,9 @@ const EmitZIR = struct { |
| 1906 | 1889 | .tag = Inst.CondBr.base_tag, |
| 1907 | 1890 | }, |
| 1908 | 1891 | .positionals = .{ |
| 1909 | .condition = try self.resolveInst(new_body, old_inst.args.condition), | |
| 1910 | .true_body = .{ .instructions = true_body.toOwnedSlice() }, | |
| 1911 | .false_body = .{ .instructions = false_body.toOwnedSlice() }, | |
| 1912 | }, | |
| 1913 | .kw_args = .{}, | |
| 1914 | }; | |
| 1915 | break :blk &new_inst.base; | |
| 1916 | }, | |
| 1917 | .isnull => blk: { | |
| 1918 | const old_inst = inst.cast(ir.Inst.IsNull).?; | |
| 1919 | const new_inst = try self.arena.allocator.create(Inst.IsNull); | |
| 1920 | new_inst.* = .{ | |
| 1921 | .base = .{ | |
| 1922 | .src = inst.src, | |
| 1923 | .tag = Inst.IsNull.base_tag, | |
| 1924 | }, | |
| 1925 | .positionals = .{ | |
| 1926 | .operand = try self.resolveInst(new_body, old_inst.args.operand), | |
| 1927 | }, | |
| 1928 | .kw_args = .{}, | |
| 1929 | }; | |
| 1930 | break :blk &new_inst.base; | |
| 1931 | }, | |
| 1932 | .isnonnull => blk: { | |
| 1933 | const old_inst = inst.cast(ir.Inst.IsNonNull).?; | |
| 1934 | const new_inst = try self.arena.allocator.create(Inst.IsNonNull); | |
| 1935 | new_inst.* = .{ | |
| 1936 | .base = .{ | |
| 1937 | .src = inst.src, | |
| 1938 | .tag = Inst.IsNonNull.base_tag, | |
| 1939 | }, | |
| 1940 | .positionals = .{ | |
| 1941 | .operand = try self.resolveInst(new_body, old_inst.args.operand), | |
| 1892 | .condition = try self.resolveInst(new_body, old_inst.condition), | |
| 1893 | .then_body = .{ .instructions = then_body.toOwnedSlice() }, | |
| 1894 | .else_body = .{ .instructions = else_body.toOwnedSlice() }, | |
| 1942 | 1895 | }, |
| 1943 | 1896 | .kw_args = .{}, |
| 1944 | 1897 | }; |
test/stage2/compare_output.zig+37| ... | ... | @@ -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 | } |