authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-20 13:12:25-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-20 13:12:25-07:00
log4abf119d95eeacadcaf839ed58bd4704332fcb2f
tree4b2ef95d5e1dba821b77bfdc4f5e25db127249cc
parent596ca6cf70cf43c27e31bbcfc36bcdc70b13897a
parentef91b11295a549a8173c488d9fd5b3f69b419829

Merge branch 'register-allocation'


10 files changed, 1819 insertions(+), 1505 deletions(-)

src-self-hosted/Module.zig+162-61
...@@ -1349,8 +1349,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1349,8 +1349,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1349fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {1349fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
1350 try self.analyzeBody(&block_scope.base, body);1350 try self.analyzeBody(&block_scope.base, body);
1351 for (block_scope.instructions.items) |inst| {1351 for (block_scope.instructions.items) |inst| {
1352 if (inst.cast(Inst.Ret)) |ret| {1352 if (inst.castTag(.ret)) |ret| {
1353 const val = try self.resolveConstValue(&block_scope.base, ret.args.operand);1353 const val = try self.resolveConstValue(&block_scope.base, ret.operand);
1354 return val.toType();1354 return val.toType();
1355 } else {1355 } else {
1356 return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});1356 return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
...@@ -1938,16 +1938,132 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const...@@ -1938,16 +1938,132 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
1938 };1938 };
1939}1939}
19401940
1941fn addNewInstArgs(1941fn addNoOp(
1942 self: *Module,1942 self: *Module,
1943 block: *Scope.Block,1943 block: *Scope.Block,
1944 src: usize,1944 src: usize,
1945 ty: Type,1945 ty: Type,
1946 comptime T: type,1946 comptime tag: Inst.Tag,
1947 args: Inst.Args(T),1947) !*Inst {
1948 const inst = try block.arena.create(tag.Type());
1949 inst.* = .{
1950 .base = .{
1951 .tag = tag,
1952 .ty = ty,
1953 .src = src,
1954 },
1955 };
1956 try block.instructions.append(self.gpa, &inst.base);
1957 return &inst.base;
1958}
1959
1960fn addUnOp(
1961 self: *Module,
1962 block: *Scope.Block,
1963 src: usize,
1964 ty: Type,
1965 tag: Inst.Tag,
1966 operand: *Inst,
1967) !*Inst {
1968 const inst = try block.arena.create(Inst.UnOp);
1969 inst.* = .{
1970 .base = .{
1971 .tag = tag,
1972 .ty = ty,
1973 .src = src,
1974 },
1975 .operand = operand,
1976 };
1977 try block.instructions.append(self.gpa, &inst.base);
1978 return &inst.base;
1979}
1980
1981fn addBinOp(
1982 self: *Module,
1983 block: *Scope.Block,
1984 src: usize,
1985 ty: Type,
1986 tag: Inst.Tag,
1987 lhs: *Inst,
1988 rhs: *Inst,
1989) !*Inst {
1990 const inst = try block.arena.create(Inst.BinOp);
1991 inst.* = .{
1992 .base = .{
1993 .tag = tag,
1994 .ty = ty,
1995 .src = src,
1996 },
1997 .lhs = lhs,
1998 .rhs = rhs,
1999 };
2000 try block.instructions.append(self.gpa, &inst.base);
2001 return &inst.base;
2002}
2003
2004fn addBr(
2005 self: *Module,
2006 scope_block: *Scope.Block,
2007 src: usize,
2008 target_block: *Inst.Block,
2009 operand: *Inst,
2010) !*Inst {
2011 const inst = try scope_block.arena.create(Inst.Br);
2012 inst.* = .{
2013 .base = .{
2014 .tag = .br,
2015 .ty = Type.initTag(.noreturn),
2016 .src = src,
2017 },
2018 .operand = operand,
2019 .block = target_block,
2020 };
2021 try scope_block.instructions.append(self.gpa, &inst.base);
2022 return &inst.base;
2023}
2024
2025fn addCondBr(
2026 self: *Module,
2027 block: *Scope.Block,
2028 src: usize,
2029 condition: *Inst,
2030 then_body: ir.Body,
2031 else_body: ir.Body,
1948) !*Inst {2032) !*Inst {
1949 const inst = try self.addNewInst(block, src, ty, T);2033 const inst = try block.arena.create(Inst.CondBr);
1950 inst.args = args;2034 inst.* = .{
2035 .base = .{
2036 .tag = .condbr,
2037 .ty = Type.initTag(.noreturn),
2038 .src = src,
2039 },
2040 .condition = condition,
2041 .then_body = then_body,
2042 .else_body = else_body,
2043 };
2044 try block.instructions.append(self.gpa, &inst.base);
2045 return &inst.base;
2046}
2047
2048fn addCall(
2049 self: *Module,
2050 block: *Scope.Block,
2051 src: usize,
2052 ty: Type,
2053 func: *Inst,
2054 args: []const *Inst,
2055) !*Inst {
2056 const inst = try block.arena.create(Inst.Call);
2057 inst.* = .{
2058 .base = .{
2059 .tag = .call,
2060 .ty = ty,
2061 .src = src,
2062 },
2063 .func = func,
2064 .args = args,
2065 };
2066 try block.instructions.append(self.gpa, &inst.base);
1951 return &inst.base;2067 return &inst.base;
1952}2068}
19532069
...@@ -2017,7 +2133,6 @@ fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime...@@ -2017,7 +2133,6 @@ fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime
2017 .ty = ty,2133 .ty = ty,
2018 .src = src,2134 .src = src,
2019 },2135 },
2020 .args = undefined,
2021 };2136 };
2022 try block.instructions.append(self.gpa, &inst.base);2137 try block.instructions.append(self.gpa, &inst.base);
2023 return inst;2138 return inst;
...@@ -2269,7 +2384,7 @@ fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!...@@ -2269,7 +2384,7 @@ fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!
2269 });2384 });
2270 }2385 }
2271 const param_type = fn_ty.fnParamType(param_index);2386 const param_type = fn_ty.fnParamType(param_index);
2272 return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, {});2387 return self.addNoOp(b, inst.base.src, param_type, .arg);
2273}2388}
22742389
2275fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {2390fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
...@@ -2285,7 +2400,7 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr...@@ -2285,7 +2400,7 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr
2285 .ty = undefined, // Set after analysis.2400 .ty = undefined, // Set after analysis.
2286 .src = inst.base.src,2401 .src = inst.base.src,
2287 },2402 },
2288 .args = undefined,2403 .body = undefined,
2289 };2404 };
22902405
2291 var child_block: Scope.Block = .{2406 var child_block: Scope.Block = .{
...@@ -2316,13 +2431,13 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr...@@ -2316,13 +2431,13 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr
2316 // to emit a jump instruction to after the block when it encounters the break.2431 // to emit a jump instruction to after the block when it encounters the break.
2317 try parent_block.instructions.append(self.gpa, &block_inst.base);2432 try parent_block.instructions.append(self.gpa, &block_inst.base);
2318 block_inst.base.ty = try self.resolvePeerTypes(scope, label.results.items);2433 block_inst.base.ty = try self.resolvePeerTypes(scope, label.results.items);
2319 block_inst.args.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };2434 block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
2320 return &block_inst.base;2435 return &block_inst.base;
2321}2436}
23222437
2323fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {2438fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
2324 const b = try self.requireRuntimeBlock(scope, inst.base.src);2439 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2325 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, {});2440 return self.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint);
2326}2441}
23272442
2328fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {2443fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
...@@ -2350,10 +2465,7 @@ fn analyzeBreak(...@@ -2350,10 +2465,7 @@ fn analyzeBreak(
2350 if (label.zir_block == zir_block) {2465 if (label.zir_block == zir_block) {
2351 try label.results.append(self.gpa, operand);2466 try label.results.append(self.gpa, operand);
2352 const b = try self.requireRuntimeBlock(scope, src);2467 const b = try self.requireRuntimeBlock(scope, src);
2353 return self.addNewInstArgs(b, src, Type.initTag(.noreturn), Inst.Br, .{2468 return self.addBr(b, src, label.block_inst, operand);
2354 .block = label.block_inst,
2355 .operand = operand,
2356 });
2357 }2469 }
2358 }2470 }
2359 opt_block = block.parent;2471 opt_block = block.parent;
...@@ -2484,10 +2596,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro...@@ -2484,10 +2596,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
2484 }2596 }
24852597
2486 const b = try self.requireRuntimeBlock(scope, inst.base.src);2598 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2487 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, .{2599 return self.addCall(b, inst.base.src, Type.initTag(.void), func, casted_args);
2488 .func = func,
2489 .args = casted_args,
2490 });
2491}2600}
24922601
2493fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {2602fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
...@@ -2570,14 +2679,14 @@ fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Ins...@@ -2570,14 +2679,14 @@ fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Ins
2570}2679}
25712680
2572fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToInt) InnerError!*Inst {2681fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToInt) InnerError!*Inst {
2573 const ptr = try self.resolveInst(scope, ptrtoint.positionals.ptr);2682 const ptr = try self.resolveInst(scope, ptrtoint.positionals.operand);
2574 if (ptr.ty.zigTypeTag() != .Pointer) {2683 if (ptr.ty.zigTypeTag() != .Pointer) {
2575 return self.fail(scope, ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});2684 return self.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty});
2576 }2685 }
2577 // TODO handle known-pointer-address2686 // TODO handle known-pointer-address
2578 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);2687 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
2579 const ty = Type.initTag(.usize);2688 const ty = Type.initTag(.usize);
2580 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, .{ .ptr = ptr });2689 return self.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr);
2581}2690}
25822691
2583fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {2692fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
...@@ -2734,10 +2843,7 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!...@@ -2734,10 +2843,7 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!
2734 }2843 }
27352844
2736 const b = try self.requireRuntimeBlock(scope, inst.base.src);2845 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2737 return self.addNewInstArgs(b, inst.base.src, lhs.ty, Inst.Add, .{2846 return self.addBinOp(b, inst.base.src, lhs.ty, .add, lhs, rhs);
2738 .lhs = lhs,
2739 .rhs = rhs,
2740 });
2741 }2847 }
2742 return self.fail(scope, inst.base.src, "TODO analyze add for {} + {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() });2848 return self.fail(scope, inst.base.src, "TODO analyze add for {} + {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() });
2743}2849}
...@@ -2783,14 +2889,22 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr...@@ -2783,14 +2889,22 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr
2783 }2889 }
27842890
2785 const b = try self.requireRuntimeBlock(scope, assembly.base.src);2891 const b = try self.requireRuntimeBlock(scope, assembly.base.src);
2786 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, .{2892 const inst = try b.arena.create(Inst.Assembly);
2893 inst.* = .{
2894 .base = .{
2895 .tag = .assembly,
2896 .ty = return_type,
2897 .src = assembly.base.src,
2898 },
2787 .asm_source = asm_source,2899 .asm_source = asm_source,
2788 .is_volatile = assembly.kw_args.@"volatile",2900 .is_volatile = assembly.kw_args.@"volatile",
2789 .output = output,2901 .output = output,
2790 .inputs = inputs,2902 .inputs = inputs,
2791 .clobbers = clobbers,2903 .clobbers = clobbers,
2792 .args = args,2904 .args = args,
2793 });2905 };
2906 try b.instructions.append(self.gpa, &inst.base);
2907 return &inst.base;
2794}2908}
27952909
2796fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!*Inst {2910fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!*Inst {
...@@ -2818,15 +2932,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!...@@ -2818,15 +2932,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
2818 return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);2932 return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);
2819 }2933 }
2820 const b = try self.requireRuntimeBlock(scope, inst.base.src);2934 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2821 switch (op) {2935 const inst_tag: Inst.Tag = switch (op) {
2822 .eq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNull, .{2936 .eq => .isnull,
2823 .operand = opt_operand,2937 .neq => .isnonnull,
2824 }),
2825 .neq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNonNull, .{
2826 .operand = opt_operand,
2827 }),
2828 else => unreachable,2938 else => unreachable,
2829 }2939 };
2940 return self.addUnOp(b, inst.base.src, Type.initTag(.bool), inst_tag, opt_operand);
2830 } else if (is_equality_cmp and2941 } else if (is_equality_cmp and
2831 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))2942 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
2832 {2943 {
...@@ -2861,7 +2972,7 @@ fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.BoolNot) Inn...@@ -2861,7 +2972,7 @@ fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.BoolNot) Inn
2861 return self.constBool(scope, inst.base.src, !val.toBool());2972 return self.constBool(scope, inst.base.src, !val.toBool());
2862 }2973 }
2863 const b = try self.requireRuntimeBlock(scope, inst.base.src);2974 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2864 return self.addNewInstArgs(b, inst.base.src, bool_type, Inst.Not, .{ .operand = operand });2975 return self.addUnOp(b, inst.base.src, bool_type, .not, operand);
2865}2976}
28662977
2867fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {2978fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {
...@@ -2879,7 +2990,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner...@@ -2879,7 +2990,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
2879 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);2990 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);
28802991
2881 if (try self.resolveDefinedValue(scope, cond)) |cond_val| {2992 if (try self.resolveDefinedValue(scope, cond)) |cond_val| {
2882 const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body;2993 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
2883 try self.analyzeBody(scope, body.*);2994 try self.analyzeBody(scope, body.*);
2884 return self.constVoid(scope, inst.base.src);2995 return self.constVoid(scope, inst.base.src);
2885 }2996 }
...@@ -2894,7 +3005,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner...@@ -2894,7 +3005,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
2894 .arena = parent_block.arena,3005 .arena = parent_block.arena,
2895 };3006 };
2896 defer true_block.instructions.deinit(self.gpa);3007 defer true_block.instructions.deinit(self.gpa);
2897 try self.analyzeBody(&true_block.base, inst.positionals.true_body);3008 try self.analyzeBody(&true_block.base, inst.positionals.then_body);
28983009
2899 var false_block: Scope.Block = .{3010 var false_block: Scope.Block = .{
2900 .parent = parent_block,3011 .parent = parent_block,
...@@ -2904,13 +3015,11 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner...@@ -2904,13 +3015,11 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
2904 .arena = parent_block.arena,3015 .arena = parent_block.arena,
2905 };3016 };
2906 defer false_block.instructions.deinit(self.gpa);3017 defer false_block.instructions.deinit(self.gpa);
2907 try self.analyzeBody(&false_block.base, inst.positionals.false_body);3018 try self.analyzeBody(&false_block.base, inst.positionals.else_body);
29083019
2909 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.noreturn), Inst.CondBr, Inst.Args(Inst.CondBr){3020 const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
2910 .condition = cond,3021 const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
2911 .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },3022 return self.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
2912 .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },
2913 });
2914}3023}
29153024
2916fn wantSafety(self: *Module, scope: *Scope) bool {3025fn wantSafety(self: *Module, scope: *Scope) bool {
...@@ -2926,20 +3035,20 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea...@@ -2926,20 +3035,20 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea
2926 const b = try self.requireRuntimeBlock(scope, unreach.base.src);3035 const b = try self.requireRuntimeBlock(scope, unreach.base.src);
2927 if (self.wantSafety(scope)) {3036 if (self.wantSafety(scope)) {
2928 // TODO Once we have a panic function to call, call it here instead of this.3037 // TODO Once we have a panic function to call, call it here instead of this.
2929 _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {});3038 _ = try self.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);
2930 }3039 }
2931 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});3040 return self.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
2932}3041}
29333042
2934fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {3043fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {
2935 const operand = try self.resolveInst(scope, inst.positionals.operand);3044 const operand = try self.resolveInst(scope, inst.positionals.operand);
2936 const b = try self.requireRuntimeBlock(scope, inst.base.src);3045 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2937 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, .{ .operand = operand });3046 return self.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
2938}3047}
29393048
2940fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst {3049fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst {
2941 const b = try self.requireRuntimeBlock(scope, inst.base.src);3050 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2942 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.RetVoid, {});3051 return self.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
2943}3052}
29443053
2945fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {3054fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {
...@@ -3027,11 +3136,7 @@ fn cmpNumeric(...@@ -3027,11 +3136,7 @@ fn cmpNumeric(
3027 };3136 };
3028 const casted_lhs = try self.coerce(scope, dest_type, lhs);3137 const casted_lhs = try self.coerce(scope, dest_type, lhs);
3029 const casted_rhs = try self.coerce(scope, dest_type, rhs);3138 const casted_rhs = try self.coerce(scope, dest_type, rhs);
3030 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{3139 return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
3031 .lhs = casted_lhs,
3032 .rhs = casted_rhs,
3033 .op = op,
3034 });
3035 }3140 }
3036 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.3141 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
3037 // For mixed signed and unsigned integers, implicit cast both operands to a signed3142 // For mixed signed and unsigned integers, implicit cast both operands to a signed
...@@ -3131,11 +3236,7 @@ fn cmpNumeric(...@@ -3131,11 +3236,7 @@ fn cmpNumeric(
3131 const casted_lhs = try self.coerce(scope, dest_type, lhs);3236 const casted_lhs = try self.coerce(scope, dest_type, lhs);
3132 const casted_rhs = try self.coerce(scope, dest_type, rhs);3237 const casted_rhs = try self.coerce(scope, dest_type, rhs);
31333238
3134 return self.addNewInstArgs(b, src, Type.initTag(.bool), Inst.Cmp, .{3239 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
3135 .lhs = casted_lhs,
3136 .rhs = casted_rhs,
3137 .op = op,
3138 });
3139}3240}
31403241
3141fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {3242fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
...@@ -3236,7 +3337,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {...@@ -3236,7 +3337,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3236 }3337 }
3237 // TODO validate the type size and other compile errors3338 // TODO validate the type size and other compile errors
3238 const b = try self.requireRuntimeBlock(scope, inst.src);3339 const b = try self.requireRuntimeBlock(scope, inst.src);
3239 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, .{ .operand = inst });3340 return self.addUnOp(b, inst.src, dest_type, .bitcast, inst);
3240}3341}
32413342
3242fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {3343fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
src-self-hosted/astgen.zig+4-4
...@@ -173,8 +173,8 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In...@@ -173,8 +173,8 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
173 const if_src = tree.token_locs[if_node.if_token].start;173 const if_src = tree.token_locs[if_node.if_token].start;
174 const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{174 const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
175 .condition = cond,175 .condition = cond,
176 .true_body = undefined, // populated below176 .then_body = undefined, // populated below
177 .false_body = undefined, // populated below177 .else_body = undefined, // populated below
178 }, .{});178 }, .{});
179179
180 const block = try mod.addZIRInstBlock(scope, if_src, .{180 const block = try mod.addZIRInstBlock(scope, if_src, .{
...@@ -196,7 +196,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In...@@ -196,7 +196,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
196 .operand = then_result,196 .operand = then_result,
197 }, .{});197 }, .{});
198 }198 }
199 condbr.positionals.true_body = .{199 condbr.positionals.then_body = .{
200 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),200 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
201 };201 };
202202
...@@ -225,7 +225,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In...@@ -225,7 +225,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
225 .block = block,225 .block = block,
226 }, .{});226 }, .{});
227 }227 }
228 condbr.positionals.false_body = .{228 condbr.positionals.else_body = .{
229 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),229 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
230 };230 };
231231
src-self-hosted/codegen.zig+1124-1040
...@@ -32,67 +32,75 @@ pub const Result = union(enum) {...@@ -32,67 +32,75 @@ pub const Result = union(enum) {
32 fail: *Module.ErrorMsg,32 fail: *Module.ErrorMsg,
33};33};
3434
35pub const GenerateSymbolError = error{
36 OutOfMemory,
37 /// A Decl that this symbol depends on had a semantic analysis failure.
38 AnalysisFail,
39};
40
35pub fn generateSymbol(41pub fn generateSymbol(
36 bin_file: *link.File.Elf,42 bin_file: *link.File.Elf,
37 src: usize,43 src: usize,
38 typed_value: TypedValue,44 typed_value: TypedValue,
39 code: *std.ArrayList(u8),45 code: *std.ArrayList(u8),
40) error{46) GenerateSymbolError!Result {
41 OutOfMemory,
42 /// A Decl that this symbol depends on had a semantic analysis failure.
43 AnalysisFail,
44}!Result {
45 const tracy = trace(@src());47 const tracy = trace(@src());
46 defer tracy.end();48 defer tracy.end();
4749
48 switch (typed_value.ty.zigTypeTag()) {50 switch (typed_value.ty.zigTypeTag()) {
49 .Fn => {51 .Fn => {
50 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;52 switch (bin_file.options.target.cpu.arch) {
5153 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),
52 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;54 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),
53 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());55 .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),
54 defer bin_file.allocator.free(param_types);56 .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code),
55 fn_type.fnParamTypes(param_types);57 .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code),
56 var mc_args = try bin_file.allocator.alloc(MCValue, param_types.len);58 .arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code),
57 defer bin_file.allocator.free(mc_args);59 .avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code),
5860 .bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code),
59 var branch_stack = std.ArrayList(Function.Branch).init(bin_file.allocator);61 .bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code),
60 defer {62 .hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code),
61 assert(branch_stack.items.len == 1);63 .mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code),
62 branch_stack.items[0].deinit(bin_file.allocator);64 .mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code),
63 branch_stack.deinit();65 .mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code),
64 }66 .mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code),
65 const branch = try branch_stack.addOne();67 .msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code),
66 branch.* = .{};68 .powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code),
6769 .powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code),
68 var function = Function{70 .powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code),
69 .gpa = bin_file.allocator,71 .r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code),
70 .target = &bin_file.options.target,72 .amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code),
71 .bin_file = bin_file,73 .riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code),
72 .mod_fn = module_fn,74 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code),
73 .code = code,75 .sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code),
74 .err_msg = null,76 .sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code),
75 .args = mc_args,77 .sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code),
76 .arg_index = 0,78 .s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code),
77 .branch_stack = &branch_stack,79 .tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code),
78 .src = src,80 .tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code),
79 };81 .thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code),
8082 .thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code),
81 const cc = fn_type.fnCallingConvention();83 .i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code),
82 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {84 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code),
83 error.CodegenFail => return Result{ .fail = function.err_msg.? },85 .xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code),
84 else => |e| return e,86 .nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code),
85 };87 .nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code),
8688 .le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code),
87 function.gen() catch |err| switch (err) {89 .le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code),
88 error.CodegenFail => return Result{ .fail = function.err_msg.? },90 .amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code),
89 else => |e| return e,91 .amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code),
90 };92 .hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code),
9193 .hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code),
92 if (function.err_msg) |em| {94 .spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code),
93 return Result{ .fail = em };95 .spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code),
94 } else {96 .kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code),
95 return Result{ .appended = {} };97 .shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code),
98 .lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code),
99 .wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code),
100 .wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code),
101 .renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code),
102 .renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code),
103 .ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code),
96 }104 }
97 },105 },
98 .Array => {106 .Array => {
...@@ -189,1101 +197,1177 @@ const InnerError = error{...@@ -189,1101 +197,1177 @@ const InnerError = error{
189 CodegenFail,197 CodegenFail,
190};198};
191199
192const MCValue = union(enum) {200fn Function(comptime arch: std.Target.Cpu.Arch) type {
193 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.201 return struct {
194 none,202 gpa: *Allocator,
195 /// Control flow will not allow this value to be observed.203 bin_file: *link.File.Elf,
196 unreach,204 target: *const std.Target,
197 /// No more references to this value remain.205 mod_fn: *const Module.Fn,
198 dead,206 code: *std.ArrayList(u8),
199 /// A pointer-sized integer that fits in a register.207 err_msg: ?*ErrorMsg,
200 immediate: u64,208 args: []MCValue,
201 /// The constant was emitted into the code, at this offset.209 arg_index: usize,
202 embedded_in_code: usize,210 src: usize,
203 /// The value is in a target-specific register. The value can211
204 /// be @intToEnum casted to the respective Reg enum.212 /// Whenever there is a runtime branch, we push a Branch onto this stack,
205 register: usize,213 /// and pop it off when the runtime branch joins. This provides an "overlay"
206 /// The value is in memory at a hard-coded address.214 /// of the table of mappings from instructions to `MCValue` from within the branch.
207 memory: u64,215 /// This way we can modify the `MCValue` for an instruction in different ways
208 /// The value is one of the stack variables.216 /// within different branches. Special consideration is needed when a branch
209 stack_offset: u64,217 /// joins with its parent, to make sure all instructions have the same MCValue
210 /// The value is in the compare flags assuming an unsigned operation,218 /// across each runtime branch upon joining.
211 /// with this operator applied on top of it.219 branch_stack: *std.ArrayList(Branch),
212 compare_flags_unsigned: std.math.CompareOperator,220
213 /// The value is in the compare flags assuming a signed operation,221 const MCValue = union(enum) {
214 /// with this operator applied on top of it.222 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
215 compare_flags_signed: std.math.CompareOperator,223 none,
216224 /// Control flow will not allow this value to be observed.
217 fn isMemory(mcv: MCValue) bool {225 unreach,
218 return switch (mcv) {226 /// No more references to this value remain.
219 .embedded_in_code, .memory, .stack_offset => true,227 dead,
220 else => false,228 /// A pointer-sized integer that fits in a register.
229 immediate: u64,
230 /// The constant was emitted into the code, at this offset.
231 embedded_in_code: usize,
232 /// The value is in a target-specific register.
233 register: Register,
234 /// The value is in memory at a hard-coded address.
235 memory: u64,
236 /// The value is one of the stack variables.
237 stack_offset: u64,
238 /// The value is in the compare flags assuming an unsigned operation,
239 /// with this operator applied on top of it.
240 compare_flags_unsigned: std.math.CompareOperator,
241 /// The value is in the compare flags assuming a signed operation,
242 /// with this operator applied on top of it.
243 compare_flags_signed: std.math.CompareOperator,
244
245 fn isMemory(mcv: MCValue) bool {
246 return switch (mcv) {
247 .embedded_in_code, .memory, .stack_offset => true,
248 else => false,
249 };
250 }
251
252 fn isImmediate(mcv: MCValue) bool {
253 return switch (mcv) {
254 .immediate => true,
255 else => false,
256 };
257 }
258
259 fn isMutable(mcv: MCValue) bool {
260 return switch (mcv) {
261 .none => unreachable,
262 .unreach => unreachable,
263 .dead => unreachable,
264
265 .immediate,
266 .embedded_in_code,
267 .memory,
268 .compare_flags_unsigned,
269 .compare_flags_signed,
270 => false,
271
272 .register,
273 .stack_offset,
274 => true,
275 };
276 }
221 };277 };
222 }
223278
224 fn isImmediate(mcv: MCValue) bool {279 const Branch = struct {
225 return switch (mcv) {280 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
226 .immediate => true,281 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},
227 else => false,282 free_registers: FreeRegInt = std.math.maxInt(FreeRegInt),
283
284 /// Maps offset to what is stored there.
285 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},
286 /// Offset from the stack base, representing the end of the stack frame.
287 max_end_stack: u32 = 0,
288 /// Represents the current end stack offset. If there is no existing slot
289 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
290 next_stack_offset: u32 = 0,
291
292 fn markRegUsed(self: *Branch, reg: Register) void {
293 if (FreeRegInt == u0) return;
294 const index = reg.allocIndex() orelse return;
295 const ShiftInt = std.math.Log2Int(FreeRegInt);
296 const shift = @intCast(ShiftInt, index);
297 self.free_registers &= ~(@as(FreeRegInt, 1) << shift);
298 }
299
300 fn markRegFree(self: *Branch, reg: Register) void {
301 if (FreeRegInt == u0) return;
302 const index = reg.allocIndex() orelse return;
303 const ShiftInt = std.math.Log2Int(FreeRegInt);
304 const shift = @intCast(ShiftInt, index);
305 self.free_registers |= @as(FreeRegInt, 1) << shift;
306 }
307
308 fn deinit(self: *Branch, gpa: *Allocator) void {
309 self.inst_table.deinit(gpa);
310 self.registers.deinit(gpa);
311 self.stack.deinit(gpa);
312 self.* = undefined;
313 }
228 };314 };
229 }
230315
231 fn isMutable(mcv: MCValue) bool {316 const RegisterAllocation = struct {
232 return switch (mcv) {317 inst: *ir.Inst,
233 .none => unreachable,
234 .unreach => unreachable,
235 .dead => unreachable,
236
237 .immediate,
238 .embedded_in_code,
239 .memory,
240 .compare_flags_unsigned,
241 .compare_flags_signed,
242 => false,
243
244 .register,
245 .stack_offset,
246 => true,
247 };318 };
248 }
249};
250319
251const Function = struct {320 const StackAllocation = struct {
252 gpa: *Allocator,321 inst: *ir.Inst,
253 bin_file: *link.File.Elf,322 size: u32,
254 target: *const std.Target,323 };
255 mod_fn: *const Module.Fn,
256 code: *std.ArrayList(u8),
257 err_msg: ?*ErrorMsg,
258 args: []MCValue,
259 arg_index: usize,
260 src: usize,
261324
262 /// Whenever there is a runtime branch, we push a Branch onto this stack,325 const Self = @This();
263 /// and pop it off when the runtime branch joins. This provides an "overlay"
264 /// of the table of mappings from instructions to `MCValue` from within the branch.
265 /// This way we can modify the `MCValue` for an instruction in different ways
266 /// within different branches. Special consideration is needed when a branch
267 /// joins with its parent, to make sure all instructions have the same MCValue
268 /// across each runtime branch upon joining.
269 branch_stack: *std.ArrayList(Branch),
270
271 const Branch = struct {
272 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
273
274 /// The key is an enum value of an arch-specific register.
275 registers: std.AutoHashMapUnmanaged(usize, RegisterAllocation) = .{},
276
277 /// Maps offset to what is stored there.
278 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},
279 /// Offset from the stack base, representing the end of the stack frame.
280 max_end_stack: u32 = 0,
281 /// Represents the current end stack offset. If there is no existing slot
282 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
283 next_stack_offset: u32 = 0,
284
285 fn deinit(self: *Branch, gpa: *Allocator) void {
286 self.inst_table.deinit(gpa);
287 self.registers.deinit(gpa);
288 self.stack.deinit(gpa);
289 self.* = undefined;
290 }
291 };
292326
293 const RegisterAllocation = struct {327 fn generateSymbol(
294 inst: *ir.Inst,328 bin_file: *link.File.Elf,
295 };329 src: usize,
330 typed_value: TypedValue,
331 code: *std.ArrayList(u8),
332 ) GenerateSymbolError!Result {
333 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
296334
297 const StackAllocation = struct {335 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
298 inst: *ir.Inst,336 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
299 size: u32,337 defer bin_file.allocator.free(param_types);
300 };338 fn_type.fnParamTypes(param_types);
339 var mc_args = try bin_file.allocator.alloc(MCValue, param_types.len);
340 defer bin_file.allocator.free(mc_args);
301341
302 fn gen(self: *Function) !void {342 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
303 switch (self.target.cpu.arch) {343 defer {
304 .arm => return self.genArch(.arm),344 assert(branch_stack.items.len == 1);
305 .armeb => return self.genArch(.armeb),345 branch_stack.items[0].deinit(bin_file.allocator);
306 .aarch64 => return self.genArch(.aarch64),346 branch_stack.deinit();
307 .aarch64_be => return self.genArch(.aarch64_be),347 }
308 .aarch64_32 => return self.genArch(.aarch64_32),348 const branch = try branch_stack.addOne();
309 .arc => return self.genArch(.arc),349 branch.* = .{};
310 .avr => return self.genArch(.avr),350
311 .bpfel => return self.genArch(.bpfel),351 var function = Self{
312 .bpfeb => return self.genArch(.bpfeb),352 .gpa = bin_file.allocator,
313 .hexagon => return self.genArch(.hexagon),353 .target = &bin_file.options.target,
314 .mips => return self.genArch(.mips),354 .bin_file = bin_file,
315 .mipsel => return self.genArch(.mipsel),355 .mod_fn = module_fn,
316 .mips64 => return self.genArch(.mips64),356 .code = code,
317 .mips64el => return self.genArch(.mips64el),357 .err_msg = null,
318 .msp430 => return self.genArch(.msp430),358 .args = mc_args,
319 .powerpc => return self.genArch(.powerpc),359 .arg_index = 0,
320 .powerpc64 => return self.genArch(.powerpc64),360 .branch_stack = &branch_stack,
321 .powerpc64le => return self.genArch(.powerpc64le),361 .src = src,
322 .r600 => return self.genArch(.r600),362 };
323 .amdgcn => return self.genArch(.amdgcn),363
324 .riscv32 => return self.genArch(.riscv32),364 const cc = fn_type.fnCallingConvention();
325 .riscv64 => return self.genArch(.riscv64),365 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
326 .sparc => return self.genArch(.sparc),366 error.CodegenFail => return Result{ .fail = function.err_msg.? },
327 .sparcv9 => return self.genArch(.sparcv9),367 else => |e| return e,
328 .sparcel => return self.genArch(.sparcel),368 };
329 .s390x => return self.genArch(.s390x),
330 .tce => return self.genArch(.tce),
331 .tcele => return self.genArch(.tcele),
332 .thumb => return self.genArch(.thumb),
333 .thumbeb => return self.genArch(.thumbeb),
334 .i386 => return self.genArch(.i386),
335 .x86_64 => return self.genArch(.x86_64),
336 .xcore => return self.genArch(.xcore),
337 .nvptx => return self.genArch(.nvptx),
338 .nvptx64 => return self.genArch(.nvptx64),
339 .le32 => return self.genArch(.le32),
340 .le64 => return self.genArch(.le64),
341 .amdil => return self.genArch(.amdil),
342 .amdil64 => return self.genArch(.amdil64),
343 .hsail => return self.genArch(.hsail),
344 .hsail64 => return self.genArch(.hsail64),
345 .spir => return self.genArch(.spir),
346 .spir64 => return self.genArch(.spir64),
347 .kalimba => return self.genArch(.kalimba),
348 .shave => return self.genArch(.shave),
349 .lanai => return self.genArch(.lanai),
350 .wasm32 => return self.genArch(.wasm32),
351 .wasm64 => return self.genArch(.wasm64),
352 .renderscript32 => return self.genArch(.renderscript32),
353 .renderscript64 => return self.genArch(.renderscript64),
354 .ve => return self.genArch(.ve),
355 }
356 }
357369
358 fn genArch(self: *Function, comptime arch: std.Target.Cpu.Arch) !void {370 function.gen() catch |err| switch (err) {
359 try self.code.ensureCapacity(self.code.items.len + 11);371 error.CodegenFail => return Result{ .fail = function.err_msg.? },
360372 else => |e| return e,
361 // push rbp373 };
362 // mov rbp, rsp374
363 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });375 if (function.err_msg) |em| {
364376 return Result{ .fail = em };
365 // sub rsp, x377 } else {
366 const stack_end = self.branch_stack.items[0].max_end_stack;378 return Result{ .appended = {} };
367 if (stack_end > std.math.maxInt(i32)) {379 }
368 return self.fail(self.src, "too much stack used in call parameters", .{});
369 } else if (stack_end > std.math.maxInt(i8)) {
370 // 48 83 ec xx sub rsp,0x10
371 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec });
372 const x = @intCast(u32, stack_end);
373 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
374 } else if (stack_end != 0) {
375 // 48 81 ec xx xx xx xx sub rsp,0x80
376 const x = @intCast(u8, stack_end);
377 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x });
378 }380 }
379381
380 try self.genBody(self.mod_fn.analysis.success, arch);382 fn gen(self: *Self) !void {
381 }383 try self.code.ensureCapacity(self.code.items.len + 11);
384
385 // push rbp
386 // mov rbp, rsp
387 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });
388
389 // sub rsp, x
390 const stack_end = self.branch_stack.items[0].max_end_stack;
391 if (stack_end > std.math.maxInt(i32)) {
392 return self.fail(self.src, "too much stack used in call parameters", .{});
393 } else if (stack_end > std.math.maxInt(i8)) {
394 // 48 83 ec xx sub rsp,0x10
395 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec });
396 const x = @intCast(u32, stack_end);
397 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
398 } else if (stack_end != 0) {
399 // 48 81 ec xx xx xx xx sub rsp,0x80
400 const x = @intCast(u8, stack_end);
401 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x });
402 }
382403
383 fn genBody(self: *Function, body: ir.Body, comptime arch: std.Target.Cpu.Arch) InnerError!void {404 try self.genBody(self.mod_fn.analysis.success);
384 const inst_table = &self.branch_stack.items[0].inst_table;
385 for (body.instructions) |inst| {
386 const new_inst = try self.genFuncInst(inst, arch);
387 try inst_table.putNoClobber(self.gpa, inst, new_inst);
388 }405 }
389 }
390406
391 fn genFuncInst(self: *Function, inst: *ir.Inst, comptime arch: std.Target.Cpu.Arch) !MCValue {407 fn genBody(self: *Self, body: ir.Body) InnerError!void {
392 switch (inst.tag) {408 const inst_table = &self.branch_stack.items[0].inst_table;
393 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),409 for (body.instructions) |inst| {
394 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),410 const new_inst = try self.genFuncInst(inst);
395 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch),411 try inst_table.putNoClobber(self.gpa, inst, new_inst);
396 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),412
397 .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch),413 var i: ir.Inst.DeathsBitIndex = 0;
398 .br => return self.genBr(inst.cast(ir.Inst.Br).?, arch),414 while (inst.getOperand(i)) |operand| : (i += 1) {
399 .breakpoint => return self.genBreakpoint(inst.src, arch),415 if (inst.operandDies(i))
400 .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?, arch),416 self.processDeath(operand);
401 .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch),417 }
402 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch),418 }
403 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch),
404 .constant => unreachable, // excluded from function bodies
405 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?, arch),
406 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?, arch),
407 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
408 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?, arch),
409 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),
410 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch),
411 .unreach => return MCValue{ .unreach = {} },
412 .not => return self.genNot(inst.cast(ir.Inst.Not).?, arch),
413 }419 }
414 }
415420
416 fn genNot(self: *Function, inst: *ir.Inst.Not, comptime arch: std.Target.Cpu.Arch) !MCValue {421 fn processDeath(self: *Self, inst: *ir.Inst) void {
417 // No side effects, so if it's unreferenced, do nothing.422 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
418 if (inst.base.isUnused())423 const entry = branch.inst_table.getEntry(inst) orelse return;
419 return MCValue.dead;424 const prev_value = entry.value;
420 const operand = try self.resolveInst(inst.args.operand);425 entry.value = .dead;
421 switch (operand) {426 switch (prev_value) {
422 .dead => unreachable,427 .register => |reg| {
423 .unreach => unreachable,428 _ = branch.registers.remove(reg);
424 .compare_flags_unsigned => |op| return MCValue{429 branch.markRegFree(reg);
425 .compare_flags_unsigned = switch (op) {
426 .gte => .lt,
427 .gt => .lte,
428 .neq => .eq,
429 .lt => .gte,
430 .lte => .gt,
431 .eq => .neq,
432 },430 },
433 },431 else => {}, // TODO process stack allocation death
434 .compare_flags_signed => |op| return MCValue{432 }
435 .compare_flags_signed = switch (op) {
436 .gte => .lt,
437 .gt => .lte,
438 .neq => .eq,
439 .lt => .gte,
440 .lte => .gt,
441 .eq => .neq,
442 },
443 },
444 else => {},
445 }433 }
446434
447 switch (arch) {435 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
448 .x86_64 => {436 switch (inst.tag) {
449 var imm = ir.Inst.Constant{437 .add => return self.genAdd(inst.castTag(.add).?),
450 .base = .{438 .arg => return self.genArg(inst.castTag(.arg).?),
451 .tag = .constant,439 .assembly => return self.genAsm(inst.castTag(.assembly).?),
452 .deaths = 0,440 .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
453 .ty = inst.args.operand.ty,441 .block => return self.genBlock(inst.castTag(.block).?),
454 .src = inst.args.operand.src,442 .br => return self.genBr(inst.castTag(.br).?),
455 },443 .breakpoint => return self.genBreakpoint(inst.src),
456 .val = Value.initTag(.bool_true),444 .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?),
457 };445 .call => return self.genCall(inst.castTag(.call).?),
458 return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);446 .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
459 },447 .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),
460 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),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).?),
453 .constant => unreachable, // excluded from function bodies
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).?),
460 .unreach => return MCValue{ .unreach = {} },
461 .not => return self.genNot(inst.castTag(.not).?),
462 }
461 }463 }
462 }
463464
464 fn genAdd(self: *Function, inst: *ir.Inst.Add, comptime arch: std.Target.Cpu.Arch) !MCValue {465 fn genNot(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
465 // No side effects, so if it's unreferenced, do nothing.466 // No side effects, so if it's unreferenced, do nothing.
466 if (inst.base.isUnused())467 if (inst.base.isUnused())
467 return MCValue.dead;468 return MCValue.dead;
468 switch (arch) {469 const operand = try self.resolveInst(inst.operand);
469 .x86_64 => {470 switch (operand) {
470 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);471 .dead => unreachable,
471 },472 .unreach => unreachable,
472 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),473 .compare_flags_unsigned => |op| return MCValue{
473 }474 .compare_flags_unsigned = switch (op) {
474 }475 .gte => .lt,
476 .gt => .lte,
477 .neq => .eq,
478 .lt => .gte,
479 .lte => .gt,
480 .eq => .neq,
481 },
482 },
483 .compare_flags_signed => |op| return MCValue{
484 .compare_flags_signed = switch (op) {
485 .gte => .lt,
486 .gt => .lte,
487 .neq => .eq,
488 .lt => .gte,
489 .lte => .gt,
490 .eq => .neq,
491 },
492 },
493 else => {},
494 }
475495
476 fn genSub(self: *Function, inst: *ir.Inst.Sub, comptime arch: std.Target.Cpu.Arch) !MCValue {496 switch (arch) {
477 // No side effects, so if it's unreferenced, do nothing.497 .x86_64 => {
478 if (inst.base.isUnused())498 var imm = ir.Inst.Constant{
479 return MCValue.dead;499 .base = .{
480 switch (arch) {500 .tag = .constant,
481 .x86_64 => {501 .deaths = 0,
482 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);502 .ty = inst.operand.ty,
483 },503 .src = inst.operand.src,
484 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),504 },
505 .val = Value.initTag(.bool_true),
506 };
507 return try self.genX8664BinMath(&inst.base, inst.operand, &imm.base, 6, 0x30);
508 },
509 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
510 }
485 }511 }
486 }
487512
488 /// ADD, SUB, XOR, OR, AND513 fn genAdd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
489 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {514 // No side effects, so if it's unreferenced, do nothing.
490 try self.code.ensureCapacity(self.code.items.len + 8);515 if (inst.base.isUnused())
491516 return MCValue.dead;
492 const lhs = try self.resolveInst(op_lhs);517 switch (arch) {
493 const rhs = try self.resolveInst(op_rhs);518 .x86_64 => {
494519 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 0, 0x00);
495 // There are 2 operands, destination and source.520 },
496 // Either one, but not both, can be a memory operand.521 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
497 // Source operand can be an immediate, 8 bits or 32 bits.
498 // So, if either one of the operands dies with this instruction, we can use it
499 // as the result MCValue.
500 var dst_mcv: MCValue = undefined;
501 var src_mcv: MCValue = undefined;
502 var src_inst: *ir.Inst = undefined;
503 if (inst.operandDies(0) and lhs.isMutable()) {
504 // LHS dies; use it as the destination.
505 // Both operands cannot be memory.
506 src_inst = op_rhs;
507 if (lhs.isMemory() and rhs.isMemory()) {
508 dst_mcv = try self.copyToNewRegister(op_lhs);
509 src_mcv = rhs;
510 } else {
511 dst_mcv = lhs;
512 src_mcv = rhs;
513 }522 }
514 } else if (inst.operandDies(1) and rhs.isMutable()) {523 }
515 // RHS dies; use it as the destination.524
516 // Both operands cannot be memory.525 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
517 src_inst = op_lhs;526 // No side effects, so if it's unreferenced, do nothing.
518 if (lhs.isMemory() and rhs.isMemory()) {527 if (inst.base.isUnused())
519 dst_mcv = try self.copyToNewRegister(op_rhs);528 return MCValue.dead;
520 src_mcv = lhs;529 switch (arch) {
521 } else {530 .x86_64 => {
522 dst_mcv = rhs;531 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 5, 0x28);
523 src_mcv = lhs;532 },
533 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
524 }534 }
525 } else {535 }
526 if (lhs.isMemory()) {536
527 dst_mcv = try self.copyToNewRegister(op_lhs);537 /// ADD, SUB, XOR, OR, AND
528 src_mcv = rhs;538 fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
539 try self.code.ensureCapacity(self.code.items.len + 8);
540
541 const lhs = try self.resolveInst(op_lhs);
542 const rhs = try self.resolveInst(op_rhs);
543
544 // There are 2 operands, destination and source.
545 // Either one, but not both, can be a memory operand.
546 // Source operand can be an immediate, 8 bits or 32 bits.
547 // So, if either one of the operands dies with this instruction, we can use it
548 // as the result MCValue.
549 var dst_mcv: MCValue = undefined;
550 var src_mcv: MCValue = undefined;
551 var src_inst: *ir.Inst = undefined;
552 if (inst.operandDies(0) and lhs.isMutable()) {
553 // LHS dies; use it as the destination.
554 // Both operands cannot be memory.
529 src_inst = op_rhs;555 src_inst = op_rhs;
530 } else {556 if (lhs.isMemory() and rhs.isMemory()) {
531 dst_mcv = try self.copyToNewRegister(op_rhs);557 dst_mcv = try self.copyToNewRegister(op_lhs);
532 src_mcv = lhs;558 src_mcv = rhs;
559 } else {
560 dst_mcv = lhs;
561 src_mcv = rhs;
562 }
563 } else if (inst.operandDies(1) and rhs.isMutable()) {
564 // RHS dies; use it as the destination.
565 // Both operands cannot be memory.
533 src_inst = op_lhs;566 src_inst = op_lhs;
567 if (lhs.isMemory() and rhs.isMemory()) {
568 dst_mcv = try self.copyToNewRegister(op_rhs);
569 src_mcv = lhs;
570 } else {
571 dst_mcv = rhs;
572 src_mcv = lhs;
573 }
574 } else {
575 if (lhs.isMemory()) {
576 dst_mcv = try self.copyToNewRegister(op_lhs);
577 src_mcv = rhs;
578 src_inst = op_rhs;
579 } else {
580 dst_mcv = try self.copyToNewRegister(op_rhs);
581 src_mcv = lhs;
582 src_inst = op_lhs;
583 }
584 }
585 // This instruction supports only signed 32-bit immediates at most. If the immediate
586 // value is larger than this, we put it in a register.
587 // A potential opportunity for future optimization here would be keeping track
588 // of the fact that the instruction is available both as an immediate
589 // and as a register.
590 switch (src_mcv) {
591 .immediate => |imm| {
592 if (imm > std.math.maxInt(u31)) {
593 src_mcv = try self.copyToNewRegister(src_inst);
594 }
595 },
596 else => {},
534 }597 }
598
599 try self.genX8664BinMathCode(inst.src, dst_mcv, src_mcv, opx, mr);
600
601 return dst_mcv;
535 }602 }
536 // This instruction supports only signed 32-bit immediates at most. If the immediate603
537 // value is larger than this, we put it in a register.604 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
538 // A potential opportunity for future optimization here would be keeping track605 switch (dst_mcv) {
539 // of the fact that the instruction is available both as an immediate606 .none => unreachable,
540 // and as a register.607 .dead, .unreach, .immediate => unreachable,
541 switch (src_mcv) {608 .compare_flags_unsigned => unreachable,
542 .immediate => |imm| {609 .compare_flags_signed => unreachable,
543 if (imm > std.math.maxInt(u31)) {610 .register => |dst_reg| {
544 src_mcv = try self.copyToNewRegister(src_inst);611 switch (src_mcv) {
545 }612 .none => unreachable,
546 },613 .dead, .unreach => unreachable,
547 else => {},614 .register => |src_reg| {
615 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
616 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
617 },
618 .immediate => |imm| {
619 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
620 // 81 /opx id
621 if (imm32 <= std.math.maxInt(u7)) {
622 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
623 self.code.appendSliceAssumeCapacity(&[_]u8{
624 0x83,
625 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
626 @intCast(u8, imm32),
627 });
628 } else {
629 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
630 self.code.appendSliceAssumeCapacity(&[_]u8{
631 0x81,
632 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
633 });
634 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
635 }
636 },
637 .embedded_in_code, .memory, .stack_offset => {
638 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
639 },
640 .compare_flags_unsigned => {
641 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
642 },
643 .compare_flags_signed => {
644 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
645 },
646 }
647 },
648 .embedded_in_code, .memory, .stack_offset => {
649 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
650 },
651 }
548 }652 }
549653
550 try self.genX8664BinMathCode(inst.src, dst_mcv, src_mcv, opx, mr);654 fn genArg(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
655 if (FreeRegInt == u0) {
656 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
657 }
658 if (inst.base.isUnused())
659 return MCValue.dead;
551660
552 return dst_mcv;661 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
553 }662 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
554663
555 fn genX8664BinMathCode(self: *Function, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {664 const result = self.args[self.arg_index];
556 switch (dst_mcv) {665 self.arg_index += 1;
557 .none => unreachable,
558 .dead, .unreach, .immediate => unreachable,
559 .compare_flags_unsigned => unreachable,
560 .compare_flags_signed => unreachable,
561 .register => |dst_reg_usize| {
562 const dst_reg = @intToEnum(Reg(.x86_64), @intCast(u8, dst_reg_usize));
563 switch (src_mcv) {
564 .none => unreachable,
565 .dead, .unreach => unreachable,
566 .register => |src_reg_usize| {
567 const src_reg = @intToEnum(Reg(.x86_64), @intCast(u8, src_reg_usize));
568 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
569 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
570 },
571 .immediate => |imm| {
572 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
573 // 81 /opx id
574 if (imm32 <= std.math.maxInt(u7)) {
575 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
576 self.code.appendSliceAssumeCapacity(&[_]u8{
577 0x83,
578 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
579 @intCast(u8, imm32),
580 });
581 } else {
582 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
583 self.code.appendSliceAssumeCapacity(&[_]u8{
584 0x81,
585 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
586 });
587 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
588 }
589 },
590 .embedded_in_code, .memory, .stack_offset => {
591 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
592 },
593 .compare_flags_unsigned => {
594 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
595 },
596 .compare_flags_signed => {
597 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
598 },
599 }
600 },
601 .embedded_in_code, .memory, .stack_offset => {
602 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
603 },
604 }
605 }
606666
607 fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue {667 switch (result) {
608 const i = self.arg_index;668 .register => |reg| {
609 self.arg_index += 1;669 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base });
610 return self.args[i];670 branch.markRegUsed(reg);
611 }671 },
672 else => {},
673 }
674 return result;
675 }
612676
613 fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue {677 fn genBreakpoint(self: *Self, src: usize) !MCValue {
614 switch (arch) {678 switch (arch) {
615 .i386, .x86_64 => {679 .i386, .x86_64 => {
616 try self.code.append(0xcc); // int3680 try self.code.append(0xcc); // int3
617 },681 },
618 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),682 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
683 }
684 return .none;
619 }685 }
620 return .none;
621 }
622686
623 fn genCall(self: *Function, inst: *ir.Inst.Call, comptime arch: std.Target.Cpu.Arch) !MCValue {687 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
624 const fn_ty = inst.args.func.ty;688 const fn_ty = inst.func.ty;
625 const cc = fn_ty.fnCallingConvention();689 const cc = fn_ty.fnCallingConvention();
626 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());690 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
627 defer self.gpa.free(param_types);691 defer self.gpa.free(param_types);
628 fn_ty.fnParamTypes(param_types);692 fn_ty.fnParamTypes(param_types);
629 var mc_args = try self.gpa.alloc(MCValue, param_types.len);693 var mc_args = try self.gpa.alloc(MCValue, param_types.len);
630 defer self.gpa.free(mc_args);694 defer self.gpa.free(mc_args);
631 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);695 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);
632696
633 switch (arch) {697 switch (arch) {
634 .x86_64 => {698 .x86_64 => {
635 for (mc_args) |mc_arg, arg_i| {699 for (mc_args) |mc_arg, arg_i| {
636 const arg = inst.args.args[arg_i];700 const arg = inst.args[arg_i];
637 const arg_mcv = try self.resolveInst(inst.args.args[arg_i]);701 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
638 switch (mc_arg) {702 switch (mc_arg) {
639 .none => continue,703 .none => continue,
640 .register => |reg| {704 .register => |reg| {
641 try self.genSetReg(arg.src, arch, @intToEnum(Reg(arch), @intCast(u8, reg)), arg_mcv);705 try self.genSetReg(arg.src, reg, arg_mcv);
642 // TODO interact with the register allocator to mark the instruction as moved.706 // TODO interact with the register allocator to mark the instruction as moved.
643 },707 },
644 .stack_offset => {708 .stack_offset => {
645 // Here we need to emit instructions like this:709 // Here we need to emit instructions like this:
646 // mov qword ptr [rsp + stack_offset], x710 // mov qword ptr [rsp + stack_offset], x
647 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});711 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
648 },712 },
649 .immediate => unreachable,713 .immediate => unreachable,
650 .unreach => unreachable,714 .unreach => unreachable,
651 .dead => unreachable,715 .dead => unreachable,
652 .embedded_in_code => unreachable,716 .embedded_in_code => unreachable,
653 .memory => unreachable,717 .memory => unreachable,
654 .compare_flags_signed => unreachable,718 .compare_flags_signed => unreachable,
655 .compare_flags_unsigned => unreachable,719 .compare_flags_unsigned => unreachable,
720 }
656 }721 }
657 }
658722
659 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {723 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
660 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {724 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
661 const func = func_val.func;725 const func = func_val.func;
662 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.?];
663 const ptr_bits = self.target.cpu.arch.ptrBitWidth();727 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
664 const ptr_bytes: u64 = @divExact(ptr_bits, 8);728 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
665 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);729 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
666 // ff 14 25 xx xx xx xx call [addr]730 // ff 14 25 xx xx xx xx call [addr]
667 try self.code.ensureCapacity(self.code.items.len + 7);731 try self.code.ensureCapacity(self.code.items.len + 7);
668 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });732 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
669 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);733 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
734 } else {
735 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
736 }
670 } else {737 } else {
671 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});738 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
672 }739 }
673 } else {740 },
674 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});741 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
675 }742 }
676 },
677 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
678 }
679743
680 const return_type = fn_ty.fnReturnType();744 const return_type = fn_ty.fnReturnType();
681 switch (return_type.zigTypeTag()) {745 switch (return_type.zigTypeTag()) {
682 .Void => return MCValue{ .none = {} },746 .Void => return MCValue{ .none = {} },
683 .NoReturn => return MCValue{ .unreach = {} },747 .NoReturn => return MCValue{ .unreach = {} },
684 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),748 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
749 }
685 }750 }
686 }
687751
688 fn ret(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, mcv: MCValue) !MCValue {752 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
689 if (mcv != .none) {753 if (mcv != .none) {
690 return self.fail(src, "TODO implement return with non-void operand", .{});754 return self.fail(src, "TODO implement return with non-void operand", .{});
691 }755 }
692 switch (arch) {756 switch (arch) {
693 .i386 => {757 .i386 => {
694 try self.code.append(0xc3); // ret758 try self.code.append(0xc3); // ret
695 },759 },
696 .x86_64 => {760 .x86_64 => {
697 try self.code.appendSlice(&[_]u8{761 try self.code.appendSlice(&[_]u8{
698 0x5d, // pop rbp762 0x5d, // pop rbp
699 0xc3, // ret763 0xc3, // ret
700 });764 });
701 },765 },
702 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),766 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
767 }
768 return .unreach;
703 }769 }
704 return .unreach;
705 }
706
707 fn genRet(self: *Function, inst: *ir.Inst.Ret, comptime arch: std.Target.Cpu.Arch) !MCValue {
708 const operand = try self.resolveInst(inst.args.operand);
709 return self.ret(inst.base.src, arch, operand);
710 }
711770
712 fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {771 fn genRet(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
713 return self.ret(inst.base.src, arch, .none);772 const operand = try self.resolveInst(inst.operand);
714 }773 return self.ret(inst.base.src, operand);
774 }
715775
716 fn genCmp(self: *Function, inst: *ir.Inst.Cmp, comptime arch: std.Target.Cpu.Arch) !MCValue {776 fn genRetVoid(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
717 // No side effects, so if it's unreferenced, do nothing.777 return self.ret(inst.base.src, .none);
718 if (inst.base.isUnused())
719 return MCValue.dead;
720 switch (arch) {
721 .x86_64 => {
722 try self.code.ensureCapacity(self.code.items.len + 8);
723
724 const lhs = try self.resolveInst(inst.args.lhs);
725 const rhs = try self.resolveInst(inst.args.rhs);
726
727 // There are 2 operands, destination and source.
728 // Either one, but not both, can be a memory operand.
729 // Source operand can be an immediate, 8 bits or 32 bits.
730 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
731 try self.copyToNewRegister(inst.args.lhs)
732 else
733 lhs;
734 // This instruction supports only signed 32-bit immediates at most.
735 const src_mcv = try self.limitImmediateType(inst.args.rhs, i32);
736
737 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
738 const info = inst.args.lhs.ty.intInfo(self.target.*);
739 if (info.signed) {
740 return MCValue{ .compare_flags_signed = inst.args.op };
741 } else {
742 return MCValue{ .compare_flags_unsigned = inst.args.op };
743 }
744 },
745 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
746 }778 }
747 }
748779
749 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {780 fn genCmp(self: *Self, inst: *ir.Inst.BinOp, op: std.math.CompareOperator) !MCValue {
750 switch (arch) {781 // No side effects, so if it's unreferenced, do nothing.
751 .x86_64 => {782 if (inst.base.isUnused())
752 try self.code.ensureCapacity(self.code.items.len + 6);783 return MCValue.dead;
753784 switch (arch) {
754 const cond = try self.resolveInst(inst.args.condition);785 .x86_64 => {
755 switch (cond) {786 try self.code.ensureCapacity(self.code.items.len + 8);
756 .compare_flags_signed => |cmp_op| {787
757 // Here we map to the opposite opcode because the jump is to the false branch.788 const lhs = try self.resolveInst(inst.lhs);
758 const opcode: u8 = switch (cmp_op) {789 const rhs = try self.resolveInst(inst.rhs);
759 .gte => 0x8c,790
760 .gt => 0x8e,791 // There are 2 operands, destination and source.
761 .neq => 0x84,792 // Either one, but not both, can be a memory operand.
762 .lt => 0x8d,793 // Source operand can be an immediate, 8 bits or 32 bits.
763 .lte => 0x8f,794 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
764 .eq => 0x85,795 try self.copyToNewRegister(inst.lhs)
765 };796 else
766 return self.genX86CondBr(inst, opcode, arch);797 lhs;
767 },798 // This instruction supports only signed 32-bit immediates at most.
768 .compare_flags_unsigned => |cmp_op| {799 const src_mcv = try self.limitImmediateType(inst.rhs, i32);
769 // Here we map to the opposite opcode because the jump is to the false branch.800
770 const opcode: u8 = switch (cmp_op) {801 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
771 .gte => 0x82,802 const info = inst.lhs.ty.intInfo(self.target.*);
772 .gt => 0x86,803 if (info.signed) {
773 .neq => 0x84,804 return MCValue{ .compare_flags_signed = op };
774 .lt => 0x83,805 } else {
775 .lte => 0x87,806 return MCValue{ .compare_flags_unsigned = op };
776 .eq => 0x85,807 }
777 };808 },
778 return self.genX86CondBr(inst, opcode, arch);809 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
779 },810 }
780 .register => |reg_usize| {
781 const reg = @intToEnum(Reg(arch), @intCast(u8, reg_usize));
782 // test reg, 1
783 // TODO detect al, ax, eax
784 try self.code.ensureCapacity(self.code.items.len + 4);
785 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
786 self.code.appendSliceAssumeCapacity(&[_]u8{
787 0xf6,
788 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
789 0x01,
790 });
791 return self.genX86CondBr(inst, 0x84, arch);
792 },
793 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
794 }
795 },
796 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
797 }811 }
798 }
799812
800 fn genX86CondBr(self: *Function, inst: *ir.Inst.CondBr, opcode: u8, comptime arch: std.Target.Cpu.Arch) !MCValue {813 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
801 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });814 switch (arch) {
802 const reloc = Reloc{ .rel32 = self.code.items.len };815 .x86_64 => {
803 self.code.items.len += 4;816 try self.code.ensureCapacity(self.code.items.len + 6);
804 try self.genBody(inst.args.true_body, arch);817
805 try self.performReloc(inst.base.src, reloc);818 const cond = try self.resolveInst(inst.condition);
806 try self.genBody(inst.args.false_body, arch);819 switch (cond) {
807 return MCValue.unreach;820 .compare_flags_signed => |cmp_op| {
808 }821 // Here we map to the opposite opcode because the jump is to the false branch.
822 const opcode: u8 = switch (cmp_op) {
823 .gte => 0x8c,
824 .gt => 0x8e,
825 .neq => 0x84,
826 .lt => 0x8d,
827 .lte => 0x8f,
828 .eq => 0x85,
829 };
830 return self.genX86CondBr(inst, opcode);
831 },
832 .compare_flags_unsigned => |cmp_op| {
833 // Here we map to the opposite opcode because the jump is to the false branch.
834 const opcode: u8 = switch (cmp_op) {
835 .gte => 0x82,
836 .gt => 0x86,
837 .neq => 0x84,
838 .lt => 0x83,
839 .lte => 0x87,
840 .eq => 0x85,
841 };
842 return self.genX86CondBr(inst, opcode);
843 },
844 .register => |reg| {
845 // test reg, 1
846 // TODO detect al, ax, eax
847 try self.code.ensureCapacity(self.code.items.len + 4);
848 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
849 self.code.appendSliceAssumeCapacity(&[_]u8{
850 0xf6,
851 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
852 0x01,
853 });
854 return self.genX86CondBr(inst, 0x84);
855 },
856 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
857 }
858 },
859 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
860 }
861 }
809862
810 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull, comptime arch: std.Target.Cpu.Arch) !MCValue {863 fn genX86CondBr(self: *Self, inst: *ir.Inst.CondBr, opcode: u8) !MCValue {
811 switch (arch) {864 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
812 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),865 const reloc = Reloc{ .rel32 = self.code.items.len };
866 self.code.items.len += 4;
867 try self.genBody(inst.then_body);
868 try self.performReloc(inst.base.src, reloc);
869 try self.genBody(inst.else_body);
870 return MCValue.unreach;
813 }871 }
814 }
815872
816 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull, comptime arch: std.Target.Cpu.Arch) !MCValue {873 fn genIsNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
817 // Here you can specialize this instruction if it makes sense to, otherwise the default874 switch (arch) {
818 // will call genIsNull and invert the result.875 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
819 switch (arch) {876 }
820 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
821 }877 }
822 }
823878
824 fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {879 fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
825 if (inst.base.ty.hasCodeGenBits()) {880 // Here you can specialize this instruction if it makes sense to, otherwise the default
826 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});881 // will call genIsNull and invert the result.
882 switch (arch) {
883 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
884 }
827 }885 }
828 // A block is nothing but a setup to be able to jump to the end.
829 defer inst.codegen.relocs.deinit(self.gpa);
830 try self.genBody(inst.args.body, arch);
831886
832 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);887 fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {
888 if (inst.base.ty.hasCodeGenBits()) {
889 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});
890 }
891 // A block is nothing but a setup to be able to jump to the end.
892 defer inst.codegen.relocs.deinit(self.gpa);
893 try self.genBody(inst.body);
833894
834 return MCValue.none;895 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
835 }
836896
837 fn performReloc(self: *Function, src: usize, reloc: Reloc) !void {897 return MCValue.none;
838 switch (reloc) {
839 .rel32 => |pos| {
840 const amt = self.code.items.len - (pos + 4);
841 const s32_amt = std.math.cast(i32, amt) catch
842 return self.fail(src, "unable to perform relocation: jump too far", .{});
843 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
844 },
845 }898 }
846 }
847899
848 fn genBr(self: *Function, inst: *ir.Inst.Br, comptime arch: std.Target.Cpu.Arch) !MCValue {900 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
849 if (!inst.args.operand.ty.hasCodeGenBits())901 switch (reloc) {
850 return self.brVoid(inst.base.src, inst.args.block, arch);902 .rel32 => |pos| {
851903 const amt = self.code.items.len - (pos + 4);
852 const operand = try self.resolveInst(inst.args.operand);904 const s32_amt = std.math.cast(i32, amt) catch
853 switch (arch) {905 return self.fail(src, "unable to perform relocation: jump too far", .{});
854 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),906 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
907 },
908 }
855 }909 }
856 }
857910
858 fn genBrVoid(self: *Function, inst: *ir.Inst.BrVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {911 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
859 return self.brVoid(inst.base.src, inst.args.block, arch);912 if (!inst.operand.ty.hasCodeGenBits())
860 }913 return self.brVoid(inst.base.src, inst.block);
861914
862 fn brVoid(self: *Function, src: usize, block: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {915 const operand = try self.resolveInst(inst.operand);
863 // Emit a jump with a relocation. It will be patched up after the block ends.916 switch (arch) {
864 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);917 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
865918 }
866 switch (arch) {
867 .i386, .x86_64 => {
868 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
869 // which is available if the jump is 127 bytes or less forward.
870 try self.code.resize(self.code.items.len + 5);
871 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
872 // Leave the jump offset undefined
873 block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 });
874 },
875 else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),
876 }919 }
877 return .none;
878 }
879920
880 fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue {921 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
881 if (!inst.args.is_volatile and inst.base.isUnused())922 return self.brVoid(inst.base.src, inst.block);
882 return MCValue.dead;
883 if (arch != .x86_64 and arch != .i386) {
884 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
885 }923 }
886 for (inst.args.inputs) |input, i| {924
887 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {925 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
888 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});926 // Emit a jump with a relocation. It will be patched up after the block ends.
927 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
928
929 switch (arch) {
930 .i386, .x86_64 => {
931 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
932 // which is available if the jump is 127 bytes or less forward.
933 try self.code.resize(self.code.items.len + 5);
934 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
935 // Leave the jump offset undefined
936 block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 });
937 },
938 else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),
889 }939 }
890 const reg_name = input[1 .. input.len - 1];940 return .none;
891 const reg = parseRegName(arch, reg_name) orelse
892 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
893 const arg = try self.resolveInst(inst.args.args[i]);
894 try self.genSetReg(inst.base.src, arch, reg, arg);
895 }941 }
896942
897 if (mem.eql(u8, inst.args.asm_source, "syscall")) {943 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
898 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });944 if (!inst.is_volatile and inst.base.isUnused())
899 } else {945 return MCValue.dead;
900 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});946 if (arch != .x86_64 and arch != .i386) {
901 }947 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
948 }
949 for (inst.inputs) |input, i| {
950 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
951 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
952 }
953 const reg_name = input[1 .. input.len - 1];
954 const reg = parseRegName(reg_name) orelse
955 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
956 const arg = try self.resolveInst(inst.args[i]);
957 try self.genSetReg(inst.base.src, reg, arg);
958 }
902959
903 if (inst.args.output) |output| {960 if (mem.eql(u8, inst.asm_source, "syscall")) {
904 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {961 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
905 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});962 } else {
963 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
906 }964 }
907 const reg_name = output[2 .. output.len - 1];
908 const reg = parseRegName(arch, reg_name) orelse
909 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
910 return MCValue{ .register = @enumToInt(reg) };
911 } else {
912 return MCValue.none;
913 }
914 }
915965
916 /// Encodes a REX prefix as specified, and appends it to the instruction966 if (inst.output) |output| {
917 /// stream. This only modifies the instruction stream if at least one bit967 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
918 /// is set true, which has a few implications:968 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
919 ///969 }
920 /// * The length of the instruction buffer will be modified *if* the970 const reg_name = output[2 .. output.len - 1];
921 /// resulting REX is meaningful, but will remain the same if it is not.971 const reg = parseRegName(reg_name) orelse
922 /// * Deliberately inserting a "meaningless REX" requires explicit usage of972 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
923 /// 0x40, and cannot be done via this function.973 return MCValue{ .register = reg };
924 fn rex(self: *Function, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {974 } else {
925 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.975 return MCValue.none;
926 var value: u8 = 0x40;976 }
927 if (arg.b) {
928 value |= 0x1;
929 }
930 if (arg.x) {
931 value |= 0x2;
932 }
933 if (arg.r) {
934 value |= 0x4;
935 }
936 if (arg.w) {
937 value |= 0x8;
938 }977 }
939 if (value != 0x40) {978
940 self.code.appendAssumeCapacity(value);979 /// Encodes a REX prefix as specified, and appends it to the instruction
980 /// stream. This only modifies the instruction stream if at least one bit
981 /// is set true, which has a few implications:
982 ///
983 /// * The length of the instruction buffer will be modified *if* the
984 /// resulting REX is meaningful, but will remain the same if it is not.
985 /// * Deliberately inserting a "meaningless REX" requires explicit usage of
986 /// 0x40, and cannot be done via this function.
987 fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
988 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
989 var value: u8 = 0x40;
990 if (arg.b) {
991 value |= 0x1;
992 }
993 if (arg.x) {
994 value |= 0x2;
995 }
996 if (arg.r) {
997 value |= 0x4;
998 }
999 if (arg.w) {
1000 value |= 0x8;
1001 }
1002 if (value != 0x40) {
1003 self.code.appendAssumeCapacity(value);
1004 }
941 }1005 }
942 }
9431006
944 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {1007 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
945 switch (arch) {1008 switch (arch) {
946 .x86_64 => switch (mcv) {1009 .x86_64 => switch (mcv) {
947 .dead => unreachable,1010 .dead => unreachable,
948 .none => unreachable,1011 .none => unreachable,
949 .unreach => unreachable,1012 .unreach => unreachable,
950 .compare_flags_unsigned => |op| {1013 .compare_flags_unsigned => |op| {
951 try self.code.ensureCapacity(self.code.items.len + 3);
952 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
953 const opcode: u8 = switch (op) {
954 .gte => 0x93,
955 .gt => 0x97,
956 .neq => 0x95,
957 .lt => 0x92,
958 .lte => 0x96,
959 .eq => 0x94,
960 };
961 const id = @as(u8, reg.id() & 0b111);
962 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
963 },
964 .compare_flags_signed => |op| {
965 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
966 },
967 .immediate => |x| {
968 if (reg.size() != 64) {
969 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
970 }
971 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
972 // register is the fastest way to zero a register.
973 if (x == 0) {
974 // The encoding for `xor r32, r32` is `0x31 /r`.
975 // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the
976 // ModR/M byte of the instruction contains a register operand and an r/m operand."
977 //
978 // R/M bytes are composed of two bits for the mode, then three bits for the register,
979 // then three bits for the operand. Since we're zeroing a register, the two three-bit
980 // values will be identical, and the mode is three (the raw register value).
981 //
982 // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
983 // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
984 // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
985 try self.code.ensureCapacity(self.code.items.len + 3);1014 try self.code.ensureCapacity(self.code.items.len + 3);
986 self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() });1015 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
1016 const opcode: u8 = switch (op) {
1017 .gte => 0x93,
1018 .gt => 0x97,
1019 .neq => 0x95,
1020 .lt => 0x92,
1021 .lte => 0x96,
1022 .eq => 0x94,
1023 };
987 const id = @as(u8, reg.id() & 0b111);1024 const id = @as(u8, reg.id() & 0b111);
988 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });1025 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
989 return;1026 },
990 }1027 .compare_flags_signed => |op| {
991 if (x <= std.math.maxInt(u32)) {1028 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
992 // Next best case: if we set the lower four bytes, the upper four will be zeroed.1029 },
993 //1030 .immediate => |x| {
994 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.1031 if (reg.size() != 64) {
995 if (reg.isExtended()) {1032 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
996 // Just as with XORing, we need a REX prefix. This time though, we only
997 // need the B bit set, as we're extending the opcode's register field,
998 // and there is no Mod R/M byte.
999 //
1000 // Thus, we need b01000001, or 0x41.
1001 try self.code.resize(self.code.items.len + 6);
1002 self.code.items[self.code.items.len - 6] = 0x41;
1003 } else {
1004 try self.code.resize(self.code.items.len + 5);
1005 }1033 }
1006 self.code.items[self.code.items.len - 5] = 0xB8 | @as(u8, reg.id() & 0b111);1034 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
1007 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];1035 // register is the fastest way to zero a register.
1008 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));1036 if (x == 0) {
1009 return;1037 // The encoding for `xor r32, r32` is `0x31 /r`.
1010 }1038 // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the
1011 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls1039 // ModR/M byte of the instruction contains a register operand and an r/m operand."
1012 // this `movabs`, though this is officially just a different variant of the plain `mov`1040 //
1013 // instruction.1041 // R/M bytes are composed of two bits for the mode, then three bits for the register,
1014 //1042 // then three bits for the operand. Since we're zeroing a register, the two three-bit
1015 // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only1043 // values will be identical, and the mode is three (the raw register value).
1016 // difference is that we set REX.W before the instruction, which extends the load to
1017 // 64-bit and uses the full bit-width of the register.
1018 //
1019 // Since we always need a REX here, let's just check if we also need to set REX.B.
1020 //
1021 // In this case, the encoding of the REX byte is 0b0100100B
1022 try self.code.ensureCapacity(self.code.items.len + 10);
1023 self.rex(.{ .w = true, .b = reg.isExtended() });
1024 self.code.items.len += 9;
1025 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
1026 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
1027 mem.writeIntLittle(u64, imm_ptr, x);
1028 },
1029 .embedded_in_code => |code_offset| {
1030 if (reg.size() != 64) {
1031 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1032 }
1033 // We need the offset from RIP in a signed i32 twos complement.
1034 // The instruction is 7 bytes long and RIP points to the next instruction.
1035 try self.code.ensureCapacity(self.code.items.len + 7);
1036 // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified,
1037 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
1038 // bits as five.
1039 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
1040 self.rex(.{ .w = true, .b = reg.isExtended() });
1041 self.code.items.len += 6;
1042 const rip = self.code.items.len;
1043 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
1044 const offset = @intCast(i32, big_offset);
1045 self.code.items[self.code.items.len - 6] = 0x8D;
1046 self.code.items[self.code.items.len - 5] = 0b101 | (@as(u8, reg.id() & 0b111) << 3);
1047 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
1048 mem.writeIntLittle(i32, imm_ptr, offset);
1049 },
1050 .register => |r| {
1051 if (reg.size() != 64) {
1052 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1053 }
1054 const src_reg = @intToEnum(Reg(arch), @intCast(u8, r));
1055 // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.
1056 // This is thus three bytes: REX 0x8B R/M.
1057 // If the destination is extended, the R field must be 1.
1058 // If the *source* is extended, the B field must be 1.
1059 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
1060 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
1061 try self.code.ensureCapacity(self.code.items.len + 3);
1062 self.rex(.{ .w = true, .r = reg.isExtended(), .b = src_reg.isExtended() });
1063 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
1064 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
1065 },
1066 .memory => |x| {
1067 if (reg.size() != 64) {
1068 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1069 }
1070 if (x <= std.math.maxInt(u32)) {
1071 // Moving from memory to a register is a variant of `8B /r`.
1072 // Since we're using 64-bit moves, we require a REX.
1073 // This variant also requires a SIB, as it would otherwise be RIP-relative.
1074 // We want mode zero with the lower three bits set to four to indicate an SIB with no other displacement.
1075 // The SIB must be 0x25, to indicate a disp32 with no scaled index.
1076 // 0b00RRR100, where RRR is the lower three bits of the register ID.
1077 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
1078 try self.code.ensureCapacity(self.code.items.len + 8);
1079 self.rex(.{ .w = true, .b = reg.isExtended() });
1080 self.code.appendSliceAssumeCapacity(&[_]u8{
1081 0x8B,
1082 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
1083 0x25,
1084 });
1085 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, x));
1086 } else {
1087 // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load
1088 // the value.
1089 if (reg.id() == 0) {
1090 // REX.W 0xA1 moffs64*
1091 // moffs64* is a 64-bit offset "relative to segment base", which really just means the
1092 // absolute address for all practical purposes.
1093 try self.code.resize(self.code.items.len + 10);
1094 // REX.W == 0x48
1095 self.code.items[self.code.items.len - 10] = 0x48;
1096 self.code.items[self.code.items.len - 9] = 0xA1;
1097 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
1098 mem.writeIntLittle(u64, imm_ptr, x);
1099 } else {
1100 // This requires two instructions; a move imm as used above, followed by an indirect load using the register
1101 // as the address and the register as the destination.
1102 //1044 //
1103 // This cannot be used if the lower three bits of the id are equal to four or five, as there1045 // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
1104 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with1046 // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
1105 // this instruction.1047 // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
1106 const id3 = @truncate(u3, reg.id());
1107 std.debug.assert(id3 != 4 and id3 != 5);
1108
1109 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
1110 try self.genSetReg(src, arch, reg, MCValue{ .immediate = x });
1111
1112 // Now, the register contains the address of the value to load into it
1113 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
1114 // TODO: determine whether to allow other sized registers, and if so, handle them properly.
1115 // This operation requires three bytes: REX 0x8B R/M
1116 try self.code.ensureCapacity(self.code.items.len + 3);1048 try self.code.ensureCapacity(self.code.items.len + 3);
1117 // For this operation, we want R/M mode *zero* (use register indirectly), and the two register1049 self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() });
1118 // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.1050 const id = @as(u8, reg.id() & 0b111);
1051 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });
1052 return;
1053 }
1054 if (x <= std.math.maxInt(u32)) {
1055 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
1119 //1056 //
1120 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*1057 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
1121 // register operands need to be marked as extended.1058 if (reg.isExtended()) {
1122 self.rex(.{ .w = true, .b = reg.isExtended(), .r = reg.isExtended() });1059 // Just as with XORing, we need a REX prefix. This time though, we only
1123 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());1060 // need the B bit set, as we're extending the opcode's register field,
1124 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });1061 // and there is no Mod R/M byte.
1062 //
1063 // Thus, we need b01000001, or 0x41.
1064 try self.code.resize(self.code.items.len + 6);
1065 self.code.items[self.code.items.len - 6] = 0x41;
1066 } else {
1067 try self.code.resize(self.code.items.len + 5);
1068 }
1069 self.code.items[self.code.items.len - 5] = 0xB8 | @as(u8, reg.id() & 0b111);
1070 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
1071 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
1072 return;
1125 }1073 }
1126 }1074 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
1127 },1075 // this `movabs`, though this is officially just a different variant of the plain `mov`
1128 .stack_offset => |off| {1076 // instruction.
1129 return self.fail(src, "TODO implement genSetReg for stack variables", .{});1077 //
1078 // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only
1079 // difference is that we set REX.W before the instruction, which extends the load to
1080 // 64-bit and uses the full bit-width of the register.
1081 //
1082 // Since we always need a REX here, let's just check if we also need to set REX.B.
1083 //
1084 // In this case, the encoding of the REX byte is 0b0100100B
1085 try self.code.ensureCapacity(self.code.items.len + 10);
1086 self.rex(.{ .w = true, .b = reg.isExtended() });
1087 self.code.items.len += 9;
1088 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
1089 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
1090 mem.writeIntLittle(u64, imm_ptr, x);
1091 },
1092 .embedded_in_code => |code_offset| {
1093 if (reg.size() != 64) {
1094 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1095 }
1096 // We need the offset from RIP in a signed i32 twos complement.
1097 // The instruction is 7 bytes long and RIP points to the next instruction.
1098 try self.code.ensureCapacity(self.code.items.len + 7);
1099 // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified,
1100 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
1101 // bits as five.
1102 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
1103 self.rex(.{ .w = true, .b = reg.isExtended() });
1104 self.code.items.len += 6;
1105 const rip = self.code.items.len;
1106 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
1107 const offset = @intCast(i32, big_offset);
1108 self.code.items[self.code.items.len - 6] = 0x8D;
1109 self.code.items[self.code.items.len - 5] = 0b101 | (@as(u8, reg.id() & 0b111) << 3);
1110 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
1111 mem.writeIntLittle(i32, imm_ptr, offset);
1112 },
1113 .register => |src_reg| {
1114 if (reg.size() != 64) {
1115 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1116 }
1117 // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.
1118 // This is thus three bytes: REX 0x8B R/M.
1119 // If the destination is extended, the R field must be 1.
1120 // If the *source* is extended, the B field must be 1.
1121 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
1122 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
1123 try self.code.ensureCapacity(self.code.items.len + 3);
1124 self.rex(.{ .w = true, .r = reg.isExtended(), .b = src_reg.isExtended() });
1125 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
1126 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
1127 },
1128 .memory => |x| {
1129 if (reg.size() != 64) {
1130 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1131 }
1132 if (x <= std.math.maxInt(u32)) {
1133 // Moving from memory to a register is a variant of `8B /r`.
1134 // Since we're using 64-bit moves, we require a REX.
1135 // This variant also requires a SIB, as it would otherwise be RIP-relative.
1136 // We want mode zero with the lower three bits set to four to indicate an SIB with no other displacement.
1137 // The SIB must be 0x25, to indicate a disp32 with no scaled index.
1138 // 0b00RRR100, where RRR is the lower three bits of the register ID.
1139 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
1140 try self.code.ensureCapacity(self.code.items.len + 8);
1141 self.rex(.{ .w = true, .b = reg.isExtended() });
1142 self.code.appendSliceAssumeCapacity(&[_]u8{
1143 0x8B,
1144 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
1145 0x25,
1146 });
1147 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, x));
1148 } else {
1149 // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load
1150 // the value.
1151 if (reg.id() == 0) {
1152 // REX.W 0xA1 moffs64*
1153 // moffs64* is a 64-bit offset "relative to segment base", which really just means the
1154 // absolute address for all practical purposes.
1155 try self.code.resize(self.code.items.len + 10);
1156 // REX.W == 0x48
1157 self.code.items[self.code.items.len - 10] = 0x48;
1158 self.code.items[self.code.items.len - 9] = 0xA1;
1159 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
1160 mem.writeIntLittle(u64, imm_ptr, x);
1161 } else {
1162 // This requires two instructions; a move imm as used above, followed by an indirect load using the register
1163 // as the address and the register as the destination.
1164 //
1165 // This cannot be used if the lower three bits of the id are equal to four or five, as there
1166 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
1167 // this instruction.
1168 const id3 = @truncate(u3, reg.id());
1169 std.debug.assert(id3 != 4 and id3 != 5);
1170
1171 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
1172 try self.genSetReg(src, reg, MCValue{ .immediate = x });
1173
1174 // Now, the register contains the address of the value to load into it
1175 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
1176 // TODO: determine whether to allow other sized registers, and if so, handle them properly.
1177 // This operation requires three bytes: REX 0x8B R/M
1178 try self.code.ensureCapacity(self.code.items.len + 3);
1179 // For this operation, we want R/M mode *zero* (use register indirectly), and the two register
1180 // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.
1181 //
1182 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
1183 // register operands need to be marked as extended.
1184 self.rex(.{ .w = true, .b = reg.isExtended(), .r = reg.isExtended() });
1185 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
1186 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
1187 }
1188 }
1189 },
1190 .stack_offset => |off| {
1191 return self.fail(src, "TODO implement genSetReg for stack variables", .{});
1192 },
1130 },1193 },
1131 },1194 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
1132 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),1195 }
1133 }1196 }
1134 }
11351197
1136 fn genPtrToInt(self: *Function, inst: *ir.Inst.PtrToInt) !MCValue {1198 fn genPtrToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1137 // no-op1199 // no-op
1138 return self.resolveInst(inst.args.ptr);1200 return self.resolveInst(inst.operand);
1139 }1201 }
11401202
1141 fn genBitCast(self: *Function, inst: *ir.Inst.BitCast) !MCValue {1203 fn genBitCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1142 const operand = try self.resolveInst(inst.args.operand);1204 const operand = try self.resolveInst(inst.operand);
1143 return operand;1205 return operand;
1144 }1206 }
11451207
1146 fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue {1208 fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {
1147 // Constants have static lifetimes, so they are always memoized in the outer most table.1209 // Constants have static lifetimes, so they are always memoized in the outer most table.
1148 if (inst.cast(ir.Inst.Constant)) |const_inst| {1210 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1149 const branch = &self.branch_stack.items[0];1211 const branch = &self.branch_stack.items[0];
1150 const gop = try branch.inst_table.getOrPut(self.gpa, inst);1212 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
1151 if (!gop.found_existing) {1213 if (!gop.found_existing) {
1152 gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });1214 gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1215 }
1216 return gop.entry.value;
1153 }1217 }
1154 return gop.entry.value;
1155 }
11561218
1157 // Treat each stack item as a "layer" on top of the previous one.1219 // Treat each stack item as a "layer" on top of the previous one.
1158 var i: usize = self.branch_stack.items.len;1220 var i: usize = self.branch_stack.items.len;
1159 while (true) {1221 while (true) {
1160 i -= 1;1222 i -= 1;
1161 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {1223 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1162 return mcv;1224 assert(mcv != .dead);
1225 return mcv;
1226 }
1163 }1227 }
1164 }1228 }
1165 }
11661229
1167 fn copyToNewRegister(self: *Function, inst: *ir.Inst) !MCValue {1230 /// Does not "move" the instruction.
1168 return self.fail(inst.src, "TODO implement copyToNewRegister", .{});1231 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
1169 }1232 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1233 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
1234 try branch.inst_table.ensureCapacity(self.gpa, branch.inst_table.items().len + 1);
1235
1236 const free_index = @ctz(FreeRegInt, branch.free_registers);
1237 if (free_index >= callee_preserved_regs.len)
1238 return self.fail(inst.src, "TODO implement spilling register to stack", .{});
1239 branch.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
1240 const reg = callee_preserved_regs[free_index];
1241 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
1242 const old_mcv = branch.inst_table.get(inst).?;
1243 const new_mcv: MCValue = .{ .register = reg };
1244 try self.genSetReg(inst.src, reg, old_mcv);
1245 return new_mcv;
1246 }
11701247
1171 /// If the MCValue is an immediate, and it does not fit within this type,1248 /// If the MCValue is an immediate, and it does not fit within this type,
1172 /// we put it in a register.1249 /// we put it in a register.
1173 /// A potential opportunity for future optimization here would be keeping track1250 /// A potential opportunity for future optimization here would be keeping track
1174 /// of the fact that the instruction is available both as an immediate1251 /// of the fact that the instruction is available both as an immediate
1175 /// and as a register.1252 /// and as a register.
1176 fn limitImmediateType(self: *Function, inst: *ir.Inst, comptime T: type) !MCValue {1253 fn limitImmediateType(self: *Self, inst: *ir.Inst, comptime T: type) !MCValue {
1177 const mcv = try self.resolveInst(inst);1254 const mcv = try self.resolveInst(inst);
1178 const ti = @typeInfo(T).Int;1255 const ti = @typeInfo(T).Int;
1179 switch (mcv) {1256 switch (mcv) {
1180 .immediate => |imm| {1257 .immediate => |imm| {
1181 // This immediate is unsigned.1258 // This immediate is unsigned.
1182 const U = @Type(.{1259 const U = @Type(.{
1183 .Int = .{1260 .Int = .{
1184 .bits = ti.bits - @boolToInt(ti.is_signed),1261 .bits = ti.bits - @boolToInt(ti.is_signed),
1185 .is_signed = false,1262 .is_signed = false,
1186 },1263 },
1187 });1264 });
1188 if (imm >= std.math.maxInt(U)) {1265 if (imm >= std.math.maxInt(U)) {
1189 return self.copyToNewRegister(inst);1266 return self.copyToNewRegister(inst);
1190 }1267 }
1191 },1268 },
1192 else => {},1269 else => {},
1270 }
1271 return mcv;
1193 }1272 }
1194 return mcv;
1195 }
11961273
1197 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {1274 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {
1198 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1275 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1199 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1276 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1200 switch (typed_value.ty.zigTypeTag()) {1277 switch (typed_value.ty.zigTypeTag()) {
1201 .Pointer => {1278 .Pointer => {
1202 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {1279 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
1203 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];1280 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1204 const decl = payload.decl;1281 const decl = payload.decl;
1205 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;1282 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
1206 return MCValue{ .memory = got_addr };1283 return MCValue{ .memory = got_addr };
1207 }1284 }
1208 return self.fail(src, "TODO codegen more kinds of const pointers", .{});1285 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
1209 },1286 },
1210 .Int => {1287 .Int => {
1211 const info = typed_value.ty.intInfo(self.target.*);1288 const info = typed_value.ty.intInfo(self.target.*);
1212 if (info.bits > ptr_bits or info.signed) {1289 if (info.bits > ptr_bits or info.signed) {
1213 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});1290 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
1214 }1291 }
1215 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };1292 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
1216 },1293 },
1217 .Bool => {1294 .Bool => {
1218 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };1295 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1219 },1296 },
1220 .ComptimeInt => unreachable, // semantic analysis prevents this1297 .ComptimeInt => unreachable, // semantic analysis prevents this
1221 .ComptimeFloat => unreachable, // semantic analysis prevents this1298 .ComptimeFloat => unreachable, // semantic analysis prevents this
1222 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),1299 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
1300 }
1223 }1301 }
1224 }
12251302
1226 fn resolveParameters(1303 fn resolveParameters(
1227 self: *Function,1304 self: *Self,
1228 src: usize,1305 src: usize,
1229 cc: std.builtin.CallingConvention,1306 cc: std.builtin.CallingConvention,
1230 param_types: []const Type,1307 param_types: []const Type,
1231 results: []MCValue,1308 results: []MCValue,
1232 ) !u32 {1309 ) !u32 {
1233 switch (self.target.cpu.arch) {1310 switch (arch) {
1234 .x86_64 => {1311 .x86_64 => {
1235 switch (cc) {1312 switch (cc) {
1236 .Naked => {1313 .Naked => {
1237 assert(results.len == 0);1314 assert(results.len == 0);
1238 return 0;1315 return 0;
1239 },1316 },
1240 .Unspecified, .C => {1317 .Unspecified, .C => {
1241 var next_int_reg: usize = 0;1318 var next_int_reg: usize = 0;
1242 var next_stack_offset: u32 = 0;1319 var next_stack_offset: u32 = 0;
12431320
1244 const integer_registers = [_]Reg(.x86_64){ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };1321 for (param_types) |ty, i| {
1245 for (param_types) |ty, i| {1322 switch (ty.zigTypeTag()) {
1246 switch (ty.zigTypeTag()) {1323 .Bool, .Int => {
1247 .Bool, .Int => {1324 if (next_int_reg >= c_abi_int_param_regs.len) {
1248 if (next_int_reg >= integer_registers.len) {1325 results[i] = .{ .stack_offset = next_stack_offset };
1249 results[i] = .{ .stack_offset = next_stack_offset };1326 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
1250 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));1327 } else {
1251 } else {1328 results[i] = .{ .register = c_abi_int_param_regs[next_int_reg] };
1252 results[i] = .{ .register = @enumToInt(integer_registers[next_int_reg]) };1329 next_int_reg += 1;
1253 next_int_reg += 1;1330 }
1254 }1331 },
1255 },1332 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
1256 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),1333 }
1257 }1334 }
1258 }1335 return next_stack_offset;
1259 return next_stack_offset;1336 },
1260 },1337 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1261 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),1338 }
1262 }1339 },
1263 },1340 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),
1264 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),1341 }
1265 }1342 }
1266 }
12671343
1268 fn fail(self: *Function, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {1344 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
1269 @setCold(true);1345 @setCold(true);
1270 assert(self.err_msg == null);1346 assert(self.err_msg == null);
1271 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);1347 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
1272 return error.CodegenFail;1348 return error.CodegenFail;
1273 }1349 }
1274};
12751350
1276const x86_64 = @import("codegen/x86_64.zig");1351 usingnamespace switch (arch) {
1277const x86 = @import("codegen/x86.zig");1352 .i386 => @import("codegen/x86.zig"),
1353 .x86_64 => @import("codegen/x86_64.zig"),
1354 else => struct {
1355 pub const Register = enum {
1356 dummy,
12781357
1279fn Reg(comptime arch: Target.Cpu.Arch) type {1358 pub fn allocIndex(self: Register) ?u4 {
1280 return switch (arch) {1359 return null;
1281 .i386 => x86.Register,1360 }
1282 .x86_64 => x86_64.Register,1361 };
1283 else => @compileError("TODO add more register enums"),1362 pub const callee_preserved_regs = [_]Register{};
1284 };1363 },
1285}1364 };
12861365
1287fn parseRegName(comptime arch: Target.Cpu.Arch, name: []const u8) ?Reg(arch) {1366 /// An integer whose bits represent all the registers and whether they are free.
1288 return std.meta.stringToEnum(Reg(arch), name);1367 const FreeRegInt = @Type(.{ .Int = .{ .is_signed = false, .bits = callee_preserved_regs.len } });
1368
1369 fn parseRegName(name: []const u8) ?Register {
1370 return std.meta.stringToEnum(Register, name);
1371 }
1372 };
1289}1373}
src-self-hosted/codegen/c.zig+10-11
...@@ -92,9 +92,9 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -92,9 +92,9 @@ fn genFn(file: *C, decl: *Decl) !void {
92 for (instructions) |inst| {92 for (instructions) |inst| {
93 try writer.writeAll("\n\t");93 try writer.writeAll("\n\t");
94 switch (inst.tag) {94 switch (inst.tag) {
95 .assembly => try genAsm(file, inst.cast(Inst.Assembly).?, decl),95 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
96 .call => try genCall(file, inst.cast(Inst.Call).?, decl),96 .call => try genCall(file, inst.castTag(.call).?, decl),
97 .ret => try genRet(file, inst.cast(Inst.Ret).?, decl, tv.ty.fnReturnType()),97 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),
98 .retvoid => try file.main.writer().print("return;", .{}),98 .retvoid => try file.main.writer().print("return;", .{}),
99 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),99 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100 }100 }
...@@ -105,9 +105,9 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -105,9 +105,9 @@ fn genFn(file: *C, decl: *Decl) !void {
105 try writer.writeAll("}\n\n");105 try writer.writeAll("}\n\n");
106}106}
107107
108fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !void {108fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !void {
109 const writer = file.main.writer();109 const writer = file.main.writer();
110 const ret_value = inst.args.operand;110 const ret_value = inst.operand;
111 const value = ret_value.value().?;111 const value = ret_value.value().?;
112 if (expected_return_type.eql(ret_value.ty))112 if (expected_return_type.eql(ret_value.ty))
113 return file.fail(decl.src(), "TODO return {}", .{expected_return_type})113 return file.fail(decl.src(), "TODO return {}", .{expected_return_type})
...@@ -126,7 +126,7 @@ fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !v...@@ -126,7 +126,7 @@ fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !v
126fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {126fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
127 const writer = file.main.writer();127 const writer = file.main.writer();
128 const header = file.header.writer();128 const header = file.header.writer();
129 if (inst.args.func.cast(Inst.Constant)) |func_inst| {129 if (inst.func.castTag(.constant)) |func_inst| {
130 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {130 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
131 const target = func_val.func.owner_decl;131 const target = func_val.func.owner_decl;
132 const target_ty = target.typed_value.most_recent.typed_value.ty;132 const target_ty = target.typed_value.most_recent.typed_value.ty;
...@@ -144,7 +144,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {...@@ -144,7 +144,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
144 } else {144 } else {
145 return file.fail(decl.src(), "TODO non-function call target?", .{});145 return file.fail(decl.src(), "TODO non-function call target?", .{});
146 }146 }
147 if (inst.args.args.len != 0) {147 if (inst.args.len != 0) {
148 return file.fail(decl.src(), "TODO function arguments", .{});148 return file.fail(decl.src(), "TODO function arguments", .{});
149 }149 }
150 } else {150 } else {
...@@ -152,14 +152,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {...@@ -152,14 +152,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
152 }152 }
153}153}
154154
155fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {155fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
156 const as = inst.args;
157 const writer = file.main.writer();156 const writer = file.main.writer();
158 for (as.inputs) |i, index| {157 for (as.inputs) |i, index| {
159 if (i[0] == '{' and i[i.len - 1] == '}') {158 if (i[0] == '{' and i[i.len - 1] == '}') {
160 const reg = i[1 .. i.len - 1];159 const reg = i[1 .. i.len - 1];
161 const arg = as.args[index];160 const arg = as.args[index];
162 if (arg.cast(Inst.Constant)) |c| {161 if (arg.castTag(.constant)) |c| {
163 if (c.val.tag() == .int_u64) {162 if (c.val.tag() == .int_u64) {
164 try writer.writeAll("register ");163 try writer.writeAll("register ");
165 try renderType(file, writer, arg.ty, decl.src());164 try renderType(file, writer, arg.ty, decl.src());
...@@ -190,7 +189,7 @@ fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {...@@ -190,7 +189,7 @@ fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {
190 if (index > 0) {189 if (index > 0) {
191 try writer.writeAll(", ");190 try writer.writeAll(", ");
192 }191 }
193 if (arg.cast(Inst.Constant)) |c| {192 if (arg.castTag(.constant)) |c| {
194 try writer.print("\"\"({}_constant)", .{reg});193 try writer.print("\"\"({}_constant)", .{reg});
195 } else {194 } else {
196 // This is blocked by the earlier test195 // This is blocked by the earlier test
src-self-hosted/codegen/x86.zig+14
...@@ -25,6 +25,20 @@ pub const Register = enum(u8) {...@@ -25,6 +25,20 @@ pub const Register = enum(u8) {
25 pub fn id(self: @This()) u3 {25 pub fn id(self: @This()) u3 {
26 return @truncate(u3, @enumToInt(self));26 return @truncate(u3, @enumToInt(self));
27 }27 }
28
29 /// Returns the index into `callee_preserved_regs`.
30 pub fn allocIndex(self: Register) ?u4 {
31 return switch (self) {
32 .eax, .ax, .al => 0,
33 .ecx, .cx, .cl => 1,
34 .edx, .dx, .dl => 2,
35 .esi, .si => 3,
36 .edi, .di => 4,
37 else => null,
38 };
39 }
28};40};
2941
30// zig fmt: on42// zig fmt: on
43
44pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi };
src-self-hosted/codegen/x86_64.zig+24-4
...@@ -38,7 +38,7 @@ pub const Register = enum(u8) {...@@ -38,7 +38,7 @@ pub const Register = enum(u8) {
38 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,38 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
3939
40 /// Returns the bit-width of the register.40 /// Returns the bit-width of the register.
41 pub fn size(self: @This()) u7 {41 pub fn size(self: Register) u7 {
42 return switch (@enumToInt(self)) {42 return switch (@enumToInt(self)) {
43 0...15 => 64,43 0...15 => 64,
44 16...31 => 32,44 16...31 => 32,
...@@ -53,7 +53,7 @@ pub const Register = enum(u8) {...@@ -53,7 +53,7 @@ pub const Register = enum(u8) {
53 /// other variant of access to those registers, such as r8b, r15d, and so53 /// other variant of access to those registers, such as r8b, r15d, and so
54 /// on. This is needed because access to these registers requires special54 /// on. This is needed because access to these registers requires special
55 /// handling via the REX prefix, via the B or R bits, depending on context.55 /// handling via the REX prefix, via the B or R bits, depending on context.
56 pub fn isExtended(self: @This()) bool {56 pub fn isExtended(self: Register) bool {
57 return @enumToInt(self) & 0x08 != 0;57 return @enumToInt(self) & 0x08 != 0;
58 }58 }
5959
...@@ -62,9 +62,29 @@ pub const Register = enum(u8) {...@@ -62,9 +62,29 @@ pub const Register = enum(u8) {
62 /// an instruction (@see isExtended), and requires special handling. The62 /// an instruction (@see isExtended), and requires special handling. The
63 /// lower three bits are often embedded directly in instructions (such as63 /// lower three bits are often embedded directly in instructions (such as
64 /// the B8 variant of moves), or used in R/M bytes.64 /// the B8 variant of moves), or used in R/M bytes.
65 pub fn id(self: @This()) u4 {65 pub fn id(self: Register) u4 {
66 return @truncate(u4, @enumToInt(self));66 return @truncate(u4, @enumToInt(self));
67 }67 }
68
69 /// Returns the index into `callee_preserved_regs`.
70 pub fn allocIndex(self: Register) ?u4 {
71 return switch (self) {
72 .rax, .eax, .ax, .al => 0,
73 .rcx, .ecx, .cx, .cl => 1,
74 .rdx, .edx, .dx, .dl => 2,
75 .rsi, .esi, .si => 3,
76 .rdi, .edi, .di => 4,
77 .r8, .r8d, .r8w, .r8b => 5,
78 .r9, .r9d, .r9w, .r9b => 6,
79 .r10, .r10d, .r10w, .r10b => 7,
80 .r11, .r11d, .r11w, .r11b => 8,
81 else => null,
82 };
83 }
68};84};
6985
70// zig fmt: on
\ No newline at end of file
86// zig fmt: on
87
88/// These registers belong to the called function.
89pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
90pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
src-self-hosted/ir.zig+231-126
...@@ -38,7 +38,7 @@ pub const Inst = struct {...@@ -38,7 +38,7 @@ pub const Inst = struct {
3838
39 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {39 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
40 assert(index < deaths_bits);40 assert(index < deaths_bits);
41 return @truncate(u1, self.deaths << index) != 0;41 return @truncate(u1, self.deaths >> index) != 0;
42 }42 }
4343
44 pub fn specialOperandDeaths(self: Inst) bool {44 pub fn specialOperandDeaths(self: Inst) bool {
...@@ -55,7 +55,12 @@ pub const Inst = struct {...@@ -55,7 +55,12 @@ pub const Inst = struct {
55 breakpoint,55 breakpoint,
56 brvoid,56 brvoid,
57 call,57 call,
58 cmp,58 cmp_lt,
59 cmp_lte,
60 cmp_eq,
61 cmp_gte,
62 cmp_gt,
63 cmp_neq,
59 condbr,64 condbr,
60 constant,65 constant,
61 isnonnull,66 isnonnull,
...@@ -66,13 +71,80 @@ pub const Inst = struct {...@@ -66,13 +71,80 @@ pub const Inst = struct {
66 sub,71 sub,
67 unreach,72 unreach,
68 not,73 not,
74
75 /// There is one-to-one correspondence between tag and type for now,
76 /// but this will not always be the case. For example, binary operations
77 /// such as + and - will have different tags but the same type.
78 pub fn Type(tag: Tag) type {
79 return switch (tag) {
80 .retvoid,
81 .unreach,
82 .arg,
83 .breakpoint,
84 => NoOp,
85
86 .ret,
87 .bitcast,
88 .not,
89 .isnonnull,
90 .isnull,
91 .ptrtoint,
92 => UnOp,
93
94 .add,
95 .sub,
96 .cmp_lt,
97 .cmp_lte,
98 .cmp_eq,
99 .cmp_gte,
100 .cmp_gt,
101 .cmp_neq,
102 => BinOp,
103
104 .assembly => Assembly,
105 .block => Block,
106 .br => Br,
107 .brvoid => BrVoid,
108 .call => Call,
109 .condbr => CondBr,
110 .constant => Constant,
111 };
112 }
113
114 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
115 return switch (op) {
116 .lt => .cmp_lt,
117 .lte => .cmp_lte,
118 .eq => .cmp_eq,
119 .gte => .cmp_gte,
120 .gt => .cmp_gt,
121 .neq => .cmp_neq,
122 };
123 }
69 };124 };
70125
126 /// Prefer `castTag` to this.
71 pub fn cast(base: *Inst, comptime T: type) ?*T {127 pub fn cast(base: *Inst, comptime T: type) ?*T {
72 if (base.tag != T.base_tag)128 if (@hasField(T, "base_tag")) {
73 return null;129 return base.castTag(T.base_tag);
130 }
131 inline for (@typeInfo(Tag).Enum.fields) |field| {
132 const tag = @intToEnum(Tag, field.value);
133 if (base.tag == tag) {
134 if (T == tag.Type()) {
135 return @fieldParentPtr(T, "base", base);
136 }
137 return null;
138 }
139 }
140 unreachable;
141 }
74142
75 return @fieldParentPtr(T, "base", base);143 pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
144 if (base.tag == tag) {
145 return @fieldParentPtr(tag.Type(), "base", base);
146 }
147 return null;
76 }148 }
77149
78 pub fn Args(comptime T: type) type {150 pub fn Args(comptime T: type) type {
...@@ -88,186 +160,219 @@ pub const Inst = struct {...@@ -88,186 +160,219 @@ pub const Inst = struct {
88 return inst.val;160 return inst.val;
89 }161 }
90162
91 pub const Add = struct {163 pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
92 pub const base_tag = Tag.add;164 return switch (self.base.tag) {
165 .cmp_lt => .lt,
166 .cmp_lte => .lte,
167 .cmp_eq => .eq,
168 .cmp_gte => .gte,
169 .cmp_gt => .gt,
170 .cmp_neq => .neq,
171 else => null,
172 };
173 }
174
175 pub fn operandCount(base: *Inst) usize {
176 inline for (@typeInfo(Tag).Enum.fields) |field| {
177 const tag = @intToEnum(Tag, field.value);
178 if (tag == base.tag) {
179 return @fieldParentPtr(tag.Type(), "base", base).operandCount();
180 }
181 }
182 unreachable;
183 }
184
185 pub fn getOperand(base: *Inst, index: usize) ?*Inst {
186 inline for (@typeInfo(Tag).Enum.fields) |field| {
187 const tag = @intToEnum(Tag, field.value);
188 if (tag == base.tag) {
189 return @fieldParentPtr(tag.Type(), "base", base).getOperand(index);
190 }
191 }
192 unreachable;
193 }
194
195 pub const NoOp = struct {
93 base: Inst,196 base: Inst,
94197
95 args: struct {198 pub fn operandCount(self: *const NoOp) usize {
96 lhs: *Inst,199 return 0;
97 rhs: *Inst,200 }
98 },201 pub fn getOperand(self: *const NoOp, index: usize) ?*Inst {
202 return null;
203 }
99 };204 };
100205
101 pub const Arg = struct {206 pub const UnOp = struct {
102 pub const base_tag = Tag.arg;
103 base: Inst,207 base: Inst,
104 args: void,208 operand: *Inst,
209
210 pub fn operandCount(self: *const UnOp) usize {
211 return 1;
212 }
213 pub fn getOperand(self: *const UnOp, index: usize) ?*Inst {
214 if (index == 0)
215 return self.operand;
216 return null;
217 }
105 };218 };
106219
107 pub const Assembly = struct {220 pub const BinOp = struct {
108 pub const base_tag = Tag.assembly;
109 base: Inst,221 base: Inst,
222 lhs: *Inst,
223 rhs: *Inst,
110224
111 args: struct {225 pub fn operandCount(self: *const BinOp) usize {
112 asm_source: []const u8,226 return 2;
113 is_volatile: bool,227 }
114 output: ?[]const u8,228 pub fn getOperand(self: *const BinOp, index: usize) ?*Inst {
115 inputs: []const []const u8,229 var i = index;
116 clobbers: []const []const u8,230
117 args: []const *Inst,231 if (i < 1)
118 },232 return self.lhs;
233 i -= 1;
234
235 if (i < 1)
236 return self.rhs;
237 i -= 1;
238
239 return null;
240 }
119 };241 };
120242
121 pub const BitCast = struct {243 pub const Assembly = struct {
122 pub const base_tag = Tag.bitcast;244 pub const base_tag = Tag.assembly;
123245
124 base: Inst,246 base: Inst,
125 args: struct {247 asm_source: []const u8,
126 operand: *Inst,248 is_volatile: bool,
127 },249 output: ?[]const u8,
250 inputs: []const []const u8,
251 clobbers: []const []const u8,
252 args: []const *Inst,
253
254 pub fn operandCount(self: *const Assembly) usize {
255 return self.args.len;
256 }
257 pub fn getOperand(self: *const Assembly, index: usize) ?*Inst {
258 if (index < self.args.len)
259 return self.args[index];
260 return null;
261 }
128 };262 };
129263
130 pub const Block = struct {264 pub const Block = struct {
131 pub const base_tag = Tag.block;265 pub const base_tag = Tag.block;
266
132 base: Inst,267 base: Inst,
133 args: struct {268 body: Body,
134 body: Body,
135 },
136 /// This memory is reserved for codegen code to do whatever it needs to here.269 /// This memory is reserved for codegen code to do whatever it needs to here.
137 codegen: codegen.BlockData = .{},270 codegen: codegen.BlockData = .{},
271
272 pub fn operandCount(self: *const Block) usize {
273 return 0;
274 }
275 pub fn getOperand(self: *const Block, index: usize) ?*Inst {
276 return null;
277 }
138 };278 };
139279
140 pub const Br = struct {280 pub const Br = struct {
141 pub const base_tag = Tag.br;281 pub const base_tag = Tag.br;
142 base: Inst,
143 args: struct {
144 block: *Block,
145 operand: *Inst,
146 },
147 };
148282
149 pub const Breakpoint = struct {
150 pub const base_tag = Tag.breakpoint;
151 base: Inst,283 base: Inst,
152 args: void,284 block: *Block,
285 operand: *Inst,
286
287 pub fn operandCount(self: *const Br) usize {
288 return 0;
289 }
290 pub fn getOperand(self: *const Br, index: usize) ?*Inst {
291 if (index == 0)
292 return self.operand;
293 return null;
294 }
153 };295 };
154296
155 pub const BrVoid = struct {297 pub const BrVoid = struct {
156 pub const base_tag = Tag.brvoid;298 pub const base_tag = Tag.brvoid;
299
157 base: Inst,300 base: Inst,
158 args: struct {301 block: *Block,
159 block: *Block,302
160 },303 pub fn operandCount(self: *const BrVoid) usize {
304 return 0;
305 }
306 pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst {
307 return null;
308 }
161 };309 };
162310
163 pub const Call = struct {311 pub const Call = struct {
164 pub const base_tag = Tag.call;312 pub const base_tag = Tag.call;
313
165 base: Inst,314 base: Inst,
166 args: struct {315 func: *Inst,
167 func: *Inst,316 args: []const *Inst,
168 args: []const *Inst,
169 },
170 };
171317
172 pub const Cmp = struct {318 pub fn operandCount(self: *const Call) usize {
173 pub const base_tag = Tag.cmp;319 return self.args.len + 1;
320 }
321 pub fn getOperand(self: *const Call, index: usize) ?*Inst {
322 var i = index;
174323
175 base: Inst,324 if (i < 1)
176 args: struct {325 return self.func;
177 lhs: *Inst,326 i -= 1;
178 op: std.math.CompareOperator,327
179 rhs: *Inst,328 if (i < self.args.len)
180 },329 return self.args[i];
330 i -= self.args.len;
331
332 return null;
333 }
181 };334 };
182335
183 pub const CondBr = struct {336 pub const CondBr = struct {
184 pub const base_tag = Tag.condbr;337 pub const base_tag = Tag.condbr;
185338
186 base: Inst,339 base: Inst,
187 args: struct {340 condition: *Inst,
188 condition: *Inst,341 then_body: Body,
189 true_body: Body,342 else_body: Body,
190 false_body: Body,
191 },
192 /// Set of instructions whose lifetimes end at the start of one of the branches.343 /// Set of instructions whose lifetimes end at the start of one of the branches.
193 /// The `true` branch is first: `deaths[0..true_death_count]`.344 /// The `true` branch is first: `deaths[0..true_death_count]`.
194 /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`.345 /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`.
195 deaths: [*]*Inst = undefined,346 deaths: [*]*Inst = undefined,
196 true_death_count: u32 = 0,347 true_death_count: u32 = 0,
197 false_death_count: u32 = 0,348 false_death_count: u32 = 0,
198 };
199349
200 pub const Not = struct {350 pub fn operandCount(self: *const CondBr) usize {
201 pub const base_tag = Tag.not;351 return 1;
352 }
353 pub fn getOperand(self: *const CondBr, index: usize) ?*Inst {
354 var i = index;
202355
203 base: Inst,356 if (i < 1)
204 args: struct {357 return self.condition;
205 operand: *Inst,358 i -= 1;
206 },359
360 return null;
361 }
207 };362 };
208363
209 pub const Constant = struct {364 pub const Constant = struct {
210 pub const base_tag = Tag.constant;365 pub const base_tag = Tag.constant;
211 base: Inst,
212
213 val: Value,
214 };
215
216 pub const IsNonNull = struct {
217 pub const base_tag = Tag.isnonnull;
218
219 base: Inst,
220 args: struct {
221 operand: *Inst,
222 },
223 };
224
225 pub const IsNull = struct {
226 pub const base_tag = Tag.isnull;
227366
228 base: Inst,367 base: Inst,
229 args: struct {368 val: Value,
230 operand: *Inst,
231 },
232 };
233
234 pub const PtrToInt = struct {
235 pub const base_tag = Tag.ptrtoint;
236
237 base: Inst,
238 args: struct {
239 ptr: *Inst,
240 },
241 };
242
243 pub const Ret = struct {
244 pub const base_tag = Tag.ret;
245 base: Inst,
246 args: struct {
247 operand: *Inst,
248 },
249 };
250
251 pub const RetVoid = struct {
252 pub const base_tag = Tag.retvoid;
253 base: Inst,
254 args: void,
255 };
256
257 pub const Sub = struct {
258 pub const base_tag = Tag.sub;
259 base: Inst,
260
261 args: struct {
262 lhs: *Inst,
263 rhs: *Inst,
264 },
265 };
266369
267 pub const Unreach = struct {370 pub fn operandCount(self: *const Constant) usize {
268 pub const base_tag = Tag.unreach;371 return 0;
269 base: Inst,372 }
270 args: void,373 pub fn getOperand(self: *const Constant, index: usize) ?*Inst {
374 return null;
375 }
271 };376 };
272};377};
273378
src-self-hosted/liveness.zig+25-65
...@@ -25,53 +25,38 @@ fn analyzeWithTable(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst,...@@ -25,53 +25,38 @@ fn analyzeWithTable(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst,
25 while (i != 0) {25 while (i != 0) {
26 i -= 1;26 i -= 1;
27 const base = body.instructions[i];27 const base = body.instructions[i];
28 try analyzeInstGeneric(arena, table, base);28 try analyzeInst(arena, table, base);
29 }29 }
30}30}
3131
32fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void {32fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void {
33 // Obtain the corresponding instruction type based on the tag type.33 if (table.contains(base)) {
34 inline for (std.meta.declarations(ir.Inst)) |decl| {34 base.deaths = 0;
35 switch (decl.data) {
36 .Type => |T| {
37 if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) {
38 if (T.base_tag == base.tag) {
39 return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base));
40 }
41 }
42 },
43 else => {},
44 }
45 }
46 unreachable;
47}
48
49fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void {
50 if (table.contains(&inst.base)) {
51 inst.base.deaths = 0;
52 } else {35 } else {
53 // No tombstone for this instruction means it is never referenced,36 // No tombstone for this instruction means it is never referenced,
54 // and its birth marks its own death. Very metal 🤘37 // and its birth marks its own death. Very metal 🤘
55 inst.base.deaths = 1 << ir.Inst.unreferenced_bit_index;38 base.deaths = 1 << ir.Inst.unreferenced_bit_index;
56 }39 }
5740
58 switch (T) {41 switch (base.tag) {
59 ir.Inst.Constant => return,42 .constant => return,
60 ir.Inst.Block => {43 .block => {
61 try analyzeWithTable(arena, table, inst.args.body);44 const inst = base.castTag(.block).?;
45 try analyzeWithTable(arena, table, inst.body);
62 // We let this continue so that it can possibly mark the block as46 // We let this continue so that it can possibly mark the block as
63 // unreferenced below.47 // unreferenced below.
64 },48 },
65 ir.Inst.CondBr => {49 .condbr => {
50 const inst = base.castTag(.condbr).?;
66 var true_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);51 var true_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
67 defer true_table.deinit();52 defer true_table.deinit();
68 try true_table.ensureCapacity(inst.args.true_body.instructions.len);53 try true_table.ensureCapacity(inst.then_body.instructions.len);
69 try analyzeWithTable(arena, &true_table, inst.args.true_body);54 try analyzeWithTable(arena, &true_table, inst.then_body);
7055
71 var false_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);56 var false_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
72 defer false_table.deinit();57 defer false_table.deinit();
73 try false_table.ensureCapacity(inst.args.false_body.instructions.len);58 try false_table.ensureCapacity(inst.else_body.instructions.len);
74 try analyzeWithTable(arena, &false_table, inst.args.false_body);59 try analyzeWithTable(arena, &false_table, inst.else_body);
7560
76 // Each death that occurs inside one branch, but not the other, needs61 // Each death that occurs inside one branch, but not the other, needs
77 // to be added as a death immediately upon entering the other branch.62 // to be added as a death immediately upon entering the other branch.
...@@ -112,47 +97,22 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void...@@ -112,47 +97,22 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
112 // instruction, and the deaths flag for the CondBr instruction will indicate whether the97 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
113 // condition's lifetime ends immediately before entering any branch.98 // condition's lifetime ends immediately before entering any branch.
114 },99 },
115 ir.Inst.Call => {
116 // Call instructions have a runtime-known number of operands so we have to handle them ourselves here.
117 const needed_bits = 1 + inst.args.args.len;
118 if (needed_bits <= ir.Inst.deaths_bits) {
119 var bit_i: ir.Inst.DeathsBitIndex = 0;
120 {
121 const prev = try table.fetchPut(inst.args.func, {});
122 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
123 bit_i += 1;
124 }
125 for (inst.args.args) |arg| {
126 const prev = try table.fetchPut(arg, {});
127 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
128 bit_i += 1;
129 }
130 } else {
131 @panic("Handle liveness analysis for function calls with many parameters");
132 }
133 },
134 else => {},100 else => {},
135 }101 }
136102
137 const Args = ir.Inst.Args(T);103 const needed_bits = base.operandCount();
138 if (Args == void) {104 if (needed_bits <= ir.Inst.deaths_bits) {
139 return;105 var bit_i: ir.Inst.DeathsBitIndex = 0;
140 }106 while (base.getOperand(bit_i)) |operand| : (bit_i += 1) {
141107 const prev = try table.fetchPut(operand, {});
142 comptime var arg_index: usize = 0;
143 inline for (std.meta.fields(Args)) |field| {
144 if (field.field_type == *ir.Inst) {
145 if (arg_index >= 6) {
146 @compileError("out of bits to mark deaths of operands");
147 }
148 const prev = try table.fetchPut(@field(inst.args, field.name), {});
149 if (prev == null) {108 if (prev == null) {
150 // Death.109 // Death.
151 inst.base.deaths |= 1 << arg_index;110 base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
152 }111 }
153 arg_index += 1;
154 }112 }
113 } else {
114 @panic("Handle liveness analysis for instructions with many parameters");
155 }115 }
156116
157 std.log.debug(.liveness, "analyze {}: 0b{b}\n", .{ inst.base.tag, inst.base.deaths });117 std.log.debug(.liveness, "analyze {}: 0b{b}\n", .{ base.tag, base.deaths });
158}118}
src-self-hosted/zir.zig+142-189
...@@ -337,7 +337,7 @@ pub const Inst = struct {...@@ -337,7 +337,7 @@ pub const Inst = struct {
337 base: Inst,337 base: Inst,
338338
339 positionals: struct {339 positionals: struct {
340 ptr: *Inst,340 operand: *Inst,
341 },341 },
342 kw_args: struct {},342 kw_args: struct {},
343 };343 };
...@@ -629,8 +629,8 @@ pub const Inst = struct {...@@ -629,8 +629,8 @@ pub const Inst = struct {
629629
630 positionals: struct {630 positionals: struct {
631 condition: *Inst,631 condition: *Inst,
632 true_body: Module.Body,632 then_body: Module.Body,
633 false_body: Module.Body,633 else_body: Module.Body,
634 },634 },
635 kw_args: struct {},635 kw_args: struct {},
636 };636 };
...@@ -1615,7 +1615,7 @@ const EmitZIR = struct {...@@ -1615,7 +1615,7 @@ const EmitZIR = struct {
1615 }1615 }
1616 }1616 }
16171617
1618 fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {1618 fn emitNoOp(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
1619 const new_inst = try self.arena.allocator.create(T);1619 const new_inst = try self.arena.allocator.create(T);
1620 new_inst.* = .{1620 new_inst.* = .{
1621 .base = .{1621 .base = .{
...@@ -1628,6 +1628,72 @@ const EmitZIR = struct {...@@ -1628,6 +1628,72 @@ const EmitZIR = struct {
1628 return &new_inst.base;1628 return &new_inst.base;
1629 }1629 }
16301630
1631 fn emitCmp(
1632 self: *EmitZIR,
1633 src: usize,
1634 new_body: ZirBody,
1635 old_inst: *ir.Inst.BinOp,
1636 op: std.math.CompareOperator,
1637 ) Allocator.Error!*Inst {
1638 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1639 new_inst.* = .{
1640 .base = .{
1641 .src = src,
1642 .tag = Inst.Cmp.base_tag,
1643 },
1644 .positionals = .{
1645 .lhs = try self.resolveInst(new_body, old_inst.lhs),
1646 .rhs = try self.resolveInst(new_body, old_inst.rhs),
1647 .op = op,
1648 },
1649 .kw_args = .{},
1650 };
1651 return &new_inst.base;
1652 }
1653
1654 fn emitUnOp(
1655 self: *EmitZIR,
1656 src: usize,
1657 new_body: ZirBody,
1658 old_inst: *ir.Inst.UnOp,
1659 comptime I: type,
1660 ) Allocator.Error!*Inst {
1661 const new_inst = try self.arena.allocator.create(I);
1662 new_inst.* = .{
1663 .base = .{
1664 .src = src,
1665 .tag = I.base_tag,
1666 },
1667 .positionals = .{
1668 .operand = try self.resolveInst(new_body, old_inst.operand),
1669 },
1670 .kw_args = .{},
1671 };
1672 return &new_inst.base;
1673 }
1674
1675 fn emitBinOp(
1676 self: *EmitZIR,
1677 src: usize,
1678 new_body: ZirBody,
1679 old_inst: *ir.Inst.BinOp,
1680 comptime I: type,
1681 ) Allocator.Error!*Inst {
1682 const new_inst = try self.arena.allocator.create(I);
1683 new_inst.* = .{
1684 .base = .{
1685 .src = src,
1686 .tag = I.base_tag,
1687 },
1688 .positionals = .{
1689 .lhs = try self.resolveInst(new_body, old_inst.lhs),
1690 .rhs = try self.resolveInst(new_body, old_inst.rhs),
1691 },
1692 .kw_args = .{},
1693 };
1694 return &new_inst.base;
1695 }
1696
1631 fn emitBody(1697 fn emitBody(
1632 self: *EmitZIR,1698 self: *EmitZIR,
1633 body: ir.Body,1699 body: ir.Body,
...@@ -1640,69 +1706,48 @@ const EmitZIR = struct {...@@ -1640,69 +1706,48 @@ const EmitZIR = struct {
1640 };1706 };
1641 for (body.instructions) |inst| {1707 for (body.instructions) |inst| {
1642 const new_inst = switch (inst.tag) {1708 const new_inst = switch (inst.tag) {
1643 .not => blk: {1709 .constant => unreachable, // excluded from function bodies
1644 const old_inst = inst.cast(ir.Inst.Not).?;1710
1645 assert(inst.ty.zigTypeTag() == .Bool);1711 .arg => try self.emitNoOp(inst.src, Inst.Arg),
1646 const new_inst = try self.arena.allocator.create(Inst.BoolNot);1712 .breakpoint => try self.emitNoOp(inst.src, Inst.Breakpoint),
1647 new_inst.* = .{1713 .unreach => try self.emitNoOp(inst.src, Inst.Unreachable),
1648 .base = .{1714 .retvoid => try self.emitNoOp(inst.src, Inst.ReturnVoid),
1649 .src = inst.src,1715
1650 .tag = Inst.BoolNot.base_tag,1716 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, Inst.BoolNot),
1651 },1717 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, Inst.Return),
1652 .positionals = .{1718 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, Inst.PtrToInt),
1653 .operand = try self.resolveInst(new_body, old_inst.args.operand),1719 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, Inst.IsNull),
1654 },1720 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, Inst.IsNonNull),
1655 .kw_args = .{},1721
1656 };1722 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, Inst.Add),
1657 break :blk &new_inst.base;1723 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, Inst.Sub),
1658 },1724
1659 .add => blk: {1725 .cmp_lt => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_lt).?, .lt),
1660 const old_inst = inst.cast(ir.Inst.Add).?;1726 .cmp_lte => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_lte).?, .lte),
1661 const new_inst = try self.arena.allocator.create(Inst.Add);1727 .cmp_eq => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_eq).?, .eq),
1662 new_inst.* = .{1728 .cmp_gte => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_gte).?, .gte),
1663 .base = .{1729 .cmp_gt => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_gt).?, .gt),
1664 .src = inst.src,1730 .cmp_neq => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_neq).?, .neq),
1665 .tag = Inst.Add.base_tag,1731
1666 },1732 .bitcast => blk: {
1667 .positionals = .{1733 const old_inst = inst.castTag(.bitcast).?;
1668 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),1734 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1669 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1670 },
1671 .kw_args = .{},
1672 };
1673 break :blk &new_inst.base;
1674 },
1675 .sub => blk: {
1676 const old_inst = inst.cast(ir.Inst.Sub).?;
1677 const new_inst = try self.arena.allocator.create(Inst.Sub);
1678 new_inst.* = .{1735 new_inst.* = .{
1679 .base = .{1736 .base = .{
1680 .src = inst.src,1737 .src = inst.src,
1681 .tag = Inst.Sub.base_tag,1738 .tag = Inst.BitCast.base_tag,
1682 },1739 },
1683 .positionals = .{1740 .positionals = .{
1684 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),1741 .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
1685 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),1742 .operand = try self.resolveInst(new_body, old_inst.operand),
1686 },
1687 .kw_args = .{},
1688 };
1689 break :blk &new_inst.base;
1690 },
1691 .arg => blk: {
1692 const old_inst = inst.cast(ir.Inst.Arg).?;
1693 const new_inst = try self.arena.allocator.create(Inst.Arg);
1694 new_inst.* = .{
1695 .base = .{
1696 .src = inst.src,
1697 .tag = Inst.Arg.base_tag,
1698 },1743 },
1699 .positionals = .{},
1700 .kw_args = .{},1744 .kw_args = .{},
1701 };1745 };
1702 break :blk &new_inst.base;1746 break :blk &new_inst.base;
1703 },1747 },
1748
1704 .block => blk: {1749 .block => blk: {
1705 const old_inst = inst.cast(ir.Inst.Block).?;1750 const old_inst = inst.castTag(.block).?;
1706 const new_inst = try self.arena.allocator.create(Inst.Block);1751 const new_inst = try self.arena.allocator.create(Inst.Block);
17071752
1708 try self.block_table.put(old_inst, new_inst);1753 try self.block_table.put(old_inst, new_inst);
...@@ -1710,7 +1755,7 @@ const EmitZIR = struct {...@@ -1710,7 +1755,7 @@ const EmitZIR = struct {
1710 var block_body = std.ArrayList(*Inst).init(self.allocator);1755 var block_body = std.ArrayList(*Inst).init(self.allocator);
1711 defer block_body.deinit();1756 defer block_body.deinit();
17121757
1713 try self.emitBody(old_inst.args.body, inst_table, &block_body);1758 try self.emitBody(old_inst.body, inst_table, &block_body);
17141759
1715 new_inst.* = .{1760 new_inst.* = .{
1716 .base = .{1761 .base = .{
...@@ -1725,47 +1770,49 @@ const EmitZIR = struct {...@@ -1725,47 +1770,49 @@ const EmitZIR = struct {
17251770
1726 break :blk &new_inst.base;1771 break :blk &new_inst.base;
1727 },1772 },
1728 .br => blk: {1773
1729 const old_inst = inst.cast(ir.Inst.Br).?;1774 .brvoid => blk: {
1730 const new_block = self.block_table.get(old_inst.args.block).?;1775 const old_inst = inst.cast(ir.Inst.BrVoid).?;
1731 const new_inst = try self.arena.allocator.create(Inst.Break);1776 const new_block = self.block_table.get(old_inst.block).?;
1777 const new_inst = try self.arena.allocator.create(Inst.BreakVoid);
1732 new_inst.* = .{1778 new_inst.* = .{
1733 .base = .{1779 .base = .{
1734 .src = inst.src,1780 .src = inst.src,
1735 .tag = Inst.Break.base_tag,1781 .tag = Inst.BreakVoid.base_tag,
1736 },1782 },
1737 .positionals = .{1783 .positionals = .{
1738 .block = new_block,1784 .block = new_block,
1739 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1740 },1785 },
1741 .kw_args = .{},1786 .kw_args = .{},
1742 };1787 };
1743 break :blk &new_inst.base;1788 break :blk &new_inst.base;
1744 },1789 },
1745 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),1790
1746 .brvoid => blk: {1791 .br => blk: {
1747 const old_inst = inst.cast(ir.Inst.BrVoid).?;1792 const old_inst = inst.castTag(.br).?;
1748 const new_block = self.block_table.get(old_inst.args.block).?;1793 const new_block = self.block_table.get(old_inst.block).?;
1749 const new_inst = try self.arena.allocator.create(Inst.BreakVoid);1794 const new_inst = try self.arena.allocator.create(Inst.Break);
1750 new_inst.* = .{1795 new_inst.* = .{
1751 .base = .{1796 .base = .{
1752 .src = inst.src,1797 .src = inst.src,
1753 .tag = Inst.BreakVoid.base_tag,1798 .tag = Inst.Break.base_tag,
1754 },1799 },
1755 .positionals = .{1800 .positionals = .{
1756 .block = new_block,1801 .block = new_block,
1802 .operand = try self.resolveInst(new_body, old_inst.operand),
1757 },1803 },
1758 .kw_args = .{},1804 .kw_args = .{},
1759 };1805 };
1760 break :blk &new_inst.base;1806 break :blk &new_inst.base;
1761 },1807 },
1808
1762 .call => blk: {1809 .call => blk: {
1763 const old_inst = inst.cast(ir.Inst.Call).?;1810 const old_inst = inst.castTag(.call).?;
1764 const new_inst = try self.arena.allocator.create(Inst.Call);1811 const new_inst = try self.arena.allocator.create(Inst.Call);
17651812
1766 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);1813 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len);
1767 for (args) |*elem, i| {1814 for (args) |*elem, i| {
1768 elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);1815 elem.* = try self.resolveInst(new_body, old_inst.args[i]);
1769 }1816 }
1770 new_inst.* = .{1817 new_inst.* = .{
1771 .base = .{1818 .base = .{
...@@ -1773,48 +1820,31 @@ const EmitZIR = struct {...@@ -1773,48 +1820,31 @@ const EmitZIR = struct {
1773 .tag = Inst.Call.base_tag,1820 .tag = Inst.Call.base_tag,
1774 },1821 },
1775 .positionals = .{1822 .positionals = .{
1776 .func = try self.resolveInst(new_body, old_inst.args.func),1823 .func = try self.resolveInst(new_body, old_inst.func),
1777 .args = args,1824 .args = args,
1778 },1825 },
1779 .kw_args = .{},1826 .kw_args = .{},
1780 };1827 };
1781 break :blk &new_inst.base;1828 break :blk &new_inst.base;
1782 },1829 },
1783 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),1830
1784 .ret => blk: {
1785 const old_inst = inst.cast(ir.Inst.Ret).?;
1786 const new_inst = try self.arena.allocator.create(Inst.Return);
1787 new_inst.* = .{
1788 .base = .{
1789 .src = inst.src,
1790 .tag = Inst.Return.base_tag,
1791 },
1792 .positionals = .{
1793 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1794 },
1795 .kw_args = .{},
1796 };
1797 break :blk &new_inst.base;
1798 },
1799 .retvoid => try self.emitTrivial(inst.src, Inst.ReturnVoid),
1800 .constant => unreachable, // excluded from function bodies
1801 .assembly => blk: {1831 .assembly => blk: {
1802 const old_inst = inst.cast(ir.Inst.Assembly).?;1832 const old_inst = inst.castTag(.assembly).?;
1803 const new_inst = try self.arena.allocator.create(Inst.Asm);1833 const new_inst = try self.arena.allocator.create(Inst.Asm);
18041834
1805 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);1835 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.inputs.len);
1806 for (inputs) |*elem, i| {1836 for (inputs) |*elem, i| {
1807 elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.inputs[i])).inst;1837 elem.* = (try self.emitStringLiteral(inst.src, old_inst.inputs[i])).inst;
1808 }1838 }
18091839
1810 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);1840 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.clobbers.len);
1811 for (clobbers) |*elem, i| {1841 for (clobbers) |*elem, i| {
1812 elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i])).inst;1842 elem.* = (try self.emitStringLiteral(inst.src, old_inst.clobbers[i])).inst;
1813 }1843 }
18141844
1815 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);1845 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len);
1816 for (args) |*elem, i| {1846 for (args) |*elem, i| {
1817 elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);1847 elem.* = try self.resolveInst(new_body, old_inst.args[i]);
1818 }1848 }
18191849
1820 new_inst.* = .{1850 new_inst.* = .{
...@@ -1823,12 +1853,12 @@ const EmitZIR = struct {...@@ -1823,12 +1853,12 @@ const EmitZIR = struct {
1823 .tag = Inst.Asm.base_tag,1853 .tag = Inst.Asm.base_tag,
1824 },1854 },
1825 .positionals = .{1855 .positionals = .{
1826 .asm_source = (try self.emitStringLiteral(inst.src, old_inst.args.asm_source)).inst,1856 .asm_source = (try self.emitStringLiteral(inst.src, old_inst.asm_source)).inst,
1827 .return_type = (try self.emitType(inst.src, inst.ty)).inst,1857 .return_type = (try self.emitType(inst.src, inst.ty)).inst,
1828 },1858 },
1829 .kw_args = .{1859 .kw_args = .{
1830 .@"volatile" = old_inst.args.is_volatile,1860 .@"volatile" = old_inst.is_volatile,
1831 .output = if (old_inst.args.output) |o|1861 .output = if (old_inst.output) |o|
1832 (try self.emitStringLiteral(inst.src, o)).inst1862 (try self.emitStringLiteral(inst.src, o)).inst
1833 else1863 else
1834 null,1864 null,
...@@ -1839,65 +1869,18 @@ const EmitZIR = struct {...@@ -1839,65 +1869,18 @@ const EmitZIR = struct {
1839 };1869 };
1840 break :blk &new_inst.base;1870 break :blk &new_inst.base;
1841 },1871 },
1842 .ptrtoint => blk: {1872
1843 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
1844 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
1845 new_inst.* = .{
1846 .base = .{
1847 .src = inst.src,
1848 .tag = Inst.PtrToInt.base_tag,
1849 },
1850 .positionals = .{
1851 .ptr = try self.resolveInst(new_body, old_inst.args.ptr),
1852 },
1853 .kw_args = .{},
1854 };
1855 break :blk &new_inst.base;
1856 },
1857 .bitcast => blk: {
1858 const old_inst = inst.cast(ir.Inst.BitCast).?;
1859 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1860 new_inst.* = .{
1861 .base = .{
1862 .src = inst.src,
1863 .tag = Inst.BitCast.base_tag,
1864 },
1865 .positionals = .{
1866 .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
1867 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1868 },
1869 .kw_args = .{},
1870 };
1871 break :blk &new_inst.base;
1872 },
1873 .cmp => blk: {
1874 const old_inst = inst.cast(ir.Inst.Cmp).?;
1875 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1876 new_inst.* = .{
1877 .base = .{
1878 .src = inst.src,
1879 .tag = Inst.Cmp.base_tag,
1880 },
1881 .positionals = .{
1882 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1883 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1884 .op = old_inst.args.op,
1885 },
1886 .kw_args = .{},
1887 };
1888 break :blk &new_inst.base;
1889 },
1890 .condbr => blk: {1873 .condbr => blk: {
1891 const old_inst = inst.cast(ir.Inst.CondBr).?;1874 const old_inst = inst.castTag(.condbr).?;
18921875
1893 var true_body = std.ArrayList(*Inst).init(self.allocator);1876 var then_body = std.ArrayList(*Inst).init(self.allocator);
1894 var false_body = std.ArrayList(*Inst).init(self.allocator);1877 var else_body = std.ArrayList(*Inst).init(self.allocator);
18951878
1896 defer true_body.deinit();1879 defer then_body.deinit();
1897 defer false_body.deinit();1880 defer else_body.deinit();
18981881
1899 try self.emitBody(old_inst.args.true_body, inst_table, &true_body);1882 try self.emitBody(old_inst.then_body, inst_table, &then_body);
1900 try self.emitBody(old_inst.args.false_body, inst_table, &false_body);1883 try self.emitBody(old_inst.else_body, inst_table, &else_body);
19011884
1902 const new_inst = try self.arena.allocator.create(Inst.CondBr);1885 const new_inst = try self.arena.allocator.create(Inst.CondBr);
1903 new_inst.* = .{1886 new_inst.* = .{
...@@ -1906,39 +1889,9 @@ const EmitZIR = struct {...@@ -1906,39 +1889,9 @@ const EmitZIR = struct {
1906 .tag = Inst.CondBr.base_tag,1889 .tag = Inst.CondBr.base_tag,
1907 },1890 },
1908 .positionals = .{1891 .positionals = .{
1909 .condition = try self.resolveInst(new_body, old_inst.args.condition),1892 .condition = try self.resolveInst(new_body, old_inst.condition),
1910 .true_body = .{ .instructions = true_body.toOwnedSlice() },1893 .then_body = .{ .instructions = then_body.toOwnedSlice() },
1911 .false_body = .{ .instructions = false_body.toOwnedSlice() },1894 .else_body = .{ .instructions = else_body.toOwnedSlice() },
1912 },
1913 .kw_args = .{},
1914 };
1915 break :blk &new_inst.base;
1916 },
1917 .isnull => blk: {
1918 const old_inst = inst.cast(ir.Inst.IsNull).?;
1919 const new_inst = try self.arena.allocator.create(Inst.IsNull);
1920 new_inst.* = .{
1921 .base = .{
1922 .src = inst.src,
1923 .tag = Inst.IsNull.base_tag,
1924 },
1925 .positionals = .{
1926 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1927 },
1928 .kw_args = .{},
1929 };
1930 break :blk &new_inst.base;
1931 },
1932 .isnonnull => blk: {
1933 const old_inst = inst.cast(ir.Inst.IsNonNull).?;
1934 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
1935 new_inst.* = .{
1936 .base = .{
1937 .src = inst.src,
1938 .tag = Inst.IsNonNull.base_tag,
1939 },
1940 .positionals = .{
1941 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1942 },1895 },
1943 .kw_args = .{},1896 .kw_args = .{},
1944 };1897 };
test/stage2/compare_output.zig+83-5
...@@ -169,9 +169,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -169,9 +169,8 @@ pub fn addCases(ctx: *TestContext) !void {
169 ,169 ,
170 "",170 "",
171 );171 );
172 }172
173 {173 // Tests the assert() function.
174 var case = ctx.exe("assert function", linux_x64);
175 case.addCompareOutput(174 case.addCompareOutput(
176 \\export fn _start() noreturn {175 \\export fn _start() noreturn {
177 \\ add(3, 4);176 \\ add(3, 4);
...@@ -199,15 +198,94 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -199,15 +198,94 @@ pub fn addCases(ctx: *TestContext) !void {
199 ,198 ,
200 "",199 "",
201 );200 );
201
202 // Tests copying a register. For the `c = a + b`, it has to
203 // preserve both a and b, because they are both used later.
202 case.addCompareOutput(204 case.addCompareOutput(
203 \\export fn _start() noreturn {205 \\export fn _start() noreturn {
204 \\ add(100, 200);206 \\ add(3, 4);
207 \\
208 \\ exit();
209 \\}
210 \\
211 \\fn add(a: u32, b: u32) void {
212 \\ const c = a + b; // 7
213 \\ const d = a + c; // 10
214 \\ const e = d + b; // 14
215 \\ assert(e == 14);
216 \\}
217 \\
218 \\pub fn assert(ok: bool) void {
219 \\ if (!ok) unreachable; // assertion failure
220 \\}
221 \\
222 \\fn exit() noreturn {
223 \\ asm volatile ("syscall"
224 \\ :
225 \\ : [number] "{rax}" (231),
226 \\ [arg1] "{rdi}" (0)
227 \\ : "rcx", "r11", "memory"
228 \\ );
229 \\ unreachable;
230 \\}
231 ,
232 "",
233 );
234
235 // More stress on the liveness detection.
236 case.addCompareOutput(
237 \\export fn _start() noreturn {
238 \\ add(3, 4);
239 \\
240 \\ exit();
241 \\}
242 \\
243 \\fn add(a: u32, b: u32) void {
244 \\ const c = a + b; // 7
245 \\ const d = a + c; // 10
246 \\ const e = d + b; // 14
247 \\ const f = d + e; // 24
248 \\ const g = e + f; // 38
249 \\ const h = f + g; // 62
250 \\ const i = g + h; // 100
251 \\ assert(i == 100);
252 \\}
253 \\
254 \\pub fn assert(ok: bool) void {
255 \\ if (!ok) unreachable; // assertion failure
256 \\}
257 \\
258 \\fn exit() noreturn {
259 \\ asm volatile ("syscall"
260 \\ :
261 \\ : [number] "{rax}" (231),
262 \\ [arg1] "{rdi}" (0)
263 \\ : "rcx", "r11", "memory"
264 \\ );
265 \\ unreachable;
266 \\}
267 ,
268 "",
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);
205 \\275 \\
206 \\ exit();276 \\ exit();
207 \\}277 \\}
208 \\278 \\
209 \\fn add(a: u32, b: u32) void {279 \\fn add(a: u32, b: u32) void {
210 \\ assert(a + b == 300);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);
211 \\}289 \\}
212 \\290 \\
213 \\pub fn assert(ok: bool) void {291 \\pub fn assert(ok: bool) void {