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();
1313instructions: std.MultiArrayList(Inst).Slice,
1414/// The meaning of this data is determined by `Inst.Tag` value.
1515/// The first few indexes are reserved. See `ExtraIndex` for the values.
16extra: []u32,
17values: []Value,
18variables: []*Module.Var,
16extra: []const u32,
17values: []const Value,
18variables: []const *Module.Var,
1919
2020pub const ExtraIndex = enum(u32) {
2121 /// Payload index of the main `Block` in the `extra` array.
......@@ -378,22 +378,109 @@ pub fn getMainBody(air: Air) []const Air.Inst.Index {
378378 return air.extra[extra.end..][0..extra.data.body_len];
379379}
380380
381pub fn getType(air: Air, inst: Air.Inst.Index) Type {
382 _ = air;
383 _ = inst;
384 @panic("TODO Air getType");
381pub fn typeOf(air: Air, inst: Air.Inst.Ref) Type {
382 const ref_int = @enumToInt(inst);
383 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
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 }
385472}
386473
387474pub fn getRefType(air: Air, ref: Air.Inst.Ref) Type {
388 var i: usize = @enumToInt(ref);
389 if (i < Air.Inst.Ref.typed_value_map.len) {
390 return Air.Inst.Ref.typed_value_map[i].val.toType(undefined) catch unreachable;
475 const ref_int = @enumToInt(ref);
476 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
477 return Air.Inst.Ref.typed_value_map[ref_int].val.toType(undefined) catch unreachable;
391478 }
392 i -= Air.Inst.Ref.typed_value_map.len;
479 const inst_index = ref_int - Air.Inst.Ref.typed_value_map.len;
393480 const air_tags = air.instructions.items(.tag);
394481 const air_datas = air.instructions.items(.data);
395 assert(air_tags[i] == .const_ty);
396 return air_datas[i].ty;
482 assert(air_tags[inst_index] == .const_ty);
483 return air_datas[inst_index].ty;
397484}
398485
399486/// 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 {
424511 gpa.free(air.variables);
425512 air.* = undefined;
426513}
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(
64126412 node: ast.Node.Index,
64136413) InnerError!Zir.Inst.Ref {
64146414 const astgen = gz.astgen;
6415 const tree = astgen.tree;
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 }
6415 const str = try astgen.strLitNodeAsString(node);
64416416 const result = try gz.add(.{
64426417 .tag = .str,
64436418 .data = .{ .str = .{
6444 .start = @intCast(u32, str_index),
6445 .len = @intCast(u32, string_bytes.items.len - str_index),
6419 .start = str.index,
6420 .len = str.len,
64466421 } },
64476422 });
64486423 return rvalue(gz, rl, result, node);
......@@ -6620,9 +6595,14 @@ fn asmExpr(
66206595 const tree = astgen.tree;
66216596 const main_tokens = tree.nodes.items(.main_token);
66226597 const node_datas = tree.nodes.items(.data);
6598 const node_tags = tree.nodes.items(.tag);
66236599 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
66276607 // See https://github.com/ziglang/zig/issues/215 and related issues discussing
66286608 // possible inline assembly improvements. Until then here is status quo AstGen
......@@ -6752,7 +6732,7 @@ fn asmExpr(
67526732
67536733 const result = try gz.addAsm(.{
67546734 .node = node,
6755 .asm_source = asm_source,
6735 .asm_source = asm_source.index,
67566736 .is_volatile = full.volatile_token != null,
67576737 .output_type_bits = output_type_bits,
67586738 .outputs = outputs,
......@@ -8579,6 +8559,41 @@ fn strLitAsString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !IndexSlice {
85798559 }
85808560}
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
85828597fn testNameString(astgen: *AstGen, str_lit_token: ast.TokenIndex) !u32 {
85838598 const gpa = astgen.gpa;
85848599 const string_bytes = &astgen.string_bytes;
......@@ -9440,7 +9455,7 @@ const GenZir = struct {
94409455 args: struct {
94419456 /// Absolute node index. This function does the conversion to offset from Decl.
94429457 node: ast.Node.Index,
9443 asm_source: Zir.Inst.Ref,
9458 asm_source: u32,
94449459 output_type_bits: u32,
94459460 is_volatile: bool,
94469461 outputs: []const Zir.Inst.Asm.Output,
src/Liveness.zig+39-15
......@@ -21,7 +21,7 @@ const Log2Int = std.math.Log2Int;
2121/// operand dies after this instruction.
2222/// Instructions which need more data to track liveness have special handling via the
2323/// `special` table.
24tomb_bits: []const usize,
24tomb_bits: []usize,
2525/// Sparse table of specially handled instructions. The value is an index into the `extra`
2626/// array. The meaning of the data depends on the AIR tag.
2727special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
......@@ -98,7 +98,7 @@ pub fn operandDies(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) bool
9898 return (l.tomb_bits[usize_index] & mask) != 0;
9999}
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 {
102102 assert(operand < bpi - 1);
103103 const usize_index = (inst * bpi) / @bitSizeOf(usize);
104104 const mask = @as(usize, 1) <<
......@@ -106,16 +106,40 @@ pub fn clearOperandDeath(l: *Liveness, inst: Air.Inst.Index, operand: OperandInt
106106 l.tomb_bits[usize_index] |= mask;
107107}
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
109132pub fn deinit(l: *Liveness, gpa: *Allocator) void {
110133 gpa.free(l.tomb_bits);
111134 gpa.free(l.extra);
112135 l.special.deinit(gpa);
136 l.* = undefined;
113137}
114138
115139/// How many tomb bits per AIR instruction.
116const bpi = 4;
117const Bpi = std.meta.Int(.unsigned, bpi);
118const OperandInt = std.math.Log2Int(Bpi);
140pub const bpi = 4;
141pub const Bpi = std.meta.Int(.unsigned, bpi);
142pub const OperandInt = std.math.Log2Int(Bpi);
119143
120144/// In-progress data; on successful analysis converted into `Liveness`.
121145const Analysis = struct {
......@@ -267,14 +291,14 @@ fn analyzeInst(
267291 const inst_data = inst_datas[inst].pl_op;
268292 const callee = inst_data.operand;
269293 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]);
271295 if (args.len <= bpi - 2) {
272 var buf: [bpi - 1]Air.Inst.Ref = undefined;
296 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
273297 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);
275299 return trackOperands(a, new_set, inst, main_tomb, buf);
276300 }
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");
278302 },
279303 .struct_field_ptr => {
280304 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
......@@ -285,12 +309,12 @@ fn analyzeInst(
285309 const extended = a.zir.instructions.items(.data)[extra.data.zir_index].extended;
286310 const outputs_len = @truncate(u5, extended.small);
287311 const inputs_len = @truncate(u5, extended.small >> 5);
288 const outputs = a.air.extra[extra.end..][0..outputs_len];
289 const inputs = a.air.extra[extra.end + outputs.len ..][0..inputs_len];
290 if (outputs.len + inputs.len <= bpi - 1) {
291 var buf: [bpi - 1]Air.Inst.Ref = undefined;
292 std.mem.copy(Air.Inst.Ref, &buf, @bitCast([]const Air.Inst.Ref, outputs));
293 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], @bitCast([]const Air.Inst.Ref, inputs));
312 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]);
313 const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]);
314 if (outputs.len + args.len <= bpi - 1) {
315 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
316 std.mem.copy(Air.Inst.Ref, &buf, outputs);
317 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
294318 return trackOperands(a, new_set, inst, main_tomb, buf);
295319 }
296320 @panic("TODO: liveness analysis for asm with greater than 3 args");
src/Module.zig+2-2
......@@ -1309,7 +1309,7 @@ pub const Scope = struct {
13091309 const result_index = @intCast(Air.Inst.Index, sema.air_instructions.len);
13101310 sema.air_instructions.appendAssumeCapacity(inst);
13111311 block.instructions.appendAssumeCapacity(result_index);
1312 return Sema.indexToRef(result_index);
1312 return Air.indexToRef(result_index);
13131313 }
13141314 };
13151315};
......@@ -3533,7 +3533,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
35333533 const ty_ref = try sema.addType(param_type);
35343534 const arg_index = @intCast(u32, sema.air_instructions.len);
35353535 inner_block.instructions.appendAssumeCapacity(arg_index);
3536 param_inst.* = Sema.indexToRef(arg_index);
3536 param_inst.* = Air.indexToRef(arg_index);
35373537 try sema.air_instructions.append(gpa, .{
35383538 .tag = .arg,
35393539 .data = .{
src/Sema.zig+23-127
......@@ -1301,7 +1301,7 @@ fn zirArg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
13011301
13021302 // Set the name of the Air.Arg instruction for use by codegen debug info.
13031303 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;
13051305 return air_arg;
13061306}
13071307
......@@ -1389,7 +1389,7 @@ fn zirAllocInferred(
13891389 // to the block even though it is currently a `.constant`.
13901390 const result = try sema.addConstant(inferred_alloc_ty, Value.initPayload(&val_payload.base));
13911391 try sema.requireFunctionBlock(block, src);
1392 try block.instructions.append(sema.gpa, refToIndex(result).?);
1392 try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
13931393 return result;
13941394}
13951395
......@@ -1400,7 +1400,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
14001400 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
14011401 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
14021402 const ptr = sema.resolveInst(inst_data.operand);
1403 const ptr_inst = refToIndex(ptr).?;
1403 const ptr_inst = Air.refToIndex(ptr).?;
14041404 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
14051405 const air_datas = sema.air_instructions.items(.data);
14061406 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)
15861586 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
15871587 const ptr = sema.resolveInst(bin_inst.lhs);
15881588 const value = sema.resolveInst(bin_inst.rhs);
1589 const ptr_inst = refToIndex(ptr).?;
1589 const ptr_inst = Air.refToIndex(ptr).?;
15901590 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
15911591 const air_datas = sema.air_instructions.items(.data);
15921592 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
......@@ -1968,13 +1968,13 @@ fn analyzeBlockBody(
19681968
19691969 // Blocks must terminate with noreturn instruction.
19701970 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
19731973 if (merges.results.items.len == 0) {
19741974 // No need for a block instruction. We can put the new instructions
19751975 // directly into the parent block.
19761976 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]);
19781978 }
19791979 if (merges.results.items.len == 1) {
19801980 const last_inst_index = child_block.instructions.items.len - 1;
......@@ -2025,7 +2025,7 @@ fn analyzeBlockBody(
20252025 continue;
20262026 }
20272027 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] ==
2028 refToIndex(coerced_operand).?);
2028 Air.refToIndex(coerced_operand).?);
20292029
20302030 // Convert the br operand to a block.
20312031 const br_operand_ty_ref = try sema.addType(br_operand_ty);
......@@ -2034,7 +2034,7 @@ fn analyzeBlockBody(
20342034 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
20352035 const sub_block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
20362036 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);
20382038 sema.air_instructions.appendAssumeCapacity(.{
20392039 .tag = .block,
20402040 .data = .{ .ty_pl = .{
......@@ -2054,7 +2054,7 @@ fn analyzeBlockBody(
20542054 } },
20552055 });
20562056 }
2057 return indexToRef(merges.block_inst);
2057 return Air.indexToRef(merges.block_inst);
20582058}
20592059
20602060fn 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
21492149 if (label.zir_block == zir_block) {
21502150 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
21512151 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).?);
21532153 return inst;
21542154 }
21552155 }
......@@ -5310,7 +5310,7 @@ fn zirBoolBr(
53105310 } } });
53115311
53125312 try parent_block.instructions.append(gpa, block_inst);
5313 return indexToRef(block_inst);
5313 return Air.indexToRef(block_inst);
53145314}
53155315
53165316fn zirIsNonNull(
......@@ -7204,7 +7204,7 @@ fn analyzeVarRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, tv: TypedVal
72047204 } },
72057205 });
72067206 try block.instructions.append(gpa, result_inst);
7207 return indexToRef(result_inst);
7207 return Air.indexToRef(result_inst);
72087208}
72097209
72107210fn analyzeRef(
......@@ -8021,107 +8021,18 @@ fn enumFieldSrcLoc(
80218021 } else unreachable;
80228022}
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
80358024/// Returns the type of the AIR instruction.
80368025fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
8037 var i: usize = @enumToInt(inst);
8038 if (i < Air.Inst.Ref.typed_value_map.len) {
8039 return Air.Inst.Ref.typed_value_map[i].ty;
8040 }
8041 i -= Air.Inst.Ref.typed_value_map.len;
8026 return sema.getTmpAir().typeOf(inst);
8027}
80428028
8043 const air_datas = sema.air_instructions.items(.data);
8044 switch (sema.air_instructions.items(.tag)[i]) {
8045 .arg => return sema.analyzeAsTypeInfallible(air_datas[i].ty_str.ty),
8046
8047 .add,
8048 .addwrap,
8049 .sub,
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 }
8029fn getTmpAir(sema: Sema) Air {
8030 return .{
8031 .instructions = sema.air_instructions.slice(),
8032 .extra = sema.air_extra.items,
8033 .values = sema.air_values.items,
8034 .variables = sema.air_variables.items,
8035 };
81258036}
81268037
81278038pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
......@@ -8185,7 +8096,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
81858096 .tag = .const_ty,
81868097 .data = .{ .ty = ty },
81878098 });
8188 return indexToRef(@intCast(u32, sema.air_instructions.len - 1));
8099 return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1));
81898100}
81908101
81918102fn 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 {
82078118 .payload = @intCast(u32, sema.air_values.items.len - 1),
82088119 } },
82098120 });
8210 return 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 }
8121 return Air.indexToRef(@intCast(u32, sema.air_instructions.len - 1));
82268122}
82278123
82288124pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {
src/Zir.zig+4-2
......@@ -2176,7 +2176,8 @@ pub const Inst = struct {
21762176 /// 2. clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
21772177 pub const Asm = struct {
21782178 src_node: i32,
2179 asm_source: Ref,
2179 // null-terminated string index
2180 asm_source: u32,
21802181 /// 1 bit for each outputs_len: whether it uses `-> T` or not.
21812182 /// 0b0 - operand is a pointer to where to store the output.
21822183 /// 0b1 - operand is a type; asm expression has the output as the result.
......@@ -3383,9 +3384,10 @@ const Writer = struct {
33833384 const inputs_len = @truncate(u5, extended.small >> 5);
33843385 const clobbers_len = @truncate(u5, extended.small >> 10);
33853386 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3387 const asm_source = self.code.nullTerminatedString(extra.data.asm_source);
33863388
33873389 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)});
33893391 try stream.writeAll(", ");
33903392
33913393 var extra_i: usize = extra.end;
src/codegen.zig+702-619
......@@ -3,6 +3,7 @@ const mem = std.mem;
33const math = std.math;
44const assert = std.debug.assert;
55const Air = @import("Air.zig");
6const Zir = @import("Zir.zig");
67const Liveness = @import("Liveness.zig");
78const Type = @import("type.zig").Type;
89const Value = @import("value.zig").Value;
......@@ -337,6 +338,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
337338 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
338339 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
340346 const MCValue = union(enum) {
341347 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
342348 /// 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 {
751757 }
752758
753759 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
754 for (body) |inst| {
755 const tomb_bits = self.liveness.getTombBits(inst);
756 try self.ensureProcessDeathCapacity(@popCount(@TypeOf(tomb_bits), tomb_bits));
760 const air_tags = self.air.instructions.items(.tag);
757761
758 const mcv = try self.genFuncInst(inst);
759 if (!self.liveness.isUnused(inst)) {
760 log.debug("{} => {}", .{ inst, mcv });
761 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
762 try branch.inst_table.putNoClobber(self.gpa, inst, mcv);
762 for (body) |inst| {
763 const old_air_bookkeeping = self.air_bookkeeping;
764 try self.ensureProcessDeathCapacity(Liveness.bpi);
765
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 }
763844 }
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 //}
772845 }
773846 }
774847
......@@ -833,9 +906,36 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
833906 }
834907 }
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
836936 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
837937 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);
839939 }
840940
841941 /// 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 {
860960 }
861961 }
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
940963 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
941964 if (abi_align > self.stack_align)
942965 self.stack_align = abi_align;
......@@ -954,7 +977,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
954977
955978 /// Use a pointer instruction as the basis for allocating stack memory.
956979 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();
958981 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
959982 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
960983 };
......@@ -964,7 +987,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
964987 }
965988
966989 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);
968991 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
969992 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
970993 };
......@@ -993,7 +1016,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
9931016 assert(reg == toCanonicalReg(reg_mcv.register));
9941017 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
9951018 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);
9971020 }
9981021
9991022 /// 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 {
10101033 /// This can have a side effect of spilling instructions to the stack to free up a register.
10111034 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
10121035 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);
10141037 return MCValue{ .register = reg };
10151038 }
10161039
1017 fn genAlloc(self: *Self, inst: Air.Inst.Index) !MCValue {
1040 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
10181041 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 });
10201043 }
10211044
1022 fn genFloatCast(self: *Self, inst: Air.Inst.Index) !MCValue {
1023 // No side effects, so if it's unreferenced, do nothing.
1024 if (self.liveness.isUnused(inst))
1025 return MCValue.dead;
1026 switch (arch) {
1045 fn airFloatCast(self: *Self, inst: Air.Inst.Index) !void {
1046 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1047 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
10271048 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 });
10291051 }
10301052
1031 fn genIntCast(self: *Self, inst: Air.Inst.Index) !MCValue {
1032 // No side effects, so if it's unreferenced, do nothing.
1053 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
1054 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
10331055 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;
1037 const operand_ty = self.air.getType(ty_op.operand);
1058 const operand_ty = self.air.typeOf(ty_op.operand);
10381059 const operand = try self.resolveInst(ty_op.operand);
10391060 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.*);
10411062 if (info_a.signedness != info_b.signedness)
10421063 return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
10431064
10441065 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) {
10481069 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 });
10501072 }
10511073
1052 fn genNot(self: *Self, inst: Air.Inst.Index) !MCValue {
1053 // No side effects, so if it's unreferenced, do nothing.
1054 if (self.liveness.isUnused(inst))
1055 return MCValue.dead;
1074 fn airNot(self: *Self, inst: Air.Inst.Index) !void {
10561075 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1057 const operand = try self.resolveInst(ty_op.operand);
1058 switch (operand) {
1059 .dead => unreachable,
1060 .unreach => unreachable,
1061 .compare_flags_unsigned => |op| return MCValue{
1062 .compare_flags_unsigned = switch (op) {
1063 .gte => .lt,
1064 .gt => .lte,
1065 .neq => .eq,
1066 .lt => .gte,
1067 .lte => .gt,
1068 .eq => .neq,
1076 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1077 const operand = try self.resolveInst(ty_op.operand);
1078 switch (operand) {
1079 .dead => unreachable,
1080 .unreach => unreachable,
1081 .compare_flags_unsigned => |op| {
1082 const r = MCValue{
1083 .compare_flags_unsigned = switch (op) {
1084 .gte => .lt,
1085 .gt => .lte,
1086 .neq => .eq,
1087 .lt => .gte,
1088 .lte => .gt,
1089 .eq => .neq,
1090 },
1091 };
1092 break :result r;
10691093 },
1070 },
1071 .compare_flags_signed => |op| return MCValue{
1072 .compare_flags_signed = switch (op) {
1073 .gte => .lt,
1074 .gt => .lte,
1075 .neq => .eq,
1076 .lt => .gte,
1077 .lte => .gt,
1078 .eq => .neq,
1094 .compare_flags_signed => |op| {
1095 const r = MCValue{
1096 .compare_flags_signed = switch (op) {
1097 .gte => .lt,
1098 .gt => .lte,
1099 .neq => .eq,
1100 .lt => .gte,
1101 .lte => .gt,
1102 .eq => .neq,
1103 },
1104 };
1105 break :result r;
10791106 },
1080 },
1081 else => {},
1082 }
1107 else => {},
1108 }
10831109
1084 switch (arch) {
1085 .x86_64 => {
1086 return try self.genX8664BinMath(inst, ty_op.operand, .bool_true);
1087 },
1088 .arm, .armeb => {
1089 return try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not);
1090 },
1091 else => return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}),
1092 }
1110 switch (arch) {
1111 .x86_64 => {
1112 break :result try self.genX8664BinMath(inst, ty_op.operand, .bool_true);
1113 },
1114 .arm, .armeb => {
1115 break :result try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not);
1116 },
1117 else => return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}),
1118 }
1119 };
1120 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
10931121 }
10941122
1095 fn genAdd(self: *Self, inst: Air.Inst.Index) !MCValue {
1096 // No side effects, so if it's unreferenced, do nothing.
1097 if (self.liveness.isUnused(inst))
1098 return MCValue.dead;
1123 fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
10991124 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1100 switch (arch) {
1101 .x86_64 => {
1102 return try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs);
1103 },
1104 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .add),
1125 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1126 .x86_64 => try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1127 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .add),
11051128 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 });
11071131 }
11081132
1109 fn genAddWrap(self: *Self, inst: Air.Inst.Index) !MCValue {
1110 // No side effects, so if it's unreferenced, do nothing.
1111 if (self.liveness.isUnused(inst))
1112 return MCValue.dead;
1133 fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
11131134 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1114 _ = bin_op;
1115 switch (arch) {
1135 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
11161136 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 });
11181139 }
11191140
1120 fn genMul(self: *Self, inst: Air.Inst.Index) !MCValue {
1121 // No side effects, so if it's unreferenced, do nothing.
1122 if (self.liveness.isUnused(inst))
1123 return MCValue.dead;
1141 fn airSub(self: *Self, inst: Air.Inst.Index) !void {
11241142 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1125 switch (arch) {
1126 .x86_64 => return try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1127 .arm, .armeb => return try self.genArmMul(inst, bin_op.lhs, bin_op.rhs),
1143 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1144 .x86_64 => try self.genX8664BinMath(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),
11281164 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 });
11301167 }
11311168
1132 fn genMulWrap(self: *Self, inst: Air.Inst.Index) !MCValue {
1133 // No side effects, so if it's unreferenced, do nothing.
1134 if (self.liveness.isUnused(inst))
1135 return MCValue.dead;
1169 fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
11361170 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1137 _ = bin_op;
1138 switch (arch) {
1171 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
11391172 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 });
11411175 }
11421176
1143 fn genDiv(self: *Self, inst: Air.Inst.Index) !MCValue {
1144 // No side effects, so if it's unreferenced, do nothing.
1145 if (self.liveness.isUnused(inst))
1146 return MCValue.dead;
1177 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
11471178 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1148 _ = bin_op;
1149 switch (arch) {
1179 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
11501180 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 });
11521183 }
11531184
1154 fn genBitAnd(self: *Self, inst: Air.Inst.Index) !MCValue {
1155 // No side effects, so if it's unreferenced, do nothing.
1156 if (self.liveness.isUnused(inst))
1157 return MCValue.dead;
1185 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
11581186 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1159 switch (arch) {
1160 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and),
1187 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1188 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and),
11611189 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 });
11631192 }
11641193
1165 fn genBitOr(self: *Self, inst: Air.Inst.Index) !MCValue {
1166 // No side effects, so if it's unreferenced, do nothing.
1167 if (self.liveness.isUnused(inst))
1168 return MCValue.dead;
1194 fn airBitOr(self: *Self, inst: Air.Inst.Index) !void {
11691195 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1170 switch (arch) {
1171 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or),
1196 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1197 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or),
11721198 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 });
11741201 }
11751202
1176 fn genXor(self: *Self, inst: Air.Inst.Index) !MCValue {
1177 // No side effects, so if it's unreferenced, do nothing.
1178 if (self.liveness.isUnused(inst))
1179 return MCValue.dead;
1203 fn airXor(self: *Self, inst: Air.Inst.Index) !void {
11801204 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1181 switch (arch) {
1182 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .xor),
1205 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1206 .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .xor),
11831207 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 });
11851210 }
11861211
1187 fn genOptionalPayload(self: *Self, inst: Air.Inst.Index) !MCValue {
1188 // No side effects, so if it's unreferenced, do nothing.
1189 if (self.liveness.isUnused(inst))
1190 return MCValue.dead;
1191 switch (arch) {
1212 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
1213 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1214 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
11921215 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 });
11941218 }
11951219
1196 fn genOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
1197 // No side effects, so if it's unreferenced, do nothing.
1198 if (self.liveness.isUnused(inst))
1199 return MCValue.dead;
1200 switch (arch) {
1220 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
1221 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1222 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
12011223 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 });
12031226 }
12041227
1205 fn genUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !MCValue {
1206 // No side effects, so if it's unreferenced, do nothing.
1207 if (self.liveness.isUnused(inst))
1208 return MCValue.dead;
1209 switch (arch) {
1228 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
1229 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1230 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
12101231 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 });
12121234 }
12131235
1214 fn genUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !MCValue {
1215 // No side effects, so if it's unreferenced, do nothing.
1216 if (self.liveness.isUnused(inst))
1217 return MCValue.dead;
1218 switch (arch) {
1236 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1237 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1238 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
12191239 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 });
12211242 }
1243
12221244 // *(E!T) -> E
1223 fn genUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
1224 // No side effects, so if it's unreferenced, do nothing.
1225 if (self.liveness.isUnused(inst))
1226 return MCValue.dead;
1227 switch (arch) {
1245 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
1246 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1247 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
12281248 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 });
12301251 }
1252
12311253 // *(E!T) -> *T
1232 fn genUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
1233 // No side effects, so if it's unreferenced, do nothing.
1234 if (self.liveness.isUnused(inst))
1235 return MCValue.dead;
1236 switch (arch) {
1254 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
1255 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1256 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
12371257 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 });
12391260 }
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 true
1248 if (optional_ty.abiSize(self.target.*) == 1)
1249 return MCValue{ .immediate = 1 };
1267 // Optional with a zero-bit payload type is just a boolean true
1268 if (optional_ty.abiSize(self.target.*) == 1)
1269 break :result MCValue{ .immediate = 1 };
12501270
1251 switch (arch) {
1252 else => return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}),
1253 }
1271 switch (arch) {
1272 else => return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}),
1273 }
1274 };
1275 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
12541276 }
12551277
12561278 /// T to E!T
1257 fn genWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !MCValue {
1258 // No side effects, so if it's unreferenced, do nothing.
1259 if (self.liveness.isUnused(inst))
1260 return MCValue.dead;
1261
1262 switch (arch) {
1279 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
1280 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1281 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
12631282 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 });
12651285 }
12661286
12671287 /// E to E!T
1268 fn genWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !MCValue {
1269 // No side effects, so if it's unreferenced, do nothing.
1270 if (self.liveness.isUnused(inst))
1271 return MCValue.dead;
1272
1273 switch (arch) {
1288 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
1289 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1290 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
12741291 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 });
12761294 }
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) {
12831298 else => return self.fail("TODO implement varptr for {}", .{self.target.cpu.arch}),
1284 }
1299 };
1300 return self.finishAir(inst, result, .{ .none, .none, .none });
12851301 }
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 {
12881304 if (!self.liveness.operandDies(inst, op_index))
12891305 return false;
12901306
......@@ -1310,12 +1326,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13101326
13111327 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
13121328 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
13151331 return true;
13161332 }
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();
13191336 switch (ptr) {
13201337 .none => unreachable,
13211338 .undef => unreachable,
......@@ -1343,31 +1360,37 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13431360 }
13441361 }
13451362
1346 fn genLoad(self: *Self, inst: Air.Inst.Index) !MCValue {
1347 const elem_ty = self.air.getType(inst);
1348 if (!elem_ty.hasCodeGenBits())
1349 return MCValue.none;
1350 const ptr = try self.resolveInst(inst.operand);
1351 const is_volatile = inst.operand.ty.isVolatilePtr();
1352 if (self.liveness.isUnused(inst) and !is_volatile)
1353 return MCValue.dead;
1354 const dst_mcv: MCValue = blk: {
1355 if (self.reuseOperand(inst, 0, ptr)) {
1356 // The MCValue that holds the pointer can be re-used as the value.
1357 break :blk ptr;
1358 } else {
1359 break :blk try self.allocRegOrMem(inst, true);
1360 }
1363 fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
1364 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1365 const elem_ty = self.air.typeOfIndex(inst);
1366 const result: MCValue = result: {
1367 if (!elem_ty.hasCodeGenBits())
1368 break :result MCValue.none;
1369
1370 const ptr = try self.resolveInst(ty_op.operand);
1371 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();
1372 if (self.liveness.isUnused(inst) and !is_volatile)
1373 break :result MCValue.dead;
1374
1375 const dst_mcv: MCValue = blk: {
1376 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
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;
13611385 };
1362 self.load(dst_mcv, ptr);
1363 return dst_mcv;
1386 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
13641387 }
13651388
1366 fn genStore(self: *Self, inst: Air.Inst.Index) !MCValue {
1389 fn airStore(self: *Self, inst: Air.Inst.Index) !void {
13671390 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
13681391 const ptr = try self.resolveInst(bin_op.lhs);
13691392 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);
13711394 switch (ptr) {
13721395 .none => unreachable,
13731396 .undef => unreachable,
......@@ -1397,36 +1420,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13971420 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
13981421 },
13991422 }
1400 return .none;
1423 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
14011424 }
14021425
1403 fn genStructFieldPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
1404 const struct_field_ptr = self.air.instructions.items(.data)[inst].struct_field_ptr;
1405 _ = struct_field_ptr;
1426 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
1427 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1428 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1429 _ = extra;
14061430 return self.fail("TODO implement codegen struct_field_ptr", .{});
1407 }
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 }
1431 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
14301432 }
14311433
14321434 fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
......@@ -1461,8 +1463,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14611463 const rhs_is_register = rhs == .register;
14621464 const lhs_should_be_register = try self.armOperandShouldBeRegister(lhs);
14631465 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
1464 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs);
1465 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs);
1466 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1467 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
14661468
14671469 // Destination must be a register
14681470 var dst_mcv: MCValue = undefined;
......@@ -1476,14 +1478,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14761478 // Allocate 0 or 1 registers
14771479 if (!rhs_is_register and rhs_should_be_register) {
14781480 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);
14801482 }
14811483 dst_mcv = lhs;
14821484 } else if (reuse_rhs) {
14831485 // Allocate 0 or 1 registers
14841486 if (!lhs_is_register and lhs_should_be_register) {
14851487 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);
14871489 }
14881490 dst_mcv = rhs;
14891491
......@@ -1508,7 +1510,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15081510 rhs_mcv = MCValue{ .register = regs[1] };
15091511 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);
15121514 }
15131515 } else if (lhs_should_be_register) {
15141516 // RHS is immediate
......@@ -1605,14 +1607,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16051607 }
16061608 }
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 {
16091611 const lhs = try self.resolveInst(op_lhs);
16101612 const rhs = try self.resolveInst(op_rhs);
16111613
16121614 const lhs_is_register = lhs == .register;
16131615 const rhs_is_register = rhs == .register;
1614 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs);
1615 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs);
1616 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1617 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
16161618
16171619 // Destination must be a register
16181620 // LHS must be a register
......@@ -1627,14 +1629,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16271629 // Allocate 0 or 1 registers
16281630 if (!rhs_is_register) {
16291631 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);
16311633 }
16321634 dst_mcv = lhs;
16331635 } else if (reuse_rhs) {
16341636 // Allocate 0 or 1 registers
16351637 if (!lhs_is_register) {
16361638 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);
16381640 }
16391641 dst_mcv = rhs;
16401642 } else {
......@@ -1656,7 +1658,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16561658 rhs_mcv = MCValue{ .register = regs[1] };
16571659 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);
16601662 }
16611663 }
16621664
......@@ -1698,8 +1700,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16981700 // as the result MCValue.
16991701 var dst_mcv: MCValue = undefined;
17001702 var src_mcv: MCValue = undefined;
1701 var src_inst: Air.Inst.Index = undefined;
1702 if (self.reuseOperand(inst, 0, lhs)) {
1703 var src_inst: Air.Inst.Ref = undefined;
1704 if (self.reuseOperand(inst, op_lhs, 0, lhs)) {
17031705 // LHS dies; use it as the destination.
17041706 // Both operands cannot be memory.
17051707 src_inst = op_rhs;
......@@ -1710,7 +1712,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17101712 dst_mcv = lhs;
17111713 src_mcv = rhs;
17121714 }
1713 } else if (self.reuseOperand(inst, 1, rhs)) {
1715 } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) {
17141716 // RHS dies; use it as the destination.
17151717 // Both operands cannot be memory.
17161718 src_inst = op_lhs;
......@@ -1747,16 +1749,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17471749 }
17481750
17491751 // Now for step 2, we perform the actual op
1752 const inst_ty = self.air.typeOfIndex(inst);
17501753 const air_tags = self.air.instructions.items(.tag);
17511754 switch (air_tags[inst]) {
17521755 // TODO: Generate wrapping and non-wrapping versions separately
1753 .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),
1755 .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),
1757 .xor, .not => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 6, 0x30),
1756 .add, .addwrap => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 0, 0x00),
1757 .bool_or, .bit_or => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 1, 0x08),
1758 .bool_and, .bit_and => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 4, 0x20),
1759 .sub, .subwrap => try self.genX8664BinMathCode(inst_ty, dst_mcv, src_mcv, 5, 0x28),
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),
17601763 else => unreachable,
17611764 }
17621765
......@@ -1958,7 +1961,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19581961 .ptr_stack_offset => unreachable,
19591962 .ptr_embedded_in_code => unreachable,
19601963 .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);
19621965 },
19631966 .immediate => |imm| {
19641967 _ = imm;
......@@ -1984,7 +1987,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19841987 /// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv.
19851988 fn genX8664Imul(
19861989 self: *Self,
1987 src: LazySrcLoc,
19881990 dst_ty: Type,
19891991 dst_mcv: MCValue,
19901992 src_mcv: MCValue,
......@@ -2067,7 +2069,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20672069 encoder.imm32(@intCast(i32, imm));
20682070 } else {
20692071 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 });
20712073 }
20722074 },
20732075 .embedded_in_code, .memory, .stack_offset => {
......@@ -2163,7 +2165,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
21632165 }
21642166
21652167 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;
21672169 const zir = &self.mod_fn.owner_decl.namespace.file_scope.zir;
21682170 const name = zir.nullTerminatedString(ty_str.str);
21692171 const name_with_null = name.ptr[0 .. name.len + 1];
......@@ -2224,11 +2226,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22242226 }
22252227 }
22262228
2227 fn genArg(self: *Self, inst: Air.Inst.Index) !MCValue {
2229 fn airArg(self: *Self, inst: Air.Inst.Index) !void {
22282230 const arg_index = self.arg_index;
22292231 self.arg_index += 1;
22302232
2231 const ty = self.air.getType(inst);
2233 const ty = self.air.typeOfIndex(inst);
22322234
22332235 const result = self.args[arg_index];
22342236 const mcv = switch (arch) {
......@@ -2252,7 +2254,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22522254 try self.genArgDbgInfo(inst, mcv);
22532255
22542256 if (self.liveness.isUnused(inst))
2255 return MCValue.dead;
2257 return self.finishAirBookkeeping();
22562258
22572259 switch (mcv) {
22582260 .register => |reg| {
......@@ -2261,10 +2263,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22612263 else => {},
22622264 }
22632265
2264 return mcv;
2266 return self.finishAir(inst, mcv, .{ .none, .none, .none });
22652267 }
22662268
2267 fn genBreakpoint(self: *Self) !MCValue {
2269 fn airBreakpoint(self: *Self) !void {
22682270 switch (arch) {
22692271 .i386, .x86_64 => {
22702272 try self.code.append(0xcc); // int3
......@@ -2280,15 +2282,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22802282 },
22812283 else => return self.fail("TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
22822284 }
2283 return .none;
2285 return self.finishAirBookkeeping();
22842286 }
22852287
2286 fn genCall(self: *Self, inst: Air.Inst.Index) !MCValue {
2287 const pl_op = self.air.instruction.items(.data)[inst].pl_op;
2288 const fn_ty = self.air.getType(pl_op.operand);
2288 fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2289 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2290 const fn_ty = self.air.typeOf(pl_op.operand);
22892291 const callee = pl_op.operand;
2290 const extra = self.air.extraData(Air.Call, inst_data.payload);
2291 const args = self.air.extra[extra.end..][0..extra.data.args_len];
2292 const extra = self.air.extraData(Air.Call, pl_op.payload);
2293 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
22922294
22932295 var info = try self.resolveCallingConventionValues(fn_ty);
22942296 defer info.deinit(self);
......@@ -2300,6 +2302,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23002302 .x86_64 => {
23012303 for (info.args) |mc_arg, arg_i| {
23022304 const arg = args[arg_i];
2305 const arg_ty = self.air.typeOf(arg);
23032306 const arg_mcv = try self.resolveInst(args[arg_i]);
23042307 // Here we do not use setRegOrMem even though the logic is similar, because
23052308 // 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 {
23072310 .none => continue,
23082311 .register => |reg| {
23092312 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);
23112314 },
23122315 .stack_offset => |off| {
23132316 // Here we need to emit instructions like this:
23142317 // 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);
23162319 },
23172320 .ptr_stack_offset => {
23182321 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 {
23892392 .arm, .armeb => {
23902393 for (info.args) |mc_arg, arg_i| {
23912394 const arg = args[arg_i];
2395 const arg_ty = self.air.typeOf(arg);
23922396 const arg_mcv = try self.resolveInst(args[arg_i]);
23932397
23942398 switch (mc_arg) {
......@@ -2403,7 +2407,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24032407 .compare_flags_unsigned => unreachable,
24042408 .register => |reg| {
24052409 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);
24072411 },
24082412 .stack_offset => {
24092413 return self.fail("TODO implement calling with parameters in memory", .{});
......@@ -2452,6 +2456,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24522456 .aarch64 => {
24532457 for (info.args) |mc_arg, arg_i| {
24542458 const arg = args[arg_i];
2459 const arg_ty = self.air.typeOf(arg);
24552460 const arg_mcv = try self.resolveInst(args[arg_i]);
24562461
24572462 switch (mc_arg) {
......@@ -2466,7 +2471,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24662471 .compare_flags_unsigned => unreachable,
24672472 .register => |reg| {
24682473 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);
24702475 },
24712476 .stack_offset => {
24722477 return self.fail("TODO implement calling with parameters in memory", .{});
......@@ -2510,6 +2515,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25102515 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
25112516 for (info.args) |mc_arg, arg_i| {
25122517 const arg = args[arg_i];
2518 const arg_ty = self.air.typeOf(arg);
25132519 const arg_mcv = try self.resolveInst(args[arg_i]);
25142520 // Here we do not use setRegOrMem even though the logic is similar, because
25152521 // 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 {
25212527 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),
25222528 else => unreachable,
25232529 }
2524 try self.genSetReg(arg.ty, reg, arg_mcv);
2530 try self.genSetReg(arg_ty, reg, arg_mcv);
25252531 },
25262532 .stack_offset => {
25272533 // Here we need to emit instructions like this:
......@@ -2612,6 +2618,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26122618 .x86_64 => {
26132619 for (info.args) |mc_arg, arg_i| {
26142620 const arg = args[arg_i];
2621 const arg_ty = self.air.typeOf(arg);
26152622 const arg_mcv = try self.resolveInst(args[arg_i]);
26162623 // Here we do not use setRegOrMem even though the logic is similar, because
26172624 // 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 {
26192626 .none => continue,
26202627 .register => |reg| {
26212628 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);
26232630 },
26242631 .stack_offset => {
26252632 // Here we need to emit instructions like this:
......@@ -2661,6 +2668,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26612668 .aarch64 => {
26622669 for (info.args) |mc_arg, arg_i| {
26632670 const arg = inst.args[arg_i];
2671 const arg_ty = self.air.typeOf(arg);
26642672 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
26652673
26662674 switch (mc_arg) {
......@@ -2675,7 +2683,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26752683 .compare_flags_unsigned => unreachable,
26762684 .register => |reg| {
26772685 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);
26792687 },
26802688 .stack_offset => {
26812689 return self.fail("TODO implement calling with parameters in memory", .{});
......@@ -2696,7 +2704,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26962704 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
26972705 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
27012709 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
27022710 } else if (func_value.castTag(.extern_fn)) |_| {
......@@ -2712,51 +2720,61 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27122720 }
27132721 } else unreachable;
27142722
2715 switch (info.return_value) {
2716 .register => |reg| {
2717 if (Register.allocIndex(reg) == null) {
2718 // Save function return value in a callee saved register
2719 return try self.copyToNewRegister(inst, info.return_value);
2720 }
2721 },
2722 else => {},
2723 }
2723 const result: MCValue = result: {
2724 switch (info.return_value) {
2725 .register => |reg| {
2726 if (Register.allocIndex(reg) == null) {
2727 // Save function return value in a callee saved register
2728 break :result try self.copyToNewRegister(inst, info.return_value);
2729 }
2730 },
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");
27262743 }
27272744
2728 fn genRef(self: *Self, inst: Air.Inst.Index) !MCValue {
2729 if (self.liveness.isUnused(inst))
2730 return MCValue.dead;
2745 fn airRef(self: *Self, inst: Air.Inst.Index) !void {
27312746 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2732 const operand_ty = self.air.getType(ty_op.operand);
2733 const operand = try self.resolveInst(ty_op.operand);
2734 switch (operand) {
2735 .unreach => unreachable,
2736 .dead => unreachable,
2737 .none => return .none,
2738
2739 .immediate,
2740 .register,
2741 .ptr_stack_offset,
2742 .ptr_embedded_in_code,
2743 .compare_flags_unsigned,
2744 .compare_flags_signed,
2745 => {
2746 const stack_offset = try self.allocMemPtr(inst);
2747 try self.genSetStack(operand_ty, stack_offset, operand);
2748 return MCValue{ .ptr_stack_offset = stack_offset };
2749 },
2747 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2748 const operand_ty = self.air.typeOf(ty_op.operand);
2749 const operand = try self.resolveInst(ty_op.operand);
2750 switch (operand) {
2751 .unreach => unreachable,
2752 .dead => unreachable,
2753 .none => break :result MCValue{ .none = {} },
2754
2755 .immediate,
2756 .register,
2757 .ptr_stack_offset,
2758 .ptr_embedded_in_code,
2759 .compare_flags_unsigned,
2760 .compare_flags_signed,
2761 => {
2762 const stack_offset = try self.allocMemPtr(inst);
2763 try self.genSetStack(operand_ty, stack_offset, operand);
2764 break :result MCValue{ .ptr_stack_offset = stack_offset };
2765 },
27502766
2751 .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset },
2752 .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },
2753 .memory => |vaddr| return MCValue{ .immediate = vaddr },
2767 .stack_offset => |offset| break :result MCValue{ .ptr_stack_offset = offset },
2768 .embedded_in_code => |offset| break :result MCValue{ .ptr_embedded_in_code = offset },
2769 .memory => |vaddr| break :result MCValue{ .immediate = vaddr },
27542770
2755 .undef => return self.fail("TODO implement ref on an undefined value", .{}),
2756 }
2771 .undef => return self.fail("TODO implement ref on an undefined value", .{}),
2772 }
2773 };
2774 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
27572775 }
27582776
2759 fn ret(self: *Self, mcv: MCValue) !MCValue {
2777 fn ret(self: *Self, mcv: MCValue) !void {
27602778 const ret_ty = self.fn_type.fnReturnType();
27612779 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
27622780 switch (arch) {
......@@ -2786,28 +2804,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27862804 },
27872805 else => return self.fail("TODO implement return for {}", .{self.target.cpu.arch}),
27882806 }
2789 return .unreach;
27902807 }
27912808
2792 fn genRet(self: *Self, inst: Air.Inst.Index) !MCValue {
2793 const operand = try self.resolveInst(self.air.instructions.items(.data)[inst].un_op);
2794 return self.ret(inst.base.src, operand);
2809 fn airRet(self: *Self, inst: Air.Inst.Index) !void {
2810 const un_op = self.air.instructions.items(.data)[inst].un_op;
2811 const operand = try self.resolveInst(un_op);
2812 try self.ret(operand);
2813 return self.finishAirBookkeeping();
27952814 }
27962815
2797 fn genCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !MCValue {
2798 // No side effects, so if it's unreferenced, do nothing.
2799 if (self.liveness.isUnused(inst))
2800 return MCValue.dead;
2816 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
28012817 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2802 const ty = self.air.getType(bin_op.lhs);
2803 assert(ty.eql(self.air.getType(bin_op.rhs)));
2818 if (self.liveness.isUnused(inst))
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)));
28042822 if (ty.zigTypeTag() == .ErrorSet)
28052823 return self.fail("TODO implement cmp for errors", .{});
28062824
28072825 const lhs = try self.resolveInst(bin_op.lhs);
28082826 const rhs = try self.resolveInst(bin_op.rhs);
2809 switch (arch) {
2810 .x86_64 => {
2827 const result: MCValue = switch (arch) {
2828 .x86_64 => result: {
28112829 try self.code.ensureCapacity(self.code.items.len + 8);
28122830
28132831 // There are 2 operands, destination and source.
......@@ -2822,12 +2840,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28222840
28232841 try self.genX8664BinMathCode(Type.initTag(.bool), dst_mcv, src_mcv, 7, 0x38);
28242842 const info = ty.intInfo(self.target.*);
2825 return switch (info.signedness) {
2843 break :result switch (info.signedness) {
28262844 .signed => MCValue{ .compare_flags_signed = op },
28272845 .unsigned => MCValue{ .compare_flags_unsigned = op },
28282846 };
28292847 },
2830 .arm, .armeb => {
2848 .arm, .armeb => result: {
28312849 const lhs_is_register = lhs == .register;
28322850 const rhs_is_register = rhs == .register;
28332851 // lhs should always be a register
......@@ -2854,39 +2872,40 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
28542872 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
28552873 if (lhs_mcv == .register and !lhs_is_register) {
28562874 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);
28582876 }
28592877 if (rhs_mcv == .register and !rhs_is_register) {
28602878 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);
28622880 }
28632881
28642882 // The destination register is not present in the cmp instruction
28652883 try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);
28662884
28672885 const info = ty.intInfo(self.target.*);
2868 return switch (info.signedness) {
2886 break :result switch (info.signedness) {
28692887 .signed => MCValue{ .compare_flags_signed = op },
28702888 .unsigned => MCValue{ .compare_flags_unsigned = op },
28712889 };
28722890 },
28732891 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 });
28752894 }
28762895
2877 fn genDbgStmt(self: *Self, inst: Air.Inst.Index) !MCValue {
2896 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
28782897 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
28792898 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);
2880 assert(self.liveness.isUnused(inst));
2881 return MCValue.dead;
2899 return self.finishAirBookkeeping();
28822900 }
28832901
2884 fn genCondBr(self: *Self, inst: Air.Inst.Index) !MCValue {
2902 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
28852903 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
28862904 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);
28882906 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
28892907 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
28912910 const reloc: Reloc = switch (arch) {
28922911 .i386, .x86_64 => reloc: {
......@@ -2985,9 +3004,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29853004
29863005 try self.branch_stack.append(.{});
29873006
2988 const then_deaths = self.liveness.thenDeaths(inst);
2989 try self.ensureProcessDeathCapacity(then_deaths.len);
2990 for (then_deaths) |operand| {
3007 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
3008 for (liveness_condbr.then_deaths) |operand| {
29913009 self.processDeath(operand);
29923010 }
29933011 try self.genBody(then_body);
......@@ -3010,9 +3028,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30103028 const else_branch = self.branch_stack.addOneAssumeCapacity();
30113029 else_branch.* = .{};
30123030
3013 const else_deaths = self.liveness.elseDeaths(inst);
3014 try self.ensureProcessDeathCapacity(else_deaths.len);
3015 for (else_deaths) |operand| {
3031 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len);
3032 for (liveness_condbr.else_deaths) |operand| {
30163033 self.processDeath(operand);
30173034 }
30183035 try self.genBody(else_body);
......@@ -3026,8 +3043,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30263043 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
30273044 // rather than assigning it.
30283045 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() +
3030 else_branch.inst_table.count());
3046 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count());
30313047
30323048 const else_slice = else_branch.inst_table.entries.slice();
30333049 const else_keys = else_slice.items(.key);
......@@ -3058,11 +3074,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30583074 log.debug("consolidating else_entry {*} {}=>{}", .{ else_key, else_value, canon_mcv });
30593075 // TODO make sure the destination stack offset / register does not already have something
30603076 // 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);
30623078 // TODO track the new register / stack allocation
30633079 }
3064 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.count() +
3065 saved_then_branch.inst_table.count());
3080 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count());
30663081 const then_slice = saved_then_branch.inst_table.entries.slice();
30673082 const then_keys = then_slice.items(.key);
30683083 const then_values = then_slice.items(.value);
......@@ -3086,13 +3101,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30863101 log.debug("consolidating then_entry {*} {}=>{}", .{ then_key, parent_mcv, then_value });
30873102 // TODO make sure the destination stack offset / register does not already have something
30883103 // 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);
30903105 // TODO track the new register / stack allocation
30913106 }
30923107
30933108 self.branch_stack.pop().deinit(self.gpa);
30943109
3095 return MCValue.unreach;
3110 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
30963111 }
30973112
30983113 fn isNull(self: *Self, operand: MCValue) !MCValue {
......@@ -3131,107 +3146,115 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31313146 }
31323147 }
31333148
3134 fn genIsNull(self: *Self, inst: Air.Inst.Index) !MCValue {
3135 if (self.liveness.isUnused(inst))
3136 return MCValue.dead;
3149 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
31373150 const un_op = self.air.instructions.items(.data)[inst].un_op;
3138 const operand = try self.resolveInst(un_op);
3139 return self.isNull(operand);
3151 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
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 });
31403156 }
31413157
3142 fn genIsNullPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
3143 if (self.liveness.isUnused(inst))
3144 return MCValue.dead;
3158 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
31453159 const un_op = self.air.instructions.items(.data)[inst].un_op;
3146 const operand_ptr = try self.resolveInst(un_op);
3147 const operand: MCValue = blk: {
3148 if (self.reuseOperand(inst, 0, operand_ptr)) {
3149 // The MCValue that holds the pointer can be re-used as the value.
3150 break :blk operand_ptr;
3151 } else {
3152 break :blk try self.allocRegOrMem(inst, true);
3153 }
3160 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3161 const operand_ptr = try self.resolveInst(un_op);
3162 const operand: MCValue = blk: {
3163 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3164 // The MCValue that holds the pointer can be re-used as the value.
3165 break :blk operand_ptr;
3166 } else {
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);
31543172 };
3155 try self.load(operand, ptr);
3156 return self.isNull(operand);
3173 return self.finishAir(inst, result, .{ un_op, .none, .none });
31573174 }
31583175
3159 fn genIsNonNull(self: *Self, inst: Air.Inst.Index) !MCValue {
3160 if (self.liveness.isUnused(inst))
3161 return MCValue.dead;
3176 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
31623177 const un_op = self.air.instructions.items(.data)[inst].un_op;
3163 const operand = try self.resolveInst(un_op);
3164 return self.isNonNull(operand);
3178 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
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 });
31653183 }
31663184
3167 fn genIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
3168 if (self.liveness.isUnused(inst))
3169 return MCValue.dead;
3185 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
31703186 const un_op = self.air.instructions.items(.data)[inst].un_op;
3171 const operand_ptr = try self.resolveInst(un_op);
3172 const operand: MCValue = blk: {
3173 if (self.reuseOperand(inst, 0, operand_ptr)) {
3174 // The MCValue that holds the pointer can be re-used as the value.
3175 break :blk operand_ptr;
3176 } else {
3177 break :blk try self.allocRegOrMem(inst, true);
3178 }
3187 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3188 const operand_ptr = try self.resolveInst(un_op);
3189 const operand: MCValue = blk: {
3190 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3191 // The MCValue that holds the pointer can be re-used as the value.
3192 break :blk operand_ptr;
3193 } else {
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);
31793199 };
3180 try self.load(operand, ptr);
3181 return self.isNonNull(operand);
3200 return self.finishAir(inst, result, .{ un_op, .none, .none });
31823201 }
31833202
3184 fn genIsErr(self: *Self, inst: Air.Inst.Index) !MCValue {
3185 if (self.liveness.isUnused(inst))
3186 return MCValue.dead;
3203 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
31873204 const un_op = self.air.instructions.items(.data)[inst].un_op;
3188 const operand = try self.resolveInst(un_op);
3189 return self.isErr(operand);
3205 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
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 });
31903210 }
31913211
3192 fn genIsErrPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
3193 if (self.liveness.isUnused(inst))
3194 return MCValue.dead;
3212 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
31953213 const un_op = self.air.instructions.items(.data)[inst].un_op;
3196 const operand_ptr = try self.resolveInst(un_op);
3197 const operand: MCValue = blk: {
3198 if (self.reuseOperand(inst, 0, operand_ptr)) {
3199 // The MCValue that holds the pointer can be re-used as the value.
3200 break :blk operand_ptr;
3201 } else {
3202 break :blk try self.allocRegOrMem(inst, true);
3203 }
3214 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3215 const operand_ptr = try self.resolveInst(un_op);
3216 const operand: MCValue = blk: {
3217 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3218 // The MCValue that holds the pointer can be re-used as the value.
3219 break :blk operand_ptr;
3220 } else {
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);
32043226 };
3205 try self.load(operand, ptr);
3206 return self.isErr(operand);
3227 return self.finishAir(inst, result, .{ un_op, .none, .none });
32073228 }
32083229
3209 fn genIsNonErr(self: *Self, inst: Air.Inst.Index) !MCValue {
3210 if (self.liveness.isUnused(inst))
3211 return MCValue.dead;
3230 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
32123231 const un_op = self.air.instructions.items(.data)[inst].un_op;
3213 const operand = try self.resolveInst(un_op);
3214 return self.isNonErr(operand);
3232 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
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 });
32153237 }
32163238
3217 fn genIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
3218 if (self.liveness.isUnused(inst))
3219 return MCValue.dead;
3239 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
32203240 const un_op = self.air.instructions.items(.data)[inst].un_op;
3221 const operand_ptr = try self.resolveInst(un_op);
3222 const operand: MCValue = blk: {
3223 if (self.reuseOperand(inst, 0, operand_ptr)) {
3224 // The MCValue that holds the pointer can be re-used as the value.
3225 break :blk operand_ptr;
3226 } else {
3227 break :blk try self.allocRegOrMem(inst, true);
3228 }
3241 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
3242 const operand_ptr = try self.resolveInst(un_op);
3243 const operand: MCValue = blk: {
3244 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
3245 // The MCValue that holds the pointer can be re-used as the value.
3246 break :blk operand_ptr;
3247 } else {
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);
32293253 };
3230 try self.load(operand, ptr);
3231 return self.isNonErr(operand);
3254 return self.finishAir(inst, result, .{ un_op, .none, .none });
32323255 }
32333256
3234 fn genLoop(self: *Self, inst: Air.Inst.Index) !MCValue {
3257 fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
32353258 // A loop is a setup to be able to jump back to the beginning.
32363259 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
32373260 const loop = self.air.extraData(Air.Block, ty_pl.payload);
......@@ -3239,7 +3262,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32393262 const start_index = self.code.items.len;
32403263 try self.genBody(body);
32413264 try self.jump(start_index);
3242 return MCValue.unreach;
3265 return self.finishAirBookkeeping();
32433266 }
32443267
32453268 /// Send control flow to the `index` of `self.code`.
......@@ -3274,7 +3297,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32743297 }
32753298 }
32763299
3277 fn genBlock(self: *Self, inst: Air.Inst.Index) !MCValue {
3300 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
32783301 try self.blocks.putNoClobber(self.gpa, inst, .{
32793302 // A block is a setup to be able to jump to the end.
32803303 .relocs = .{},
......@@ -3288,21 +3311,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32883311 const block_data = self.blocks.getPtr(inst).?;
32893312 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;
32923315 const extra = self.air.extraData(Air.Block, ty_pl.payload);
32933316 const body = self.air.extra[extra.end..][0..extra.data.body_len];
32943317 try self.genBody(body);
32953318
32963319 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 });
32993323 }
33003324
3301 fn genSwitch(self: *Self, inst: Air.Inst.Index) !MCValue {
3302 _ = inst;
3325 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
3326 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3327 const condition = pl_op.operand;
33033328 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}),
33053330 }
3331 return self.finishAir(inst, .dead, .{ condition, .none, .none });
33063332 }
33073333
33083334 fn performReloc(self: *Self, reloc: Reloc) !void {
......@@ -3335,54 +3361,49 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
33353361 }
33363362 }
33373363
3338 fn genBrBlockFlat(self: *Self, inst: Air.Inst.Index) !MCValue {
3339 try self.genBody(inst.body);
3340 const last = inst.body.instructions[inst.body.instructions.len - 1];
3341 return self.br(inst.block, last);
3342 }
3343
3344 fn genBr(self: *Self, inst: Air.Inst.Index) !MCValue {
3345 return self.br(inst.block, inst.operand);
3364 fn airBr(self: *Self, inst: Air.Inst.Index) !void {
3365 const branch = self.air.instructions.items(.data)[inst].br;
3366 try self.br(branch.block_inst, branch.operand);
3367 return self.finishAirBookkeeping();
33463368 }
33473369
3348 fn genBoolOp(self: *Self, inst: Air.Inst.Index) !MCValue {
3349 if (self.liveness.isUnused(inst))
3350 return MCValue.dead;
3370 fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
33513371 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
33523372 const air_tags = self.air.instructions.items(.tag);
3353 switch (arch) {
3373 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
33543374 .x86_64 => switch (air_tags[inst]) {
33553375 // 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),
33573377 // 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),
33593379 else => unreachable, // Not a boolean operation
33603380 },
33613381 .arm, .armeb => switch (air_tags[inst]) {
3362 .bool_and => return 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),
3382 .bool_and => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_and),
3383 .bool_or => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_or),
33643384 else => unreachable, // Not a boolean operation
33653385 },
33663386 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 });
33683389 }
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 {
33713392 const block_data = self.blocks.getPtr(block).?;
33723393
3373 if (operand.ty.hasCodeGenBits()) {
3394 if (self.air.typeOf(operand).hasCodeGenBits()) {
33743395 const operand_mcv = try self.resolveInst(operand);
33753396 const block_mcv = block_data.mcv;
33763397 if (block_mcv == .none) {
33773398 block_data.mcv = operand_mcv;
33783399 } else {
3379 try self.setRegOrMem(block.base.ty, block_mcv, operand_mcv);
3400 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);
33803401 }
33813402 }
33823403 return self.brVoid(block);
33833404 }
33843405
3385 fn brVoid(self: *Self, block: Air.Inst.Index) !MCValue {
3406 fn brVoid(self: *Self, block: Air.Inst.Index) !void {
33863407 const block_data = self.blocks.getPtr(block).?;
33873408
33883409 // 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 {
34083429 },
34093430 else => return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch}),
34103431 }
3411 return .none;
34123432 }
34133433
3414 fn genAsm(self: *Self, inst: Air.Inst.Index) !MCValue {
3415 if (!inst.is_volatile and self.liveness.isUnused(inst))
3416 return MCValue.dead;
3417 switch (arch) {
3418 .arm, .armeb => {
3419 for (inst.inputs) |input, i| {
3420 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3421 return self.fail("unrecognized asm input constraint: '{s}'", .{input});
3434 fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3435 const air_datas = self.air.instructions.items(.data);
3436 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
3437 const zir = self.mod_fn.owner_decl.namespace.file_scope.zir;
3438 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
3439 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
3440 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
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});
34223473 }
3423 const reg_name = input[1 .. input.len - 1];
3474 const reg_name = constraint[1 .. constraint.len - 1];
34243475 const reg = parseRegName(reg_name) orelse
34253476 return self.fail("unrecognized register: '{s}'", .{reg_name});
34263477
3427 const arg = inst.args[i];
34283478 const arg_mcv = try self.resolveInst(arg);
34293479 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);
34313481 }
34323482
3433 if (mem.eql(u8, inst.asm_source, "svc #0")) {
3483 if (mem.eql(u8, asm_source, "svc #0")) {
34343484 writeInt(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());
34353485 } else {
34363486 return self.fail("TODO implement support for more arm assembly instructions", .{});
34373487 }
34383488
3439 if (inst.output_constraint) |output| {
3489 if (output_constraint) |output| {
34403490 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
34413491 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
34423492 }
34433493 const reg_name = output[2 .. output.len - 1];
34443494 const reg = parseRegName(reg_name) orelse
34453495 return self.fail("unrecognized register: '{s}'", .{reg_name});
3446 return MCValue{ .register = reg };
3496
3497 break :result MCValue{ .register = reg };
34473498 } else {
3448 return MCValue.none;
3499 break :result MCValue.none;
34493500 }
34503501 },
3451 .aarch64 => {
3452 for (inst.inputs) |input, i| {
3453 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3454 return self.fail("unrecognized asm input constraint: '{s}'", .{input});
3502 .aarch64 => result: {
3503 for (args) |arg| {
3504 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
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});
34553510 }
3456 const reg_name = input[1 .. input.len - 1];
3511 const reg_name = constraint[1 .. constraint.len - 1];
34573512 const reg = parseRegName(reg_name) orelse
34583513 return self.fail("unrecognized register: '{s}'", .{reg_name});
34593514
3460 const arg = inst.args[i];
34613515 const arg_mcv = try self.resolveInst(arg);
34623516 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);
34643518 }
34653519
3466 if (mem.eql(u8, inst.asm_source, "svc #0")) {
3520 if (mem.eql(u8, asm_source, "svc #0")) {
34673521 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")) {
34693523 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());
34703524 } else {
34713525 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});
34723526 }
34733527
3474 if (inst.output_constraint) |output| {
3528 if (output_constraint) |output| {
34753529 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
34763530 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
34773531 }
34783532 const reg_name = output[2 .. output.len - 1];
34793533 const reg = parseRegName(reg_name) orelse
34803534 return self.fail("unrecognized register: '{s}'", .{reg_name});
3481 return MCValue{ .register = reg };
3535 break :result MCValue{ .register = reg };
34823536 } else {
3483 return MCValue.none;
3537 break :result MCValue.none;
34843538 }
34853539 },
3486 .riscv64 => {
3487 for (inst.inputs) |input, i| {
3488 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3489 return self.fail("unrecognized asm input constraint: '{s}'", .{input});
3540 .riscv64 => result: {
3541 for (args) |arg| {
3542 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
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});
34903548 }
3491 const reg_name = input[1 .. input.len - 1];
3549 const reg_name = constraint[1 .. constraint.len - 1];
34923550 const reg = parseRegName(reg_name) orelse
34933551 return self.fail("unrecognized register: '{s}'", .{reg_name});
34943552
3495 const arg = inst.args[i];
34963553 const arg_mcv = try self.resolveInst(arg);
34973554 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);
34993556 }
35003557
3501 if (mem.eql(u8, inst.asm_source, "ecall")) {
3558 if (mem.eql(u8, asm_source, "ecall")) {
35023559 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
35033560 } else {
35043561 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
35053562 }
35063563
3507 if (inst.output_constraint) |output| {
3564 if (output_constraint) |output| {
35083565 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
35093566 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
35103567 }
35113568 const reg_name = output[2 .. output.len - 1];
35123569 const reg = parseRegName(reg_name) orelse
35133570 return self.fail("unrecognized register: '{s}'", .{reg_name});
3514 return MCValue{ .register = reg };
3571 break :result MCValue{ .register = reg };
35153572 } else {
3516 return MCValue.none;
3573 break :result MCValue.none;
35173574 }
35183575 },
3519 .x86_64, .i386 => {
3520 for (inst.inputs) |input, i| {
3521 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3522 return self.fail("unrecognized asm input constraint: '{s}'", .{input});
3576 .x86_64, .i386 => result: {
3577 for (args) |arg| {
3578 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
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});
35233584 }
3524 const reg_name = input[1 .. input.len - 1];
3585 const reg_name = constraint[1 .. constraint.len - 1];
35253586 const reg = parseRegName(reg_name) orelse
35263587 return self.fail("unrecognized register: '{s}'", .{reg_name});
35273588
3528 const arg = inst.args[i];
35293589 const arg_mcv = try self.resolveInst(arg);
35303590 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);
35323592 }
35333593
35343594 {
3535 var iter = std.mem.tokenize(inst.asm_source, "\n\r");
3595 var iter = std.mem.tokenize(asm_source, "\n\r");
35363596 while (iter.next()) |ins| {
35373597 if (mem.eql(u8, ins, "syscall")) {
35383598 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
......@@ -3571,20 +3631,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
35713631 }
35723632 }
35733633
3574 if (inst.output_constraint) |output| {
3634 if (output_constraint) |output| {
35753635 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
35763636 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
35773637 }
35783638 const reg_name = output[2 .. output.len - 1];
35793639 const reg = parseRegName(reg_name) orelse
35803640 return self.fail("unrecognized register: '{s}'", .{reg_name});
3581 return MCValue{ .register = reg };
3641 break :result MCValue{ .register = reg };
35823642 } else {
3583 return MCValue.none;
3643 break :result MCValue{ .none = {} };
35843644 }
35853645 },
35863646 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);
35873653 }
3654 @panic("TODO: codegen for asm with greater than 3 args");
35883655 }
35893656
35903657 /// 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 {
37613828 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
37623829 },
37633830 .register => |reg| {
3764 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
3831 try self.genX8664ModRMRegToStack(ty, stack_offset, reg, 0x89);
37653832 },
37663833 .memory => |vaddr| {
37673834 _ = vaddr;
......@@ -4409,32 +4476,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
44094476 }
44104477 }
44114478
4412 fn genPtrToInt(self: *Self, inst: Air.Inst.Index) !MCValue {
4479 fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
44134480 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 });
44154483 }
44164484
4417 fn genBitCast(self: *Self, inst: Air.Inst.Index) !MCValue {
4485 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
44184486 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 });
44204489 }
44214490
4422 fn resolveInst(self: *Self, inst: Air.Inst.Index) !MCValue {
4423 // If the type has no codegen bits, no need to store it.
4424 if (!inst.ty.hasCodeGenBits())
4425 return MCValue.none;
4426
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.*;
4491 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
4492 // First section of indexes correspond to a set number of constant values.
4493 const ref_int = @enumToInt(inst);
4494 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
4495 return self.genTypedValue(Air.Inst.Ref.typed_value_map[ref_int]);
44354496 }
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 }
44384521 }
44394522
44404523 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
......@@ -4454,8 +4537,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
44544537 /// A potential opportunity for future optimization here would be keeping track
44554538 /// of the fact that the instruction is available both as an immediate
44564539 /// and as a register.
4457 fn limitImmediateType(self: *Self, inst: Air.Inst.Index, comptime T: type) !MCValue {
4458 const mcv = try self.resolveInst(inst);
4540 fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue {
4541 const mcv = try self.resolveInst(operand);
44594542 const ti = @typeInfo(T).Int;
44604543 switch (mcv) {
44614544 .immediate => |imm| {
......@@ -4470,7 +4553,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
44704553 return mcv;
44714554 }
44724555
4473 fn genTypedValue(self: *Self, src: LazySrcLoc, typed_value: TypedValue) InnerError!MCValue {
4556 fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
44744557 if (typed_value.val.isUndef())
44754558 return MCValue{ .undef = {} };
44764559 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
......@@ -4480,7 +4563,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
44804563 .Slice => {
44814564 var buf: Type.Payload.ElemType = undefined;
44824565 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 });
44844567 const slice_len = typed_value.val.sliceLen();
44854568 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
44864569 // 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 {
45414624 return MCValue{ .immediate = 0 };
45424625
45434626 var buf: Type.Payload.ElemType = undefined;
4544 return self.genTypedValue(src, .{
4627 return self.genTypedValue(.{
45454628 .ty = typed_value.ty.optionalChild(&buf),
45464629 .val = typed_value.val,
45474630 });