authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-18 19:41:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-18 19:41:32-07:00
log4e1e5ab6221b72ef2be9f1fb40c2e6d1235718fe
tree2210dfbf4e6d445b6f3e6cdba4e6d18e5a41cbde
parent123076ea88a809888692b308ab5bb214dc3a2caf

stage2: make AIR not reference ZIR for inline assembly

Instead it stores all the information it needs to into AIR. closes #10784

11 files changed, 506 insertions(+), 351 deletions(-)

src/Air.zig+18-4
...@@ -697,11 +697,25 @@ pub const Bin = struct {...@@ -697,11 +697,25 @@ pub const Bin = struct {
697/// Trailing:697/// Trailing:
698/// 0. `Inst.Ref` for every outputs_len698/// 0. `Inst.Ref` for every outputs_len
699/// 1. `Inst.Ref` for every inputs_len699/// 1. `Inst.Ref` for every inputs_len
700/// 2. for every outputs_len
701/// - constraint: memory at this position is reinterpreted as a null
702/// terminated string. pad to the next u32 after the null byte.
703/// 3. for every inputs_len
704/// - constraint: memory at this position is reinterpreted as a null
705/// terminated string. pad to the next u32 after the null byte.
706/// 4. for every clobbers_len
707/// - clobber_name: memory at this position is reinterpreted as a null
708/// terminated string. pad to the next u32 after the null byte.
709/// 5. A number of u32 elements follow according to the equation `(source_len + 3) / 4`.
710/// Memory starting at this position is reinterpreted as the source bytes.
700pub const Asm = struct {711pub const Asm = struct {
701 /// Index to the corresponding ZIR instruction.712 /// Length of the assembly source in bytes.
702 /// `asm_source`, `outputs_len`, `inputs_len`, `clobbers_len`, `is_volatile`, and713 source_len: u32,
703 /// clobbers are found via here.714 outputs_len: u32,
704 zir_index: u32,715 inputs_len: u32,
716 /// The MSB is `is_volatile`.
717 /// The rest of the bits are `clobbers_len`.
718 flags: u32,
705};719};
706720
707pub const Cmpxchg = struct {721pub const Cmpxchg = struct {
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);
...@@ -444,15 +441,24 @@ fn analyzeInst(...@@ -444,15 +441,24 @@ fn analyzeInst(
444 },441 },
445 .assembly => {442 .assembly => {
446 const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);443 const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);
447 const extended = a.zir.instructions.items(.data)[extra.data.zir_index].extended;444 var extra_i: usize = extra.end;
448 const outputs_len = @truncate(u5, extended.small);445 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]);
449 const inputs_len = @truncate(u5, extended.small >> 5);446 extra_i += outputs.len;
450 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]);447 const inputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]);
451 const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]);448 extra_i += inputs.len;
452 if (outputs.len + args.len <= bpi - 1) {449
450 simple: {
453 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);451 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
454 std.mem.copy(Air.Inst.Ref, &buf, outputs);452 var buf_index: usize = 0;
455 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);453 for (outputs) |output| {
454 if (output != .none) {
455 if (buf_index >= buf.len) break :simple;
456 buf[buf_index] = output;
457 buf_index += 1;
458 }
459 }
460 if (buf_index + inputs.len > buf.len) break :simple;
461 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
456 return trackOperands(a, new_set, inst, main_tomb, buf);462 return trackOperands(a, new_set, inst, main_tomb, buf);
457 }463 }
458 var extra_tombs: ExtraTombs = .{464 var extra_tombs: ExtraTombs = .{
...@@ -462,10 +468,12 @@ fn analyzeInst(...@@ -462,10 +468,12 @@ fn analyzeInst(
462 .main_tomb = main_tomb,468 .main_tomb = main_tomb,
463 };469 };
464 for (outputs) |output| {470 for (outputs) |output| {
465 try extra_tombs.feed(output);471 if (output != .none) {
472 try extra_tombs.feed(output);
473 }
466 }474 }
467 for (args) |arg| {475 for (inputs) |input| {
468 try extra_tombs.feed(arg);476 try extra_tombs.feed(input);
469 }477 }
470 return extra_tombs.finish();478 return extra_tombs.finish();
471 },479 },
src/Sema.zig+46-6
...@@ -1124,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -1124,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1124 .frame_address => return sema.zirFrameAddress( block, extended),1124 .frame_address => return sema.zirFrameAddress( block, extended),
1125 .alloc => return sema.zirAllocExtended( block, extended),1125 .alloc => return sema.zirAllocExtended( block, extended),
1126 .builtin_extern => return sema.zirBuiltinExtern( block, extended),1126 .builtin_extern => return sema.zirBuiltinExtern( block, extended),
1127 .@"asm" => return sema.zirAsm( block, extended, inst),1127 .@"asm" => return sema.zirAsm( block, extended),
1128 .typeof_peer => return sema.zirTypeofPeer( block, extended),1128 .typeof_peer => return sema.zirTypeofPeer( block, extended),
1129 .compile_log => return sema.zirCompileLog( block, extended),1129 .compile_log => return sema.zirCompileLog( block, extended),
1130 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),1130 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
...@@ -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");
...@@ -2007,36 +2006,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2007,36 +2006,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2007 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });2006 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
2008}2007}
20092008
2010fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
2011 const ty_str = self.air.instructions.items(.data)[inst].ty_str;
2012 const zir = &self.mod_fn.owner_decl.getFileScope().zir;
2013 const name = zir.nullTerminatedString(ty_str.str);
2014 const name_with_null = name.ptr[0 .. name.len + 1];
2015 const ty = self.air.getRefType(ty_str.ty);
2016
2017 switch (mcv) {
2018 .register => |reg| {
2019 switch (self.debug_output) {
2020 .dwarf => |dbg_out| {
2021 try dbg_out.dbg_info.ensureUnusedCapacity(3);
2022 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
2023 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
2024 1, // ULEB128 dwarf expression length
2025 reg.dwarfLocOp(),
2026 });
2027 try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
2028 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
2029 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
2030 },
2031 .plan9 => {},
2032 .none => {},
2033 }
2034 },
2035 .stack_offset => {},
2036 else => {},
2037 }
2038}
2039
2040fn airArg(self: *Self, inst: Air.Inst.Index) !void {2009fn airArg(self: *Self, inst: Air.Inst.Index) !void {
2041 const arg_index = self.arg_index;2010 const arg_index = self.arg_index;
2042 self.arg_index += 1;2011 self.arg_index += 1;
...@@ -2852,40 +2821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -2852,40 +2821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
2852}2821}
28532822
2854fn airAsm(self: *Self, inst: Air.Inst.Index) !void {2823fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2855 const air_datas = self.air.instructions.items(.data);2824 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2856 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);2825 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
2857 const zir = self.mod_fn.owner_decl.getFileScope().zir;2826 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2858 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;2827 const clobbers_len = @truncate(u31, extra.data.flags);
2859 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);2828 var extra_i: usize = extra.end;
2860 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);2829 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
2861 const outputs_len = @truncate(u5, extended.small);2830 extra_i += outputs.len;
2862 const args_len = @truncate(u5, extended.small >> 5);2831 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
2863 const clobbers_len = @truncate(u5, extended.small >> 10);2832 extra_i += inputs.len;
2864 _ = clobbers_len; // TODO honor these
2865 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
2866 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
2867 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
2868
2869 if (outputs_len > 1) {
2870 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
2871 }
2872 var extra_i: usize = zir_extra.end;
2873 const output_constraint: ?[]const u8 = out: {
2874 var i: usize = 0;
2875 while (i < outputs_len) : (i += 1) {
2876 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
2877 extra_i = output.end;
2878 break :out zir.nullTerminatedString(output.data.constraint);
2879 }
2880 break :out null;
2881 };
28822833
2883 const dead = !is_volatile and self.liveness.isUnused(inst);2834 const dead = !is_volatile and self.liveness.isUnused(inst);
2884 const result: MCValue = if (dead) .dead else result: {2835 const result: MCValue = if (dead) .dead else result: {
2885 for (args) |arg| {2836 if (outputs.len > 1) {
2886 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);2837 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
2887 extra_i = input.end;2838 }
2888 const constraint = zir.nullTerminatedString(input.data.constraint);2839
2840 const output_constraint: ?[]const u8 = for (outputs) |output| {
2841 if (output != .none) {
2842 return self.fail("TODO implement codegen for non-expr asm", .{});
2843 }
2844 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2845 // This equation accounts for the fact that even if we have exactly 4 bytes
2846 // for the string, we still use the next u32 for the null terminator.
2847 extra_i += constraint.len / 4 + 1;
2848
2849 break constraint;
2850 } else null;
2851
2852 for (inputs) |input| {
2853 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2854 // This equation accounts for the fact that even if we have exactly 4 bytes
2855 // for the string, we still use the next u32 for the null terminator.
2856 extra_i += constraint.len / 4 + 1;
28892857
2890 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {2858 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
2891 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});2859 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
...@@ -2894,11 +2862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -2894,11 +2862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2894 const reg = parseRegName(reg_name) orelse2862 const reg = parseRegName(reg_name) orelse
2895 return self.fail("unrecognized register: '{s}'", .{reg_name});2863 return self.fail("unrecognized register: '{s}'", .{reg_name});
28962864
2897 const arg_mcv = try self.resolveInst(arg);2865 const arg_mcv = try self.resolveInst(input);
2898 try self.register_manager.getReg(reg, null);2866 try self.register_manager.getReg(reg, null);
2899 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);2867 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
2868 }
2869
2870 {
2871 var clobber_i: u32 = 0;
2872 while (clobber_i < clobbers_len) : (clobber_i += 1) {
2873 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2874 // This equation accounts for the fact that even if we have exactly 4 bytes
2875 // for the string, we still use the next u32 for the null terminator.
2876 extra_i += clobber.len / 4 + 1;
2877
2878 // TODO honor these
2879 }
2900 }2880 }
29012881
2882 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
2883
2902 if (mem.eql(u8, asm_source, "svc #0")) {2884 if (mem.eql(u8, asm_source, "svc #0")) {
2903 _ = try self.addInst(.{2885 _ = try self.addInst(.{
2904 .tag = .svc,2886 .tag = .svc,
...@@ -2925,18 +2907,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -2925,18 +2907,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2925 break :result MCValue{ .none = {} };2907 break :result MCValue{ .none = {} };
2926 }2908 }
2927 };2909 };
2928 if (outputs.len + args.len <= Liveness.bpi - 1) {2910
2911 simple: {
2929 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);2912 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2930 std.mem.copy(Air.Inst.Ref, &buf, outputs);2913 var buf_index: usize = 0;
2931 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);2914 for (outputs) |output| {
2915 if (output == .none) continue;
2916
2917 if (buf_index >= buf.len) break :simple;
2918 buf[buf_index] = output;
2919 buf_index += 1;
2920 }
2921 if (buf_index + inputs.len > buf.len) break :simple;
2922 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
2932 return self.finishAir(inst, result, buf);2923 return self.finishAir(inst, result, buf);
2933 }2924 }
2934 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);2925 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
2935 for (outputs) |output| {2926 for (outputs) |output| {
2927 if (output == .none) continue;
2928
2936 bt.feed(output);2929 bt.feed(output);
2937 }2930 }
2938 for (args) |arg| {2931 for (inputs) |input| {
2939 bt.feed(arg);2932 bt.feed(input);
2940 }2933 }
2941 return bt.finishAir(result);2934 return bt.finishAir(result);
2942}2935}
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");
...@@ -3059,40 +3058,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -3059,40 +3058,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
3059}3058}
30603059
3061fn airAsm(self: *Self, inst: Air.Inst.Index) !void {3060fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3062 const air_datas = self.air.instructions.items(.data);3061 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3063 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);3062 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
3064 const zir = self.mod_fn.owner_decl.getFileScope().zir;3063 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
3065 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;3064 const clobbers_len = @truncate(u31, extra.data.flags);
3066 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);3065 var extra_i: usize = extra.end;
3067 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);3066 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
3068 const outputs_len = @truncate(u5, extended.small);3067 extra_i += outputs.len;
3069 const args_len = @truncate(u5, extended.small >> 5);3068 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
3070 const clobbers_len = @truncate(u5, extended.small >> 10);3069 extra_i += inputs.len;
3071 _ = clobbers_len; // TODO honor these
3072 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3073 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
3074 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
3075
3076 if (outputs_len > 1) {
3077 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3078 }
3079 var extra_i: usize = zir_extra.end;
3080 const output_constraint: ?[]const u8 = out: {
3081 var i: usize = 0;
3082 while (i < outputs_len) : (i += 1) {
3083 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
3084 extra_i = output.end;
3085 break :out zir.nullTerminatedString(output.data.constraint);
3086 }
3087 break :out null;
3088 };
30893070
3090 const dead = !is_volatile and self.liveness.isUnused(inst);3071 const dead = !is_volatile and self.liveness.isUnused(inst);
3091 const result: MCValue = if (dead) .dead else result: {3072 const result: MCValue = if (dead) .dead else result: {
3092 for (args) |arg| {3073 if (outputs.len > 1) {
3093 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);3074 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3094 extra_i = input.end;3075 }
3095 const constraint = zir.nullTerminatedString(input.data.constraint);3076
3077 const output_constraint: ?[]const u8 = for (outputs) |output| {
3078 if (output != .none) {
3079 return self.fail("TODO implement codegen for non-expr asm", .{});
3080 }
3081 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3082 // This equation accounts for the fact that even if we have exactly 4 bytes
3083 // for the string, we still use the next u32 for the null terminator.
3084 extra_i += constraint.len / 4 + 1;
3085
3086 break constraint;
3087 } else null;
3088
3089 for (inputs) |input| {
3090 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3091 // This equation accounts for the fact that even if we have exactly 4 bytes
3092 // for the string, we still use the next u32 for the null terminator.
3093 extra_i += constraint.len / 4 + 1;
30963094
3097 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {3095 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3098 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});3096 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
...@@ -3101,11 +3099,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -3101,11 +3099,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3101 const reg = parseRegName(reg_name) orelse3099 const reg = parseRegName(reg_name) orelse
3102 return self.fail("unrecognized register: '{s}'", .{reg_name});3100 return self.fail("unrecognized register: '{s}'", .{reg_name});
31033101
3104 const arg_mcv = try self.resolveInst(arg);3102 const arg_mcv = try self.resolveInst(input);
3105 try self.register_manager.getReg(reg, null);3103 try self.register_manager.getReg(reg, null);
3106 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);3104 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
3105 }
3106
3107 {
3108 var clobber_i: u32 = 0;
3109 while (clobber_i < clobbers_len) : (clobber_i += 1) {
3110 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3111 // This equation accounts for the fact that even if we have exactly 4 bytes
3112 // for the string, we still use the next u32 for the null terminator.
3113 extra_i += clobber.len / 4 + 1;
3114
3115 // TODO honor these
3116 }
3107 }3117 }
31083118
3119 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
3120
3109 if (mem.eql(u8, asm_source, "svc #0")) {3121 if (mem.eql(u8, asm_source, "svc #0")) {
3110 _ = try self.addInst(.{3122 _ = try self.addInst(.{
3111 .tag = .svc,3123 .tag = .svc,
...@@ -3128,18 +3140,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -3128,18 +3140,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3128 break :result MCValue{ .none = {} };3140 break :result MCValue{ .none = {} };
3129 }3141 }
3130 };3142 };
3131 if (outputs.len + args.len <= Liveness.bpi - 1) {3143
3144 simple: {
3132 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);3145 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3133 std.mem.copy(Air.Inst.Ref, &buf, outputs);3146 var buf_index: usize = 0;
3134 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);3147 for (outputs) |output| {
3148 if (output == .none) continue;
3149
3150 if (buf_index >= buf.len) break :simple;
3151 buf[buf_index] = output;
3152 buf_index += 1;
3153 }
3154 if (buf_index + inputs.len > buf.len) break :simple;
3155 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
3135 return self.finishAir(inst, result, buf);3156 return self.finishAir(inst, result, buf);
3136 }3157 }
3137 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);3158 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
3138 for (outputs) |output| {3159 for (outputs) |output| {
3160 if (output == .none) continue;
3161
3139 bt.feed(output);3162 bt.feed(output);
3140 }3163 }
3141 for (args) |arg| {3164 for (inputs) |input| {
3142 bt.feed(arg);3165 bt.feed(input);
3143 }3166 }
3144 return bt.finishAir(result);3167 return bt.finishAir(result);
3145}3168}
src/arch/riscv64/CodeGen.zig+62-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");
...@@ -1822,40 +1821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -1822,40 +1821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
1822}1821}
18231822
1824fn airAsm(self: *Self, inst: Air.Inst.Index) !void {1823fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1825 const air_datas = self.air.instructions.items(.data);1824 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1826 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);1825 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
1827 const zir = self.mod_fn.owner_decl.getFileScope().zir;1826 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
1828 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;1827 const clobbers_len = @truncate(u31, extra.data.flags);
1829 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);1828 var extra_i: usize = extra.end;
1830 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);1829 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
1831 const outputs_len = @truncate(u5, extended.small);1830 extra_i += outputs.len;
1832 const args_len = @truncate(u5, extended.small >> 5);1831 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
1833 const clobbers_len = @truncate(u5, extended.small >> 10);1832 extra_i += inputs.len;
1834 _ = clobbers_len; // TODO honor these
1835 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
1836 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
1837 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
1838
1839 if (outputs_len > 1) {
1840 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
1841 }
1842 var extra_i: usize = zir_extra.end;
1843 const output_constraint: ?[]const u8 = out: {
1844 var i: usize = 0;
1845 while (i < outputs_len) : (i += 1) {
1846 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
1847 extra_i = output.end;
1848 break :out zir.nullTerminatedString(output.data.constraint);
1849 }
1850 break :out null;
1851 };
18521833
1853 const dead = !is_volatile and self.liveness.isUnused(inst);1834 const dead = !is_volatile and self.liveness.isUnused(inst);
1854 const result: MCValue = if (dead) .dead else result: {1835 const result: MCValue = if (dead) .dead else result: {
1855 for (args) |arg| {1836 if (outputs.len > 1) {
1856 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);1837 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
1857 extra_i = input.end;1838 }
1858 const constraint = zir.nullTerminatedString(input.data.constraint);1839
1840 const output_constraint: ?[]const u8 = for (outputs) |output| {
1841 if (output != .none) {
1842 return self.fail("TODO implement codegen for non-expr asm", .{});
1843 }
1844 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
1845 // This equation accounts for the fact that even if we have exactly 4 bytes
1846 // for the string, we still use the next u32 for the null terminator.
1847 extra_i += constraint.len / 4 + 1;
1848
1849 break constraint;
1850 } else null;
1851
1852 for (inputs) |input| {
1853 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
1854 // This equation accounts for the fact that even if we have exactly 4 bytes
1855 // for the string, we still use the next u32 for the null terminator.
1856 extra_i += constraint.len / 4 + 1;
18591857
1860 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {1858 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
1861 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});1859 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
...@@ -1864,11 +1862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -1864,11 +1862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1864 const reg = parseRegName(reg_name) orelse1862 const reg = parseRegName(reg_name) orelse
1865 return self.fail("unrecognized register: '{s}'", .{reg_name});1863 return self.fail("unrecognized register: '{s}'", .{reg_name});
18661864
1867 const arg_mcv = try self.resolveInst(arg);1865 const arg_mcv = try self.resolveInst(input);
1868 try self.register_manager.getReg(reg, null);1866 try self.register_manager.getReg(reg, null);
1869 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);1867 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
1870 }1868 }
18711869
1870 {
1871 var clobber_i: u32 = 0;
1872 while (clobber_i < clobbers_len) : (clobber_i += 1) {
1873 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
1874 // This equation accounts for the fact that even if we have exactly 4 bytes
1875 // for the string, we still use the next u32 for the null terminator.
1876 extra_i += clobber.len / 4 + 1;
1877
1878 // TODO honor these
1879 }
1880 }
1881
1882 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
1883
1872 if (mem.eql(u8, asm_source, "ecall")) {1884 if (mem.eql(u8, asm_source, "ecall")) {
1873 _ = try self.addInst(.{1885 _ = try self.addInst(.{
1874 .tag = .ecall,1886 .tag = .ecall,
...@@ -1890,18 +1902,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -1890,18 +1902,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1890 break :result MCValue{ .none = {} };1902 break :result MCValue{ .none = {} };
1891 }1903 }
1892 };1904 };
1893 if (outputs.len + args.len <= Liveness.bpi - 1) {1905 simple: {
1894 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);1906 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
1895 std.mem.copy(Air.Inst.Ref, &buf, outputs);1907 var buf_index: usize = 0;
1896 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);1908 for (outputs) |output| {
1909 if (output == .none) continue;
1910
1911 if (buf_index >= buf.len) break :simple;
1912 buf[buf_index] = output;
1913 buf_index += 1;
1914 }
1915 if (buf_index + inputs.len > buf.len) break :simple;
1916 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
1897 return self.finishAir(inst, result, buf);1917 return self.finishAir(inst, result, buf);
1898 }1918 }
1899 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);1919 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
1900 for (outputs) |output| {1920 for (outputs) |output| {
1921 if (output == .none) continue;
1922
1901 bt.feed(output);1923 bt.feed(output);
1902 }1924 }
1903 for (args) |arg| {1925 for (inputs) |input| {
1904 bt.feed(arg);1926 bt.feed(input);
1905 }1927 }
1906 return bt.finishAir(result);1928 return bt.finishAir(result);
1907}1929}
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,
...@@ -3415,41 +3414,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {...@@ -3415,41 +3414,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
3415}3414}
34163415
3417fn airAsm(self: *Self, inst: Air.Inst.Index) !void {3416fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3418 const air_datas = self.air.instructions.items(.data);3417 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3419 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);3418 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
3420 const zir = self.mod_fn.owner_decl.getFileScope().zir;3419 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
3421 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;3420 const clobbers_len = @truncate(u31, extra.data.flags);
3422 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);3421 var extra_i: usize = extra.end;
3423 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);3422 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
3424 const outputs_len = @truncate(u5, extended.small);3423 extra_i += outputs.len;
3425 const args_len = @truncate(u5, extended.small >> 5);3424 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
3426 const clobbers_len = @truncate(u5, extended.small >> 10);3425 extra_i += inputs.len;
3427 _ = clobbers_len; // TODO honor these
3428 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
3429 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..args_len]);
3430
3431 if (outputs_len > 1) {
3432 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3433 }
3434 var extra_i: usize = zir_extra.end;
3435 const output_constraint: ?[]const u8 = out: {
3436 var i: usize = 0;
3437 while (i < outputs_len) : (i += 1) {
3438 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
3439 extra_i = output.end;
3440 break :out zir.nullTerminatedString(output.data.constraint);
3441 }
3442 break :out null;
3443 };
34443426
3445 const dead = !is_volatile and self.liveness.isUnused(inst);3427 const dead = !is_volatile and self.liveness.isUnused(inst);
3446 const result: MCValue = if (dead)3428 const result: MCValue = if (dead) .dead else result: {
3447 .dead3429 if (outputs.len > 1) {
3448 else result: {3430 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3449 for (args) |arg| {3431 }
3450 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);3432
3451 extra_i = input.end;3433 const output_constraint: ?[]const u8 = for (outputs) |output| {
3452 const constraint = zir.nullTerminatedString(input.data.constraint);3434 if (output != .none) {
3435 return self.fail("TODO implement codegen for non-expr asm", .{});
3436 }
3437 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3438 // This equation accounts for the fact that even if we have exactly 4 bytes
3439 // for the string, we still use the next u32 for the null terminator.
3440 extra_i += constraint.len / 4 + 1;
3441
3442 break constraint;
3443 } else null;
3444
3445 for (inputs) |input| {
3446 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
3447 // This equation accounts for the fact that even if we have exactly 4 bytes
3448 // for the string, we still use the next u32 for the null terminator.
3449 extra_i += constraint.len / 4 + 1;
34533450
3454 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {3451 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3455 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});3452 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
...@@ -3458,11 +3455,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -3458,11 +3455,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3458 const reg = parseRegName(reg_name) orelse3455 const reg = parseRegName(reg_name) orelse
3459 return self.fail("unrecognized register: '{s}'", .{reg_name});3456 return self.fail("unrecognized register: '{s}'", .{reg_name});
34603457
3461 const arg_mcv = try self.resolveInst(arg);3458 const arg_mcv = try self.resolveInst(input);
3462 try self.register_manager.getReg(reg, null);3459 try self.register_manager.getReg(reg, null);
3463 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);3460 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
3461 }
3462
3463 {
3464 var clobber_i: u32 = 0;
3465 while (clobber_i < clobbers_len) : (clobber_i += 1) {
3466 const clobber = 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 += clobber.len / 4 + 1;
3470
3471 // TODO honor these
3472 }
3464 }3473 }
34653474
3475 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
3476
3466 {3477 {
3467 var iter = std.mem.tokenize(u8, asm_source, "\n\r");3478 var iter = std.mem.tokenize(u8, asm_source, "\n\r");
3468 while (iter.next()) |ins| {3479 while (iter.next()) |ins| {
...@@ -3529,14 +3540,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -3529,14 +3540,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3529 break :result MCValue{ .none = {} };3540 break :result MCValue{ .none = {} };
3530 }3541 }
3531 };3542 };
3532 if (args.len <= Liveness.bpi - 1) {3543
3544 simple: {
3533 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);3545 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3534 std.mem.copy(Air.Inst.Ref, &buf, args);3546 var buf_index: usize = 0;
3547 for (outputs) |output| {
3548 if (output == .none) continue;
3549
3550 if (buf_index >= buf.len) break :simple;
3551 buf[buf_index] = output;
3552 buf_index += 1;
3553 }
3554 if (buf_index + inputs.len > buf.len) break :simple;
3555 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
3535 return self.finishAir(inst, result, buf);3556 return self.finishAir(inst, result, buf);
3536 }3557 }
3537 var bt = try self.iterateBigTomb(inst, args.len);3558 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
3538 for (args) |arg| {3559 for (outputs) |output| {
3539 bt.feed(arg);3560 if (output == .none) continue;
3561
3562 bt.feed(output);
3563 }
3564 for (inputs) |input| {
3565 bt.feed(input);
3540 }3566 }
3541 return bt.finishAir(result);3567 return bt.finishAir(result);
3542}3568}
...@@ -3615,7 +3641,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -3615,7 +3641,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
3615 const reg = try self.copyToTmpRegister(ty, mcv);3641 const reg = try self.copyToTmpRegister(ty, mcv);
3616 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });3642 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
3617 },3643 },
3618 else => return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}),3644 else => return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}),
3619 }3645 }
3620 },3646 },
3621 .embedded_in_code => {3647 .embedded_in_code => {
...@@ -3623,7 +3649,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -3623,7 +3649,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
3623 const reg = try self.copyToTmpRegister(ty, mcv);3649 const reg = try self.copyToTmpRegister(ty, mcv);
3624 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });3650 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
3625 }3651 }
3626 return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv});3652 return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv});
3627 },3653 },
3628 .memory,3654 .memory,
3629 .direct_load,3655 .direct_load,
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 };
...@@ -2805,49 +2804,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2805,49 +2804,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
2805}2804}
28062805
2807fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {2806fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
2808 const air_datas = f.air.instructions.items(.data);2807 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
2809 const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);2808 const extra = f.air.extraData(Air.Asm, ty_pl.payload);
2810 const zir = f.object.dg.decl.getFileScope().zir;2809 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2811 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;2810 const clobbers_len = @truncate(u31, extra.data.flags);
2812 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);2811 var extra_i: usize = extra.end;
2813 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);2812 const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]);
2814 const outputs_len = @truncate(u5, extended.small);2813 extra_i += outputs.len;
2815 const args_len = @truncate(u5, extended.small >> 5);2814 const inputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]);
2816 const clobbers_len = @truncate(u5, extended.small >> 10);2815 extra_i += inputs.len;
2817 _ = clobbers_len; // TODO honor these2816
2818 const is_volatile = @truncate(u1, extended.small >> 15) != 0;2817 if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none;
2819 const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]);2818
2820 const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]);2819 if (outputs.len > 1) {
2821
2822 if (outputs_len > 1) {
2823 return f.fail("TODO implement codegen for asm with more than 1 output", .{});2820 return f.fail("TODO implement codegen for asm with more than 1 output", .{});
2824 }2821 }
28252822
2826 if (f.liveness.isUnused(inst) and !is_volatile)2823 const output_constraint: ?[]const u8 = for (outputs) |output| {
2827 return CValue.none;2824 if (output != .none) {
28282825 return f.fail("TODO implement codegen for non-expr asm", .{});
2829 var extra_i: usize = zir_extra.end;
2830 const output_constraint: ?[]const u8 = out: {
2831 var i: usize = 0;
2832 while (i < outputs_len) : (i += 1) {
2833 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
2834 extra_i = output.end;
2835 break :out zir.nullTerminatedString(output.data.constraint);
2836 }2826 }
2837 break :out null;2827 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2838 };2828 // This equation accounts for the fact that even if we have exactly 4 bytes
2839 const args_extra_begin = extra_i;2829 // for the string, we still use the next u32 for the null terminator.
2830 extra_i += constraint.len / 4 + 1;
2831
2832 break constraint;
2833 } else null;
28402834
2841 const writer = f.object.writer();2835 const writer = f.object.writer();
2842 for (args) |arg| {2836 const inputs_extra_begin = extra_i;
2843 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);2837
2844 extra_i = input.end;2838 for (inputs) |input| {
2845 const constraint = zir.nullTerminatedString(input.data.constraint);2839 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2840 // This equation accounts for the fact that even if we have exactly 4 bytes
2841 // for the string, we still use the next u32 for the null terminator.
2842 extra_i += constraint.len / 4 + 1;
2843
2846 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {2844 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
2847 const reg = constraint[1 .. constraint.len - 1];2845 const reg = constraint[1 .. constraint.len - 1];
2848 const arg_c_value = try f.resolveInst(arg);2846 const arg_c_value = try f.resolveInst(input);
2849 try writer.writeAll("register ");2847 try writer.writeAll("register ");
2850 try f.renderType(writer, f.air.typeOf(arg));2848 try f.renderType(writer, f.air.typeOf(input));
28512849
2852 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });2850 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
2853 try f.writeCValue(writer, arg_c_value);2851 try f.writeCValue(writer, arg_c_value);
...@@ -2856,21 +2854,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2856,21 +2854,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
2856 return f.fail("TODO non-explicit inline asm regs", .{});2854 return f.fail("TODO non-explicit inline asm regs", .{});
2857 }2855 }
2858 }2856 }
2857
2858 {
2859 var clobber_i: u32 = 0;
2860 while (clobber_i < clobbers_len) : (clobber_i += 1) {
2861 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2862 // This equation accounts for the fact that even if we have exactly 4 bytes
2863 // for the string, we still use the next u32 for the null terminator.
2864 extra_i += clobber.len / 4 + 1;
2865
2866 // TODO honor these
2867 }
2868 }
2869
2870 const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len];
2871
2859 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";2872 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";
2860 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });2873 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });
2861 if (output_constraint) |_| {2874 if (output_constraint) |_| {
2862 return f.fail("TODO: CBE inline asm output", .{});2875 return f.fail("TODO: CBE inline asm output", .{});
2863 }2876 }
2864 if (args.len > 0) {2877 if (inputs.len > 0) {
2865 if (output_constraint == null) {2878 if (output_constraint == null) {
2866 try writer.writeAll(" :");2879 try writer.writeAll(" :");
2867 }2880 }
2868 try writer.writeAll(": ");2881 try writer.writeAll(": ");
2869 extra_i = args_extra_begin;2882 extra_i = inputs_extra_begin;
2870 for (args) |_, index| {2883 for (inputs) |_, index| {
2871 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);2884 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2872 extra_i = input.end;2885 // This equation accounts for the fact that even if we have exactly 4 bytes
2873 const constraint = zir.nullTerminatedString(input.data.constraint);2886 // for the string, we still use the next u32 for the null terminator.
2887 extra_i += constraint.len / 4 + 1;
2888
2874 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {2889 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
2875 const reg = constraint[1 .. constraint.len - 1];2890 const reg = constraint[1 .. constraint.len - 1];
2876 if (index > 0) {2891 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 };
...@@ -2893,33 +2890,21 @@ pub const FuncGen = struct {...@@ -2893,33 +2890,21 @@ pub const FuncGen = struct {
2893 // as stage1.2890 // as stage1.
28942891
2895 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2892 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2896 const air_asm = self.air.extraData(Air.Asm, ty_pl.payload);2893 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
2897 const zir = self.dg.decl.getFileScope().zir;2894 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2898 const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;2895 const clobbers_len = @truncate(u31, extra.data.flags);
2899 const is_volatile = @truncate(u1, extended.small >> 15) != 0;2896 var extra_i: usize = extra.end;
2900 if (!is_volatile and self.liveness.isUnused(inst)) {2897
2901 return null;2898 if (!is_volatile and self.liveness.isUnused(inst)) return null;
2902 }2899
2903 const outputs_len = @truncate(u5, extended.small);2900 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
2904 if (outputs_len > 1) {2901 extra_i += outputs.len;
2902 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
2903 extra_i += inputs.len;
2904
2905 if (outputs.len > 1) {
2905 return self.todo("implement llvm codegen for asm with more than 1 output", .{});2906 return self.todo("implement llvm codegen for asm with more than 1 output", .{});
2906 }2907 }
2907 const args_len = @truncate(u5, extended.small >> 5);
2908 const clobbers_len = @truncate(u5, extended.small >> 10);
2909 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
2910 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
2911 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end..][0..args_len]);
2912
2913 var extra_i: usize = zir_extra.end;
2914 const output_constraint: ?[]const u8 = out: {
2915 var i: usize = 0;
2916 while (i < outputs_len) : (i += 1) {
2917 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
2918 extra_i = output.end;
2919 break :out zir.nullTerminatedString(output.data.constraint);
2920 }
2921 break :out null;
2922 };
29232908
2924 var llvm_constraints: std.ArrayListUnmanaged(u8) = .{};2909 var llvm_constraints: std.ArrayListUnmanaged(u8) = .{};
2925 defer llvm_constraints.deinit(self.gpa);2910 defer llvm_constraints.deinit(self.gpa);
...@@ -2928,14 +2913,21 @@ pub const FuncGen = struct {...@@ -2928,14 +2913,21 @@ pub const FuncGen = struct {
2928 defer arena_allocator.deinit();2913 defer arena_allocator.deinit();
2929 const arena = arena_allocator.allocator();2914 const arena = arena_allocator.allocator();
29302915
2931 const llvm_params_len = args.len;2916 const llvm_params_len = inputs.len;
2932 const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);2917 const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);
2933 const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);2918 const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);
2934
2935 var llvm_param_i: usize = 0;2919 var llvm_param_i: usize = 0;
2936 var total_i: usize = 0;2920 var total_i: usize = 0;
29372921
2938 if (output_constraint) |constraint| {2922 for (outputs) |output| {
2923 if (output != .none) {
2924 return self.todo("implement inline asm with non-returned output", .{});
2925 }
2926 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2927 // This equation accounts for the fact that even if we have exactly 4 bytes
2928 // for the string, we still use the next u32 for the null terminator.
2929 extra_i += constraint.len / 4 + 1;
2930
2939 try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);2931 try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);
2940 if (total_i != 0) {2932 if (total_i != 0) {
2941 llvm_constraints.appendAssumeCapacity(',');2933 llvm_constraints.appendAssumeCapacity(',');
...@@ -2946,11 +2938,13 @@ pub const FuncGen = struct {...@@ -2946,11 +2938,13 @@ pub const FuncGen = struct {
2946 total_i += 1;2938 total_i += 1;
2947 }2939 }
29482940
2949 for (args) |arg| {2941 for (inputs) |input| {
2950 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);2942 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2951 extra_i = input.end;2943 // This equation accounts for the fact that even if we have exactly 4 bytes
2952 const constraint = zir.nullTerminatedString(input.data.constraint);2944 // for the string, we still use the next u32 for the null terminator.
2953 const arg_llvm_value = try self.resolveInst(arg);2945 extra_i += constraint.len / 4 + 1;
2946
2947 const arg_llvm_value = try self.resolveInst(input);
29542948
2955 llvm_param_values[llvm_param_i] = arg_llvm_value;2949 llvm_param_values[llvm_param_i] = arg_llvm_value;
2956 llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf();2950 llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf();
...@@ -2965,19 +2959,26 @@ pub const FuncGen = struct {...@@ -2965,19 +2959,26 @@ pub const FuncGen = struct {
2965 total_i += 1;2959 total_i += 1;
2966 }2960 }
29672961
2968 const clobbers = zir.extra[extra_i..][0..clobbers_len];2962 {
2969 for (clobbers) |clobber_index| {2963 var clobber_i: u32 = 0;
2970 const clobber = zir.nullTerminatedString(clobber_index);2964 while (clobber_i < clobbers_len) : (clobber_i += 1) {
2971 try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);2965 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2972 if (total_i != 0) {2966 // This equation accounts for the fact that even if we have exactly 4 bytes
2973 llvm_constraints.appendAssumeCapacity(',');2967 // for the string, we still use the next u32 for the null terminator.
2974 }2968 extra_i += clobber.len / 4 + 1;
2975 llvm_constraints.appendSliceAssumeCapacity("~{");2969
2976 llvm_constraints.appendSliceAssumeCapacity(clobber);2970 try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
2977 llvm_constraints.appendSliceAssumeCapacity("}");2971 if (total_i != 0) {
2972 llvm_constraints.appendAssumeCapacity(',');
2973 }
2974 llvm_constraints.appendSliceAssumeCapacity("~{");
2975 llvm_constraints.appendSliceAssumeCapacity(clobber);
2976 llvm_constraints.appendSliceAssumeCapacity("}");
29782977
2979 total_i += 1;2978 total_i += 1;
2979 }
2980 }2980 }
2981 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
29812982
2982 const ret_ty = self.air.typeOfIndex(inst);2983 const ret_ty = self.air.typeOfIndex(inst);
2983 const ret_llvm_ty = try self.dg.llvmType(ret_ty);2984 const ret_llvm_ty = try self.dg.llvmType(ret_ty);
src/print_air.zig+53-40
...@@ -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
...@@ -431,51 +428,67 @@ const Writer = struct {...@@ -431,51 +428,67 @@ const Writer = struct {
431428
432 fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {429 fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
433 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;430 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
434 const air_asm = w.air.extraData(Air.Asm, ty_pl.payload);431 const extra = w.air.extraData(Air.Asm, ty_pl.payload);
435 const zir = w.zir;432 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
436 const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;433 const clobbers_len = @truncate(u31, extra.data.flags);
437 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);434 var extra_i: usize = extra.end;
438 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);435 var op_index: usize = 0;
439 const outputs_len = @truncate(u5, extended.small);436
440 const args_len = @truncate(u5, extended.small >> 5);437 const ret_ty = w.air.typeOfIndex(inst);
441 const clobbers_len = @truncate(u5, extended.small >> 10);438 try s.print("{}", .{ret_ty});
442 const args = @bitCast([]const Air.Inst.Ref, w.air.extra[air_asm.end..][0..args_len]);439
443440 if (is_volatile) {
444 var extra_i: usize = zir_extra.end;441 try s.writeAll(", volatile");
445 const output_constraint: ?[]const u8 = out: {442 }
446 var i: usize = 0;
447 while (i < outputs_len) : (i += 1) {
448 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
449 extra_i = output.end;
450 break :out zir.nullTerminatedString(output.data.constraint);
451 }
452 break :out null;
453 };
454443
455 try s.print("\"{s}\"", .{asm_source});444 const outputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.outputs_len]);
445 extra_i += outputs.len;
446 const inputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.inputs_len]);
447 extra_i += inputs.len;
456448
457 if (output_constraint) |constraint| {449 for (outputs) |output| {
458 const ret_ty = w.air.typeOfIndex(inst);450 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
459 try s.print(", {s} -> {}", .{ constraint, ret_ty });451 // This equation accounts for the fact that even if we have exactly 4 bytes
452 // for the string, we still use the next u32 for the null terminator.
453 extra_i += constraint.len / 4 + 1;
454
455 if (output == .none) {
456 try s.print(", -> {s}", .{constraint});
457 } else {
458 try s.print(", out {s} = (", .{constraint});
459 try w.writeOperand(s, inst, op_index, output);
460 op_index += 1;
461 try s.writeByte(')');
462 }
460 }463 }
461464
462 for (args) |arg| {465 for (inputs) |input| {
463 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);466 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
464 extra_i = input.end;467 // This equation accounts for the fact that even if we have exactly 4 bytes
465 const constraint = zir.nullTerminatedString(input.data.constraint);468 // for the string, we still use the next u32 for the null terminator.
469 extra_i += constraint.len / 4 + 1;
466470
467 try s.print(", {s} = (", .{constraint});471 try s.print(", in {s} = (", .{constraint});
468 try w.writeOperand(s, inst, 0, arg);472 try w.writeOperand(s, inst, op_index, input);
473 op_index += 1;
469 try s.writeByte(')');474 try s.writeByte(')');
470 }475 }
471476
472 const clobbers = zir.extra[extra_i..][0..clobbers_len];477 {
473 for (clobbers) |clobber_index| {478 var clobber_i: u32 = 0;
474 const clobber = zir.nullTerminatedString(clobber_index);479 while (clobber_i < clobbers_len) : (clobber_i += 1) {
475 try s.writeAll(", ~{");480 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
476 try s.writeAll(clobber);481 // This equation accounts for the fact that even if we have exactly 4 bytes
477 try s.writeAll("}");482 // for the string, we still use the next u32 for the null terminator.
483 extra_i += clobber.len / 4 + 1;
484
485 try s.writeAll(", ~{");
486 try s.writeAll(clobber);
487 try s.writeAll("}");
488 }
478 }489 }
490 const asm_source = std.mem.sliceAsBytes(w.air.extra[extra_i..])[0..extra.data.source_len];
491 try s.print(", \"{s}\"", .{asm_source});
479 }492 }
480493
481 fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {494 fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {