| 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,11 +697,25 @@ pub const Bin = struct { |
| 697 | /// Trailing: | 697 | /// Trailing: |
| 698 | /// 0. `Inst.Ref` for every outputs_len | 698 | /// 0. `Inst.Ref` for every outputs_len |
| 699 | /// 1. `Inst.Ref` for every inputs_len | 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 | pub const Asm = struct { | 711 | pub const Asm = struct { |
| 701 | /// Index to the corresponding ZIR instruction. | 712 | /// Length of the assembly source in bytes. |
| 702 | /// `asm_source`, `outputs_len`, `inputs_len`, `clobbers_len`, `is_volatile`, and | 713 | source_len: u32, |
| 703 | /// clobbers are found via here. | 714 | outputs_len: u32, |
| 704 | zir_index: u32, | 715 | inputs_len: u32, |
| 716 | /// The MSB is `is_volatile`. | ||
| 717 | /// The rest of the bits are `clobbers_len`. | ||
| 718 | flags: u32, | ||
| 705 | }; | 719 | }; |
| 706 | 720 | ||
| 707 | pub const Cmpxchg = struct { | 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,7 +2778,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2778 | errdefer if (!liveness_frame_ended) liveness_frame.end(); | 2778 | errdefer if (!liveness_frame_ended) liveness_frame.end(); |
| 2779 | 2779 | ||
| 2780 | log.debug("analyze liveness of {s}", .{decl.name}); | 2780 | log.debug("analyze liveness of {s}", .{decl.name}); |
| 2781 | var liveness = try Liveness.analyze(gpa, air, decl.getFileScope().zir); | 2781 | var liveness = try Liveness.analyze(gpa, air); |
| 2782 | defer liveness.deinit(gpa); | 2782 | defer liveness.deinit(gpa); |
| 2783 | 2783 | ||
| 2784 | liveness_frame.end(); | 2784 | liveness_frame.end(); |
| ... | @@ -2786,7 +2786,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress | ... | @@ -2786,7 +2786,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2786 | 2786 | ||
| 2787 | if (builtin.mode == .Debug and comp.verbose_air) { | 2787 | if (builtin.mode == .Debug and comp.verbose_air) { |
| 2788 | std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); | 2788 | std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); |
| 2789 | @import("print_air.zig").dump(gpa, air, decl.getFileScope().zir, liveness); | 2789 | @import("print_air.zig").dump(gpa, air, liveness); |
| 2790 | std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); | 2790 | std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); |
| 2791 | } | 2791 | } |
| 2792 | 2792 |
src/Liveness.zig+23-15| ... | @@ -12,7 +12,6 @@ const log = std.log.scoped(.liveness); | ... | @@ -12,7 +12,6 @@ const log = std.log.scoped(.liveness); |
| 12 | const assert = std.debug.assert; | 12 | const assert = std.debug.assert; |
| 13 | const Allocator = std.mem.Allocator; | 13 | const Allocator = std.mem.Allocator; |
| 14 | const Air = @import("Air.zig"); | 14 | const Air = @import("Air.zig"); |
| 15 | const Zir = @import("Zir.zig"); | ||
| 16 | const Log2Int = std.math.Log2Int; | 15 | const Log2Int = std.math.Log2Int; |
| 17 | 16 | ||
| 18 | /// This array is split into sets of 4 bits per AIR instruction. | 17 | /// This array is split into sets of 4 bits per AIR instruction. |
| ... | @@ -52,7 +51,7 @@ pub const SwitchBr = struct { | ... | @@ -52,7 +51,7 @@ pub const SwitchBr = struct { |
| 52 | else_death_count: u32, | 51 | else_death_count: u32, |
| 53 | }; | 52 | }; |
| 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 | const tracy = trace(@src()); | 55 | const tracy = trace(@src()); |
| 57 | defer tracy.end(); | 56 | defer tracy.end(); |
| 58 | 57 | ||
| ... | @@ -66,7 +65,6 @@ pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness { | ... | @@ -66,7 +65,6 @@ pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness { |
| 66 | ), | 65 | ), |
| 67 | .extra = .{}, | 66 | .extra = .{}, |
| 68 | .special = .{}, | 67 | .special = .{}, |
| 69 | .zir = &zir, | ||
| 70 | }; | 68 | }; |
| 71 | errdefer gpa.free(a.tomb_bits); | 69 | errdefer gpa.free(a.tomb_bits); |
| 72 | errdefer a.special.deinit(gpa); | 70 | errdefer a.special.deinit(gpa); |
| ... | @@ -157,7 +155,6 @@ const Analysis = struct { | ... | @@ -157,7 +155,6 @@ const Analysis = struct { |
| 157 | tomb_bits: []usize, | 155 | tomb_bits: []usize, |
| 158 | special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32), | 156 | special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32), |
| 159 | extra: std.ArrayListUnmanaged(u32), | 157 | extra: std.ArrayListUnmanaged(u32), |
| 160 | zir: *const Zir, | ||
| 161 | 158 | ||
| 162 | fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void { | 159 | fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void { |
| 163 | const usize_index = (inst * bpi) / @bitSizeOf(usize); | 160 | const usize_index = (inst * bpi) / @bitSizeOf(usize); |
| ... | @@ -444,15 +441,24 @@ fn analyzeInst( | ... | @@ -444,15 +441,24 @@ fn analyzeInst( |
| 444 | }, | 441 | }, |
| 445 | .assembly => { | 442 | .assembly => { |
| 446 | const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload); | 443 | const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload); |
| 447 | const extended = a.zir.instructions.items(.data)[extra.data.zir_index].extended; | 444 | var extra_i: usize = extra.end; |
| 448 | const outputs_len = @truncate(u5, extended.small); | 445 | const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]); |
| 449 | const inputs_len = @truncate(u5, extended.small >> 5); | 446 | extra_i += outputs.len; |
| 450 | const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]); | 447 | const inputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 451 | const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]); | 448 | extra_i += inputs.len; |
| 452 | if (outputs.len + args.len <= bpi - 1) { | 449 | |
| 450 | simple: { | ||
| 453 | var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); | 451 | var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); |
| 454 | std.mem.copy(Air.Inst.Ref, &buf, outputs); | 452 | var buf_index: usize = 0; |
| 455 | std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); | 453 | for (outputs) |output| { |
| 454 | if (output != .none) { | ||
| 455 | if (buf_index >= buf.len) break :simple; | ||
| 456 | buf[buf_index] = output; | ||
| 457 | buf_index += 1; | ||
| 458 | } | ||
| 459 | } | ||
| 460 | if (buf_index + inputs.len > buf.len) break :simple; | ||
| 461 | std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); | ||
| 456 | return trackOperands(a, new_set, inst, main_tomb, buf); | 462 | return trackOperands(a, new_set, inst, main_tomb, buf); |
| 457 | } | 463 | } |
| 458 | var extra_tombs: ExtraTombs = .{ | 464 | var extra_tombs: ExtraTombs = .{ |
| ... | @@ -462,10 +468,12 @@ fn analyzeInst( | ... | @@ -462,10 +468,12 @@ fn analyzeInst( |
| 462 | .main_tomb = main_tomb, | 468 | .main_tomb = main_tomb, |
| 463 | }; | 469 | }; |
| 464 | for (outputs) |output| { | 470 | for (outputs) |output| { |
| 465 | try extra_tombs.feed(output); | 471 | if (output != .none) { |
| 472 | try extra_tombs.feed(output); | ||
| 473 | } | ||
| 466 | } | 474 | } |
| 467 | for (args) |arg| { | 475 | for (inputs) |input| { |
| 468 | try extra_tombs.feed(arg); | 476 | try extra_tombs.feed(input); |
| 469 | } | 477 | } |
| 470 | return extra_tombs.finish(); | 478 | return extra_tombs.finish(); |
| 471 | }, | 479 | }, |
src/Sema.zig+46-6| ... | @@ -1124,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -1124,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 1124 | .frame_address => return sema.zirFrameAddress( block, extended), | 1124 | .frame_address => return sema.zirFrameAddress( block, extended), |
| 1125 | .alloc => return sema.zirAllocExtended( block, extended), | 1125 | .alloc => return sema.zirAllocExtended( block, extended), |
| 1126 | .builtin_extern => return sema.zirBuiltinExtern( block, extended), | 1126 | .builtin_extern => return sema.zirBuiltinExtern( block, extended), |
| 1127 | .@"asm" => return sema.zirAsm( block, extended, inst), | 1127 | .@"asm" => return sema.zirAsm( block, extended), |
| 1128 | .typeof_peer => return sema.zirTypeofPeer( block, extended), | 1128 | .typeof_peer => return sema.zirTypeofPeer( block, extended), |
| 1129 | .compile_log => return sema.zirCompileLog( block, extended), | 1129 | .compile_log => return sema.zirCompileLog( block, extended), |
| 1130 | .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode), | 1130 | .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode), |
| ... | @@ -9083,7 +9083,6 @@ fn zirAsm( | ... | @@ -9083,7 +9083,6 @@ fn zirAsm( |
| 9083 | sema: *Sema, | 9083 | sema: *Sema, |
| 9084 | block: *Block, | 9084 | block: *Block, |
| 9085 | extended: Zir.Inst.Extended.InstData, | 9085 | extended: Zir.Inst.Extended.InstData, |
| 9086 | inst: Zir.Inst.Index, | ||
| 9087 | ) CompileError!Air.Inst.Ref { | 9086 | ) CompileError!Air.Inst.Ref { |
| 9088 | const tracy = trace(@src()); | 9087 | const tracy = trace(@src()); |
| 9089 | defer tracy.end(); | 9088 | defer tracy.end(); |
| ... | @@ -9094,6 +9093,7 @@ fn zirAsm( | ... | @@ -9094,6 +9093,7 @@ fn zirAsm( |
| 9094 | const outputs_len = @truncate(u5, extended.small); | 9093 | const outputs_len = @truncate(u5, extended.small); |
| 9095 | const inputs_len = @truncate(u5, extended.small >> 5); | 9094 | const inputs_len = @truncate(u5, extended.small >> 5); |
| 9096 | const clobbers_len = @truncate(u5, extended.small >> 10); | 9095 | const clobbers_len = @truncate(u5, extended.small >> 10); |
| 9096 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | ||
| 9097 | 9097 | ||
| 9098 | if (extra.data.asm_source == 0) { | 9098 | if (extra.data.asm_source == 0) { |
| 9099 | // This can move to become an AstGen error after inline assembly improvements land | 9099 | // This can move to become an AstGen error after inline assembly improvements land |
| ... | @@ -9107,6 +9107,7 @@ fn zirAsm( | ... | @@ -9107,6 +9107,7 @@ fn zirAsm( |
| 9107 | 9107 | ||
| 9108 | var extra_i = extra.end; | 9108 | var extra_i = extra.end; |
| 9109 | var output_type_bits = extra.data.output_type_bits; | 9109 | var output_type_bits = extra.data.output_type_bits; |
| 9110 | var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len; | ||
| 9110 | 9111 | ||
| 9111 | const Output = struct { constraint: []const u8, ty: Type }; | 9112 | const Output = struct { constraint: []const u8, ty: Type }; |
| 9112 | const output: ?Output = if (outputs_len == 0) null else blk: { | 9113 | const output: ?Output = if (outputs_len == 0) null else blk: { |
| ... | @@ -9121,6 +9122,8 @@ fn zirAsm( | ... | @@ -9121,6 +9122,8 @@ fn zirAsm( |
| 9121 | } | 9122 | } |
| 9122 | 9123 | ||
| 9123 | const constraint = sema.code.nullTerminatedString(output.data.constraint); | 9124 | const constraint = sema.code.nullTerminatedString(output.data.constraint); |
| 9125 | needed_capacity += constraint.len / 4 + 1; | ||
| 9126 | |||
| 9124 | break :blk Output{ | 9127 | break :blk Output{ |
| 9125 | .constraint = constraint, | 9128 | .constraint = constraint, |
| 9126 | .ty = try sema.resolveType(block, ret_ty_src, output.data.operand), | 9129 | .ty = try sema.resolveType(block, ret_ty_src, output.data.operand), |
| ... | @@ -9138,28 +9141,65 @@ fn zirAsm( | ... | @@ -9138,28 +9141,65 @@ fn zirAsm( |
| 9138 | _ = name; // TODO: use the name | 9141 | _ = name; // TODO: use the name |
| 9139 | 9142 | ||
| 9140 | arg.* = sema.resolveInst(input.data.operand); | 9143 | arg.* = sema.resolveInst(input.data.operand); |
| 9141 | inputs[arg_i] = sema.code.nullTerminatedString(input.data.constraint); | 9144 | const constraint = sema.code.nullTerminatedString(input.data.constraint); |
| 9145 | needed_capacity += constraint.len / 4 + 1; | ||
| 9146 | inputs[arg_i] = constraint; | ||
| 9142 | } | 9147 | } |
| 9143 | 9148 | ||
| 9144 | const clobbers = try sema.arena.alloc([]const u8, clobbers_len); | 9149 | const clobbers = try sema.arena.alloc([]const u8, clobbers_len); |
| 9145 | for (clobbers) |*name| { | 9150 | for (clobbers) |*name| { |
| 9146 | name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | 9151 | name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); |
| 9147 | extra_i += 1; | 9152 | extra_i += 1; |
| 9153 | |||
| 9154 | needed_capacity += name.*.len / 4 + 1; | ||
| 9148 | } | 9155 | } |
| 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 | const gpa = sema.gpa; | 9160 | const gpa = sema.gpa; |
| 9152 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Asm).Struct.fields.len + args.len); | 9161 | try sema.requireRuntimeBlock(block, src); |
| 9162 | try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); | ||
| 9153 | const asm_air = try block.addInst(.{ | 9163 | const asm_air = try block.addInst(.{ |
| 9154 | .tag = .assembly, | 9164 | .tag = .assembly, |
| 9155 | .data = .{ .ty_pl = .{ | 9165 | .data = .{ .ty_pl = .{ |
| 9156 | .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type, | 9166 | .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type, |
| 9157 | .payload = sema.addExtraAssumeCapacity(Air.Asm{ | 9167 | .payload = sema.addExtraAssumeCapacity(Air.Asm{ |
| 9158 | .zir_index = inst, | 9168 | .source_len = @intCast(u32, asm_source.len), |
| 9169 | .outputs_len = outputs_len, | ||
| 9170 | .inputs_len = @intCast(u32, args.len), | ||
| 9171 | .flags = (@as(u32, @boolToInt(is_volatile)) << 31) | @intCast(u32, clobbers.len), | ||
| 9159 | }), | 9172 | }), |
| 9160 | } }, | 9173 | } }, |
| 9161 | }); | 9174 | }); |
| 9175 | if (output != null) { | ||
| 9176 | // Indicate the output is the asm instruction return value. | ||
| 9177 | sema.air_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.none)); | ||
| 9178 | } | ||
| 9162 | sema.appendRefsAssumeCapacity(args); | 9179 | sema.appendRefsAssumeCapacity(args); |
| 9180 | if (output) |o| { | ||
| 9181 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); | ||
| 9182 | mem.copy(u8, buffer, o.constraint); | ||
| 9183 | buffer[o.constraint.len] = 0; | ||
| 9184 | sema.air_extra.items.len += o.constraint.len / 4 + 1; | ||
| 9185 | } | ||
| 9186 | for (inputs) |constraint| { | ||
| 9187 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); | ||
| 9188 | mem.copy(u8, buffer, constraint); | ||
| 9189 | buffer[constraint.len] = 0; | ||
| 9190 | sema.air_extra.items.len += constraint.len / 4 + 1; | ||
| 9191 | } | ||
| 9192 | for (clobbers) |clobber| { | ||
| 9193 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); | ||
| 9194 | mem.copy(u8, buffer, clobber); | ||
| 9195 | buffer[clobber.len] = 0; | ||
| 9196 | sema.air_extra.items.len += clobber.len / 4 + 1; | ||
| 9197 | } | ||
| 9198 | { | ||
| 9199 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); | ||
| 9200 | mem.copy(u8, buffer, asm_source); | ||
| 9201 | sema.air_extra.items.len += (asm_source.len + 3) / 4; | ||
| 9202 | } | ||
| 9163 | return asm_air; | 9203 | return asm_air; |
| 9164 | } | 9204 | } |
| 9165 | 9205 |
src/arch/aarch64/CodeGen.zig+63-70| ... | @@ -4,7 +4,6 @@ const mem = std.mem; | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | const math = std.math; | 4 | const math = std.math; |
| 5 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 6 | const Air = @import("../../Air.zig"); | 6 | const Air = @import("../../Air.zig"); |
| 7 | const Zir = @import("../../Zir.zig"); | ||
| 8 | const Mir = @import("Mir.zig"); | 7 | const Mir = @import("Mir.zig"); |
| 9 | const Emit = @import("Emit.zig"); | 8 | const Emit = @import("Emit.zig"); |
| 10 | const Liveness = @import("../../Liveness.zig"); | 9 | const Liveness = @import("../../Liveness.zig"); |
| ... | @@ -2007,36 +2006,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2007,36 +2006,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2007 | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); | 2006 | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); |
| 2008 | } | 2007 | } |
| 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 | fn airArg(self: *Self, inst: Air.Inst.Index) !void { | 2009 | fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2041 | const arg_index = self.arg_index; | 2010 | const arg_index = self.arg_index; |
| 2042 | self.arg_index += 1; | 2011 | self.arg_index += 1; |
| ... | @@ -2852,40 +2821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { | ... | @@ -2852,40 +2821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 2852 | } | 2821 | } |
| 2853 | 2822 | ||
| 2854 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | 2823 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2855 | const air_datas = self.air.instructions.items(.data); | 2824 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2856 | const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | 2825 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); |
| 2857 | const zir = self.mod_fn.owner_decl.getFileScope().zir; | 2826 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; |
| 2858 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; | 2827 | const clobbers_len = @truncate(u31, extra.data.flags); |
| 2859 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | 2828 | var extra_i: usize = extra.end; |
| 2860 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | 2829 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); |
| 2861 | const outputs_len = @truncate(u5, extended.small); | 2830 | extra_i += outputs.len; |
| 2862 | const args_len = @truncate(u5, extended.small >> 5); | 2831 | const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 2863 | const clobbers_len = @truncate(u5, extended.small >> 10); | 2832 | extra_i += inputs.len; |
| 2864 | _ = clobbers_len; // TODO honor these | ||
| 2865 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | ||
| 2866 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]); | ||
| 2867 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]); | ||
| 2868 | |||
| 2869 | if (outputs_len > 1) { | ||
| 2870 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | ||
| 2871 | } | ||
| 2872 | var extra_i: usize = zir_extra.end; | ||
| 2873 | const output_constraint: ?[]const u8 = out: { | ||
| 2874 | var i: usize = 0; | ||
| 2875 | while (i < outputs_len) : (i += 1) { | ||
| 2876 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | ||
| 2877 | extra_i = output.end; | ||
| 2878 | break :out zir.nullTerminatedString(output.data.constraint); | ||
| 2879 | } | ||
| 2880 | break :out null; | ||
| 2881 | }; | ||
| 2882 | 2833 | ||
| 2883 | const dead = !is_volatile and self.liveness.isUnused(inst); | 2834 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 2884 | const result: MCValue = if (dead) .dead else result: { | 2835 | const result: MCValue = if (dead) .dead else result: { |
| 2885 | for (args) |arg| { | 2836 | if (outputs.len > 1) { |
| 2886 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | 2837 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); |
| 2887 | extra_i = input.end; | 2838 | } |
| 2888 | const constraint = zir.nullTerminatedString(input.data.constraint); | 2839 | |
| 2840 | const output_constraint: ?[]const u8 = for (outputs) |output| { | ||
| 2841 | if (output != .none) { | ||
| 2842 | return self.fail("TODO implement codegen for non-expr asm", .{}); | ||
| 2843 | } | ||
| 2844 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 2845 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 2846 | // for the string, we still use the next u32 for the null terminator. | ||
| 2847 | extra_i += constraint.len / 4 + 1; | ||
| 2848 | |||
| 2849 | break constraint; | ||
| 2850 | } else null; | ||
| 2851 | |||
| 2852 | for (inputs) |input| { | ||
| 2853 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 2854 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 2855 | // for the string, we still use the next u32 for the null terminator. | ||
| 2856 | extra_i += constraint.len / 4 + 1; | ||
| 2889 | 2857 | ||
| 2890 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { | 2858 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 2891 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); | 2859 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | @@ -2894,11 +2862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2894,11 +2862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2894 | const reg = parseRegName(reg_name) orelse | 2862 | const reg = parseRegName(reg_name) orelse |
| 2895 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 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 | try self.register_manager.getReg(reg, null); | 2866 | try self.register_manager.getReg(reg, null); |
| 2899 | try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); | 2867 | try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); |
| 2868 | } | ||
| 2869 | |||
| 2870 | { | ||
| 2871 | var clobber_i: u32 = 0; | ||
| 2872 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | ||
| 2873 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 2874 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 2875 | // for the string, we still use the next u32 for the null terminator. | ||
| 2876 | extra_i += clobber.len / 4 + 1; | ||
| 2877 | |||
| 2878 | // TODO honor these | ||
| 2879 | } | ||
| 2900 | } | 2880 | } |
| 2901 | 2881 | ||
| 2882 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | ||
| 2883 | |||
| 2902 | if (mem.eql(u8, asm_source, "svc #0")) { | 2884 | if (mem.eql(u8, asm_source, "svc #0")) { |
| 2903 | _ = try self.addInst(.{ | 2885 | _ = try self.addInst(.{ |
| 2904 | .tag = .svc, | 2886 | .tag = .svc, |
| ... | @@ -2925,18 +2907,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2925,18 +2907,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2925 | break :result MCValue{ .none = {} }; | 2907 | break :result MCValue{ .none = {} }; |
| 2926 | } | 2908 | } |
| 2927 | }; | 2909 | }; |
| 2928 | if (outputs.len + args.len <= Liveness.bpi - 1) { | 2910 | |
| 2911 | simple: { | ||
| 2929 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); | 2912 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| 2930 | std.mem.copy(Air.Inst.Ref, &buf, outputs); | 2913 | var buf_index: usize = 0; |
| 2931 | std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); | 2914 | for (outputs) |output| { |
| 2915 | if (output == .none) continue; | ||
| 2916 | |||
| 2917 | if (buf_index >= buf.len) break :simple; | ||
| 2918 | buf[buf_index] = output; | ||
| 2919 | buf_index += 1; | ||
| 2920 | } | ||
| 2921 | if (buf_index + inputs.len > buf.len) break :simple; | ||
| 2922 | std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); | ||
| 2932 | return self.finishAir(inst, result, buf); | 2923 | return self.finishAir(inst, result, buf); |
| 2933 | } | 2924 | } |
| 2934 | var bt = try self.iterateBigTomb(inst, outputs.len + args.len); | 2925 | var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); |
| 2935 | for (outputs) |output| { | 2926 | for (outputs) |output| { |
| 2927 | if (output == .none) continue; | ||
| 2928 | |||
| 2936 | bt.feed(output); | 2929 | bt.feed(output); |
| 2937 | } | 2930 | } |
| 2938 | for (args) |arg| { | 2931 | for (inputs) |input| { |
| 2939 | bt.feed(arg); | 2932 | bt.feed(input); |
| 2940 | } | 2933 | } |
| 2941 | return bt.finishAir(result); | 2934 | return bt.finishAir(result); |
| 2942 | } | 2935 | } |
src/arch/arm/CodeGen.zig+63-40| ... | @@ -4,7 +4,6 @@ const mem = std.mem; | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | const math = std.math; | 4 | const math = std.math; |
| 5 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 6 | const Air = @import("../../Air.zig"); | 6 | const Air = @import("../../Air.zig"); |
| 7 | const Zir = @import("../../Zir.zig"); | ||
| 8 | const Mir = @import("Mir.zig"); | 7 | const Mir = @import("Mir.zig"); |
| 9 | const Emit = @import("Emit.zig"); | 8 | const Emit = @import("Emit.zig"); |
| 10 | const Liveness = @import("../../Liveness.zig"); | 9 | const Liveness = @import("../../Liveness.zig"); |
| ... | @@ -3059,40 +3058,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { | ... | @@ -3059,40 +3058,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 3059 | } | 3058 | } |
| 3060 | 3059 | ||
| 3061 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | 3060 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3062 | const air_datas = self.air.instructions.items(.data); | 3061 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3063 | const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | 3062 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); |
| 3064 | const zir = self.mod_fn.owner_decl.getFileScope().zir; | 3063 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; |
| 3065 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; | 3064 | const clobbers_len = @truncate(u31, extra.data.flags); |
| 3066 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | 3065 | var extra_i: usize = extra.end; |
| 3067 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | 3066 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); |
| 3068 | const outputs_len = @truncate(u5, extended.small); | 3067 | extra_i += outputs.len; |
| 3069 | const args_len = @truncate(u5, extended.small >> 5); | 3068 | const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 3070 | const clobbers_len = @truncate(u5, extended.small >> 10); | 3069 | extra_i += inputs.len; |
| 3071 | _ = clobbers_len; // TODO honor these | ||
| 3072 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | ||
| 3073 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]); | ||
| 3074 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]); | ||
| 3075 | |||
| 3076 | if (outputs_len > 1) { | ||
| 3077 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | ||
| 3078 | } | ||
| 3079 | var extra_i: usize = zir_extra.end; | ||
| 3080 | const output_constraint: ?[]const u8 = out: { | ||
| 3081 | var i: usize = 0; | ||
| 3082 | while (i < outputs_len) : (i += 1) { | ||
| 3083 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | ||
| 3084 | extra_i = output.end; | ||
| 3085 | break :out zir.nullTerminatedString(output.data.constraint); | ||
| 3086 | } | ||
| 3087 | break :out null; | ||
| 3088 | }; | ||
| 3089 | 3070 | ||
| 3090 | const dead = !is_volatile and self.liveness.isUnused(inst); | 3071 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 3091 | const result: MCValue = if (dead) .dead else result: { | 3072 | const result: MCValue = if (dead) .dead else result: { |
| 3092 | for (args) |arg| { | 3073 | if (outputs.len > 1) { |
| 3093 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | 3074 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); |
| 3094 | extra_i = input.end; | 3075 | } |
| 3095 | const constraint = zir.nullTerminatedString(input.data.constraint); | 3076 | |
| 3077 | const output_constraint: ?[]const u8 = for (outputs) |output| { | ||
| 3078 | if (output != .none) { | ||
| 3079 | return self.fail("TODO implement codegen for non-expr asm", .{}); | ||
| 3080 | } | ||
| 3081 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 3082 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 3083 | // for the string, we still use the next u32 for the null terminator. | ||
| 3084 | extra_i += constraint.len / 4 + 1; | ||
| 3085 | |||
| 3086 | break constraint; | ||
| 3087 | } else null; | ||
| 3088 | |||
| 3089 | for (inputs) |input| { | ||
| 3090 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 3091 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 3092 | // for the string, we still use the next u32 for the null terminator. | ||
| 3093 | extra_i += constraint.len / 4 + 1; | ||
| 3096 | 3094 | ||
| 3097 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { | 3095 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 3098 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); | 3096 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | @@ -3101,11 +3099,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3101,11 +3099,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3101 | const reg = parseRegName(reg_name) orelse | 3099 | const reg = parseRegName(reg_name) orelse |
| 3102 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 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 | try self.register_manager.getReg(reg, null); | 3103 | try self.register_manager.getReg(reg, null); |
| 3106 | try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); | 3104 | try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); |
| 3105 | } | ||
| 3106 | |||
| 3107 | { | ||
| 3108 | var clobber_i: u32 = 0; | ||
| 3109 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | ||
| 3110 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 3111 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 3112 | // for the string, we still use the next u32 for the null terminator. | ||
| 3113 | extra_i += clobber.len / 4 + 1; | ||
| 3114 | |||
| 3115 | // TODO honor these | ||
| 3116 | } | ||
| 3107 | } | 3117 | } |
| 3108 | 3118 | ||
| 3119 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | ||
| 3120 | |||
| 3109 | if (mem.eql(u8, asm_source, "svc #0")) { | 3121 | if (mem.eql(u8, asm_source, "svc #0")) { |
| 3110 | _ = try self.addInst(.{ | 3122 | _ = try self.addInst(.{ |
| 3111 | .tag = .svc, | 3123 | .tag = .svc, |
| ... | @@ -3128,18 +3140,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3128,18 +3140,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3128 | break :result MCValue{ .none = {} }; | 3140 | break :result MCValue{ .none = {} }; |
| 3129 | } | 3141 | } |
| 3130 | }; | 3142 | }; |
| 3131 | if (outputs.len + args.len <= Liveness.bpi - 1) { | 3143 | |
| 3144 | simple: { | ||
| 3132 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); | 3145 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| 3133 | std.mem.copy(Air.Inst.Ref, &buf, outputs); | 3146 | var buf_index: usize = 0; |
| 3134 | std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); | 3147 | for (outputs) |output| { |
| 3148 | if (output == .none) continue; | ||
| 3149 | |||
| 3150 | if (buf_index >= buf.len) break :simple; | ||
| 3151 | buf[buf_index] = output; | ||
| 3152 | buf_index += 1; | ||
| 3153 | } | ||
| 3154 | if (buf_index + inputs.len > buf.len) break :simple; | ||
| 3155 | std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); | ||
| 3135 | return self.finishAir(inst, result, buf); | 3156 | return self.finishAir(inst, result, buf); |
| 3136 | } | 3157 | } |
| 3137 | var bt = try self.iterateBigTomb(inst, outputs.len + args.len); | 3158 | var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); |
| 3138 | for (outputs) |output| { | 3159 | for (outputs) |output| { |
| 3160 | if (output == .none) continue; | ||
| 3161 | |||
| 3139 | bt.feed(output); | 3162 | bt.feed(output); |
| 3140 | } | 3163 | } |
| 3141 | for (args) |arg| { | 3164 | for (inputs) |input| { |
| 3142 | bt.feed(arg); | 3165 | bt.feed(input); |
| 3143 | } | 3166 | } |
| 3144 | return bt.finishAir(result); | 3167 | return bt.finishAir(result); |
| 3145 | } | 3168 | } |
src/arch/riscv64/CodeGen.zig+62-40| ... | @@ -4,7 +4,6 @@ const mem = std.mem; | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | const math = std.math; | 4 | const math = std.math; |
| 5 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 6 | const Air = @import("../../Air.zig"); | 6 | const Air = @import("../../Air.zig"); |
| 7 | const Zir = @import("../../Zir.zig"); | ||
| 8 | const Mir = @import("Mir.zig"); | 7 | const Mir = @import("Mir.zig"); |
| 9 | const Emit = @import("Emit.zig"); | 8 | const Emit = @import("Emit.zig"); |
| 10 | const Liveness = @import("../../Liveness.zig"); | 9 | const Liveness = @import("../../Liveness.zig"); |
| ... | @@ -1822,40 +1821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { | ... | @@ -1822,40 +1821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 1822 | } | 1821 | } |
| 1823 | 1822 | ||
| 1824 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | 1823 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 1825 | const air_datas = self.air.instructions.items(.data); | 1824 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1826 | const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | 1825 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); |
| 1827 | const zir = self.mod_fn.owner_decl.getFileScope().zir; | 1826 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; |
| 1828 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; | 1827 | const clobbers_len = @truncate(u31, extra.data.flags); |
| 1829 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | 1828 | var extra_i: usize = extra.end; |
| 1830 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | 1829 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); |
| 1831 | const outputs_len = @truncate(u5, extended.small); | 1830 | extra_i += outputs.len; |
| 1832 | const args_len = @truncate(u5, extended.small >> 5); | 1831 | const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 1833 | const clobbers_len = @truncate(u5, extended.small >> 10); | 1832 | extra_i += inputs.len; |
| 1834 | _ = clobbers_len; // TODO honor these | ||
| 1835 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | ||
| 1836 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]); | ||
| 1837 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]); | ||
| 1838 | |||
| 1839 | if (outputs_len > 1) { | ||
| 1840 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | ||
| 1841 | } | ||
| 1842 | var extra_i: usize = zir_extra.end; | ||
| 1843 | const output_constraint: ?[]const u8 = out: { | ||
| 1844 | var i: usize = 0; | ||
| 1845 | while (i < outputs_len) : (i += 1) { | ||
| 1846 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | ||
| 1847 | extra_i = output.end; | ||
| 1848 | break :out zir.nullTerminatedString(output.data.constraint); | ||
| 1849 | } | ||
| 1850 | break :out null; | ||
| 1851 | }; | ||
| 1852 | 1833 | ||
| 1853 | const dead = !is_volatile and self.liveness.isUnused(inst); | 1834 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 1854 | const result: MCValue = if (dead) .dead else result: { | 1835 | const result: MCValue = if (dead) .dead else result: { |
| 1855 | for (args) |arg| { | 1836 | if (outputs.len > 1) { |
| 1856 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | 1837 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); |
| 1857 | extra_i = input.end; | 1838 | } |
| 1858 | const constraint = zir.nullTerminatedString(input.data.constraint); | 1839 | |
| 1840 | const output_constraint: ?[]const u8 = for (outputs) |output| { | ||
| 1841 | if (output != .none) { | ||
| 1842 | return self.fail("TODO implement codegen for non-expr asm", .{}); | ||
| 1843 | } | ||
| 1844 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 1845 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 1846 | // for the string, we still use the next u32 for the null terminator. | ||
| 1847 | extra_i += constraint.len / 4 + 1; | ||
| 1848 | |||
| 1849 | break constraint; | ||
| 1850 | } else null; | ||
| 1851 | |||
| 1852 | for (inputs) |input| { | ||
| 1853 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 1854 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 1855 | // for the string, we still use the next u32 for the null terminator. | ||
| 1856 | extra_i += constraint.len / 4 + 1; | ||
| 1859 | 1857 | ||
| 1860 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { | 1858 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 1861 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); | 1859 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | @@ -1864,11 +1862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1864,11 +1862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 1864 | const reg = parseRegName(reg_name) orelse | 1862 | const reg = parseRegName(reg_name) orelse |
| 1865 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 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 | try self.register_manager.getReg(reg, null); | 1866 | try self.register_manager.getReg(reg, null); |
| 1869 | try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); | 1867 | try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); |
| 1870 | } | 1868 | } |
| 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 | if (mem.eql(u8, asm_source, "ecall")) { | 1884 | if (mem.eql(u8, asm_source, "ecall")) { |
| 1873 | _ = try self.addInst(.{ | 1885 | _ = try self.addInst(.{ |
| 1874 | .tag = .ecall, | 1886 | .tag = .ecall, |
| ... | @@ -1890,18 +1902,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1890,18 +1902,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 1890 | break :result MCValue{ .none = {} }; | 1902 | break :result MCValue{ .none = {} }; |
| 1891 | } | 1903 | } |
| 1892 | }; | 1904 | }; |
| 1893 | if (outputs.len + args.len <= Liveness.bpi - 1) { | 1905 | simple: { |
| 1894 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); | 1906 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| 1895 | std.mem.copy(Air.Inst.Ref, &buf, outputs); | 1907 | var buf_index: usize = 0; |
| 1896 | std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); | 1908 | for (outputs) |output| { |
| 1909 | if (output == .none) continue; | ||
| 1910 | |||
| 1911 | if (buf_index >= buf.len) break :simple; | ||
| 1912 | buf[buf_index] = output; | ||
| 1913 | buf_index += 1; | ||
| 1914 | } | ||
| 1915 | if (buf_index + inputs.len > buf.len) break :simple; | ||
| 1916 | std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); | ||
| 1897 | return self.finishAir(inst, result, buf); | 1917 | return self.finishAir(inst, result, buf); |
| 1898 | } | 1918 | } |
| 1899 | var bt = try self.iterateBigTomb(inst, outputs.len + args.len); | 1919 | var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); |
| 1900 | for (outputs) |output| { | 1920 | for (outputs) |output| { |
| 1921 | if (output == .none) continue; | ||
| 1922 | |||
| 1901 | bt.feed(output); | 1923 | bt.feed(output); |
| 1902 | } | 1924 | } |
| 1903 | for (args) |arg| { | 1925 | for (inputs) |input| { |
| 1904 | bt.feed(arg); | 1926 | bt.feed(input); |
| 1905 | } | 1927 | } |
| 1906 | return bt.finishAir(result); | 1928 | return bt.finishAir(result); |
| 1907 | } | 1929 | } |
src/arch/x86_64/CodeGen.zig+69-43| ... | @@ -26,7 +26,6 @@ const Target = std.Target; | ... | @@ -26,7 +26,6 @@ const Target = std.Target; |
| 26 | const Type = @import("../../type.zig").Type; | 26 | const Type = @import("../../type.zig").Type; |
| 27 | const TypedValue = @import("../../TypedValue.zig"); | 27 | const TypedValue = @import("../../TypedValue.zig"); |
| 28 | const Value = @import("../../value.zig").Value; | 28 | const Value = @import("../../value.zig").Value; |
| 29 | const Zir = @import("../../Zir.zig"); | ||
| 30 | 29 | ||
| 31 | const InnerError = error{ | 30 | const InnerError = error{ |
| 32 | OutOfMemory, | 31 | OutOfMemory, |
| ... | @@ -3415,41 +3414,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { | ... | @@ -3415,41 +3414,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 3415 | } | 3414 | } |
| 3416 | 3415 | ||
| 3417 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | 3416 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3418 | const air_datas = self.air.instructions.items(.data); | 3417 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3419 | const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | 3418 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); |
| 3420 | const zir = self.mod_fn.owner_decl.getFileScope().zir; | 3419 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; |
| 3421 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; | 3420 | const clobbers_len = @truncate(u31, extra.data.flags); |
| 3422 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | 3421 | var extra_i: usize = extra.end; |
| 3423 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | 3422 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); |
| 3424 | const outputs_len = @truncate(u5, extended.small); | 3423 | extra_i += outputs.len; |
| 3425 | const args_len = @truncate(u5, extended.small >> 5); | 3424 | const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 3426 | const clobbers_len = @truncate(u5, extended.small >> 10); | 3425 | extra_i += inputs.len; |
| 3427 | _ = clobbers_len; // TODO honor these | ||
| 3428 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | ||
| 3429 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..args_len]); | ||
| 3430 | |||
| 3431 | if (outputs_len > 1) { | ||
| 3432 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | ||
| 3433 | } | ||
| 3434 | var extra_i: usize = zir_extra.end; | ||
| 3435 | const output_constraint: ?[]const u8 = out: { | ||
| 3436 | var i: usize = 0; | ||
| 3437 | while (i < outputs_len) : (i += 1) { | ||
| 3438 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | ||
| 3439 | extra_i = output.end; | ||
| 3440 | break :out zir.nullTerminatedString(output.data.constraint); | ||
| 3441 | } | ||
| 3442 | break :out null; | ||
| 3443 | }; | ||
| 3444 | 3426 | ||
| 3445 | const dead = !is_volatile and self.liveness.isUnused(inst); | 3427 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 3446 | const result: MCValue = if (dead) | 3428 | const result: MCValue = if (dead) .dead else result: { |
| 3447 | .dead | 3429 | if (outputs.len > 1) { |
| 3448 | else result: { | 3430 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); |
| 3449 | for (args) |arg| { | 3431 | } |
| 3450 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | 3432 | |
| 3451 | extra_i = input.end; | 3433 | const output_constraint: ?[]const u8 = for (outputs) |output| { |
| 3452 | const constraint = zir.nullTerminatedString(input.data.constraint); | 3434 | if (output != .none) { |
| 3435 | return self.fail("TODO implement codegen for non-expr asm", .{}); | ||
| 3436 | } | ||
| 3437 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 3438 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 3439 | // for the string, we still use the next u32 for the null terminator. | ||
| 3440 | extra_i += constraint.len / 4 + 1; | ||
| 3441 | |||
| 3442 | break constraint; | ||
| 3443 | } else null; | ||
| 3444 | |||
| 3445 | for (inputs) |input| { | ||
| 3446 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 3447 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 3448 | // for the string, we still use the next u32 for the null terminator. | ||
| 3449 | extra_i += constraint.len / 4 + 1; | ||
| 3453 | 3450 | ||
| 3454 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { | 3451 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 3455 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); | 3452 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | @@ -3458,11 +3455,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3458,11 +3455,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3458 | const reg = parseRegName(reg_name) orelse | 3455 | const reg = parseRegName(reg_name) orelse |
| 3459 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 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 | try self.register_manager.getReg(reg, null); | 3459 | try self.register_manager.getReg(reg, null); |
| 3463 | try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); | 3460 | try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); |
| 3461 | } | ||
| 3462 | |||
| 3463 | { | ||
| 3464 | var clobber_i: u32 = 0; | ||
| 3465 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | ||
| 3466 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | ||
| 3467 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 3468 | // for the string, we still use the next u32 for the null terminator. | ||
| 3469 | extra_i += clobber.len / 4 + 1; | ||
| 3470 | |||
| 3471 | // TODO honor these | ||
| 3472 | } | ||
| 3464 | } | 3473 | } |
| 3465 | 3474 | ||
| 3475 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | ||
| 3476 | |||
| 3466 | { | 3477 | { |
| 3467 | var iter = std.mem.tokenize(u8, asm_source, "\n\r"); | 3478 | var iter = std.mem.tokenize(u8, asm_source, "\n\r"); |
| 3468 | while (iter.next()) |ins| { | 3479 | while (iter.next()) |ins| { |
| ... | @@ -3529,14 +3540,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3529,14 +3540,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3529 | break :result MCValue{ .none = {} }; | 3540 | break :result MCValue{ .none = {} }; |
| 3530 | } | 3541 | } |
| 3531 | }; | 3542 | }; |
| 3532 | if (args.len <= Liveness.bpi - 1) { | 3543 | |
| 3544 | simple: { | ||
| 3533 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); | 3545 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| 3534 | std.mem.copy(Air.Inst.Ref, &buf, args); | 3546 | var buf_index: usize = 0; |
| 3547 | for (outputs) |output| { | ||
| 3548 | if (output == .none) continue; | ||
| 3549 | |||
| 3550 | if (buf_index >= buf.len) break :simple; | ||
| 3551 | buf[buf_index] = output; | ||
| 3552 | buf_index += 1; | ||
| 3553 | } | ||
| 3554 | if (buf_index + inputs.len > buf.len) break :simple; | ||
| 3555 | std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); | ||
| 3535 | return self.finishAir(inst, result, buf); | 3556 | return self.finishAir(inst, result, buf); |
| 3536 | } | 3557 | } |
| 3537 | var bt = try self.iterateBigTomb(inst, args.len); | 3558 | var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); |
| 3538 | for (args) |arg| { | 3559 | for (outputs) |output| { |
| 3539 | bt.feed(arg); | 3560 | if (output == .none) continue; |
| 3561 | |||
| 3562 | bt.feed(output); | ||
| 3563 | } | ||
| 3564 | for (inputs) |input| { | ||
| 3565 | bt.feed(input); | ||
| 3540 | } | 3566 | } |
| 3541 | return bt.finishAir(result); | 3567 | return bt.finishAir(result); |
| 3542 | } | 3568 | } |
| ... | @@ -3615,7 +3641,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE | ... | @@ -3615,7 +3641,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 3615 | const reg = try self.copyToTmpRegister(ty, mcv); | 3641 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3616 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); | 3642 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| 3617 | }, | 3643 | }, |
| 3618 | else => return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}), | 3644 | else => return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}), |
| 3619 | } | 3645 | } |
| 3620 | }, | 3646 | }, |
| 3621 | .embedded_in_code => { | 3647 | .embedded_in_code => { |
| ... | @@ -3623,7 +3649,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE | ... | @@ -3623,7 +3649,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 3623 | const reg = try self.copyToTmpRegister(ty, mcv); | 3649 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3624 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); | 3650 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| 3625 | } | 3651 | } |
| 3626 | return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}); | 3652 | return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}); |
| 3627 | }, | 3653 | }, |
| 3628 | .memory, | 3654 | .memory, |
| 3629 | .direct_load, | 3655 | .direct_load, |
src/codegen/c.zig+56-41| ... | @@ -15,7 +15,6 @@ const Decl = Module.Decl; | ... | @@ -15,7 +15,6 @@ const Decl = Module.Decl; |
| 15 | const trace = @import("../tracy.zig").trace; | 15 | const trace = @import("../tracy.zig").trace; |
| 16 | const LazySrcLoc = Module.LazySrcLoc; | 16 | const LazySrcLoc = Module.LazySrcLoc; |
| 17 | const Air = @import("../Air.zig"); | 17 | const Air = @import("../Air.zig"); |
| 18 | const Zir = @import("../Zir.zig"); | ||
| 19 | const Liveness = @import("../Liveness.zig"); | 18 | const Liveness = @import("../Liveness.zig"); |
| 20 | 19 | ||
| 21 | const Mutability = enum { Const, Mut }; | 20 | const Mutability = enum { Const, Mut }; |
| ... | @@ -2805,49 +2804,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -2805,49 +2804,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2805 | } | 2804 | } |
| 2806 | 2805 | ||
| 2807 | fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | 2806 | fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2808 | const air_datas = f.air.instructions.items(.data); | 2807 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; |
| 2809 | const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | 2808 | const extra = f.air.extraData(Air.Asm, ty_pl.payload); |
| 2810 | const zir = f.object.dg.decl.getFileScope().zir; | 2809 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; |
| 2811 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; | 2810 | const clobbers_len = @truncate(u31, extra.data.flags); |
| 2812 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | 2811 | var extra_i: usize = extra.end; |
| 2813 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | 2812 | const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]); |
| 2814 | const outputs_len = @truncate(u5, extended.small); | 2813 | extra_i += outputs.len; |
| 2815 | const args_len = @truncate(u5, extended.small >> 5); | 2814 | const inputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 2816 | const clobbers_len = @truncate(u5, extended.small >> 10); | 2815 | extra_i += inputs.len; |
| 2817 | _ = clobbers_len; // TODO honor these | 2816 | |
| 2818 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | 2817 | if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none; |
| 2819 | const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]); | 2818 | |
| 2820 | const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]); | 2819 | if (outputs.len > 1) { |
| 2821 | |||
| 2822 | if (outputs_len > 1) { | ||
| 2823 | return f.fail("TODO implement codegen for asm with more than 1 output", .{}); | 2820 | return f.fail("TODO implement codegen for asm with more than 1 output", .{}); |
| 2824 | } | 2821 | } |
| 2825 | 2822 | ||
| 2826 | if (f.liveness.isUnused(inst) and !is_volatile) | 2823 | const output_constraint: ?[]const u8 = for (outputs) |output| { |
| 2827 | return CValue.none; | 2824 | if (output != .none) { |
| 2828 | 2825 | return f.fail("TODO implement codegen for non-expr asm", .{}); | |
| 2829 | var extra_i: usize = zir_extra.end; | ||
| 2830 | const output_constraint: ?[]const u8 = out: { | ||
| 2831 | var i: usize = 0; | ||
| 2832 | while (i < outputs_len) : (i += 1) { | ||
| 2833 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | ||
| 2834 | extra_i = output.end; | ||
| 2835 | break :out zir.nullTerminatedString(output.data.constraint); | ||
| 2836 | } | 2826 | } |
| 2837 | break :out null; | 2827 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); |
| 2838 | }; | 2828 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 2839 | const args_extra_begin = extra_i; | 2829 | // for the string, we still use the next u32 for the null terminator. |
| 2830 | extra_i += constraint.len / 4 + 1; | ||
| 2831 | |||
| 2832 | break constraint; | ||
| 2833 | } else null; | ||
| 2840 | 2834 | ||
| 2841 | const writer = f.object.writer(); | 2835 | const writer = f.object.writer(); |
| 2842 | for (args) |arg| { | 2836 | const inputs_extra_begin = extra_i; |
| 2843 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | 2837 | |
| 2844 | extra_i = input.end; | 2838 | for (inputs) |input| { |
| 2845 | const constraint = zir.nullTerminatedString(input.data.constraint); | 2839 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); |
| 2840 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 2841 | // for the string, we still use the next u32 for the null terminator. | ||
| 2842 | extra_i += constraint.len / 4 + 1; | ||
| 2843 | |||
| 2846 | if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { | 2844 | if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { |
| 2847 | const reg = constraint[1 .. constraint.len - 1]; | 2845 | const reg = constraint[1 .. constraint.len - 1]; |
| 2848 | const arg_c_value = try f.resolveInst(arg); | 2846 | const arg_c_value = try f.resolveInst(input); |
| 2849 | try writer.writeAll("register "); | 2847 | try writer.writeAll("register "); |
| 2850 | try f.renderType(writer, f.air.typeOf(arg)); | 2848 | try f.renderType(writer, f.air.typeOf(input)); |
| 2851 | 2849 | ||
| 2852 | try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); | 2850 | try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); |
| 2853 | try f.writeCValue(writer, arg_c_value); | 2851 | try f.writeCValue(writer, arg_c_value); |
| ... | @@ -2856,21 +2854,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -2856,21 +2854,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2856 | return f.fail("TODO non-explicit inline asm regs", .{}); | 2854 | return f.fail("TODO non-explicit inline asm regs", .{}); |
| 2857 | } | 2855 | } |
| 2858 | } | 2856 | } |
| 2857 | |||
| 2858 | { | ||
| 2859 | var clobber_i: u32 = 0; | ||
| 2860 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | ||
| 2861 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); | ||
| 2862 | // This equation accounts for the fact that even if we have exactly 4 bytes | ||
| 2863 | // for the string, we still use the next u32 for the null terminator. | ||
| 2864 | extra_i += clobber.len / 4 + 1; | ||
| 2865 | |||
| 2866 | // TODO honor these | ||
| 2867 | } | ||
| 2868 | } | ||
| 2869 | |||
| 2870 | const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; | ||
| 2871 | |||
| 2859 | const volatile_string: []const u8 = if (is_volatile) "volatile " else ""; | 2872 | const volatile_string: []const u8 = if (is_volatile) "volatile " else ""; |
| 2860 | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source }); | 2873 | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source }); |
| 2861 | if (output_constraint) |_| { | 2874 | if (output_constraint) |_| { |
| 2862 | return f.fail("TODO: CBE inline asm output", .{}); | 2875 | return f.fail("TODO: CBE inline asm output", .{}); |
| 2863 | } | 2876 | } |
| 2864 | if (args.len > 0) { | 2877 | if (inputs.len > 0) { |
| 2865 | if (output_constraint == null) { | 2878 | if (output_constraint == null) { |
| 2866 | try writer.writeAll(" :"); | 2879 | try writer.writeAll(" :"); |
| 2867 | } | 2880 | } |
| 2868 | try writer.writeAll(": "); | 2881 | try writer.writeAll(": "); |
| 2869 | extra_i = args_extra_begin; | 2882 | extra_i = inputs_extra_begin; |
| 2870 | for (args) |_, index| { | 2883 | for (inputs) |_, index| { |
| 2871 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | 2884 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); |
| 2872 | extra_i = input.end; | 2885 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 2873 | const constraint = zir.nullTerminatedString(input.data.constraint); | 2886 | // for the string, we still use the next u32 for the null terminator. |
| 2887 | extra_i += constraint.len / 4 + 1; | ||
| 2888 | |||
| 2874 | if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { | 2889 | if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { |
| 2875 | const reg = constraint[1 .. constraint.len - 1]; | 2890 | const reg = constraint[1 .. constraint.len - 1]; |
| 2876 | if (index > 0) { | 2891 | if (index > 0) { |
src/codegen/llvm.zig+51-50| ... | @@ -2,24 +2,21 @@ const std = @import("std"); | ... | @@ -2,24 +2,21 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const Allocator = std.mem.Allocator; | 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 | const log = std.log.scoped(.codegen); | 5 | const log = std.log.scoped(.codegen); |
| 9 | const math = std.math; | 6 | const math = std.math; |
| 10 | const native_endian = builtin.cpu.arch.endian(); | 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 | const build_options = @import("build_options"); | 12 | const build_options = @import("build_options"); |
| 13 | const Module = @import("../Module.zig"); | 13 | const Module = @import("../Module.zig"); |
| 14 | const TypedValue = @import("../TypedValue.zig"); | 14 | const TypedValue = @import("../TypedValue.zig"); |
| 15 | const Zir = @import("../Zir.zig"); | ||
| 16 | const Air = @import("../Air.zig"); | 15 | const Air = @import("../Air.zig"); |
| 17 | const Liveness = @import("../Liveness.zig"); | 16 | const Liveness = @import("../Liveness.zig"); |
| 18 | const target_util = @import("../target.zig"); | 17 | const target_util = @import("../target.zig"); |
| 19 | |||
| 20 | const Value = @import("../value.zig").Value; | 18 | const Value = @import("../value.zig").Value; |
| 21 | const Type = @import("../type.zig").Type; | 19 | const Type = @import("../type.zig").Type; |
| 22 | |||
| 23 | const LazySrcLoc = Module.LazySrcLoc; | 20 | const LazySrcLoc = Module.LazySrcLoc; |
| 24 | 21 | ||
| 25 | const Error = error{ OutOfMemory, CodegenFail }; | 22 | const Error = error{ OutOfMemory, CodegenFail }; |
| ... | @@ -2893,33 +2890,21 @@ pub const FuncGen = struct { | ... | @@ -2893,33 +2890,21 @@ pub const FuncGen = struct { |
| 2893 | // as stage1. | 2890 | // as stage1. |
| 2894 | 2891 | ||
| 2895 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 2892 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2896 | const air_asm = self.air.extraData(Air.Asm, ty_pl.payload); | 2893 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); |
| 2897 | const zir = self.dg.decl.getFileScope().zir; | 2894 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; |
| 2898 | const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended; | 2895 | const clobbers_len = @truncate(u31, extra.data.flags); |
| 2899 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | 2896 | var extra_i: usize = extra.end; |
| 2900 | if (!is_volatile and self.liveness.isUnused(inst)) { | 2897 | |
| 2901 | return null; | 2898 | if (!is_volatile and self.liveness.isUnused(inst)) return null; |
| 2902 | } | 2899 | |
| 2903 | const outputs_len = @truncate(u5, extended.small); | 2900 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); |
| 2904 | if (outputs_len > 1) { | 2901 | extra_i += outputs.len; |
| 2902 | const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); | ||
| 2903 | extra_i += inputs.len; | ||
| 2904 | |||
| 2905 | if (outputs.len > 1) { | ||
| 2905 | return self.todo("implement llvm codegen for asm with more than 1 output", .{}); | 2906 | return self.todo("implement llvm codegen for asm with more than 1 output", .{}); |
| 2906 | } | 2907 | } |
| 2907 | const args_len = @truncate(u5, extended.small >> 5); | ||
| 2908 | const clobbers_len = @truncate(u5, extended.small >> 10); | ||
| 2909 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | ||
| 2910 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | ||
| 2911 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end..][0..args_len]); | ||
| 2912 | |||
| 2913 | var extra_i: usize = zir_extra.end; | ||
| 2914 | const output_constraint: ?[]const u8 = out: { | ||
| 2915 | var i: usize = 0; | ||
| 2916 | while (i < outputs_len) : (i += 1) { | ||
| 2917 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | ||
| 2918 | extra_i = output.end; | ||
| 2919 | break :out zir.nullTerminatedString(output.data.constraint); | ||
| 2920 | } | ||
| 2921 | break :out null; | ||
| 2922 | }; | ||
| 2923 | 2908 | ||
| 2924 | var llvm_constraints: std.ArrayListUnmanaged(u8) = .{}; | 2909 | var llvm_constraints: std.ArrayListUnmanaged(u8) = .{}; |
| 2925 | defer llvm_constraints.deinit(self.gpa); | 2910 | defer llvm_constraints.deinit(self.gpa); |
| ... | @@ -2928,14 +2913,21 @@ pub const FuncGen = struct { | ... | @@ -2928,14 +2913,21 @@ pub const FuncGen = struct { |
| 2928 | defer arena_allocator.deinit(); | 2913 | defer arena_allocator.deinit(); |
| 2929 | const arena = arena_allocator.allocator(); | 2914 | const arena = arena_allocator.allocator(); |
| 2930 | 2915 | ||
| 2931 | const llvm_params_len = args.len; | 2916 | const llvm_params_len = inputs.len; |
| 2932 | const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len); | 2917 | const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len); |
| 2933 | const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len); | 2918 | const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len); |
| 2934 | |||
| 2935 | var llvm_param_i: usize = 0; | 2919 | var llvm_param_i: usize = 0; |
| 2936 | var total_i: usize = 0; | 2920 | var total_i: usize = 0; |
| 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 | try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); | 2931 | try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); |
| 2940 | if (total_i != 0) { | 2932 | if (total_i != 0) { |
| 2941 | llvm_constraints.appendAssumeCapacity(','); | 2933 | llvm_constraints.appendAssumeCapacity(','); |
| ... | @@ -2946,11 +2938,13 @@ pub const FuncGen = struct { | ... | @@ -2946,11 +2938,13 @@ pub const FuncGen = struct { |
| 2946 | total_i += 1; | 2938 | total_i += 1; |
| 2947 | } | 2939 | } |
| 2948 | 2940 | ||
| 2949 | for (args) |arg| { | 2941 | for (inputs) |input| { |
| 2950 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | 2942 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); |
| 2951 | extra_i = input.end; | 2943 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 2952 | const constraint = zir.nullTerminatedString(input.data.constraint); | 2944 | // for the string, we still use the next u32 for the null terminator. |
| 2953 | const arg_llvm_value = try self.resolveInst(arg); | 2945 | extra_i += constraint.len / 4 + 1; |
| 2946 | |||
| 2947 | const arg_llvm_value = try self.resolveInst(input); | ||
| 2954 | 2948 | ||
| 2955 | llvm_param_values[llvm_param_i] = arg_llvm_value; | 2949 | llvm_param_values[llvm_param_i] = arg_llvm_value; |
| 2956 | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); | 2950 | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); |
| ... | @@ -2965,19 +2959,26 @@ pub const FuncGen = struct { | ... | @@ -2965,19 +2959,26 @@ pub const FuncGen = struct { |
| 2965 | total_i += 1; | 2959 | total_i += 1; |
| 2966 | } | 2960 | } |
| 2967 | 2961 | ||
| 2968 | const clobbers = zir.extra[extra_i..][0..clobbers_len]; | 2962 | { |
| 2969 | for (clobbers) |clobber_index| { | 2963 | var clobber_i: u32 = 0; |
| 2970 | const clobber = zir.nullTerminatedString(clobber_index); | 2964 | while (clobber_i < clobbers_len) : (clobber_i += 1) { |
| 2971 | try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4); | 2965 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); |
| 2972 | if (total_i != 0) { | 2966 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 2973 | llvm_constraints.appendAssumeCapacity(','); | 2967 | // for the string, we still use the next u32 for the null terminator. |
| 2974 | } | 2968 | extra_i += clobber.len / 4 + 1; |
| 2975 | llvm_constraints.appendSliceAssumeCapacity("~{"); | 2969 | |
| 2976 | llvm_constraints.appendSliceAssumeCapacity(clobber); | 2970 | try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4); |
| 2977 | llvm_constraints.appendSliceAssumeCapacity("}"); | 2971 | if (total_i != 0) { |
| 2972 | llvm_constraints.appendAssumeCapacity(','); | ||
| 2973 | } | ||
| 2974 | llvm_constraints.appendSliceAssumeCapacity("~{"); | ||
| 2975 | llvm_constraints.appendSliceAssumeCapacity(clobber); | ||
| 2976 | llvm_constraints.appendSliceAssumeCapacity("}"); | ||
| 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 | const ret_ty = self.air.typeOfIndex(inst); | 2983 | const ret_ty = self.air.typeOfIndex(inst); |
| 2983 | const ret_llvm_ty = try self.dg.llvmType(ret_ty); | 2984 | const ret_llvm_ty = try self.dg.llvmType(ret_ty); |
src/print_air.zig+53-40| ... | @@ -4,11 +4,10 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin; | ... | @@ -4,11 +4,10 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin; |
| 4 | 4 | ||
| 5 | const Module = @import("Module.zig"); | 5 | const Module = @import("Module.zig"); |
| 6 | const Value = @import("value.zig").Value; | 6 | const Value = @import("value.zig").Value; |
| 7 | const Zir = @import("Zir.zig"); | ||
| 8 | const Air = @import("Air.zig"); | 7 | const Air = @import("Air.zig"); |
| 9 | const Liveness = @import("Liveness.zig"); | 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 | const instruction_bytes = air.instructions.len * | 11 | const instruction_bytes = air.instructions.len * |
| 13 | // Here we don't use @sizeOf(Air.Inst.Data) because it would include | 12 | // Here we don't use @sizeOf(Air.Inst.Data) because it would include |
| 14 | // the debug safety tag but we want to measure release size. | 13 | // the debug safety tag but we want to measure release size. |
| ... | @@ -49,7 +48,6 @@ pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void { | ... | @@ -49,7 +48,6 @@ pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void { |
| 49 | .gpa = gpa, | 48 | .gpa = gpa, |
| 50 | .arena = arena.allocator(), | 49 | .arena = arena.allocator(), |
| 51 | .air = air, | 50 | .air = air, |
| 52 | .zir = zir, | ||
| 53 | .liveness = liveness, | 51 | .liveness = liveness, |
| 54 | .indent = 2, | 52 | .indent = 2, |
| 55 | }; | 53 | }; |
| ... | @@ -63,7 +61,6 @@ const Writer = struct { | ... | @@ -63,7 +61,6 @@ const Writer = struct { |
| 63 | gpa: Allocator, | 61 | gpa: Allocator, |
| 64 | arena: Allocator, | 62 | arena: Allocator, |
| 65 | air: Air, | 63 | air: Air, |
| 66 | zir: Zir, | ||
| 67 | liveness: Liveness, | 64 | liveness: Liveness, |
| 68 | indent: usize, | 65 | indent: usize, |
| 69 | 66 | ||
| ... | @@ -431,51 +428,67 @@ const Writer = struct { | ... | @@ -431,51 +428,67 @@ const Writer = struct { |
| 431 | 428 | ||
| 432 | fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | 429 | fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 433 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; | 430 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; |
| 434 | const air_asm = w.air.extraData(Air.Asm, ty_pl.payload); | 431 | const extra = w.air.extraData(Air.Asm, ty_pl.payload); |
| 435 | const zir = w.zir; | 432 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; |
| 436 | const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended; | 433 | const clobbers_len = @truncate(u31, extra.data.flags); |
| 437 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | 434 | var extra_i: usize = extra.end; |
| 438 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | 435 | var op_index: usize = 0; |
| 439 | const outputs_len = @truncate(u5, extended.small); | 436 | |
| 440 | const args_len = @truncate(u5, extended.small >> 5); | 437 | const ret_ty = w.air.typeOfIndex(inst); |
| 441 | const clobbers_len = @truncate(u5, extended.small >> 10); | 438 | try s.print("{}", .{ret_ty}); |
| 442 | const args = @bitCast([]const Air.Inst.Ref, w.air.extra[air_asm.end..][0..args_len]); | 439 | |
| 443 | 440 | if (is_volatile) { | |
| 444 | var extra_i: usize = zir_extra.end; | 441 | try s.writeAll(", volatile"); |
| 445 | const output_constraint: ?[]const u8 = out: { | 442 | } |
| 446 | var i: usize = 0; | ||
| 447 | while (i < outputs_len) : (i += 1) { | ||
| 448 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | ||
| 449 | extra_i = output.end; | ||
| 450 | break :out zir.nullTerminatedString(output.data.constraint); | ||
| 451 | } | ||
| 452 | break :out null; | ||
| 453 | }; | ||
| 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| { | 449 | for (outputs) |output| { |
| 458 | const ret_ty = w.air.typeOfIndex(inst); | 450 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0); |
| 459 | try s.print(", {s} -> {}", .{ constraint, ret_ty }); | 451 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 452 | // for the string, we still use the next u32 for the null terminator. | ||
| 453 | extra_i += constraint.len / 4 + 1; | ||
| 454 | |||
| 455 | if (output == .none) { | ||
| 456 | try s.print(", -> {s}", .{constraint}); | ||
| 457 | } else { | ||
| 458 | try s.print(", out {s} = (", .{constraint}); | ||
| 459 | try w.writeOperand(s, inst, op_index, output); | ||
| 460 | op_index += 1; | ||
| 461 | try s.writeByte(')'); | ||
| 462 | } | ||
| 460 | } | 463 | } |
| 461 | 464 | ||
| 462 | for (args) |arg| { | 465 | for (inputs) |input| { |
| 463 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | 466 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0); |
| 464 | extra_i = input.end; | 467 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 465 | const constraint = zir.nullTerminatedString(input.data.constraint); | 468 | // for the string, we still use the next u32 for the null terminator. |
| 469 | extra_i += constraint.len / 4 + 1; | ||
| 466 | 470 | ||
| 467 | try s.print(", {s} = (", .{constraint}); | 471 | try s.print(", in {s} = (", .{constraint}); |
| 468 | try w.writeOperand(s, inst, 0, arg); | 472 | try w.writeOperand(s, inst, op_index, input); |
| 473 | op_index += 1; | ||
| 469 | try s.writeByte(')'); | 474 | try s.writeByte(')'); |
| 470 | } | 475 | } |
| 471 | 476 | ||
| 472 | const clobbers = zir.extra[extra_i..][0..clobbers_len]; | 477 | { |
| 473 | for (clobbers) |clobber_index| { | 478 | var clobber_i: u32 = 0; |
| 474 | const clobber = zir.nullTerminatedString(clobber_index); | 479 | while (clobber_i < clobbers_len) : (clobber_i += 1) { |
| 475 | try s.writeAll(", ~{"); | 480 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0); |
| 476 | try s.writeAll(clobber); | 481 | // This equation accounts for the fact that even if we have exactly 4 bytes |
| 477 | try s.writeAll("}"); | 482 | // for the string, we still use the next u32 for the null terminator. |
| 483 | extra_i += clobber.len / 4 + 1; | ||
| 484 | |||
| 485 | try s.writeAll(", ~{"); | ||
| 486 | try s.writeAll(clobber); | ||
| 487 | try s.writeAll("}"); | ||
| 488 | } | ||
| 478 | } | 489 | } |
| 490 | const asm_source = std.mem.sliceAsBytes(w.air.extra[extra_i..])[0..extra.data.source_len]; | ||
| 491 | try s.print(", \"{s}\"", .{asm_source}); | ||
| 479 | } | 492 | } |
| 480 | 493 | ||
| 481 | fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | 494 | fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |