diff --git a/src/Air.zig b/src/Air.zig index 586fe65c677c..5bb6d9f3ff13 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -697,11 +697,25 @@ pub const Bin = struct { /// Trailing: /// 0. `Inst.Ref` for every outputs_len /// 1. `Inst.Ref` for every inputs_len +/// 2. for every outputs_len +/// - constraint: memory at this position is reinterpreted as a null +/// terminated string. pad to the next u32 after the null byte. +/// 3. for every inputs_len +/// - constraint: memory at this position is reinterpreted as a null +/// terminated string. pad to the next u32 after the null byte. +/// 4. for every clobbers_len +/// - clobber_name: memory at this position is reinterpreted as a null +/// terminated string. pad to the next u32 after the null byte. +/// 5. A number of u32 elements follow according to the equation `(source_len + 3) / 4`. +/// Memory starting at this position is reinterpreted as the source bytes. pub const Asm = struct { - /// Index to the corresponding ZIR instruction. - /// `asm_source`, `outputs_len`, `inputs_len`, `clobbers_len`, `is_volatile`, and - /// clobbers are found via here. - zir_index: u32, + /// Length of the assembly source in bytes. + source_len: u32, + outputs_len: u32, + inputs_len: u32, + /// The MSB is `is_volatile`. + /// The rest of the bits are `clobbers_len`. + flags: u32, }; pub const Cmpxchg = struct { diff --git a/src/Compilation.zig b/src/Compilation.zig index e776b43508a9..118fa92a0066 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2778,7 +2778,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress errdefer if (!liveness_frame_ended) liveness_frame.end(); log.debug("analyze liveness of {s}", .{decl.name}); - var liveness = try Liveness.analyze(gpa, air, decl.getFileScope().zir); + var liveness = try Liveness.analyze(gpa, air); defer liveness.deinit(gpa); liveness_frame.end(); @@ -2786,7 +2786,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress if (builtin.mode == .Debug and comp.verbose_air) { std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); - @import("print_air.zig").dump(gpa, air, decl.getFileScope().zir, liveness); + @import("print_air.zig").dump(gpa, air, liveness); std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); } diff --git a/src/Liveness.zig b/src/Liveness.zig index 12ba63fc005b..e2c729b87368 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -12,7 +12,6 @@ const log = std.log.scoped(.liveness); const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Air = @import("Air.zig"); -const Zir = @import("Zir.zig"); const Log2Int = std.math.Log2Int; /// This array is split into sets of 4 bits per AIR instruction. @@ -52,7 +51,7 @@ pub const SwitchBr = struct { else_death_count: u32, }; -pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness { +pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness { const tracy = trace(@src()); defer tracy.end(); @@ -66,7 +65,6 @@ pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness { ), .extra = .{}, .special = .{}, - .zir = &zir, }; errdefer gpa.free(a.tomb_bits); errdefer a.special.deinit(gpa); @@ -157,7 +155,6 @@ const Analysis = struct { tomb_bits: []usize, special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32), extra: std.ArrayListUnmanaged(u32), - zir: *const Zir, fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void { const usize_index = (inst * bpi) / @bitSizeOf(usize); @@ -444,15 +441,24 @@ fn analyzeInst( }, .assembly => { const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload); - const extended = a.zir.instructions.items(.data)[extra.data.zir_index].extended; - const outputs_len = @truncate(u5, extended.small); - const inputs_len = @truncate(u5, extended.small >> 5); - const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]); - const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]); - if (outputs.len + args.len <= bpi - 1) { + var extra_i: usize = extra.end; + const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]); + extra_i += outputs.len; + const inputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]); + extra_i += inputs.len; + + simple: { var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); - std.mem.copy(Air.Inst.Ref, &buf, outputs); - std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); + var buf_index: usize = 0; + for (outputs) |output| { + if (output != .none) { + if (buf_index >= buf.len) break :simple; + buf[buf_index] = output; + buf_index += 1; + } + } + if (buf_index + inputs.len > buf.len) break :simple; + std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); return trackOperands(a, new_set, inst, main_tomb, buf); } var extra_tombs: ExtraTombs = .{ @@ -462,10 +468,12 @@ fn analyzeInst( .main_tomb = main_tomb, }; for (outputs) |output| { - try extra_tombs.feed(output); + if (output != .none) { + try extra_tombs.feed(output); + } } - for (args) |arg| { - try extra_tombs.feed(arg); + for (inputs) |input| { + try extra_tombs.feed(input); } return extra_tombs.finish(); }, diff --git a/src/Sema.zig b/src/Sema.zig index 7bf1b88260b5..d3b6ae25210e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1124,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .frame_address => return sema.zirFrameAddress( block, extended), .alloc => return sema.zirAllocExtended( block, extended), .builtin_extern => return sema.zirBuiltinExtern( block, extended), - .@"asm" => return sema.zirAsm( block, extended, inst), + .@"asm" => return sema.zirAsm( block, extended), .typeof_peer => return sema.zirTypeofPeer( block, extended), .compile_log => return sema.zirCompileLog( block, extended), .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode), @@ -9083,7 +9083,6 @@ fn zirAsm( sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, - inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -9094,6 +9093,7 @@ fn zirAsm( const outputs_len = @truncate(u5, extended.small); const inputs_len = @truncate(u5, extended.small >> 5); const clobbers_len = @truncate(u5, extended.small >> 10); + const is_volatile = @truncate(u1, extended.small >> 15) != 0; if (extra.data.asm_source == 0) { // This can move to become an AstGen error after inline assembly improvements land @@ -9107,6 +9107,7 @@ fn zirAsm( var extra_i = extra.end; var output_type_bits = extra.data.output_type_bits; + var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len; const Output = struct { constraint: []const u8, ty: Type }; const output: ?Output = if (outputs_len == 0) null else blk: { @@ -9121,6 +9122,8 @@ fn zirAsm( } const constraint = sema.code.nullTerminatedString(output.data.constraint); + needed_capacity += constraint.len / 4 + 1; + break :blk Output{ .constraint = constraint, .ty = try sema.resolveType(block, ret_ty_src, output.data.operand), @@ -9138,28 +9141,65 @@ fn zirAsm( _ = name; // TODO: use the name arg.* = sema.resolveInst(input.data.operand); - inputs[arg_i] = sema.code.nullTerminatedString(input.data.constraint); + const constraint = sema.code.nullTerminatedString(input.data.constraint); + needed_capacity += constraint.len / 4 + 1; + inputs[arg_i] = constraint; } const clobbers = try sema.arena.alloc([]const u8, clobbers_len); for (clobbers) |*name| { name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); extra_i += 1; + + needed_capacity += name.*.len / 4 + 1; } - try sema.requireRuntimeBlock(block, src); + const asm_source = sema.code.nullTerminatedString(extra.data.asm_source); + needed_capacity += (asm_source.len + 3) / 4; + const gpa = sema.gpa; - try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Asm).Struct.fields.len + args.len); + try sema.requireRuntimeBlock(block, src); + try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); const asm_air = try block.addInst(.{ .tag = .assembly, .data = .{ .ty_pl = .{ .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type, .payload = sema.addExtraAssumeCapacity(Air.Asm{ - .zir_index = inst, + .source_len = @intCast(u32, asm_source.len), + .outputs_len = outputs_len, + .inputs_len = @intCast(u32, args.len), + .flags = (@as(u32, @boolToInt(is_volatile)) << 31) | @intCast(u32, clobbers.len), }), } }, }); + if (output != null) { + // Indicate the output is the asm instruction return value. + sema.air_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.none)); + } sema.appendRefsAssumeCapacity(args); + if (output) |o| { + const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); + mem.copy(u8, buffer, o.constraint); + buffer[o.constraint.len] = 0; + sema.air_extra.items.len += o.constraint.len / 4 + 1; + } + for (inputs) |constraint| { + const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); + mem.copy(u8, buffer, constraint); + buffer[constraint.len] = 0; + sema.air_extra.items.len += constraint.len / 4 + 1; + } + for (clobbers) |clobber| { + const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); + mem.copy(u8, buffer, clobber); + buffer[clobber.len] = 0; + sema.air_extra.items.len += clobber.len / 4 + 1; + } + { + const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); + mem.copy(u8, buffer, asm_source); + sema.air_extra.items.len += (asm_source.len + 3) / 4; + } return asm_air; } diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 6e83ad72d1e3..d911c49904b4 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4,7 +4,6 @@ const mem = std.mem; const math = std.math; const assert = std.debug.assert; const Air = @import("../../Air.zig"); -const Zir = @import("../../Zir.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); @@ -2007,36 +2006,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); } -fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { - const ty_str = self.air.instructions.items(.data)[inst].ty_str; - const zir = &self.mod_fn.owner_decl.getFileScope().zir; - const name = zir.nullTerminatedString(ty_str.str); - const name_with_null = name.ptr[0 .. name.len + 1]; - const ty = self.air.getRefType(ty_str.ty); - - switch (mcv) { - .register => |reg| { - switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_info.ensureUnusedCapacity(3); - dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter); - dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc - 1, // ULEB128 dwarf expression length - reg.dwarfLocOp(), - }); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - }, - .plan9 => {}, - .none => {}, - } - }, - .stack_offset => {}, - else => {}, - } -} - fn airArg(self: *Self, inst: Air.Inst.Index) !void { const arg_index = self.arg_index; self.arg_index += 1; @@ -2852,40 +2821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { } fn airAsm(self: *Self, inst: Air.Inst.Index) !void { - const air_datas = self.air.instructions.items(.data); - const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); - const zir = self.mod_fn.owner_decl.getFileScope().zir; - const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; - const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); - const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); - const outputs_len = @truncate(u5, extended.small); - const args_len = @truncate(u5, extended.small >> 5); - const clobbers_len = @truncate(u5, extended.small >> 10); - _ = clobbers_len; // TODO honor these - const is_volatile = @truncate(u1, extended.small >> 15) != 0; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]); - - if (outputs_len > 1) { - return self.fail("TODO implement codegen for asm with more than 1 output", .{}); - } - var extra_i: usize = zir_extra.end; - const output_constraint: ?[]const u8 = out: { - var i: usize = 0; - while (i < outputs_len) : (i += 1) { - const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); - extra_i = output.end; - break :out zir.nullTerminatedString(output.data.constraint); - } - break :out null; - }; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Asm, ty_pl.payload); + const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; + const clobbers_len = @truncate(u31, extra.data.flags); + var extra_i: usize = extra.end; + const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + extra_i += outputs.len; + const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); const result: MCValue = if (dead) .dead else result: { - for (args) |arg| { - const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); - extra_i = input.end; - const constraint = zir.nullTerminatedString(input.data.constraint); + if (outputs.len > 1) { + return self.fail("TODO implement codegen for asm with more than 1 output", .{}); + } + + const output_constraint: ?[]const u8 = for (outputs) |output| { + if (output != .none) { + return self.fail("TODO implement codegen for non-expr asm", .{}); + } + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + + break constraint; + } else null; + + for (inputs) |input| { + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); @@ -2894,11 +2862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const reg = parseRegName(reg_name) orelse return self.fail("unrecognized register: '{s}'", .{reg_name}); - const arg_mcv = try self.resolveInst(arg); + const arg_mcv = try self.resolveInst(input); try self.register_manager.getReg(reg, null); - try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); + try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); + } + + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + // TODO honor these + } } + const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; + if (mem.eql(u8, asm_source, "svc #0")) { _ = try self.addInst(.{ .tag = .svc, @@ -2925,18 +2907,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .none = {} }; } }; - if (outputs.len + args.len <= Liveness.bpi - 1) { + + simple: { var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); - std.mem.copy(Air.Inst.Ref, &buf, outputs); - std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); + var buf_index: usize = 0; + for (outputs) |output| { + if (output == .none) continue; + + if (buf_index >= buf.len) break :simple; + buf[buf_index] = output; + buf_index += 1; + } + if (buf_index + inputs.len > buf.len) break :simple; + std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); return self.finishAir(inst, result, buf); } - var bt = try self.iterateBigTomb(inst, outputs.len + args.len); + var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); for (outputs) |output| { + if (output == .none) continue; + bt.feed(output); } - for (args) |arg| { - bt.feed(arg); + for (inputs) |input| { + bt.feed(input); } return bt.finishAir(result); } diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index e211f9d7bc84..7f0cecc14d76 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4,7 +4,6 @@ const mem = std.mem; const math = std.math; const assert = std.debug.assert; const Air = @import("../../Air.zig"); -const Zir = @import("../../Zir.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); @@ -3059,40 +3058,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { } fn airAsm(self: *Self, inst: Air.Inst.Index) !void { - const air_datas = self.air.instructions.items(.data); - const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); - const zir = self.mod_fn.owner_decl.getFileScope().zir; - const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; - const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); - const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); - const outputs_len = @truncate(u5, extended.small); - const args_len = @truncate(u5, extended.small >> 5); - const clobbers_len = @truncate(u5, extended.small >> 10); - _ = clobbers_len; // TODO honor these - const is_volatile = @truncate(u1, extended.small >> 15) != 0; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]); - - if (outputs_len > 1) { - return self.fail("TODO implement codegen for asm with more than 1 output", .{}); - } - var extra_i: usize = zir_extra.end; - const output_constraint: ?[]const u8 = out: { - var i: usize = 0; - while (i < outputs_len) : (i += 1) { - const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); - extra_i = output.end; - break :out zir.nullTerminatedString(output.data.constraint); - } - break :out null; - }; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Asm, ty_pl.payload); + const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; + const clobbers_len = @truncate(u31, extra.data.flags); + var extra_i: usize = extra.end; + const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + extra_i += outputs.len; + const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); const result: MCValue = if (dead) .dead else result: { - for (args) |arg| { - const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); - extra_i = input.end; - const constraint = zir.nullTerminatedString(input.data.constraint); + if (outputs.len > 1) { + return self.fail("TODO implement codegen for asm with more than 1 output", .{}); + } + + const output_constraint: ?[]const u8 = for (outputs) |output| { + if (output != .none) { + return self.fail("TODO implement codegen for non-expr asm", .{}); + } + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + + break constraint; + } else null; + + for (inputs) |input| { + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); @@ -3101,11 +3099,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const reg = parseRegName(reg_name) orelse return self.fail("unrecognized register: '{s}'", .{reg_name}); - const arg_mcv = try self.resolveInst(arg); + const arg_mcv = try self.resolveInst(input); try self.register_manager.getReg(reg, null); - try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); + try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); + } + + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + // TODO honor these + } } + const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; + if (mem.eql(u8, asm_source, "svc #0")) { _ = try self.addInst(.{ .tag = .svc, @@ -3128,18 +3140,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .none = {} }; } }; - if (outputs.len + args.len <= Liveness.bpi - 1) { + + simple: { var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); - std.mem.copy(Air.Inst.Ref, &buf, outputs); - std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); + var buf_index: usize = 0; + for (outputs) |output| { + if (output == .none) continue; + + if (buf_index >= buf.len) break :simple; + buf[buf_index] = output; + buf_index += 1; + } + if (buf_index + inputs.len > buf.len) break :simple; + std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); return self.finishAir(inst, result, buf); } - var bt = try self.iterateBigTomb(inst, outputs.len + args.len); + var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); for (outputs) |output| { + if (output == .none) continue; + bt.feed(output); } - for (args) |arg| { - bt.feed(arg); + for (inputs) |input| { + bt.feed(input); } return bt.finishAir(result); } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index a6e15dd76c2c..bcb96188041f 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -4,7 +4,6 @@ const mem = std.mem; const math = std.math; const assert = std.debug.assert; const Air = @import("../../Air.zig"); -const Zir = @import("../../Zir.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); @@ -1822,40 +1821,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { } fn airAsm(self: *Self, inst: Air.Inst.Index) !void { - const air_datas = self.air.instructions.items(.data); - const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); - const zir = self.mod_fn.owner_decl.getFileScope().zir; - const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; - const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); - const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); - const outputs_len = @truncate(u5, extended.small); - const args_len = @truncate(u5, extended.small >> 5); - const clobbers_len = @truncate(u5, extended.small >> 10); - _ = clobbers_len; // TODO honor these - const is_volatile = @truncate(u1, extended.small >> 15) != 0; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]); - - if (outputs_len > 1) { - return self.fail("TODO implement codegen for asm with more than 1 output", .{}); - } - var extra_i: usize = zir_extra.end; - const output_constraint: ?[]const u8 = out: { - var i: usize = 0; - while (i < outputs_len) : (i += 1) { - const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); - extra_i = output.end; - break :out zir.nullTerminatedString(output.data.constraint); - } - break :out null; - }; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Asm, ty_pl.payload); + const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; + const clobbers_len = @truncate(u31, extra.data.flags); + var extra_i: usize = extra.end; + const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + extra_i += outputs.len; + const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); const result: MCValue = if (dead) .dead else result: { - for (args) |arg| { - const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); - extra_i = input.end; - const constraint = zir.nullTerminatedString(input.data.constraint); + if (outputs.len > 1) { + return self.fail("TODO implement codegen for asm with more than 1 output", .{}); + } + + const output_constraint: ?[]const u8 = for (outputs) |output| { + if (output != .none) { + return self.fail("TODO implement codegen for non-expr asm", .{}); + } + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + + break constraint; + } else null; + + for (inputs) |input| { + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); @@ -1864,11 +1862,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const reg = parseRegName(reg_name) orelse return self.fail("unrecognized register: '{s}'", .{reg_name}); - const arg_mcv = try self.resolveInst(arg); + const arg_mcv = try self.resolveInst(input); try self.register_manager.getReg(reg, null); - try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); + try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); } + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + // TODO honor these + } + } + + const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; + if (mem.eql(u8, asm_source, "ecall")) { _ = try self.addInst(.{ .tag = .ecall, @@ -1890,18 +1902,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .none = {} }; } }; - if (outputs.len + args.len <= Liveness.bpi - 1) { + simple: { var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); - std.mem.copy(Air.Inst.Ref, &buf, outputs); - std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); + var buf_index: usize = 0; + for (outputs) |output| { + if (output == .none) continue; + + if (buf_index >= buf.len) break :simple; + buf[buf_index] = output; + buf_index += 1; + } + if (buf_index + inputs.len > buf.len) break :simple; + std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); return self.finishAir(inst, result, buf); } - var bt = try self.iterateBigTomb(inst, outputs.len + args.len); + var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); for (outputs) |output| { + if (output == .none) continue; + bt.feed(output); } - for (args) |arg| { - bt.feed(arg); + for (inputs) |input| { + bt.feed(input); } return bt.finishAir(result); } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bf49aedd2ddd..6c04023c383c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -26,7 +26,6 @@ const Target = std.Target; const Type = @import("../../type.zig").Type; const TypedValue = @import("../../TypedValue.zig"); const Value = @import("../../value.zig").Value; -const Zir = @import("../../Zir.zig"); const InnerError = error{ OutOfMemory, @@ -3415,41 +3414,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { } fn airAsm(self: *Self, inst: Air.Inst.Index) !void { - const air_datas = self.air.instructions.items(.data); - const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); - const zir = self.mod_fn.owner_decl.getFileScope().zir; - const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; - const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); - const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); - const outputs_len = @truncate(u5, extended.small); - const args_len = @truncate(u5, extended.small >> 5); - const clobbers_len = @truncate(u5, extended.small >> 10); - _ = clobbers_len; // TODO honor these - const is_volatile = @truncate(u1, extended.small >> 15) != 0; - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..args_len]); - - if (outputs_len > 1) { - return self.fail("TODO implement codegen for asm with more than 1 output", .{}); - } - var extra_i: usize = zir_extra.end; - const output_constraint: ?[]const u8 = out: { - var i: usize = 0; - while (i < outputs_len) : (i += 1) { - const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); - extra_i = output.end; - break :out zir.nullTerminatedString(output.data.constraint); - } - break :out null; - }; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Asm, ty_pl.payload); + const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; + const clobbers_len = @truncate(u31, extra.data.flags); + var extra_i: usize = extra.end; + const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + extra_i += outputs.len; + const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); - const result: MCValue = if (dead) - .dead - else result: { - for (args) |arg| { - const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); - extra_i = input.end; - const constraint = zir.nullTerminatedString(input.data.constraint); + const result: MCValue = if (dead) .dead else result: { + if (outputs.len > 1) { + return self.fail("TODO implement codegen for asm with more than 1 output", .{}); + } + + const output_constraint: ?[]const u8 = for (outputs) |output| { + if (output != .none) { + return self.fail("TODO implement codegen for non-expr asm", .{}); + } + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + + break constraint; + } else null; + + for (inputs) |input| { + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); @@ -3458,11 +3455,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const reg = parseRegName(reg_name) orelse return self.fail("unrecognized register: '{s}'", .{reg_name}); - const arg_mcv = try self.resolveInst(arg); + const arg_mcv = try self.resolveInst(input); try self.register_manager.getReg(reg, null); - try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); + try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); + } + + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + // TODO honor these + } } + const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; + { var iter = std.mem.tokenize(u8, asm_source, "\n\r"); while (iter.next()) |ins| { @@ -3529,14 +3540,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .none = {} }; } }; - if (args.len <= Liveness.bpi - 1) { + + simple: { var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); - std.mem.copy(Air.Inst.Ref, &buf, args); + var buf_index: usize = 0; + for (outputs) |output| { + if (output == .none) continue; + + if (buf_index >= buf.len) break :simple; + buf[buf_index] = output; + buf_index += 1; + } + if (buf_index + inputs.len > buf.len) break :simple; + std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); return self.finishAir(inst, result, buf); } - var bt = try self.iterateBigTomb(inst, args.len); - for (args) |arg| { - bt.feed(arg); + var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); + for (outputs) |output| { + if (output == .none) continue; + + bt.feed(output); + } + for (inputs) |input| { + bt.feed(input); } return bt.finishAir(result); } @@ -3615,7 +3641,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); }, - else => return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}), + else => return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}), } }, .embedded_in_code => { @@ -3623,7 +3649,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); } - return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}); + return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}); }, .memory, .direct_load, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 0391fa4c8445..460347740411 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -15,7 +15,6 @@ const Decl = Module.Decl; const trace = @import("../tracy.zig").trace; const LazySrcLoc = Module.LazySrcLoc; const Air = @import("../Air.zig"); -const Zir = @import("../Zir.zig"); const Liveness = @import("../Liveness.zig"); const Mutability = enum { Const, Mut }; @@ -2805,49 +2804,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { } fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { - const air_datas = f.air.instructions.items(.data); - const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); - const zir = f.object.dg.decl.getFileScope().zir; - const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; - const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); - const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); - const outputs_len = @truncate(u5, extended.small); - const args_len = @truncate(u5, extended.small >> 5); - const clobbers_len = @truncate(u5, extended.small >> 10); - _ = clobbers_len; // TODO honor these - const is_volatile = @truncate(u1, extended.small >> 15) != 0; - const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]); - const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]); - - if (outputs_len > 1) { + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const extra = f.air.extraData(Air.Asm, ty_pl.payload); + const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; + const clobbers_len = @truncate(u31, extra.data.flags); + var extra_i: usize = extra.end; + const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]); + extra_i += outputs.len; + const inputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); + extra_i += inputs.len; + + if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none; + + if (outputs.len > 1) { return f.fail("TODO implement codegen for asm with more than 1 output", .{}); } - if (f.liveness.isUnused(inst) and !is_volatile) - return CValue.none; - - var extra_i: usize = zir_extra.end; - const output_constraint: ?[]const u8 = out: { - var i: usize = 0; - while (i < outputs_len) : (i += 1) { - const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); - extra_i = output.end; - break :out zir.nullTerminatedString(output.data.constraint); + const output_constraint: ?[]const u8 = for (outputs) |output| { + if (output != .none) { + return f.fail("TODO implement codegen for non-expr asm", .{}); } - break :out null; - }; - const args_extra_begin = extra_i; + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + + break constraint; + } else null; const writer = f.object.writer(); - for (args) |arg| { - const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); - extra_i = input.end; - const constraint = zir.nullTerminatedString(input.data.constraint); + const inputs_extra_begin = extra_i; + + for (inputs) |input| { + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { const reg = constraint[1 .. constraint.len - 1]; - const arg_c_value = try f.resolveInst(arg); + const arg_c_value = try f.resolveInst(input); try writer.writeAll("register "); - try f.renderType(writer, f.air.typeOf(arg)); + try f.renderType(writer, f.air.typeOf(input)); try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); try f.writeCValue(writer, arg_c_value); @@ -2856,21 +2854,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { return f.fail("TODO non-explicit inline asm regs", .{}); } } + + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + // TODO honor these + } + } + + const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; + const volatile_string: []const u8 = if (is_volatile) "volatile " else ""; try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source }); if (output_constraint) |_| { return f.fail("TODO: CBE inline asm output", .{}); } - if (args.len > 0) { + if (inputs.len > 0) { if (output_constraint == null) { try writer.writeAll(" :"); } try writer.writeAll(": "); - extra_i = args_extra_begin; - for (args) |_, index| { - const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); - extra_i = input.end; - const constraint = zir.nullTerminatedString(input.data.constraint); + extra_i = inputs_extra_begin; + for (inputs) |_, index| { + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { const reg = constraint[1 .. constraint.len - 1]; if (index > 0) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 3d65829c1ed3..03db7306e660 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2,24 +2,21 @@ const std = @import("std"); const builtin = @import("builtin"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; -const Compilation = @import("../Compilation.zig"); -const llvm = @import("llvm/bindings.zig"); -const link = @import("../link.zig"); const log = std.log.scoped(.codegen); const math = std.math; const native_endian = builtin.cpu.arch.endian(); +const llvm = @import("llvm/bindings.zig"); +const link = @import("../link.zig"); +const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); const Module = @import("../Module.zig"); const TypedValue = @import("../TypedValue.zig"); -const Zir = @import("../Zir.zig"); const Air = @import("../Air.zig"); const Liveness = @import("../Liveness.zig"); const target_util = @import("../target.zig"); - const Value = @import("../value.zig").Value; const Type = @import("../type.zig").Type; - const LazySrcLoc = Module.LazySrcLoc; const Error = error{ OutOfMemory, CodegenFail }; @@ -2893,33 +2890,21 @@ pub const FuncGen = struct { // as stage1. const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const air_asm = self.air.extraData(Air.Asm, ty_pl.payload); - const zir = self.dg.decl.getFileScope().zir; - const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended; - const is_volatile = @truncate(u1, extended.small >> 15) != 0; - if (!is_volatile and self.liveness.isUnused(inst)) { - return null; - } - const outputs_len = @truncate(u5, extended.small); - if (outputs_len > 1) { + const extra = self.air.extraData(Air.Asm, ty_pl.payload); + const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; + const clobbers_len = @truncate(u31, extra.data.flags); + var extra_i: usize = extra.end; + + if (!is_volatile and self.liveness.isUnused(inst)) return null; + + const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + extra_i += outputs.len; + const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + extra_i += inputs.len; + + if (outputs.len > 1) { return self.todo("implement llvm codegen for asm with more than 1 output", .{}); } - const args_len = @truncate(u5, extended.small >> 5); - const clobbers_len = @truncate(u5, extended.small >> 10); - const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); - const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end..][0..args_len]); - - var extra_i: usize = zir_extra.end; - const output_constraint: ?[]const u8 = out: { - var i: usize = 0; - while (i < outputs_len) : (i += 1) { - const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); - extra_i = output.end; - break :out zir.nullTerminatedString(output.data.constraint); - } - break :out null; - }; var llvm_constraints: std.ArrayListUnmanaged(u8) = .{}; defer llvm_constraints.deinit(self.gpa); @@ -2928,14 +2913,21 @@ pub const FuncGen = struct { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const llvm_params_len = args.len; + const llvm_params_len = inputs.len; const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len); const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len); - var llvm_param_i: usize = 0; var total_i: usize = 0; - if (output_constraint) |constraint| { + for (outputs) |output| { + if (output != .none) { + return self.todo("implement inline asm with non-returned output", .{}); + } + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); if (total_i != 0) { llvm_constraints.appendAssumeCapacity(','); @@ -2946,11 +2938,13 @@ pub const FuncGen = struct { total_i += 1; } - for (args) |arg| { - const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); - extra_i = input.end; - const constraint = zir.nullTerminatedString(input.data.constraint); - const arg_llvm_value = try self.resolveInst(arg); + for (inputs) |input| { + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + + const arg_llvm_value = try self.resolveInst(input); llvm_param_values[llvm_param_i] = arg_llvm_value; llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); @@ -2965,19 +2959,26 @@ pub const FuncGen = struct { total_i += 1; } - const clobbers = zir.extra[extra_i..][0..clobbers_len]; - for (clobbers) |clobber_index| { - const clobber = zir.nullTerminatedString(clobber_index); - try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4); - if (total_i != 0) { - llvm_constraints.appendAssumeCapacity(','); - } - llvm_constraints.appendSliceAssumeCapacity("~{"); - llvm_constraints.appendSliceAssumeCapacity(clobber); - llvm_constraints.appendSliceAssumeCapacity("}"); + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4); + if (total_i != 0) { + llvm_constraints.appendAssumeCapacity(','); + } + llvm_constraints.appendSliceAssumeCapacity("~{"); + llvm_constraints.appendSliceAssumeCapacity(clobber); + llvm_constraints.appendSliceAssumeCapacity("}"); - total_i += 1; + total_i += 1; + } } + const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; const ret_ty = self.air.typeOfIndex(inst); const ret_llvm_ty = try self.dg.llvmType(ret_ty); diff --git a/src/print_air.zig b/src/print_air.zig index 7270f626021a..dd83d3fdb8d2 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -4,11 +4,10 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin; const Module = @import("Module.zig"); const Value = @import("value.zig").Value; -const Zir = @import("Zir.zig"); const Air = @import("Air.zig"); const Liveness = @import("Liveness.zig"); -pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void { +pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void { const instruction_bytes = air.instructions.len * // Here we don't use @sizeOf(Air.Inst.Data) because it would include // 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 { .gpa = gpa, .arena = arena.allocator(), .air = air, - .zir = zir, .liveness = liveness, .indent = 2, }; @@ -63,7 +61,6 @@ const Writer = struct { gpa: Allocator, arena: Allocator, air: Air, - zir: Zir, liveness: Liveness, indent: usize, @@ -431,51 +428,67 @@ const Writer = struct { fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; - const air_asm = w.air.extraData(Air.Asm, ty_pl.payload); - const zir = w.zir; - const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended; - const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); - const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); - const outputs_len = @truncate(u5, extended.small); - const args_len = @truncate(u5, extended.small >> 5); - const clobbers_len = @truncate(u5, extended.small >> 10); - const args = @bitCast([]const Air.Inst.Ref, w.air.extra[air_asm.end..][0..args_len]); - - var extra_i: usize = zir_extra.end; - const output_constraint: ?[]const u8 = out: { - var i: usize = 0; - while (i < outputs_len) : (i += 1) { - const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); - extra_i = output.end; - break :out zir.nullTerminatedString(output.data.constraint); - } - break :out null; - }; + const extra = w.air.extraData(Air.Asm, ty_pl.payload); + const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; + const clobbers_len = @truncate(u31, extra.data.flags); + var extra_i: usize = extra.end; + var op_index: usize = 0; + + const ret_ty = w.air.typeOfIndex(inst); + try s.print("{}", .{ret_ty}); + + if (is_volatile) { + try s.writeAll(", volatile"); + } - try s.print("\"{s}\"", .{asm_source}); + const outputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.outputs_len]); + extra_i += outputs.len; + const inputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.inputs_len]); + extra_i += inputs.len; - if (output_constraint) |constraint| { - const ret_ty = w.air.typeOfIndex(inst); - try s.print(", {s} -> {}", .{ constraint, ret_ty }); + for (outputs) |output| { + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + + if (output == .none) { + try s.print(", -> {s}", .{constraint}); + } else { + try s.print(", out {s} = (", .{constraint}); + try w.writeOperand(s, inst, op_index, output); + op_index += 1; + try s.writeByte(')'); + } } - for (args) |arg| { - const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); - extra_i = input.end; - const constraint = zir.nullTerminatedString(input.data.constraint); + for (inputs) |input| { + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; - try s.print(", {s} = (", .{constraint}); - try w.writeOperand(s, inst, 0, arg); + try s.print(", in {s} = (", .{constraint}); + try w.writeOperand(s, inst, op_index, input); + op_index += 1; try s.writeByte(')'); } - const clobbers = zir.extra[extra_i..][0..clobbers_len]; - for (clobbers) |clobber_index| { - const clobber = zir.nullTerminatedString(clobber_index); - try s.writeAll(", ~{"); - try s.writeAll(clobber); - try s.writeAll("}"); + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + try s.writeAll(", ~{"); + try s.writeAll(clobber); + try s.writeAll("}"); + } } + const asm_source = std.mem.sliceAsBytes(w.air.extra[extra_i..])[0..extra.data.source_len]; + try s.print(", \"{s}\"", .{asm_source}); } fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {