| author | |
| committer | |
| log | 4e1e5ab6221b72ef2be9f1fb40c2e6d1235718fe |
| tree | 2210dfbf4e6d445b6f3e6cdba4e6d18e5a41cbde |
| parent | 123076ea88a809888692b308ab5bb214dc3a2caf |
Instead it stores all the information it needs to into AIR.
closes #1078411 files changed, 506 insertions(+), 351 deletions(-)
src/Air.zig+18-4| ... | ... | @@ -697,11 +697,25 @@ pub const Bin = struct { |
| 697 | 697 | /// Trailing: |
| 698 | 698 | /// 0. `Inst.Ref` for every outputs_len |
| 699 | 699 | /// 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. | |
| 700 | 711 | pub 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, | |
| 705 | 719 | }; |
| 706 | 720 | |
| 707 | 721 | pub const Cmpxchg = struct { |
src/Compilation.zig+2-2| ... | ... | @@ -2778,7 +2778,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2778 | 2778 | errdefer if (!liveness_frame_ended) liveness_frame.end(); |
| 2779 | 2779 | |
| 2780 | 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 | 2782 | defer liveness.deinit(gpa); |
| 2783 | 2783 | |
| 2784 | 2784 | liveness_frame.end(); |
| ... | ... | @@ -2786,7 +2786,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2786 | 2786 | |
| 2787 | 2787 | if (builtin.mode == .Debug and comp.verbose_air) { |
| 2788 | 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 | 2790 | std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); |
| 2791 | 2791 | } |
| 2792 | 2792 |
src/Liveness.zig+23-15| ... | ... | @@ -12,7 +12,6 @@ const log = std.log.scoped(.liveness); |
| 12 | 12 | const assert = std.debug.assert; |
| 13 | 13 | const Allocator = std.mem.Allocator; |
| 14 | 14 | const Air = @import("Air.zig"); |
| 15 | const Zir = @import("Zir.zig"); | |
| 16 | 15 | const Log2Int = std.math.Log2Int; |
| 17 | 16 | |
| 18 | 17 | /// This array is split into sets of 4 bits per AIR instruction. |
| ... | ... | @@ -52,7 +51,7 @@ pub const SwitchBr = struct { |
| 52 | 51 | else_death_count: u32, |
| 53 | 52 | }; |
| 54 | 53 | |
| 55 | pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness { | |
| 54 | pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness { | |
| 56 | 55 | const tracy = trace(@src()); |
| 57 | 56 | defer tracy.end(); |
| 58 | 57 | |
| ... | ... | @@ -66,7 +65,6 @@ pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness { |
| 66 | 65 | ), |
| 67 | 66 | .extra = .{}, |
| 68 | 67 | .special = .{}, |
| 69 | .zir = &zir, | |
| 70 | 68 | }; |
| 71 | 69 | errdefer gpa.free(a.tomb_bits); |
| 72 | 70 | errdefer a.special.deinit(gpa); |
| ... | ... | @@ -157,7 +155,6 @@ const Analysis = struct { |
| 157 | 155 | tomb_bits: []usize, |
| 158 | 156 | special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32), |
| 159 | 157 | extra: std.ArrayListUnmanaged(u32), |
| 160 | zir: *const Zir, | |
| 161 | 158 | |
| 162 | 159 | fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void { |
| 163 | 160 | const usize_index = (inst * bpi) / @bitSizeOf(usize); |
| ... | ... | @@ -444,15 +441,24 @@ fn analyzeInst( |
| 444 | 441 | }, |
| 445 | 442 | .assembly => { |
| 446 | 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; | |
| 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: { | |
| 453 | 451 | 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); | |
| 456 | 462 | return trackOperands(a, new_set, inst, main_tomb, buf); |
| 457 | 463 | } |
| 458 | 464 | var extra_tombs: ExtraTombs = .{ |
| ... | ... | @@ -462,10 +468,12 @@ fn analyzeInst( |
| 462 | 468 | .main_tomb = main_tomb, |
| 463 | 469 | }; |
| 464 | 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| { | |
| 468 | try extra_tombs.feed(arg); | |
| 475 | for (inputs) |input| { | |
| 476 | try extra_tombs.feed(input); | |
| 469 | 477 | } |
| 470 | 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 | 1124 | .frame_address => return sema.zirFrameAddress( block, extended), |
| 1125 | 1125 | .alloc => return sema.zirAllocExtended( block, extended), |
| 1126 | 1126 | .builtin_extern => return sema.zirBuiltinExtern( block, extended), |
| 1127 | .@"asm" => return sema.zirAsm( block, extended, inst), | |
| 1127 | .@"asm" => return sema.zirAsm( block, extended), | |
| 1128 | 1128 | .typeof_peer => return sema.zirTypeofPeer( block, extended), |
| 1129 | 1129 | .compile_log => return sema.zirCompileLog( block, extended), |
| 1130 | 1130 | .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode), |
| ... | ... | @@ -9083,7 +9083,6 @@ fn zirAsm( |
| 9083 | 9083 | sema: *Sema, |
| 9084 | 9084 | block: *Block, |
| 9085 | 9085 | extended: Zir.Inst.Extended.InstData, |
| 9086 | inst: Zir.Inst.Index, | |
| 9087 | 9086 | ) CompileError!Air.Inst.Ref { |
| 9088 | 9087 | const tracy = trace(@src()); |
| 9089 | 9088 | defer tracy.end(); |
| ... | ... | @@ -9094,6 +9093,7 @@ fn zirAsm( |
| 9094 | 9093 | const outputs_len = @truncate(u5, extended.small); |
| 9095 | 9094 | const inputs_len = @truncate(u5, extended.small >> 5); |
| 9096 | 9095 | const clobbers_len = @truncate(u5, extended.small >> 10); |
| 9096 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | |
| 9097 | 9097 | |
| 9098 | 9098 | if (extra.data.asm_source == 0) { |
| 9099 | 9099 | // This can move to become an AstGen error after inline assembly improvements land |
| ... | ... | @@ -9107,6 +9107,7 @@ fn zirAsm( |
| 9107 | 9107 | |
| 9108 | 9108 | var extra_i = extra.end; |
| 9109 | 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; | |
| 9110 | 9111 | |
| 9111 | 9112 | const Output = struct { constraint: []const u8, ty: Type }; |
| 9112 | 9113 | const output: ?Output = if (outputs_len == 0) null else blk: { |
| ... | ... | @@ -9121,6 +9122,8 @@ fn zirAsm( |
| 9121 | 9122 | } |
| 9122 | 9123 | |
| 9123 | 9124 | const constraint = sema.code.nullTerminatedString(output.data.constraint); |
| 9125 | needed_capacity += constraint.len / 4 + 1; | |
| 9126 | ||
| 9124 | 9127 | break :blk Output{ |
| 9125 | 9128 | .constraint = constraint, |
| 9126 | 9129 | .ty = try sema.resolveType(block, ret_ty_src, output.data.operand), |
| ... | ... | @@ -9138,28 +9141,65 @@ fn zirAsm( |
| 9138 | 9141 | _ = name; // TODO: use the name |
| 9139 | 9142 | |
| 9140 | 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 | } |
| 9143 | 9148 | |
| 9144 | 9149 | const clobbers = try sema.arena.alloc([]const u8, clobbers_len); |
| 9145 | 9150 | for (clobbers) |*name| { |
| 9146 | 9151 | name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); |
| 9147 | 9152 | extra_i += 1; |
| 9153 | ||
| 9154 | needed_capacity += name.*.len / 4 + 1; | |
| 9148 | 9155 | } |
| 9149 | 9156 | |
| 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 | 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 | 9163 | const asm_air = try block.addInst(.{ |
| 9154 | 9164 | .tag = .assembly, |
| 9155 | 9165 | .data = .{ .ty_pl = .{ |
| 9156 | 9166 | .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type, |
| 9157 | 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 | 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 | 9203 | return asm_air; |
| 9164 | 9204 | } |
| 9165 | 9205 |
src/arch/aarch64/CodeGen.zig+63-70| ... | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | 4 | const math = std.math; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const Air = @import("../../Air.zig"); |
| 7 | const Zir = @import("../../Zir.zig"); | |
| 8 | 7 | const Mir = @import("Mir.zig"); |
| 9 | 8 | const Emit = @import("Emit.zig"); |
| 10 | 9 | const Liveness = @import("../../Liveness.zig"); |
| ... | ... | @@ -2007,36 +2006,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2007 | 2006 | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); |
| 2008 | 2007 | } |
| 2009 | 2008 | |
| 2010 | fn 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 | ||
| 2040 | 2009 | fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2041 | 2010 | const arg_index = self.arg_index; |
| 2042 | 2011 | self.arg_index += 1; |
| ... | ... | @@ -2852,40 +2821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 2852 | 2821 | } |
| 2853 | 2822 | |
| 2854 | 2823 | fn 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; | |
| 2882 | 2833 | |
| 2883 | 2834 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 2884 | 2835 | 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; | |
| 2889 | 2857 | |
| 2890 | 2858 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 2891 | 2859 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | ... | @@ -2894,11 +2862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2894 | 2862 | const reg = parseRegName(reg_name) orelse |
| 2895 | 2863 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 2896 | 2864 | |
| 2897 | const arg_mcv = try self.resolveInst(arg); | |
| 2865 | const arg_mcv = try self.resolveInst(input); | |
| 2898 | 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 | } |
| 2901 | 2881 | |
| 2882 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 2883 | ||
| 2902 | 2884 | if (mem.eql(u8, asm_source, "svc #0")) { |
| 2903 | 2885 | _ = try self.addInst(.{ |
| 2904 | 2886 | .tag = .svc, |
| ... | ... | @@ -2925,18 +2907,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2925 | 2907 | break :result MCValue{ .none = {} }; |
| 2926 | 2908 | } |
| 2927 | 2909 | }; |
| 2928 | if (outputs.len + args.len <= Liveness.bpi - 1) { | |
| 2910 | ||
| 2911 | simple: { | |
| 2929 | 2912 | 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); | |
| 2932 | 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 | 2926 | for (outputs) |output| { |
| 2927 | if (output == .none) continue; | |
| 2928 | ||
| 2936 | 2929 | bt.feed(output); |
| 2937 | 2930 | } |
| 2938 | for (args) |arg| { | |
| 2939 | bt.feed(arg); | |
| 2931 | for (inputs) |input| { | |
| 2932 | bt.feed(input); | |
| 2940 | 2933 | } |
| 2941 | 2934 | return bt.finishAir(result); |
| 2942 | 2935 | } |
src/arch/arm/CodeGen.zig+63-40| ... | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | 4 | const math = std.math; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const Air = @import("../../Air.zig"); |
| 7 | const Zir = @import("../../Zir.zig"); | |
| 8 | 7 | const Mir = @import("Mir.zig"); |
| 9 | 8 | const Emit = @import("Emit.zig"); |
| 10 | 9 | const Liveness = @import("../../Liveness.zig"); |
| ... | ... | @@ -3059,40 +3058,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 3059 | 3058 | } |
| 3060 | 3059 | |
| 3061 | 3060 | fn 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; | |
| 3089 | 3070 | |
| 3090 | 3071 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 3091 | 3072 | 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; | |
| 3096 | 3094 | |
| 3097 | 3095 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 3098 | 3096 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | ... | @@ -3101,11 +3099,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3101 | 3099 | const reg = parseRegName(reg_name) orelse |
| 3102 | 3100 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 3103 | 3101 | |
| 3104 | const arg_mcv = try self.resolveInst(arg); | |
| 3102 | const arg_mcv = try self.resolveInst(input); | |
| 3105 | 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 | } |
| 3108 | 3118 | |
| 3119 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 3120 | ||
| 3109 | 3121 | if (mem.eql(u8, asm_source, "svc #0")) { |
| 3110 | 3122 | _ = try self.addInst(.{ |
| 3111 | 3123 | .tag = .svc, |
| ... | ... | @@ -3128,18 +3140,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3128 | 3140 | break :result MCValue{ .none = {} }; |
| 3129 | 3141 | } |
| 3130 | 3142 | }; |
| 3131 | if (outputs.len + args.len <= Liveness.bpi - 1) { | |
| 3143 | ||
| 3144 | simple: { | |
| 3132 | 3145 | 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); | |
| 3135 | 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 | 3159 | for (outputs) |output| { |
| 3160 | if (output == .none) continue; | |
| 3161 | ||
| 3139 | 3162 | bt.feed(output); |
| 3140 | 3163 | } |
| 3141 | for (args) |arg| { | |
| 3142 | bt.feed(arg); | |
| 3164 | for (inputs) |input| { | |
| 3165 | bt.feed(input); | |
| 3143 | 3166 | } |
| 3144 | 3167 | return bt.finishAir(result); |
| 3145 | 3168 | } |
src/arch/riscv64/CodeGen.zig+62-40| ... | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | 4 | const math = std.math; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const Air = @import("../../Air.zig"); |
| 7 | const Zir = @import("../../Zir.zig"); | |
| 8 | 7 | const Mir = @import("Mir.zig"); |
| 9 | 8 | const Emit = @import("Emit.zig"); |
| 10 | 9 | const Liveness = @import("../../Liveness.zig"); |
| ... | ... | @@ -1822,40 +1821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 1822 | 1821 | } |
| 1823 | 1822 | |
| 1824 | 1823 | fn 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; | |
| 1852 | 1833 | |
| 1853 | 1834 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 1854 | 1835 | 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; | |
| 1859 | 1857 | |
| 1860 | 1858 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 1861 | 1859 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | ... | @@ -1864,11 +1862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 1864 | 1862 | const reg = parseRegName(reg_name) orelse |
| 1865 | 1863 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 1866 | 1864 | |
| 1867 | const arg_mcv = try self.resolveInst(arg); | |
| 1865 | const arg_mcv = try self.resolveInst(input); | |
| 1868 | 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 | } |
| 1871 | 1869 | |
| 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 | 1884 | if (mem.eql(u8, asm_source, "ecall")) { |
| 1873 | 1885 | _ = try self.addInst(.{ |
| 1874 | 1886 | .tag = .ecall, |
| ... | ... | @@ -1890,18 +1902,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 1890 | 1902 | break :result MCValue{ .none = {} }; |
| 1891 | 1903 | } |
| 1892 | 1904 | }; |
| 1893 | if (outputs.len + args.len <= Liveness.bpi - 1) { | |
| 1905 | simple: { | |
| 1894 | 1906 | 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); | |
| 1897 | 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 | 1920 | for (outputs) |output| { |
| 1921 | if (output == .none) continue; | |
| 1922 | ||
| 1901 | 1923 | bt.feed(output); |
| 1902 | 1924 | } |
| 1903 | for (args) |arg| { | |
| 1904 | bt.feed(arg); | |
| 1925 | for (inputs) |input| { | |
| 1926 | bt.feed(input); | |
| 1905 | 1927 | } |
| 1906 | 1928 | return bt.finishAir(result); |
| 1907 | 1929 | } |
src/arch/x86_64/CodeGen.zig+69-43| ... | ... | @@ -26,7 +26,6 @@ const Target = std.Target; |
| 26 | 26 | const Type = @import("../../type.zig").Type; |
| 27 | 27 | const TypedValue = @import("../../TypedValue.zig"); |
| 28 | 28 | const Value = @import("../../value.zig").Value; |
| 29 | const Zir = @import("../../Zir.zig"); | |
| 30 | 29 | |
| 31 | 30 | const InnerError = error{ |
| 32 | 31 | OutOfMemory, |
| ... | ... | @@ -3415,41 +3414,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 3415 | 3414 | } |
| 3416 | 3415 | |
| 3417 | 3416 | fn 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; | |
| 3444 | 3426 | |
| 3445 | 3427 | 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; | |
| 3453 | 3450 | |
| 3454 | 3451 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 3455 | 3452 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | ... | @@ -3458,11 +3455,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3458 | 3455 | const reg = parseRegName(reg_name) orelse |
| 3459 | 3456 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 3460 | 3457 | |
| 3461 | const arg_mcv = try self.resolveInst(arg); | |
| 3458 | const arg_mcv = try self.resolveInst(input); | |
| 3462 | 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 | } |
| 3465 | 3474 | |
| 3475 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 3476 | ||
| 3466 | 3477 | { |
| 3467 | 3478 | var iter = std.mem.tokenize(u8, asm_source, "\n\r"); |
| 3468 | 3479 | while (iter.next()) |ins| { |
| ... | ... | @@ -3529,14 +3540,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3529 | 3540 | break :result MCValue{ .none = {} }; |
| 3530 | 3541 | } |
| 3531 | 3542 | }; |
| 3532 | if (args.len <= Liveness.bpi - 1) { | |
| 3543 | ||
| 3544 | simple: { | |
| 3533 | 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 | 3556 | return self.finishAir(inst, result, buf); |
| 3536 | 3557 | } |
| 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); | |
| 3540 | 3566 | } |
| 3541 | 3567 | return bt.finishAir(result); |
| 3542 | 3568 | } |
| ... | ... | @@ -3615,7 +3641,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 3615 | 3641 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3616 | 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 | 3647 | .embedded_in_code => { |
| ... | ... | @@ -3623,7 +3649,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 3623 | 3649 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3624 | 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 | 3654 | .memory, |
| 3629 | 3655 | .direct_load, |
src/codegen/c.zig+56-41| ... | ... | @@ -15,7 +15,6 @@ const Decl = Module.Decl; |
| 15 | 15 | const trace = @import("../tracy.zig").trace; |
| 16 | 16 | const LazySrcLoc = Module.LazySrcLoc; |
| 17 | 17 | const Air = @import("../Air.zig"); |
| 18 | const Zir = @import("../Zir.zig"); | |
| 19 | 18 | const Liveness = @import("../Liveness.zig"); |
| 20 | 19 | |
| 21 | 20 | const Mutability = enum { Const, Mut }; |
| ... | ... | @@ -2805,49 +2804,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2805 | 2804 | } |
| 2806 | 2805 | |
| 2807 | 2806 | fn 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) { | |
| 2823 | 2820 | return f.fail("TODO implement codegen for asm with more than 1 output", .{}); |
| 2824 | 2821 | } |
| 2825 | 2822 | |
| 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", .{}); | |
| 2836 | 2826 | } |
| 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; | |
| 2840 | 2834 | |
| 2841 | 2835 | 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 | ||
| 2846 | 2844 | if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { |
| 2847 | 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 | 2847 | try writer.writeAll("register "); |
| 2850 | try f.renderType(writer, f.air.typeOf(arg)); | |
| 2848 | try f.renderType(writer, f.air.typeOf(input)); | |
| 2851 | 2849 | |
| 2852 | 2850 | try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); |
| 2853 | 2851 | try f.writeCValue(writer, arg_c_value); |
| ... | ... | @@ -2856,21 +2854,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2856 | 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 | 2872 | const volatile_string: []const u8 = if (is_volatile) "volatile " else ""; |
| 2860 | 2873 | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source }); |
| 2861 | 2874 | if (output_constraint) |_| { |
| 2862 | 2875 | return f.fail("TODO: CBE inline asm output", .{}); |
| 2863 | 2876 | } |
| 2864 | if (args.len > 0) { | |
| 2877 | if (inputs.len > 0) { | |
| 2865 | 2878 | if (output_constraint == null) { |
| 2866 | 2879 | try writer.writeAll(" :"); |
| 2867 | 2880 | } |
| 2868 | 2881 | 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 | ||
| 2874 | 2889 | if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { |
| 2875 | 2890 | const reg = constraint[1 .. constraint.len - 1]; |
| 2876 | 2891 | if (index > 0) { |
src/codegen/llvm.zig+51-50| ... | ... | @@ -2,24 +2,21 @@ const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const Allocator = std.mem.Allocator; |
| 5 | const Compilation = @import("../Compilation.zig"); | |
| 6 | const llvm = @import("llvm/bindings.zig"); | |
| 7 | const link = @import("../link.zig"); | |
| 8 | 5 | const log = std.log.scoped(.codegen); |
| 9 | 6 | const math = std.math; |
| 10 | 7 | const native_endian = builtin.cpu.arch.endian(); |
| 11 | 8 | |
| 9 | const llvm = @import("llvm/bindings.zig"); | |
| 10 | const link = @import("../link.zig"); | |
| 11 | const Compilation = @import("../Compilation.zig"); | |
| 12 | 12 | const build_options = @import("build_options"); |
| 13 | 13 | const Module = @import("../Module.zig"); |
| 14 | 14 | const TypedValue = @import("../TypedValue.zig"); |
| 15 | const Zir = @import("../Zir.zig"); | |
| 16 | 15 | const Air = @import("../Air.zig"); |
| 17 | 16 | const Liveness = @import("../Liveness.zig"); |
| 18 | 17 | const target_util = @import("../target.zig"); |
| 19 | ||
| 20 | 18 | const Value = @import("../value.zig").Value; |
| 21 | 19 | const Type = @import("../type.zig").Type; |
| 22 | ||
| 23 | 20 | const LazySrcLoc = Module.LazySrcLoc; |
| 24 | 21 | |
| 25 | 22 | const Error = error{ OutOfMemory, CodegenFail }; |
| ... | ... | @@ -2893,33 +2890,21 @@ pub const FuncGen = struct { |
| 2893 | 2890 | // as stage1. |
| 2894 | 2891 | |
| 2895 | 2892 | 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) { | |
| 2905 | 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 | }; | |
| 2923 | 2908 | |
| 2924 | 2909 | var llvm_constraints: std.ArrayListUnmanaged(u8) = .{}; |
| 2925 | 2910 | defer llvm_constraints.deinit(self.gpa); |
| ... | ... | @@ -2928,14 +2913,21 @@ pub const FuncGen = struct { |
| 2928 | 2913 | defer arena_allocator.deinit(); |
| 2929 | 2914 | const arena = arena_allocator.allocator(); |
| 2930 | 2915 | |
| 2931 | const llvm_params_len = args.len; | |
| 2916 | const llvm_params_len = inputs.len; | |
| 2932 | 2917 | const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len); |
| 2933 | 2918 | const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len); |
| 2934 | ||
| 2935 | 2919 | var llvm_param_i: usize = 0; |
| 2936 | 2920 | var total_i: usize = 0; |
| 2937 | 2921 | |
| 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 | 2931 | try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); |
| 2940 | 2932 | if (total_i != 0) { |
| 2941 | 2933 | llvm_constraints.appendAssumeCapacity(','); |
| ... | ... | @@ -2946,11 +2938,13 @@ pub const FuncGen = struct { |
| 2946 | 2938 | total_i += 1; |
| 2947 | 2939 | } |
| 2948 | 2940 | |
| 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); | |
| 2954 | 2948 | |
| 2955 | 2949 | llvm_param_values[llvm_param_i] = arg_llvm_value; |
| 2956 | 2950 | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); |
| ... | ... | @@ -2965,19 +2959,26 @@ pub const FuncGen = struct { |
| 2965 | 2959 | total_i += 1; |
| 2966 | 2960 | } |
| 2967 | 2961 | |
| 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("}"); | |
| 2978 | 2977 | |
| 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]; | |
| 2981 | 2982 | |
| 2982 | 2983 | const ret_ty = self.air.typeOfIndex(inst); |
| 2983 | 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 | 4 | |
| 5 | 5 | const Module = @import("Module.zig"); |
| 6 | 6 | const Value = @import("value.zig").Value; |
| 7 | const Zir = @import("Zir.zig"); | |
| 8 | 7 | const Air = @import("Air.zig"); |
| 9 | 8 | const Liveness = @import("Liveness.zig"); |
| 10 | 9 | |
| 11 | pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void { | |
| 10 | pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void { | |
| 12 | 11 | const instruction_bytes = air.instructions.len * |
| 13 | 12 | // Here we don't use @sizeOf(Air.Inst.Data) because it would include |
| 14 | 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 | 48 | .gpa = gpa, |
| 50 | 49 | .arena = arena.allocator(), |
| 51 | 50 | .air = air, |
| 52 | .zir = zir, | |
| 53 | 51 | .liveness = liveness, |
| 54 | 52 | .indent = 2, |
| 55 | 53 | }; |
| ... | ... | @@ -63,7 +61,6 @@ const Writer = struct { |
| 63 | 61 | gpa: Allocator, |
| 64 | 62 | arena: Allocator, |
| 65 | 63 | air: Air, |
| 66 | zir: Zir, | |
| 67 | 64 | liveness: Liveness, |
| 68 | 65 | indent: usize, |
| 69 | 66 | |
| ... | ... | @@ -431,51 +428,67 @@ const Writer = struct { |
| 431 | 428 | |
| 432 | 429 | fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 433 | 430 | 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 | } | |
| 454 | 443 | |
| 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; | |
| 456 | 448 | |
| 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 | } | |
| 460 | 463 | } |
| 461 | 464 | |
| 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; | |
| 466 | 470 | |
| 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; | |
| 469 | 474 | try s.writeByte(')'); |
| 470 | 475 | } |
| 471 | 476 | |
| 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 | } | |
| 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 | } |
| 480 | 493 | |
| 481 | 494 | fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |