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
signature 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
780780 pub fn allocatedSlice(self: Self) Slice {
781781 return self.items.ptr[0..self.capacity];
782782 }
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 }
783791 };
784792}
785793
src/Air.zig+20-13
......@@ -32,8 +32,7 @@ pub const Inst = struct {
3232 /// The first N instructions in the main block must be one arg instruction per
3333 /// function parameter. This makes function parameters participate in
3434 /// liveness analysis without any special handling.
35 /// Uses the `ty_str` field.
36 /// The string is the parameter name.
35 /// Uses the `ty` field.
3736 arg,
3837 /// Float or integer addition. For integers, wrapping is undefined behavior.
3938 /// Both operands are guaranteed to be the same type, and the result type
......@@ -621,11 +620,6 @@ pub const Inst = struct {
621620 // Index into a different array.
622621 payload: u32,
623622 },
624 ty_str: struct {
625 ty: Ref,
626 // ZIR string table index.
627 str: u32,
628 },
629623 br: struct {
630624 block_inst: Index,
631625 operand: Ref,
......@@ -709,11 +703,25 @@ pub const Bin = struct {
709703/// Trailing:
710704/// 0. `Inst.Ref` for every outputs_len
711705/// 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.
712717pub const Asm = struct {
713 /// Index to the corresponding ZIR instruction.
714 /// `asm_source`, `outputs_len`, `inputs_len`, `clobbers_len`, `is_volatile`, and
715 /// clobbers are found via here.
716 zir_index: u32,
718 /// Length of the assembly source in bytes.
719 source_len: u32,
720 outputs_len: u32,
721 inputs_len: u32,
722 /// The MSB is `is_volatile`.
723 /// The rest of the bits are `clobbers_len`.
724 flags: u32,
717725};
718726
719727pub const Cmpxchg = struct {
......@@ -765,8 +773,6 @@ pub fn typeOf(air: Air, inst: Air.Inst.Ref) Type {
765773pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
766774 const datas = air.instructions.items(.data);
767775 switch (air.instructions.items(.tag)[inst]) {
768 .arg => return air.getRefType(datas[inst].ty_str.ty),
769
770776 .add,
771777 .addwrap,
772778 .add_sat,
......@@ -833,6 +839,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
833839
834840 .alloc,
835841 .ret_ptr,
842 .arg,
836843 => return datas[inst].ty,
837844
838845 .assembly,
src/Compilation.zig+2-2
......@@ -2778,7 +2778,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress
27782778 errdefer if (!liveness_frame_ended) liveness_frame.end();
27792779
27802780 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);
27822782 defer liveness.deinit(gpa);
27832783
27842784 liveness_frame.end();
......@@ -2786,7 +2786,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress
27862786
27872787 if (builtin.mode == .Debug and comp.verbose_air) {
27882788 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);
27902790 std.debug.print("# End Function AIR: {s}\n\n", .{decl.name});
27912791 }
27922792
src/Liveness.zig+23-15
......@@ -12,7 +12,6 @@ const log = std.log.scoped(.liveness);
1212const assert = std.debug.assert;
1313const Allocator = std.mem.Allocator;
1414const Air = @import("Air.zig");
15const Zir = @import("Zir.zig");
1615const Log2Int = std.math.Log2Int;
1716
1817/// This array is split into sets of 4 bits per AIR instruction.
......@@ -52,7 +51,7 @@ pub const SwitchBr = struct {
5251 else_death_count: u32,
5352};
5453
55pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness {
54pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
5655 const tracy = trace(@src());
5756 defer tracy.end();
5857
......@@ -66,7 +65,6 @@ pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness {
6665 ),
6766 .extra = .{},
6867 .special = .{},
69 .zir = &zir,
7068 };
7169 errdefer gpa.free(a.tomb_bits);
7270 errdefer a.special.deinit(gpa);
......@@ -157,7 +155,6 @@ const Analysis = struct {
157155 tomb_bits: []usize,
158156 special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
159157 extra: std.ArrayListUnmanaged(u32),
160 zir: *const Zir,
161158
162159 fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void {
163160 const usize_index = (inst * bpi) / @bitSizeOf(usize);
......@@ -446,15 +443,24 @@ fn analyzeInst(
446443 },
447444 .assembly => {
448445 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;
450 const outputs_len = @truncate(u5, extended.small);
451 const inputs_len = @truncate(u5, extended.small >> 5);
452 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]);
453 const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]);
454 if (outputs.len + args.len <= bpi - 1) {
446 var extra_i: usize = extra.end;
447 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]);
448 extra_i += outputs.len;
449 const inputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]);
450 extra_i += inputs.len;
451
452 simple: {
455453 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
456 std.mem.copy(Air.Inst.Ref, &buf, outputs);
457 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
454 var buf_index: usize = 0;
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);
458464 return trackOperands(a, new_set, inst, main_tomb, buf);
459465 }
460466 var extra_tombs: ExtraTombs = .{
......@@ -464,10 +470,12 @@ fn analyzeInst(
464470 .main_tomb = main_tomb,
465471 };
466472 for (outputs) |output| {
467 try extra_tombs.feed(output);
473 if (output != .none) {
474 try extra_tombs.feed(output);
475 }
468476 }
469 for (args) |arg| {
470 try extra_tombs.feed(arg);
477 for (inputs) |input| {
478 try extra_tombs.feed(input);
471479 }
472480 return extra_tombs.finish();
473481 },
src/Module.zig+21-5
......@@ -1370,6 +1370,14 @@ pub const Fn = struct {
13701370 /// ZIR instruction.
13711371 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
13731381 /// Relative to owner Decl.
13741382 lbrace_line: u32,
13751383 /// Relative to owner Decl.
......@@ -1466,6 +1474,18 @@ pub const Fn = struct {
14661474 gpa.destroy(node);
14671475 it = next;
14681476 }
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];
14691489 }
14701490};
14711491
......@@ -4606,15 +4626,11 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
46064626 runtime_param_index += 1;
46074627 continue;
46084628 }
4609 const ty_ref = try sema.addType(param_type);
46104629 const arg_index = @intCast(u32, sema.air_instructions.len);
46114630 inner_block.instructions.appendAssumeCapacity(arg_index);
46124631 sema.air_instructions.appendAssumeCapacity(.{
46134632 .tag = .arg,
4614 .data = .{ .ty_str = .{
4615 .ty = ty_ref,
4616 .str = param.name,
4617 } },
4633 .data = .{ .ty = param_type },
46184634 });
46194635 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
46204636 total_param_index += 1;
src/Sema.zig+60-20
......@@ -134,6 +134,7 @@ pub const Block = struct {
134134 /// `noreturn` means `anytype`.
135135 ty: Type,
136136 is_comptime: bool,
137 name: []const u8,
137138 };
138139
139140 /// This `Block` maps a block ZIR instruction to the corresponding
......@@ -284,13 +285,10 @@ pub const Block = struct {
284285 });
285286 }
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 {
288289 return block.addInst(.{
289290 .tag = .arg,
290 .data = .{ .ty_str = .{
291 .ty = try block.sema.addType(ty),
292 .str = name,
293 } },
291 .data = .{ .ty = ty },
294292 });
295293 }
296294
......@@ -1126,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
11261124 .frame_address => return sema.zirFrameAddress( block, extended),
11271125 .alloc => return sema.zirAllocExtended( block, extended),
11281126 .builtin_extern => return sema.zirBuiltinExtern( block, extended),
1129 .@"asm" => return sema.zirAsm( block, extended, inst),
1127 .@"asm" => return sema.zirAsm( block, extended),
11301128 .typeof_peer => return sema.zirTypeofPeer( block, extended),
11311129 .compile_log => return sema.zirCompileLog( block, extended),
11321130 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
......@@ -4645,7 +4643,7 @@ fn analyzeCall(
46454643 } else {
46464644 // We insert into the map an instruction which is runtime-known
46474645 // 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);
46494647 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
46504648 }
46514649 }
......@@ -5712,6 +5710,11 @@ fn funcCommon(
57125710 break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr;
57135711 } 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
57155718 const fn_payload = try sema.arena.create(Value.Payload.Function);
57165719 new_func.* = .{
57175720 .state = anal_state,
......@@ -5722,6 +5725,7 @@ fn funcCommon(
57225725 .rbrace_line = src_locs.rbrace_line,
57235726 .lbrace_column = @truncate(u16, src_locs.columns),
57245727 .rbrace_column = @truncate(u16, src_locs.columns >> 16),
5728 .param_names = param_names,
57255729 };
57265730 if (maybe_inferred_error_set_node) |node| {
57275731 new_func.inferred_error_sets.prepend(node);
......@@ -5746,10 +5750,6 @@ fn zirParam(
57465750 const param_name = sema.code.nullTerminatedString(extra.data.name);
57475751 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
57535753 // We could be in a generic function instantiation, or we could be evaluating a generic
57545754 // function without any comptime args provided.
57555755 const param_ty = param_ty: {
......@@ -5776,6 +5776,7 @@ fn zirParam(
57765776 try block.params.append(sema.gpa, .{
57775777 .ty = Type.initTag(.generic_poison),
57785778 .is_comptime = comptime_syntax,
5779 .name = param_name,
57795780 });
57805781 try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison);
57815782 return;
......@@ -5801,6 +5802,7 @@ fn zirParam(
58015802 try block.params.append(sema.gpa, .{
58025803 .ty = param_ty,
58035804 .is_comptime = is_comptime,
5805 .name = param_name,
58045806 });
58055807 const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
58065808 try sema.inst_map.putNoClobber(sema.gpa, inst, result);
......@@ -5816,10 +5818,6 @@ fn zirParamAnytype(
58165818 const src = inst_data.src();
58175819 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
58235821 if (sema.inst_map.get(inst)) |air_ref| {
58245822 const param_ty = sema.typeOf(air_ref);
58255823 if (comptime_syntax or try sema.typeRequiresComptime(block, src, param_ty)) {
......@@ -5831,6 +5829,7 @@ fn zirParamAnytype(
58315829 try block.params.append(sema.gpa, .{
58325830 .ty = param_ty,
58335831 .is_comptime = false,
5832 .name = param_name,
58345833 });
58355834 return;
58365835 }
......@@ -5840,6 +5839,7 @@ fn zirParamAnytype(
58405839 try block.params.append(sema.gpa, .{
58415840 .ty = Type.initTag(.generic_poison),
58425841 .is_comptime = comptime_syntax,
5842 .name = param_name,
58435843 });
58445844 try sema.inst_map.put(sema.gpa, inst, .generic_poison);
58455845}
......@@ -9083,7 +9083,6 @@ fn zirAsm(
90839083 sema: *Sema,
90849084 block: *Block,
90859085 extended: Zir.Inst.Extended.InstData,
9086 inst: Zir.Inst.Index,
90879086) CompileError!Air.Inst.Ref {
90889087 const tracy = trace(@src());
90899088 defer tracy.end();
......@@ -9094,6 +9093,7 @@ fn zirAsm(
90949093 const outputs_len = @truncate(u5, extended.small);
90959094 const inputs_len = @truncate(u5, extended.small >> 5);
90969095 const clobbers_len = @truncate(u5, extended.small >> 10);
9096 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
90979097
90989098 if (extra.data.asm_source == 0) {
90999099 // This can move to become an AstGen error after inline assembly improvements land
......@@ -9107,6 +9107,7 @@ fn zirAsm(
91079107
91089108 var extra_i = extra.end;
91099109 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
91119112 const Output = struct { constraint: []const u8, ty: Type };
91129113 const output: ?Output = if (outputs_len == 0) null else blk: {
......@@ -9121,6 +9122,8 @@ fn zirAsm(
91219122 }
91229123
91239124 const constraint = sema.code.nullTerminatedString(output.data.constraint);
9125 needed_capacity += constraint.len / 4 + 1;
9126
91249127 break :blk Output{
91259128 .constraint = constraint,
91269129 .ty = try sema.resolveType(block, ret_ty_src, output.data.operand),
......@@ -9138,28 +9141,65 @@ fn zirAsm(
91389141 _ = name; // TODO: use the name
91399142
91409143 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;
91429147 }
91439148
91449149 const clobbers = try sema.arena.alloc([]const u8, clobbers_len);
91459150 for (clobbers) |*name| {
91469151 name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
91479152 extra_i += 1;
9153
9154 needed_capacity += name.*.len / 4 + 1;
91489155 }
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
91519160 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);
91539163 const asm_air = try block.addInst(.{
91549164 .tag = .assembly,
91559165 .data = .{ .ty_pl = .{
91569166 .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type,
91579167 .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),
91599172 }),
91609173 } },
91619174 });
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 }
91629179 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 }
91639203 return asm_air;
91649204}
91659205
src/arch/aarch64/CodeGen.zig+63-70
......@@ -4,7 +4,6 @@ const mem = std.mem;
44const math = std.math;
55const assert = std.debug.assert;
66const Air = @import("../../Air.zig");
7const Zir = @import("../../Zir.zig");
87const Mir = @import("Mir.zig");
98const Emit = @import("Emit.zig");
109const Liveness = @import("../../Liveness.zig");
......@@ -2021,36 +2020,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
20212020 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
20222021}
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
20542023fn airArg(self: *Self, inst: Air.Inst.Index) !void {
20552024 const arg_index = self.arg_index;
20562025 self.arg_index += 1;
......@@ -2866,40 +2835,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
28662835}
28672836
28682837fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2869 const air_datas = self.air.instructions.items(.data);
2870 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
2871 const zir = self.mod_fn.owner_decl.getFileScope().zir;
2872 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
2873 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
2874 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
2875 const outputs_len = @truncate(u5, extended.small);
2876 const args_len = @truncate(u5, extended.small >> 5);
2877 const clobbers_len = @truncate(u5, extended.small >> 10);
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 };
2838 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2839 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
2840 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2841 const clobbers_len = @truncate(u31, extra.data.flags);
2842 var extra_i: usize = extra.end;
2843 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
2844 extra_i += outputs.len;
2845 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
2846 extra_i += inputs.len;
28962847
28972848 const dead = !is_volatile and self.liveness.isUnused(inst);
28982849 const result: MCValue = if (dead) .dead else result: {
2899 for (args) |arg| {
2900 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
2901 extra_i = input.end;
2902 const constraint = zir.nullTerminatedString(input.data.constraint);
2850 if (outputs.len > 1) {
2851 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
2852 }
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
29042872 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
29052873 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
......@@ -2908,11 +2876,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
29082876 const reg = parseRegName(reg_name) orelse
29092877 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);
29122880 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 }
29142894 }
29152895
2896 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
2897
29162898 if (mem.eql(u8, asm_source, "svc #0")) {
29172899 _ = try self.addInst(.{
29182900 .tag = .svc,
......@@ -2939,18 +2921,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
29392921 break :result MCValue{ .none = {} };
29402922 }
29412923 };
2942 if (outputs.len + args.len <= Liveness.bpi - 1) {
2924
2925 simple: {
29432926 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2944 std.mem.copy(Air.Inst.Ref, &buf, outputs);
2945 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
2927 var buf_index: usize = 0;
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);
29462937 return self.finishAir(inst, result, buf);
29472938 }
2948 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
2939 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
29492940 for (outputs) |output| {
2941 if (output == .none) continue;
2942
29502943 bt.feed(output);
29512944 }
2952 for (args) |arg| {
2953 bt.feed(arg);
2945 for (inputs) |input| {
2946 bt.feed(input);
29542947 }
29552948 return bt.finishAir(result);
29562949}
src/arch/arm/CodeGen.zig+63-40
......@@ -4,7 +4,6 @@ const mem = std.mem;
44const math = std.math;
55const assert = std.debug.assert;
66const Air = @import("../../Air.zig");
7const Zir = @import("../../Zir.zig");
87const Mir = @import("Mir.zig");
98const Emit = @import("Emit.zig");
109const Liveness = @import("../../Liveness.zig");
......@@ -3075,40 +3074,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
30753074}
30763075
30773076fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3078 const air_datas = self.air.instructions.items(.data);
3079 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
3080 const zir = self.mod_fn.owner_decl.getFileScope().zir;
3081 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
3082 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
3083 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
3084 const outputs_len = @truncate(u5, extended.small);
3085 const args_len = @truncate(u5, extended.small >> 5);
3086 const clobbers_len = @truncate(u5, extended.small >> 10);
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 };
3077 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3078 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
3079 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
3080 const clobbers_len = @truncate(u31, extra.data.flags);
3081 var extra_i: usize = extra.end;
3082 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
3083 extra_i += outputs.len;
3084 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
3085 extra_i += inputs.len;
31053086
31063087 const dead = !is_volatile and self.liveness.isUnused(inst);
31073088 const result: MCValue = if (dead) .dead else result: {
3108 for (args) |arg| {
3109 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3110 extra_i = input.end;
3111 const constraint = zir.nullTerminatedString(input.data.constraint);
3089 if (outputs.len > 1) {
3090 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3091 }
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
31133111 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
31143112 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
......@@ -3117,11 +3115,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
31173115 const reg = parseRegName(reg_name) orelse
31183116 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);
31213119 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 }
31233133 }
31243134
3135 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
3136
31253137 if (mem.eql(u8, asm_source, "svc #0")) {
31263138 _ = try self.addInst(.{
31273139 .tag = .svc,
......@@ -3144,18 +3156,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
31443156 break :result MCValue{ .none = {} };
31453157 }
31463158 };
3147 if (outputs.len + args.len <= Liveness.bpi - 1) {
3159
3160 simple: {
31483161 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3149 std.mem.copy(Air.Inst.Ref, &buf, outputs);
3150 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
3162 var buf_index: usize = 0;
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);
31513172 return self.finishAir(inst, result, buf);
31523173 }
3153 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
3174 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
31543175 for (outputs) |output| {
3176 if (output == .none) continue;
3177
31553178 bt.feed(output);
31563179 }
3157 for (args) |arg| {
3158 bt.feed(arg);
3180 for (inputs) |input| {
3181 bt.feed(input);
31593182 }
31603183 return bt.finishAir(result);
31613184}
src/arch/arm/Emit.zig+2-4
......@@ -393,11 +393,9 @@ fn addDbgInfoTypeReloc(self: *Emit, ty: Type) !void {
393393fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void {
394394 const mcv = self.function.args[arg_index];
395395
396 const ty_str = self.function.air.instructions.items(.data)[inst].ty_str;
397 const zir = &self.function.mod_fn.owner_decl.getFileScope().zir;
398 const name = zir.nullTerminatedString(ty_str.str);
396 const ty = self.function.air.instructions.items(.data)[inst].ty;
397 const name = self.function.mod_fn.getParamName(arg_index);
399398 const name_with_null = name.ptr[0 .. name.len + 1];
400 const ty = self.function.air.getRefType(ty_str.ty);
401399
402400 switch (mcv) {
403401 .register => |reg| {
src/arch/riscv64/CodeGen.zig+66-46
......@@ -4,7 +4,6 @@ const mem = std.mem;
44const math = std.math;
55const assert = std.debug.assert;
66const Air = @import("../../Air.zig");
7const Zir = @import("../../Zir.zig");
87const Mir = @import("Mir.zig");
98const Emit = @import("Emit.zig");
109const Liveness = @import("../../Liveness.zig");
......@@ -1354,12 +1353,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
13541353 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
13551354}
13561355
1357fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
1358 const ty_str = self.air.instructions.items(.data)[inst].ty_str;
1359 const zir = &self.mod_fn.owner_decl.getFileScope().zir;
1360 const name = zir.nullTerminatedString(ty_str.str);
1356fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void {
1357 const ty = self.air.instructions.items(.data)[inst].ty;
1358 const name = self.mod_fn.getParamName(arg_index);
13611359 const name_with_null = name.ptr[0 .. name.len + 1];
1362 const ty = self.air.getRefType(ty_str.ty);
13631360
13641361 switch (mcv) {
13651362 .register => |reg| {
......@@ -1402,7 +1399,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
14021399 // TODO support stack-only arguments
14031400 // TODO Copy registers to the stack
14041401 const mcv = result;
1405 try self.genArgDbgInfo(inst, mcv);
1402 try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index));
14061403
14071404 if (self.liveness.isUnused(inst))
14081405 return self.finishAirBookkeeping();
......@@ -1838,40 +1835,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
18381835}
18391836
18401837fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1841 const air_datas = self.air.instructions.items(.data);
1842 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
1843 const zir = self.mod_fn.owner_decl.getFileScope().zir;
1844 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
1845 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
1846 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
1847 const outputs_len = @truncate(u5, extended.small);
1848 const args_len = @truncate(u5, extended.small >> 5);
1849 const clobbers_len = @truncate(u5, extended.small >> 10);
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 };
1838 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1839 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
1840 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
1841 const clobbers_len = @truncate(u31, extra.data.flags);
1842 var extra_i: usize = extra.end;
1843 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
1844 extra_i += outputs.len;
1845 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
1846 extra_i += inputs.len;
18681847
18691848 const dead = !is_volatile and self.liveness.isUnused(inst);
18701849 const result: MCValue = if (dead) .dead else result: {
1871 for (args) |arg| {
1872 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
1873 extra_i = input.end;
1874 const constraint = zir.nullTerminatedString(input.data.constraint);
1850 if (outputs.len > 1) {
1851 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
1852 }
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
18761872 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
18771873 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
......@@ -1880,11 +1876,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
18801876 const reg = parseRegName(reg_name) orelse
18811877 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);
18841880 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);
18861882 }
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
18881898 if (mem.eql(u8, asm_source, "ecall")) {
18891899 _ = try self.addInst(.{
18901900 .tag = .ecall,
......@@ -1906,18 +1916,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
19061916 break :result MCValue{ .none = {} };
19071917 }
19081918 };
1909 if (outputs.len + args.len <= Liveness.bpi - 1) {
1919 simple: {
19101920 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
1911 std.mem.copy(Air.Inst.Ref, &buf, outputs);
1912 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
1921 var buf_index: usize = 0;
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);
19131931 return self.finishAir(inst, result, buf);
19141932 }
1915 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
1933 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
19161934 for (outputs) |output| {
1935 if (output == .none) continue;
1936
19171937 bt.feed(output);
19181938 }
1919 for (args) |arg| {
1920 bt.feed(arg);
1939 for (inputs) |input| {
1940 bt.feed(input);
19211941 }
19221942 return bt.finishAir(result);
19231943}
src/arch/x86_64/CodeGen.zig+69-43
......@@ -26,7 +26,6 @@ const Target = std.Target;
2626const Type = @import("../../type.zig").Type;
2727const TypedValue = @import("../../TypedValue.zig");
2828const Value = @import("../../value.zig").Value;
29const Zir = @import("../../Zir.zig");
3029
3130const InnerError = error{
3231 OutOfMemory,
......@@ -3435,41 +3434,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
34353434}
34363435
34373436fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3438 const air_datas = self.air.instructions.items(.data);
3439 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
3440 const zir = self.mod_fn.owner_decl.getFileScope().zir;
3441 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
3442 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
3443 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
3444 const outputs_len = @truncate(u5, extended.small);
3445 const args_len = @truncate(u5, extended.small >> 5);
3446 const clobbers_len = @truncate(u5, extended.small >> 10);
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 };
3437 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3438 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
3439 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
3440 const clobbers_len = @truncate(u31, extra.data.flags);
3441 var extra_i: usize = extra.end;
3442 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
3443 extra_i += outputs.len;
3444 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
3445 extra_i += inputs.len;
34643446
34653447 const dead = !is_volatile and self.liveness.isUnused(inst);
3466 const result: MCValue = if (dead)
3467 .dead
3468 else result: {
3469 for (args) |arg| {
3470 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3471 extra_i = input.end;
3472 const constraint = zir.nullTerminatedString(input.data.constraint);
3448 const result: MCValue = if (dead) .dead else result: {
3449 if (outputs.len > 1) {
3450 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3451 }
3452
3453 const output_constraint: ?[]const u8 = for (outputs) |output| {
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
34743471 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
34753472 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
......@@ -3478,11 +3475,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
34783475 const reg = parseRegName(reg_name) orelse
34793476 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);
34823479 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 }
34843493 }
34853494
3495 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
3496
34863497 {
34873498 var iter = std.mem.tokenize(u8, asm_source, "\n\r");
34883499 while (iter.next()) |ins| {
......@@ -3549,14 +3560,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
35493560 break :result MCValue{ .none = {} };
35503561 }
35513562 };
3552 if (args.len <= Liveness.bpi - 1) {
3563
3564 simple: {
35533565 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);
35553576 return self.finishAir(inst, result, buf);
35563577 }
3557 var bt = try self.iterateBigTomb(inst, args.len);
3558 for (args) |arg| {
3559 bt.feed(arg);
3578 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
3579 for (outputs) |output| {
3580 if (output == .none) continue;
3581
3582 bt.feed(output);
3583 }
3584 for (inputs) |input| {
3585 bt.feed(input);
35603586 }
35613587 return bt.finishAir(result);
35623588}
......@@ -3635,7 +3661,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
36353661 const reg = try self.copyToTmpRegister(ty, mcv);
36363662 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
36373663 },
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}),
36393665 }
36403666 },
36413667 .embedded_in_code => {
......@@ -3643,7 +3669,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
36433669 const reg = try self.copyToTmpRegister(ty, mcv);
36443670 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
36453671 }
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});
36473673 },
36483674 .memory,
36493675 .direct_load,
src/arch/x86_64/Emit.zig+4-6
......@@ -946,15 +946,13 @@ fn mirArgDbgInfo(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
946946 const payload = emit.mir.instructions.items(.data)[inst].payload;
947947 const arg_dbg_info = emit.mir.extraData(Mir.ArgDbgInfo, payload).data;
948948 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);
950950}
951951
952fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32) !void {
953 const ty_str = emit.mir.function.air.instructions.items(.data)[inst].ty_str;
954 const zir = &emit.mir.function.mod_fn.owner_decl.getFileScope().zir;
955 const name = zir.nullTerminatedString(ty_str.str);
952fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32, arg_index: u32) !void {
953 const ty = emit.mir.function.air.instructions.items(.data)[inst].ty;
954 const name = emit.mir.function.mod_fn.getParamName(arg_index);
956955 const name_with_null = name.ptr[0 .. name.len + 1];
957 const ty = emit.mir.function.air.getRefType(ty_str.ty);
958956
959957 switch (mcv) {
960958 .register => |reg| {
src/codegen/c.zig+56-41
......@@ -15,7 +15,6 @@ const Decl = Module.Decl;
1515const trace = @import("../tracy.zig").trace;
1616const LazySrcLoc = Module.LazySrcLoc;
1717const Air = @import("../Air.zig");
18const Zir = @import("../Zir.zig");
1918const Liveness = @import("../Liveness.zig");
2019
2120const Mutability = enum { Const, Mut };
......@@ -2807,49 +2806,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
28072806}
28082807
28092808fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
2810 const air_datas = f.air.instructions.items(.data);
2811 const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
2812 const zir = f.object.dg.decl.getFileScope().zir;
2813 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
2814 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
2815 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
2816 const outputs_len = @truncate(u5, extended.small);
2817 const args_len = @truncate(u5, extended.small >> 5);
2818 const clobbers_len = @truncate(u5, extended.small >> 10);
2819 _ = clobbers_len; // TODO honor these
2820 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
2821 const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]);
2822 const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]);
2823
2824 if (outputs_len > 1) {
2809 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
2810 const extra = f.air.extraData(Air.Asm, ty_pl.payload);
2811 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2812 const clobbers_len = @truncate(u31, extra.data.flags);
2813 var extra_i: usize = extra.end;
2814 const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]);
2815 extra_i += outputs.len;
2816 const inputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]);
2817 extra_i += inputs.len;
2818
2819 if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none;
2820
2821 if (outputs.len > 1) {
28252822 return f.fail("TODO implement codegen for asm with more than 1 output", .{});
28262823 }
28272824
2828 if (f.liveness.isUnused(inst) and !is_volatile)
2829 return CValue.none;
2830
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);
2825 const output_constraint: ?[]const u8 = for (outputs) |output| {
2826 if (output != .none) {
2827 return f.fail("TODO implement codegen for non-expr asm", .{});
28382828 }
2839 break :out null;
2840 };
2841 const args_extra_begin = extra_i;
2829 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2830 // This equation accounts for the fact that even if we have exactly 4 bytes
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
28432837 const writer = f.object.writer();
2844 for (args) |arg| {
2845 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
2846 extra_i = input.end;
2847 const constraint = zir.nullTerminatedString(input.data.constraint);
2838 const inputs_extra_begin = extra_i;
2839
2840 for (inputs) |input| {
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
28482846 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
28492847 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);
28512849 try writer.writeAll("register ");
2852 try f.renderType(writer, f.air.typeOf(arg));
2850 try f.renderType(writer, f.air.typeOf(input));
28532851
28542852 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
28552853 try f.writeCValue(writer, arg_c_value);
......@@ -2858,21 +2856,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
28582856 return f.fail("TODO non-explicit inline asm regs", .{});
28592857 }
28602858 }
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
28612874 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";
28622875 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });
28632876 if (output_constraint) |_| {
28642877 return f.fail("TODO: CBE inline asm output", .{});
28652878 }
2866 if (args.len > 0) {
2879 if (inputs.len > 0) {
28672880 if (output_constraint == null) {
28682881 try writer.writeAll(" :");
28692882 }
28702883 try writer.writeAll(": ");
2871 extra_i = args_extra_begin;
2872 for (args) |_, index| {
2873 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
2874 extra_i = input.end;
2875 const constraint = zir.nullTerminatedString(input.data.constraint);
2884 extra_i = inputs_extra_begin;
2885 for (inputs) |_, index| {
2886 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2887 // This equation accounts for the fact that even if we have exactly 4 bytes
2888 // for the string, we still use the next u32 for the null terminator.
2889 extra_i += constraint.len / 4 + 1;
2890
28762891 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
28772892 const reg = constraint[1 .. constraint.len - 1];
28782893 if (index > 0) {
src/codegen/llvm.zig+51-50
......@@ -2,24 +2,21 @@ const std = @import("std");
22const builtin = @import("builtin");
33const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
5const Compilation = @import("../Compilation.zig");
6const llvm = @import("llvm/bindings.zig");
7const link = @import("../link.zig");
85const log = std.log.scoped(.codegen);
96const math = std.math;
107const native_endian = builtin.cpu.arch.endian();
118
9const llvm = @import("llvm/bindings.zig");
10const link = @import("../link.zig");
11const Compilation = @import("../Compilation.zig");
1212const build_options = @import("build_options");
1313const Module = @import("../Module.zig");
1414const TypedValue = @import("../TypedValue.zig");
15const Zir = @import("../Zir.zig");
1615const Air = @import("../Air.zig");
1716const Liveness = @import("../Liveness.zig");
1817const target_util = @import("../target.zig");
19
2018const Value = @import("../value.zig").Value;
2119const Type = @import("../type.zig").Type;
22
2320const LazySrcLoc = Module.LazySrcLoc;
2421
2522const Error = error{ OutOfMemory, CodegenFail };
......@@ -2895,33 +2892,21 @@ pub const FuncGen = struct {
28952892 // as stage1.
28962893
28972894 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2898 const air_asm = self.air.extraData(Air.Asm, ty_pl.payload);
2899 const zir = self.dg.decl.getFileScope().zir;
2900 const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;
2901 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
2902 if (!is_volatile and self.liveness.isUnused(inst)) {
2903 return null;
2904 }
2905 const outputs_len = @truncate(u5, extended.small);
2906 if (outputs_len > 1) {
2895 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
2896 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2897 const clobbers_len = @truncate(u31, extra.data.flags);
2898 var extra_i: usize = extra.end;
2899
2900 if (!is_volatile and self.liveness.isUnused(inst)) return null;
2901
2902 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
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) {
29072908 return self.todo("implement llvm codegen for asm with more than 1 output", .{});
29082909 }
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
29262911 var llvm_constraints: std.ArrayListUnmanaged(u8) = .{};
29272912 defer llvm_constraints.deinit(self.gpa);
......@@ -2930,14 +2915,21 @@ pub const FuncGen = struct {
29302915 defer arena_allocator.deinit();
29312916 const arena = arena_allocator.allocator();
29322917
2933 const llvm_params_len = args.len;
2918 const llvm_params_len = inputs.len;
29342919 const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);
29352920 const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);
2936
29372921 var llvm_param_i: usize = 0;
29382922 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
29412933 try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);
29422934 if (total_i != 0) {
29432935 llvm_constraints.appendAssumeCapacity(',');
......@@ -2948,11 +2940,13 @@ pub const FuncGen = struct {
29482940 total_i += 1;
29492941 }
29502942
2951 for (args) |arg| {
2952 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
2953 extra_i = input.end;
2954 const constraint = zir.nullTerminatedString(input.data.constraint);
2955 const arg_llvm_value = try self.resolveInst(arg);
2943 for (inputs) |input| {
2944 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2945 // This equation accounts for the fact that even if we have exactly 4 bytes
2946 // for the string, we still use the next u32 for the null terminator.
2947 extra_i += constraint.len / 4 + 1;
2948
2949 const arg_llvm_value = try self.resolveInst(input);
29562950
29572951 llvm_param_values[llvm_param_i] = arg_llvm_value;
29582952 llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf();
......@@ -2967,19 +2961,26 @@ pub const FuncGen = struct {
29672961 total_i += 1;
29682962 }
29692963
2970 const clobbers = zir.extra[extra_i..][0..clobbers_len];
2971 for (clobbers) |clobber_index| {
2972 const clobber = zir.nullTerminatedString(clobber_index);
2973 try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
2974 if (total_i != 0) {
2975 llvm_constraints.appendAssumeCapacity(',');
2976 }
2977 llvm_constraints.appendSliceAssumeCapacity("~{");
2978 llvm_constraints.appendSliceAssumeCapacity(clobber);
2979 llvm_constraints.appendSliceAssumeCapacity("}");
2964 {
2965 var clobber_i: u32 = 0;
2966 while (clobber_i < clobbers_len) : (clobber_i += 1) {
2967 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2968 // This equation accounts for the fact that even if we have exactly 4 bytes
2969 // for the string, we still use the next u32 for the null terminator.
2970 extra_i += clobber.len / 4 + 1;
2971
2972 try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
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 }
29822982 }
2983 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
29832984
29842985 const ret_ty = self.air.typeOfIndex(inst);
29852986 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;
44
55const Module = @import("Module.zig");
66const Value = @import("value.zig").Value;
7const Zir = @import("Zir.zig");
87const Air = @import("Air.zig");
98const 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 {
1211 const instruction_bytes = air.instructions.len *
1312 // Here we don't use @sizeOf(Air.Inst.Data) because it would include
1413 // 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 {
4948 .gpa = gpa,
5049 .arena = arena.allocator(),
5150 .air = air,
52 .zir = zir,
5351 .liveness = liveness,
5452 .indent = 2,
5553 };
......@@ -63,7 +61,6 @@ const Writer = struct {
6361 gpa: Allocator,
6462 arena: Allocator,
6563 air: Air,
66 zir: Zir,
6764 liveness: Liveness,
6865 indent: usize,
6966
......@@ -100,8 +97,6 @@ const Writer = struct {
10097 const tag = tags[inst];
10198 try s.print("= {s}(", .{@tagName(tags[inst])});
10299 switch (tag) {
103 .arg => try w.writeTyStr(s, inst),
104
105100 .add,
106101 .addwrap,
107102 .add_sat,
......@@ -181,6 +176,7 @@ const Writer = struct {
181176 .const_ty,
182177 .alloc,
183178 .ret_ptr,
179 .arg,
184180 => try w.writeTy(s, inst),
185181
186182 .not,
......@@ -259,12 +255,6 @@ const Writer = struct {
259255 }
260256 }
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
268258 fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
269259 const bin_op = w.air.instructions.items(.data)[inst].bin_op;
270260 try w.writeOperand(s, inst, 0, bin_op.lhs);
......@@ -440,51 +430,67 @@ const Writer = struct {
440430
441431 fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
442432 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
443 const air_asm = w.air.extraData(Air.Asm, ty_pl.payload);
444 const zir = w.zir;
445 const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;
446 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
447 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
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 };
433 const extra = w.air.extraData(Air.Asm, ty_pl.payload);
434 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
435 const clobbers_len = @truncate(u31, extra.data.flags);
436 var extra_i: usize = extra.end;
437 var op_index: usize = 0;
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| {
467 const ret_ty = w.air.typeOfIndex(inst);
468 try s.print(", {s} -> {}", .{ constraint, ret_ty });
442 if (is_volatile) {
443 try s.writeAll(", volatile");
469444 }
470445
471 for (args) |arg| {
472 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
473 extra_i = input.end;
474 const constraint = zir.nullTerminatedString(input.data.constraint);
446 const outputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.outputs_len]);
447 extra_i += outputs.len;
448 const inputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.inputs_len]);
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});
477 try w.writeOperand(s, inst, 0, arg);
457 if (output == .none) {
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;
478476 try s.writeByte(')');
479477 }
480478
481 const clobbers = zir.extra[extra_i..][0..clobbers_len];
482 for (clobbers) |clobber_index| {
483 const clobber = zir.nullTerminatedString(clobber_index);
484 try s.writeAll(", ~{");
485 try s.writeAll(clobber);
486 try s.writeAll("}");
479 {
480 var clobber_i: u32 = 0;
481 while (clobber_i < clobbers_len) : (clobber_i += 1) {
482 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
483 // This equation accounts for the fact that even if we have exactly 4 bytes
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 }
487491 }
492 const asm_source = std.mem.sliceAsBytes(w.air.extra[extra_i..])[0..extra.data.source_len];
493 try s.print(", \"{s}\"", .{asm_source});
488494 }
489495
490496 fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {