authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-16 18:22:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 12:19:16-07:00
log8082660118bba78de00e1e103e53730a87b2b70f
treeb66a61ec4b338600dba6dbb6ecdaffd0acec3a51
parenteadbee2041bba1cd03b24d8f30161025af8e3590

stage2: codegen.zig updated to new AIR memory layout


7 files changed, 946 insertions(+), 809 deletions(-)

src/Air.zig+130-13
...@@ -13,9 +13,9 @@ const Air = @This();...@@ -13,9 +13,9 @@ const Air = @This();
13instructions: std.MultiArrayList(Inst).Slice,13instructions: std.MultiArrayList(Inst).Slice,
14/// The meaning of this data is determined by `Inst.Tag` value.14/// The meaning of this data is determined by `Inst.Tag` value.
15/// The first few indexes are reserved. See `ExtraIndex` for the values.15/// The first few indexes are reserved. See `ExtraIndex` for the values.
16extra: []u32,16extra: []const u32,
17values: []Value,17values: []const Value,
18variables: []*Module.Var,18variables: []const *Module.Var,
1919
20pub const ExtraIndex = enum(u32) {20pub const ExtraIndex = enum(u32) {
21 /// Payload index of the main `Block` in the `extra` array.21 /// Payload index of the main `Block` in the `extra` array.
...@@ -378,22 +378,109 @@ pub fn getMainBody(air: Air) []const Air.Inst.Index {...@@ -378,22 +378,109 @@ pub fn getMainBody(air: Air) []const Air.Inst.Index {
378 return air.extra[extra.end..][0..extra.data.body_len];378 return air.extra[extra.end..][0..extra.data.body_len];
379}379}
380380
381pub fn getType(air: Air, inst: Air.Inst.Index) Type {381pub fn typeOf(air: Air, inst: Air.Inst.Ref) Type {
382 _ = air;382 const ref_int = @enumToInt(inst);
383 _ = inst;383 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
384 @panic("TODO Air getType");384 return Air.Inst.Ref.typed_value_map[ref_int].ty;
385 }
386 return air.typeOfIndex(@intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len));
387}
388
389pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
390 const datas = air.instructions.items(.data);
391 switch (air.instructions.items(.tag)[inst]) {
392 .arg => return air.getRefType(datas[inst].ty_str.ty),
393
394 .add,
395 .addwrap,
396 .sub,
397 .subwrap,
398 .mul,
399 .mulwrap,
400 .div,
401 .bit_and,
402 .bit_or,
403 .xor,
404 => return air.typeOf(datas[inst].bin_op.lhs),
405
406 .cmp_lt,
407 .cmp_lte,
408 .cmp_eq,
409 .cmp_gte,
410 .cmp_gt,
411 .cmp_neq,
412 .is_null,
413 .is_non_null,
414 .is_null_ptr,
415 .is_non_null_ptr,
416 .is_err,
417 .is_non_err,
418 .is_err_ptr,
419 .is_non_err_ptr,
420 .bool_and,
421 .bool_or,
422 => return Type.initTag(.bool),
423
424 .const_ty => return Type.initTag(.type),
425
426 .alloc => return datas[inst].ty,
427
428 .assembly,
429 .block,
430 .constant,
431 .varptr,
432 .struct_field_ptr,
433 => return air.getRefType(datas[inst].ty_pl.ty),
434
435 .not,
436 .bitcast,
437 .load,
438 .ref,
439 .floatcast,
440 .intcast,
441 .optional_payload,
442 .optional_payload_ptr,
443 .wrap_optional,
444 .unwrap_errunion_payload,
445 .unwrap_errunion_err,
446 .unwrap_errunion_payload_ptr,
447 .unwrap_errunion_err_ptr,
448 .wrap_errunion_payload,
449 .wrap_errunion_err,
450 => return air.getRefType(datas[inst].ty_op.ty),
451
452 .loop,
453 .br,
454 .cond_br,
455 .switch_br,
456 .ret,
457 .unreach,
458 => return Type.initTag(.noreturn),
459
460 .breakpoint,
461 .dbg_stmt,
462 .store,
463 => return Type.initTag(.void),
464
465 .ptrtoint => return Type.initTag(.usize),
466
467 .call => {
468 const callee_ty = air.typeOf(datas[inst].pl_op.operand);
469 return callee_ty.fnReturnType();
470 },
471 }
385}472}
386473
387pub fn getRefType(air: Air, ref: Air.Inst.Ref) Type {474pub fn getRefType(air: Air, ref: Air.Inst.Ref) Type {
388 var i: usize = @enumToInt(ref);475 const ref_int = @enumToInt(ref);
389 if (i < Air.Inst.Ref.typed_value_map.len) {476 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
390 return Air.Inst.Ref.typed_value_map[i].val.toType(undefined) catch unreachable;477 return Air.Inst.Ref.typed_value_map[ref_int].val.toType(undefined) catch unreachable;
391 }478 }
392 i -= Air.Inst.Ref.typed_value_map.len;479 const inst_index = ref_int - Air.Inst.Ref.typed_value_map.len;
393 const air_tags = air.instructions.items(.tag);480 const air_tags = air.instructions.items(.tag);
394 const air_datas = air.instructions.items(.data);481 const air_datas = air.instructions.items(.data);
395 assert(air_tags[i] == .const_ty);482 assert(air_tags[inst_index] == .const_ty);
396 return air_datas[i].ty;483 return air_datas[inst_index].ty;
397}484}
398485
399/// Returns the requested data, as well as the new index which is at the start of the486/// Returns the requested data, as well as the new index which is at the start of the
...@@ -424,3 +511,33 @@ pub fn deinit(air: *Air, gpa: *std.mem.Allocator) void {...@@ -424,3 +511,33 @@ pub fn deinit(air: *Air, gpa: *std.mem.Allocator) void {
424 gpa.free(air.variables);511 gpa.free(air.variables);
425 air.* = undefined;512 air.* = undefined;
426}513}
514
515const ref_start_index: u32 = Air.Inst.Ref.typed_value_map.len;
516
517pub fn indexToRef(inst: Air.Inst.Index) Air.Inst.Ref {
518 return @intToEnum(Air.Inst.Ref, ref_start_index + inst);
519}
520
521pub fn refToIndex(inst: Air.Inst.Ref) ?Air.Inst.Index {
522 const ref_int = @enumToInt(inst);
523 if (ref_int >= ref_start_index) {
524 return ref_int - ref_start_index;
525 } else {
526 return null;
527 }
528}
529
530/// Returns `null` if runtime-known.
531pub fn value(air: Air, inst: Air.Inst.Ref) ?Value {
532 const ref_int = @enumToInt(inst);
533 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
534 return Air.Inst.Ref.typed_value_map[ref_int].val;
535 }
536 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
537 const air_datas = air.instructions.items(.data);
538 switch (air.instructions.items(.tag)[inst_index]) {
539 .constant => return air.values[air_datas[inst_index].ty_pl.payload],
540 .const_ty => unreachable,
541 else => return air.typeOfIndex(inst_index).onePossibleValue(),
542 }
543}
src/AstGen.zig+46-31
...@@ -6412,37 +6412,12 @@ fn multilineStringLiteral(...@@ -6412,37 +6412,12 @@ fn multilineStringLiteral(
6412 node: ast.Node.Index,6412 node: ast.Node.Index,
6413) InnerError!Zir.Inst.Ref {6413) InnerError!Zir.Inst.Ref {
6414 const astgen = gz.astgen;6414 const astgen = gz.astgen;
6415 const tree = astgen.tree;6415 const str = try astgen.strLitNodeAsString(node);
6416 const node_datas = tree.nodes.items(.data);
6417
6418 const start = node_datas[node].lhs;
6419 const end = node_datas[node].rhs;
6420
6421 const gpa = gz.astgen.gpa;
6422 const string_bytes = &gz.astgen.string_bytes;
6423 const str_index = string_bytes.items.len;
6424
6425 // First line: do not append a newline.
6426 var tok_i = start;
6427 {
6428 const slice = tree.tokenSlice(tok_i);
6429 const line_bytes = slice[2 .. slice.len - 1];
6430 try string_bytes.appendSlice(gpa, line_bytes);
6431 tok_i += 1;
6432 }
6433 // Following lines: each line prepends a newline.
6434 while (tok_i <= end) : (tok_i += 1) {
6435 const slice = tree.tokenSlice(tok_i);
6436 const line_bytes = slice[2 .. slice.len - 1];
6437 try string_bytes.ensureCapacity(gpa, string_bytes.items.len + line_bytes.len + 1);
6438 string_bytes.appendAssumeCapacity('\n');
6439 string_bytes.appendSliceAssumeCapacity(line_bytes);
6440 }
6441 const result = try gz.add(.{6416 const result = try gz.add(.{
6442 .tag = .str,6417 .tag = .str,
6443 .data = .{ .str = .{6418 .data = .{ .str = .{
6444 .start = @intCast(u32, str_index),6419 .start = str.index,
6445 .len = @intCast(u32, string_bytes.items.len - str_index),6420 .len = str.len,
6446 } },6421 } },
6447 });6422 });
6448 return rvalue(gz, rl, result, node);6423 return rvalue(gz, rl, result, node);
...@@ -6620,9 +6595,14 @@ fn asmExpr(...@@ -6620,9 +6595,14 @@ fn asmExpr(
6620 const tree = astgen.tree;6595 const tree = astgen.tree;
6621 const main_tokens = tree.nodes.items(.main_token);6596 const main_tokens = tree.nodes.items(.main_token);
6622 const node_datas = tree.nodes.items(.data);6597 const node_datas = tree.nodes.items(.data);
6598 const node_tags = tree.nodes.items(.tag);
6623 const token_tags = tree.tokens.items(.tag);6599 const token_tags = tree.tokens.items(.tag);
66246600
6625 const asm_source = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);6601 const asm_source = switch (node_tags[full.ast.template]) {
6602 .string_literal => try astgen.strLitAsString(main_tokens[full.ast.template]),
6603 .multiline_string_literal => try astgen.strLitNodeAsString(full.ast.template),
6604 else => return astgen.failNode(node, "assembly code must use string literal syntax", .{}),
6605 };
66266606
6627 // See https://github.com/ziglang/zig/issues/215 and related issues discussing6607 // See https://github.com/ziglang/zig/issues/215 and related issues discussing
6628 // possible inline assembly improvements. Until then here is status quo AstGen6608 // possible inline assembly improvements. Until then here is status quo AstGen
...@@ -6752,7 +6732,7 @@ fn asmExpr(...@@ -6752,7 +6732,7 @@ fn asmExpr(
67526732
6753 const result = try gz.addAsm(.{6733 const result = try gz.addAsm(.{
6754 .node = node,6734 .node = node,
6755 .asm_source = asm_source,6735 .asm_source = asm_source.index,
6756 .is_volatile = full.volatile_token != null,6736 .is_volatile = full.volatile_token != null,
6757 .output_type_bits = output_type_bits,6737 .output_type_bits = output_type_bits,
6758 .outputs = outputs,6738 .outputs = outputs,
...@@ -8579,6 +8559,41 @@ fn strLitAsString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !IndexSlice {...@@ -8579,6 +8559,41 @@ fn strLitAsString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !IndexSlice {
8579 }8559 }
8580}8560}
85818561
8562fn strLitNodeAsString(astgen: *AstGen, node: ast.Node.Index) !IndexSlice {
8563 const tree = astgen.tree;
8564 const node_datas = tree.nodes.items(.data);
8565
8566 const start = node_datas[node].lhs;
8567 const end = node_datas[node].rhs;
8568
8569 const gpa = astgen.gpa;
8570 const string_bytes = &astgen.string_bytes;
8571 const str_index = string_bytes.items.len;
8572
8573 // First line: do not append a newline.
8574 var tok_i = start;
8575 {
8576 const slice = tree.tokenSlice(tok_i);
8577 const line_bytes = slice[2 .. slice.len - 1];
8578 try string_bytes.appendSlice(gpa, line_bytes);
8579 tok_i += 1;
8580 }
8581 // Following lines: each line prepends a newline.
8582 while (tok_i <= end) : (tok_i += 1) {
8583 const slice = tree.tokenSlice(tok_i);
8584 const line_bytes = slice[2 .. slice.len - 1];
8585 try string_bytes.ensureCapacity(gpa, string_bytes.items.len + line_bytes.len + 1);
8586 string_bytes.appendAssumeCapacity('\n');
8587 string_bytes.appendSliceAssumeCapacity(line_bytes);
8588 }
8589 const len = string_bytes.items.len - str_index;
8590 try string_bytes.append(gpa, 0);
8591 return IndexSlice{
8592 .index = @intCast(u32, str_index),
8593 .len = @intCast(u32, len),
8594 };
8595}
8596
8582fn testNameString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !u32 {8597fn testNameString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !u32 {
8583 const gpa = astgen.gpa;8598 const gpa = astgen.gpa;
8584 const string_bytes = &astgen.string_bytes;8599 const string_bytes = &astgen.string_bytes;
...@@ -9440,7 +9455,7 @@ const GenZir = struct {...@@ -9440,7 +9455,7 @@ const GenZir = struct {
9440 args: struct {9455 args: struct {
9441 /// Absolute node index. This function does the conversion to offset from Decl.9456 /// Absolute node index. This function does the conversion to offset from Decl.
9442 node: ast.Node.Index,9457 node: ast.Node.Index,
9443 asm_source: Zir.Inst.Ref,9458 asm_source: u32,
9444 output_type_bits: u32,9459 output_type_bits: u32,
9445 is_volatile: bool,9460 is_volatile: bool,
9446 outputs: []const Zir.Inst.Asm.Output,9461 outputs: []const Zir.Inst.Asm.Output,
src/Liveness.zig+39-15
...@@ -21,7 +21,7 @@ const Log2Int = std.math.Log2Int;...@@ -21,7 +21,7 @@ const Log2Int = std.math.Log2Int;
21/// operand dies after this instruction.21/// operand dies after this instruction.
22/// Instructions which need more data to track liveness have special handling via the22/// Instructions which need more data to track liveness have special handling via the
23/// `special` table.23/// `special` table.
24tomb_bits: []const usize,24tomb_bits: []usize,
25/// Sparse table of specially handled instructions. The value is an index into the `extra`25/// Sparse table of specially handled instructions. The value is an index into the `extra`
26/// array. The meaning of the data depends on the AIR tag.26/// array. The meaning of the data depends on the AIR tag.
27special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),27special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
...@@ -98,7 +98,7 @@ pub fn operandDies(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) bool...@@ -98,7 +98,7 @@ pub fn operandDies(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) bool
98 return (l.tomb_bits[usize_index] & mask) != 0;98 return (l.tomb_bits[usize_index] & mask) != 0;
99}99}
100100
101pub fn clearOperandDeath(l: *Liveness, inst: Air.Inst.Index, operand: OperandInt) void {101pub fn clearOperandDeath(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) void {
102 assert(operand < bpi - 1);102 assert(operand < bpi - 1);
103 const usize_index = (inst * bpi) / @bitSizeOf(usize);103 const usize_index = (inst * bpi) / @bitSizeOf(usize);
104 const mask = @as(usize, 1) <<104 const mask = @as(usize, 1) <<
...@@ -106,16 +106,40 @@ pub fn clearOperandDeath(l: *Liveness, inst: Air.Inst.Index, operand: OperandInt...@@ -106,16 +106,40 @@ pub fn clearOperandDeath(l: *Liveness, inst: Air.Inst.Index, operand: OperandInt
106 l.tomb_bits[usize_index] |= mask;106 l.tomb_bits[usize_index] |= mask;
107}107}
108108
109/// Higher level API.
110pub const CondBrSlices = struct {
111 then_deaths: []const Air.Inst.Index,
112 else_deaths: []const Air.Inst.Index,
113};
114
115pub fn getCondBr(l: Liveness, inst: Air.Inst.Index) CondBrSlices {
116 var index: usize = l.special.get(inst) orelse return .{
117 .then_deaths = &.{},
118 .else_deaths = &.{},
119 };
120 const then_death_count = l.extra[index];
121 index += 1;
122 const else_death_count = l.extra[index];
123 index += 1;
124 const then_deaths = l.extra[index..][0..then_death_count];
125 index += then_death_count;
126 return .{
127 .then_deaths = then_deaths,
128 .else_deaths = l.extra[index..][0..else_death_count],
129 };
130}
131
109pub fn deinit(l: *Liveness, gpa: *Allocator) void {132pub fn deinit(l: *Liveness, gpa: *Allocator) void {
110 gpa.free(l.tomb_bits);133 gpa.free(l.tomb_bits);
111 gpa.free(l.extra);134 gpa.free(l.extra);
112 l.special.deinit(gpa);135 l.special.deinit(gpa);
136 l.* = undefined;
113}137}
114138
115/// How many tomb bits per AIR instruction.139/// How many tomb bits per AIR instruction.
116const bpi = 4;140pub const bpi = 4;
117const Bpi = std.meta.Int(.unsigned, bpi);141pub const Bpi = std.meta.Int(.unsigned, bpi);
118const OperandInt = std.math.Log2Int(Bpi);142pub const OperandInt = std.math.Log2Int(Bpi);
119143
120/// In-progress data; on successful analysis converted into `Liveness`.144/// In-progress data; on successful analysis converted into `Liveness`.
121const Analysis = struct {145const Analysis = struct {
...@@ -267,14 +291,14 @@ fn analyzeInst(...@@ -267,14 +291,14 @@ fn analyzeInst(
267 const inst_data = inst_datas[inst].pl_op;291 const inst_data = inst_datas[inst].pl_op;
268 const callee = inst_data.operand;292 const callee = inst_data.operand;
269 const extra = a.air.extraData(Air.Call, inst_data.payload);293 const extra = a.air.extraData(Air.Call, inst_data.payload);
270 const args = a.air.extra[extra.end..][0..extra.data.args_len];294 const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..extra.data.args_len]);
271 if (args.len <= bpi - 2) {295 if (args.len <= bpi - 2) {
272 var buf: [bpi - 1]Air.Inst.Ref = undefined;296 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
273 buf[0] = callee;297 buf[0] = callee;
274 std.mem.copy(Air.Inst.Ref, buf[1..], @bitCast([]const Air.Inst.Ref, args));298 std.mem.copy(Air.Inst.Ref, buf[1..], args);
275 return trackOperands(a, new_set, inst, main_tomb, buf);299 return trackOperands(a, new_set, inst, main_tomb, buf);
276 }300 }
277 @panic("TODO: liveness analysis for function with greater than 2 args");301 @panic("TODO: liveness analysis for function call with greater than 2 args");
278 },302 },
279 .struct_field_ptr => {303 .struct_field_ptr => {
280 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;304 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
...@@ -285,12 +309,12 @@ fn analyzeInst(...@@ -285,12 +309,12 @@ fn analyzeInst(
285 const extended = a.zir.instructions.items(.data)[extra.data.zir_index].extended;309 const extended = a.zir.instructions.items(.data)[extra.data.zir_index].extended;
286 const outputs_len = @truncate(u5, extended.small);310 const outputs_len = @truncate(u5, extended.small);
287 const inputs_len = @truncate(u5, extended.small >> 5);311 const inputs_len = @truncate(u5, extended.small >> 5);
288 const outputs = a.air.extra[extra.end..][0..outputs_len];312 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]);
289 const inputs = a.air.extra[extra.end + outputs.len ..][0..inputs_len];313 const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]);
290 if (outputs.len + inputs.len <= bpi - 1) {314 if (outputs.len + args.len <= bpi - 1) {
291 var buf: [bpi - 1]Air.Inst.Ref = undefined;315 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
292 std.mem.copy(Air.Inst.Ref, &buf, @bitCast([]const Air.Inst.Ref, outputs));316 std.mem.copy(Air.Inst.Ref, &buf, outputs);
293 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], @bitCast([]const Air.Inst.Ref, inputs));317 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
294 return trackOperands(a, new_set, inst, main_tomb, buf);318 return trackOperands(a, new_set, inst, main_tomb, buf);
295 }319 }
296 @panic("TODO: liveness analysis for asm with greater than 3 args");320 @panic("TODO: liveness analysis for asm with greater than 3 args");
src/Module.zig+2-2
...@@ -1309,7 +1309,7 @@ pub const Scope = struct {...@@ -1309,7 +1309,7 @@ pub const Scope = struct {
1309 const result_index = @intCast(Air.Inst.Index, sema.air_instructions.len);1309 const result_index = @intCast(Air.Inst.Index, sema.air_instructions.len);
1310 sema.air_instructions.appendAssumeCapacity(inst);1310 sema.air_instructions.appendAssumeCapacity(inst);
1311 block.instructions.appendAssumeCapacity(result_index);1311 block.instructions.appendAssumeCapacity(result_index);
1312 return Sema.indexToRef(result_index);1312 return Air.indexToRef(result_index);
1313 }1313 }
1314 };1314 };
1315};1315};
...@@ -3533,7 +3533,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {...@@ -3533,7 +3533,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3533 const ty_ref = try sema.addType(param_type);3533 const ty_ref = try sema.addType(param_type);
3534 const arg_index = @intCast(u32, sema.air_instructions.len);3534 const arg_index = @intCast(u32, sema.air_instructions.len);
3535 inner_block.instructions.appendAssumeCapacity(arg_index);3535 inner_block.instructions.appendAssumeCapacity(arg_index);
3536 param_inst.* = Sema.indexToRef(arg_index);3536 param_inst.* = Air.indexToRef(arg_index);
3537 try sema.air_instructions.append(gpa, .{3537 try sema.air_instructions.append(gpa, .{
3538 .tag = .arg,3538 .tag = .arg,
3539 .data = .{3539 .data = .{
src/Sema.zig+23-127
...@@ -1301,7 +1301,7 @@ fn zirArg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A...@@ -1301,7 +1301,7 @@ fn zirArg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
13011301
1302 // Set the name of the Air.Arg instruction for use by codegen debug info.1302 // Set the name of the Air.Arg instruction for use by codegen debug info.
1303 const air_arg = sema.param_inst_list[arg_index];1303 const air_arg = sema.param_inst_list[arg_index];
1304 sema.air_instructions.items(.data)[refToIndex(air_arg).?].ty_str.str = inst_data.start;1304 sema.air_instructions.items(.data)[Air.refToIndex(air_arg).?].ty_str.str = inst_data.start;
1305 return air_arg;1305 return air_arg;
1306}1306}
13071307
...@@ -1389,7 +1389,7 @@ fn zirAllocInferred(...@@ -1389,7 +1389,7 @@ fn zirAllocInferred(
1389 // to the block even though it is currently a `.constant`.1389 // to the block even though it is currently a `.constant`.
1390 const result = try sema.addConstant(inferred_alloc_ty, Value.initPayload(&val_payload.base));1390 const result = try sema.addConstant(inferred_alloc_ty, Value.initPayload(&val_payload.base));
1391 try sema.requireFunctionBlock(block, src);1391 try sema.requireFunctionBlock(block, src);
1392 try block.instructions.append(sema.gpa, refToIndex(result).?);1392 try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
1393 return result;1393 return result;
1394}1394}
13951395
...@@ -1400,7 +1400,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde...@@ -1400,7 +1400,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
1400 const inst_data = sema.code.instructions.items(.data)[inst].un_node;1400 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1401 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };1401 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
1402 const ptr = sema.resolveInst(inst_data.operand);1402 const ptr = sema.resolveInst(inst_data.operand);
1403 const ptr_inst = refToIndex(ptr).?;1403 const ptr_inst = Air.refToIndex(ptr).?;
1404 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);1404 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
1405 const air_datas = sema.air_instructions.items(.data);1405 const air_datas = sema.air_instructions.items(.data);
1406 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];1406 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
...@@ -1586,7 +1586,7 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)...@@ -1586,7 +1586,7 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
1586 const bin_inst = sema.code.instructions.items(.data)[inst].bin;1586 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
1587 const ptr = sema.resolveInst(bin_inst.lhs);1587 const ptr = sema.resolveInst(bin_inst.lhs);
1588 const value = sema.resolveInst(bin_inst.rhs);1588 const value = sema.resolveInst(bin_inst.rhs);
1589 const ptr_inst = refToIndex(ptr).?;1589 const ptr_inst = Air.refToIndex(ptr).?;
1590 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);1590 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
1591 const air_datas = sema.air_instructions.items(.data);1591 const air_datas = sema.air_instructions.items(.data);
1592 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];1592 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
...@@ -1968,13 +1968,13 @@ fn analyzeBlockBody(...@@ -1968,13 +1968,13 @@ fn analyzeBlockBody(
19681968
1969 // Blocks must terminate with noreturn instruction.1969 // Blocks must terminate with noreturn instruction.
1970 assert(child_block.instructions.items.len != 0);1970 assert(child_block.instructions.items.len != 0);
1971 assert(sema.typeOf(indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1])).isNoReturn());1971 assert(sema.typeOf(Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1])).isNoReturn());
19721972
1973 if (merges.results.items.len == 0) {1973 if (merges.results.items.len == 0) {
1974 // No need for a block instruction. We can put the new instructions1974 // No need for a block instruction. We can put the new instructions
1975 // directly into the parent block.1975 // directly into the parent block.
1976 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);1976 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
1977 return indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1]);1977 return Air.indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1]);
1978 }1978 }
1979 if (merges.results.items.len == 1) {1979 if (merges.results.items.len == 1) {
1980 const last_inst_index = child_block.instructions.items.len - 1;1980 const last_inst_index = child_block.instructions.items.len - 1;
...@@ -2025,7 +2025,7 @@ fn analyzeBlockBody(...@@ -2025,7 +2025,7 @@ fn analyzeBlockBody(
2025 continue;2025 continue;
2026 }2026 }
2027 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] ==2027 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] ==
2028 refToIndex(coerced_operand).?);2028 Air.refToIndex(coerced_operand).?);
20292029
2030 // Convert the br operand to a block.2030 // Convert the br operand to a block.
2031 const br_operand_ty_ref = try sema.addType(br_operand_ty);2031 const br_operand_ty_ref = try sema.addType(br_operand_ty);
...@@ -2034,7 +2034,7 @@ fn analyzeBlockBody(...@@ -2034,7 +2034,7 @@ fn analyzeBlockBody(
2034 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);2034 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
2035 const sub_block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);2035 const sub_block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
2036 const sub_br_inst = sub_block_inst + 1;2036 const sub_br_inst = sub_block_inst + 1;
2037 sema.air_instructions.items(.data)[br].br.operand = indexToRef(sub_block_inst);2037 sema.air_instructions.items(.data)[br].br.operand = Air.indexToRef(sub_block_inst);
2038 sema.air_instructions.appendAssumeCapacity(.{2038 sema.air_instructions.appendAssumeCapacity(.{
2039 .tag = .block,2039 .tag = .block,
2040 .data = .{ .ty_pl = .{2040 .data = .{ .ty_pl = .{
...@@ -2054,7 +2054,7 @@ fn analyzeBlockBody(...@@ -2054,7 +2054,7 @@ fn analyzeBlockBody(
2054 } },2054 } },
2055 });2055 });
2056 }2056 }
2057 return indexToRef(merges.block_inst);2057 return Air.indexToRef(merges.block_inst);
2058}2058}
20592059
2060fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {2060fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
...@@ -2149,7 +2149,7 @@ fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) Compil...@@ -2149,7 +2149,7 @@ fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: Zir.Inst.Index) Compil
2149 if (label.zir_block == zir_block) {2149 if (label.zir_block == zir_block) {
2150 const br_ref = try start_block.addBr(label.merges.block_inst, operand);2150 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
2151 try label.merges.results.append(sema.gpa, operand);2151 try label.merges.results.append(sema.gpa, operand);
2152 try label.merges.br_list.append(sema.gpa, refToIndex(br_ref).?);2152 try label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?);
2153 return inst;2153 return inst;
2154 }2154 }
2155 }2155 }
...@@ -5310,7 +5310,7 @@ fn zirBoolBr(...@@ -5310,7 +5310,7 @@ fn zirBoolBr(
5310 } } });5310 } } });
53115311
5312 try parent_block.instructions.append(gpa, block_inst);5312 try parent_block.instructions.append(gpa, block_inst);
5313 return indexToRef(block_inst);5313 return Air.indexToRef(block_inst);
5314}5314}
53155315
5316fn zirIsNonNull(5316fn zirIsNonNull(
...@@ -7204,7 +7204,7 @@ fn analyzeVarRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, tv: TypedVal...@@ -7204,7 +7204,7 @@ fn analyzeVarRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, tv: TypedVal
7204 } },7204 } },
7205 });7205 });
7206 try block.instructions.append(gpa, result_inst);7206 try block.instructions.append(gpa, result_inst);
7207 return indexToRef(result_inst);7207 return Air.indexToRef(result_inst);
7208}7208}
72097209
7210fn analyzeRef(7210fn analyzeRef(
...@@ -8021,107 +8021,18 @@ fn enumFieldSrcLoc(...@@ -8021,107 +8021,18 @@ fn enumFieldSrcLoc(
8021 } else unreachable;8021 } else unreachable;
8022}8022}
80238023
8024/// This is only meant to be called by `typeOf`.
8025fn analyzeAsTypeInfallible(sema: *Sema, inst: Air.Inst.Ref) Type {
8026 var i: usize = @enumToInt(inst);
8027 if (i < Air.Inst.Ref.typed_value_map.len) {
8028 return Air.Inst.Ref.typed_value_map[i].val.toType(undefined) catch unreachable;
8029 }
8030 i -= Air.Inst.Ref.typed_value_map.len;
8031 assert(sema.air_instructions.items(.tag)[i] == .const_ty);
8032 return sema.air_instructions.items(.data)[i].ty;
8033}
8034
8035/// Returns the type of the AIR instruction.8024/// Returns the type of the AIR instruction.
8036fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {8025fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
8037 var i: usize = @enumToInt(inst);8026 return sema.getTmpAir().typeOf(inst);
8038 if (i < Air.Inst.Ref.typed_value_map.len) {8027}
8039 return Air.Inst.Ref.typed_value_map[i].ty;
8040 }
8041 i -= Air.Inst.Ref.typed_value_map.len;
80428028
8043 const air_datas = sema.air_instructions.items(.data);8029fn getTmpAir(sema: Sema) Air {
8044 switch (sema.air_instructions.items(.tag)[i]) {8030 return .{
8045 .arg => return sema.analyzeAsTypeInfallible(air_datas[i].ty_str.ty),8031 .instructions = sema.air_instructions.slice(),
80468032 .extra = sema.air_extra.items,
8047 .add,8033 .values = sema.air_values.items,
8048 .addwrap,8034 .variables = sema.air_variables.items,
8049 .sub,8035 };
8050 .subwrap,
8051 .mul,
8052 .mulwrap,
8053 .div,
8054 .bit_and,
8055 .bit_or,
8056 .xor,
8057 => return sema.typeOf(air_datas[i].bin_op.lhs),
8058
8059 .cmp_lt,
8060 .cmp_lte,
8061 .cmp_eq,
8062 .cmp_gte,
8063 .cmp_gt,
8064 .cmp_neq,
8065 .is_null,
8066 .is_non_null,
8067 .is_null_ptr,
8068 .is_non_null_ptr,
8069 .is_err,
8070 .is_non_err,
8071 .is_err_ptr,
8072 .is_non_err_ptr,
8073 .bool_and,
8074 .bool_or,
8075 => return Type.initTag(.bool),
8076
8077 .const_ty => return Type.initTag(.type),
8078
8079 .alloc => return air_datas[i].ty,
8080
8081 .assembly,
8082 .block,
8083 .constant,
8084 .varptr,
8085 .struct_field_ptr,
8086 => return sema.analyzeAsTypeInfallible(air_datas[i].ty_pl.ty),
8087
8088 .not,
8089 .bitcast,
8090 .load,
8091 .ref,
8092 .floatcast,
8093 .intcast,
8094 .optional_payload,
8095 .optional_payload_ptr,
8096 .wrap_optional,
8097 .unwrap_errunion_payload,
8098 .unwrap_errunion_err,
8099 .unwrap_errunion_payload_ptr,
8100 .unwrap_errunion_err_ptr,
8101 .wrap_errunion_payload,
8102 .wrap_errunion_err,
8103 => return sema.analyzeAsTypeInfallible(air_datas[i].ty_op.ty),
8104
8105 .loop,
8106 .br,
8107 .cond_br,
8108 .switch_br,
8109 .ret,
8110 .unreach,
8111 => return Type.initTag(.noreturn),
8112
8113 .breakpoint,
8114 .dbg_stmt,
8115 .store,
8116 => return Type.initTag(.void),
8117
8118 .ptrtoint => return Type.initTag(.usize),
8119
8120 .call => {
8121 const callee_ty = sema.typeOf(air_datas[i].pl_op.operand);
8122 return callee_ty.fnReturnType();
8123 },
8124 }
8125}8036}
81268037
8127pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {8038pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
...@@ -8185,7 +8096,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {...@@ -8185,7 +8096,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
8185 .tag = .const_ty,8096 .tag = .const_ty,
8186 .data = .{ .ty = ty },8097 .data = .{ .ty = ty },
8187 });8098 });
8188 return indexToRef(@intCast(u32, sema.air_instructions.len - 1));8099 return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1));
8189}8100}
81908101
8191fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {8102fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
...@@ -8207,22 +8118,7 @@ fn addConstant(sema: *Sema, ty: Type, val: Value) CompileError!Air.Inst.Ref {...@@ -8207,22 +8118,7 @@ fn addConstant(sema: *Sema, ty: Type, val: Value) CompileError!Air.Inst.Ref {
8207 .payload = @intCast(u32, sema.air_values.items.len - 1),8118 .payload = @intCast(u32, sema.air_values.items.len - 1),
8208 } },8119 } },
8209 });8120 });
8210 return indexToRef(@intCast(u32, sema.air_instructions.len - 1));8121 return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1));
8211}
8212
8213const ref_start_index: u32 = Air.Inst.Ref.typed_value_map.len;
8214
8215pub fn indexToRef(inst: Air.Inst.Index) Air.Inst.Ref {
8216 return @intToEnum(Air.Inst.Ref, ref_start_index + inst);
8217}
8218
8219pub fn refToIndex(inst: Air.Inst.Ref) ?Air.Inst.Index {
8220 const ref_int = @enumToInt(inst);
8221 if (ref_int >= ref_start_index) {
8222 return ref_int - ref_start_index;
8223 } else {
8224 return null;
8225 }
8226}8122}
82278123
8228pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {8124pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {
src/Zir.zig+4-2
...@@ -2176,7 +2176,8 @@ pub const Inst = struct {...@@ -2176,7 +2176,8 @@ pub const Inst = struct {
2176 /// 2. clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.2176 /// 2. clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
2177 pub const Asm = struct {2177 pub const Asm = struct {
2178 src_node: i32,2178 src_node: i32,
2179 asm_source: Ref,2179 // null-terminated string index
2180 asm_source: u32,
2180 /// 1 bit for each outputs_len: whether it uses `-> T` or not.2181 /// 1 bit for each outputs_len: whether it uses `-> T` or not.
2181 /// 0b0 - operand is a pointer to where to store the output.2182 /// 0b0 - operand is a pointer to where to store the output.
2182 /// 0b1 - operand is a type; asm expression has the output as the result.2183 /// 0b1 - operand is a type; asm expression has the output as the result.
...@@ -3383,9 +3384,10 @@ const Writer = struct {...@@ -3383,9 +3384,10 @@ const Writer = struct {
3383 const inputs_len = @truncate(u5, extended.small >> 5);3384 const inputs_len = @truncate(u5, extended.small >> 5);
3384 const clobbers_len = @truncate(u5, extended.small >> 10);3385 const clobbers_len = @truncate(u5, extended.small >> 10);
3385 const is_volatile = @truncate(u1, extended.small >> 15) != 0;3386 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3387 const asm_source = self.code.nullTerminatedString(extra.data.asm_source);
33863388
3387 try self.writeFlag(stream, "volatile, ", is_volatile);3389 try self.writeFlag(stream, "volatile, ", is_volatile);
3388 try self.writeInstRef(stream, extra.data.asm_source);3390 try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)});
3389 try stream.writeAll(", ");3391 try stream.writeAll(", ");
33903392
3391 var extra_i: usize = extra.end;3393 var extra_i: usize = extra.end;
src/codegen.zig+702-619
...@@ -3,6 +3,7 @@ const mem = std.mem;...@@ -3,6 +3,7 @@ const mem = std.mem;
3const math = std.math;3const math = std.math;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const Air = @import("Air.zig");5const Air = @import("Air.zig");
6const Zir = @import("Zir.zig");
6const Liveness = @import("Liveness.zig");7const Liveness = @import("Liveness.zig");
7const Type = @import("type.zig").Type;8const Type = @import("type.zig").Type;
8const Value = @import("value.zig").Value;9const Value = @import("value.zig").Value;
...@@ -337,6 +338,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -337,6 +338,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
337 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.338 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
338 next_stack_offset: u32 = 0,339 next_stack_offset: u32 = 0,
339340
341 /// Debug field, used to find bugs in the compiler.
342 air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
343
344 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
345
340 const MCValue = union(enum) {346 const MCValue = union(enum) {
341 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.347 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
342 /// TODO Look into deleting this tag and using `dead` instead, since every use348 /// TODO Look into deleting this tag and using `dead` instead, since every use
...@@ -751,24 +757,91 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -751,24 +757,91 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
751 }757 }
752758
753 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {759 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
754 for (body) |inst| {760 const air_tags = self.air.instructions.items(.tag);
755 const tomb_bits = self.liveness.getTombBits(inst);
756 try self.ensureProcessDeathCapacity(@popCount(@TypeOf(tomb_bits), tomb_bits));
757761
758 const mcv = try self.genFuncInst(inst);762 for (body) |inst| {
759 if (!self.liveness.isUnused(inst)) {763 const old_air_bookkeeping = self.air_bookkeeping;
760 log.debug("{} => {}", .{ inst, mcv });764 try self.ensureProcessDeathCapacity(Liveness.bpi);
761 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];765
762 try branch.inst_table.putNoClobber(self.gpa, inst, mcv);766 switch (air_tags[inst]) {
767 // zig fmt: off
768 .add => try self.airAdd(inst),
769 .addwrap => try self.airAddWrap(inst),
770 .sub => try self.airSub(inst),
771 .subwrap => try self.airSubWrap(inst),
772 .mul => try self.airMul(inst),
773 .mulwrap => try self.airMulWrap(inst),
774 .div => try self.airDiv(inst),
775
776 .cmp_lt => try self.airCmp(inst, .lt),
777 .cmp_lte => try self.airCmp(inst, .lte),
778 .cmp_eq => try self.airCmp(inst, .eq),
779 .cmp_gte => try self.airCmp(inst, .gte),
780 .cmp_gt => try self.airCmp(inst, .gt),
781 .cmp_neq => try self.airCmp(inst, .neq),
782
783 .bool_and => try self.airBoolOp(inst),
784 .bool_or => try self.airBoolOp(inst),
785 .bit_and => try self.airBitAnd(inst),
786 .bit_or => try self.airBitOr(inst),
787 .xor => try self.airXor(inst),
788
789 .alloc => try self.airAlloc(inst),
790 .arg => try self.airArg(inst),
791 .assembly => try self.airAsm(inst),
792 .bitcast => try self.airBitCast(inst),
793 .block => try self.airBlock(inst),
794 .br => try self.airBr(inst),
795 .breakpoint => try self.airBreakpoint(),
796 .call => try self.airCall(inst),
797 .cond_br => try self.airCondBr(inst),
798 .dbg_stmt => try self.airDbgStmt(inst),
799 .floatcast => try self.airFloatCast(inst),
800 .intcast => try self.airIntCast(inst),
801 .is_non_null => try self.airIsNonNull(inst),
802 .is_non_null_ptr => try self.airIsNonNullPtr(inst),
803 .is_null => try self.airIsNull(inst),
804 .is_null_ptr => try self.airIsNullPtr(inst),
805 .is_non_err => try self.airIsNonErr(inst),
806 .is_non_err_ptr => try self.airIsNonErrPtr(inst),
807 .is_err => try self.airIsErr(inst),
808 .is_err_ptr => try self.airIsErrPtr(inst),
809 .load => try self.airLoad(inst),
810 .loop => try self.airLoop(inst),
811 .not => try self.airNot(inst),
812 .ptrtoint => try self.airPtrToInt(inst),
813 .ref => try self.airRef(inst),
814 .ret => try self.airRet(inst),
815 .store => try self.airStore(inst),
816 .struct_field_ptr=> try self.airStructFieldPtr(inst),
817 .switch_br => try self.airSwitch(inst),
818 .varptr => try self.airVarPtr(inst),
819
820 .constant => unreachable, // excluded from function bodies
821 .const_ty => unreachable, // excluded from function bodies
822 .unreach => self.finishAirBookkeeping(),
823
824 .optional_payload => try self.airOptionalPayload(inst),
825 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst),
826 .unwrap_errunion_err => try self.airUnwrapErrErr(inst),
827 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
828 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
829 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
830
831 .wrap_optional => try self.airWrapOptional(inst),
832 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
833 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
834 // zig fmt: on
835 }
836 if (std.debug.runtime_safety) {
837 if (self.air_bookkeeping != old_air_bookkeeping + 1) {
838 std.debug.panic(
839 \\in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping.
840 \\Look for a missing call to finishAir or an extra call to it.
841 \\
842 , .{ inst, air_tags[inst] });
843 }
763 }844 }
764
765 // TODO inline this logic into every instruction
766 @panic("TODO rework AIR memory layout codegen for processing deaths");
767 //var i: ir.Inst.DeathsBitIndex = 0;
768 //while (inst.getOperand(i)) |operand| : (i += 1) {
769 // if (inst.operandDies(i))
770 // self.processDeath(operand);
771 //}
772 }845 }
773 }846 }
774847
...@@ -833,9 +906,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -833,9 +906,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
833 }906 }
834 }907 }
835908
909 /// Called when there are no operands, and the instruction is always unreferenced.
910 fn finishAirBookkeeping(self: *Self) void {
911 if (std.debug.runtime_safety) {
912 self.air_bookkeeping += 1;
913 }
914 }
915
916 fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
917 var tomb_bits = self.liveness.getTombBits(inst);
918 for (operands) |op| {
919 const dies = @truncate(u1, tomb_bits) != 0;
920 tomb_bits >>= 1;
921 if (!dies) continue;
922 const op_int = @enumToInt(op);
923 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
924 const operand: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len);
925 self.processDeath(operand);
926 }
927 const is_used = @truncate(u1, tomb_bits) == 0;
928 if (is_used) {
929 log.debug("{} => {}", .{ inst, result });
930 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
931 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
932 }
933 self.finishAirBookkeeping();
934 }
935
836 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {936 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
837 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;937 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
838 try table.ensureCapacity(self.gpa, table.count() + additional_count);938 try table.ensureUnusedCapacity(self.gpa, additional_count);
839 }939 }
840940
841 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,941 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
...@@ -860,83 +960,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -860,83 +960,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
860 }960 }
861 }961 }
862962
863 fn genFuncInst(self: *Self, inst: Air.Inst.Index) !MCValue {
864 const air_tags = self.air.instructions.items(.tag);
865 switch (air_tags[inst]) {
866 // zig fmt: off
867 //.add => return self.genAdd(inst.castTag(.add).?),
868 //.addwrap => return self.genAddWrap(inst.castTag(.addwrap).?),
869 //.sub => return self.genSub(inst.castTag(.sub).?),
870 //.subwrap => return self.genSubWrap(inst.castTag(.subwrap).?),
871 //.mul => return self.genMul(inst.castTag(.mul).?),
872 //.mulwrap => return self.genMulWrap(inst.castTag(.mulwrap).?),
873 //.div => return self.genDiv(inst.castTag(.div).?),
874
875 //.cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
876 //.cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),
877 //.cmp_eq => return self.genCmp(inst.castTag(.cmp_eq).?, .eq),
878 //.cmp_gte => return self.genCmp(inst.castTag(.cmp_gte).?, .gte),
879 //.cmp_gt => return self.genCmp(inst.castTag(.cmp_gt).?, .gt),
880 //.cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
881
882 //.bool_and => return self.genBoolOp(inst.castTag(.bool_and).?),
883 //.bool_or => return self.genBoolOp(inst.castTag(.bool_or).?),
884 //.bit_and => return self.genBitAnd(inst.castTag(.bit_and).?),
885 //.bit_or => return self.genBitOr(inst.castTag(.bit_or).?),
886 //.xor => return self.genXor(inst.castTag(.xor).?),
887
888 //.alloc => return self.genAlloc(inst.castTag(.alloc).?),
889 //.arg => return self.genArg(inst.castTag(.arg).?),
890 //.assembly => return self.genAsm(inst.castTag(.assembly).?),
891 //.bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
892 //.block => return self.genBlock(inst.castTag(.block).?),
893 //.br => return self.genBr(inst.castTag(.br).?),
894 //.br_block_flat => return self.genBrBlockFlat(inst.castTag(.br_block_flat).?),
895 //.breakpoint => return self.genBreakpoint(inst.src),
896 //.call => return self.genCall(inst.castTag(.call).?),
897 //.cond_br => return self.genCondBr(inst.castTag(.condbr).?),
898 //.dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),
899 //.floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
900 //.intcast => return self.genIntCast(inst.castTag(.intcast).?),
901 //.is_non_null => return self.genIsNonNull(inst.castTag(.is_non_null).?),
902 //.is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?),
903 //.is_null => return self.genIsNull(inst.castTag(.is_null).?),
904 //.is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
905 //.is_non_err => return self.genIsNonErr(inst.castTag(.is_non_err).?),
906 //.is_non_err_ptr => return self.genIsNonErrPtr(inst.castTag(.is_non_err_ptr).?),
907 //.is_err => return self.genIsErr(inst.castTag(.is_err).?),
908 //.is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
909 //.load => return self.genLoad(inst.castTag(.load).?),
910 //.loop => return self.genLoop(inst.castTag(.loop).?),
911 //.not => return self.genNot(inst.castTag(.not).?),
912 //.ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
913 //.ref => return self.genRef(inst.castTag(.ref).?),
914 //.ret => return self.genRet(inst.castTag(.ret).?),
915 //.store => return self.genStore(inst.castTag(.store).?),
916 //.struct_field_ptr=> return self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),
917 //.switch_br => return self.genSwitch(inst.castTag(.switchbr).?),
918 //.varptr => return self.genVarPtr(inst.castTag(.varptr).?),
919
920 //.constant => unreachable, // excluded from function bodies
921 //.unreach => return MCValue{ .unreach = {} },
922
923 //.optional_payload => return self.genOptionalPayload(inst.castTag(.optional_payload).?),
924 //.optional_payload_ptr => return self.genOptionalPayloadPtr(inst.castTag(.optional_payload_ptr).?),
925 //.unwrap_errunion_err => return self.genUnwrapErrErr(inst.castTag(.unwrap_errunion_err).?),
926 //.unwrap_errunion_payload => return self.genUnwrapErrPayload(inst.castTag(.unwrap_errunion_payload).?),
927 //.unwrap_errunion_err_ptr => return self.genUnwrapErrErrPtr(inst.castTag(.unwrap_errunion_err_ptr).?),
928 //.unwrap_errunion_payload_ptr=> return self.genUnwrapErrPayloadPtr(inst.castTag(.unwrap_errunion_payload_ptr).?),
929
930 //.wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
931 //.wrap_errunion_payload => return self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),
932 //.wrap_errunion_err => return self.genWrapErrUnionErr(inst.castTag(.wrap_errunion_err).?),
933
934 // zig fmt: on
935
936 else => @panic("TODO finish air memory layout branch, more codegen.zig instructions"),
937 }
938 }
939
940 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {963 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
941 if (abi_align > self.stack_align)964 if (abi_align > self.stack_align)
942 self.stack_align = abi_align;965 self.stack_align = abi_align;
...@@ -954,7 +977,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -954,7 +977,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
954977
955 /// Use a pointer instruction as the basis for allocating stack memory.978 /// Use a pointer instruction as the basis for allocating stack memory.
956 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {979 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
957 const elem_ty = self.air.getType(inst).elemType();980 const elem_ty = self.air.typeOfIndex(inst).elemType();
958 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {981 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
959 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});982 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
960 };983 };
...@@ -964,7 +987,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -964,7 +987,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
964 }987 }
965988
966 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {989 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
967 const elem_ty = inst.ty;990 const elem_ty = self.air.typeOfIndex(inst);
968 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {991 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
969 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});992 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
970 };993 };
...@@ -993,7 +1016,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -993,7 +1016,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
993 assert(reg == toCanonicalReg(reg_mcv.register));1016 assert(reg == toCanonicalReg(reg_mcv.register));
994 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1017 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
995 try branch.inst_table.put(self.gpa, inst, stack_mcv);1018 try branch.inst_table.put(self.gpa, inst, stack_mcv);
996 try self.genSetStack(inst.ty, stack_mcv.stack_offset, reg_mcv);1019 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
997 }1020 }
9981021
999 /// Copies a value to a register without tracking the register. The register is not considered1022 /// Copies a value to a register without tracking the register. The register is not considered
...@@ -1010,281 +1033,274 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1010,281 +1033,274 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1010 /// This can have a side effect of spilling instructions to the stack to free up a register.1033 /// This can have a side effect of spilling instructions to the stack to free up a register.
1011 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {1034 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
1012 const reg = try self.register_manager.allocReg(reg_owner, &.{});1035 const reg = try self.register_manager.allocReg(reg_owner, &.{});
1013 try self.genSetReg(reg_owner.ty, reg, mcv);1036 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
1014 return MCValue{ .register = reg };1037 return MCValue{ .register = reg };
1015 }1038 }
10161039
1017 fn genAlloc(self: *Self, inst: Air.Inst.Index) !MCValue {1040 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
1018 const stack_offset = try self.allocMemPtr(inst);1041 const stack_offset = try self.allocMemPtr(inst);
1019 return MCValue{ .ptr_stack_offset = stack_offset };1042 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
1020 }1043 }
10211044
1022 fn genFloatCast(self: *Self, inst: Air.Inst.Index) !MCValue {1045 fn airFloatCast(self: *Self, inst: Air.Inst.Index) !void {
1023 // No side effects, so if it's unreferenced, do nothing.1046 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1024 if (self.liveness.isUnused(inst))1047 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1025 return MCValue.dead;
1026 switch (arch) {
1027 else => return self.fail("TODO implement floatCast for {}", .{self.target.cpu.arch}),1048 else => return self.fail("TODO implement floatCast for {}", .{self.target.cpu.arch}),
1028 }1049 };
1050 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1029 }1051 }
10301052
1031 fn genIntCast(self: *Self, inst: Air.Inst.Index) !MCValue {1053 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
1032 // No side effects, so if it's unreferenced, do nothing.1054 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1033 if (self.liveness.isUnused(inst))1055 if (self.liveness.isUnused(inst))
1034 return MCValue.dead;1056 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
10351057
1036 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1058 const operand_ty = self.air.typeOf(ty_op.operand);
1037 const operand_ty = self.air.getType(ty_op.operand);
1038 const operand = try self.resolveInst(ty_op.operand);1059 const operand = try self.resolveInst(ty_op.operand);
1039 const info_a = operand_ty.intInfo(self.target.*);1060 const info_a = operand_ty.intInfo(self.target.*);
1040 const info_b = self.air.getType(inst).intInfo(self.target.*);1061 const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
1041 if (info_a.signedness != info_b.signedness)1062 if (info_a.signedness != info_b.signedness)
1042 return self.fail("TODO gen intcast sign safety in semantic analysis", .{});1063 return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
10431064
1044 if (info_a.bits == info_b.bits)1065 if (info_a.bits == info_b.bits)
1045 return operand;1066 return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
10461067
1047 switch (arch) {1068 const result: MCValue = switch (arch) {
1048 else => return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}),1069 else => return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}),
1049 }1070 };
1071 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1050 }1072 }
10511073
1052 fn genNot(self: *Self, inst: Air.Inst.Index) !MCValue {1074 fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1053 // No side effects, so if it's unreferenced, do nothing.
1054 if (self.liveness.isUnused(inst))
1055 return MCValue.dead;
1056 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1075 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1057 const operand = try self.resolveInst(ty_op.operand);1076 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1058 switch (operand) {1077 const operand = try self.resolveInst(ty_op.operand);
1059 .dead => unreachable,1078 switch (operand) {
1060 .unreach => unreachable,1079 .dead => unreachable,
1061 .compare_flags_unsigned => |op| return MCValue{1080 .unreach => unreachable,
1062 .compare_flags_unsigned = switch (op) {1081 .compare_flags_unsigned => |op| {
1063 .gte => .lt,1082 const r = MCValue{
1064 .gt => .lte,1083 .compare_flags_unsigned = switch (op) {
1065 .neq => .eq,1084 .gte => .lt,
1066 .lt => .gte,1085 .gt => .lte,
1067 .lte => .gt,1086 .neq => .eq,
1068 .eq => .neq,1087 .lt => .gte,
1088 .lte => .gt,
1089 .eq => .neq,
1090 },
1091 };
1092 break :result r;
1069 },1093 },
1070 },1094 .compare_flags_signed => |op| {
1071 .compare_flags_signed => |op| return MCValue{1095 const r = MCValue{
1072 .compare_flags_signed = switch (op) {1096 .compare_flags_signed = switch (op) {
1073 .gte => .lt,1097 .gte => .lt,
1074 .gt => .lte,1098 .gt => .lte,
1075 .neq => .eq,1099 .neq => .eq,
1076 .lt => .gte,1100 .lt => .gte,
1077 .lte => .gt,1101 .lte => .gt,
1078 .eq => .neq,1102 .eq => .neq,
1103 },
1104 };
1105 break :result r;
1079 },1106 },
1080 },1107 else => {},
1081 else => {},1108 }
1082 }
10831109
1084 switch (arch) {1110 switch (arch) {
1085 .x86_64 => {1111 .x86_64 => {
1086 return try self.genX8664BinMath(inst, ty_op.operand, .bool_true);1112 break :result try self.genX8664BinMath(inst, ty_op.operand, .bool_true);
1087 },1113 },
1088 .arm, .armeb => {1114 .arm, .armeb => {
1089 return try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not);1115 break :result try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not);
1090 },1116 },
1091 else => return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}),1117 else => return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}),
1092 }1118 }
1119 };
1120 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1093 }1121 }
10941122
1095 fn genAdd(self: *Self, inst: Air.Inst.Index) !MCValue {1123 fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
1096 // No side effects, so if it's unreferenced, do nothing.
1097 if (self.liveness.isUnused(inst))
1098 return MCValue.dead;
1099 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1124 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1100 switch (arch) {1125 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1101 .x86_64 => {1126 .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1102 return try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs);1127 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .add),
1103 },
1104 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .add),
1105 else => return self.fail("TODO implement add for {}", .{self.target.cpu.arch}),1128 else => return self.fail("TODO implement add for {}", .{self.target.cpu.arch}),
1106 }1129 };
1130 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1107 }1131 }
11081132
1109 fn genAddWrap(self: *Self, inst: Air.Inst.Index) !MCValue {1133 fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
1110 // No side effects, so if it's unreferenced, do nothing.
1111 if (self.liveness.isUnused(inst))
1112 return MCValue.dead;
1113 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1134 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1114 _ = bin_op;1135 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1115 switch (arch) {
1116 else => return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch}),1136 else => return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch}),
1117 }1137 };
1138 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1118 }1139 }
11191140
1120 fn genMul(self: *Self, inst: Air.Inst.Index) !MCValue {1141 fn airSub(self: *Self, inst: Air.Inst.Index) !void {
1121 // No side effects, so if it's unreferenced, do nothing.
1122 if (self.liveness.isUnused(inst))
1123 return MCValue.dead;
1124 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1142 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1125 switch (arch) {1143 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1126 .x86_64 => return try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),1144 .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1127 .arm, .armeb => return try self.genArmMul(inst, bin_op.lhs, bin_op.rhs),1145 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .sub),
1146 else => return self.fail("TODO implement sub for {}", .{self.target.cpu.arch}),
1147 };
1148 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1149 }
1150
1151 fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
1152 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1153 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1154 else => return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}),
1155 };
1156 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1157 }
1158
1159 fn airMul(self: *Self, inst: Air.Inst.Index) !void {
1160 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1161 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1162 .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1163 .arm, .armeb => try self.genArmMul(inst, bin_op.lhs, bin_op.rhs),
1128 else => return self.fail("TODO implement mul for {}", .{self.target.cpu.arch}),1164 else => return self.fail("TODO implement mul for {}", .{self.target.cpu.arch}),
1129 }1165 };
1166 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1130 }1167 }
11311168
1132 fn genMulWrap(self: *Self, inst: Air.Inst.Index) !MCValue {1169 fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
1133 // No side effects, so if it's unreferenced, do nothing.
1134 if (self.liveness.isUnused(inst))
1135 return MCValue.dead;
1136 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1170 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1137 _ = bin_op;1171 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1138 switch (arch) {
1139 else => return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch}),1172 else => return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch}),
1140 }1173 };
1174 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1141 }1175 }
11421176
1143 fn genDiv(self: *Self, inst: Air.Inst.Index) !MCValue {1177 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1144 // No side effects, so if it's unreferenced, do nothing.
1145 if (self.liveness.isUnused(inst))
1146 return MCValue.dead;
1147 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1178 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1148 _ = bin_op;1179 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1149 switch (arch) {
1150 else => return self.fail("TODO implement div for {}", .{self.target.cpu.arch}),1180 else => return self.fail("TODO implement div for {}", .{self.target.cpu.arch}),
1151 }1181 };
1182 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1152 }1183 }
11531184
1154 fn genBitAnd(self: *Self, inst: Air.Inst.Index) !MCValue {1185 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
1155 // No side effects, so if it's unreferenced, do nothing.
1156 if (self.liveness.isUnused(inst))
1157 return MCValue.dead;
1158 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1186 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1159 switch (arch) {1187 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1160 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and),1188 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and),
1161 else => return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch}),1189 else => return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch}),
1162 }1190 };
1191 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1163 }1192 }
11641193
1165 fn genBitOr(self: *Self, inst: Air.Inst.Index) !MCValue {1194 fn airBitOr(self: *Self, inst: Air.Inst.Index) !void {
1166 // No side effects, so if it's unreferenced, do nothing.
1167 if (self.liveness.isUnused(inst))
1168 return MCValue.dead;
1169 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1195 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1170 switch (arch) {1196 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1171 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or),1197 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or),
1172 else => return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch}),1198 else => return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch}),
1173 }1199 };
1200 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1174 }1201 }
11751202
1176 fn genXor(self: *Self, inst: Air.Inst.Index) !MCValue {1203 fn airXor(self: *Self, inst: Air.Inst.Index) !void {
1177 // No side effects, so if it's unreferenced, do nothing.
1178 if (self.liveness.isUnused(inst))
1179 return MCValue.dead;
1180 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1204 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1181 switch (arch) {1205 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1182 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .xor),1206 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .xor),
1183 else => return self.fail("TODO implement xor for {}", .{self.target.cpu.arch}),1207 else => return self.fail("TODO implement xor for {}", .{self.target.cpu.arch}),
1184 }1208 };
1209 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1185 }1210 }
11861211
1187 fn genOptionalPayload(self: *Self, inst: Air.Inst.Index) !MCValue {1212 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
1188 // No side effects, so if it's unreferenced, do nothing.1213 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1189 if (self.liveness.isUnused(inst))1214 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1190 return MCValue.dead;
1191 switch (arch) {
1192 else => return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}),1215 else => return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}),
1193 }1216 };
1217 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1194 }1218 }
11951219
1196 fn genOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !MCValue {1220 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
1197 // No side effects, so if it's unreferenced, do nothing.1221 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1198 if (self.liveness.isUnused(inst))1222 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1199 return MCValue.dead;
1200 switch (arch) {
1201 else => return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}),1223 else => return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}),
1202 }1224 };
1225 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1203 }1226 }
12041227
1205 fn genUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !MCValue {1228 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
1206 // No side effects, so if it's unreferenced, do nothing.1229 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1207 if (self.liveness.isUnused(inst))1230 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1208 return MCValue.dead;
1209 switch (arch) {
1210 else => return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch}),1231 else => return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch}),
1211 }1232 };
1233 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1212 }1234 }
12131235
1214 fn genUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !MCValue {1236 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1215 // No side effects, so if it's unreferenced, do nothing.1237 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1216 if (self.liveness.isUnused(inst))1238 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1217 return MCValue.dead;
1218 switch (arch) {
1219 else => return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}),1239 else => return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}),
1220 }1240 };
1241 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1221 }1242 }
1243
1222 // *(E!T) -> E1244 // *(E!T) -> E
1223 fn genUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !MCValue {1245 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
1224 // No side effects, so if it's unreferenced, do nothing.1246 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1225 if (self.liveness.isUnused(inst))1247 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1226 return MCValue.dead;
1227 switch (arch) {
1228 else => return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch}),1248 else => return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch}),
1229 }1249 };
1250 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1230 }1251 }
1252
1231 // *(E!T) -> *T1253 // *(E!T) -> *T
1232 fn genUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !MCValue {1254 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
1233 // No side effects, so if it's unreferenced, do nothing.1255 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1234 if (self.liveness.isUnused(inst))1256 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1235 return MCValue.dead;
1236 switch (arch) {
1237 else => return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch}),1257 else => return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch}),
1238 }1258 };
1259 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1239 }1260 }
1240 fn genWrapOptional(self: *Self, inst: Air.Inst.Index) !MCValue {
1241 // No side effects, so if it's unreferenced, do nothing.
1242 if (self.liveness.isUnused(inst))
1243 return MCValue.dead;
12441261
1245 const optional_ty = self.air.getType(inst);1262 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
1263 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1264 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1265 const optional_ty = self.air.typeOfIndex(inst);
12461266
1247 // Optional type is just a boolean true1267 // Optional with a zero-bit payload type is just a boolean true
1248 if (optional_ty.abiSize(self.target.*) == 1)1268 if (optional_ty.abiSize(self.target.*) == 1)
1249 return MCValue{ .immediate = 1 };1269 break :result MCValue{ .immediate = 1 };
12501270
1251 switch (arch) {1271 switch (arch) {
1252 else => return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}),1272 else => return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}),
1253 }1273 }
1274 };
1275 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1254 }1276 }
12551277
1256 /// T to E!T1278 /// T to E!T
1257 fn genWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !MCValue {1279 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
1258 // No side effects, so if it's unreferenced, do nothing.1280 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1259 if (self.liveness.isUnused(inst))1281 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1260 return MCValue.dead;
1261
1262 switch (arch) {
1263 else => return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}),1282 else => return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}),
1264 }1283 };
1284 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1265 }1285 }
12661286
1267 /// E to E!T1287 /// E to E!T
1268 fn genWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !MCValue {1288 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
1269 // No side effects, so if it's unreferenced, do nothing.1289 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1270 if (self.liveness.isUnused(inst))1290 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1271 return MCValue.dead;
1272
1273 switch (arch) {
1274 else => return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch}),1291 else => return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch}),
1275 }1292 };
1293 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1276 }1294 }
1277 fn genVarPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
1278 // No side effects, so if it's unreferenced, do nothing.
1279 if (self.liveness.isUnused(inst))
1280 return MCValue.dead;
12811295
1282 switch (arch) {1296 fn airVarPtr(self: *Self, inst: Air.Inst.Index) !void {
1297 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1283 else => return self.fail("TODO implement varptr for {}", .{self.target.cpu.arch}),1298 else => return self.fail("TODO implement varptr for {}", .{self.target.cpu.arch}),
1284 }1299 };
1300 return self.finishAir(inst, result, .{ .none, .none, .none });
1285 }1301 }
12861302
1287 fn reuseOperand(self: *Self, inst: Air.Inst.Index, op_index: u2, mcv: MCValue) bool {1303 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1288 if (!self.liveness.operandDies(inst, op_index))1304 if (!self.liveness.operandDies(inst, op_index))
1289 return false;1305 return false;
12901306
...@@ -1310,12 +1326,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1310,12 +1326,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13101326
1311 // That makes us responsible for doing the rest of the stuff that processDeath would have done.1327 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
1312 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1328 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1313 branch.inst_table.putAssumeCapacity(inst.getOperand(op_index).?, .dead);1329 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
13141330
1315 return true;1331 return true;
1316 }1332 }
13171333
1318 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue) !void {1334 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) !void {
1335 const elem_ty = ptr_ty.elemType();
1319 switch (ptr) {1336 switch (ptr) {
1320 .none => unreachable,1337 .none => unreachable,
1321 .undef => unreachable,1338 .undef => unreachable,
...@@ -1343,31 +1360,37 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1343,31 +1360,37 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1343 }1360 }
1344 }1361 }
13451362
1346 fn genLoad(self: *Self, inst: Air.Inst.Index) !MCValue {1363 fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
1347 const elem_ty = self.air.getType(inst);1364 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1348 if (!elem_ty.hasCodeGenBits())1365 const elem_ty = self.air.typeOfIndex(inst);
1349 return MCValue.none;1366 const result: MCValue = result: {
1350 const ptr = try self.resolveInst(inst.operand);1367 if (!elem_ty.hasCodeGenBits())
1351 const is_volatile = inst.operand.ty.isVolatilePtr();1368 break :result MCValue.none;
1352 if (self.liveness.isUnused(inst) and !is_volatile)1369
1353 return MCValue.dead;1370 const ptr = try self.resolveInst(ty_op.operand);
1354 const dst_mcv: MCValue = blk: {1371 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();
1355 if (self.reuseOperand(inst, 0, ptr)) {1372 if (self.liveness.isUnused(inst) and !is_volatile)
1356 // The MCValue that holds the pointer can be re-used as the value.1373 break :result MCValue.dead;
1357 break :blk ptr;1374
1358 } else {1375 const dst_mcv: MCValue = blk: {
1359 break :blk try self.allocRegOrMem(inst, true);1376 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
1360 }1377 // The MCValue that holds the pointer can be re-used as the value.
1378 break :blk ptr;
1379 } else {
1380 break :blk try self.allocRegOrMem(inst, true);
1381 }
1382 };
1383 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));
1384 break :result dst_mcv;
1361 };1385 };
1362 self.load(dst_mcv, ptr);1386 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1363 return dst_mcv;
1364 }1387 }
13651388
1366 fn genStore(self: *Self, inst: Air.Inst.Index) !MCValue {1389 fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1367 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1390 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1368 const ptr = try self.resolveInst(bin_op.lhs);1391 const ptr = try self.resolveInst(bin_op.lhs);
1369 const value = try self.resolveInst(bin_op.rhs);1392 const value = try self.resolveInst(bin_op.rhs);
1370 const elem_ty = self.getType(bin_op.rhs);1393 const elem_ty = self.air.typeOf(bin_op.rhs);
1371 switch (ptr) {1394 switch (ptr) {
1372 .none => unreachable,1395 .none => unreachable,
1373 .undef => unreachable,1396 .undef => unreachable,
...@@ -1397,36 +1420,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1397,36 +1420,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1397 return self.fail("TODO implement storing to MCValue.stack_offset", .{});1420 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
1398 },1421 },
1399 }1422 }
1400 return .none;1423 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1401 }1424 }
14021425
1403 fn genStructFieldPtr(self: *Self, inst: Air.Inst.Index) !MCValue {1426 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
1404 const struct_field_ptr = self.air.instructions.items(.data)[inst].struct_field_ptr;1427 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1405 _ = struct_field_ptr;1428 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1429 _ = extra;
1406 return self.fail("TODO implement codegen struct_field_ptr", .{});1430 return self.fail("TODO implement codegen struct_field_ptr", .{});
1407 }1431 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
1408
1409 fn genSub(self: *Self, inst: Air.Inst.Index) !MCValue {
1410 // No side effects, so if it's unreferenced, do nothing.
1411 if (self.liveness.isUnused(inst))
1412 return MCValue.dead;
1413 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1414 switch (arch) {
1415 .x86_64 => return self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1416 .arm, .armeb => return self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .sub),
1417 else => return self.fail("TODO implement sub for {}", .{self.target.cpu.arch}),
1418 }
1419 }
1420
1421 fn genSubWrap(self: *Self, inst: Air.Inst.Index) !MCValue {
1422 // No side effects, so if it's unreferenced, do nothing.
1423 if (self.liveness.isUnused(inst))
1424 return MCValue.dead;
1425 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1426 _ = bin_op;
1427 switch (arch) {
1428 else => return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}),
1429 }
1430 }1432 }
14311433
1432 fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {1434 fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
...@@ -1461,8 +1463,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1461,8 +1463,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1461 const rhs_is_register = rhs == .register;1463 const rhs_is_register = rhs == .register;
1462 const lhs_should_be_register = try self.armOperandShouldBeRegister(lhs);1464 const lhs_should_be_register = try self.armOperandShouldBeRegister(lhs);
1463 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);1465 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
1464 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs);1466 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1465 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs);1467 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
14661468
1467 // Destination must be a register1469 // Destination must be a register
1468 var dst_mcv: MCValue = undefined;1470 var dst_mcv: MCValue = undefined;
...@@ -1476,14 +1478,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1476,14 +1478,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1476 // Allocate 0 or 1 registers1478 // Allocate 0 or 1 registers
1477 if (!rhs_is_register and rhs_should_be_register) {1479 if (!rhs_is_register and rhs_should_be_register) {
1478 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) };1480 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) };
1479 branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv);1481 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1480 }1482 }
1481 dst_mcv = lhs;1483 dst_mcv = lhs;
1482 } else if (reuse_rhs) {1484 } else if (reuse_rhs) {
1483 // Allocate 0 or 1 registers1485 // Allocate 0 or 1 registers
1484 if (!lhs_is_register and lhs_should_be_register) {1486 if (!lhs_is_register and lhs_should_be_register) {
1485 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) };1487 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) };
1486 branch.inst_table.putAssumeCapacity(op_lhs, lhs_mcv);1488 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
1487 }1489 }
1488 dst_mcv = rhs;1490 dst_mcv = rhs;
14891491
...@@ -1508,7 +1510,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1508,7 +1510,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1508 rhs_mcv = MCValue{ .register = regs[1] };1510 rhs_mcv = MCValue{ .register = regs[1] };
1509 dst_mcv = lhs_mcv;1511 dst_mcv = lhs_mcv;
15101512
1511 branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv);1513 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1512 }1514 }
1513 } else if (lhs_should_be_register) {1515 } else if (lhs_should_be_register) {
1514 // RHS is immediate1516 // RHS is immediate
...@@ -1605,14 +1607,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1605,14 +1607,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1605 }1607 }
1606 }1608 }
16071609
1608 fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Index, op_rhs: Air.Inst.Index) !MCValue {1610 fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
1609 const lhs = try self.resolveInst(op_lhs);1611 const lhs = try self.resolveInst(op_lhs);
1610 const rhs = try self.resolveInst(op_rhs);1612 const rhs = try self.resolveInst(op_rhs);
16111613
1612 const lhs_is_register = lhs == .register;1614 const lhs_is_register = lhs == .register;
1613 const rhs_is_register = rhs == .register;1615 const rhs_is_register = rhs == .register;
1614 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs);1616 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1615 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs);1617 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
16161618
1617 // Destination must be a register1619 // Destination must be a register
1618 // LHS must be a register1620 // LHS must be a register
...@@ -1627,14 +1629,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1627,14 +1629,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1627 // Allocate 0 or 1 registers1629 // Allocate 0 or 1 registers
1628 if (!rhs_is_register) {1630 if (!rhs_is_register) {
1629 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) };1631 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) };
1630 branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv);1632 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1631 }1633 }
1632 dst_mcv = lhs;1634 dst_mcv = lhs;
1633 } else if (reuse_rhs) {1635 } else if (reuse_rhs) {
1634 // Allocate 0 or 1 registers1636 // Allocate 0 or 1 registers
1635 if (!lhs_is_register) {1637 if (!lhs_is_register) {
1636 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) };1638 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) };
1637 branch.inst_table.putAssumeCapacity(op_lhs, lhs_mcv);1639 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
1638 }1640 }
1639 dst_mcv = rhs;1641 dst_mcv = rhs;
1640 } else {1642 } else {
...@@ -1656,7 +1658,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1656,7 +1658,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1656 rhs_mcv = MCValue{ .register = regs[1] };1658 rhs_mcv = MCValue{ .register = regs[1] };
1657 dst_mcv = lhs_mcv;1659 dst_mcv = lhs_mcv;
16581660
1659 branch.inst_table.putAssumeCapacity(op_rhs, rhs_mcv);1661 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1660 }1662 }
1661 }1663 }
16621664
...@@ -1698,8 +1700,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1698,8 +1700,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1698 // as the result MCValue.1700 // as the result MCValue.
1699 var dst_mcv: MCValue = undefined;1701 var dst_mcv: MCValue = undefined;
1700 var src_mcv: MCValue = undefined;1702 var src_mcv: MCValue = undefined;
1701 var src_inst: Air.Inst.Index = undefined;1703 var src_inst: Air.Inst.Ref = undefined;
1702 if (self.reuseOperand(inst, 0, lhs)) {1704 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {
1703 // LHS dies; use it as the destination.1705 // LHS dies; use it as the destination.
1704 // Both operands cannot be memory.1706 // Both operands cannot be memory.
1705 src_inst = op_rhs;1707 src_inst = op_rhs;
...@@ -1710,7 +1712,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1710,7 +1712,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1710 dst_mcv = lhs;1712 dst_mcv = lhs;
1711 src_mcv = rhs;1713 src_mcv = rhs;
1712 }1714 }
1713 } else if (self.reuseOperand(inst, 1, rhs)) {1715 } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) {
1714 // RHS dies; use it as the destination.1716 // RHS dies; use it as the destination.
1715 // Both operands cannot be memory.1717 // Both operands cannot be memory.
1716 src_inst = op_lhs;1718 src_inst = op_lhs;
...@@ -1747,16 +1749,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1747,16 +1749,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1747 }1749 }
17481750
1749 // Now for step 2, we perform the actual op1751 // Now for step 2, we perform the actual op
1752 const inst_ty = self.air.typeOfIndex(inst);
1750 const air_tags = self.air.instructions.items(.tag);1753 const air_tags = self.air.instructions.items(.tag);
1751 switch (air_tags[inst]) {1754 switch (air_tags[inst]) {
1752 // TODO: Generate wrapping and non-wrapping versions separately1755 // TODO: Generate wrapping and non-wrapping versions separately
1753 .add, .addwrap => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 0, 0x00),1756 .add, .addwrap => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 0, 0x00),
1754 .bool_or, .bit_or => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 1, 0x08),1757 .bool_or, .bit_or => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 1, 0x08),
1755 .bool_and, .bit_and => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 4, 0x20),1758 .bool_and, .bit_and => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 4, 0x20),
1756 .sub, .subwrap => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 5, 0x28),1759 .sub, .subwrap => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 5, 0x28),
1757 .xor, .not => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 6, 0x30),1760 .xor, .not => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 6, 0x30),
17581761
1759 .mul, .mulwrap => try self.genX8664Imul(inst.src, inst.ty, dst_mcv, src_mcv),1762 .mul, .mulwrap => try self.genX8664Imul(inst_ty, dst_mcv, src_mcv),
1760 else => unreachable,1763 else => unreachable,
1761 }1764 }
17621765
...@@ -1958,7 +1961,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1958,7 +1961,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1958 .ptr_stack_offset => unreachable,1961 .ptr_stack_offset => unreachable,
1959 .ptr_embedded_in_code => unreachable,1962 .ptr_embedded_in_code => unreachable,
1960 .register => |src_reg| {1963 .register => |src_reg| {
1961 try self.genX8664ModRMRegToStack(src, dst_ty, off, src_reg, mr + 0x1);1964 try self.genX8664ModRMRegToStack(dst_ty, off, src_reg, mr + 0x1);
1962 },1965 },
1963 .immediate => |imm| {1966 .immediate => |imm| {
1964 _ = imm;1967 _ = imm;
...@@ -1984,7 +1987,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1984,7 +1987,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1984 /// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.1987 /// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.
1985 fn genX8664Imul(1988 fn genX8664Imul(
1986 self: *Self,1989 self: *Self,
1987 src: LazySrcLoc,
1988 dst_ty: Type,1990 dst_ty: Type,
1989 dst_mcv: MCValue,1991 dst_mcv: MCValue,
1990 src_mcv: MCValue,1992 src_mcv: MCValue,
...@@ -2067,7 +2069,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2067,7 +2069,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2067 encoder.imm32(@intCast(i32, imm));2069 encoder.imm32(@intCast(i32, imm));
2068 } else {2070 } else {
2069 const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv);2071 const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv);
2070 return self.genX8664Imul(src, dst_ty, dst_mcv, MCValue{ .register = src_reg });2072 return self.genX8664Imul(dst_ty, dst_mcv, MCValue{ .register = src_reg });
2071 }2073 }
2072 },2074 },
2073 .embedded_in_code, .memory, .stack_offset => {2075 .embedded_in_code, .memory, .stack_offset => {
...@@ -2163,7 +2165,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2163,7 +2165,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2163 }2165 }
21642166
2165 fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {2167 fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
2166 const ty_str = self.air.instruction.items(.data)[inst].ty_str;2168 const ty_str = self.air.instructions.items(.data)[inst].ty_str;
2167 const zir = &self.mod_fn.owner_decl.namespace.file_scope.zir;2169 const zir = &self.mod_fn.owner_decl.namespace.file_scope.zir;
2168 const name = zir.nullTerminatedString(ty_str.str);2170 const name = zir.nullTerminatedString(ty_str.str);
2169 const name_with_null = name.ptr[0 .. name.len + 1];2171 const name_with_null = name.ptr[0 .. name.len + 1];
...@@ -2224,11 +2226,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2224,11 +2226,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2224 }2226 }
2225 }2227 }
22262228
2227 fn genArg(self: *Self, inst: Air.Inst.Index) !MCValue {2229 fn airArg(self: *Self, inst: Air.Inst.Index) !void {
2228 const arg_index = self.arg_index;2230 const arg_index = self.arg_index;
2229 self.arg_index += 1;2231 self.arg_index += 1;
22302232
2231 const ty = self.air.getType(inst);2233 const ty = self.air.typeOfIndex(inst);
22322234
2233 const result = self.args[arg_index];2235 const result = self.args[arg_index];
2234 const mcv = switch (arch) {2236 const mcv = switch (arch) {
...@@ -2252,7 +2254,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2252,7 +2254,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2252 try self.genArgDbgInfo(inst, mcv);2254 try self.genArgDbgInfo(inst, mcv);
22532255
2254 if (self.liveness.isUnused(inst))2256 if (self.liveness.isUnused(inst))
2255 return MCValue.dead;2257 return self.finishAirBookkeeping();
22562258
2257 switch (mcv) {2259 switch (mcv) {
2258 .register => |reg| {2260 .register => |reg| {
...@@ -2261,10 +2263,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2261,10 +2263,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2261 else => {},2263 else => {},
2262 }2264 }
22632265
2264 return mcv;2266 return self.finishAir(inst, mcv, .{ .none, .none, .none });
2265 }2267 }
22662268
2267 fn genBreakpoint(self: *Self) !MCValue {2269 fn airBreakpoint(self: *Self) !void {
2268 switch (arch) {2270 switch (arch) {
2269 .i386, .x86_64 => {2271 .i386, .x86_64 => {
2270 try self.code.append(0xcc); // int32272 try self.code.append(0xcc); // int3
...@@ -2280,15 +2282,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2280,15 +2282,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2280 },2282 },
2281 else => return self.fail("TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),2283 else => return self.fail("TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
2282 }2284 }
2283 return .none;2285 return self.finishAirBookkeeping();
2284 }2286 }
22852287
2286 fn genCall(self: *Self, inst: Air.Inst.Index) !MCValue {2288 fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2287 const pl_op = self.air.instruction.items(.data)[inst].pl_op;2289 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2288 const fn_ty = self.air.getType(pl_op.operand);2290 const fn_ty = self.air.typeOf(pl_op.operand);
2289 const callee = pl_op.operand;2291 const callee = pl_op.operand;
2290 const extra = self.air.extraData(Air.Call, inst_data.payload);2292 const extra = self.air.extraData(Air.Call, pl_op.payload);
2291 const args = self.air.extra[extra.end..][0..extra.data.args_len];2293 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
22922294
2293 var info = try self.resolveCallingConventionValues(fn_ty);2295 var info = try self.resolveCallingConventionValues(fn_ty);
2294 defer info.deinit(self);2296 defer info.deinit(self);
...@@ -2300,6 +2302,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2300,6 +2302,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2300 .x86_64 => {2302 .x86_64 => {
2301 for (info.args) |mc_arg, arg_i| {2303 for (info.args) |mc_arg, arg_i| {
2302 const arg = args[arg_i];2304 const arg = args[arg_i];
2305 const arg_ty = self.air.typeOf(arg);
2303 const arg_mcv = try self.resolveInst(args[arg_i]);2306 const arg_mcv = try self.resolveInst(args[arg_i]);
2304 // Here we do not use setRegOrMem even though the logic is similar, because2307 // Here we do not use setRegOrMem even though the logic is similar, because
2305 // the function call will move the stack pointer, so the offsets are different.2308 // the function call will move the stack pointer, so the offsets are different.
...@@ -2307,12 +2310,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2307,12 +2310,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2307 .none => continue,2310 .none => continue,
2308 .register => |reg| {2311 .register => |reg| {
2309 try self.register_manager.getReg(reg, null);2312 try self.register_manager.getReg(reg, null);
2310 try self.genSetReg(arg.ty, reg, arg_mcv);2313 try self.genSetReg(arg_ty, reg, arg_mcv);
2311 },2314 },
2312 .stack_offset => |off| {2315 .stack_offset => |off| {
2313 // Here we need to emit instructions like this:2316 // Here we need to emit instructions like this:
2314 // mov qword ptr [rsp + stack_offset], x2317 // mov qword ptr [rsp + stack_offset], x
2315 try self.genSetStack(arg.ty, off, arg_mcv);2318 try self.genSetStack(arg_ty, off, arg_mcv);
2316 },2319 },
2317 .ptr_stack_offset => {2320 .ptr_stack_offset => {
2318 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});2321 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
...@@ -2389,6 +2392,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2389,6 +2392,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2389 .arm, .armeb => {2392 .arm, .armeb => {
2390 for (info.args) |mc_arg, arg_i| {2393 for (info.args) |mc_arg, arg_i| {
2391 const arg = args[arg_i];2394 const arg = args[arg_i];
2395 const arg_ty = self.air.typeOf(arg);
2392 const arg_mcv = try self.resolveInst(args[arg_i]);2396 const arg_mcv = try self.resolveInst(args[arg_i]);
23932397
2394 switch (mc_arg) {2398 switch (mc_arg) {
...@@ -2403,7 +2407,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2403,7 +2407,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2403 .compare_flags_unsigned => unreachable,2407 .compare_flags_unsigned => unreachable,
2404 .register => |reg| {2408 .register => |reg| {
2405 try self.register_manager.getReg(reg, null);2409 try self.register_manager.getReg(reg, null);
2406 try self.genSetReg(arg.ty, reg, arg_mcv);2410 try self.genSetReg(arg_ty, reg, arg_mcv);
2407 },2411 },
2408 .stack_offset => {2412 .stack_offset => {
2409 return self.fail("TODO implement calling with parameters in memory", .{});2413 return self.fail("TODO implement calling with parameters in memory", .{});
...@@ -2452,6 +2456,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2452,6 +2456,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2452 .aarch64 => {2456 .aarch64 => {
2453 for (info.args) |mc_arg, arg_i| {2457 for (info.args) |mc_arg, arg_i| {
2454 const arg = args[arg_i];2458 const arg = args[arg_i];
2459 const arg_ty = self.air.typeOf(arg);
2455 const arg_mcv = try self.resolveInst(args[arg_i]);2460 const arg_mcv = try self.resolveInst(args[arg_i]);
24562461
2457 switch (mc_arg) {2462 switch (mc_arg) {
...@@ -2466,7 +2471,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2466,7 +2471,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2466 .compare_flags_unsigned => unreachable,2471 .compare_flags_unsigned => unreachable,
2467 .register => |reg| {2472 .register => |reg| {
2468 try self.register_manager.getReg(reg, null);2473 try self.register_manager.getReg(reg, null);
2469 try self.genSetReg(arg.ty, reg, arg_mcv);2474 try self.genSetReg(arg_ty, reg, arg_mcv);
2470 },2475 },
2471 .stack_offset => {2476 .stack_offset => {
2472 return self.fail("TODO implement calling with parameters in memory", .{});2477 return self.fail("TODO implement calling with parameters in memory", .{});
...@@ -2510,6 +2515,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2510,6 +2515,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2510 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {2515 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2511 for (info.args) |mc_arg, arg_i| {2516 for (info.args) |mc_arg, arg_i| {
2512 const arg = args[arg_i];2517 const arg = args[arg_i];
2518 const arg_ty = self.air.typeOf(arg);
2513 const arg_mcv = try self.resolveInst(args[arg_i]);2519 const arg_mcv = try self.resolveInst(args[arg_i]);
2514 // Here we do not use setRegOrMem even though the logic is similar, because2520 // Here we do not use setRegOrMem even though the logic is similar, because
2515 // the function call will move the stack pointer, so the offsets are different.2521 // the function call will move the stack pointer, so the offsets are different.
...@@ -2521,7 +2527,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2521,7 +2527,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2521 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),2527 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),
2522 else => unreachable,2528 else => unreachable,
2523 }2529 }
2524 try self.genSetReg(arg.ty, reg, arg_mcv);2530 try self.genSetReg(arg_ty, reg, arg_mcv);
2525 },2531 },
2526 .stack_offset => {2532 .stack_offset => {
2527 // Here we need to emit instructions like this:2533 // Here we need to emit instructions like this:
...@@ -2612,6 +2618,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2612,6 +2618,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2612 .x86_64 => {2618 .x86_64 => {
2613 for (info.args) |mc_arg, arg_i| {2619 for (info.args) |mc_arg, arg_i| {
2614 const arg = args[arg_i];2620 const arg = args[arg_i];
2621 const arg_ty = self.air.typeOf(arg);
2615 const arg_mcv = try self.resolveInst(args[arg_i]);2622 const arg_mcv = try self.resolveInst(args[arg_i]);
2616 // Here we do not use setRegOrMem even though the logic is similar, because2623 // Here we do not use setRegOrMem even though the logic is similar, because
2617 // the function call will move the stack pointer, so the offsets are different.2624 // the function call will move the stack pointer, so the offsets are different.
...@@ -2619,7 +2626,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2619,7 +2626,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2619 .none => continue,2626 .none => continue,
2620 .register => |reg| {2627 .register => |reg| {
2621 try self.register_manager.getReg(reg, null);2628 try self.register_manager.getReg(reg, null);
2622 try self.genSetReg(arg.ty, reg, arg_mcv);2629 try self.genSetReg(arg_ty, reg, arg_mcv);
2623 },2630 },
2624 .stack_offset => {2631 .stack_offset => {
2625 // Here we need to emit instructions like this:2632 // Here we need to emit instructions like this:
...@@ -2661,6 +2668,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2661,6 +2668,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2661 .aarch64 => {2668 .aarch64 => {
2662 for (info.args) |mc_arg, arg_i| {2669 for (info.args) |mc_arg, arg_i| {
2663 const arg = inst.args[arg_i];2670 const arg = inst.args[arg_i];
2671 const arg_ty = self.air.typeOf(arg);
2664 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2672 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
26652673
2666 switch (mc_arg) {2674 switch (mc_arg) {
...@@ -2675,7 +2683,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2675,7 +2683,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2675 .compare_flags_unsigned => unreachable,2683 .compare_flags_unsigned => unreachable,
2676 .register => |reg| {2684 .register => |reg| {
2677 try self.register_manager.getReg(reg, null);2685 try self.register_manager.getReg(reg, null);
2678 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2686 try self.genSetReg(arg_ty, reg, arg_mcv);
2679 },2687 },
2680 .stack_offset => {2688 .stack_offset => {
2681 return self.fail("TODO implement calling with parameters in memory", .{});2689 return self.fail("TODO implement calling with parameters in memory", .{});
...@@ -2696,7 +2704,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2696,7 +2704,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2696 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;2704 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
2697 const fn_got_addr = got_addr + got_index * ptr_bytes;2705 const fn_got_addr = got_addr + got_index * ptr_bytes;
26982706
2699 try self.genSetReg(inst.base.src, Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });2707 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
27002708
2701 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());2709 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
2702 } else if (func_value.castTag(.extern_fn)) |_| {2710 } else if (func_value.castTag(.extern_fn)) |_| {
...@@ -2712,51 +2720,61 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2712,51 +2720,61 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2712 }2720 }
2713 } else unreachable;2721 } else unreachable;
27142722
2715 switch (info.return_value) {2723 const result: MCValue = result: {
2716 .register => |reg| {2724 switch (info.return_value) {
2717 if (Register.allocIndex(reg) == null) {2725 .register => |reg| {
2718 // Save function return value in a callee saved register2726 if (Register.allocIndex(reg) == null) {
2719 return try self.copyToNewRegister(inst, info.return_value);2727 // Save function return value in a callee saved register
2720 }2728 break :result try self.copyToNewRegister(inst, info.return_value);
2721 },2729 }
2722 else => {},2730 },
2723 }2731 else => {},
2732 }
2733 break :result info.return_value;
2734 };
27242735
2725 return info.return_value;2736 if (args.len <= Liveness.bpi - 2) {
2737 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2738 buf[0] = callee;
2739 std.mem.copy(Air.Inst.Ref, buf[1..], args);
2740 return self.finishAir(inst, result, buf);
2741 }
2742 @panic("TODO: codegen for function call with greater than 2 args");
2726 }2743 }
27272744
2728 fn genRef(self: *Self, inst: Air.Inst.Index) !MCValue {2745 fn airRef(self: *Self, inst: Air.Inst.Index) !void {
2729 if (self.liveness.isUnused(inst))
2730 return MCValue.dead;
2731 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2746 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2732 const operand_ty = self.air.getType(ty_op.operand);2747 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2733 const operand = try self.resolveInst(ty_op.operand);2748 const operand_ty = self.air.typeOf(ty_op.operand);
2734 switch (operand) {2749 const operand = try self.resolveInst(ty_op.operand);
2735 .unreach => unreachable,2750 switch (operand) {
2736 .dead => unreachable,2751 .unreach => unreachable,
2737 .none => return .none,2752 .dead => unreachable,
27382753 .none => break :result MCValue{ .none = {} },
2739 .immediate,2754
2740 .register,2755 .immediate,
2741 .ptr_stack_offset,2756 .register,
2742 .ptr_embedded_in_code,2757 .ptr_stack_offset,
2743 .compare_flags_unsigned,2758 .ptr_embedded_in_code,
2744 .compare_flags_signed,2759 .compare_flags_unsigned,
2745 => {2760 .compare_flags_signed,
2746 const stack_offset = try self.allocMemPtr(inst);2761 => {
2747 try self.genSetStack(operand_ty, stack_offset, operand);2762 const stack_offset = try self.allocMemPtr(inst);
2748 return MCValue{ .ptr_stack_offset = stack_offset };2763 try self.genSetStack(operand_ty, stack_offset, operand);
2749 },2764 break :result MCValue{ .ptr_stack_offset = stack_offset };
2765 },
27502766
2751 .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset },2767 .stack_offset => |offset| break :result MCValue{ .ptr_stack_offset = offset },
2752 .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },2768 .embedded_in_code => |offset| break :result MCValue{ .ptr_embedded_in_code = offset },
2753 .memory => |vaddr| return MCValue{ .immediate = vaddr },2769 .memory => |vaddr| break :result MCValue{ .immediate = vaddr },
27542770
2755 .undef => return self.fail("TODO implement ref on an undefined value", .{}),2771 .undef => return self.fail("TODO implement ref on an undefined value", .{}),
2756 }2772 }
2773 };
2774 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2757 }2775 }
27582776
2759 fn ret(self: *Self, mcv: MCValue) !MCValue {2777 fn ret(self: *Self, mcv: MCValue) !void {
2760 const ret_ty = self.fn_type.fnReturnType();2778 const ret_ty = self.fn_type.fnReturnType();
2761 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);2779 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
2762 switch (arch) {2780 switch (arch) {
...@@ -2786,28 +2804,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2786,28 +2804,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2786 },2804 },
2787 else => return self.fail("TODO implement return for {}", .{self.target.cpu.arch}),2805 else => return self.fail("TODO implement return for {}", .{self.target.cpu.arch}),
2788 }2806 }
2789 return .unreach;
2790 }2807 }
27912808
2792 fn genRet(self: *Self, inst: Air.Inst.Index) !MCValue {2809 fn airRet(self: *Self, inst: Air.Inst.Index) !void {
2793 const operand = try self.resolveInst(self.air.instructions.items(.data)[inst].un_op);2810 const un_op = self.air.instructions.items(.data)[inst].un_op;
2794 return self.ret(inst.base.src, operand);2811 const operand = try self.resolveInst(un_op);
2812 try self.ret(operand);
2813 return self.finishAirBookkeeping();
2795 }2814 }
27962815
2797 fn genCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !MCValue {2816 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
2798 // No side effects, so if it's unreferenced, do nothing.
2799 if (self.liveness.isUnused(inst))
2800 return MCValue.dead;
2801 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2817 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2802 const ty = self.air.getType(bin_op.lhs);2818 if (self.liveness.isUnused(inst))
2803 assert(ty.eql(self.air.getType(bin_op.rhs)));2819 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
2820 const ty = self.air.typeOf(bin_op.lhs);
2821 assert(ty.eql(self.air.typeOf(bin_op.rhs)));
2804 if (ty.zigTypeTag() == .ErrorSet)2822 if (ty.zigTypeTag() == .ErrorSet)
2805 return self.fail("TODO implement cmp for errors", .{});2823 return self.fail("TODO implement cmp for errors", .{});
28062824
2807 const lhs = try self.resolveInst(bin_op.lhs);2825 const lhs = try self.resolveInst(bin_op.lhs);
2808 const rhs = try self.resolveInst(bin_op.rhs);2826 const rhs = try self.resolveInst(bin_op.rhs);
2809 switch (arch) {2827 const result: MCValue = switch (arch) {
2810 .x86_64 => {2828 .x86_64 => result: {
2811 try self.code.ensureCapacity(self.code.items.len + 8);2829 try self.code.ensureCapacity(self.code.items.len + 8);
28122830
2813 // There are 2 operands, destination and source.2831 // There are 2 operands, destination and source.
...@@ -2822,12 +2840,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2822,12 +2840,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28222840
2823 try self.genX8664BinMathCode(Type.initTag(.bool), dst_mcv, src_mcv, 7, 0x38);2841 try self.genX8664BinMathCode(Type.initTag(.bool), dst_mcv, src_mcv, 7, 0x38);
2824 const info = ty.intInfo(self.target.*);2842 const info = ty.intInfo(self.target.*);
2825 return switch (info.signedness) {2843 break :result switch (info.signedness) {
2826 .signed => MCValue{ .compare_flags_signed = op },2844 .signed => MCValue{ .compare_flags_signed = op },
2827 .unsigned => MCValue{ .compare_flags_unsigned = op },2845 .unsigned => MCValue{ .compare_flags_unsigned = op },
2828 };2846 };
2829 },2847 },
2830 .arm, .armeb => {2848 .arm, .armeb => result: {
2831 const lhs_is_register = lhs == .register;2849 const lhs_is_register = lhs == .register;
2832 const rhs_is_register = rhs == .register;2850 const rhs_is_register = rhs == .register;
2833 // lhs should always be a register2851 // lhs should always be a register
...@@ -2854,39 +2872,40 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2854,39 +2872,40 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2854 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];2872 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2855 if (lhs_mcv == .register and !lhs_is_register) {2873 if (lhs_mcv == .register and !lhs_is_register) {
2856 try self.genSetReg(ty, lhs_mcv.register, lhs);2874 try self.genSetReg(ty, lhs_mcv.register, lhs);
2857 branch.inst_table.putAssumeCapacity(bin_op.lhs, lhs);2875 branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs);
2858 }2876 }
2859 if (rhs_mcv == .register and !rhs_is_register) {2877 if (rhs_mcv == .register and !rhs_is_register) {
2860 try self.genSetReg(ty, rhs_mcv.register, rhs);2878 try self.genSetReg(ty, rhs_mcv.register, rhs);
2861 branch.inst_table.putAssumeCapacity(bin_op.rhs, rhs);2879 branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs);
2862 }2880 }
28632881
2864 // The destination register is not present in the cmp instruction2882 // The destination register is not present in the cmp instruction
2865 try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);2883 try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);
28662884
2867 const info = ty.intInfo(self.target.*);2885 const info = ty.intInfo(self.target.*);
2868 return switch (info.signedness) {2886 break :result switch (info.signedness) {
2869 .signed => MCValue{ .compare_flags_signed = op },2887 .signed => MCValue{ .compare_flags_signed = op },
2870 .unsigned => MCValue{ .compare_flags_unsigned = op },2888 .unsigned => MCValue{ .compare_flags_unsigned = op },
2871 };2889 };
2872 },2890 },
2873 else => return self.fail("TODO implement cmp for {}", .{self.target.cpu.arch}),2891 else => return self.fail("TODO implement cmp for {}", .{self.target.cpu.arch}),
2874 }2892 };
2893 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2875 }2894 }
28762895
2877 fn genDbgStmt(self: *Self, inst: Air.Inst.Index) !MCValue {2896 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
2878 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;2897 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
2879 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);2898 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);
2880 assert(self.liveness.isUnused(inst));2899 return self.finishAirBookkeeping();
2881 return MCValue.dead;
2882 }2900 }
28832901
2884 fn genCondBr(self: *Self, inst: Air.Inst.Index) !MCValue {2902 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
2885 const pl_op = self.air.instructions.items(.data)[inst].pl_op;2903 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2886 const cond = try self.resolveInst(pl_op.operand);2904 const cond = try self.resolveInst(pl_op.operand);
2887 const extra = self.air.extraData(Air.CondBr, inst_data.payload);2905 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
2888 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];2906 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
2889 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];2907 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
2908 const liveness_condbr = self.liveness.getCondBr(inst);
28902909
2891 const reloc: Reloc = switch (arch) {2910 const reloc: Reloc = switch (arch) {
2892 .i386, .x86_64 => reloc: {2911 .i386, .x86_64 => reloc: {
...@@ -2985,9 +3004,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2985,9 +3004,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29853004
2986 try self.branch_stack.append(.{});3005 try self.branch_stack.append(.{});
29873006
2988 const then_deaths = self.liveness.thenDeaths(inst);3007 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
2989 try self.ensureProcessDeathCapacity(then_deaths.len);3008 for (liveness_condbr.then_deaths) |operand| {
2990 for (then_deaths) |operand| {
2991 self.processDeath(operand);3009 self.processDeath(operand);
2992 }3010 }
2993 try self.genBody(then_body);3011 try self.genBody(then_body);
...@@ -3010,9 +3028,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3010,9 +3028,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3010 const else_branch = self.branch_stack.addOneAssumeCapacity();3028 const else_branch = self.branch_stack.addOneAssumeCapacity();
3011 else_branch.* = .{};3029 else_branch.* = .{};
30123030
3013 const else_deaths = self.liveness.elseDeaths(inst);3031 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
3014 try self.ensureProcessDeathCapacity(else_deaths.len);3032 for (liveness_condbr.else_deaths) |operand| {
3015 for (else_deaths) |operand| {
3016 self.processDeath(operand);3033 self.processDeath(operand);
3017 }3034 }
3018 try self.genBody(else_body);3035 try self.genBody(else_body);
...@@ -3026,8 +3043,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3026,8 +3043,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3026 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers3043 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
3027 // rather than assigning it.3044 // rather than assigning it.
3028 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];3045 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
3029 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.count() +3046 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
3030 else_branch.inst_table.count());
30313047
3032 const else_slice = else_branch.inst_table.entries.slice();3048 const else_slice = else_branch.inst_table.entries.slice();
3033 const else_keys = else_slice.items(.key);3049 const else_keys = else_slice.items(.key);
...@@ -3058,11 +3074,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3058,11 +3074,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3058 log.debug("consolidating else_entry {*} {}=>{}", .{ else_key, else_value, canon_mcv });3074 log.debug("consolidating else_entry {*} {}=>{}", .{ else_key, else_value, canon_mcv });
3059 // TODO make sure the destination stack offset / register does not already have something3075 // TODO make sure the destination stack offset / register does not already have something
3060 // going on there.3076 // going on there.
3061 try self.setRegOrMem(else_key.ty, canon_mcv, else_value);3077 try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value);
3062 // TODO track the new register / stack allocation3078 // TODO track the new register / stack allocation
3063 }3079 }
3064 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.count() +3080 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
3065 saved_then_branch.inst_table.count());
3066 const then_slice = saved_then_branch.inst_table.entries.slice();3081 const then_slice = saved_then_branch.inst_table.entries.slice();
3067 const then_keys = then_slice.items(.key);3082 const then_keys = then_slice.items(.key);
3068 const then_values = then_slice.items(.value);3083 const then_values = then_slice.items(.value);
...@@ -3086,13 +3101,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3086,13 +3101,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3086 log.debug("consolidating then_entry {*} {}=>{}", .{ then_key, parent_mcv, then_value });3101 log.debug("consolidating then_entry {*} {}=>{}", .{ then_key, parent_mcv, then_value });
3087 // TODO make sure the destination stack offset / register does not already have something3102 // TODO make sure the destination stack offset / register does not already have something
3088 // going on there.3103 // going on there.
3089 try self.setRegOrMem(then_key.ty, parent_mcv, then_value);3104 try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value);
3090 // TODO track the new register / stack allocation3105 // TODO track the new register / stack allocation
3091 }3106 }
30923107
3093 self.branch_stack.pop().deinit(self.gpa);3108 self.branch_stack.pop().deinit(self.gpa);
30943109
3095 return MCValue.unreach;3110 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
3096 }3111 }
30973112
3098 fn isNull(self: *Self, operand: MCValue) !MCValue {3113 fn isNull(self: *Self, operand: MCValue) !MCValue {
...@@ -3131,107 +3146,115 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3131,107 +3146,115 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3131 }3146 }
3132 }3147 }
31333148
3134 fn genIsNull(self: *Self, inst: Air.Inst.Index) !MCValue {3149 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
3135 if (self.liveness.isUnused(inst))
3136 return MCValue.dead;
3137 const un_op = self.air.instructions.items(.data)[inst].un_op;3150 const un_op = self.air.instructions.items(.data)[inst].un_op;
3138 const operand = try self.resolveInst(un_op);3151 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3139 return self.isNull(operand);3152 const operand = try self.resolveInst(un_op);
3153 break :result try self.isNull(operand);
3154 };
3155 return self.finishAir(inst, result, .{ un_op, .none, .none });
3140 }3156 }
31413157
3142 fn genIsNullPtr(self: *Self, inst: Air.Inst.Index) !MCValue {3158 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
3143 if (self.liveness.isUnused(inst))
3144 return MCValue.dead;
3145 const un_op = self.air.instructions.items(.data)[inst].un_op;3159 const un_op = self.air.instructions.items(.data)[inst].un_op;
3146 const operand_ptr = try self.resolveInst(un_op);3160 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3147 const operand: MCValue = blk: {3161 const operand_ptr = try self.resolveInst(un_op);
3148 if (self.reuseOperand(inst, 0, operand_ptr)) {3162 const operand: MCValue = blk: {
3149 // The MCValue that holds the pointer can be re-used as the value.3163 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3150 break :blk operand_ptr;3164 // The MCValue that holds the pointer can be re-used as the value.
3151 } else {3165 break :blk operand_ptr;
3152 break :blk try self.allocRegOrMem(inst, true);3166 } else {
3153 }3167 break :blk try self.allocRegOrMem(inst, true);
3168 }
3169 };
3170 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3171 break :result try self.isNull(operand);
3154 };3172 };
3155 try self.load(operand, ptr);3173 return self.finishAir(inst, result, .{ un_op, .none, .none });
3156 return self.isNull(operand);
3157 }3174 }
31583175
3159 fn genIsNonNull(self: *Self, inst: Air.Inst.Index) !MCValue {3176 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
3160 if (self.liveness.isUnused(inst))
3161 return MCValue.dead;
3162 const un_op = self.air.instructions.items(.data)[inst].un_op;3177 const un_op = self.air.instructions.items(.data)[inst].un_op;
3163 const operand = try self.resolveInst(un_op);3178 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3164 return self.isNonNull(operand);3179 const operand = try self.resolveInst(un_op);
3180 break :result try self.isNonNull(operand);
3181 };
3182 return self.finishAir(inst, result, .{ un_op, .none, .none });
3165 }3183 }
31663184
3167 fn genIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !MCValue {3185 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
3168 if (self.liveness.isUnused(inst))
3169 return MCValue.dead;
3170 const un_op = self.air.instructions.items(.data)[inst].un_op;3186 const un_op = self.air.instructions.items(.data)[inst].un_op;
3171 const operand_ptr = try self.resolveInst(un_op);3187 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3172 const operand: MCValue = blk: {3188 const operand_ptr = try self.resolveInst(un_op);
3173 if (self.reuseOperand(inst, 0, operand_ptr)) {3189 const operand: MCValue = blk: {
3174 // The MCValue that holds the pointer can be re-used as the value.3190 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3175 break :blk operand_ptr;3191 // The MCValue that holds the pointer can be re-used as the value.
3176 } else {3192 break :blk operand_ptr;
3177 break :blk try self.allocRegOrMem(inst, true);3193 } else {
3178 }3194 break :blk try self.allocRegOrMem(inst, true);
3195 }
3196 };
3197 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3198 break :result try self.isNonNull(operand);
3179 };3199 };
3180 try self.load(operand, ptr);3200 return self.finishAir(inst, result, .{ un_op, .none, .none });
3181 return self.isNonNull(operand);
3182 }3201 }
31833202
3184 fn genIsErr(self: *Self, inst: Air.Inst.Index) !MCValue {3203 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
3185 if (self.liveness.isUnused(inst))
3186 return MCValue.dead;
3187 const un_op = self.air.instructions.items(.data)[inst].un_op;3204 const un_op = self.air.instructions.items(.data)[inst].un_op;
3188 const operand = try self.resolveInst(un_op);3205 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3189 return self.isErr(operand);3206 const operand = try self.resolveInst(un_op);
3207 break :result try self.isErr(operand);
3208 };
3209 return self.finishAir(inst, result, .{ un_op, .none, .none });
3190 }3210 }
31913211
3192 fn genIsErrPtr(self: *Self, inst: Air.Inst.Index) !MCValue {3212 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
3193 if (self.liveness.isUnused(inst))
3194 return MCValue.dead;
3195 const un_op = self.air.instructions.items(.data)[inst].un_op;3213 const un_op = self.air.instructions.items(.data)[inst].un_op;
3196 const operand_ptr = try self.resolveInst(un_op);3214 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3197 const operand: MCValue = blk: {3215 const operand_ptr = try self.resolveInst(un_op);
3198 if (self.reuseOperand(inst, 0, operand_ptr)) {3216 const operand: MCValue = blk: {
3199 // The MCValue that holds the pointer can be re-used as the value.3217 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3200 break :blk operand_ptr;3218 // The MCValue that holds the pointer can be re-used as the value.
3201 } else {3219 break :blk operand_ptr;
3202 break :blk try self.allocRegOrMem(inst, true);3220 } else {
3203 }3221 break :blk try self.allocRegOrMem(inst, true);
3222 }
3223 };
3224 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3225 break :result try self.isErr(operand);
3204 };3226 };
3205 try self.load(operand, ptr);3227 return self.finishAir(inst, result, .{ un_op, .none, .none });
3206 return self.isErr(operand);
3207 }3228 }
32083229
3209 fn genIsNonErr(self: *Self, inst: Air.Inst.Index) !MCValue {3230 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
3210 if (self.liveness.isUnused(inst))
3211 return MCValue.dead;
3212 const un_op = self.air.instructions.items(.data)[inst].un_op;3231 const un_op = self.air.instructions.items(.data)[inst].un_op;
3213 const operand = try self.resolveInst(un_op);3232 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3214 return self.isNonErr(operand);3233 const operand = try self.resolveInst(un_op);
3234 break :result try self.isNonErr(operand);
3235 };
3236 return self.finishAir(inst, result, .{ un_op, .none, .none });
3215 }3237 }
32163238
3217 fn genIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !MCValue {3239 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
3218 if (self.liveness.isUnused(inst))
3219 return MCValue.dead;
3220 const un_op = self.air.instructions.items(.data)[inst].un_op;3240 const un_op = self.air.instructions.items(.data)[inst].un_op;
3221 const operand_ptr = try self.resolveInst(un_op);3241 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3222 const operand: MCValue = blk: {3242 const operand_ptr = try self.resolveInst(un_op);
3223 if (self.reuseOperand(inst, 0, operand_ptr)) {3243 const operand: MCValue = blk: {
3224 // The MCValue that holds the pointer can be re-used as the value.3244 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3225 break :blk operand_ptr;3245 // The MCValue that holds the pointer can be re-used as the value.
3226 } else {3246 break :blk operand_ptr;
3227 break :blk try self.allocRegOrMem(inst, true);3247 } else {
3228 }3248 break :blk try self.allocRegOrMem(inst, true);
3249 }
3250 };
3251 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
3252 break :result try self.isNonErr(operand);
3229 };3253 };
3230 try self.load(operand, ptr);3254 return self.finishAir(inst, result, .{ un_op, .none, .none });
3231 return self.isNonErr(operand);
3232 }3255 }
32333256
3234 fn genLoop(self: *Self, inst: Air.Inst.Index) !MCValue {3257 fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
3235 // A loop is a setup to be able to jump back to the beginning.3258 // A loop is a setup to be able to jump back to the beginning.
3236 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3259 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3237 const loop = self.air.extraData(Air.Block, ty_pl.payload);3260 const loop = self.air.extraData(Air.Block, ty_pl.payload);
...@@ -3239,7 +3262,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3239,7 +3262,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3239 const start_index = self.code.items.len;3262 const start_index = self.code.items.len;
3240 try self.genBody(body);3263 try self.genBody(body);
3241 try self.jump(start_index);3264 try self.jump(start_index);
3242 return MCValue.unreach;3265 return self.finishAirBookkeeping();
3243 }3266 }
32443267
3245 /// Send control flow to the `index` of `self.code`.3268 /// Send control flow to the `index` of `self.code`.
...@@ -3274,7 +3297,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3274,7 +3297,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3274 }3297 }
3275 }3298 }
32763299
3277 fn genBlock(self: *Self, inst: Air.Inst.Index) !MCValue {3300 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
3278 try self.blocks.putNoClobber(self.gpa, inst, .{3301 try self.blocks.putNoClobber(self.gpa, inst, .{
3279 // A block is a setup to be able to jump to the end.3302 // A block is a setup to be able to jump to the end.
3280 .relocs = .{},3303 .relocs = .{},
...@@ -3288,21 +3311,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3288,21 +3311,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3288 const block_data = self.blocks.getPtr(inst).?;3311 const block_data = self.blocks.getPtr(inst).?;
3289 defer block_data.relocs.deinit(self.gpa);3312 defer block_data.relocs.deinit(self.gpa);
32903313
3291 const ty_pl = self.air.instructions.items(.data).ty_pl;3314 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3292 const extra = self.air.extraData(Air.Block, ty_pl.payload);3315 const extra = self.air.extraData(Air.Block, ty_pl.payload);
3293 const body = self.air.extra[extra.end..][0..extra.data.body_len];3316 const body = self.air.extra[extra.end..][0..extra.data.body_len];
3294 try self.genBody(body);3317 try self.genBody(body);
32953318
3296 for (block_data.relocs.items) |reloc| try self.performReloc(reloc);3319 for (block_data.relocs.items) |reloc| try self.performReloc(reloc);
32973320
3298 return @bitCast(MCValue, block_data.mcv);3321 const result = @bitCast(MCValue, block_data.mcv);
3322 return self.finishAir(inst, result, .{ .none, .none, .none });
3299 }3323 }
33003324
3301 fn genSwitch(self: *Self, inst: Air.Inst.Index) !MCValue {3325 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
3302 _ = inst;3326 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3327 const condition = pl_op.operand;
3303 switch (arch) {3328 switch (arch) {
3304 else => return self.fail("TODO genSwitch for {}", .{self.target.cpu.arch}),3329 else => return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch}),
3305 }3330 }
3331 return self.finishAir(inst, .dead, .{ condition, .none, .none });
3306 }3332 }
33073333
3308 fn performReloc(self: *Self, reloc: Reloc) !void {3334 fn performReloc(self: *Self, reloc: Reloc) !void {
...@@ -3335,54 +3361,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3335,54 +3361,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3335 }3361 }
3336 }3362 }
33373363
3338 fn genBrBlockFlat(self: *Self, inst: Air.Inst.Index) !MCValue {3364 fn airBr(self: *Self, inst: Air.Inst.Index) !void {
3339 try self.genBody(inst.body);3365 const branch = self.air.instructions.items(.data)[inst].br;
3340 const last = inst.body.instructions[inst.body.instructions.len - 1];3366 try self.br(branch.block_inst, branch.operand);
3341 return self.br(inst.block, last);3367 return self.finishAirBookkeeping();
3342 }
3343
3344 fn genBr(self: *Self, inst: Air.Inst.Index) !MCValue {
3345 return self.br(inst.block, inst.operand);
3346 }3368 }
33473369
3348 fn genBoolOp(self: *Self, inst: Air.Inst.Index) !MCValue {3370 fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
3349 if (self.liveness.isUnused(inst))
3350 return MCValue.dead;
3351 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3371 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3352 const air_tags = self.air.instructions.items(.tag);3372 const air_tags = self.air.instructions.items(.tag);
3353 switch (arch) {3373 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
3354 .x86_64 => switch (air_tags[inst]) {3374 .x86_64 => switch (air_tags[inst]) {
3355 // lhs AND rhs3375 // lhs AND rhs
3356 .bool_and => return try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),3376 .bool_and => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
3357 // lhs OR rhs3377 // lhs OR rhs
3358 .bool_or => return try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),3378 .bool_or => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
3359 else => unreachable, // Not a boolean operation3379 else => unreachable, // Not a boolean operation
3360 },3380 },
3361 .arm, .armeb => switch (air_tags[inst]) {3381 .arm, .armeb => switch (air_tags[inst]) {
3362 .bool_and => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_and),3382 .bool_and => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_and),
3363 .bool_or => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_or),3383 .bool_or => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_or),
3364 else => unreachable, // Not a boolean operation3384 else => unreachable, // Not a boolean operation
3365 },3385 },
3366 else => return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch}),3386 else => return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch}),
3367 }3387 };
3388 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3368 }3389 }
33693390
3370 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Index) !MCValue {3391 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
3371 const block_data = self.blocks.getPtr(block).?;3392 const block_data = self.blocks.getPtr(block).?;
33723393
3373 if (operand.ty.hasCodeGenBits()) {3394 if (self.air.typeOf(operand).hasCodeGenBits()) {
3374 const operand_mcv = try self.resolveInst(operand);3395 const operand_mcv = try self.resolveInst(operand);
3375 const block_mcv = block_data.mcv;3396 const block_mcv = block_data.mcv;
3376 if (block_mcv == .none) {3397 if (block_mcv == .none) {
3377 block_data.mcv = operand_mcv;3398 block_data.mcv = operand_mcv;
3378 } else {3399 } else {
3379 try self.setRegOrMem(block.base.ty, block_mcv, operand_mcv);3400 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);
3380 }3401 }
3381 }3402 }
3382 return self.brVoid(block);3403 return self.brVoid(block);
3383 }3404 }
33843405
3385 fn brVoid(self: *Self, block: Air.Inst.Index) !MCValue {3406 fn brVoid(self: *Self, block: Air.Inst.Index) !void {
3386 const block_data = self.blocks.getPtr(block).?;3407 const block_data = self.blocks.getPtr(block).?;
33873408
3388 // Emit a jump with a relocation. It will be patched up after the block ends.3409 // Emit a jump with a relocation. It will be patched up after the block ends.
...@@ -3408,131 +3429,170 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3408,131 +3429,170 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3408 },3429 },
3409 else => return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch}),3430 else => return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch}),
3410 }3431 }
3411 return .none;
3412 }3432 }
34133433
3414 fn genAsm(self: *Self, inst: Air.Inst.Index) !MCValue {3434 fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3415 if (!inst.is_volatile and self.liveness.isUnused(inst))3435 const air_datas = self.air.instructions.items(.data);
3416 return MCValue.dead;3436 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
3417 switch (arch) {3437 const zir = self.mod_fn.owner_decl.namespace.file_scope.zir;
3418 .arm, .armeb => {3438 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
3419 for (inst.inputs) |input, i| {3439 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
3420 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3440 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
3421 return self.fail("unrecognized asm input constraint: '{s}'", .{input});3441 const outputs_len = @truncate(u5, extended.small);
3442 const args_len = @truncate(u5, extended.small >> 5);
3443 const clobbers_len = @truncate(u5, extended.small >> 10);
3444 _ = clobbers_len; // TODO honor these
3445 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3446 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
3447 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
3448
3449 if (outputs_len > 1) {
3450 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3451 }
3452 var extra_i: usize = zir_extra.end;
3453 const output_constraint: ?[]const u8 = out: {
3454 var i: usize = 0;
3455 while (i < outputs_len) : (i += 1) {
3456 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
3457 extra_i = output.end;
3458 break :out zir.nullTerminatedString(output.data.constraint);
3459 }
3460 break :out null;
3461 };
3462
3463 const dead = !is_volatile and self.liveness.isUnused(inst);
3464 const result: MCValue = if (dead) .dead else switch (arch) {
3465 .arm, .armeb => result: {
3466 for (args) |arg| {
3467 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3468 extra_i = input.end;
3469 const constraint = zir.nullTerminatedString(input.data.constraint);
3470
3471 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3472 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
3422 }3473 }
3423 const reg_name = input[1 .. input.len - 1];3474 const reg_name = constraint[1 .. constraint.len - 1];
3424 const reg = parseRegName(reg_name) orelse3475 const reg = parseRegName(reg_name) orelse
3425 return self.fail("unrecognized register: '{s}'", .{reg_name});3476 return self.fail("unrecognized register: '{s}'", .{reg_name});
34263477
3427 const arg = inst.args[i];
3428 const arg_mcv = try self.resolveInst(arg);3478 const arg_mcv = try self.resolveInst(arg);
3429 try self.register_manager.getReg(reg, null);3479 try self.register_manager.getReg(reg, null);
3430 try self.genSetReg(arg.ty, reg, arg_mcv);3480 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
3431 }3481 }
34323482
3433 if (mem.eql(u8, inst.asm_source, "svc #0")) {3483 if (mem.eql(u8, asm_source, "svc #0")) {
3434 writeInt(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());3484 writeInt(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());
3435 } else {3485 } else {
3436 return self.fail("TODO implement support for more arm assembly instructions", .{});3486 return self.fail("TODO implement support for more arm assembly instructions", .{});
3437 }3487 }
34383488
3439 if (inst.output_constraint) |output| {3489 if (output_constraint) |output| {
3440 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3490 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3441 return self.fail("unrecognized asm output constraint: '{s}'", .{output});3491 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3442 }3492 }
3443 const reg_name = output[2 .. output.len - 1];3493 const reg_name = output[2 .. output.len - 1];
3444 const reg = parseRegName(reg_name) orelse3494 const reg = parseRegName(reg_name) orelse
3445 return self.fail("unrecognized register: '{s}'", .{reg_name});3495 return self.fail("unrecognized register: '{s}'", .{reg_name});
3446 return MCValue{ .register = reg };3496
3497 break :result MCValue{ .register = reg };
3447 } else {3498 } else {
3448 return MCValue.none;3499 break :result MCValue.none;
3449 }3500 }
3450 },3501 },
3451 .aarch64 => {3502 .aarch64 => result: {
3452 for (inst.inputs) |input, i| {3503 for (args) |arg| {
3453 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3504 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3454 return self.fail("unrecognized asm input constraint: '{s}'", .{input});3505 extra_i = input.end;
3506 const constraint = zir.nullTerminatedString(input.data.constraint);
3507
3508 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3509 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
3455 }3510 }
3456 const reg_name = input[1 .. input.len - 1];3511 const reg_name = constraint[1 .. constraint.len - 1];
3457 const reg = parseRegName(reg_name) orelse3512 const reg = parseRegName(reg_name) orelse
3458 return self.fail("unrecognized register: '{s}'", .{reg_name});3513 return self.fail("unrecognized register: '{s}'", .{reg_name});
34593514
3460 const arg = inst.args[i];
3461 const arg_mcv = try self.resolveInst(arg);3515 const arg_mcv = try self.resolveInst(arg);
3462 try self.register_manager.getReg(reg, null);3516 try self.register_manager.getReg(reg, null);
3463 try self.genSetReg(arg.ty, reg, arg_mcv);3517 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
3464 }3518 }
34653519
3466 if (mem.eql(u8, inst.asm_source, "svc #0")) {3520 if (mem.eql(u8, asm_source, "svc #0")) {
3467 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());3521 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());
3468 } else if (mem.eql(u8, inst.asm_source, "svc #0x80")) {3522 } else if (mem.eql(u8, asm_source, "svc #0x80")) {
3469 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());3523 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());
3470 } else {3524 } else {
3471 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});3525 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});
3472 }3526 }
34733527
3474 if (inst.output_constraint) |output| {3528 if (output_constraint) |output| {
3475 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3529 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3476 return self.fail("unrecognized asm output constraint: '{s}'", .{output});3530 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3477 }3531 }
3478 const reg_name = output[2 .. output.len - 1];3532 const reg_name = output[2 .. output.len - 1];
3479 const reg = parseRegName(reg_name) orelse3533 const reg = parseRegName(reg_name) orelse
3480 return self.fail("unrecognized register: '{s}'", .{reg_name});3534 return self.fail("unrecognized register: '{s}'", .{reg_name});
3481 return MCValue{ .register = reg };3535 break :result MCValue{ .register = reg };
3482 } else {3536 } else {
3483 return MCValue.none;3537 break :result MCValue.none;
3484 }3538 }
3485 },3539 },
3486 .riscv64 => {3540 .riscv64 => result: {
3487 for (inst.inputs) |input, i| {3541 for (args) |arg| {
3488 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3542 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3489 return self.fail("unrecognized asm input constraint: '{s}'", .{input});3543 extra_i = input.end;
3544 const constraint = zir.nullTerminatedString(input.data.constraint);
3545
3546 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3547 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
3490 }3548 }
3491 const reg_name = input[1 .. input.len - 1];3549 const reg_name = constraint[1 .. constraint.len - 1];
3492 const reg = parseRegName(reg_name) orelse3550 const reg = parseRegName(reg_name) orelse
3493 return self.fail("unrecognized register: '{s}'", .{reg_name});3551 return self.fail("unrecognized register: '{s}'", .{reg_name});
34943552
3495 const arg = inst.args[i];
3496 const arg_mcv = try self.resolveInst(arg);3553 const arg_mcv = try self.resolveInst(arg);
3497 try self.register_manager.getReg(reg, null);3554 try self.register_manager.getReg(reg, null);
3498 try self.genSetReg(arg.ty, reg, arg_mcv);3555 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
3499 }3556 }
35003557
3501 if (mem.eql(u8, inst.asm_source, "ecall")) {3558 if (mem.eql(u8, asm_source, "ecall")) {
3502 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());3559 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
3503 } else {3560 } else {
3504 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});3561 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
3505 }3562 }
35063563
3507 if (inst.output_constraint) |output| {3564 if (output_constraint) |output| {
3508 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3565 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3509 return self.fail("unrecognized asm output constraint: '{s}'", .{output});3566 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3510 }3567 }
3511 const reg_name = output[2 .. output.len - 1];3568 const reg_name = output[2 .. output.len - 1];
3512 const reg = parseRegName(reg_name) orelse3569 const reg = parseRegName(reg_name) orelse
3513 return self.fail("unrecognized register: '{s}'", .{reg_name});3570 return self.fail("unrecognized register: '{s}'", .{reg_name});
3514 return MCValue{ .register = reg };3571 break :result MCValue{ .register = reg };
3515 } else {3572 } else {
3516 return MCValue.none;3573 break :result MCValue.none;
3517 }3574 }
3518 },3575 },
3519 .x86_64, .i386 => {3576 .x86_64, .i386 => result: {
3520 for (inst.inputs) |input, i| {3577 for (args) |arg| {
3521 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3578 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3522 return self.fail("unrecognized asm input constraint: '{s}'", .{input});3579 extra_i = input.end;
3580 const constraint = zir.nullTerminatedString(input.data.constraint);
3581
3582 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3583 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
3523 }3584 }
3524 const reg_name = input[1 .. input.len - 1];3585 const reg_name = constraint[1 .. constraint.len - 1];
3525 const reg = parseRegName(reg_name) orelse3586 const reg = parseRegName(reg_name) orelse
3526 return self.fail("unrecognized register: '{s}'", .{reg_name});3587 return self.fail("unrecognized register: '{s}'", .{reg_name});
35273588
3528 const arg = inst.args[i];
3529 const arg_mcv = try self.resolveInst(arg);3589 const arg_mcv = try self.resolveInst(arg);
3530 try self.register_manager.getReg(reg, null);3590 try self.register_manager.getReg(reg, null);
3531 try self.genSetReg(arg.ty, reg, arg_mcv);3591 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
3532 }3592 }
35333593
3534 {3594 {
3535 var iter = std.mem.tokenize(inst.asm_source, "\n\r");3595 var iter = std.mem.tokenize(asm_source, "\n\r");
3536 while (iter.next()) |ins| {3596 while (iter.next()) |ins| {
3537 if (mem.eql(u8, ins, "syscall")) {3597 if (mem.eql(u8, ins, "syscall")) {
3538 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });3598 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
...@@ -3571,20 +3631,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3571,20 +3631,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3571 }3631 }
3572 }3632 }
35733633
3574 if (inst.output_constraint) |output| {3634 if (output_constraint) |output| {
3575 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3635 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3576 return self.fail("unrecognized asm output constraint: '{s}'", .{output});3636 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3577 }3637 }
3578 const reg_name = output[2 .. output.len - 1];3638 const reg_name = output[2 .. output.len - 1];
3579 const reg = parseRegName(reg_name) orelse3639 const reg = parseRegName(reg_name) orelse
3580 return self.fail("unrecognized register: '{s}'", .{reg_name});3640 return self.fail("unrecognized register: '{s}'", .{reg_name});
3581 return MCValue{ .register = reg };3641 break :result MCValue{ .register = reg };
3582 } else {3642 } else {
3583 return MCValue.none;3643 break :result MCValue{ .none = {} };
3584 }3644 }
3585 },3645 },
3586 else => return self.fail("TODO implement inline asm support for more architectures", .{}),3646 else => return self.fail("TODO implement inline asm support for more architectures", .{}),
3647 };
3648 if (outputs.len + args.len <= Liveness.bpi - 1) {
3649 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3650 std.mem.copy(Air.Inst.Ref, &buf, outputs);
3651 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
3652 return self.finishAir(inst, result, buf);
3587 }3653 }
3654 @panic("TODO: codegen for asm with greater than 3 args");
3588 }3655 }
35893656
3590 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.3657 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
...@@ -3761,7 +3828,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3761,7 +3828,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3761 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });3828 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3762 },3829 },
3763 .register => |reg| {3830 .register => |reg| {
3764 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);3831 try self.genX8664ModRMRegToStack(ty, stack_offset, reg, 0x89);
3765 },3832 },
3766 .memory => |vaddr| {3833 .memory => |vaddr| {
3767 _ = vaddr;3834 _ = vaddr;
...@@ -4409,32 +4476,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4409,32 +4476,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4409 }4476 }
4410 }4477 }
44114478
4412 fn genPtrToInt(self: *Self, inst: Air.Inst.Index) !MCValue {4479 fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
4413 const un_op = self.air.instructions.items(.data)[inst].un_op;4480 const un_op = self.air.instructions.items(.data)[inst].un_op;
4414 return self.resolveInst(un_op);4481 const result = try self.resolveInst(un_op);
4482 return self.finishAir(inst, result, .{ un_op, .none, .none });
4415 }4483 }
44164484
4417 fn genBitCast(self: *Self, inst: Air.Inst.Index) !MCValue {4485 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
4418 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4486 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4419 return self.resolveInst(ty_op.operand);4487 const result = try self.resolveInst(ty_op.operand);
4488 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
4420 }4489 }
44214490
4422 fn resolveInst(self: *Self, inst: Air.Inst.Index) !MCValue {4491 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
4423 // If the type has no codegen bits, no need to store it.4492 // First section of indexes correspond to a set number of constant values.
4424 if (!inst.ty.hasCodeGenBits())4493 const ref_int = @enumToInt(inst);
4425 return MCValue.none;4494 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
44264495 return self.genTypedValue(Air.Inst.Ref.typed_value_map[ref_int]);
4427 // Constants have static lifetimes, so they are always memoized in the outer most table.
4428 if (inst.castTag(.constant)) |const_inst| {
4429 const branch = &self.branch_stack.items[0];
4430 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
4431 if (!gop.found_existing) {
4432 gop.value_ptr.* = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
4433 }
4434 return gop.value_ptr.*;
4435 }4496 }
44364497
4437 return self.getResolvedInstValue(inst);4498 // If the type has no codegen bits, no need to store it.
4499 const inst_ty = self.air.typeOf(inst);
4500 if (!inst_ty.hasCodeGenBits())
4501 return MCValue{ .none = {} };
4502
4503 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
4504 switch (self.air.instructions.items(.tag)[inst_index]) {
4505 .constant => {
4506 // Constants have static lifetimes, so they are always memoized in the outer most table.
4507 const branch = &self.branch_stack.items[0];
4508 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
4509 if (!gop.found_existing) {
4510 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
4511 gop.value_ptr.* = try self.genTypedValue(.{
4512 .ty = inst_ty,
4513 .val = self.air.values[ty_pl.payload],
4514 });
4515 }
4516 return gop.value_ptr.*;
4517 },
4518 .const_ty => unreachable,
4519 else => return self.getResolvedInstValue(inst_index),
4520 }
4438 }4521 }
44394522
4440 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {4523 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
...@@ -4454,8 +4537,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4454,8 +4537,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4454 /// A potential opportunity for future optimization here would be keeping track4537 /// A potential opportunity for future optimization here would be keeping track
4455 /// of the fact that the instruction is available both as an immediate4538 /// of the fact that the instruction is available both as an immediate
4456 /// and as a register.4539 /// and as a register.
4457 fn limitImmediateType(self: *Self, inst: Air.Inst.Index, comptime T: type) !MCValue {4540 fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue {
4458 const mcv = try self.resolveInst(inst);4541 const mcv = try self.resolveInst(operand);
4459 const ti = @typeInfo(T).Int;4542 const ti = @typeInfo(T).Int;
4460 switch (mcv) {4543 switch (mcv) {
4461 .immediate => |imm| {4544 .immediate => |imm| {
...@@ -4470,7 +4553,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4470,7 +4553,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4470 return mcv;4553 return mcv;
4471 }4554 }
44724555
4473 fn genTypedValue(self: *Self, src: LazySrcLoc, typed_value: TypedValue) InnerError!MCValue {4556 fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
4474 if (typed_value.val.isUndef())4557 if (typed_value.val.isUndef())
4475 return MCValue{ .undef = {} };4558 return MCValue{ .undef = {} };
4476 const ptr_bits = self.target.cpu.arch.ptrBitWidth();4559 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
...@@ -4480,7 +4563,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4480,7 +4563,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4480 .Slice => {4563 .Slice => {
4481 var buf: Type.Payload.ElemType = undefined;4564 var buf: Type.Payload.ElemType = undefined;
4482 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);4565 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
4483 const ptr_mcv = try self.genTypedValue(src, .{ .ty = ptr_type, .val = typed_value.val });4566 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
4484 const slice_len = typed_value.val.sliceLen();4567 const slice_len = typed_value.val.sliceLen();
4485 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean4568 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
4486 // the Sema code needs to use anonymous Decls or alloca instructions to store data.4569 // the Sema code needs to use anonymous Decls or alloca instructions to store data.
...@@ -4541,7 +4624,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4541,7 +4624,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4541 return MCValue{ .immediate = 0 };4624 return MCValue{ .immediate = 0 };
45424625
4543 var buf: Type.Payload.ElemType = undefined;4626 var buf: Type.Payload.ElemType = undefined;
4544 return self.genTypedValue(src, .{4627 return self.genTypedValue(.{
4545 .ty = typed_value.ty.optionalChild(&buf),4628 .ty = typed_value.ty.optionalChild(&buf),
4546 .val = typed_value.val,4629 .val = typed_value.val,
4547 });4630 });