authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-19 02:57:48-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-19 02:57:48-05:00
log2e1c16d64979c15604b90128fbd63d26ab4b796d
treea6cea3b541c8902a8a6b63a17e2b4a2b076a1b08
parent09d93ec845f2f1adaefc512fccaeaa0ea8beed61
parent4e1e5ab6221b72ef2be9f1fb40c2e6d1235718fe
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10924 from ziglang/air-independence-day

AIR independence day

15 files changed, 562 insertions(+), 403 deletions(-)

lib/std/array_list.zig+8
...@@ -780,6 +780,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -780,6 +780,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
780 pub fn allocatedSlice(self: Self) Slice {780 pub fn allocatedSlice(self: Self) Slice {
781 return self.items.ptr[0..self.capacity];781 return self.items.ptr[0..self.capacity];
782 }782 }
783
784 /// Returns a slice of only the extra capacity after items.
785 /// This can be useful for writing directly into an ArrayList.
786 /// Note that such an operation must be followed up with a direct
787 /// modification of `self.items.len`.
788 pub fn unusedCapacitySlice(self: Self) Slice {
789 return self.allocatedSlice()[self.items.len..];
790 }
783 };791 };
784}792}
785793
src/Air.zig+20-13
...@@ -32,8 +32,7 @@ pub const Inst = struct {...@@ -32,8 +32,7 @@ pub const Inst = struct {
32 /// The first N instructions in the main block must be one arg instruction per32 /// The first N instructions in the main block must be one arg instruction per
33 /// function parameter. This makes function parameters participate in33 /// function parameter. This makes function parameters participate in
34 /// liveness analysis without any special handling.34 /// liveness analysis without any special handling.
35 /// Uses the `ty_str` field.35 /// Uses the `ty` field.
36 /// The string is the parameter name.
37 arg,36 arg,
38 /// Float or integer addition. For integers, wrapping is undefined behavior.37 /// Float or integer addition. For integers, wrapping is undefined behavior.
39 /// Both operands are guaranteed to be the same type, and the result type38 /// Both operands are guaranteed to be the same type, and the result type
...@@ -621,11 +620,6 @@ pub const Inst = struct {...@@ -621,11 +620,6 @@ pub const Inst = struct {
621 // Index into a different array.620 // Index into a different array.
622 payload: u32,621 payload: u32,
623 },622 },
624 ty_str: struct {
625 ty: Ref,
626 // ZIR string table index.
627 str: u32,
628 },
629 br: struct {623 br: struct {
630 block_inst: Index,624 block_inst: Index,
631 operand: Ref,625 operand: Ref,
...@@ -709,11 +703,25 @@ pub const Bin = struct {...@@ -709,11 +703,25 @@ pub const Bin = struct {
709/// Trailing:703/// Trailing:
710/// 0. `Inst.Ref` for every outputs_len704/// 0. `Inst.Ref` for every outputs_len
711/// 1. `Inst.Ref` for every inputs_len705/// 1. `Inst.Ref` for every inputs_len
706/// 2. for every outputs_len
707/// - constraint: memory at this position is reinterpreted as a null
708/// terminated string. pad to the next u32 after the null byte.
709/// 3. for every inputs_len
710/// - constraint: memory at this position is reinterpreted as a null
711/// terminated string. pad to the next u32 after the null byte.
712/// 4. for every clobbers_len
713/// - clobber_name: memory at this position is reinterpreted as a null
714/// terminated string. pad to the next u32 after the null byte.
715/// 5. A number of u32 elements follow according to the equation `(source_len + 3) / 4`.
716/// Memory starting at this position is reinterpreted as the source bytes.
712pub const Asm = struct {717pub const Asm = struct {
713 /// Index to the corresponding ZIR instruction.718 /// Length of the assembly source in bytes.
714 /// `asm_source`, `outputs_len`, `inputs_len`, `clobbers_len`, `is_volatile`, and719 source_len: u32,
715 /// clobbers are found via here.720 outputs_len: u32,
716 zir_index: u32,721 inputs_len: u32,
722 /// The MSB is `is_volatile`.
723 /// The rest of the bits are `clobbers_len`.
724 flags: u32,
717};725};
718726
719pub const Cmpxchg = struct {727pub const Cmpxchg = struct {
...@@ -765,8 +773,6 @@ pub fn typeOf(air: Air, inst: Air.Inst.Ref) Type {...@@ -765,8 +773,6 @@ pub fn typeOf(air: Air, inst: Air.Inst.Ref) Type {
765pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {773pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
766 const datas = air.instructions.items(.data);774 const datas = air.instructions.items(.data);
767 switch (air.instructions.items(.tag)[inst]) {775 switch (air.instructions.items(.tag)[inst]) {
768 .arg => return air.getRefType(datas[inst].ty_str.ty),
769
770 .add,776 .add,
771 .addwrap,777 .addwrap,
772 .add_sat,778 .add_sat,
...@@ -833,6 +839,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -833,6 +839,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
833839
834 .alloc,840 .alloc,
835 .ret_ptr,841 .ret_ptr,
842 .arg,
836 => return datas[inst].ty,843 => return datas[inst].ty,
837844
838 .assembly,845 .assembly,
src/Compilation.zig+2-2
...@@ -2778,7 +2778,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress...@@ -2778,7 +2778,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress
2778 errdefer if (!liveness_frame_ended) liveness_frame.end();2778 errdefer if (!liveness_frame_ended) liveness_frame.end();
27792779
2780 log.debug("analyze liveness of {s}", .{decl.name});2780 log.debug("analyze liveness of {s}", .{decl.name});
2781 var liveness = try Liveness.analyze(gpa, air, decl.getFileScope().zir);2781 var liveness = try Liveness.analyze(gpa, air);
2782 defer liveness.deinit(gpa);2782 defer liveness.deinit(gpa);
27832783
2784 liveness_frame.end();2784 liveness_frame.end();
...@@ -2786,7 +2786,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress...@@ -2786,7 +2786,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress
27862786
2787 if (builtin.mode == .Debug and comp.verbose_air) {2787 if (builtin.mode == .Debug and comp.verbose_air) {
2788 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});2788 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
2789 @import("print_air.zig").dump(gpa, air, decl.getFileScope().zir, liveness);2789 @import("print_air.zig").dump(gpa, air, liveness);
2790 std.debug.print("# End Function AIR: {s}\n\n", .{decl.name});2790 std.debug.print("# End Function AIR: {s}\n\n", .{decl.name});
2791 }2791 }
27922792
src/Liveness.zig+23-15
...@@ -12,7 +12,6 @@ const log = std.log.scoped(.liveness);...@@ -12,7 +12,6 @@ const log = std.log.scoped(.liveness);
12const assert = std.debug.assert;12const assert = std.debug.assert;
13const Allocator = std.mem.Allocator;13const Allocator = std.mem.Allocator;
14const Air = @import("Air.zig");14const Air = @import("Air.zig");
15const Zir = @import("Zir.zig");
16const Log2Int = std.math.Log2Int;15const Log2Int = std.math.Log2Int;
1716
18/// This array is split into sets of 4 bits per AIR instruction.17/// This array is split into sets of 4 bits per AIR instruction.
...@@ -52,7 +51,7 @@ pub const SwitchBr = struct {...@@ -52,7 +51,7 @@ pub const SwitchBr = struct {
52 else_death_count: u32,51 else_death_count: u32,
53};52};
5453
55pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness {54pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
56 const tracy = trace(@src());55 const tracy = trace(@src());
57 defer tracy.end();56 defer tracy.end();
5857
...@@ -66,7 +65,6 @@ pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness {...@@ -66,7 +65,6 @@ pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness {
66 ),65 ),
67 .extra = .{},66 .extra = .{},
68 .special = .{},67 .special = .{},
69 .zir = &zir,
70 };68 };
71 errdefer gpa.free(a.tomb_bits);69 errdefer gpa.free(a.tomb_bits);
72 errdefer a.special.deinit(gpa);70 errdefer a.special.deinit(gpa);
...@@ -157,7 +155,6 @@ const Analysis = struct {...@@ -157,7 +155,6 @@ const Analysis = struct {
157 tomb_bits: []usize,155 tomb_bits: []usize,
158 special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),156 special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
159 extra: std.ArrayListUnmanaged(u32),157 extra: std.ArrayListUnmanaged(u32),
160 zir: *const Zir,
161158
162 fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void {159 fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void {
163 const usize_index = (inst * bpi) / @bitSizeOf(usize);160 const usize_index = (inst * bpi) / @bitSizeOf(usize);
...@@ -446,15 +443,24 @@ fn analyzeInst(...@@ -446,15 +443,24 @@ fn analyzeInst(
446 },443 },
447 .assembly => {444 .assembly => {
448 const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);445 const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);
449 const extended = a.zir.instructions.items(.data)[extra.data.zir_index].extended;446 var extra_i: usize = extra.end;
450 const outputs_len = @truncate(u5, extended.small);447 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]);
451 const inputs_len = @truncate(u5, extended.small >> 5);448 extra_i += outputs.len;
452 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]);449 const inputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]);
453 const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]);450 extra_i += inputs.len;
454 if (outputs.len + args.len <= bpi - 1) {451
452 simple: {
455 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);453 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
456 std.mem.copy(Air.Inst.Ref, &buf, outputs);454 var buf_index: usize = 0;
457 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);455 for (outputs) |output| {
456 if (output != .none) {
457 if (buf_index >= buf.len) break :simple;
458 buf[buf_index] = output;
459 buf_index += 1;
460 }
461 }
462 if (buf_index + inputs.len > buf.len) break :simple;
463 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
458 return trackOperands(a, new_set, inst, main_tomb, buf);464 return trackOperands(a, new_set, inst, main_tomb, buf);
459 }465 }
460 var extra_tombs: ExtraTombs = .{466 var extra_tombs: ExtraTombs = .{
...@@ -464,10 +470,12 @@ fn analyzeInst(...@@ -464,10 +470,12 @@ fn analyzeInst(
464 .main_tomb = main_tomb,470 .main_tomb = main_tomb,
465 };471 };
466 for (outputs) |output| {472 for (outputs) |output| {
467 try extra_tombs.feed(output);473 if (output != .none) {
474 try extra_tombs.feed(output);
475 }
468 }476 }
469 for (args) |arg| {477 for (inputs) |input| {
470 try extra_tombs.feed(arg);478 try extra_tombs.feed(input);
471 }479 }
472 return extra_tombs.finish();480 return extra_tombs.finish();
473 },481 },
src/Module.zig+21-5
...@@ -1370,6 +1370,14 @@ pub const Fn = struct {...@@ -1370,6 +1370,14 @@ pub const Fn = struct {
1370 /// ZIR instruction.1370 /// ZIR instruction.
1371 zir_body_inst: Zir.Inst.Index,1371 zir_body_inst: Zir.Inst.Index,
13721372
1373 /// Prefer to use `getParamName` to access this because of the future improvement
1374 /// we want to do mentioned in the TODO below.
1375 /// Stored in gpa.
1376 /// TODO: change param ZIR instructions to be embedded inside the function
1377 /// ZIR instruction instead of before it, so that `zir_body_inst` can be used to
1378 /// determine param names rather than redundantly storing them here.
1379 param_names: []const [:0]const u8,
1380
1373 /// Relative to owner Decl.1381 /// Relative to owner Decl.
1374 lbrace_line: u32,1382 lbrace_line: u32,
1375 /// Relative to owner Decl.1383 /// Relative to owner Decl.
...@@ -1466,6 +1474,18 @@ pub const Fn = struct {...@@ -1466,6 +1474,18 @@ pub const Fn = struct {
1466 gpa.destroy(node);1474 gpa.destroy(node);
1467 it = next;1475 it = next;
1468 }1476 }
1477
1478 for (func.param_names) |param_name| {
1479 gpa.free(param_name);
1480 }
1481 gpa.free(func.param_names);
1482 }
1483
1484 pub fn getParamName(func: Fn, index: u32) [:0]const u8 {
1485 // TODO rework ZIR of parameters so that this function looks up
1486 // param names in ZIR instead of redundantly saving them into Fn.
1487 // const zir = func.owner_decl.getFileScope().zir;
1488 return func.param_names[index];
1469 }1489 }
1470};1490};
14711491
...@@ -4606,15 +4626,11 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem...@@ -4606,15 +4626,11 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
4606 runtime_param_index += 1;4626 runtime_param_index += 1;
4607 continue;4627 continue;
4608 }4628 }
4609 const ty_ref = try sema.addType(param_type);
4610 const arg_index = @intCast(u32, sema.air_instructions.len);4629 const arg_index = @intCast(u32, sema.air_instructions.len);
4611 inner_block.instructions.appendAssumeCapacity(arg_index);4630 inner_block.instructions.appendAssumeCapacity(arg_index);
4612 sema.air_instructions.appendAssumeCapacity(.{4631 sema.air_instructions.appendAssumeCapacity(.{
4613 .tag = .arg,4632 .tag = .arg,
4614 .data = .{ .ty_str = .{4633 .data = .{ .ty = param_type },
4615 .ty = ty_ref,
4616 .str = param.name,
4617 } },
4618 });4634 });
4619 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));4635 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
4620 total_param_index += 1;4636 total_param_index += 1;
src/Sema.zig+60-20
...@@ -134,6 +134,7 @@ pub const Block = struct {...@@ -134,6 +134,7 @@ pub const Block = struct {
134 /// `noreturn` means `anytype`.134 /// `noreturn` means `anytype`.
135 ty: Type,135 ty: Type,
136 is_comptime: bool,136 is_comptime: bool,
137 name: []const u8,
137 };138 };
138139
139 /// This `Block` maps a block ZIR instruction to the corresponding140 /// This `Block` maps a block ZIR instruction to the corresponding
...@@ -284,13 +285,10 @@ pub const Block = struct {...@@ -284,13 +285,10 @@ pub const Block = struct {
284 });285 });
285 }286 }
286287
287 fn addArg(block: *Block, ty: Type, name: u32) error{OutOfMemory}!Air.Inst.Ref {288 fn addArg(block: *Block, ty: Type) error{OutOfMemory}!Air.Inst.Ref {
288 return block.addInst(.{289 return block.addInst(.{
289 .tag = .arg,290 .tag = .arg,
290 .data = .{ .ty_str = .{291 .data = .{ .ty = ty },
291 .ty = try block.sema.addType(ty),
292 .str = name,
293 } },
294 });292 });
295 }293 }
296294
...@@ -1126,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -1126,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1126 .frame_address => return sema.zirFrameAddress( block, extended),1124 .frame_address => return sema.zirFrameAddress( block, extended),
1127 .alloc => return sema.zirAllocExtended( block, extended),1125 .alloc => return sema.zirAllocExtended( block, extended),
1128 .builtin_extern => return sema.zirBuiltinExtern( block, extended),1126 .builtin_extern => return sema.zirBuiltinExtern( block, extended),
1129 .@"asm" => return sema.zirAsm( block, extended, inst),1127 .@"asm" => return sema.zirAsm( block, extended),
1130 .typeof_peer => return sema.zirTypeofPeer( block, extended),1128 .typeof_peer => return sema.zirTypeofPeer( block, extended),
1131 .compile_log => return sema.zirCompileLog( block, extended),1129 .compile_log => return sema.zirCompileLog( block, extended),
1132 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),1130 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
...@@ -4645,7 +4643,7 @@ fn analyzeCall(...@@ -4645,7 +4643,7 @@ fn analyzeCall(
4645 } else {4643 } else {
4646 // We insert into the map an instruction which is runtime-known4644 // We insert into the map an instruction which is runtime-known
4647 // but has the type of the argument.4645 // but has the type of the argument.
4648 const child_arg = try child_block.addArg(arg_ty, 0);4646 const child_arg = try child_block.addArg(arg_ty);
4649 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);4647 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
4650 }4648 }
4651 }4649 }
...@@ -5712,6 +5710,11 @@ fn funcCommon(...@@ -5712,6 +5710,11 @@ fn funcCommon(
5712 break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr;5710 break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr;
5713 } else null;5711 } else null;
57145712
5713 const param_names = try sema.gpa.alloc([:0]const u8, block.params.items.len);
5714 for (param_names) |*param_name, i| {
5715 param_name.* = try sema.gpa.dupeZ(u8, block.params.items[i].name);
5716 }
5717
5715 const fn_payload = try sema.arena.create(Value.Payload.Function);5718 const fn_payload = try sema.arena.create(Value.Payload.Function);
5716 new_func.* = .{5719 new_func.* = .{
5717 .state = anal_state,5720 .state = anal_state,
...@@ -5722,6 +5725,7 @@ fn funcCommon(...@@ -5722,6 +5725,7 @@ fn funcCommon(
5722 .rbrace_line = src_locs.rbrace_line,5725 .rbrace_line = src_locs.rbrace_line,
5723 .lbrace_column = @truncate(u16, src_locs.columns),5726 .lbrace_column = @truncate(u16, src_locs.columns),
5724 .rbrace_column = @truncate(u16, src_locs.columns >> 16),5727 .rbrace_column = @truncate(u16, src_locs.columns >> 16),
5728 .param_names = param_names,
5725 };5729 };
5726 if (maybe_inferred_error_set_node) |node| {5730 if (maybe_inferred_error_set_node) |node| {
5727 new_func.inferred_error_sets.prepend(node);5731 new_func.inferred_error_sets.prepend(node);
...@@ -5746,10 +5750,6 @@ fn zirParam(...@@ -5746,10 +5750,6 @@ fn zirParam(
5746 const param_name = sema.code.nullTerminatedString(extra.data.name);5750 const param_name = sema.code.nullTerminatedString(extra.data.name);
5747 const body = sema.code.extra[extra.end..][0..extra.data.body_len];5751 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
57485752
5749 // TODO check if param_name shadows a Decl. This only needs to be done if
5750 // usingnamespace is implemented.
5751 _ = param_name;
5752
5753 // We could be in a generic function instantiation, or we could be evaluating a generic5753 // We could be in a generic function instantiation, or we could be evaluating a generic
5754 // function without any comptime args provided.5754 // function without any comptime args provided.
5755 const param_ty = param_ty: {5755 const param_ty = param_ty: {
...@@ -5776,6 +5776,7 @@ fn zirParam(...@@ -5776,6 +5776,7 @@ fn zirParam(
5776 try block.params.append(sema.gpa, .{5776 try block.params.append(sema.gpa, .{
5777 .ty = Type.initTag(.generic_poison),5777 .ty = Type.initTag(.generic_poison),
5778 .is_comptime = comptime_syntax,5778 .is_comptime = comptime_syntax,
5779 .name = param_name,
5779 });5780 });
5780 try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison);5781 try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison);
5781 return;5782 return;
...@@ -5801,6 +5802,7 @@ fn zirParam(...@@ -5801,6 +5802,7 @@ fn zirParam(
5801 try block.params.append(sema.gpa, .{5802 try block.params.append(sema.gpa, .{
5802 .ty = param_ty,5803 .ty = param_ty,
5803 .is_comptime = is_comptime,5804 .is_comptime = is_comptime,
5805 .name = param_name,
5804 });5806 });
5805 const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));5807 const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
5806 try sema.inst_map.putNoClobber(sema.gpa, inst, result);5808 try sema.inst_map.putNoClobber(sema.gpa, inst, result);
...@@ -5816,10 +5818,6 @@ fn zirParamAnytype(...@@ -5816,10 +5818,6 @@ fn zirParamAnytype(
5816 const src = inst_data.src();5818 const src = inst_data.src();
5817 const param_name = inst_data.get(sema.code);5819 const param_name = inst_data.get(sema.code);
58185820
5819 // TODO check if param_name shadows a Decl. This only needs to be done if
5820 // usingnamespace is implemented.
5821 _ = param_name;
5822
5823 if (sema.inst_map.get(inst)) |air_ref| {5821 if (sema.inst_map.get(inst)) |air_ref| {
5824 const param_ty = sema.typeOf(air_ref);5822 const param_ty = sema.typeOf(air_ref);
5825 if (comptime_syntax or try sema.typeRequiresComptime(block, src, param_ty)) {5823 if (comptime_syntax or try sema.typeRequiresComptime(block, src, param_ty)) {
...@@ -5831,6 +5829,7 @@ fn zirParamAnytype(...@@ -5831,6 +5829,7 @@ fn zirParamAnytype(
5831 try block.params.append(sema.gpa, .{5829 try block.params.append(sema.gpa, .{
5832 .ty = param_ty,5830 .ty = param_ty,
5833 .is_comptime = false,5831 .is_comptime = false,
5832 .name = param_name,
5834 });5833 });
5835 return;5834 return;
5836 }5835 }
...@@ -5840,6 +5839,7 @@ fn zirParamAnytype(...@@ -5840,6 +5839,7 @@ fn zirParamAnytype(
5840 try block.params.append(sema.gpa, .{5839 try block.params.append(sema.gpa, .{
5841 .ty = Type.initTag(.generic_poison),5840 .ty = Type.initTag(.generic_poison),
5842 .is_comptime = comptime_syntax,5841 .is_comptime = comptime_syntax,
5842 .name = param_name,
5843 });5843 });
5844 try sema.inst_map.put(sema.gpa, inst, .generic_poison);5844 try sema.inst_map.put(sema.gpa, inst, .generic_poison);
5845}5845}
...@@ -9083,7 +9083,6 @@ fn zirAsm(...@@ -9083,7 +9083,6 @@ fn zirAsm(
9083 sema: *Sema,9083 sema: *Sema,
9084 block: *Block,9084 block: *Block,
9085 extended: Zir.Inst.Extended.InstData,9085 extended: Zir.Inst.Extended.InstData,
9086 inst: Zir.Inst.Index,
9087) CompileError!Air.Inst.Ref {9086) CompileError!Air.Inst.Ref {
9088 const tracy = trace(@src());9087 const tracy = trace(@src());
9089 defer tracy.end();9088 defer tracy.end();
...@@ -9094,6 +9093,7 @@ fn zirAsm(...@@ -9094,6 +9093,7 @@ fn zirAsm(
9094 const outputs_len = @truncate(u5, extended.small);9093 const outputs_len = @truncate(u5, extended.small);
9095 const inputs_len = @truncate(u5, extended.small >> 5);9094 const inputs_len = @truncate(u5, extended.small >> 5);
9096 const clobbers_len = @truncate(u5, extended.small >> 10);9095 const clobbers_len = @truncate(u5, extended.small >> 10);
9096 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
90979097
9098 if (extra.data.asm_source == 0) {9098 if (extra.data.asm_source == 0) {
9099 // This can move to become an AstGen error after inline assembly improvements land9099 // This can move to become an AstGen error after inline assembly improvements land
...@@ -9107,6 +9107,7 @@ fn zirAsm(...@@ -9107,6 +9107,7 @@ fn zirAsm(
91079107
9108 var extra_i = extra.end;9108 var extra_i = extra.end;
9109 var output_type_bits = extra.data.output_type_bits;9109 var output_type_bits = extra.data.output_type_bits;
9110 var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len;
91109111
9111 const Output = struct { constraint: []const u8, ty: Type };9112 const Output = struct { constraint: []const u8, ty: Type };
9112 const output: ?Output = if (outputs_len == 0) null else blk: {9113 const output: ?Output = if (outputs_len == 0) null else blk: {
...@@ -9121,6 +9122,8 @@ fn zirAsm(...@@ -9121,6 +9122,8 @@ fn zirAsm(
9121 }9122 }
91229123
9123 const constraint = sema.code.nullTerminatedString(output.data.constraint);9124 const constraint = sema.code.nullTerminatedString(output.data.constraint);
9125 needed_capacity += constraint.len / 4 + 1;
9126
9124 break :blk Output{9127 break :blk Output{
9125 .constraint = constraint,9128 .constraint = constraint,
9126 .ty = try sema.resolveType(block, ret_ty_src, output.data.operand),9129 .ty = try sema.resolveType(block, ret_ty_src, output.data.operand),
...@@ -9138,28 +9141,65 @@ fn zirAsm(...@@ -9138,28 +9141,65 @@ fn zirAsm(
9138 _ = name; // TODO: use the name9141 _ = name; // TODO: use the name
91399142
9140 arg.* = sema.resolveInst(input.data.operand);9143 arg.* = sema.resolveInst(input.data.operand);
9141 inputs[arg_i] = sema.code.nullTerminatedString(input.data.constraint);9144 const constraint = sema.code.nullTerminatedString(input.data.constraint);
9145 needed_capacity += constraint.len / 4 + 1;
9146 inputs[arg_i] = constraint;
9142 }9147 }
91439148
9144 const clobbers = try sema.arena.alloc([]const u8, clobbers_len);9149 const clobbers = try sema.arena.alloc([]const u8, clobbers_len);
9145 for (clobbers) |*name| {9150 for (clobbers) |*name| {
9146 name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);9151 name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
9147 extra_i += 1;9152 extra_i += 1;
9153
9154 needed_capacity += name.*.len / 4 + 1;
9148 }9155 }
91499156
9150 try sema.requireRuntimeBlock(block, src);9157 const asm_source = sema.code.nullTerminatedString(extra.data.asm_source);
9158 needed_capacity += (asm_source.len + 3) / 4;
9159
9151 const gpa = sema.gpa;9160 const gpa = sema.gpa;
9152 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Asm).Struct.fields.len + args.len);9161 try sema.requireRuntimeBlock(block, src);
9162 try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity);
9153 const asm_air = try block.addInst(.{9163 const asm_air = try block.addInst(.{
9154 .tag = .assembly,9164 .tag = .assembly,
9155 .data = .{ .ty_pl = .{9165 .data = .{ .ty_pl = .{
9156 .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type,9166 .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type,
9157 .payload = sema.addExtraAssumeCapacity(Air.Asm{9167 .payload = sema.addExtraAssumeCapacity(Air.Asm{
9158 .zir_index = inst,9168 .source_len = @intCast(u32, asm_source.len),
9169 .outputs_len = outputs_len,
9170 .inputs_len = @intCast(u32, args.len),
9171 .flags = (@as(u32, @boolToInt(is_volatile)) << 31) | @intCast(u32, clobbers.len),
9159 }),9172 }),
9160 } },9173 } },
9161 });9174 });
9175 if (output != null) {
9176 // Indicate the output is the asm instruction return value.
9177 sema.air_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.none));
9178 }
9162 sema.appendRefsAssumeCapacity(args);9179 sema.appendRefsAssumeCapacity(args);
9180 if (output) |o| {
9181 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
9182 mem.copy(u8, buffer, o.constraint);
9183 buffer[o.constraint.len] = 0;
9184 sema.air_extra.items.len += o.constraint.len / 4 + 1;
9185 }
9186 for (inputs) |constraint| {
9187 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
9188 mem.copy(u8, buffer, constraint);
9189 buffer[constraint.len] = 0;
9190 sema.air_extra.items.len += constraint.len / 4 + 1;
9191 }
9192 for (clobbers) |clobber| {
9193 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
9194 mem.copy(u8, buffer, clobber);
9195 buffer[clobber.len] = 0;
9196 sema.air_extra.items.len += clobber.len / 4 + 1;
9197 }
9198 {
9199 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
9200 mem.copy(u8, buffer, asm_source);
9201 sema.air_extra.items.len += (asm_source.len + 3) / 4;
9202 }
9163 return asm_air;9203 return asm_air;
9164}9204}
91659205
src/arch/aarch64/CodeGen.zig+63-70
...@@ -4,7 +4,6 @@ const mem = std.mem;...@@ -4,7 +4,6 @@ const mem = std.mem;
4const math = std.math;4const math = std.math;
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const Air = @import("../../Air.zig");6const Air = @import("../../Air.zig");
7const Zir = @import("../../Zir.zig");
8const Mir = @import("Mir.zig");7const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");8const Emit = @import("Emit.zig");
10const Liveness = @import("../../Liveness.zig");9const Liveness = @import("../../Liveness.zig");
...@@ -2021,36 +2020,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2021,36 +2020,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2021 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });2020 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
2022}2021}
20232022
2024fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
2025 const ty_str = self.air.instructions.items(.data)[inst].ty_str;
2026 const zir = &self.mod_fn.owner_decl.getFileScope().zir;
2027 const name = zir.nullTerminatedString(ty_str.str);
2028 const name_with_null = name.ptr[0 .. name.len + 1];
2029 const ty = self.air.getRefType(ty_str.ty);
2030
2031 switch (mcv) {
2032 .register => |reg| {
2033 switch (self.debug_output) {
2034 .dwarf => |dbg_out| {
2035 try dbg_out.dbg_info.ensureUnusedCapacity(3);
2036 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
2037 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
2038 1, // ULEB128 dwarf expression length
2039 reg.dwarfLocOp(),
2040 });
2041 try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
2042 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
2043 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
2044 },
2045 .plan9 => {},
2046 .none => {},
2047 }
2048 },
2049 .stack_offset => {},
2050 else => {},
2051 }
2052}
2053
2054fn airArg(self: *Self, inst: Air.Inst.Index) !void {2023fn airArg(self: *Self, inst: Air.Inst.Index) !void {
2055 const arg_index = self.arg_index;2024 const arg_index = self.arg_index;
2056 self.arg_index += 1;2025 self.arg_index += 1;
...@@ -2866,40 +2835,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -2866,40 +2835,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
2866}2835}
28672836
2868fn airAsm(self: *Self, inst: Air.Inst.Index) !void {2837fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2869 const air_datas = self.air.instructions.items(.data);2838 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2870 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);2839 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
2871 const zir = self.mod_fn.owner_decl.getFileScope().zir;2840 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2872 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;2841 const clobbers_len = @truncate(u31, extra.data.flags);
2873 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);2842 var extra_i: usize = extra.end;
2874 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);2843 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
2875 const outputs_len = @truncate(u5, extended.small);2844 extra_i += outputs.len;
2876 const args_len = @truncate(u5, extended.small >> 5);2845 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
2877 const clobbers_len = @truncate(u5, extended.small >> 10);2846 extra_i += inputs.len;
2878 _ = clobbers_len; // TODO honor these
2879 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
2880 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
2881 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
2882
2883 if (outputs_len > 1) {
2884 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
2885 }
2886 var extra_i: usize = zir_extra.end;
2887 const output_constraint: ?[]const u8 = out: {
2888 var i: usize = 0;
2889 while (i < outputs_len) : (i += 1) {
2890 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
2891 extra_i = output.end;
2892 break :out zir.nullTerminatedString(output.data.constraint);
2893 }
2894 break :out null;
2895 };
28962847
2897 const dead = !is_volatile and self.liveness.isUnused(inst);2848 const dead = !is_volatile and self.liveness.isUnused(inst);
2898 const result: MCValue = if (dead) .dead else result: {2849 const result: MCValue = if (dead) .dead else result: {
2899 for (args) |arg| {2850 if (outputs.len > 1) {
2900 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);2851 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
2901 extra_i = input.end;2852 }
2902 const constraint = zir.nullTerminatedString(input.data.constraint);2853
2854 const output_constraint: ?[]const u8 = for (outputs) |output| {
2855 if (output != .none) {
2856 return self.fail("TODO implement codegen for non-expr asm", .{});
2857 }
2858 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2859 // This equation accounts for the fact that even if we have exactly 4 bytes
2860 // for the string, we still use the next u32 for the null terminator.
2861 extra_i += constraint.len / 4 + 1;
2862
2863 break constraint;
2864 } else null;
2865
2866 for (inputs) |input| {
2867 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2868 // This equation accounts for the fact that even if we have exactly 4 bytes
2869 // for the string, we still use the next u32 for the null terminator.
2870 extra_i += constraint.len / 4 + 1;
29032871
2904 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {2872 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
2905 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});2873 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
...@@ -2908,11 +2876,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -2908,11 +2876,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2908 const reg = parseRegName(reg_name) orelse2876 const reg = parseRegName(reg_name) orelse
2909 return self.fail("unrecognized register: '{s}'", .{reg_name});2877 return self.fail("unrecognized register: '{s}'", .{reg_name});
29102878
2911 const arg_mcv = try self.resolveInst(arg);2879 const arg_mcv = try self.resolveInst(input);
2912 try self.register_manager.getReg(reg, null);2880 try self.register_manager.getReg(reg, null);
2913 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);2881 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
2882 }
2883
2884 {
2885 var clobber_i: u32 = 0;
2886 while (clobber_i < clobbers_len) : (clobber_i += 1) {
2887 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2888 // This equation accounts for the fact that even if we have exactly 4 bytes
2889 // for the string, we still use the next u32 for the null terminator.
2890 extra_i += clobber.len / 4 + 1;
2891
2892 // TODO honor these
2893 }
2914 }2894 }
29152895
2896 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
2897
2916 if (mem.eql(u8, asm_source, "svc #0")) {2898 if (mem.eql(u8, asm_source, "svc #0")) {
2917 _ = try self.addInst(.{2899 _ = try self.addInst(.{
2918 .tag = .svc,2900 .tag = .svc,
...@@ -2939,18 +2921,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -2939,18 +2921,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2939 break :result MCValue{ .none = {} };2921 break :result MCValue{ .none = {} };
2940 }2922 }
2941 };2923 };
2942 if (outputs.len + args.len <= Liveness.bpi - 1) {2924
2925 simple: {
2943 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);2926 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2944 std.mem.copy(Air.Inst.Ref, &buf, outputs);2927 var buf_index: usize = 0;
2945 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);2928 for (outputs) |output| {
2929 if (output == .none) continue;
2930
2931 if (buf_index >= buf.len) break :simple;
2932 buf[buf_index] = output;
2933 buf_index += 1;
2934 }
2935 if (buf_index + inputs.len > buf.len) break :simple;
2936 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
2946 return self.finishAir(inst, result, buf);2937 return self.finishAir(inst, result, buf);
2947 }2938 }
2948 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);2939 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
2949 for (outputs) |output| {2940 for (outputs) |output| {
2941 if (output == .none) continue;
2942
2950 bt.feed(output);2943 bt.feed(output);
2951 }2944 }
2952 for (args) |arg| {2945 for (inputs) |input| {
2953 bt.feed(arg);2946 bt.feed(input);
2954 }2947 }
2955 return bt.finishAir(result);2948 return bt.finishAir(result);
2956}2949}
src/arch/arm/CodeGen.zig+63-40
...@@ -4,7 +4,6 @@ const mem = std.mem;...@@ -4,7 +4,6 @@ const mem = std.mem;
4const math = std.math;4const math = std.math;
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const Air = @import("../../Air.zig");6const Air = @import("../../Air.zig");
7const Zir = @import("../../Zir.zig");
8const Mir = @import("Mir.zig");7const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");8const Emit = @import("Emit.zig");
10const Liveness = @import("../../Liveness.zig");9const Liveness = @import("../../Liveness.zig");
...@@ -3075,40 +3074,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -3075,40 +3074,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
3075}3074}
30763075
3077fn airAsm(self: *Self, inst: Air.Inst.Index) !void {3076fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3078 const air_datas = self.air.instructions.items(.data);3077 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3079 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);3078 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
3080 const zir = self.mod_fn.owner_decl.getFileScope().zir;3079 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
3081 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;3080 const clobbers_len = @truncate(u31, extra.data.flags);
3082 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);3081 var extra_i: usize = extra.end;
3083 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);3082 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
3084 const outputs_len = @truncate(u5, extended.small);3083 extra_i += outputs.len;
3085 const args_len = @truncate(u5, extended.small >> 5);3084 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
3086 const clobbers_len = @truncate(u5, extended.small >> 10);3085 extra_i += inputs.len;
3087 _ = clobbers_len; // TODO honor these
3088 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3089 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
3090 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
3091
3092 if (outputs_len > 1) {
3093 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3094 }
3095 var extra_i: usize = zir_extra.end;
3096 const output_constraint: ?[]const u8 = out: {
3097 var i: usize = 0;
3098 while (i < outputs_len) : (i += 1) {
3099 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
3100 extra_i = output.end;
3101 break :out zir.nullTerminatedString(output.data.constraint);
3102 }
3103 break :out null;
3104 };
31053086
3106 const dead = !is_volatile and self.liveness.isUnused(inst);3087 const dead = !is_volatile and self.liveness.isUnused(inst);
3107 const result: MCValue = if (dead) .dead else result: {3088 const result: MCValue = if (dead) .dead else result: {
3108 for (args) |arg| {3089 if (outputs.len > 1) {
3109 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);3090 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3110 extra_i = input.end;3091 }
3111 const constraint = zir.nullTerminatedString(input.data.constraint);3092
3093 const output_constraint: ?[]const u8 = for (outputs) |output| {
3094 if (output != .none) {
3095 return self.fail("TODO implement codegen for non-expr asm", .{});
3096 }
3097 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3098 // This equation accounts for the fact that even if we have exactly 4 bytes
3099 // for the string, we still use the next u32 for the null terminator.
3100 extra_i += constraint.len / 4 + 1;
3101
3102 break constraint;
3103 } else null;
3104
3105 for (inputs) |input| {
3106 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3107 // This equation accounts for the fact that even if we have exactly 4 bytes
3108 // for the string, we still use the next u32 for the null terminator.
3109 extra_i += constraint.len / 4 + 1;
31123110
3113 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {3111 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3114 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});3112 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
...@@ -3117,11 +3115,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -3117,11 +3115,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3117 const reg = parseRegName(reg_name) orelse3115 const reg = parseRegName(reg_name) orelse
3118 return self.fail("unrecognized register: '{s}'", .{reg_name});3116 return self.fail("unrecognized register: '{s}'", .{reg_name});
31193117
3120 const arg_mcv = try self.resolveInst(arg);3118 const arg_mcv = try self.resolveInst(input);
3121 try self.register_manager.getReg(reg, null);3119 try self.register_manager.getReg(reg, null);
3122 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);3120 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
3121 }
3122
3123 {
3124 var clobber_i: u32 = 0;
3125 while (clobber_i < clobbers_len) : (clobber_i += 1) {
3126 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3127 // This equation accounts for the fact that even if we have exactly 4 bytes
3128 // for the string, we still use the next u32 for the null terminator.
3129 extra_i += clobber.len / 4 + 1;
3130
3131 // TODO honor these
3132 }
3123 }3133 }
31243134
3135 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
3136
3125 if (mem.eql(u8, asm_source, "svc #0")) {3137 if (mem.eql(u8, asm_source, "svc #0")) {
3126 _ = try self.addInst(.{3138 _ = try self.addInst(.{
3127 .tag = .svc,3139 .tag = .svc,
...@@ -3144,18 +3156,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -3144,18 +3156,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3144 break :result MCValue{ .none = {} };3156 break :result MCValue{ .none = {} };
3145 }3157 }
3146 };3158 };
3147 if (outputs.len + args.len <= Liveness.bpi - 1) {3159
3160 simple: {
3148 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);3161 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3149 std.mem.copy(Air.Inst.Ref, &buf, outputs);3162 var buf_index: usize = 0;
3150 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);3163 for (outputs) |output| {
3164 if (output == .none) continue;
3165
3166 if (buf_index >= buf.len) break :simple;
3167 buf[buf_index] = output;
3168 buf_index += 1;
3169 }
3170 if (buf_index + inputs.len > buf.len) break :simple;
3171 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
3151 return self.finishAir(inst, result, buf);3172 return self.finishAir(inst, result, buf);
3152 }3173 }
3153 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);3174 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
3154 for (outputs) |output| {3175 for (outputs) |output| {
3176 if (output == .none) continue;
3177
3155 bt.feed(output);3178 bt.feed(output);
3156 }3179 }
3157 for (args) |arg| {3180 for (inputs) |input| {
3158 bt.feed(arg);3181 bt.feed(input);
3159 }3182 }
3160 return bt.finishAir(result);3183 return bt.finishAir(result);
3161}3184}
src/arch/arm/Emit.zig+2-4
...@@ -393,11 +393,9 @@ fn addDbgInfoTypeReloc(self: *Emit, ty: Type) !void {...@@ -393,11 +393,9 @@ fn addDbgInfoTypeReloc(self: *Emit, ty: Type) !void {
393fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void {393fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void {
394 const mcv = self.function.args[arg_index];394 const mcv = self.function.args[arg_index];
395395
396 const ty_str = self.function.air.instructions.items(.data)[inst].ty_str;396 const ty = self.function.air.instructions.items(.data)[inst].ty;
397 const zir = &self.function.mod_fn.owner_decl.getFileScope().zir;397 const name = self.function.mod_fn.getParamName(arg_index);
398 const name = zir.nullTerminatedString(ty_str.str);
399 const name_with_null = name.ptr[0 .. name.len + 1];398 const name_with_null = name.ptr[0 .. name.len + 1];
400 const ty = self.function.air.getRefType(ty_str.ty);
401399
402 switch (mcv) {400 switch (mcv) {
403 .register => |reg| {401 .register => |reg| {
src/arch/riscv64/CodeGen.zig+66-46
...@@ -4,7 +4,6 @@ const mem = std.mem;...@@ -4,7 +4,6 @@ const mem = std.mem;
4const math = std.math;4const math = std.math;
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const Air = @import("../../Air.zig");6const Air = @import("../../Air.zig");
7const Zir = @import("../../Zir.zig");
8const Mir = @import("Mir.zig");7const Mir = @import("Mir.zig");
9const Emit = @import("Emit.zig");8const Emit = @import("Emit.zig");
10const Liveness = @import("../../Liveness.zig");9const Liveness = @import("../../Liveness.zig");
...@@ -1354,12 +1353,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -1354,12 +1353,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
1354 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });1353 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
1355}1354}
13561355
1357fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {1356fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void {
1358 const ty_str = self.air.instructions.items(.data)[inst].ty_str;1357 const ty = self.air.instructions.items(.data)[inst].ty;
1359 const zir = &self.mod_fn.owner_decl.getFileScope().zir;1358 const name = self.mod_fn.getParamName(arg_index);
1360 const name = zir.nullTerminatedString(ty_str.str);
1361 const name_with_null = name.ptr[0 .. name.len + 1];1359 const name_with_null = name.ptr[0 .. name.len + 1];
1362 const ty = self.air.getRefType(ty_str.ty);
13631360
1364 switch (mcv) {1361 switch (mcv) {
1365 .register => |reg| {1362 .register => |reg| {
...@@ -1402,7 +1399,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -1402,7 +1399,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1402 // TODO support stack-only arguments1399 // TODO support stack-only arguments
1403 // TODO Copy registers to the stack1400 // TODO Copy registers to the stack
1404 const mcv = result;1401 const mcv = result;
1405 try self.genArgDbgInfo(inst, mcv);1402 try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index));
14061403
1407 if (self.liveness.isUnused(inst))1404 if (self.liveness.isUnused(inst))
1408 return self.finishAirBookkeeping();1405 return self.finishAirBookkeeping();
...@@ -1838,40 +1835,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -1838,40 +1835,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
1838}1835}
18391836
1840fn airAsm(self: *Self, inst: Air.Inst.Index) !void {1837fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1841 const air_datas = self.air.instructions.items(.data);1838 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1842 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);1839 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
1843 const zir = self.mod_fn.owner_decl.getFileScope().zir;1840 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
1844 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;1841 const clobbers_len = @truncate(u31, extra.data.flags);
1845 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);1842 var extra_i: usize = extra.end;
1846 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);1843 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
1847 const outputs_len = @truncate(u5, extended.small);1844 extra_i += outputs.len;
1848 const args_len = @truncate(u5, extended.small >> 5);1845 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
1849 const clobbers_len = @truncate(u5, extended.small >> 10);1846 extra_i += inputs.len;
1850 _ = clobbers_len; // TODO honor these
1851 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
1852 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
1853 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
1854
1855 if (outputs_len > 1) {
1856 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
1857 }
1858 var extra_i: usize = zir_extra.end;
1859 const output_constraint: ?[]const u8 = out: {
1860 var i: usize = 0;
1861 while (i < outputs_len) : (i += 1) {
1862 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
1863 extra_i = output.end;
1864 break :out zir.nullTerminatedString(output.data.constraint);
1865 }
1866 break :out null;
1867 };
18681847
1869 const dead = !is_volatile and self.liveness.isUnused(inst);1848 const dead = !is_volatile and self.liveness.isUnused(inst);
1870 const result: MCValue = if (dead) .dead else result: {1849 const result: MCValue = if (dead) .dead else result: {
1871 for (args) |arg| {1850 if (outputs.len > 1) {
1872 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);1851 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
1873 extra_i = input.end;1852 }
1874 const constraint = zir.nullTerminatedString(input.data.constraint);1853
1854 const output_constraint: ?[]const u8 = for (outputs) |output| {
1855 if (output != .none) {
1856 return self.fail("TODO implement codegen for non-expr asm", .{});
1857 }
1858 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
1859 // This equation accounts for the fact that even if we have exactly 4 bytes
1860 // for the string, we still use the next u32 for the null terminator.
1861 extra_i += constraint.len / 4 + 1;
1862
1863 break constraint;
1864 } else null;
1865
1866 for (inputs) |input| {
1867 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
1868 // This equation accounts for the fact that even if we have exactly 4 bytes
1869 // for the string, we still use the next u32 for the null terminator.
1870 extra_i += constraint.len / 4 + 1;
18751871
1876 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {1872 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
1877 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});1873 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
...@@ -1880,11 +1876,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -1880,11 +1876,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1880 const reg = parseRegName(reg_name) orelse1876 const reg = parseRegName(reg_name) orelse
1881 return self.fail("unrecognized register: '{s}'", .{reg_name});1877 return self.fail("unrecognized register: '{s}'", .{reg_name});
18821878
1883 const arg_mcv = try self.resolveInst(arg);1879 const arg_mcv = try self.resolveInst(input);
1884 try self.register_manager.getReg(reg, null);1880 try self.register_manager.getReg(reg, null);
1885 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);1881 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
1886 }1882 }
18871883
1884 {
1885 var clobber_i: u32 = 0;
1886 while (clobber_i < clobbers_len) : (clobber_i += 1) {
1887 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
1888 // This equation accounts for the fact that even if we have exactly 4 bytes
1889 // for the string, we still use the next u32 for the null terminator.
1890 extra_i += clobber.len / 4 + 1;
1891
1892 // TODO honor these
1893 }
1894 }
1895
1896 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
1897
1888 if (mem.eql(u8, asm_source, "ecall")) {1898 if (mem.eql(u8, asm_source, "ecall")) {
1889 _ = try self.addInst(.{1899 _ = try self.addInst(.{
1890 .tag = .ecall,1900 .tag = .ecall,
...@@ -1906,18 +1916,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -1906,18 +1916,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1906 break :result MCValue{ .none = {} };1916 break :result MCValue{ .none = {} };
1907 }1917 }
1908 };1918 };
1909 if (outputs.len + args.len <= Liveness.bpi - 1) {1919 simple: {
1910 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);1920 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
1911 std.mem.copy(Air.Inst.Ref, &buf, outputs);1921 var buf_index: usize = 0;
1912 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);1922 for (outputs) |output| {
1923 if (output == .none) continue;
1924
1925 if (buf_index >= buf.len) break :simple;
1926 buf[buf_index] = output;
1927 buf_index += 1;
1928 }
1929 if (buf_index + inputs.len > buf.len) break :simple;
1930 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
1913 return self.finishAir(inst, result, buf);1931 return self.finishAir(inst, result, buf);
1914 }1932 }
1915 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);1933 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
1916 for (outputs) |output| {1934 for (outputs) |output| {
1935 if (output == .none) continue;
1936
1917 bt.feed(output);1937 bt.feed(output);
1918 }1938 }
1919 for (args) |arg| {1939 for (inputs) |input| {
1920 bt.feed(arg);1940 bt.feed(input);
1921 }1941 }
1922 return bt.finishAir(result);1942 return bt.finishAir(result);
1923}1943}
src/arch/x86_64/CodeGen.zig+69-43
...@@ -26,7 +26,6 @@ const Target = std.Target;...@@ -26,7 +26,6 @@ const Target = std.Target;
26const Type = @import("../../type.zig").Type;26const Type = @import("../../type.zig").Type;
27const TypedValue = @import("../../TypedValue.zig");27const TypedValue = @import("../../TypedValue.zig");
28const Value = @import("../../value.zig").Value;28const Value = @import("../../value.zig").Value;
29const Zir = @import("../../Zir.zig");
3029
31const InnerError = error{30const InnerError = error{
32 OutOfMemory,31 OutOfMemory,
...@@ -3435,41 +3434,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -3435,41 +3434,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
3435}3434}
34363435
3437fn airAsm(self: *Self, inst: Air.Inst.Index) !void {3436fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3438 const air_datas = self.air.instructions.items(.data);3437 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3439 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);3438 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
3440 const zir = self.mod_fn.owner_decl.getFileScope().zir;3439 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
3441 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;3440 const clobbers_len = @truncate(u31, extra.data.flags);
3442 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);3441 var extra_i: usize = extra.end;
3443 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);3442 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
3444 const outputs_len = @truncate(u5, extended.small);3443 extra_i += outputs.len;
3445 const args_len = @truncate(u5, extended.small >> 5);3444 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
3446 const clobbers_len = @truncate(u5, extended.small >> 10);3445 extra_i += inputs.len;
3447 _ = clobbers_len; // TODO honor these
3448 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3449 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..args_len]);
3450
3451 if (outputs_len > 1) {
3452 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3453 }
3454 var extra_i: usize = zir_extra.end;
3455 const output_constraint: ?[]const u8 = out: {
3456 var i: usize = 0;
3457 while (i < outputs_len) : (i += 1) {
3458 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
3459 extra_i = output.end;
3460 break :out zir.nullTerminatedString(output.data.constraint);
3461 }
3462 break :out null;
3463 };
34643446
3465 const dead = !is_volatile and self.liveness.isUnused(inst);3447 const dead = !is_volatile and self.liveness.isUnused(inst);
3466 const result: MCValue = if (dead)3448 const result: MCValue = if (dead) .dead else result: {
3467 .dead3449 if (outputs.len > 1) {
3468 else result: {3450 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3469 for (args) |arg| {3451 }
3470 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);3452
3471 extra_i = input.end;3453 const output_constraint: ?[]const u8 = for (outputs) |output| {
3472 const constraint = zir.nullTerminatedString(input.data.constraint);3454 if (output != .none) {
3455 return self.fail("TODO implement codegen for non-expr asm", .{});
3456 }
3457 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3458 // This equation accounts for the fact that even if we have exactly 4 bytes
3459 // for the string, we still use the next u32 for the null terminator.
3460 extra_i += constraint.len / 4 + 1;
3461
3462 break constraint;
3463 } else null;
3464
3465 for (inputs) |input| {
3466 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3467 // This equation accounts for the fact that even if we have exactly 4 bytes
3468 // for the string, we still use the next u32 for the null terminator.
3469 extra_i += constraint.len / 4 + 1;
34733470
3474 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {3471 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3475 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});3472 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
...@@ -3478,11 +3475,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -3478,11 +3475,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3478 const reg = parseRegName(reg_name) orelse3475 const reg = parseRegName(reg_name) orelse
3479 return self.fail("unrecognized register: '{s}'", .{reg_name});3476 return self.fail("unrecognized register: '{s}'", .{reg_name});
34803477
3481 const arg_mcv = try self.resolveInst(arg);3478 const arg_mcv = try self.resolveInst(input);
3482 try self.register_manager.getReg(reg, null);3479 try self.register_manager.getReg(reg, null);
3483 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);3480 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
3481 }
3482
3483 {
3484 var clobber_i: u32 = 0;
3485 while (clobber_i < clobbers_len) : (clobber_i += 1) {
3486 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3487 // This equation accounts for the fact that even if we have exactly 4 bytes
3488 // for the string, we still use the next u32 for the null terminator.
3489 extra_i += clobber.len / 4 + 1;
3490
3491 // TODO honor these
3492 }
3484 }3493 }
34853494
3495 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
3496
3486 {3497 {
3487 var iter = std.mem.tokenize(u8, asm_source, "\n\r");3498 var iter = std.mem.tokenize(u8, asm_source, "\n\r");
3488 while (iter.next()) |ins| {3499 while (iter.next()) |ins| {
...@@ -3549,14 +3560,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -3549,14 +3560,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3549 break :result MCValue{ .none = {} };3560 break :result MCValue{ .none = {} };
3550 }3561 }
3551 };3562 };
3552 if (args.len <= Liveness.bpi - 1) {3563
3564 simple: {
3553 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);3565 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3554 std.mem.copy(Air.Inst.Ref, &buf, args);3566 var buf_index: usize = 0;
3567 for (outputs) |output| {
3568 if (output == .none) continue;
3569
3570 if (buf_index >= buf.len) break :simple;
3571 buf[buf_index] = output;
3572 buf_index += 1;
3573 }
3574 if (buf_index + inputs.len > buf.len) break :simple;
3575 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
3555 return self.finishAir(inst, result, buf);3576 return self.finishAir(inst, result, buf);
3556 }3577 }
3557 var bt = try self.iterateBigTomb(inst, args.len);3578 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
3558 for (args) |arg| {3579 for (outputs) |output| {
3559 bt.feed(arg);3580 if (output == .none) continue;
3581
3582 bt.feed(output);
3583 }
3584 for (inputs) |input| {
3585 bt.feed(input);
3560 }3586 }
3561 return bt.finishAir(result);3587 return bt.finishAir(result);
3562}3588}
...@@ -3635,7 +3661,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -3635,7 +3661,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
3635 const reg = try self.copyToTmpRegister(ty, mcv);3661 const reg = try self.copyToTmpRegister(ty, mcv);
3636 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });3662 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
3637 },3663 },
3638 else => return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}),3664 else => return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}),
3639 }3665 }
3640 },3666 },
3641 .embedded_in_code => {3667 .embedded_in_code => {
...@@ -3643,7 +3669,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -3643,7 +3669,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
3643 const reg = try self.copyToTmpRegister(ty, mcv);3669 const reg = try self.copyToTmpRegister(ty, mcv);
3644 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });3670 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
3645 }3671 }
3646 return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv});3672 return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv});
3647 },3673 },
3648 .memory,3674 .memory,
3649 .direct_load,3675 .direct_load,
src/arch/x86_64/Emit.zig+4-6
...@@ -946,15 +946,13 @@ fn mirArgDbgInfo(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -946,15 +946,13 @@ fn mirArgDbgInfo(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
946 const payload = emit.mir.instructions.items(.data)[inst].payload;946 const payload = emit.mir.instructions.items(.data)[inst].payload;
947 const arg_dbg_info = emit.mir.extraData(Mir.ArgDbgInfo, payload).data;947 const arg_dbg_info = emit.mir.extraData(Mir.ArgDbgInfo, payload).data;
948 const mcv = emit.mir.function.args[arg_dbg_info.arg_index];948 const mcv = emit.mir.function.args[arg_dbg_info.arg_index];
949 try emit.genArgDbgInfo(arg_dbg_info.air_inst, mcv, arg_dbg_info.max_stack);949 try emit.genArgDbgInfo(arg_dbg_info.air_inst, mcv, arg_dbg_info.max_stack, arg_dbg_info.arg_index);
950}950}
951951
952fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32) !void {952fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32, arg_index: u32) !void {
953 const ty_str = emit.mir.function.air.instructions.items(.data)[inst].ty_str;953 const ty = emit.mir.function.air.instructions.items(.data)[inst].ty;
954 const zir = &emit.mir.function.mod_fn.owner_decl.getFileScope().zir;954 const name = emit.mir.function.mod_fn.getParamName(arg_index);
955 const name = zir.nullTerminatedString(ty_str.str);
956 const name_with_null = name.ptr[0 .. name.len + 1];955 const name_with_null = name.ptr[0 .. name.len + 1];
957 const ty = emit.mir.function.air.getRefType(ty_str.ty);
958956
959 switch (mcv) {957 switch (mcv) {
960 .register => |reg| {958 .register => |reg| {
src/codegen/c.zig+56-41
...@@ -15,7 +15,6 @@ const Decl = Module.Decl;...@@ -15,7 +15,6 @@ const Decl = Module.Decl;
15const trace = @import("../tracy.zig").trace;15const trace = @import("../tracy.zig").trace;
16const LazySrcLoc = Module.LazySrcLoc;16const LazySrcLoc = Module.LazySrcLoc;
17const Air = @import("../Air.zig");17const Air = @import("../Air.zig");
18const Zir = @import("../Zir.zig");
19const Liveness = @import("../Liveness.zig");18const Liveness = @import("../Liveness.zig");
2019
21const Mutability = enum { Const, Mut };20const Mutability = enum { Const, Mut };
...@@ -2807,49 +2806,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2807,49 +2806,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
2807}2806}
28082807
2809fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {2808fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
2810 const air_datas = f.air.instructions.items(.data);2809 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
2811 const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);2810 const extra = f.air.extraData(Air.Asm, ty_pl.payload);
2812 const zir = f.object.dg.decl.getFileScope().zir;2811 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2813 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;2812 const clobbers_len = @truncate(u31, extra.data.flags);
2814 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);2813 var extra_i: usize = extra.end;
2815 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);2814 const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]);
2816 const outputs_len = @truncate(u5, extended.small);2815 extra_i += outputs.len;
2817 const args_len = @truncate(u5, extended.small >> 5);2816 const inputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]);
2818 const clobbers_len = @truncate(u5, extended.small >> 10);2817 extra_i += inputs.len;
2819 _ = clobbers_len; // TODO honor these2818
2820 const is_volatile = @truncate(u1, extended.small >> 15) != 0;2819 if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none;
2821 const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]);2820
2822 const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]);2821 if (outputs.len > 1) {
2823
2824 if (outputs_len > 1) {
2825 return f.fail("TODO implement codegen for asm with more than 1 output", .{});2822 return f.fail("TODO implement codegen for asm with more than 1 output", .{});
2826 }2823 }
28272824
2828 if (f.liveness.isUnused(inst) and !is_volatile)2825 const output_constraint: ?[]const u8 = for (outputs) |output| {
2829 return CValue.none;2826 if (output != .none) {
28302827 return f.fail("TODO implement codegen for non-expr asm", .{});
2831 var extra_i: usize = zir_extra.end;
2832 const output_constraint: ?[]const u8 = out: {
2833 var i: usize = 0;
2834 while (i < outputs_len) : (i += 1) {
2835 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
2836 extra_i = output.end;
2837 break :out zir.nullTerminatedString(output.data.constraint);
2838 }2828 }
2839 break :out null;2829 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2840 };2830 // This equation accounts for the fact that even if we have exactly 4 bytes
2841 const args_extra_begin = extra_i;2831 // for the string, we still use the next u32 for the null terminator.
2832 extra_i += constraint.len / 4 + 1;
2833
2834 break constraint;
2835 } else null;
28422836
2843 const writer = f.object.writer();2837 const writer = f.object.writer();
2844 for (args) |arg| {2838 const inputs_extra_begin = extra_i;
2845 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);2839
2846 extra_i = input.end;2840 for (inputs) |input| {
2847 const constraint = zir.nullTerminatedString(input.data.constraint);2841 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2842 // This equation accounts for the fact that even if we have exactly 4 bytes
2843 // for the string, we still use the next u32 for the null terminator.
2844 extra_i += constraint.len / 4 + 1;
2845
2848 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {2846 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
2849 const reg = constraint[1 .. constraint.len - 1];2847 const reg = constraint[1 .. constraint.len - 1];
2850 const arg_c_value = try f.resolveInst(arg);2848 const arg_c_value = try f.resolveInst(input);
2851 try writer.writeAll("register ");2849 try writer.writeAll("register ");
2852 try f.renderType(writer, f.air.typeOf(arg));2850 try f.renderType(writer, f.air.typeOf(input));
28532851
2854 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });2852 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
2855 try f.writeCValue(writer, arg_c_value);2853 try f.writeCValue(writer, arg_c_value);
...@@ -2858,21 +2856,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2858,21 +2856,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
2858 return f.fail("TODO non-explicit inline asm regs", .{});2856 return f.fail("TODO non-explicit inline asm regs", .{});
2859 }2857 }
2860 }2858 }
2859
2860 {
2861 var clobber_i: u32 = 0;
2862 while (clobber_i < clobbers_len) : (clobber_i += 1) {
2863 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2864 // This equation accounts for the fact that even if we have exactly 4 bytes
2865 // for the string, we still use the next u32 for the null terminator.
2866 extra_i += clobber.len / 4 + 1;
2867
2868 // TODO honor these
2869 }
2870 }
2871
2872 const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len];
2873
2861 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";2874 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";
2862 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });2875 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });
2863 if (output_constraint) |_| {2876 if (output_constraint) |_| {
2864 return f.fail("TODO: CBE inline asm output", .{});2877 return f.fail("TODO: CBE inline asm output", .{});
2865 }2878 }
2866 if (args.len > 0) {2879 if (inputs.len > 0) {
2867 if (output_constraint == null) {2880 if (output_constraint == null) {
2868 try writer.writeAll(" :");2881 try writer.writeAll(" :");
2869 }2882 }
2870 try writer.writeAll(": ");2883 try writer.writeAll(": ");
2871 extra_i = args_extra_begin;2884 extra_i = inputs_extra_begin;
2872 for (args) |_, index| {2885 for (inputs) |_, index| {
2873 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);2886 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2874 extra_i = input.end;2887 // This equation accounts for the fact that even if we have exactly 4 bytes
2875 const constraint = zir.nullTerminatedString(input.data.constraint);2888 // for the string, we still use the next u32 for the null terminator.
2889 extra_i += constraint.len / 4 + 1;
2890
2876 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {2891 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
2877 const reg = constraint[1 .. constraint.len - 1];2892 const reg = constraint[1 .. constraint.len - 1];
2878 if (index > 0) {2893 if (index > 0) {
src/codegen/llvm.zig+51-50
...@@ -2,24 +2,21 @@ const std = @import("std");...@@ -2,24 +2,21 @@ const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;4const Allocator = std.mem.Allocator;
5const Compilation = @import("../Compilation.zig");
6const llvm = @import("llvm/bindings.zig");
7const link = @import("../link.zig");
8const log = std.log.scoped(.codegen);5const log = std.log.scoped(.codegen);
9const math = std.math;6const math = std.math;
10const native_endian = builtin.cpu.arch.endian();7const native_endian = builtin.cpu.arch.endian();
118
9const llvm = @import("llvm/bindings.zig");
10const link = @import("../link.zig");
11const Compilation = @import("../Compilation.zig");
12const build_options = @import("build_options");12const build_options = @import("build_options");
13const Module = @import("../Module.zig");13const Module = @import("../Module.zig");
14const TypedValue = @import("../TypedValue.zig");14const TypedValue = @import("../TypedValue.zig");
15const Zir = @import("../Zir.zig");
16const Air = @import("../Air.zig");15const Air = @import("../Air.zig");
17const Liveness = @import("../Liveness.zig");16const Liveness = @import("../Liveness.zig");
18const target_util = @import("../target.zig");17const target_util = @import("../target.zig");
19
20const Value = @import("../value.zig").Value;18const Value = @import("../value.zig").Value;
21const Type = @import("../type.zig").Type;19const Type = @import("../type.zig").Type;
22
23const LazySrcLoc = Module.LazySrcLoc;20const LazySrcLoc = Module.LazySrcLoc;
2421
25const Error = error{ OutOfMemory, CodegenFail };22const Error = error{ OutOfMemory, CodegenFail };
...@@ -2895,33 +2892,21 @@ pub const FuncGen = struct {...@@ -2895,33 +2892,21 @@ pub const FuncGen = struct {
2895 // as stage1.2892 // as stage1.
28962893
2897 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2894 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2898 const air_asm = self.air.extraData(Air.Asm, ty_pl.payload);2895 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
2899 const zir = self.dg.decl.getFileScope().zir;2896 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2900 const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;2897 const clobbers_len = @truncate(u31, extra.data.flags);
2901 const is_volatile = @truncate(u1, extended.small >> 15) != 0;2898 var extra_i: usize = extra.end;
2902 if (!is_volatile and self.liveness.isUnused(inst)) {2899
2903 return null;2900 if (!is_volatile and self.liveness.isUnused(inst)) return null;
2904 }2901
2905 const outputs_len = @truncate(u5, extended.small);2902 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
2906 if (outputs_len > 1) {2903 extra_i += outputs.len;
2904 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
2905 extra_i += inputs.len;
2906
2907 if (outputs.len > 1) {
2907 return self.todo("implement llvm codegen for asm with more than 1 output", .{});2908 return self.todo("implement llvm codegen for asm with more than 1 output", .{});
2908 }2909 }
2909 const args_len = @truncate(u5, extended.small >> 5);
2910 const clobbers_len = @truncate(u5, extended.small >> 10);
2911 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
2912 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
2913 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end..][0..args_len]);
2914
2915 var extra_i: usize = zir_extra.end;
2916 const output_constraint: ?[]const u8 = out: {
2917 var i: usize = 0;
2918 while (i < outputs_len) : (i += 1) {
2919 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
2920 extra_i = output.end;
2921 break :out zir.nullTerminatedString(output.data.constraint);
2922 }
2923 break :out null;
2924 };
29252910
2926 var llvm_constraints: std.ArrayListUnmanaged(u8) = .{};2911 var llvm_constraints: std.ArrayListUnmanaged(u8) = .{};
2927 defer llvm_constraints.deinit(self.gpa);2912 defer llvm_constraints.deinit(self.gpa);
...@@ -2930,14 +2915,21 @@ pub const FuncGen = struct {...@@ -2930,14 +2915,21 @@ pub const FuncGen = struct {
2930 defer arena_allocator.deinit();2915 defer arena_allocator.deinit();
2931 const arena = arena_allocator.allocator();2916 const arena = arena_allocator.allocator();
29322917
2933 const llvm_params_len = args.len;2918 const llvm_params_len = inputs.len;
2934 const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);2919 const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);
2935 const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);2920 const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);
2936
2937 var llvm_param_i: usize = 0;2921 var llvm_param_i: usize = 0;
2938 var total_i: usize = 0;2922 var total_i: usize = 0;
29392923
2940 if (output_constraint) |constraint| {2924 for (outputs) |output| {
2925 if (output != .none) {
2926 return self.todo("implement inline asm with non-returned output", .{});
2927 }
2928 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2929 // This equation accounts for the fact that even if we have exactly 4 bytes
2930 // for the string, we still use the next u32 for the null terminator.
2931 extra_i += constraint.len / 4 + 1;
2932
2941 try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);2933 try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);
2942 if (total_i != 0) {2934 if (total_i != 0) {
2943 llvm_constraints.appendAssumeCapacity(',');2935 llvm_constraints.appendAssumeCapacity(',');
...@@ -2948,11 +2940,13 @@ pub const FuncGen = struct {...@@ -2948,11 +2940,13 @@ pub const FuncGen = struct {
2948 total_i += 1;2940 total_i += 1;
2949 }2941 }
29502942
2951 for (args) |arg| {2943 for (inputs) |input| {
2952 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);2944 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2953 extra_i = input.end;2945 // This equation accounts for the fact that even if we have exactly 4 bytes
2954 const constraint = zir.nullTerminatedString(input.data.constraint);2946 // for the string, we still use the next u32 for the null terminator.
2955 const arg_llvm_value = try self.resolveInst(arg);2947 extra_i += constraint.len / 4 + 1;
2948
2949 const arg_llvm_value = try self.resolveInst(input);
29562950
2957 llvm_param_values[llvm_param_i] = arg_llvm_value;2951 llvm_param_values[llvm_param_i] = arg_llvm_value;
2958 llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf();2952 llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf();
...@@ -2967,19 +2961,26 @@ pub const FuncGen = struct {...@@ -2967,19 +2961,26 @@ pub const FuncGen = struct {
2967 total_i += 1;2961 total_i += 1;
2968 }2962 }
29692963
2970 const clobbers = zir.extra[extra_i..][0..clobbers_len];2964 {
2971 for (clobbers) |clobber_index| {2965 var clobber_i: u32 = 0;
2972 const clobber = zir.nullTerminatedString(clobber_index);2966 while (clobber_i < clobbers_len) : (clobber_i += 1) {
2973 try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);2967 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2974 if (total_i != 0) {2968 // This equation accounts for the fact that even if we have exactly 4 bytes
2975 llvm_constraints.appendAssumeCapacity(',');2969 // for the string, we still use the next u32 for the null terminator.
2976 }2970 extra_i += clobber.len / 4 + 1;
2977 llvm_constraints.appendSliceAssumeCapacity("~{");2971
2978 llvm_constraints.appendSliceAssumeCapacity(clobber);2972 try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
2979 llvm_constraints.appendSliceAssumeCapacity("}");2973 if (total_i != 0) {
2974 llvm_constraints.appendAssumeCapacity(',');
2975 }
2976 llvm_constraints.appendSliceAssumeCapacity("~{");
2977 llvm_constraints.appendSliceAssumeCapacity(clobber);
2978 llvm_constraints.appendSliceAssumeCapacity("}");
29802979
2981 total_i += 1;2980 total_i += 1;
2981 }
2982 }2982 }
2983 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
29832984
2984 const ret_ty = self.air.typeOfIndex(inst);2985 const ret_ty = self.air.typeOfIndex(inst);
2985 const ret_llvm_ty = try self.dg.llvmType(ret_ty);2986 const ret_llvm_ty = try self.dg.llvmType(ret_ty);
src/print_air.zig+54-48
...@@ -4,11 +4,10 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin;...@@ -4,11 +4,10 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
44
5const Module = @import("Module.zig");5const Module = @import("Module.zig");
6const Value = @import("value.zig").Value;6const Value = @import("value.zig").Value;
7const Zir = @import("Zir.zig");
8const Air = @import("Air.zig");7const Air = @import("Air.zig");
9const Liveness = @import("Liveness.zig");8const Liveness = @import("Liveness.zig");
109
11pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void {10pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void {
12 const instruction_bytes = air.instructions.len *11 const instruction_bytes = air.instructions.len *
13 // Here we don't use @sizeOf(Air.Inst.Data) because it would include12 // Here we don't use @sizeOf(Air.Inst.Data) because it would include
14 // the debug safety tag but we want to measure release size.13 // the debug safety tag but we want to measure release size.
...@@ -49,7 +48,6 @@ pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void {...@@ -49,7 +48,6 @@ pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void {
49 .gpa = gpa,48 .gpa = gpa,
50 .arena = arena.allocator(),49 .arena = arena.allocator(),
51 .air = air,50 .air = air,
52 .zir = zir,
53 .liveness = liveness,51 .liveness = liveness,
54 .indent = 2,52 .indent = 2,
55 };53 };
...@@ -63,7 +61,6 @@ const Writer = struct {...@@ -63,7 +61,6 @@ const Writer = struct {
63 gpa: Allocator,61 gpa: Allocator,
64 arena: Allocator,62 arena: Allocator,
65 air: Air,63 air: Air,
66 zir: Zir,
67 liveness: Liveness,64 liveness: Liveness,
68 indent: usize,65 indent: usize,
6966
...@@ -100,8 +97,6 @@ const Writer = struct {...@@ -100,8 +97,6 @@ const Writer = struct {
100 const tag = tags[inst];97 const tag = tags[inst];
101 try s.print("= {s}(", .{@tagName(tags[inst])});98 try s.print("= {s}(", .{@tagName(tags[inst])});
102 switch (tag) {99 switch (tag) {
103 .arg => try w.writeTyStr(s, inst),
104
105 .add,100 .add,
106 .addwrap,101 .addwrap,
107 .add_sat,102 .add_sat,
...@@ -181,6 +176,7 @@ const Writer = struct {...@@ -181,6 +176,7 @@ const Writer = struct {
181 .const_ty,176 .const_ty,
182 .alloc,177 .alloc,
183 .ret_ptr,178 .ret_ptr,
179 .arg,
184 => try w.writeTy(s, inst),180 => try w.writeTy(s, inst),
185181
186 .not,182 .not,
...@@ -259,12 +255,6 @@ const Writer = struct {...@@ -259,12 +255,6 @@ const Writer = struct {
259 }255 }
260 }256 }
261257
262 fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
263 const ty_str = w.air.instructions.items(.data)[inst].ty_str;
264 const name = w.zir.nullTerminatedString(ty_str.str);
265 try s.print("\"{}\", {}", .{ std.zig.fmtEscapes(name), w.air.getRefType(ty_str.ty) });
266 }
267
268 fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {258 fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
269 const bin_op = w.air.instructions.items(.data)[inst].bin_op;259 const bin_op = w.air.instructions.items(.data)[inst].bin_op;
270 try w.writeOperand(s, inst, 0, bin_op.lhs);260 try w.writeOperand(s, inst, 0, bin_op.lhs);
...@@ -440,51 +430,67 @@ const Writer = struct {...@@ -440,51 +430,67 @@ const Writer = struct {
440430
441 fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {431 fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
442 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;432 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
443 const air_asm = w.air.extraData(Air.Asm, ty_pl.payload);433 const extra = w.air.extraData(Air.Asm, ty_pl.payload);
444 const zir = w.zir;434 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
445 const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;435 const clobbers_len = @truncate(u31, extra.data.flags);
446 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);436 var extra_i: usize = extra.end;
447 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);437 var op_index: usize = 0;
448 const outputs_len = @truncate(u5, extended.small);
449 const args_len = @truncate(u5, extended.small >> 5);
450 const clobbers_len = @truncate(u5, extended.small >> 10);
451 const args = @bitCast([]const Air.Inst.Ref, w.air.extra[air_asm.end..][0..args_len]);
452
453 var extra_i: usize = zir_extra.end;
454 const output_constraint: ?[]const u8 = out: {
455 var i: usize = 0;
456 while (i < outputs_len) : (i += 1) {
457 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
458 extra_i = output.end;
459 break :out zir.nullTerminatedString(output.data.constraint);
460 }
461 break :out null;
462 };
463438
464 try s.print("\"{s}\"", .{asm_source});439 const ret_ty = w.air.typeOfIndex(inst);
440 try s.print("{}", .{ret_ty});
465441
466 if (output_constraint) |constraint| {442 if (is_volatile) {
467 const ret_ty = w.air.typeOfIndex(inst);443 try s.writeAll(", volatile");
468 try s.print(", {s} -> {}", .{ constraint, ret_ty });
469 }444 }
470445
471 for (args) |arg| {446 const outputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.outputs_len]);
472 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);447 extra_i += outputs.len;
473 extra_i = input.end;448 const inputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.inputs_len]);
474 const constraint = zir.nullTerminatedString(input.data.constraint);449 extra_i += inputs.len;
450
451 for (outputs) |output| {
452 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
453 // This equation accounts for the fact that even if we have exactly 4 bytes
454 // for the string, we still use the next u32 for the null terminator.
455 extra_i += constraint.len / 4 + 1;
475456
476 try s.print(", {s} = (", .{constraint});457 if (output == .none) {
477 try w.writeOperand(s, inst, 0, arg);458 try s.print(", -> {s}", .{constraint});
459 } else {
460 try s.print(", out {s} = (", .{constraint});
461 try w.writeOperand(s, inst, op_index, output);
462 op_index += 1;
463 try s.writeByte(')');
464 }
465 }
466
467 for (inputs) |input| {
468 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
469 // This equation accounts for the fact that even if we have exactly 4 bytes
470 // for the string, we still use the next u32 for the null terminator.
471 extra_i += constraint.len / 4 + 1;
472
473 try s.print(", in {s} = (", .{constraint});
474 try w.writeOperand(s, inst, op_index, input);
475 op_index += 1;
478 try s.writeByte(')');476 try s.writeByte(')');
479 }477 }
480478
481 const clobbers = zir.extra[extra_i..][0..clobbers_len];479 {
482 for (clobbers) |clobber_index| {480 var clobber_i: u32 = 0;
483 const clobber = zir.nullTerminatedString(clobber_index);481 while (clobber_i < clobbers_len) : (clobber_i += 1) {
484 try s.writeAll(", ~{");482 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
485 try s.writeAll(clobber);483 // This equation accounts for the fact that even if we have exactly 4 bytes
486 try s.writeAll("}");484 // for the string, we still use the next u32 for the null terminator.
485 extra_i += clobber.len / 4 + 1;
486
487 try s.writeAll(", ~{");
488 try s.writeAll(clobber);
489 try s.writeAll("}");
490 }
487 }491 }
492 const asm_source = std.mem.sliceAsBytes(w.air.extra[extra_i..])[0..extra.data.source_len];
493 try s.print(", \"{s}\"", .{asm_source});
488 }494 }
489495
490 fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {496 fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {