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 {
13491349fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
13501350 try self.analyzeBody(&block_scope.base, body);
13511351 for (block_scope.instructions.items) |inst| {
1352 if (inst.cast(Inst.Ret)) |ret| {
1353 const val = try self.resolveConstValue(&block_scope.base, ret.args.operand);
1352 if (inst.castTag(.ret)) |ret| {
1353 const val = try self.resolveConstValue(&block_scope.base, ret.operand);
13541354 return val.toType();
13551355 } else {
13561356 return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
......@@ -1938,16 +1938,132 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
19381938 };
19391939}
19401940
1941fn addNewInstArgs(
1941fn addNoOp(
19421942 self: *Module,
19431943 block: *Scope.Block,
19441944 src: usize,
19451945 ty: Type,
1946 comptime T: type,
1947 args: Inst.Args(T),
1946 comptime tag: Inst.Tag,
1947) !*Inst {
1948 const inst = try block.arena.create(tag.Type());
1949 inst.* = .{
1950 .base = .{
1951 .tag = tag,
1952 .ty = ty,
1953 .src = src,
1954 },
1955 };
1956 try block.instructions.append(self.gpa, &inst.base);
1957 return &inst.base;
1958}
1959
1960fn addUnOp(
1961 self: *Module,
1962 block: *Scope.Block,
1963 src: usize,
1964 ty: Type,
1965 tag: Inst.Tag,
1966 operand: *Inst,
1967) !*Inst {
1968 const inst = try block.arena.create(Inst.UnOp);
1969 inst.* = .{
1970 .base = .{
1971 .tag = tag,
1972 .ty = ty,
1973 .src = src,
1974 },
1975 .operand = operand,
1976 };
1977 try block.instructions.append(self.gpa, &inst.base);
1978 return &inst.base;
1979}
1980
1981fn addBinOp(
1982 self: *Module,
1983 block: *Scope.Block,
1984 src: usize,
1985 ty: Type,
1986 tag: Inst.Tag,
1987 lhs: *Inst,
1988 rhs: *Inst,
1989) !*Inst {
1990 const inst = try block.arena.create(Inst.BinOp);
1991 inst.* = .{
1992 .base = .{
1993 .tag = tag,
1994 .ty = ty,
1995 .src = src,
1996 },
1997 .lhs = lhs,
1998 .rhs = rhs,
1999 };
2000 try block.instructions.append(self.gpa, &inst.base);
2001 return &inst.base;
2002}
2003
2004fn addBr(
2005 self: *Module,
2006 scope_block: *Scope.Block,
2007 src: usize,
2008 target_block: *Inst.Block,
2009 operand: *Inst,
2010) !*Inst {
2011 const inst = try scope_block.arena.create(Inst.Br);
2012 inst.* = .{
2013 .base = .{
2014 .tag = .br,
2015 .ty = Type.initTag(.noreturn),
2016 .src = src,
2017 },
2018 .operand = operand,
2019 .block = target_block,
2020 };
2021 try scope_block.instructions.append(self.gpa, &inst.base);
2022 return &inst.base;
2023}
2024
2025fn addCondBr(
2026 self: *Module,
2027 block: *Scope.Block,
2028 src: usize,
2029 condition: *Inst,
2030 then_body: ir.Body,
2031 else_body: ir.Body,
19482032) !*Inst {
1949 const inst = try self.addNewInst(block, src, ty, T);
1950 inst.args = args;
2033 const inst = try block.arena.create(Inst.CondBr);
2034 inst.* = .{
2035 .base = .{
2036 .tag = .condbr,
2037 .ty = Type.initTag(.noreturn),
2038 .src = src,
2039 },
2040 .condition = condition,
2041 .then_body = then_body,
2042 .else_body = else_body,
2043 };
2044 try block.instructions.append(self.gpa, &inst.base);
2045 return &inst.base;
2046}
2047
2048fn addCall(
2049 self: *Module,
2050 block: *Scope.Block,
2051 src: usize,
2052 ty: Type,
2053 func: *Inst,
2054 args: []const *Inst,
2055) !*Inst {
2056 const inst = try block.arena.create(Inst.Call);
2057 inst.* = .{
2058 .base = .{
2059 .tag = .call,
2060 .ty = ty,
2061 .src = src,
2062 },
2063 .func = func,
2064 .args = args,
2065 };
2066 try block.instructions.append(self.gpa, &inst.base);
19512067 return &inst.base;
19522068}
19532069
......@@ -2017,7 +2133,6 @@ fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime
20172133 .ty = ty,
20182134 .src = src,
20192135 },
2020 .args = undefined,
20212136 };
20222137 try block.instructions.append(self.gpa, &inst.base);
20232138 return inst;
......@@ -2269,7 +2384,7 @@ fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!
22692384 });
22702385 }
22712386 const param_type = fn_ty.fnParamType(param_index);
2272 return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, {});
2387 return self.addNoOp(b, inst.base.src, param_type, .arg);
22732388}
22742389
22752390fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
......@@ -2285,7 +2400,7 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr
22852400 .ty = undefined, // Set after analysis.
22862401 .src = inst.base.src,
22872402 },
2288 .args = undefined,
2403 .body = undefined,
22892404 };
22902405
22912406 var child_block: Scope.Block = .{
......@@ -2316,13 +2431,13 @@ fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerEr
23162431 // to emit a jump instruction to after the block when it encounters the break.
23172432 try parent_block.instructions.append(self.gpa, &block_inst.base);
23182433 block_inst.base.ty = try self.resolvePeerTypes(scope, label.results.items);
2319 block_inst.args.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
2434 block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
23202435 return &block_inst.base;
23212436}
23222437
23232438fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
23242439 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2325 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, {});
2440 return self.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint);
23262441}
23272442
23282443fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
......@@ -2350,10 +2465,7 @@ fn analyzeBreak(
23502465 if (label.zir_block == zir_block) {
23512466 try label.results.append(self.gpa, operand);
23522467 const b = try self.requireRuntimeBlock(scope, src);
2353 return self.addNewInstArgs(b, src, Type.initTag(.noreturn), Inst.Br, .{
2354 .block = label.block_inst,
2355 .operand = operand,
2356 });
2468 return self.addBr(b, src, label.block_inst, operand);
23572469 }
23582470 }
23592471 opt_block = block.parent;
......@@ -2484,10 +2596,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
24842596 }
24852597
24862598 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2487 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, .{
2488 .func = func,
2489 .args = casted_args,
2490 });
2599 return self.addCall(b, inst.base.src, Type.initTag(.void), func, casted_args);
24912600}
24922601
24932602fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
......@@ -2570,14 +2679,14 @@ fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Ins
25702679}
25712680
25722681fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToInt) InnerError!*Inst {
2573 const ptr = try self.resolveInst(scope, ptrtoint.positionals.ptr);
2682 const ptr = try self.resolveInst(scope, ptrtoint.positionals.operand);
25742683 if (ptr.ty.zigTypeTag() != .Pointer) {
2575 return self.fail(scope, ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty});
2684 return self.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty});
25762685 }
25772686 // TODO handle known-pointer-address
25782687 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
25792688 const ty = Type.initTag(.usize);
2580 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, .{ .ptr = ptr });
2689 return self.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr);
25812690}
25822691
25832692fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
......@@ -2734,10 +2843,7 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!
27342843 }
27352844
27362845 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2737 return self.addNewInstArgs(b, inst.base.src, lhs.ty, Inst.Add, .{
2738 .lhs = lhs,
2739 .rhs = rhs,
2740 });
2846 return self.addBinOp(b, inst.base.src, lhs.ty, .add, lhs, rhs);
27412847 }
27422848 return self.fail(scope, inst.base.src, "TODO analyze add for {} + {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() });
27432849}
......@@ -2783,14 +2889,22 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr
27832889 }
27842890
27852891 const b = try self.requireRuntimeBlock(scope, assembly.base.src);
2786 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, .{
2892 const inst = try b.arena.create(Inst.Assembly);
2893 inst.* = .{
2894 .base = .{
2895 .tag = .assembly,
2896 .ty = return_type,
2897 .src = assembly.base.src,
2898 },
27872899 .asm_source = asm_source,
27882900 .is_volatile = assembly.kw_args.@"volatile",
27892901 .output = output,
27902902 .inputs = inputs,
27912903 .clobbers = clobbers,
27922904 .args = args,
2793 });
2905 };
2906 try b.instructions.append(self.gpa, &inst.base);
2907 return &inst.base;
27942908}
27952909
27962910fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!*Inst {
......@@ -2818,15 +2932,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
28182932 return self.constBool(scope, inst.base.src, if (op == .eq) is_null else !is_null);
28192933 }
28202934 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2821 switch (op) {
2822 .eq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNull, .{
2823 .operand = opt_operand,
2824 }),
2825 .neq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNonNull, .{
2826 .operand = opt_operand,
2827 }),
2935 const inst_tag: Inst.Tag = switch (op) {
2936 .eq => .isnull,
2937 .neq => .isnonnull,
28282938 else => unreachable,
2829 }
2939 };
2940 return self.addUnOp(b, inst.base.src, Type.initTag(.bool), inst_tag, opt_operand);
28302941 } else if (is_equality_cmp and
28312942 ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
28322943 {
......@@ -2861,7 +2972,7 @@ fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.BoolNot) Inn
28612972 return self.constBool(scope, inst.base.src, !val.toBool());
28622973 }
28632974 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2864 return self.addNewInstArgs(b, inst.base.src, bool_type, Inst.Not, .{ .operand = operand });
2975 return self.addUnOp(b, inst.base.src, bool_type, .not, operand);
28652976}
28662977
28672978fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {
......@@ -2879,7 +2990,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
28792990 const cond = try self.coerce(scope, Type.initTag(.bool), uncasted_cond);
28802991
28812992 if (try self.resolveDefinedValue(scope, cond)) |cond_val| {
2882 const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body;
2993 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
28832994 try self.analyzeBody(scope, body.*);
28842995 return self.constVoid(scope, inst.base.src);
28852996 }
......@@ -2894,7 +3005,7 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
28943005 .arena = parent_block.arena,
28953006 };
28963007 defer true_block.instructions.deinit(self.gpa);
2897 try self.analyzeBody(&true_block.base, inst.positionals.true_body);
3008 try self.analyzeBody(&true_block.base, inst.positionals.then_body);
28983009
28993010 var false_block: Scope.Block = .{
29003011 .parent = parent_block,
......@@ -2904,13 +3015,11 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
29043015 .arena = parent_block.arena,
29053016 };
29063017 defer false_block.instructions.deinit(self.gpa);
2907 try self.analyzeBody(&false_block.base, inst.positionals.false_body);
3018 try self.analyzeBody(&false_block.base, inst.positionals.else_body);
29083019
2909 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.noreturn), Inst.CondBr, Inst.Args(Inst.CondBr){
2910 .condition = cond,
2911 .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },
2912 .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },
2913 });
3020 const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) };
3021 const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) };
3022 return self.addCondBr(parent_block, inst.base.src, cond, then_body, else_body);
29143023}
29153024
29163025fn wantSafety(self: *Module, scope: *Scope) bool {
......@@ -2926,20 +3035,20 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea
29263035 const b = try self.requireRuntimeBlock(scope, unreach.base.src);
29273036 if (self.wantSafety(scope)) {
29283037 // TODO Once we have a panic function to call, call it here instead of this.
2929 _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {});
3038 _ = try self.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);
29303039 }
2931 return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
3040 return self.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach);
29323041}
29333042
29343043fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {
29353044 const operand = try self.resolveInst(scope, inst.positionals.operand);
29363045 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2937 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, .{ .operand = operand });
3046 return self.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand);
29383047}
29393048
29403049fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst {
29413050 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2942 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.RetVoid, {});
3051 return self.addNoOp(b, inst.base.src, Type.initTag(.noreturn), .retvoid);
29433052}
29443053
29453054fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {
......@@ -3027,11 +3136,7 @@ fn cmpNumeric(
30273136 };
30283137 const casted_lhs = try self.coerce(scope, dest_type, lhs);
30293138 const casted_rhs = try self.coerce(scope, dest_type, rhs);
3030 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{
3031 .lhs = casted_lhs,
3032 .rhs = casted_rhs,
3033 .op = op,
3034 });
3139 return self.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
30353140 }
30363141 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
30373142 // For mixed signed and unsigned integers, implicit cast both operands to a signed
......@@ -3131,11 +3236,7 @@ fn cmpNumeric(
31313236 const casted_lhs = try self.coerce(scope, dest_type, lhs);
31323237 const casted_rhs = try self.coerce(scope, dest_type, rhs);
31333238
3134 return self.addNewInstArgs(b, src, Type.initTag(.bool), Inst.Cmp, .{
3135 .lhs = casted_lhs,
3136 .rhs = casted_rhs,
3137 .op = op,
3138 });
3239 return self.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
31393240}
31403241
31413242fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
......@@ -3236,7 +3337,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
32363337 }
32373338 // TODO validate the type size and other compile errors
32383339 const b = try self.requireRuntimeBlock(scope, inst.src);
3239 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, .{ .operand = inst });
3340 return self.addUnOp(b, inst.src, dest_type, .bitcast, inst);
32403341}
32413342
32423343fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
src-self-hosted/astgen.zig+4-4
......@@ -173,8 +173,8 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
173173 const if_src = tree.token_locs[if_node.if_token].start;
174174 const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
175175 .condition = cond,
176 .true_body = undefined, // populated below
177 .false_body = undefined, // populated below
176 .then_body = undefined, // populated below
177 .else_body = undefined, // populated below
178178 }, .{});
179179
180180 const block = try mod.addZIRInstBlock(scope, if_src, .{
......@@ -196,7 +196,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
196196 .operand = then_result,
197197 }, .{});
198198 }
199 condbr.positionals.true_body = .{
199 condbr.positionals.then_body = .{
200200 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
201201 };
202202
......@@ -225,7 +225,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
225225 .block = block,
226226 }, .{});
227227 }
228 condbr.positionals.false_body = .{
228 condbr.positionals.else_body = .{
229229 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
230230 };
231231
src-self-hosted/codegen.zig+1124-1040
......@@ -32,67 +32,75 @@ pub const Result = union(enum) {
3232 fail: *Module.ErrorMsg,
3333};
3434
35pub const GenerateSymbolError = error{
36 OutOfMemory,
37 /// A Decl that this symbol depends on had a semantic analysis failure.
38 AnalysisFail,
39};
40
3541pub fn generateSymbol(
3642 bin_file: *link.File.Elf,
3743 src: usize,
3844 typed_value: TypedValue,
3945 code: *std.ArrayList(u8),
40) error{
41 OutOfMemory,
42 /// A Decl that this symbol depends on had a semantic analysis failure.
43 AnalysisFail,
44}!Result {
46) GenerateSymbolError!Result {
4547 const tracy = trace(@src());
4648 defer tracy.end();
4749
4850 switch (typed_value.ty.zigTypeTag()) {
4951 .Fn => {
50 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
51
52 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
53 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
54 defer bin_file.allocator.free(param_types);
55 fn_type.fnParamTypes(param_types);
56 var mc_args = try bin_file.allocator.alloc(MCValue, param_types.len);
57 defer bin_file.allocator.free(mc_args);
58
59 var branch_stack = std.ArrayList(Function.Branch).init(bin_file.allocator);
60 defer {
61 assert(branch_stack.items.len == 1);
62 branch_stack.items[0].deinit(bin_file.allocator);
63 branch_stack.deinit();
64 }
65 const branch = try branch_stack.addOne();
66 branch.* = .{};
67
68 var function = Function{
69 .gpa = bin_file.allocator,
70 .target = &bin_file.options.target,
71 .bin_file = bin_file,
72 .mod_fn = module_fn,
73 .code = code,
74 .err_msg = null,
75 .args = mc_args,
76 .arg_index = 0,
77 .branch_stack = &branch_stack,
78 .src = src,
79 };
80
81 const cc = fn_type.fnCallingConvention();
82 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
83 error.CodegenFail => return Result{ .fail = function.err_msg.? },
84 else => |e| return e,
85 };
86
87 function.gen() catch |err| switch (err) {
88 error.CodegenFail => return Result{ .fail = function.err_msg.? },
89 else => |e| return e,
90 };
91
92 if (function.err_msg) |em| {
93 return Result{ .fail = em };
94 } else {
95 return Result{ .appended = {} };
52 switch (bin_file.options.target.cpu.arch) {
53 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),
54 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),
55 .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),
56 .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code),
57 .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code),
58 .arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code),
59 .avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code),
60 .bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code),
61 .bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code),
62 .hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code),
63 .mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code),
64 .mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code),
65 .mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code),
66 .mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code),
67 .msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code),
68 .powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code),
69 .powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code),
70 .powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code),
71 .r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code),
72 .amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code),
73 .riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code),
74 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code),
75 .sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code),
76 .sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code),
77 .sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code),
78 .s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code),
79 .tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code),
80 .tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code),
81 .thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code),
82 .thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code),
83 .i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code),
84 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code),
85 .xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code),
86 .nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code),
87 .nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code),
88 .le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code),
89 .le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code),
90 .amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code),
91 .amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code),
92 .hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code),
93 .hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code),
94 .spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code),
95 .spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code),
96 .kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code),
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),
96104 }
97105 },
98106 .Array => {
......@@ -189,1101 +197,1177 @@ const InnerError = error{
189197 CodegenFail,
190198};
191199
192const MCValue = union(enum) {
193 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
194 none,
195 /// Control flow will not allow this value to be observed.
196 unreach,
197 /// No more references to this value remain.
198 dead,
199 /// A pointer-sized integer that fits in a register.
200 immediate: u64,
201 /// The constant was emitted into the code, at this offset.
202 embedded_in_code: usize,
203 /// The value is in a target-specific register. The value can
204 /// be @intToEnum casted to the respective Reg enum.
205 register: usize,
206 /// The value is in memory at a hard-coded address.
207 memory: u64,
208 /// The value is one of the stack variables.
209 stack_offset: u64,
210 /// The value is in the compare flags assuming an unsigned operation,
211 /// with this operator applied on top of it.
212 compare_flags_unsigned: std.math.CompareOperator,
213 /// The value is in the compare flags assuming a signed operation,
214 /// with this operator applied on top of it.
215 compare_flags_signed: std.math.CompareOperator,
216
217 fn isMemory(mcv: MCValue) bool {
218 return switch (mcv) {
219 .embedded_in_code, .memory, .stack_offset => true,
220 else => false,
200fn Function(comptime arch: std.Target.Cpu.Arch) type {
201 return struct {
202 gpa: *Allocator,
203 bin_file: *link.File.Elf,
204 target: *const std.Target,
205 mod_fn: *const Module.Fn,
206 code: *std.ArrayList(u8),
207 err_msg: ?*ErrorMsg,
208 args: []MCValue,
209 arg_index: usize,
210 src: usize,
211
212 /// Whenever there is a runtime branch, we push a Branch onto this stack,
213 /// and pop it off when the runtime branch joins. This provides an "overlay"
214 /// of the table of mappings from instructions to `MCValue` from within the branch.
215 /// This way we can modify the `MCValue` for an instruction in different ways
216 /// within different branches. Special consideration is needed when a branch
217 /// joins with its parent, to make sure all instructions have the same MCValue
218 /// across each runtime branch upon joining.
219 branch_stack: *std.ArrayList(Branch),
220
221 const MCValue = union(enum) {
222 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
223 none,
224 /// Control flow will not allow this value to be observed.
225 unreach,
226 /// No more references to this value remain.
227 dead,
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 }
221277 };
222 }
223278
224 fn isImmediate(mcv: MCValue) bool {
225 return switch (mcv) {
226 .immediate => true,
227 else => false,
279 const Branch = struct {
280 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
281 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},
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 }
228314 };
229 }
230315
231 fn isMutable(mcv: MCValue) bool {
232 return switch (mcv) {
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,
316 const RegisterAllocation = struct {
317 inst: *ir.Inst,
247318 };
248 }
249};
250319
251const Function = struct {
252 gpa: *Allocator,
253 bin_file: *link.File.Elf,
254 target: *const std.Target,
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,
320 const StackAllocation = struct {
321 inst: *ir.Inst,
322 size: u32,
323 };
261324
262 /// Whenever there is a runtime branch, we push a Branch onto this stack,
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 };
325 const Self = @This();
292326
293 const RegisterAllocation = struct {
294 inst: *ir.Inst,
295 };
327 fn generateSymbol(
328 bin_file: *link.File.Elf,
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 {
298 inst: *ir.Inst,
299 size: u32,
300 };
335 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
336 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
337 defer bin_file.allocator.free(param_types);
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 {
303 switch (self.target.cpu.arch) {
304 .arm => return self.genArch(.arm),
305 .armeb => return self.genArch(.armeb),
306 .aarch64 => return self.genArch(.aarch64),
307 .aarch64_be => return self.genArch(.aarch64_be),
308 .aarch64_32 => return self.genArch(.aarch64_32),
309 .arc => return self.genArch(.arc),
310 .avr => return self.genArch(.avr),
311 .bpfel => return self.genArch(.bpfel),
312 .bpfeb => return self.genArch(.bpfeb),
313 .hexagon => return self.genArch(.hexagon),
314 .mips => return self.genArch(.mips),
315 .mipsel => return self.genArch(.mipsel),
316 .mips64 => return self.genArch(.mips64),
317 .mips64el => return self.genArch(.mips64el),
318 .msp430 => return self.genArch(.msp430),
319 .powerpc => return self.genArch(.powerpc),
320 .powerpc64 => return self.genArch(.powerpc64),
321 .powerpc64le => return self.genArch(.powerpc64le),
322 .r600 => return self.genArch(.r600),
323 .amdgcn => return self.genArch(.amdgcn),
324 .riscv32 => return self.genArch(.riscv32),
325 .riscv64 => return self.genArch(.riscv64),
326 .sparc => return self.genArch(.sparc),
327 .sparcv9 => return self.genArch(.sparcv9),
328 .sparcel => return self.genArch(.sparcel),
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 }
342 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
343 defer {
344 assert(branch_stack.items.len == 1);
345 branch_stack.items[0].deinit(bin_file.allocator);
346 branch_stack.deinit();
347 }
348 const branch = try branch_stack.addOne();
349 branch.* = .{};
350
351 var function = Self{
352 .gpa = bin_file.allocator,
353 .target = &bin_file.options.target,
354 .bin_file = bin_file,
355 .mod_fn = module_fn,
356 .code = code,
357 .err_msg = null,
358 .args = mc_args,
359 .arg_index = 0,
360 .branch_stack = &branch_stack,
361 .src = src,
362 };
363
364 const cc = fn_type.fnCallingConvention();
365 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
366 error.CodegenFail => return Result{ .fail = function.err_msg.? },
367 else => |e| return e,
368 };
357369
358 fn genArch(self: *Function, comptime arch: std.Target.Cpu.Arch) !void {
359 try self.code.ensureCapacity(self.code.items.len + 11);
360
361 // push rbp
362 // mov rbp, rsp
363 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });
364
365 // sub rsp, x
366 const stack_end = self.branch_stack.items[0].max_end_stack;
367 if (stack_end > std.math.maxInt(i32)) {
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 });
370 function.gen() catch |err| switch (err) {
371 error.CodegenFail => return Result{ .fail = function.err_msg.? },
372 else => |e| return e,
373 };
374
375 if (function.err_msg) |em| {
376 return Result{ .fail = em };
377 } else {
378 return Result{ .appended = {} };
379 }
378380 }
379381
380 try self.genBody(self.mod_fn.analysis.success, arch);
381 }
382 fn gen(self: *Self) !void {
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 {
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);
404 try self.genBody(self.mod_fn.analysis.success);
388405 }
389 }
390406
391 fn genFuncInst(self: *Function, inst: *ir.Inst, comptime arch: std.Target.Cpu.Arch) !MCValue {
392 switch (inst.tag) {
393 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),
394 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),
395 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch),
396 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
397 .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch),
398 .br => return self.genBr(inst.cast(ir.Inst.Br).?, arch),
399 .breakpoint => return self.genBreakpoint(inst.src, arch),
400 .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?, arch),
401 .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch),
402 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch),
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),
407 fn genBody(self: *Self, body: ir.Body) InnerError!void {
408 const inst_table = &self.branch_stack.items[0].inst_table;
409 for (body.instructions) |inst| {
410 const new_inst = try self.genFuncInst(inst);
411 try inst_table.putNoClobber(self.gpa, inst, new_inst);
412
413 var i: ir.Inst.DeathsBitIndex = 0;
414 while (inst.getOperand(i)) |operand| : (i += 1) {
415 if (inst.operandDies(i))
416 self.processDeath(operand);
417 }
418 }
413419 }
414 }
415420
416 fn genNot(self: *Function, inst: *ir.Inst.Not, comptime arch: std.Target.Cpu.Arch) !MCValue {
417 // No side effects, so if it's unreferenced, do nothing.
418 if (inst.base.isUnused())
419 return MCValue.dead;
420 const operand = try self.resolveInst(inst.args.operand);
421 switch (operand) {
422 .dead => unreachable,
423 .unreach => unreachable,
424 .compare_flags_unsigned => |op| return MCValue{
425 .compare_flags_unsigned = switch (op) {
426 .gte => .lt,
427 .gt => .lte,
428 .neq => .eq,
429 .lt => .gte,
430 .lte => .gt,
431 .eq => .neq,
421 fn processDeath(self: *Self, inst: *ir.Inst) void {
422 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
423 const entry = branch.inst_table.getEntry(inst) orelse return;
424 const prev_value = entry.value;
425 entry.value = .dead;
426 switch (prev_value) {
427 .register => |reg| {
428 _ = branch.registers.remove(reg);
429 branch.markRegFree(reg);
432430 },
433 },
434 .compare_flags_signed => |op| return MCValue{
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 => {},
431 else => {}, // TODO process stack allocation death
432 }
445433 }
446434
447 switch (arch) {
448 .x86_64 => {
449 var imm = ir.Inst.Constant{
450 .base = .{
451 .tag = .constant,
452 .deaths = 0,
453 .ty = inst.args.operand.ty,
454 .src = inst.args.operand.src,
455 },
456 .val = Value.initTag(.bool_true),
457 };
458 return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);
459 },
460 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
435 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
436 switch (inst.tag) {
437 .add => return self.genAdd(inst.castTag(.add).?),
438 .arg => return self.genArg(inst.castTag(.arg).?),
439 .assembly => return self.genAsm(inst.castTag(.assembly).?),
440 .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
441 .block => return self.genBlock(inst.castTag(.block).?),
442 .br => return self.genBr(inst.castTag(.br).?),
443 .breakpoint => return self.genBreakpoint(inst.src),
444 .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?),
445 .call => return self.genCall(inst.castTag(.call).?),
446 .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
447 .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),
448 .cmp_eq => return self.genCmp(inst.castTag(.cmp_eq).?, .eq),
449 .cmp_gte => return self.genCmp(inst.castTag(.cmp_gte).?, .gte),
450 .cmp_gt => return self.genCmp(inst.castTag(.cmp_gt).?, .gt),
451 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
452 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
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 }
461463 }
462 }
463464
464 fn genAdd(self: *Function, inst: *ir.Inst.Add, comptime arch: std.Target.Cpu.Arch) !MCValue {
465 // No side effects, so if it's unreferenced, do nothing.
466 if (inst.base.isUnused())
467 return MCValue.dead;
468 switch (arch) {
469 .x86_64 => {
470 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);
471 },
472 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
473 }
474 }
465 fn genNot(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
466 // No side effects, so if it's unreferenced, do nothing.
467 if (inst.base.isUnused())
468 return MCValue.dead;
469 const operand = try self.resolveInst(inst.operand);
470 switch (operand) {
471 .dead => unreachable,
472 .unreach => unreachable,
473 .compare_flags_unsigned => |op| return MCValue{
474 .compare_flags_unsigned = switch (op) {
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 {
477 // No side effects, so if it's unreferenced, do nothing.
478 if (inst.base.isUnused())
479 return MCValue.dead;
480 switch (arch) {
481 .x86_64 => {
482 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);
483 },
484 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
496 switch (arch) {
497 .x86_64 => {
498 var imm = ir.Inst.Constant{
499 .base = .{
500 .tag = .constant,
501 .deaths = 0,
502 .ty = inst.operand.ty,
503 .src = inst.operand.src,
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 }
485511 }
486 }
487512
488 /// ADD, SUB, XOR, OR, AND
489 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
490 try self.code.ensureCapacity(self.code.items.len + 8);
491
492 const lhs = try self.resolveInst(op_lhs);
493 const rhs = try self.resolveInst(op_rhs);
494
495 // There are 2 operands, destination and source.
496 // Either one, but not both, can be a memory operand.
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 fn genAdd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
514 // No side effects, so if it's unreferenced, do nothing.
515 if (inst.base.isUnused())
516 return MCValue.dead;
517 switch (arch) {
518 .x86_64 => {
519 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 0, 0x00);
520 },
521 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
513522 }
514 } else if (inst.operandDies(1) and rhs.isMutable()) {
515 // RHS dies; use it as the destination.
516 // Both operands cannot be memory.
517 src_inst = op_lhs;
518 if (lhs.isMemory() and rhs.isMemory()) {
519 dst_mcv = try self.copyToNewRegister(op_rhs);
520 src_mcv = lhs;
521 } else {
522 dst_mcv = rhs;
523 src_mcv = lhs;
523 }
524
525 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
526 // No side effects, so if it's unreferenced, do nothing.
527 if (inst.base.isUnused())
528 return MCValue.dead;
529 switch (arch) {
530 .x86_64 => {
531 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 5, 0x28);
532 },
533 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
524534 }
525 } else {
526 if (lhs.isMemory()) {
527 dst_mcv = try self.copyToNewRegister(op_lhs);
528 src_mcv = rhs;
535 }
536
537 /// ADD, SUB, XOR, OR, AND
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.
529555 src_inst = op_rhs;
530 } else {
531 dst_mcv = try self.copyToNewRegister(op_rhs);
532 src_mcv = lhs;
556 if (lhs.isMemory() and rhs.isMemory()) {
557 dst_mcv = try self.copyToNewRegister(op_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.
533566 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 => {},
534597 }
598
599 try self.genX8664BinMathCode(inst.src, dst_mcv, src_mcv, opx, mr);
600
601 return dst_mcv;
535602 }
536 // This instruction supports only signed 32-bit immediates at most. If the immediate
537 // value is larger than this, we put it in a register.
538 // A potential opportunity for future optimization here would be keeping track
539 // of the fact that the instruction is available both as an immediate
540 // and as a register.
541 switch (src_mcv) {
542 .immediate => |imm| {
543 if (imm > std.math.maxInt(u31)) {
544 src_mcv = try self.copyToNewRegister(src_inst);
545 }
546 },
547 else => {},
603
604 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
605 switch (dst_mcv) {
606 .none => unreachable,
607 .dead, .unreach, .immediate => unreachable,
608 .compare_flags_unsigned => unreachable,
609 .compare_flags_signed => unreachable,
610 .register => |dst_reg| {
611 switch (src_mcv) {
612 .none => unreachable,
613 .dead, .unreach => unreachable,
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 }
548652 }
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;
553 }
661 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
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 {
556 switch (dst_mcv) {
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 }
664 const result = self.args[self.arg_index];
665 self.arg_index += 1;
606666
607 fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue {
608 const i = self.arg_index;
609 self.arg_index += 1;
610 return self.args[i];
611 }
667 switch (result) {
668 .register => |reg| {
669 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = &inst.base });
670 branch.markRegUsed(reg);
671 },
672 else => {},
673 }
674 return result;
675 }
612676
613 fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue {
614 switch (arch) {
615 .i386, .x86_64 => {
616 try self.code.append(0xcc); // int3
617 },
618 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
677 fn genBreakpoint(self: *Self, src: usize) !MCValue {
678 switch (arch) {
679 .i386, .x86_64 => {
680 try self.code.append(0xcc); // int3
681 },
682 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
683 }
684 return .none;
619685 }
620 return .none;
621 }
622686
623 fn genCall(self: *Function, inst: *ir.Inst.Call, comptime arch: std.Target.Cpu.Arch) !MCValue {
624 const fn_ty = inst.args.func.ty;
625 const cc = fn_ty.fnCallingConvention();
626 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
627 defer self.gpa.free(param_types);
628 fn_ty.fnParamTypes(param_types);
629 var mc_args = try self.gpa.alloc(MCValue, param_types.len);
630 defer self.gpa.free(mc_args);
631 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);
632
633 switch (arch) {
634 .x86_64 => {
635 for (mc_args) |mc_arg, arg_i| {
636 const arg = inst.args.args[arg_i];
637 const arg_mcv = try self.resolveInst(inst.args.args[arg_i]);
638 switch (mc_arg) {
639 .none => continue,
640 .register => |reg| {
641 try self.genSetReg(arg.src, arch, @intToEnum(Reg(arch), @intCast(u8, reg)), arg_mcv);
642 // TODO interact with the register allocator to mark the instruction as moved.
643 },
644 .stack_offset => {
645 // Here we need to emit instructions like this:
646 // mov qword ptr [rsp + stack_offset], x
647 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
648 },
649 .immediate => unreachable,
650 .unreach => unreachable,
651 .dead => unreachable,
652 .embedded_in_code => unreachable,
653 .memory => unreachable,
654 .compare_flags_signed => unreachable,
655 .compare_flags_unsigned => unreachable,
687 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
688 const fn_ty = inst.func.ty;
689 const cc = fn_ty.fnCallingConvention();
690 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
691 defer self.gpa.free(param_types);
692 fn_ty.fnParamTypes(param_types);
693 var mc_args = try self.gpa.alloc(MCValue, param_types.len);
694 defer self.gpa.free(mc_args);
695 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);
696
697 switch (arch) {
698 .x86_64 => {
699 for (mc_args) |mc_arg, arg_i| {
700 const arg = inst.args[arg_i];
701 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
702 switch (mc_arg) {
703 .none => continue,
704 .register => |reg| {
705 try self.genSetReg(arg.src, reg, arg_mcv);
706 // TODO interact with the register allocator to mark the instruction as moved.
707 },
708 .stack_offset => {
709 // Here we need to emit instructions like this:
710 // mov qword ptr [rsp + stack_offset], x
711 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
712 },
713 .immediate => unreachable,
714 .unreach => unreachable,
715 .dead => unreachable,
716 .embedded_in_code => unreachable,
717 .memory => unreachable,
718 .compare_flags_signed => unreachable,
719 .compare_flags_unsigned => unreachable,
720 }
656721 }
657 }
658722
659 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
660 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
661 const func = func_val.func;
662 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
663 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
664 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);
666 // ff 14 25 xx xx xx xx call [addr]
667 try self.code.ensureCapacity(self.code.items.len + 7);
668 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
669 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
723 if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
724 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
725 const func = func_val.func;
726 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
727 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
728 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
729 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
730 // ff 14 25 xx xx xx xx call [addr]
731 try self.code.ensureCapacity(self.code.items.len + 7);
732 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
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 }
670737 } 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", .{});
672739 }
673 } else {
674 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
675 }
676 },
677 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
678 }
740 },
741 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
742 }
679743
680 const return_type = fn_ty.fnReturnType();
681 switch (return_type.zigTypeTag()) {
682 .Void => return MCValue{ .none = {} },
683 .NoReturn => return MCValue{ .unreach = {} },
684 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
744 const return_type = fn_ty.fnReturnType();
745 switch (return_type.zigTypeTag()) {
746 .Void => return MCValue{ .none = {} },
747 .NoReturn => return MCValue{ .unreach = {} },
748 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
749 }
685750 }
686 }
687751
688 fn ret(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, mcv: MCValue) !MCValue {
689 if (mcv != .none) {
690 return self.fail(src, "TODO implement return with non-void operand", .{});
691 }
692 switch (arch) {
693 .i386 => {
694 try self.code.append(0xc3); // ret
695 },
696 .x86_64 => {
697 try self.code.appendSlice(&[_]u8{
698 0x5d, // pop rbp
699 0xc3, // ret
700 });
701 },
702 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
752 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
753 if (mcv != .none) {
754 return self.fail(src, "TODO implement return with non-void operand", .{});
755 }
756 switch (arch) {
757 .i386 => {
758 try self.code.append(0xc3); // ret
759 },
760 .x86_64 => {
761 try self.code.appendSlice(&[_]u8{
762 0x5d, // pop rbp
763 0xc3, // ret
764 });
765 },
766 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
767 }
768 return .unreach;
703769 }
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 {
713 return self.ret(inst.base.src, arch, .none);
714 }
771 fn genRet(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
772 const operand = try self.resolveInst(inst.operand);
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 {
717 // No side effects, so if it's unreferenced, do nothing.
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}),
776 fn genRetVoid(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
777 return self.ret(inst.base.src, .none);
746778 }
747 }
748779
749 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
750 switch (arch) {
751 .x86_64 => {
752 try self.code.ensureCapacity(self.code.items.len + 6);
753
754 const cond = try self.resolveInst(inst.args.condition);
755 switch (cond) {
756 .compare_flags_signed => |cmp_op| {
757 // Here we map to the opposite opcode because the jump is to the false branch.
758 const opcode: u8 = switch (cmp_op) {
759 .gte => 0x8c,
760 .gt => 0x8e,
761 .neq => 0x84,
762 .lt => 0x8d,
763 .lte => 0x8f,
764 .eq => 0x85,
765 };
766 return self.genX86CondBr(inst, opcode, arch);
767 },
768 .compare_flags_unsigned => |cmp_op| {
769 // Here we map to the opposite opcode because the jump is to the false branch.
770 const opcode: u8 = switch (cmp_op) {
771 .gte => 0x82,
772 .gt => 0x86,
773 .neq => 0x84,
774 .lt => 0x83,
775 .lte => 0x87,
776 .eq => 0x85,
777 };
778 return self.genX86CondBr(inst, opcode, arch);
779 },
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}),
780 fn genCmp(self: *Self, inst: *ir.Inst.BinOp, op: std.math.CompareOperator) !MCValue {
781 // No side effects, so if it's unreferenced, do nothing.
782 if (inst.base.isUnused())
783 return MCValue.dead;
784 switch (arch) {
785 .x86_64 => {
786 try self.code.ensureCapacity(self.code.items.len + 8);
787
788 const lhs = try self.resolveInst(inst.lhs);
789 const rhs = try self.resolveInst(inst.rhs);
790
791 // There are 2 operands, destination and source.
792 // Either one, but not both, can be a memory operand.
793 // Source operand can be an immediate, 8 bits or 32 bits.
794 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
795 try self.copyToNewRegister(inst.lhs)
796 else
797 lhs;
798 // This instruction supports only signed 32-bit immediates at most.
799 const src_mcv = try self.limitImmediateType(inst.rhs, i32);
800
801 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
802 const info = inst.lhs.ty.intInfo(self.target.*);
803 if (info.signed) {
804 return MCValue{ .compare_flags_signed = op };
805 } else {
806 return MCValue{ .compare_flags_unsigned = op };
807 }
808 },
809 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
810 }
797811 }
798 }
799812
800 fn genX86CondBr(self: *Function, inst: *ir.Inst.CondBr, opcode: u8, comptime arch: std.Target.Cpu.Arch) !MCValue {
801 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
802 const reloc = Reloc{ .rel32 = self.code.items.len };
803 self.code.items.len += 4;
804 try self.genBody(inst.args.true_body, arch);
805 try self.performReloc(inst.base.src, reloc);
806 try self.genBody(inst.args.false_body, arch);
807 return MCValue.unreach;
808 }
813 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
814 switch (arch) {
815 .x86_64 => {
816 try self.code.ensureCapacity(self.code.items.len + 6);
817
818 const cond = try self.resolveInst(inst.condition);
819 switch (cond) {
820 .compare_flags_signed => |cmp_op| {
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 {
811 switch (arch) {
812 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
863 fn genX86CondBr(self: *Self, inst: *ir.Inst.CondBr, opcode: u8) !MCValue {
864 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
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;
813871 }
814 }
815872
816 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
817 // Here you can specialize this instruction if it makes sense to, otherwise the default
818 // will call genIsNull and invert the result.
819 switch (arch) {
820 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
873 fn genIsNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
874 switch (arch) {
875 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
876 }
821877 }
822 }
823878
824 fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {
825 if (inst.base.ty.hasCodeGenBits()) {
826 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});
879 fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
880 // Here you can specialize this instruction if it makes sense to, otherwise the default
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 }
827885 }
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;
835 }
895 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
836896
837 fn performReloc(self: *Function, src: usize, reloc: Reloc) !void {
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 },
897 return MCValue.none;
845898 }
846 }
847899
848 fn genBr(self: *Function, inst: *ir.Inst.Br, comptime arch: std.Target.Cpu.Arch) !MCValue {
849 if (!inst.args.operand.ty.hasCodeGenBits())
850 return self.brVoid(inst.base.src, inst.args.block, arch);
851
852 const operand = try self.resolveInst(inst.args.operand);
853 switch (arch) {
854 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
900 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
901 switch (reloc) {
902 .rel32 => |pos| {
903 const amt = self.code.items.len - (pos + 4);
904 const s32_amt = std.math.cast(i32, amt) catch
905 return self.fail(src, "unable to perform relocation: jump too far", .{});
906 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
907 },
908 }
855909 }
856 }
857910
858 fn genBrVoid(self: *Function, inst: *ir.Inst.BrVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {
859 return self.brVoid(inst.base.src, inst.args.block, arch);
860 }
911 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
912 if (!inst.operand.ty.hasCodeGenBits())
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 {
863 // Emit a jump with a relocation. It will be patched up after the block ends.
864 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
865
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}),
915 const operand = try self.resolveInst(inst.operand);
916 switch (arch) {
917 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
918 }
876919 }
877 return .none;
878 }
879920
880 fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue {
881 if (!inst.args.is_volatile and inst.base.isUnused())
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", .{});
921 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
922 return self.brVoid(inst.base.src, inst.block);
885923 }
886 for (inst.args.inputs) |input, i| {
887 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
888 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
924
925 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
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}),
889939 }
890 const reg_name = input[1 .. input.len - 1];
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);
940 return .none;
895941 }
896942
897 if (mem.eql(u8, inst.args.asm_source, "syscall")) {
898 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
899 } else {
900 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
901 }
943 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
944 if (!inst.is_volatile and inst.base.isUnused())
945 return MCValue.dead;
946 if (arch != .x86_64 and arch != .i386) {
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| {
904 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
905 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
960 if (mem.eql(u8, inst.asm_source, "syscall")) {
961 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
962 } else {
963 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
906964 }
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 instruction
917 /// stream. This only modifies the instruction stream if at least one bit
918 /// is set true, which has a few implications:
919 ///
920 /// * The length of the instruction buffer will be modified *if* the
921 /// resulting REX is meaningful, but will remain the same if it is not.
922 /// * Deliberately inserting a "meaningless REX" requires explicit usage of
923 /// 0x40, and cannot be done via this function.
924 fn rex(self: *Function, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
925 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
926 var value: u8 = 0x40;
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;
966 if (inst.output) |output| {
967 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
968 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
969 }
970 const reg_name = output[2 .. output.len - 1];
971 const reg = parseRegName(reg_name) orelse
972 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
973 return MCValue{ .register = reg };
974 } else {
975 return MCValue.none;
976 }
938977 }
939 if (value != 0x40) {
940 self.code.appendAssumeCapacity(value);
978
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 }
9411005 }
942 }
9431006
944 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
945 switch (arch) {
946 .x86_64 => switch (mcv) {
947 .dead => unreachable,
948 .none => unreachable,
949 .unreach => unreachable,
950 .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.
1007 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
1008 switch (arch) {
1009 .x86_64 => switch (mcv) {
1010 .dead => unreachable,
1011 .none => unreachable,
1012 .unreach => unreachable,
1013 .compare_flags_unsigned => |op| {
9851014 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 };
9871024 const id = @as(u8, reg.id() & 0b111);
988 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });
989 return;
990 }
991 if (x <= std.math.maxInt(u32)) {
992 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
993 //
994 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
995 if (reg.isExtended()) {
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);
1025 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
1026 },
1027 .compare_flags_signed => |op| {
1028 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
1029 },
1030 .immediate => |x| {
1031 if (reg.size() != 64) {
1032 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
10051033 }
1006 self.code.items[self.code.items.len - 5] = 0xB8 | @as(u8, reg.id() & 0b111);
1007 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
1008 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
1009 return;
1010 }
1011 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
1012 // this `movabs`, though this is officially just a different variant of the plain `mov`
1013 // instruction.
1014 //
1015 // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only
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.
1034 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
1035 // register is the fastest way to zero a register.
1036 if (x == 0) {
1037 // The encoding for `xor r32, r32` is `0x31 /r`.
1038 // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the
1039 // ModR/M byte of the instruction contains a register operand and an r/m operand."
1040 //
1041 // R/M bytes are composed of two bits for the mode, then three bits for the register,
1042 // then three bits for the operand. Since we're zeroing a register, the two three-bit
1043 // values will be identical, and the mode is three (the raw register value).
11021044 //
1103 // This cannot be used if the lower three bits of the id are equal to four or five, as there
1104 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
1105 // this instruction.
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
1045 // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
1046 // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
1047 // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
11161048 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 register
1118 // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.
1049 self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() });
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.
11191056 //
1120 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
1121 // register operands need to be marked as extended.
1122 self.rex(.{ .w = true, .b = reg.isExtended(), .r = reg.isExtended() });
1123 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
1124 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
1057 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
1058 if (reg.isExtended()) {
1059 // Just as with XORing, we need a REX prefix. This time though, we only
1060 // need the B bit set, as we're extending the opcode's register field,
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;
11251073 }
1126 }
1127 },
1128 .stack_offset => |off| {
1129 return self.fail(src, "TODO implement genSetReg for stack variables", .{});
1074 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
1075 // this `movabs`, though this is officially just a different variant of the plain `mov`
1076 // instruction.
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 },
11301193 },
1131 },
1132 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
1194 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
1195 }
11331196 }
1134 }
11351197
1136 fn genPtrToInt(self: *Function, inst: *ir.Inst.PtrToInt) !MCValue {
1137 // no-op
1138 return self.resolveInst(inst.args.ptr);
1139 }
1198 fn genPtrToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1199 // no-op
1200 return self.resolveInst(inst.operand);
1201 }
11401202
1141 fn genBitCast(self: *Function, inst: *ir.Inst.BitCast) !MCValue {
1142 const operand = try self.resolveInst(inst.args.operand);
1143 return operand;
1144 }
1203 fn genBitCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1204 const operand = try self.resolveInst(inst.operand);
1205 return operand;
1206 }
11451207
1146 fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue {
1147 // Constants have static lifetimes, so they are always memoized in the outer most table.
1148 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1149 const branch = &self.branch_stack.items[0];
1150 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
1151 if (!gop.found_existing) {
1152 gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1208 fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {
1209 // Constants have static lifetimes, so they are always memoized in the outer most table.
1210 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1211 const branch = &self.branch_stack.items[0];
1212 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
1213 if (!gop.found_existing) {
1214 gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1215 }
1216 return gop.entry.value;
11531217 }
1154 return gop.entry.value;
1155 }
11561218
1157 // Treat each stack item as a "layer" on top of the previous one.
1158 var i: usize = self.branch_stack.items.len;
1159 while (true) {
1160 i -= 1;
1161 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1162 return mcv;
1219 // Treat each stack item as a "layer" on top of the previous one.
1220 var i: usize = self.branch_stack.items.len;
1221 while (true) {
1222 i -= 1;
1223 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1224 assert(mcv != .dead);
1225 return mcv;
1226 }
11631227 }
11641228 }
1165 }
11661229
1167 fn copyToNewRegister(self: *Function, inst: *ir.Inst) !MCValue {
1168 return self.fail(inst.src, "TODO implement copyToNewRegister", .{});
1169 }
1230 /// Does not "move" the instruction.
1231 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
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,
1172 /// we put it in a register.
1173 /// A potential opportunity for future optimization here would be keeping track
1174 /// of the fact that the instruction is available both as an immediate
1175 /// and as a register.
1176 fn limitImmediateType(self: *Function, inst: *ir.Inst, comptime T: type) !MCValue {
1177 const mcv = try self.resolveInst(inst);
1178 const ti = @typeInfo(T).Int;
1179 switch (mcv) {
1180 .immediate => |imm| {
1181 // This immediate is unsigned.
1182 const U = @Type(.{
1183 .Int = .{
1184 .bits = ti.bits - @boolToInt(ti.is_signed),
1185 .is_signed = false,
1186 },
1187 });
1188 if (imm >= std.math.maxInt(U)) {
1189 return self.copyToNewRegister(inst);
1190 }
1191 },
1192 else => {},
1248 /// If the MCValue is an immediate, and it does not fit within this type,
1249 /// we put it in a register.
1250 /// A potential opportunity for future optimization here would be keeping track
1251 /// of the fact that the instruction is available both as an immediate
1252 /// and as a register.
1253 fn limitImmediateType(self: *Self, inst: *ir.Inst, comptime T: type) !MCValue {
1254 const mcv = try self.resolveInst(inst);
1255 const ti = @typeInfo(T).Int;
1256 switch (mcv) {
1257 .immediate => |imm| {
1258 // This immediate is unsigned.
1259 const U = @Type(.{
1260 .Int = .{
1261 .bits = ti.bits - @boolToInt(ti.is_signed),
1262 .is_signed = false,
1263 },
1264 });
1265 if (imm >= std.math.maxInt(U)) {
1266 return self.copyToNewRegister(inst);
1267 }
1268 },
1269 else => {},
1270 }
1271 return mcv;
11931272 }
1194 return mcv;
1195 }
11961273
1197 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {
1198 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1199 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1200 switch (typed_value.ty.zigTypeTag()) {
1201 .Pointer => {
1202 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
1203 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1204 const decl = payload.decl;
1205 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
1206 return MCValue{ .memory = got_addr };
1207 }
1208 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
1209 },
1210 .Int => {
1211 const info = typed_value.ty.intInfo(self.target.*);
1212 if (info.bits > ptr_bits or info.signed) {
1213 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
1214 }
1215 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
1216 },
1217 .Bool => {
1218 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1219 },
1220 .ComptimeInt => unreachable, // semantic analysis prevents this
1221 .ComptimeFloat => unreachable, // semantic analysis prevents this
1222 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
1274 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {
1275 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1276 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1277 switch (typed_value.ty.zigTypeTag()) {
1278 .Pointer => {
1279 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
1280 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1281 const decl = payload.decl;
1282 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
1283 return MCValue{ .memory = got_addr };
1284 }
1285 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
1286 },
1287 .Int => {
1288 const info = typed_value.ty.intInfo(self.target.*);
1289 if (info.bits > ptr_bits or info.signed) {
1290 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
1291 }
1292 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
1293 },
1294 .Bool => {
1295 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1296 },
1297 .ComptimeInt => unreachable, // semantic analysis prevents this
1298 .ComptimeFloat => unreachable, // semantic analysis prevents this
1299 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
1300 }
12231301 }
1224 }
12251302
1226 fn resolveParameters(
1227 self: *Function,
1228 src: usize,
1229 cc: std.builtin.CallingConvention,
1230 param_types: []const Type,
1231 results: []MCValue,
1232 ) !u32 {
1233 switch (self.target.cpu.arch) {
1234 .x86_64 => {
1235 switch (cc) {
1236 .Naked => {
1237 assert(results.len == 0);
1238 return 0;
1239 },
1240 .Unspecified, .C => {
1241 var next_int_reg: usize = 0;
1242 var next_stack_offset: u32 = 0;
1243
1244 const integer_registers = [_]Reg(.x86_64){ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
1245 for (param_types) |ty, i| {
1246 switch (ty.zigTypeTag()) {
1247 .Bool, .Int => {
1248 if (next_int_reg >= integer_registers.len) {
1249 results[i] = .{ .stack_offset = next_stack_offset };
1250 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
1251 } else {
1252 results[i] = .{ .register = @enumToInt(integer_registers[next_int_reg]) };
1253 next_int_reg += 1;
1254 }
1255 },
1256 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
1303 fn resolveParameters(
1304 self: *Self,
1305 src: usize,
1306 cc: std.builtin.CallingConvention,
1307 param_types: []const Type,
1308 results: []MCValue,
1309 ) !u32 {
1310 switch (arch) {
1311 .x86_64 => {
1312 switch (cc) {
1313 .Naked => {
1314 assert(results.len == 0);
1315 return 0;
1316 },
1317 .Unspecified, .C => {
1318 var next_int_reg: usize = 0;
1319 var next_stack_offset: u32 = 0;
1320
1321 for (param_types) |ty, i| {
1322 switch (ty.zigTypeTag()) {
1323 .Bool, .Int => {
1324 if (next_int_reg >= c_abi_int_param_regs.len) {
1325 results[i] = .{ .stack_offset = next_stack_offset };
1326 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
1327 } else {
1328 results[i] = .{ .register = c_abi_int_param_regs[next_int_reg] };
1329 next_int_reg += 1;
1330 }
1331 },
1332 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
1333 }
12571334 }
1258 }
1259 return next_stack_offset;
1260 },
1261 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1262 }
1263 },
1264 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),
1335 return next_stack_offset;
1336 },
1337 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1338 }
1339 },
1340 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),
1341 }
12651342 }
1266 }
12671343
1268 fn fail(self: *Function, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
1269 @setCold(true);
1270 assert(self.err_msg == null);
1271 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
1272 return error.CodegenFail;
1273 }
1274};
1344 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
1345 @setCold(true);
1346 assert(self.err_msg == null);
1347 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
1348 return error.CodegenFail;
1349 }
12751350
1276const x86_64 = @import("codegen/x86_64.zig");
1277const x86 = @import("codegen/x86.zig");
1351 usingnamespace switch (arch) {
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 {
1280 return switch (arch) {
1281 .i386 => x86.Register,
1282 .x86_64 => x86_64.Register,
1283 else => @compileError("TODO add more register enums"),
1284 };
1285}
1358 pub fn allocIndex(self: Register) ?u4 {
1359 return null;
1360 }
1361 };
1362 pub const callee_preserved_regs = [_]Register{};
1363 },
1364 };
12861365
1287fn parseRegName(comptime arch: Target.Cpu.Arch, name: []const u8) ?Reg(arch) {
1288 return std.meta.stringToEnum(Reg(arch), name);
1366 /// An integer whose bits represent all the registers and whether they are free.
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 };
12891373}
src-self-hosted/codegen/c.zig+10-11
......@@ -92,9 +92,9 @@ fn genFn(file: *C, decl: *Decl) !void {
9292 for (instructions) |inst| {
9393 try writer.writeAll("\n\t");
9494 switch (inst.tag) {
95 .assembly => try genAsm(file, inst.cast(Inst.Assembly).?, decl),
96 .call => try genCall(file, inst.cast(Inst.Call).?, decl),
97 .ret => try genRet(file, inst.cast(Inst.Ret).?, decl, tv.ty.fnReturnType()),
95 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
96 .call => try genCall(file, inst.castTag(.call).?, decl),
97 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),
9898 .retvoid => try file.main.writer().print("return;", .{}),
9999 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100100 }
......@@ -105,9 +105,9 @@ fn genFn(file: *C, decl: *Decl) !void {
105105 try writer.writeAll("}\n\n");
106106}
107107
108fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !void {
108fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !void {
109109 const writer = file.main.writer();
110 const ret_value = inst.args.operand;
110 const ret_value = inst.operand;
111111 const value = ret_value.value().?;
112112 if (expected_return_type.eql(ret_value.ty))
113113 return file.fail(decl.src(), "TODO return {}", .{expected_return_type})
......@@ -126,7 +126,7 @@ fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !v
126126fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
127127 const writer = file.main.writer();
128128 const header = file.header.writer();
129 if (inst.args.func.cast(Inst.Constant)) |func_inst| {
129 if (inst.func.castTag(.constant)) |func_inst| {
130130 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
131131 const target = func_val.func.owner_decl;
132132 const target_ty = target.typed_value.most_recent.typed_value.ty;
......@@ -144,7 +144,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
144144 } else {
145145 return file.fail(decl.src(), "TODO non-function call target?", .{});
146146 }
147 if (inst.args.args.len != 0) {
147 if (inst.args.len != 0) {
148148 return file.fail(decl.src(), "TODO function arguments", .{});
149149 }
150150 } else {
......@@ -152,14 +152,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
152152 }
153153}
154154
155fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {
156 const as = inst.args;
155fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
157156 const writer = file.main.writer();
158157 for (as.inputs) |i, index| {
159158 if (i[0] == '{' and i[i.len - 1] == '}') {
160159 const reg = i[1 .. i.len - 1];
161160 const arg = as.args[index];
162 if (arg.cast(Inst.Constant)) |c| {
161 if (arg.castTag(.constant)) |c| {
163162 if (c.val.tag() == .int_u64) {
164163 try writer.writeAll("register ");
165164 try renderType(file, writer, arg.ty, decl.src());
......@@ -190,7 +189,7 @@ fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {
190189 if (index > 0) {
191190 try writer.writeAll(", ");
192191 }
193 if (arg.cast(Inst.Constant)) |c| {
192 if (arg.castTag(.constant)) |c| {
194193 try writer.print("\"\"({}_constant)", .{reg});
195194 } else {
196195 // This is blocked by the earlier test
src-self-hosted/codegen/x86.zig+14
......@@ -25,6 +25,20 @@ pub const Register = enum(u8) {
2525 pub fn id(self: @This()) u3 {
2626 return @truncate(u3, @enumToInt(self));
2727 }
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 }
2840};
2941
3042// 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) {
3838 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
3939
4040 /// Returns the bit-width of the register.
41 pub fn size(self: @This()) u7 {
41 pub fn size(self: Register) u7 {
4242 return switch (@enumToInt(self)) {
4343 0...15 => 64,
4444 16...31 => 32,
......@@ -53,7 +53,7 @@ pub const Register = enum(u8) {
5353 /// other variant of access to those registers, such as r8b, r15d, and so
5454 /// on. This is needed because access to these registers requires special
5555 /// 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 {
5757 return @enumToInt(self) & 0x08 != 0;
5858 }
5959
......@@ -62,9 +62,29 @@ pub const Register = enum(u8) {
6262 /// an instruction (@see isExtended), and requires special handling. The
6363 /// lower three bits are often embedded directly in instructions (such as
6464 /// 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 {
6666 return @truncate(u4, @enumToInt(self));
6767 }
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 }
6884};
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 {
3838
3939 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
4040 assert(index < deaths_bits);
41 return @truncate(u1, self.deaths << index) != 0;
41 return @truncate(u1, self.deaths >> index) != 0;
4242 }
4343
4444 pub fn specialOperandDeaths(self: Inst) bool {
......@@ -55,7 +55,12 @@ pub const Inst = struct {
5555 breakpoint,
5656 brvoid,
5757 call,
58 cmp,
58 cmp_lt,
59 cmp_lte,
60 cmp_eq,
61 cmp_gte,
62 cmp_gt,
63 cmp_neq,
5964 condbr,
6065 constant,
6166 isnonnull,
......@@ -66,13 +71,80 @@ pub const Inst = struct {
6671 sub,
6772 unreach,
6873 not,
74
75 /// There is one-to-one correspondence between tag and type for now,
76 /// but this will not always be the case. For example, binary operations
77 /// such as + and - will have different tags but the same type.
78 pub fn Type(tag: Tag) type {
79 return switch (tag) {
80 .retvoid,
81 .unreach,
82 .arg,
83 .breakpoint,
84 => NoOp,
85
86 .ret,
87 .bitcast,
88 .not,
89 .isnonnull,
90 .isnull,
91 .ptrtoint,
92 => UnOp,
93
94 .add,
95 .sub,
96 .cmp_lt,
97 .cmp_lte,
98 .cmp_eq,
99 .cmp_gte,
100 .cmp_gt,
101 .cmp_neq,
102 => BinOp,
103
104 .assembly => Assembly,
105 .block => Block,
106 .br => Br,
107 .brvoid => BrVoid,
108 .call => Call,
109 .condbr => CondBr,
110 .constant => Constant,
111 };
112 }
113
114 pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
115 return switch (op) {
116 .lt => .cmp_lt,
117 .lte => .cmp_lte,
118 .eq => .cmp_eq,
119 .gte => .cmp_gte,
120 .gt => .cmp_gt,
121 .neq => .cmp_neq,
122 };
123 }
69124 };
70125
126 /// Prefer `castTag` to this.
71127 pub fn cast(base: *Inst, comptime T: type) ?*T {
72 if (base.tag != T.base_tag)
73 return null;
128 if (@hasField(T, "base_tag")) {
129 return base.castTag(T.base_tag);
130 }
131 inline for (@typeInfo(Tag).Enum.fields) |field| {
132 const tag = @intToEnum(Tag, field.value);
133 if (base.tag == tag) {
134 if (T == tag.Type()) {
135 return @fieldParentPtr(T, "base", base);
136 }
137 return null;
138 }
139 }
140 unreachable;
141 }
74142
75 return @fieldParentPtr(T, "base", base);
143 pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() {
144 if (base.tag == tag) {
145 return @fieldParentPtr(tag.Type(), "base", base);
146 }
147 return null;
76148 }
77149
78150 pub fn Args(comptime T: type) type {
......@@ -88,186 +160,219 @@ pub const Inst = struct {
88160 return inst.val;
89161 }
90162
91 pub const Add = struct {
92 pub const base_tag = Tag.add;
163 pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
164 return switch (self.base.tag) {
165 .cmp_lt => .lt,
166 .cmp_lte => .lte,
167 .cmp_eq => .eq,
168 .cmp_gte => .gte,
169 .cmp_gt => .gt,
170 .cmp_neq => .neq,
171 else => null,
172 };
173 }
174
175 pub fn operandCount(base: *Inst) usize {
176 inline for (@typeInfo(Tag).Enum.fields) |field| {
177 const tag = @intToEnum(Tag, field.value);
178 if (tag == base.tag) {
179 return @fieldParentPtr(tag.Type(), "base", base).operandCount();
180 }
181 }
182 unreachable;
183 }
184
185 pub fn getOperand(base: *Inst, index: usize) ?*Inst {
186 inline for (@typeInfo(Tag).Enum.fields) |field| {
187 const tag = @intToEnum(Tag, field.value);
188 if (tag == base.tag) {
189 return @fieldParentPtr(tag.Type(), "base", base).getOperand(index);
190 }
191 }
192 unreachable;
193 }
194
195 pub const NoOp = struct {
93196 base: Inst,
94197
95 args: struct {
96 lhs: *Inst,
97 rhs: *Inst,
98 },
198 pub fn operandCount(self: *const NoOp) usize {
199 return 0;
200 }
201 pub fn getOperand(self: *const NoOp, index: usize) ?*Inst {
202 return null;
203 }
99204 };
100205
101 pub const Arg = struct {
102 pub const base_tag = Tag.arg;
206 pub const UnOp = struct {
103207 base: Inst,
104 args: void,
208 operand: *Inst,
209
210 pub fn operandCount(self: *const UnOp) usize {
211 return 1;
212 }
213 pub fn getOperand(self: *const UnOp, index: usize) ?*Inst {
214 if (index == 0)
215 return self.operand;
216 return null;
217 }
105218 };
106219
107 pub const Assembly = struct {
108 pub const base_tag = Tag.assembly;
220 pub const BinOp = struct {
109221 base: Inst,
222 lhs: *Inst,
223 rhs: *Inst,
110224
111 args: struct {
112 asm_source: []const u8,
113 is_volatile: bool,
114 output: ?[]const u8,
115 inputs: []const []const u8,
116 clobbers: []const []const u8,
117 args: []const *Inst,
118 },
225 pub fn operandCount(self: *const BinOp) usize {
226 return 2;
227 }
228 pub fn getOperand(self: *const BinOp, index: usize) ?*Inst {
229 var i = index;
230
231 if (i < 1)
232 return self.lhs;
233 i -= 1;
234
235 if (i < 1)
236 return self.rhs;
237 i -= 1;
238
239 return null;
240 }
119241 };
120242
121 pub const BitCast = struct {
122 pub const base_tag = Tag.bitcast;
243 pub const Assembly = struct {
244 pub const base_tag = Tag.assembly;
123245
124246 base: Inst,
125 args: struct {
126 operand: *Inst,
127 },
247 asm_source: []const u8,
248 is_volatile: bool,
249 output: ?[]const u8,
250 inputs: []const []const u8,
251 clobbers: []const []const u8,
252 args: []const *Inst,
253
254 pub fn operandCount(self: *const Assembly) usize {
255 return self.args.len;
256 }
257 pub fn getOperand(self: *const Assembly, index: usize) ?*Inst {
258 if (index < self.args.len)
259 return self.args[index];
260 return null;
261 }
128262 };
129263
130264 pub const Block = struct {
131265 pub const base_tag = Tag.block;
266
132267 base: Inst,
133 args: struct {
134 body: Body,
135 },
268 body: Body,
136269 /// This memory is reserved for codegen code to do whatever it needs to here.
137270 codegen: codegen.BlockData = .{},
271
272 pub fn operandCount(self: *const Block) usize {
273 return 0;
274 }
275 pub fn getOperand(self: *const Block, index: usize) ?*Inst {
276 return null;
277 }
138278 };
139279
140280 pub const Br = struct {
141281 pub const base_tag = Tag.br;
142 base: Inst,
143 args: struct {
144 block: *Block,
145 operand: *Inst,
146 },
147 };
148282
149 pub const Breakpoint = struct {
150 pub const base_tag = Tag.breakpoint;
151283 base: Inst,
152 args: void,
284 block: *Block,
285 operand: *Inst,
286
287 pub fn operandCount(self: *const Br) usize {
288 return 0;
289 }
290 pub fn getOperand(self: *const Br, index: usize) ?*Inst {
291 if (index == 0)
292 return self.operand;
293 return null;
294 }
153295 };
154296
155297 pub const BrVoid = struct {
156298 pub const base_tag = Tag.brvoid;
299
157300 base: Inst,
158 args: struct {
159 block: *Block,
160 },
301 block: *Block,
302
303 pub fn operandCount(self: *const BrVoid) usize {
304 return 0;
305 }
306 pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst {
307 return null;
308 }
161309 };
162310
163311 pub const Call = struct {
164312 pub const base_tag = Tag.call;
313
165314 base: Inst,
166 args: struct {
167 func: *Inst,
168 args: []const *Inst,
169 },
170 };
315 func: *Inst,
316 args: []const *Inst,
171317
172 pub const Cmp = struct {
173 pub const base_tag = Tag.cmp;
318 pub fn operandCount(self: *const Call) usize {
319 return self.args.len + 1;
320 }
321 pub fn getOperand(self: *const Call, index: usize) ?*Inst {
322 var i = index;
174323
175 base: Inst,
176 args: struct {
177 lhs: *Inst,
178 op: std.math.CompareOperator,
179 rhs: *Inst,
180 },
324 if (i < 1)
325 return self.func;
326 i -= 1;
327
328 if (i < self.args.len)
329 return self.args[i];
330 i -= self.args.len;
331
332 return null;
333 }
181334 };
182335
183336 pub const CondBr = struct {
184337 pub const base_tag = Tag.condbr;
185338
186339 base: Inst,
187 args: struct {
188 condition: *Inst,
189 true_body: Body,
190 false_body: Body,
191 },
340 condition: *Inst,
341 then_body: Body,
342 else_body: Body,
192343 /// Set of instructions whose lifetimes end at the start of one of the branches.
193344 /// The `true` branch is first: `deaths[0..true_death_count]`.
194345 /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`.
195346 deaths: [*]*Inst = undefined,
196347 true_death_count: u32 = 0,
197348 false_death_count: u32 = 0,
198 };
199349
200 pub const Not = struct {
201 pub const base_tag = Tag.not;
350 pub fn operandCount(self: *const CondBr) usize {
351 return 1;
352 }
353 pub fn getOperand(self: *const CondBr, index: usize) ?*Inst {
354 var i = index;
202355
203 base: Inst,
204 args: struct {
205 operand: *Inst,
206 },
356 if (i < 1)
357 return self.condition;
358 i -= 1;
359
360 return null;
361 }
207362 };
208363
209364 pub const Constant = struct {
210365 pub const base_tag = Tag.constant;
211 base: Inst,
212
213 val: Value,
214 };
215
216 pub const IsNonNull = struct {
217 pub const base_tag = Tag.isnonnull;
218
219 base: Inst,
220 args: struct {
221 operand: *Inst,
222 },
223 };
224
225 pub const IsNull = struct {
226 pub const base_tag = Tag.isnull;
227366
228367 base: Inst,
229 args: struct {
230 operand: *Inst,
231 },
232 };
233
234 pub const PtrToInt = struct {
235 pub const base_tag = Tag.ptrtoint;
236
237 base: Inst,
238 args: struct {
239 ptr: *Inst,
240 },
241 };
242
243 pub const Ret = struct {
244 pub const base_tag = Tag.ret;
245 base: Inst,
246 args: struct {
247 operand: *Inst,
248 },
249 };
250
251 pub const RetVoid = struct {
252 pub const base_tag = Tag.retvoid;
253 base: Inst,
254 args: void,
255 };
256
257 pub const Sub = struct {
258 pub const base_tag = Tag.sub;
259 base: Inst,
260
261 args: struct {
262 lhs: *Inst,
263 rhs: *Inst,
264 },
265 };
368 val: Value,
266369
267 pub const Unreach = struct {
268 pub const base_tag = Tag.unreach;
269 base: Inst,
270 args: void,
370 pub fn operandCount(self: *const Constant) usize {
371 return 0;
372 }
373 pub fn getOperand(self: *const Constant, index: usize) ?*Inst {
374 return null;
375 }
271376 };
272377};
273378
src-self-hosted/liveness.zig+25-65
......@@ -25,53 +25,38 @@ fn analyzeWithTable(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst,
2525 while (i != 0) {
2626 i -= 1;
2727 const base = body.instructions[i];
28 try analyzeInstGeneric(arena, table, base);
28 try analyzeInst(arena, table, base);
2929 }
3030}
3131
32fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void {
33 // Obtain the corresponding instruction type based on the tag type.
34 inline for (std.meta.declarations(ir.Inst)) |decl| {
35 switch (decl.data) {
36 .Type => |T| {
37 if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) {
38 if (T.base_tag == base.tag) {
39 return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base));
40 }
41 }
42 },
43 else => {},
44 }
45 }
46 unreachable;
47}
48
49fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void {
50 if (table.contains(&inst.base)) {
51 inst.base.deaths = 0;
32fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void {
33 if (table.contains(base)) {
34 base.deaths = 0;
5235 } else {
5336 // No tombstone for this instruction means it is never referenced,
5437 // and its birth marks its own death. Very metal 🤘
55 inst.base.deaths = 1 << ir.Inst.unreferenced_bit_index;
38 base.deaths = 1 << ir.Inst.unreferenced_bit_index;
5639 }
5740
58 switch (T) {
59 ir.Inst.Constant => return,
60 ir.Inst.Block => {
61 try analyzeWithTable(arena, table, inst.args.body);
41 switch (base.tag) {
42 .constant => return,
43 .block => {
44 const inst = base.castTag(.block).?;
45 try analyzeWithTable(arena, table, inst.body);
6246 // We let this continue so that it can possibly mark the block as
6347 // unreferenced below.
6448 },
65 ir.Inst.CondBr => {
49 .condbr => {
50 const inst = base.castTag(.condbr).?;
6651 var true_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
6752 defer true_table.deinit();
68 try true_table.ensureCapacity(inst.args.true_body.instructions.len);
69 try analyzeWithTable(arena, &true_table, inst.args.true_body);
53 try true_table.ensureCapacity(inst.then_body.instructions.len);
54 try analyzeWithTable(arena, &true_table, inst.then_body);
7055
7156 var false_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
7257 defer false_table.deinit();
73 try false_table.ensureCapacity(inst.args.false_body.instructions.len);
74 try analyzeWithTable(arena, &false_table, inst.args.false_body);
58 try false_table.ensureCapacity(inst.else_body.instructions.len);
59 try analyzeWithTable(arena, &false_table, inst.else_body);
7560
7661 // Each death that occurs inside one branch, but not the other, needs
7762 // to be added as a death immediately upon entering the other branch.
......@@ -112,47 +97,22 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
11297 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
11398 // condition's lifetime ends immediately before entering any branch.
11499 },
115 ir.Inst.Call => {
116 // Call instructions have a runtime-known number of operands so we have to handle them ourselves here.
117 const needed_bits = 1 + inst.args.args.len;
118 if (needed_bits <= ir.Inst.deaths_bits) {
119 var bit_i: ir.Inst.DeathsBitIndex = 0;
120 {
121 const prev = try table.fetchPut(inst.args.func, {});
122 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
123 bit_i += 1;
124 }
125 for (inst.args.args) |arg| {
126 const prev = try table.fetchPut(arg, {});
127 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
128 bit_i += 1;
129 }
130 } else {
131 @panic("Handle liveness analysis for function calls with many parameters");
132 }
133 },
134100 else => {},
135101 }
136102
137 const Args = ir.Inst.Args(T);
138 if (Args == void) {
139 return;
140 }
141
142 comptime var arg_index: usize = 0;
143 inline for (std.meta.fields(Args)) |field| {
144 if (field.field_type == *ir.Inst) {
145 if (arg_index >= 6) {
146 @compileError("out of bits to mark deaths of operands");
147 }
148 const prev = try table.fetchPut(@field(inst.args, field.name), {});
103 const needed_bits = base.operandCount();
104 if (needed_bits <= ir.Inst.deaths_bits) {
105 var bit_i: ir.Inst.DeathsBitIndex = 0;
106 while (base.getOperand(bit_i)) |operand| : (bit_i += 1) {
107 const prev = try table.fetchPut(operand, {});
149108 if (prev == null) {
150109 // Death.
151 inst.base.deaths |= 1 << arg_index;
110 base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
152111 }
153 arg_index += 1;
154112 }
113 } else {
114 @panic("Handle liveness analysis for instructions with many parameters");
155115 }
156116
157 std.log.debug(.liveness, "analyze {}: 0b{b}\n", .{ inst.base.tag, inst.base.deaths });
117 std.log.debug(.liveness, "analyze {}: 0b{b}\n", .{ base.tag, base.deaths });
158118}
src-self-hosted/zir.zig+142-189
......@@ -337,7 +337,7 @@ pub const Inst = struct {
337337 base: Inst,
338338
339339 positionals: struct {
340 ptr: *Inst,
340 operand: *Inst,
341341 },
342342 kw_args: struct {},
343343 };
......@@ -629,8 +629,8 @@ pub const Inst = struct {
629629
630630 positionals: struct {
631631 condition: *Inst,
632 true_body: Module.Body,
633 false_body: Module.Body,
632 then_body: Module.Body,
633 else_body: Module.Body,
634634 },
635635 kw_args: struct {},
636636 };
......@@ -1615,7 +1615,7 @@ const EmitZIR = struct {
16151615 }
16161616 }
16171617
1618 fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
1618 fn emitNoOp(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst {
16191619 const new_inst = try self.arena.allocator.create(T);
16201620 new_inst.* = .{
16211621 .base = .{
......@@ -1628,6 +1628,72 @@ const EmitZIR = struct {
16281628 return &new_inst.base;
16291629 }
16301630
1631 fn emitCmp(
1632 self: *EmitZIR,
1633 src: usize,
1634 new_body: ZirBody,
1635 old_inst: *ir.Inst.BinOp,
1636 op: std.math.CompareOperator,
1637 ) Allocator.Error!*Inst {
1638 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1639 new_inst.* = .{
1640 .base = .{
1641 .src = src,
1642 .tag = Inst.Cmp.base_tag,
1643 },
1644 .positionals = .{
1645 .lhs = try self.resolveInst(new_body, old_inst.lhs),
1646 .rhs = try self.resolveInst(new_body, old_inst.rhs),
1647 .op = op,
1648 },
1649 .kw_args = .{},
1650 };
1651 return &new_inst.base;
1652 }
1653
1654 fn emitUnOp(
1655 self: *EmitZIR,
1656 src: usize,
1657 new_body: ZirBody,
1658 old_inst: *ir.Inst.UnOp,
1659 comptime I: type,
1660 ) Allocator.Error!*Inst {
1661 const new_inst = try self.arena.allocator.create(I);
1662 new_inst.* = .{
1663 .base = .{
1664 .src = src,
1665 .tag = I.base_tag,
1666 },
1667 .positionals = .{
1668 .operand = try self.resolveInst(new_body, old_inst.operand),
1669 },
1670 .kw_args = .{},
1671 };
1672 return &new_inst.base;
1673 }
1674
1675 fn emitBinOp(
1676 self: *EmitZIR,
1677 src: usize,
1678 new_body: ZirBody,
1679 old_inst: *ir.Inst.BinOp,
1680 comptime I: type,
1681 ) Allocator.Error!*Inst {
1682 const new_inst = try self.arena.allocator.create(I);
1683 new_inst.* = .{
1684 .base = .{
1685 .src = src,
1686 .tag = I.base_tag,
1687 },
1688 .positionals = .{
1689 .lhs = try self.resolveInst(new_body, old_inst.lhs),
1690 .rhs = try self.resolveInst(new_body, old_inst.rhs),
1691 },
1692 .kw_args = .{},
1693 };
1694 return &new_inst.base;
1695 }
1696
16311697 fn emitBody(
16321698 self: *EmitZIR,
16331699 body: ir.Body,
......@@ -1640,69 +1706,48 @@ const EmitZIR = struct {
16401706 };
16411707 for (body.instructions) |inst| {
16421708 const new_inst = switch (inst.tag) {
1643 .not => blk: {
1644 const old_inst = inst.cast(ir.Inst.Not).?;
1645 assert(inst.ty.zigTypeTag() == .Bool);
1646 const new_inst = try self.arena.allocator.create(Inst.BoolNot);
1647 new_inst.* = .{
1648 .base = .{
1649 .src = inst.src,
1650 .tag = Inst.BoolNot.base_tag,
1651 },
1652 .positionals = .{
1653 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1654 },
1655 .kw_args = .{},
1656 };
1657 break :blk &new_inst.base;
1658 },
1659 .add => blk: {
1660 const old_inst = inst.cast(ir.Inst.Add).?;
1661 const new_inst = try self.arena.allocator.create(Inst.Add);
1662 new_inst.* = .{
1663 .base = .{
1664 .src = inst.src,
1665 .tag = Inst.Add.base_tag,
1666 },
1667 .positionals = .{
1668 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1669 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1670 },
1671 .kw_args = .{},
1672 };
1673 break :blk &new_inst.base;
1674 },
1675 .sub => blk: {
1676 const old_inst = inst.cast(ir.Inst.Sub).?;
1677 const new_inst = try self.arena.allocator.create(Inst.Sub);
1709 .constant => unreachable, // excluded from function bodies
1710
1711 .arg => try self.emitNoOp(inst.src, Inst.Arg),
1712 .breakpoint => try self.emitNoOp(inst.src, Inst.Breakpoint),
1713 .unreach => try self.emitNoOp(inst.src, Inst.Unreachable),
1714 .retvoid => try self.emitNoOp(inst.src, Inst.ReturnVoid),
1715
1716 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, Inst.BoolNot),
1717 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, Inst.Return),
1718 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, Inst.PtrToInt),
1719 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, Inst.IsNull),
1720 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, Inst.IsNonNull),
1721
1722 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, Inst.Add),
1723 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, Inst.Sub),
1724
1725 .cmp_lt => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_lt).?, .lt),
1726 .cmp_lte => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_lte).?, .lte),
1727 .cmp_eq => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_eq).?, .eq),
1728 .cmp_gte => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_gte).?, .gte),
1729 .cmp_gt => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_gt).?, .gt),
1730 .cmp_neq => try self.emitCmp(inst.src, new_body, inst.castTag(.cmp_neq).?, .neq),
1731
1732 .bitcast => blk: {
1733 const old_inst = inst.castTag(.bitcast).?;
1734 const new_inst = try self.arena.allocator.create(Inst.BitCast);
16781735 new_inst.* = .{
16791736 .base = .{
16801737 .src = inst.src,
1681 .tag = Inst.Sub.base_tag,
1738 .tag = Inst.BitCast.base_tag,
16821739 },
16831740 .positionals = .{
1684 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1685 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1686 },
1687 .kw_args = .{},
1688 };
1689 break :blk &new_inst.base;
1690 },
1691 .arg => blk: {
1692 const old_inst = inst.cast(ir.Inst.Arg).?;
1693 const new_inst = try self.arena.allocator.create(Inst.Arg);
1694 new_inst.* = .{
1695 .base = .{
1696 .src = inst.src,
1697 .tag = Inst.Arg.base_tag,
1741 .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
1742 .operand = try self.resolveInst(new_body, old_inst.operand),
16981743 },
1699 .positionals = .{},
17001744 .kw_args = .{},
17011745 };
17021746 break :blk &new_inst.base;
17031747 },
1748
17041749 .block => blk: {
1705 const old_inst = inst.cast(ir.Inst.Block).?;
1750 const old_inst = inst.castTag(.block).?;
17061751 const new_inst = try self.arena.allocator.create(Inst.Block);
17071752
17081753 try self.block_table.put(old_inst, new_inst);
......@@ -1710,7 +1755,7 @@ const EmitZIR = struct {
17101755 var block_body = std.ArrayList(*Inst).init(self.allocator);
17111756 defer block_body.deinit();
17121757
1713 try self.emitBody(old_inst.args.body, inst_table, &block_body);
1758 try self.emitBody(old_inst.body, inst_table, &block_body);
17141759
17151760 new_inst.* = .{
17161761 .base = .{
......@@ -1725,47 +1770,49 @@ const EmitZIR = struct {
17251770
17261771 break :blk &new_inst.base;
17271772 },
1728 .br => blk: {
1729 const old_inst = inst.cast(ir.Inst.Br).?;
1730 const new_block = self.block_table.get(old_inst.args.block).?;
1731 const new_inst = try self.arena.allocator.create(Inst.Break);
1773
1774 .brvoid => blk: {
1775 const old_inst = inst.cast(ir.Inst.BrVoid).?;
1776 const new_block = self.block_table.get(old_inst.block).?;
1777 const new_inst = try self.arena.allocator.create(Inst.BreakVoid);
17321778 new_inst.* = .{
17331779 .base = .{
17341780 .src = inst.src,
1735 .tag = Inst.Break.base_tag,
1781 .tag = Inst.BreakVoid.base_tag,
17361782 },
17371783 .positionals = .{
17381784 .block = new_block,
1739 .operand = try self.resolveInst(new_body, old_inst.args.operand),
17401785 },
17411786 .kw_args = .{},
17421787 };
17431788 break :blk &new_inst.base;
17441789 },
1745 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1746 .brvoid => blk: {
1747 const old_inst = inst.cast(ir.Inst.BrVoid).?;
1748 const new_block = self.block_table.get(old_inst.args.block).?;
1749 const new_inst = try self.arena.allocator.create(Inst.BreakVoid);
1790
1791 .br => blk: {
1792 const old_inst = inst.castTag(.br).?;
1793 const new_block = self.block_table.get(old_inst.block).?;
1794 const new_inst = try self.arena.allocator.create(Inst.Break);
17501795 new_inst.* = .{
17511796 .base = .{
17521797 .src = inst.src,
1753 .tag = Inst.BreakVoid.base_tag,
1798 .tag = Inst.Break.base_tag,
17541799 },
17551800 .positionals = .{
17561801 .block = new_block,
1802 .operand = try self.resolveInst(new_body, old_inst.operand),
17571803 },
17581804 .kw_args = .{},
17591805 };
17601806 break :blk &new_inst.base;
17611807 },
1808
17621809 .call => blk: {
1763 const old_inst = inst.cast(ir.Inst.Call).?;
1810 const old_inst = inst.castTag(.call).?;
17641811 const new_inst = try self.arena.allocator.create(Inst.Call);
17651812
1766 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1813 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len);
17671814 for (args) |*elem, i| {
1768 elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
1815 elem.* = try self.resolveInst(new_body, old_inst.args[i]);
17691816 }
17701817 new_inst.* = .{
17711818 .base = .{
......@@ -1773,48 +1820,31 @@ const EmitZIR = struct {
17731820 .tag = Inst.Call.base_tag,
17741821 },
17751822 .positionals = .{
1776 .func = try self.resolveInst(new_body, old_inst.args.func),
1823 .func = try self.resolveInst(new_body, old_inst.func),
17771824 .args = args,
17781825 },
17791826 .kw_args = .{},
17801827 };
17811828 break :blk &new_inst.base;
17821829 },
1783 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
1784 .ret => blk: {
1785 const old_inst = inst.cast(ir.Inst.Ret).?;
1786 const new_inst = try self.arena.allocator.create(Inst.Return);
1787 new_inst.* = .{
1788 .base = .{
1789 .src = inst.src,
1790 .tag = Inst.Return.base_tag,
1791 },
1792 .positionals = .{
1793 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1794 },
1795 .kw_args = .{},
1796 };
1797 break :blk &new_inst.base;
1798 },
1799 .retvoid => try self.emitTrivial(inst.src, Inst.ReturnVoid),
1800 .constant => unreachable, // excluded from function bodies
1830
18011831 .assembly => blk: {
1802 const old_inst = inst.cast(ir.Inst.Assembly).?;
1832 const old_inst = inst.castTag(.assembly).?;
18031833 const new_inst = try self.arena.allocator.create(Inst.Asm);
18041834
1805 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
1835 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.inputs.len);
18061836 for (inputs) |*elem, i| {
1807 elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.inputs[i])).inst;
1837 elem.* = (try self.emitStringLiteral(inst.src, old_inst.inputs[i])).inst;
18081838 }
18091839
1810 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
1840 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.clobbers.len);
18111841 for (clobbers) |*elem, i| {
1812 elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i])).inst;
1842 elem.* = (try self.emitStringLiteral(inst.src, old_inst.clobbers[i])).inst;
18131843 }
18141844
1815 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1845 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.len);
18161846 for (args) |*elem, i| {
1817 elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
1847 elem.* = try self.resolveInst(new_body, old_inst.args[i]);
18181848 }
18191849
18201850 new_inst.* = .{
......@@ -1823,12 +1853,12 @@ const EmitZIR = struct {
18231853 .tag = Inst.Asm.base_tag,
18241854 },
18251855 .positionals = .{
1826 .asm_source = (try self.emitStringLiteral(inst.src, old_inst.args.asm_source)).inst,
1856 .asm_source = (try self.emitStringLiteral(inst.src, old_inst.asm_source)).inst,
18271857 .return_type = (try self.emitType(inst.src, inst.ty)).inst,
18281858 },
18291859 .kw_args = .{
1830 .@"volatile" = old_inst.args.is_volatile,
1831 .output = if (old_inst.args.output) |o|
1860 .@"volatile" = old_inst.is_volatile,
1861 .output = if (old_inst.output) |o|
18321862 (try self.emitStringLiteral(inst.src, o)).inst
18331863 else
18341864 null,
......@@ -1839,65 +1869,18 @@ const EmitZIR = struct {
18391869 };
18401870 break :blk &new_inst.base;
18411871 },
1842 .ptrtoint => blk: {
1843 const old_inst = inst.cast(ir.Inst.PtrToInt).?;
1844 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
1845 new_inst.* = .{
1846 .base = .{
1847 .src = inst.src,
1848 .tag = Inst.PtrToInt.base_tag,
1849 },
1850 .positionals = .{
1851 .ptr = try self.resolveInst(new_body, old_inst.args.ptr),
1852 },
1853 .kw_args = .{},
1854 };
1855 break :blk &new_inst.base;
1856 },
1857 .bitcast => blk: {
1858 const old_inst = inst.cast(ir.Inst.BitCast).?;
1859 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1860 new_inst.* = .{
1861 .base = .{
1862 .src = inst.src,
1863 .tag = Inst.BitCast.base_tag,
1864 },
1865 .positionals = .{
1866 .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
1867 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1868 },
1869 .kw_args = .{},
1870 };
1871 break :blk &new_inst.base;
1872 },
1873 .cmp => blk: {
1874 const old_inst = inst.cast(ir.Inst.Cmp).?;
1875 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1876 new_inst.* = .{
1877 .base = .{
1878 .src = inst.src,
1879 .tag = Inst.Cmp.base_tag,
1880 },
1881 .positionals = .{
1882 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1883 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1884 .op = old_inst.args.op,
1885 },
1886 .kw_args = .{},
1887 };
1888 break :blk &new_inst.base;
1889 },
1872
18901873 .condbr => blk: {
1891 const old_inst = inst.cast(ir.Inst.CondBr).?;
1874 const old_inst = inst.castTag(.condbr).?;
18921875
1893 var true_body = std.ArrayList(*Inst).init(self.allocator);
1894 var false_body = std.ArrayList(*Inst).init(self.allocator);
1876 var then_body = std.ArrayList(*Inst).init(self.allocator);
1877 var else_body = std.ArrayList(*Inst).init(self.allocator);
18951878
1896 defer true_body.deinit();
1897 defer false_body.deinit();
1879 defer then_body.deinit();
1880 defer else_body.deinit();
18981881
1899 try self.emitBody(old_inst.args.true_body, inst_table, &true_body);
1900 try self.emitBody(old_inst.args.false_body, inst_table, &false_body);
1882 try self.emitBody(old_inst.then_body, inst_table, &then_body);
1883 try self.emitBody(old_inst.else_body, inst_table, &else_body);
19011884
19021885 const new_inst = try self.arena.allocator.create(Inst.CondBr);
19031886 new_inst.* = .{
......@@ -1906,39 +1889,9 @@ const EmitZIR = struct {
19061889 .tag = Inst.CondBr.base_tag,
19071890 },
19081891 .positionals = .{
1909 .condition = try self.resolveInst(new_body, old_inst.args.condition),
1910 .true_body = .{ .instructions = true_body.toOwnedSlice() },
1911 .false_body = .{ .instructions = false_body.toOwnedSlice() },
1912 },
1913 .kw_args = .{},
1914 };
1915 break :blk &new_inst.base;
1916 },
1917 .isnull => blk: {
1918 const old_inst = inst.cast(ir.Inst.IsNull).?;
1919 const new_inst = try self.arena.allocator.create(Inst.IsNull);
1920 new_inst.* = .{
1921 .base = .{
1922 .src = inst.src,
1923 .tag = Inst.IsNull.base_tag,
1924 },
1925 .positionals = .{
1926 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1927 },
1928 .kw_args = .{},
1929 };
1930 break :blk &new_inst.base;
1931 },
1932 .isnonnull => blk: {
1933 const old_inst = inst.cast(ir.Inst.IsNonNull).?;
1934 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
1935 new_inst.* = .{
1936 .base = .{
1937 .src = inst.src,
1938 .tag = Inst.IsNonNull.base_tag,
1939 },
1940 .positionals = .{
1941 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1892 .condition = try self.resolveInst(new_body, old_inst.condition),
1893 .then_body = .{ .instructions = then_body.toOwnedSlice() },
1894 .else_body = .{ .instructions = else_body.toOwnedSlice() },
19421895 },
19431896 .kw_args = .{},
19441897 };
test/stage2/compare_output.zig+83-5
......@@ -169,9 +169,8 @@ pub fn addCases(ctx: *TestContext) !void {
169169 ,
170170 "",
171171 );
172 }
173 {
174 var case = ctx.exe("assert function", linux_x64);
172
173 // Tests the assert() function.
175174 case.addCompareOutput(
176175 \\export fn _start() noreturn {
177176 \\ add(3, 4);
......@@ -199,15 +198,94 @@ pub fn addCases(ctx: *TestContext) !void {
199198 ,
200199 "",
201200 );
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.
202204 case.addCompareOutput(
203205 \\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);
205275 \\
206276 \\ exit();
207277 \\}
208278 \\
209279 \\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);
211289 \\}
212290 \\
213291 \\pub fn assert(ok: bool) void {