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 {
697697/// Trailing:
698698/// 0. `Inst.Ref` for every outputs_len
699699/// 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.
700711pub const Asm = struct {
701 /// Index to the corresponding ZIR instruction.
702 /// `asm_source`, `outputs_len`, `inputs_len`, `clobbers_len`, `is_volatile`, and
703 /// clobbers are found via here.
704 zir_index: u32,
712 /// Length of the assembly source in bytes.
713 source_len: u32,
714 outputs_len: u32,
715 inputs_len: u32,
716 /// The MSB is `is_volatile`.
717 /// The rest of the bits are `clobbers_len`.
718 flags: u32,
705719};
706720
707721pub const Cmpxchg = struct {
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);
......@@ -444,15 +441,24 @@ fn analyzeInst(
444441 },
445442 .assembly => {
446443 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;
448 const outputs_len = @truncate(u5, extended.small);
449 const inputs_len = @truncate(u5, extended.small >> 5);
450 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]);
451 const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]);
452 if (outputs.len + args.len <= bpi - 1) {
444 var extra_i: usize = extra.end;
445 const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]);
446 extra_i += outputs.len;
447 const inputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]);
448 extra_i += inputs.len;
449
450 simple: {
453451 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
454 std.mem.copy(Air.Inst.Ref, &buf, outputs);
455 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
452 var buf_index: usize = 0;
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);
456462 return trackOperands(a, new_set, inst, main_tomb, buf);
457463 }
458464 var extra_tombs: ExtraTombs = .{
......@@ -462,10 +468,12 @@ fn analyzeInst(
462468 .main_tomb = main_tomb,
463469 };
464470 for (outputs) |output| {
465 try extra_tombs.feed(output);
471 if (output != .none) {
472 try extra_tombs.feed(output);
473 }
466474 }
467 for (args) |arg| {
468 try extra_tombs.feed(arg);
475 for (inputs) |input| {
476 try extra_tombs.feed(input);
469477 }
470478 return extra_tombs.finish();
471479 },
src/Sema.zig+46-6
......@@ -1124,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
11241124 .frame_address => return sema.zirFrameAddress( block, extended),
11251125 .alloc => return sema.zirAllocExtended( block, extended),
11261126 .builtin_extern => return sema.zirBuiltinExtern( block, extended),
1127 .@"asm" => return sema.zirAsm( block, extended, inst),
1127 .@"asm" => return sema.zirAsm( block, extended),
11281128 .typeof_peer => return sema.zirTypeofPeer( block, extended),
11291129 .compile_log => return sema.zirCompileLog( block, extended),
11301130 .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
......@@ -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");
......@@ -2007,36 +2006,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
20072006 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
20082007}
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
20402009fn airArg(self: *Self, inst: Air.Inst.Index) !void {
20412010 const arg_index = self.arg_index;
20422011 self.arg_index += 1;
......@@ -2852,40 +2821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
28522821}
28532822
28542823fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2855 const air_datas = self.air.instructions.items(.data);
2856 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
2857 const zir = self.mod_fn.owner_decl.getFileScope().zir;
2858 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
2859 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
2860 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
2861 const outputs_len = @truncate(u5, extended.small);
2862 const args_len = @truncate(u5, extended.small >> 5);
2863 const clobbers_len = @truncate(u5, extended.small >> 10);
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 };
2824 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2825 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
2826 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2827 const clobbers_len = @truncate(u31, extra.data.flags);
2828 var extra_i: usize = extra.end;
2829 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
2830 extra_i += outputs.len;
2831 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
2832 extra_i += inputs.len;
28822833
28832834 const dead = !is_volatile and self.liveness.isUnused(inst);
28842835 const result: MCValue = if (dead) .dead else result: {
2885 for (args) |arg| {
2886 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
2887 extra_i = input.end;
2888 const constraint = zir.nullTerminatedString(input.data.constraint);
2836 if (outputs.len > 1) {
2837 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
2838 }
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
28902858 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
28912859 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
......@@ -2894,11 +2862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
28942862 const reg = parseRegName(reg_name) orelse
28952863 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);
28982866 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 }
29002880 }
29012881
2882 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
2883
29022884 if (mem.eql(u8, asm_source, "svc #0")) {
29032885 _ = try self.addInst(.{
29042886 .tag = .svc,
......@@ -2925,18 +2907,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
29252907 break :result MCValue{ .none = {} };
29262908 }
29272909 };
2928 if (outputs.len + args.len <= Liveness.bpi - 1) {
2910
2911 simple: {
29292912 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2930 std.mem.copy(Air.Inst.Ref, &buf, outputs);
2931 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
2913 var buf_index: usize = 0;
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);
29322923 return self.finishAir(inst, result, buf);
29332924 }
2934 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
2925 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
29352926 for (outputs) |output| {
2927 if (output == .none) continue;
2928
29362929 bt.feed(output);
29372930 }
2938 for (args) |arg| {
2939 bt.feed(arg);
2931 for (inputs) |input| {
2932 bt.feed(input);
29402933 }
29412934 return bt.finishAir(result);
29422935}
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");
......@@ -3059,40 +3058,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
30593058}
30603059
30613060fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3062 const air_datas = self.air.instructions.items(.data);
3063 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
3064 const zir = self.mod_fn.owner_decl.getFileScope().zir;
3065 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
3066 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
3067 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
3068 const outputs_len = @truncate(u5, extended.small);
3069 const args_len = @truncate(u5, extended.small >> 5);
3070 const clobbers_len = @truncate(u5, extended.small >> 10);
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 };
3061 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3062 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
3063 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
3064 const clobbers_len = @truncate(u31, extra.data.flags);
3065 var extra_i: usize = extra.end;
3066 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
3067 extra_i += outputs.len;
3068 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
3069 extra_i += inputs.len;
30893070
30903071 const dead = !is_volatile and self.liveness.isUnused(inst);
30913072 const result: MCValue = if (dead) .dead else result: {
3092 for (args) |arg| {
3093 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3094 extra_i = input.end;
3095 const constraint = zir.nullTerminatedString(input.data.constraint);
3073 if (outputs.len > 1) {
3074 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3075 }
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
30973095 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
30983096 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
......@@ -3101,11 +3099,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
31013099 const reg = parseRegName(reg_name) orelse
31023100 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);
31053103 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 }
31073117 }
31083118
3119 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
3120
31093121 if (mem.eql(u8, asm_source, "svc #0")) {
31103122 _ = try self.addInst(.{
31113123 .tag = .svc,
......@@ -3128,18 +3140,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
31283140 break :result MCValue{ .none = {} };
31293141 }
31303142 };
3131 if (outputs.len + args.len <= Liveness.bpi - 1) {
3143
3144 simple: {
31323145 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
3133 std.mem.copy(Air.Inst.Ref, &buf, outputs);
3134 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
3146 var buf_index: usize = 0;
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);
31353156 return self.finishAir(inst, result, buf);
31363157 }
3137 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
3158 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
31383159 for (outputs) |output| {
3160 if (output == .none) continue;
3161
31393162 bt.feed(output);
31403163 }
3141 for (args) |arg| {
3142 bt.feed(arg);
3164 for (inputs) |input| {
3165 bt.feed(input);
31433166 }
31443167 return bt.finishAir(result);
31453168}
src/arch/riscv64/CodeGen.zig+62-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");
......@@ -1822,40 +1821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
18221821}
18231822
18241823fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1825 const air_datas = self.air.instructions.items(.data);
1826 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
1827 const zir = self.mod_fn.owner_decl.getFileScope().zir;
1828 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
1829 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
1830 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
1831 const outputs_len = @truncate(u5, extended.small);
1832 const args_len = @truncate(u5, extended.small >> 5);
1833 const clobbers_len = @truncate(u5, extended.small >> 10);
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 };
1824 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1825 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
1826 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
1827 const clobbers_len = @truncate(u31, extra.data.flags);
1828 var extra_i: usize = extra.end;
1829 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
1830 extra_i += outputs.len;
1831 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
1832 extra_i += inputs.len;
18521833
18531834 const dead = !is_volatile and self.liveness.isUnused(inst);
18541835 const result: MCValue = if (dead) .dead else result: {
1855 for (args) |arg| {
1856 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
1857 extra_i = input.end;
1858 const constraint = zir.nullTerminatedString(input.data.constraint);
1836 if (outputs.len > 1) {
1837 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
1838 }
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
18601858 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
18611859 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
......@@ -1864,11 +1862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
18641862 const reg = parseRegName(reg_name) orelse
18651863 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);
18681866 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);
18701868 }
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
18721884 if (mem.eql(u8, asm_source, "ecall")) {
18731885 _ = try self.addInst(.{
18741886 .tag = .ecall,
......@@ -1890,18 +1902,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
18901902 break :result MCValue{ .none = {} };
18911903 }
18921904 };
1893 if (outputs.len + args.len <= Liveness.bpi - 1) {
1905 simple: {
18941906 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
1895 std.mem.copy(Air.Inst.Ref, &buf, outputs);
1896 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
1907 var buf_index: usize = 0;
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);
18971917 return self.finishAir(inst, result, buf);
18981918 }
1899 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
1919 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
19001920 for (outputs) |output| {
1921 if (output == .none) continue;
1922
19011923 bt.feed(output);
19021924 }
1903 for (args) |arg| {
1904 bt.feed(arg);
1925 for (inputs) |input| {
1926 bt.feed(input);
19051927 }
19061928 return bt.finishAir(result);
19071929}
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,
......@@ -3415,41 +3414,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
34153414}
34163415
34173416fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
3418 const air_datas = self.air.instructions.items(.data);
3419 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
3420 const zir = self.mod_fn.owner_decl.getFileScope().zir;
3421 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
3422 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
3423 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
3424 const outputs_len = @truncate(u5, extended.small);
3425 const args_len = @truncate(u5, extended.small >> 5);
3426 const clobbers_len = @truncate(u5, extended.small >> 10);
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 };
3417 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3418 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
3419 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
3420 const clobbers_len = @truncate(u31, extra.data.flags);
3421 var extra_i: usize = extra.end;
3422 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
3423 extra_i += outputs.len;
3424 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
3425 extra_i += inputs.len;
34443426
34453427 const dead = !is_volatile and self.liveness.isUnused(inst);
3446 const result: MCValue = if (dead)
3447 .dead
3448 else result: {
3449 for (args) |arg| {
3450 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3451 extra_i = input.end;
3452 const constraint = zir.nullTerminatedString(input.data.constraint);
3428 const result: MCValue = if (dead) .dead else result: {
3429 if (outputs.len > 1) {
3430 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
3431 }
3432
3433 const output_constraint: ?[]const u8 = for (outputs) |output| {
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
34543451 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
34553452 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
......@@ -3458,11 +3455,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
34583455 const reg = parseRegName(reg_name) orelse
34593456 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);
34623459 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 }
34643473 }
34653474
3475 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
3476
34663477 {
34673478 var iter = std.mem.tokenize(u8, asm_source, "\n\r");
34683479 while (iter.next()) |ins| {
......@@ -3529,14 +3540,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
35293540 break :result MCValue{ .none = {} };
35303541 }
35313542 };
3532 if (args.len <= Liveness.bpi - 1) {
3543
3544 simple: {
35333545 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);
35353556 return self.finishAir(inst, result, buf);
35363557 }
3537 var bt = try self.iterateBigTomb(inst, args.len);
3538 for (args) |arg| {
3539 bt.feed(arg);
3558 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
3559 for (outputs) |output| {
3560 if (output == .none) continue;
3561
3562 bt.feed(output);
3563 }
3564 for (inputs) |input| {
3565 bt.feed(input);
35403566 }
35413567 return bt.finishAir(result);
35423568}
......@@ -3615,7 +3641,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
36153641 const reg = try self.copyToTmpRegister(ty, mcv);
36163642 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
36173643 },
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}),
36193645 }
36203646 },
36213647 .embedded_in_code => {
......@@ -3623,7 +3649,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
36233649 const reg = try self.copyToTmpRegister(ty, mcv);
36243650 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
36253651 }
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});
36273653 },
36283654 .memory,
36293655 .direct_load,
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 };
......@@ -2805,49 +2804,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
28052804}
28062805
28072806fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
2808 const air_datas = f.air.instructions.items(.data);
2809 const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
2810 const zir = f.object.dg.decl.getFileScope().zir;
2811 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
2812 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
2813 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
2814 const outputs_len = @truncate(u5, extended.small);
2815 const args_len = @truncate(u5, extended.small >> 5);
2816 const clobbers_len = @truncate(u5, extended.small >> 10);
2817 _ = clobbers_len; // TODO honor these
2818 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
2819 const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]);
2820 const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]);
2821
2822 if (outputs_len > 1) {
2807 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
2808 const extra = f.air.extraData(Air.Asm, ty_pl.payload);
2809 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2810 const clobbers_len = @truncate(u31, extra.data.flags);
2811 var extra_i: usize = extra.end;
2812 const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]);
2813 extra_i += outputs.len;
2814 const inputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]);
2815 extra_i += inputs.len;
2816
2817 if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none;
2818
2819 if (outputs.len > 1) {
28232820 return f.fail("TODO implement codegen for asm with more than 1 output", .{});
28242821 }
28252822
2826 if (f.liveness.isUnused(inst) and !is_volatile)
2827 return CValue.none;
2828
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);
2823 const output_constraint: ?[]const u8 = for (outputs) |output| {
2824 if (output != .none) {
2825 return f.fail("TODO implement codegen for non-expr asm", .{});
28362826 }
2837 break :out null;
2838 };
2839 const args_extra_begin = extra_i;
2827 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2828 // This equation accounts for the fact that even if we have exactly 4 bytes
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
28412835 const writer = f.object.writer();
2842 for (args) |arg| {
2843 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
2844 extra_i = input.end;
2845 const constraint = zir.nullTerminatedString(input.data.constraint);
2836 const inputs_extra_begin = extra_i;
2837
2838 for (inputs) |input| {
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
28462844 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
28472845 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);
28492847 try writer.writeAll("register ");
2850 try f.renderType(writer, f.air.typeOf(arg));
2848 try f.renderType(writer, f.air.typeOf(input));
28512849
28522850 try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
28532851 try f.writeCValue(writer, arg_c_value);
......@@ -2856,21 +2854,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
28562854 return f.fail("TODO non-explicit inline asm regs", .{});
28572855 }
28582856 }
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
28592872 const volatile_string: []const u8 = if (is_volatile) "volatile " else "";
28602873 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });
28612874 if (output_constraint) |_| {
28622875 return f.fail("TODO: CBE inline asm output", .{});
28632876 }
2864 if (args.len > 0) {
2877 if (inputs.len > 0) {
28652878 if (output_constraint == null) {
28662879 try writer.writeAll(" :");
28672880 }
28682881 try writer.writeAll(": ");
2869 extra_i = args_extra_begin;
2870 for (args) |_, index| {
2871 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
2872 extra_i = input.end;
2873 const constraint = zir.nullTerminatedString(input.data.constraint);
2882 extra_i = inputs_extra_begin;
2883 for (inputs) |_, index| {
2884 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
2885 // This equation accounts for the fact that even if we have exactly 4 bytes
2886 // for the string, we still use the next u32 for the null terminator.
2887 extra_i += constraint.len / 4 + 1;
2888
28742889 if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
28752890 const reg = constraint[1 .. constraint.len - 1];
28762891 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 };
......@@ -2893,33 +2890,21 @@ pub const FuncGen = struct {
28932890 // as stage1.
28942891
28952892 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2896 const air_asm = self.air.extraData(Air.Asm, ty_pl.payload);
2897 const zir = self.dg.decl.getFileScope().zir;
2898 const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;
2899 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
2900 if (!is_volatile and self.liveness.isUnused(inst)) {
2901 return null;
2902 }
2903 const outputs_len = @truncate(u5, extended.small);
2904 if (outputs_len > 1) {
2893 const extra = self.air.extraData(Air.Asm, ty_pl.payload);
2894 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
2895 const clobbers_len = @truncate(u31, extra.data.flags);
2896 var extra_i: usize = extra.end;
2897
2898 if (!is_volatile and self.liveness.isUnused(inst)) return null;
2899
2900 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
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) {
29052906 return self.todo("implement llvm codegen for asm with more than 1 output", .{});
29062907 }
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
29242909 var llvm_constraints: std.ArrayListUnmanaged(u8) = .{};
29252910 defer llvm_constraints.deinit(self.gpa);
......@@ -2928,14 +2913,21 @@ pub const FuncGen = struct {
29282913 defer arena_allocator.deinit();
29292914 const arena = arena_allocator.allocator();
29302915
2931 const llvm_params_len = args.len;
2916 const llvm_params_len = inputs.len;
29322917 const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);
29332918 const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);
2934
29352919 var llvm_param_i: usize = 0;
29362920 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
29392931 try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);
29402932 if (total_i != 0) {
29412933 llvm_constraints.appendAssumeCapacity(',');
......@@ -2946,11 +2938,13 @@ pub const FuncGen = struct {
29462938 total_i += 1;
29472939 }
29482940
2949 for (args) |arg| {
2950 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
2951 extra_i = input.end;
2952 const constraint = zir.nullTerminatedString(input.data.constraint);
2953 const arg_llvm_value = try self.resolveInst(arg);
2941 for (inputs) |input| {
2942 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2943 // This equation accounts for the fact that even if we have exactly 4 bytes
2944 // for the string, we still use the next u32 for the null terminator.
2945 extra_i += constraint.len / 4 + 1;
2946
2947 const arg_llvm_value = try self.resolveInst(input);
29542948
29552949 llvm_param_values[llvm_param_i] = arg_llvm_value;
29562950 llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf();
......@@ -2965,19 +2959,26 @@ pub const FuncGen = struct {
29652959 total_i += 1;
29662960 }
29672961
2968 const clobbers = zir.extra[extra_i..][0..clobbers_len];
2969 for (clobbers) |clobber_index| {
2970 const clobber = zir.nullTerminatedString(clobber_index);
2971 try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
2972 if (total_i != 0) {
2973 llvm_constraints.appendAssumeCapacity(',');
2974 }
2975 llvm_constraints.appendSliceAssumeCapacity("~{");
2976 llvm_constraints.appendSliceAssumeCapacity(clobber);
2977 llvm_constraints.appendSliceAssumeCapacity("}");
2962 {
2963 var clobber_i: u32 = 0;
2964 while (clobber_i < clobbers_len) : (clobber_i += 1) {
2965 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
2966 // This equation accounts for the fact that even if we have exactly 4 bytes
2967 // for the string, we still use the next u32 for the null terminator.
2968 extra_i += clobber.len / 4 + 1;
2969
2970 try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
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 }
29802980 }
2981 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
29812982
29822983 const ret_ty = self.air.typeOfIndex(inst);
29832984 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;
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
......@@ -431,51 +428,67 @@ const Writer = struct {
431428
432429 fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
433430 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
434 const air_asm = w.air.extraData(Air.Asm, ty_pl.payload);
435 const zir = w.zir;
436 const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;
437 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
438 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
439 const outputs_len = @truncate(u5, extended.small);
440 const args_len = @truncate(u5, extended.small >> 5);
441 const clobbers_len = @truncate(u5, extended.small >> 10);
442 const args = @bitCast([]const Air.Inst.Ref, w.air.extra[air_asm.end..][0..args_len]);
443
444 var extra_i: usize = zir_extra.end;
445 const output_constraint: ?[]const u8 = out: {
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 };
431 const extra = w.air.extraData(Air.Asm, ty_pl.payload);
432 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
433 const clobbers_len = @truncate(u31, extra.data.flags);
434 var extra_i: usize = extra.end;
435 var op_index: usize = 0;
436
437 const ret_ty = w.air.typeOfIndex(inst);
438 try s.print("{}", .{ret_ty});
439
440 if (is_volatile) {
441 try s.writeAll(", volatile");
442 }
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| {
458 const ret_ty = w.air.typeOfIndex(inst);
459 try s.print(", {s} -> {}", .{ constraint, ret_ty });
449 for (outputs) |output| {
450 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
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 }
460463 }
461464
462 for (args) |arg| {
463 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
464 extra_i = input.end;
465 const constraint = zir.nullTerminatedString(input.data.constraint);
465 for (inputs) |input| {
466 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
467 // This equation accounts for the fact that even if we have exactly 4 bytes
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});
468 try w.writeOperand(s, inst, 0, arg);
471 try s.print(", in {s} = (", .{constraint});
472 try w.writeOperand(s, inst, op_index, input);
473 op_index += 1;
469474 try s.writeByte(')');
470475 }
471476
472 const clobbers = zir.extra[extra_i..][0..clobbers_len];
473 for (clobbers) |clobber_index| {
474 const clobber = zir.nullTerminatedString(clobber_index);
475 try s.writeAll(", ~{");
476 try s.writeAll(clobber);
477 try s.writeAll("}");
477 {
478 var clobber_i: u32 = 0;
479 while (clobber_i < clobbers_len) : (clobber_i += 1) {
480 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
481 // This equation accounts for the fact that even if we have exactly 4 bytes
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 }
478489 }
490 const asm_source = std.mem.sliceAsBytes(w.air.extra[extra_i..])[0..extra.data.source_len];
491 try s.print(", \"{s}\"", .{asm_source});
479492 }
480493
481494 fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {